kern
view src/proc.c @ 98:921a264297a4
merged the filesystem stuff
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Thu, 17 Apr 2014 17:03:30 +0300 |
parents | 7ff2b4971216 |
children |
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->umask = 022;
82 p->ticks_left = TIMESLICE_TICKS;
83 p->next = p->prev = 0;
85 /* the first process may keep this existing page table */
86 p->ctx.pgtbl_paddr = get_pgdir_addr();
88 /* allocate a chunk of memory for the process image
89 * and copy the code of test_proc there.
90 */
91 proc_size_pg = (test_proc_end - test_proc) / PGSIZE + 1;
92 if((img_start_pg = pgalloc(proc_size_pg, MEM_USER)) == -1) {
93 panic("failed to allocate space for the init process image\n");
94 }
95 img_start_addr = PAGE_TO_ADDR(img_start_pg);
96 memcpy((void*)img_start_addr, test_proc, proc_size_pg * PGSIZE);
97 printf("copied init process at: %x\n", img_start_addr);
99 /* allocate the first page of the user stack */
100 stack_pg = ADDR_TO_PAGE(KMEM_START) - 1;
101 if(pgalloc_vrange(stack_pg, 1) == -1) {
102 panic("failed to allocate user stack page\n");
103 }
104 p->user_stack_pg = stack_pg;
106 /* allocate a kernel stack for this process */
107 if((p->kern_stack_pg = pgalloc(KERN_STACK_SIZE / PGSIZE, MEM_KERNEL)) == -1) {
108 panic("failed to allocate kernel stack for the init process\n");
109 }
110 /* when switching from user space to kernel space, the ss0:esp0 from TSS
111 * will be used to switch to the per-process kernel stack, so we need to
112 * set it correctly before switching to user space.
113 * tss->ss0 is already set in init_proc above.
114 */
115 tss->esp0 = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE;
118 /* now we need to fill in the fake interrupt stack frame */
119 memset(&ifrm, 0, sizeof ifrm);
120 /* after the priviledge switch, this ss:esp will be used in userspace */
121 ifrm.esp = PAGE_TO_ADDR(stack_pg) + PGSIZE;
122 ifrm.ss = selector(SEGM_UDATA, 3);
123 /* instruction pointer at the beginning of the process image */
124 ifrm.eip = img_start_addr;
125 ifrm.cs = selector(SEGM_UCODE, 3);
126 /* make sure the user will run with interrupts enabled */
127 ifrm.eflags = FLAGS_INTR_BIT;
128 /* user data selectors should all be the same */
129 ifrm.ds = ifrm.es = ifrm.fs = ifrm.gs = ifrm.ss;
131 /* add it to the scheduler queues */
132 add_proc(p->id);
134 /* make it current */
135 set_current_pid(p->id);
137 /* build the current vm map */
138 cons_vmmap(&p->vmmap);
140 /* execute a fake return from interrupt with the fake stack frame */
141 intr_ret(ifrm);
142 }
144 int sys_fork(void)
145 {
146 int i, pid;
147 struct process *p, *parent;
149 disable_intr();
151 /* find a free process slot */
152 /* TODO don't search up to MAX_PROC if uid != 0 */
153 pid = -1;
154 for(i=1; i<MAX_PROC; i++) {
155 if(proc[i].id == 0) {
156 pid = i;
157 break;
158 }
159 }
161 if(pid == -1) {
162 /* process table full */
163 return -EAGAIN;
164 }
167 p = proc + pid;
168 parent = get_current_proc();
170 /* copy file table */
171 memcpy(p->files, parent->files, sizeof p->files);
173 p->umask = parent->umask;
175 /* allocate a kernel stack for the new process */
176 if((p->kern_stack_pg = pgalloc(KERN_STACK_SIZE / PGSIZE, MEM_KERNEL)) == -1) {
177 return -EAGAIN;
178 }
179 p->ctx.stack_ptr = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE;
180 /* we need to copy the current interrupt frame to the new kernel stack so
181 * that the new process will return to the same point as the parent, just
182 * after the fork syscall.
183 */
184 p->ctx.stack_ptr -= sizeof(struct intr_frame);
185 memcpy((void*)p->ctx.stack_ptr, get_intr_frame(), sizeof(struct intr_frame));
186 /* child's return from fork returns 0 */
187 ((struct intr_frame*)p->ctx.stack_ptr)->regs.eax = 0;
189 /* we also need the address of just_forked in the stack, so that switch_stacks
190 * called from context_switch, will return to just_forked when we first switch
191 * to a newly forked process. just_forked then just calls intr_ret to return to
192 * userspace with the already constructed interrupt frame (see above).
193 */
194 p->ctx.stack_ptr -= 4;
195 *(uint32_t*)p->ctx.stack_ptr = (uint32_t)just_forked;
197 /* initialize the rest of the process structure */
198 p->id = pid;
199 p->parent = parent->id;
200 p->child_list = 0;
201 p->next = p->prev = 0;
203 /* add to the child list */
204 p->sib_next = parent->child_list;
205 parent->child_list = p;
207 /* will be copied on write */
208 p->user_stack_pg = parent->user_stack_pg;
210 /* clone the parent's virtual memory */
211 clone_vm(p, parent, CLONE_COW);
213 /* done, now let's add it to the scheduler runqueue */
214 add_proc(p->id);
216 return pid;
217 }
219 int sys_exit(int status)
220 {
221 struct process *p, *child;
223 p = get_current_proc();
225 printf("process %d exit(%d)\n", p->id, status);
227 /* TODO deliver SIGCHLD to the parent */
229 /* find any child processes and make init adopt them */
230 child = p->child_list;
231 while(child) {
232 child->parent = 1;
233 child = child->sib_next;
234 }
236 cleanup_vm(p);
238 /* remove it from the runqueue */
239 remove_proc(p->id);
241 /* make it a zombie until its parent reaps it */
242 p->state = STATE_ZOMBIE;
243 p->exit_status = (status & _WSTATUS_MASK) | (_WREASON_EXITED << _WREASON_SHIFT);
245 /* wakeup any processes waiting for it
246 * we're waking up the parent's address, because waitpid waits
247 * on it's own process struct, not knowing which child will die
248 * first.
249 */
250 wakeup(get_process(p->parent));
251 return 0;
252 }
254 int sys_waitpid(int pid, int *status, int opt)
255 {
256 struct process *p, *child;
258 p = get_current_proc();
260 restart:
261 if(pid <= 0) {
262 /* search for zombie children */
263 child = p->child_list;
264 while(child) {
265 if(child->state == STATE_ZOMBIE) {
266 break;
267 }
268 child = child->sib_next;
269 }
270 } else {
271 if(!(child = get_process(pid)) || child->parent != p->id) {
272 return -ECHILD;
273 }
274 if(child->state != STATE_ZOMBIE) {
275 child = 0;
276 }
277 }
279 /* found ? */
280 if(child) {
281 int res;
282 struct process *prev, dummy;
284 if(status) {
285 *status = child->exit_status;
286 }
287 res = child->id;
289 /* remove it from our children list */
290 dummy.sib_next = p->child_list;
291 prev = &dummy;
292 while(prev->next) {
293 if(prev->next == child) {
294 prev->next = child->next;
295 break;
296 }
297 }
298 p->child_list = dummy.next;
300 /* invalidate the id */
301 child->id = 0;
302 return res;
303 }
305 /* not found, wait or sod off */
306 if(!(opt & WNOHANG)) {
307 /* wait on our own process struct because
308 * we have no way of knowing which child will
309 * die first.
310 * exit will wakeup the parent structure...
311 */
312 wait(p);
313 /* done waiting, restart waitpid */
314 goto restart;
315 }
317 return 0; /* he's not dead jim */
318 }
320 void context_switch(int pid)
321 {
322 static struct process *prev, *new;
324 assert(get_intr_state() == 0);
325 assert(pid > 0);
326 assert(last_pid > 0);
328 prev = proc + last_pid;
329 new = proc + pid;
331 if(last_pid != pid) {
332 set_current_pid(new->id);
334 /* switch to the new process' address space */
335 set_pgdir_addr(new->ctx.pgtbl_paddr);
337 /* make sure we'll return to the correct kernel stack next time
338 * we enter from userspace
339 */
340 tss->esp0 = PAGE_TO_ADDR(new->kern_stack_pg) + KERN_STACK_SIZE;
342 /* push all registers onto the stack before switching stacks */
343 push_regs();
345 /* XXX: when switching to newly forked processes this switch_stack call
346 * WILL NOT RETURN HERE. It will return to just_forked instead. So the
347 * rest of this function will not run.
348 */
349 switch_stack(new->ctx.stack_ptr, &prev->ctx.stack_ptr);
351 /* restore registers from the new stack */
352 pop_regs();
353 } else {
354 set_current_pid(new->id);
355 }
356 }
359 void set_current_pid(int pid)
360 {
361 cur_pid = pid;
362 if(pid > 0) {
363 last_pid = pid;
364 }
365 }
367 int get_current_pid(void)
368 {
369 return cur_pid;
370 }
372 struct process *get_current_proc(void)
373 {
374 return cur_pid > 0 ? &proc[cur_pid] : 0;
375 }
377 struct process *get_process(int pid)
378 {
379 struct process *p = proc + pid;
380 if(p->id != pid) {
381 printf("get_process called with invalid pid: %d\n", pid);
382 return 0;
383 }
384 return p;
385 }
387 int sys_getpid(void)
388 {
389 return cur_pid;
390 }
392 int sys_getppid(void)
393 {
394 struct process *p = get_current_proc();
396 if(!p) {
397 return 0;
398 }
399 return p->parent;
400 }