This repository has been archived by the owner on Mar 19, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 8
/
task.c
243 lines (204 loc) · 7.09 KB
/
task.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#include <system.h>
#include <frame.h>
#include <task.h>
#include <page.h>
#include <tss.h>
/* The currently running task */
volatile struct task* current_task;
/* The start of the task linked list */
volatile struct task* ready_queue;
/* Some externs are required to access members
* in page.c / process.asm */
extern addr initial_esp;
extern addr read_eip();
/* The next available process ID */
unsigned int next_pid = 1;
/* Moves the current stack into the new position */
void move_stack(void* new_stack_start, addr size)
{
addr i;
/* Allocate some space for the new stack */
for (i = (addr)new_stack_start; i >= ((addr)new_stack_start-size); i -= 0x1000)
{
/* General-purpose stack is in user-mode */
frame_alloc((struct page*)get_page(i, 1, current_directory), 0 /* User mode */, 1 /* Writable */);
}
/* Flush the TLB by reading and writing the page directory
* address again */
addr pd_addr;
asm volatile("mov %%cr3, %0" : "=r" (pd_addr));
asm volatile("mov %0, %%cr3" : : "r" (pd_addr));
/* Copy the old ESP and EBP registers and create
* new stack and base registers */
addr old_stack_pointer;
addr old_base_pointer;
asm volatile("mov %%esp, %0" : "=r" (old_stack_pointer));
asm volatile("mov %%ebp, %0" : "=r" (old_base_pointer));
addr offset = (addr)new_stack_start - initial_esp;
addr new_stack_pointer = old_stack_pointer + offset;
addr new_base_pointer = old_base_pointer + offset;
/* Copy the stack */
memcpy((void*)new_stack_pointer, (void*)old_stack_pointer, initial_esp - old_stack_pointer);
/* Backtrace through the original stack, copying new values into
* the new stack */
for (i = (addr)new_stack_start; i > (addr)new_stack_start - size; i -= 4)
{
addr tmp = *(addr*)i;
/* If the value of tmp is inside the range of the old stack, assume
* it is a base pointer and remap it. This will unfortunately remap
* ANY value in this range, whether they are base pointers or not */
if ((old_stack_pointer < tmp) && (tmp < initial_esp))
{
tmp = tmp + offset;
addr* tmp2 = (addr*)i;
*tmp2 = tmp;
}
}
/* Change stacks */
asm volatile("mov %0, %%esp" : : "r" (new_stack_pointer));
asm volatile("mov %0, %%ebp" : : "r" (new_base_pointer));
}
/* Forks the current process */
int fork()
{
/* We are modifying kernel structures, so disable interrupts */
asm volatile("cli");
/* Take a pointer to this process' task struct for later reference */
struct task* parent_task = (struct task*)current_task;
/* Clone the address space */
struct page_directory* directory = (struct page_directory*)clone_directory(current_directory);
/* Create a new process */
struct task* new_task = (struct task*)kmalloc(sizeof(struct task));
new_task->id = next_pid++;
new_task->esp = new_task->ebp = 0;
new_task->eip = 0;
new_task->page_directory = directory;
new_task->kernel_stack = kmalloc_a(KERNEL_STACK_SIZE);
new_task->next = 0;
/* Add it to the end of the ready queue
* Find the end of the ready queue */
struct task* tmp_task = (struct task*)ready_queue;
while (tmp_task->next)
tmp_task = tmp_task->next;
/* And extend it */
tmp_task->next = new_task;
/* This will be the entry point for the new process */
addr eip = read_eip();
/* When we fork the child process, it will begin executing
* here, so we need to be able to tell whether or not we
* are executing as the parent task or the child task. We
* can check current_task against parent_task to determine
* who we are */
/* We could be the parent of the child here, check */
if (current_task == parent_task)
{
/* We are the parent, so set up the esp / ebp / eip
* for our child */
addr esp; asm volatile("mov %%esp, %0" : "=r"(esp));
addr ebp; asm volatile("mov %%ebp, %0" : "=r"(ebp));
new_task->esp = esp;
new_task->ebp = ebp;
new_task->eip = eip;
/* All finished; re-enable interrupts */
asm volatile("sti");
return new_task->id;
}
else
{
/* We are the child, by convention return 0 */
return 0;
}
}
/* Gets the current process ID */
int getpid()
{
return current_task->id;
}
/* Switchs between tasks; called by the timer interrupt automatically */
void switch_task()
{
/* If we haven't initialized tasking yet, just return */
if (!current_task)
return;
/* Quickly grab the esp, ebp now for usage later on */
addr esp, ebp, eip;
asm volatile("mov %%esp, %0" : "=r"(esp));
asm volatile("mov %%ebp, %0" : "=r"(ebp));
/* Read the instruction pointer. We do some cunning logic here;
* as one of two things could have happened when this function exits:
*
* (a) We call the function and it returns EIP as requested.
* (b) We have just switched tasks, and because the saved EIP is
* essentially the instruction after read_eip(), it will seem as
* if read_eip has just returned.
*
* In the second case we need to return immediately. To detect it,
* we put a dummy value in EAX further down at the end of this
* function. As C returns values in EAX, it will look like the
* return value is this dummy value (0x12345)! */
eip = read_eip();
/* Have we just switched tasks? */
if (eip == 0x12345)
return;
/* No, we didn't switch tasks. Let's save some register values
* and then switch */
current_task->eip = eip;
current_task->esp = esp;
current_task->ebp = ebp;
/* Go to the next task to run */
current_task = current_task->next;
if (!current_task) current_task = ready_queue;
/* Store the new esp and ebp values */
eip = current_task->eip;
esp = current_task->esp;
ebp = current_task->ebp;
/* Make sure the memory manager knows we've changed
* page directory */
current_directory = current_task->page_directory;
/* Change the kernel stack over */
tss_set_kernel_stack(current_task->kernel_stack + KERNEL_STACK_SIZE);
/* Here we:
* - Stop interrupts so we don't get interrupted.
* - Temporarily put the new EIP location in ECX.
* - Load the stack and base pointers from the new
* task structure.
* - Change page directory to the physical address
* of the new page directory.
* - Put a dummy value (0x12345) in EAX so that above
* we can recognise that we've just switched tasks.
* - Restart interrupts. The STI instruction has a
* delay; it doesn't take effect until after the
* next instruction.
* - Jump to the location in ECX (where EIP is stored).
*/
asm volatile(" \
cli; \
mov %0, %%ecx; \
mov %1, %%esp; \
mov %2, %%ebp; \
mov %3, %%cr3; \
mov $0x12345, %%eax; \
sti; \
jmp *%%ecx "
: : "r"(eip), "r"(esp), "r"(ebp), "r"(current_directory->phys_addr));
}
/* Installs the task manager */
void task_install()
{
puts("Enabling multitasking... ");
/* Disable interrupts */
asm volatile("cli");
/* Relocate the stack so we know where it is */
move_stack((void*)0xE0000000, 0x2000);
/* Initialize the first task (kernel task) */
current_task = ready_queue = (struct task*)kmalloc(sizeof(struct task));
current_task->id = next_pid++;
current_task->esp = current_task->ebp = 0;
current_task->eip = 0;
current_task->page_directory = current_directory;
current_task->next = 0;
current_task->kernel_stack = kmalloc_a(KERNEL_STACK_SIZE);
/* Re-enable interrupts */
asm volatile("sti");
puts("done.\n");
}