kern

view src/proc.c @ 80:4db99a52863e

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