rev |
line source |
nuclear@52
|
1 #include <stdio.h>
|
nuclear@47
|
2 #include <string.h>
|
nuclear@52
|
3 #include <assert.h>
|
nuclear@57
|
4 #include <errno.h>
|
nuclear@55
|
5 #include "config.h"
|
nuclear@42
|
6 #include "proc.h"
|
nuclear@42
|
7 #include "tss.h"
|
nuclear@45
|
8 #include "vm.h"
|
nuclear@47
|
9 #include "segm.h"
|
nuclear@47
|
10 #include "intr.h"
|
nuclear@47
|
11 #include "panic.h"
|
nuclear@51
|
12 #include "syscall.h"
|
nuclear@51
|
13 #include "sched.h"
|
nuclear@54
|
14 #include "tss.h"
|
nuclear@72
|
15 #include "kdef.h"
|
nuclear@47
|
16
|
nuclear@55
|
17 #define FLAGS_INTR_BIT (1 << 9)
|
nuclear@47
|
18
|
nuclear@54
|
19 static void start_first_proc(void);
|
nuclear@54
|
20
|
nuclear@55
|
21 /* defined in proc-asm.S */
|
nuclear@57
|
22 uint32_t switch_stack(uint32_t new_stack, uint32_t *old_stack);
|
nuclear@57
|
23 void just_forked(void);
|
nuclear@54
|
24
|
nuclear@47
|
25 /* defined in test_proc.S */
|
nuclear@47
|
26 void test_proc(void);
|
nuclear@47
|
27 void test_proc_end(void);
|
nuclear@42
|
28
|
nuclear@42
|
29 static struct process proc[MAX_PROC];
|
nuclear@56
|
30
|
nuclear@56
|
31 /* cur_pid: pid of the currently executing process.
|
nuclear@56
|
32 * when we're in the idle process cur_pid will be 0.
|
nuclear@56
|
33 * last_pid: pid of the last real process that was running, this should
|
nuclear@56
|
34 * never become 0. Essentially this defines the active kernel stack.
|
nuclear@56
|
35 */
|
nuclear@56
|
36 static int cur_pid, last_pid;
|
nuclear@42
|
37
|
nuclear@54
|
38 static struct task_state *tss;
|
nuclear@54
|
39
|
nuclear@54
|
40
|
nuclear@42
|
41 void init_proc(void)
|
nuclear@42
|
42 {
|
nuclear@54
|
43 int tss_page;
|
nuclear@51
|
44
|
nuclear@54
|
45 /* allocate a page for the task state segment, to make sure
|
nuclear@54
|
46 * it doesn't cross page boundaries
|
nuclear@54
|
47 */
|
nuclear@54
|
48 if((tss_page = pgalloc(1, MEM_KERNEL)) == -1) {
|
nuclear@54
|
49 panic("failed to allocate memory for the task state segment\n");
|
nuclear@54
|
50 }
|
nuclear@55
|
51 tss = (struct task_state*)PAGE_TO_ADDR(tss_page);
|
nuclear@54
|
52
|
nuclear@54
|
53 /* the kernel stack segment never changes so we might as well set it now
|
nuclear@54
|
54 * the only other thing that we use in the tss is the kernel stack pointer
|
nuclear@54
|
55 * which is different for each process, and thus managed by context_switch
|
nuclear@54
|
56 */
|
nuclear@54
|
57 memset(tss, 0, sizeof *tss);
|
nuclear@54
|
58 tss->ss0 = selector(SEGM_KDATA, 0);
|
nuclear@54
|
59
|
nuclear@55
|
60 set_tss((uint32_t)tss);
|
nuclear@54
|
61
|
nuclear@54
|
62 /* initialize system call handler (see syscall.c) */
|
nuclear@51
|
63 init_syscall();
|
nuclear@42
|
64
|
nuclear@54
|
65 start_first_proc(); /* XXX never returns */
|
nuclear@54
|
66 }
|
nuclear@54
|
67
|
nuclear@54
|
68 static void start_first_proc(void)
|
nuclear@54
|
69 {
|
nuclear@54
|
70 struct process *p;
|
nuclear@54
|
71 int proc_size_pg, img_start_pg, stack_pg;
|
nuclear@55
|
72 uint32_t img_start_addr;
|
nuclear@54
|
73 struct intr_frame ifrm;
|
nuclear@54
|
74
|
nuclear@42
|
75 /* prepare the first process */
|
nuclear@54
|
76 p = proc + 1;
|
nuclear@54
|
77 p->id = 1;
|
nuclear@54
|
78 p->parent = 0; /* no parent for init */
|
nuclear@42
|
79
|
nuclear@55
|
80 p->ticks_left = TIMESLICE_TICKS;
|
nuclear@55
|
81 p->next = p->prev = 0;
|
nuclear@55
|
82
|
nuclear@55
|
83 /* the first process may keep this existing page table */
|
nuclear@55
|
84 p->ctx.pgtbl_paddr = get_pgdir_addr();
|
nuclear@55
|
85
|
nuclear@42
|
86 /* allocate a chunk of memory for the process image
|
nuclear@42
|
87 * and copy the code of test_proc there.
|
nuclear@42
|
88 */
|
nuclear@51
|
89 proc_size_pg = (test_proc_end - test_proc) / PGSIZE + 1;
|
nuclear@45
|
90 if((img_start_pg = pgalloc(proc_size_pg, MEM_USER)) == -1) {
|
nuclear@45
|
91 panic("failed to allocate space for the init process image\n");
|
nuclear@45
|
92 }
|
nuclear@54
|
93 img_start_addr = PAGE_TO_ADDR(img_start_pg);
|
nuclear@54
|
94 memcpy((void*)img_start_addr, test_proc, proc_size_pg * PGSIZE);
|
nuclear@54
|
95 printf("copied init process at: %x\n", img_start_addr);
|
nuclear@47
|
96
|
nuclear@69
|
97 /* allocate the first page of the user stack */
|
nuclear@47
|
98 stack_pg = ADDR_TO_PAGE(KMEM_START) - 1;
|
nuclear@47
|
99 if(pgalloc_vrange(stack_pg, 1) == -1) {
|
nuclear@47
|
100 panic("failed to allocate user stack page\n");
|
nuclear@47
|
101 }
|
nuclear@54
|
102 p->user_stack_pg = stack_pg;
|
nuclear@52
|
103
|
nuclear@54
|
104 /* allocate a kernel stack for this process */
|
nuclear@54
|
105 if((p->kern_stack_pg = pgalloc(KERN_STACK_SIZE / PGSIZE, MEM_KERNEL)) == -1) {
|
nuclear@54
|
106 panic("failed to allocate kernel stack for the init process\n");
|
nuclear@54
|
107 }
|
nuclear@54
|
108 /* when switching from user space to kernel space, the ss0:esp0 from TSS
|
nuclear@54
|
109 * will be used to switch to the per-process kernel stack, so we need to
|
nuclear@54
|
110 * set it correctly before switching to user space.
|
nuclear@54
|
111 * tss->ss0 is already set in init_proc above.
|
nuclear@54
|
112 */
|
nuclear@54
|
113 tss->esp0 = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE;
|
nuclear@45
|
114
|
nuclear@45
|
115
|
nuclear@54
|
116 /* now we need to fill in the fake interrupt stack frame */
|
nuclear@54
|
117 memset(&ifrm, 0, sizeof ifrm);
|
nuclear@54
|
118 /* after the priviledge switch, this ss:esp will be used in userspace */
|
nuclear@54
|
119 ifrm.esp = PAGE_TO_ADDR(stack_pg) + PGSIZE;
|
nuclear@54
|
120 ifrm.ss = selector(SEGM_UDATA, 3);
|
nuclear@54
|
121 /* instruction pointer at the beginning of the process image */
|
nuclear@55
|
122 ifrm.eip = img_start_addr;
|
nuclear@54
|
123 ifrm.cs = selector(SEGM_UCODE, 3);
|
nuclear@54
|
124 /* make sure the user will run with interrupts enabled */
|
nuclear@54
|
125 ifrm.eflags = FLAGS_INTR_BIT;
|
nuclear@54
|
126 /* user data selectors should all be the same */
|
nuclear@54
|
127 ifrm.ds = ifrm.es = ifrm.fs = ifrm.gs = ifrm.ss;
|
nuclear@42
|
128
|
nuclear@51
|
129 /* add it to the scheduler queues */
|
nuclear@55
|
130 add_proc(p->id);
|
nuclear@55
|
131
|
nuclear@56
|
132 /* make it current */
|
nuclear@56
|
133 set_current_pid(p->id);
|
nuclear@42
|
134
|
nuclear@68
|
135 /* build the current vm map */
|
nuclear@68
|
136 cons_vmmap(&p->vmmap);
|
nuclear@68
|
137
|
nuclear@54
|
138 /* execute a fake return from interrupt with the fake stack frame */
|
nuclear@54
|
139 intr_ret(ifrm);
|
nuclear@42
|
140 }
|
nuclear@42
|
141
|
nuclear@72
|
142 int sys_fork(void)
|
nuclear@57
|
143 {
|
nuclear@57
|
144 int i, pid;
|
nuclear@57
|
145 struct process *p, *parent;
|
nuclear@57
|
146
|
nuclear@57
|
147 disable_intr();
|
nuclear@57
|
148
|
nuclear@57
|
149 /* find a free process slot */
|
nuclear@57
|
150 /* TODO don't search up to MAX_PROC if uid != 0 */
|
nuclear@57
|
151 pid = -1;
|
nuclear@57
|
152 for(i=1; i<MAX_PROC; i++) {
|
nuclear@57
|
153 if(proc[i].id == 0) {
|
nuclear@57
|
154 pid = i;
|
nuclear@57
|
155 break;
|
nuclear@57
|
156 }
|
nuclear@57
|
157 }
|
nuclear@57
|
158
|
nuclear@57
|
159 if(pid == -1) {
|
nuclear@57
|
160 /* process table full */
|
nuclear@57
|
161 return -EAGAIN;
|
nuclear@57
|
162 }
|
nuclear@57
|
163
|
nuclear@57
|
164
|
nuclear@57
|
165 p = proc + pid;
|
nuclear@57
|
166 parent = get_current_proc();
|
nuclear@57
|
167
|
nuclear@90
|
168 /* copy file table */
|
nuclear@90
|
169 memcpy(p->files, parent->files, sizeof p->files);
|
nuclear@90
|
170
|
nuclear@57
|
171 /* allocate a kernel stack for the new process */
|
nuclear@57
|
172 if((p->kern_stack_pg = pgalloc(KERN_STACK_SIZE / PGSIZE, MEM_KERNEL)) == -1) {
|
nuclear@57
|
173 return -EAGAIN;
|
nuclear@57
|
174 }
|
nuclear@57
|
175 p->ctx.stack_ptr = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE;
|
nuclear@57
|
176 /* we need to copy the current interrupt frame to the new kernel stack so
|
nuclear@57
|
177 * that the new process will return to the same point as the parent, just
|
nuclear@57
|
178 * after the fork syscall.
|
nuclear@57
|
179 */
|
nuclear@57
|
180 p->ctx.stack_ptr -= sizeof(struct intr_frame);
|
nuclear@57
|
181 memcpy((void*)p->ctx.stack_ptr, get_intr_frame(), sizeof(struct intr_frame));
|
nuclear@57
|
182 /* child's return from fork returns 0 */
|
nuclear@57
|
183 ((struct intr_frame*)p->ctx.stack_ptr)->regs.eax = 0;
|
nuclear@57
|
184
|
nuclear@59
|
185 /* we also need the address of just_forked in the stack, so that switch_stacks
|
nuclear@59
|
186 * called from context_switch, will return to just_forked when we first switch
|
nuclear@59
|
187 * to a newly forked process. just_forked then just calls intr_ret to return to
|
nuclear@59
|
188 * userspace with the already constructed interrupt frame (see above).
|
nuclear@59
|
189 */
|
nuclear@57
|
190 p->ctx.stack_ptr -= 4;
|
nuclear@57
|
191 *(uint32_t*)p->ctx.stack_ptr = (uint32_t)just_forked;
|
nuclear@57
|
192
|
nuclear@57
|
193 /* initialize the rest of the process structure */
|
nuclear@57
|
194 p->id = pid;
|
nuclear@57
|
195 p->parent = parent->id;
|
nuclear@72
|
196 p->child_list = 0;
|
nuclear@57
|
197 p->next = p->prev = 0;
|
nuclear@57
|
198
|
nuclear@72
|
199 /* add to the child list */
|
nuclear@72
|
200 p->sib_next = parent->child_list;
|
nuclear@72
|
201 parent->child_list = p;
|
nuclear@72
|
202
|
nuclear@57
|
203 /* will be copied on write */
|
nuclear@57
|
204 p->user_stack_pg = parent->user_stack_pg;
|
nuclear@57
|
205
|
nuclear@69
|
206 /* clone the parent's virtual memory */
|
nuclear@69
|
207 clone_vm(p, parent, CLONE_COW);
|
nuclear@57
|
208
|
nuclear@57
|
209 /* done, now let's add it to the scheduler runqueue */
|
nuclear@57
|
210 add_proc(p->id);
|
nuclear@57
|
211
|
nuclear@57
|
212 return pid;
|
nuclear@57
|
213 }
|
nuclear@47
|
214
|
nuclear@72
|
215 int sys_exit(int status)
|
nuclear@72
|
216 {
|
nuclear@72
|
217 struct process *p, *child;
|
nuclear@72
|
218
|
nuclear@72
|
219 p = get_current_proc();
|
nuclear@72
|
220
|
nuclear@75
|
221 printf("process %d exit(%d)\n", p->id, status);
|
nuclear@75
|
222
|
nuclear@72
|
223 /* TODO deliver SIGCHLD to the parent */
|
nuclear@72
|
224
|
nuclear@72
|
225 /* find any child processes and make init adopt them */
|
nuclear@72
|
226 child = p->child_list;
|
nuclear@72
|
227 while(child) {
|
nuclear@72
|
228 child->parent = 1;
|
nuclear@72
|
229 child = child->sib_next;
|
nuclear@72
|
230 }
|
nuclear@72
|
231
|
nuclear@72
|
232 cleanup_vm(p);
|
nuclear@72
|
233
|
nuclear@72
|
234 /* remove it from the runqueue */
|
nuclear@72
|
235 remove_proc(p->id);
|
nuclear@72
|
236
|
nuclear@72
|
237 /* make it a zombie until its parent reaps it */
|
nuclear@72
|
238 p->state = STATE_ZOMBIE;
|
nuclear@72
|
239 p->exit_status = (status & _WSTATUS_MASK) | (_WREASON_EXITED << _WREASON_SHIFT);
|
nuclear@72
|
240
|
nuclear@72
|
241 /* wakeup any processes waiting for it
|
nuclear@72
|
242 * we're waking up the parent's address, because waitpid waits
|
nuclear@72
|
243 * on it's own process struct, not knowing which child will die
|
nuclear@72
|
244 * first.
|
nuclear@72
|
245 */
|
nuclear@72
|
246 wakeup(get_process(p->parent));
|
nuclear@72
|
247 return 0;
|
nuclear@72
|
248 }
|
nuclear@72
|
249
|
nuclear@72
|
250 int sys_waitpid(int pid, int *status, int opt)
|
nuclear@72
|
251 {
|
nuclear@72
|
252 struct process *p, *child;
|
nuclear@72
|
253
|
nuclear@72
|
254 p = get_current_proc();
|
nuclear@72
|
255
|
nuclear@72
|
256 restart:
|
nuclear@72
|
257 if(pid <= 0) {
|
nuclear@72
|
258 /* search for zombie children */
|
nuclear@72
|
259 child = p->child_list;
|
nuclear@72
|
260 while(child) {
|
nuclear@72
|
261 if(child->state == STATE_ZOMBIE) {
|
nuclear@72
|
262 break;
|
nuclear@72
|
263 }
|
nuclear@72
|
264 child = child->sib_next;
|
nuclear@72
|
265 }
|
nuclear@72
|
266 } else {
|
nuclear@72
|
267 if(!(child = get_process(pid)) || child->parent != p->id) {
|
nuclear@72
|
268 return -ECHILD;
|
nuclear@72
|
269 }
|
nuclear@72
|
270 if(child->state != STATE_ZOMBIE) {
|
nuclear@72
|
271 child = 0;
|
nuclear@72
|
272 }
|
nuclear@72
|
273 }
|
nuclear@72
|
274
|
nuclear@72
|
275 /* found ? */
|
nuclear@72
|
276 if(child) {
|
nuclear@72
|
277 int res;
|
nuclear@72
|
278 struct process *prev, dummy;
|
nuclear@72
|
279
|
nuclear@72
|
280 if(status) {
|
nuclear@72
|
281 *status = child->exit_status;
|
nuclear@72
|
282 }
|
nuclear@72
|
283 res = child->id;
|
nuclear@72
|
284
|
nuclear@72
|
285 /* remove it from our children list */
|
nuclear@72
|
286 dummy.sib_next = p->child_list;
|
nuclear@72
|
287 prev = &dummy;
|
nuclear@72
|
288 while(prev->next) {
|
nuclear@72
|
289 if(prev->next == child) {
|
nuclear@72
|
290 prev->next = child->next;
|
nuclear@72
|
291 break;
|
nuclear@72
|
292 }
|
nuclear@72
|
293 }
|
nuclear@72
|
294 p->child_list = dummy.next;
|
nuclear@72
|
295
|
nuclear@72
|
296 /* invalidate the id */
|
nuclear@72
|
297 child->id = 0;
|
nuclear@72
|
298 return res;
|
nuclear@72
|
299 }
|
nuclear@72
|
300
|
nuclear@72
|
301 /* not found, wait or sod off */
|
nuclear@72
|
302 if(!(opt & WNOHANG)) {
|
nuclear@72
|
303 /* wait on our own process struct because
|
nuclear@72
|
304 * we have no way of knowing which child will
|
nuclear@72
|
305 * die first.
|
nuclear@72
|
306 * exit will wakeup the parent structure...
|
nuclear@72
|
307 */
|
nuclear@72
|
308 wait(p);
|
nuclear@72
|
309 /* done waiting, restart waitpid */
|
nuclear@72
|
310 goto restart;
|
nuclear@72
|
311 }
|
nuclear@72
|
312
|
nuclear@72
|
313 return 0; /* he's not dead jim */
|
nuclear@72
|
314 }
|
nuclear@72
|
315
|
nuclear@47
|
316 void context_switch(int pid)
|
nuclear@42
|
317 {
|
nuclear@56
|
318 static struct process *prev, *new;
|
nuclear@49
|
319
|
nuclear@55
|
320 assert(get_intr_state() == 0);
|
nuclear@56
|
321 assert(pid > 0);
|
nuclear@56
|
322 assert(last_pid > 0);
|
nuclear@55
|
323
|
nuclear@56
|
324 prev = proc + last_pid;
|
nuclear@54
|
325 new = proc + pid;
|
nuclear@52
|
326
|
nuclear@56
|
327 if(last_pid != pid) {
|
nuclear@57
|
328 set_current_pid(new->id);
|
nuclear@47
|
329
|
nuclear@56
|
330 /* switch to the new process' address space */
|
nuclear@56
|
331 set_pgdir_addr(new->ctx.pgtbl_paddr);
|
nuclear@47
|
332
|
nuclear@56
|
333 /* make sure we'll return to the correct kernel stack next time
|
nuclear@56
|
334 * we enter from userspace
|
nuclear@56
|
335 */
|
nuclear@56
|
336 tss->esp0 = PAGE_TO_ADDR(new->kern_stack_pg) + KERN_STACK_SIZE;
|
nuclear@57
|
337
|
nuclear@57
|
338 /* push all registers onto the stack before switching stacks */
|
nuclear@57
|
339 push_regs();
|
nuclear@57
|
340
|
nuclear@57
|
341 /* XXX: when switching to newly forked processes this switch_stack call
|
nuclear@57
|
342 * WILL NOT RETURN HERE. It will return to just_forked instead. So the
|
nuclear@57
|
343 * rest of this function will not run.
|
nuclear@57
|
344 */
|
nuclear@57
|
345 switch_stack(new->ctx.stack_ptr, &prev->ctx.stack_ptr);
|
nuclear@57
|
346
|
nuclear@57
|
347 /* restore registers from the new stack */
|
nuclear@57
|
348 pop_regs();
|
nuclear@57
|
349 } else {
|
nuclear@57
|
350 set_current_pid(new->id);
|
nuclear@56
|
351 }
|
nuclear@56
|
352 }
|
nuclear@56
|
353
|
nuclear@56
|
354
|
nuclear@56
|
355 void set_current_pid(int pid)
|
nuclear@56
|
356 {
|
nuclear@56
|
357 cur_pid = pid;
|
nuclear@56
|
358 if(pid > 0) {
|
nuclear@56
|
359 last_pid = pid;
|
nuclear@56
|
360 }
|
nuclear@47
|
361 }
|
nuclear@51
|
362
|
nuclear@51
|
363 int get_current_pid(void)
|
nuclear@51
|
364 {
|
nuclear@51
|
365 return cur_pid;
|
nuclear@51
|
366 }
|
nuclear@51
|
367
|
nuclear@51
|
368 struct process *get_current_proc(void)
|
nuclear@51
|
369 {
|
nuclear@56
|
370 return cur_pid > 0 ? &proc[cur_pid] : 0;
|
nuclear@51
|
371 }
|
nuclear@51
|
372
|
nuclear@51
|
373 struct process *get_process(int pid)
|
nuclear@51
|
374 {
|
nuclear@72
|
375 struct process *p = proc + pid;
|
nuclear@72
|
376 if(p->id != pid) {
|
nuclear@72
|
377 printf("get_process called with invalid pid: %d\n", pid);
|
nuclear@72
|
378 return 0;
|
nuclear@72
|
379 }
|
nuclear@72
|
380 return p;
|
nuclear@51
|
381 }
|
nuclear@72
|
382
|
nuclear@72
|
383 int sys_getpid(void)
|
nuclear@72
|
384 {
|
nuclear@72
|
385 return cur_pid;
|
nuclear@72
|
386 }
|
nuclear@72
|
387
|
nuclear@72
|
388 int sys_getppid(void)
|
nuclear@72
|
389 {
|
nuclear@72
|
390 struct process *p = get_current_proc();
|
nuclear@72
|
391
|
nuclear@72
|
392 if(!p) {
|
nuclear@72
|
393 return 0;
|
nuclear@72
|
394 }
|
nuclear@72
|
395 return p->parent;
|
nuclear@72
|
396 }
|