kern

annotate src/proc.c @ 95:ec62cbe00b55

whatever
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 11 Dec 2011 21:15:35 +0200
parents 8b21fe04ba2c
children 07fe6a614185
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 }