MushOS  0.1
A UNIX-like OS prototype, written from scratch
Loading...
Searching...
No Matches
task.c
1#include "task.h"
2
3#include "../../lib/base/generic.h"
4#include "../../lib/base/heap.h"
5#include "../../lib/base/memory.h"
6#include "../../lib/base/stdio.h"
7
8#include "pages.h"
9
10
11extern page_folder* kernel_directory, * current_directory;
12extern page_folder* clone_directory(page_folder* src);
13
14extern u_dword get_instruction_pointer();
15
16// The currently running task.
17// The start of the task linked list.
18volatile task* current_task, * ready_queue;
19
20// The next available process ID.
21u_dword next_pid = 0;
22
23
24void initialise_tasking() {
25 asm volatile("cli");
26
27 // Initialise the first task (kernel task)
28 current_task = ready_queue = (task*) ralloc(sizeof(task));
29 current_task->id = next_pid++;
30 current_task->esp = current_task->ebp = 0;
31 current_task->eip = 0;
32 current_task->page_directory = current_directory;
33 current_task->next = 0;
34
35 // Reenable interrupts.
36 asm volatile("sti");
37}
38
39
40u_dword fork() {
41 // We are modifying kernel structures, and so cannot be interrupted.
42 asm volatile("cli");
43
44 // Take a pointer to this process' task struct for later reference.
45 task *parent_task = (task*) current_task;
46
47 // Clone the address space.
48 page_folder* directory = clone_directory(current_directory);
49
50 // Create a new process.
51 task* new_task = (task*) ralloc(sizeof(task));
52 new_task->id = next_pid++;
53 new_task->esp = new_task->ebp = 0;
54 new_task->eip = 0;
55 new_task->page_directory = directory;
56 new_task->next = 0;
57
58 // Add it to the end of the ready queue.
59 // Find the end of the ready queue...
60 task* tmp_task = (task*) ready_queue;
61 while (tmp_task->next) tmp_task = tmp_task->next;
62 // ...And extend it.
63 tmp_task->next = new_task;
64
65 // This will be the entry point for the new process.
66 u_dword eip = get_instruction_pointer();
67
68 // We could be the parent or the child here - check.
69 if (current_task == parent_task) {
70 // We are the parent, so set up the esp/ebp/eip for our child.
71 asm volatile("mov %%esp, %0" : "=r"(new_task->esp));
72 asm volatile("mov %%ebp, %0" : "=r"(new_task->ebp));
73 new_task->eip = eip;
74 // All finished: Reenable interrupts.
75 asm volatile("sti");
76 return new_task->id;
77 } else {
78 return 0; // We are the child - by convention return 0.
79 }
80}
81
82void switch_task() {
83 // If we haven't initialised tasking yet, just return.
84 if (!current_task) return;
85
86 // Read esp, ebp now for saving later on.
87 u_dword esp, ebp;
88 asm volatile("mov %%esp, %0" : "=r"(esp));
89 asm volatile("mov %%ebp, %0" : "=r"(ebp));
90
91 // Read the instruction pointer. We do some cunning logic here:
92 // One of two things could have happened when this function exits -
93 // (a) We called the function and it returned the EIP as requested.
94 // (b) We have just switched tasks, and because the saved EIP is essentially
95 // the instruction after read_eip(), it will seem as if read_eip has just
96 // returned.
97 // In the second case we need to return immediately. To detect it we put a dummy
98 // value in EAX further down at the end of this function. As C returns values in EAX,
99 // it will look like the return value is this dummy value! (0xDEADBABA).
100 u_dword eip = get_instruction_pointer();
101
102 // Have we just switched tasks?
103 if (eip == 0xDEADBABA) return;
104
105 // No, we didn't switch tasks. Let's save some register values and switch.
106 current_task->eip = eip;
107 current_task->esp = esp;
108 current_task->ebp = ebp;
109
110 // Get the next task to run.
111 current_task = current_task->next;
112 // If we fell off the end of the linked list start again at the beginning.
113 if (current_task == nullptr) current_task = ready_queue;
114 eip = current_task->eip;
115 esp = current_task->esp;
116 ebp = current_task->ebp;
117
118 // Make sure the memory manager knows we've changed page directory.
119 current_directory = current_task->page_directory;
120
121 // Here we:
122 // * Stop interrupts so we don't get interrupted.
123 // * Temporarily put the new EIP location in ECX.
124 // * Load the stack and base pointers from the new task struct.
125 // * Change page directory to the physical address (physicalAddr) of the new directory.
126 // * Put a dummy value (0xDEADBABA) in EAX so that above we can recognise that we've just
127 // switched task.
128 // * Restart interrupts. The STI instruction has a delay - it doesn't take effect until after
129 // the next instruction.
130 // * Jump to the location in ECX (remember we put the new EIP in there).
131 asm volatile(
132 "cli;\n"
133 "mov %1, %%esp;\n"
134 "mov %2, %%ebp;\n"
135 "mov %3, %%cr3;\n"
136 "mov $0xDEADBABA, %%eax;\n"
137 "sti;\n"
138 "jmp *%%ecx;\n"
139 :: "c"(eip), "rm"(esp), "rm"(ebp), "r"(&current_directory->contents)
140 );
141}
142
143
144int getpid() {
145 return current_task->id;
146}
void * ralloc(u_dword size)