kern
diff src/proc.c @ 54:4eaecb14fe31
bringing the task switching thing into shape with proper per-process kernel stacks and shit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sun, 14 Aug 2011 16:57:23 +0300 |
parents | 23abbeea4d5f |
children | 88a6c4e192f9 |
line diff
1.1 --- a/src/proc.c Mon Aug 08 09:53:10 2011 +0300 1.2 +++ b/src/proc.c Sun Aug 14 16:57:23 2011 +0300 1.3 @@ -9,9 +9,13 @@ 1.4 #include "panic.h" 1.5 #include "syscall.h" 1.6 #include "sched.h" 1.7 +#include "tss.h" 1.8 1.9 #define FLAGS_INTR_BIT 9 1.10 1.11 +static void start_first_proc(void); 1.12 + 1.13 + 1.14 /* defined in test_proc.S */ 1.15 void test_proc(void); 1.16 void test_proc_end(void); 1.17 @@ -19,90 +23,125 @@ 1.18 static struct process proc[MAX_PROC]; 1.19 static int cur_pid; 1.20 1.21 +static struct task_state *tss; 1.22 + 1.23 + 1.24 void init_proc(void) 1.25 { 1.26 - int proc_size_pg, img_start_pg, stack_pg; 1.27 - void *img_start; 1.28 - cur_pid = 0; 1.29 + int tss_page; 1.30 1.31 + /* allocate a page for the task state segment, to make sure 1.32 + * it doesn't cross page boundaries 1.33 + */ 1.34 + if((tss_page = pgalloc(1, MEM_KERNEL)) == -1) { 1.35 + panic("failed to allocate memory for the task state segment\n"); 1.36 + } 1.37 + tss = (struct tss*)PAGE_TO_ADDR(tss_page); 1.38 + 1.39 + /* the kernel stack segment never changes so we might as well set it now 1.40 + * the only other thing that we use in the tss is the kernel stack pointer 1.41 + * which is different for each process, and thus managed by context_switch 1.42 + */ 1.43 + memset(tss, 0, sizeof *tss); 1.44 + tss->ss0 = selector(SEGM_KDATA, 0); 1.45 + 1.46 + set_tss((uint32_t)virt_to_phys(tss)); 1.47 + 1.48 + /* initialize system call handler (see syscall.c) */ 1.49 init_syscall(); 1.50 1.51 + start_first_proc(); /* XXX never returns */ 1.52 +} 1.53 + 1.54 + 1.55 +static void start_first_proc(void) 1.56 +{ 1.57 + struct process *p; 1.58 + int proc_size_pg, img_start_pg, stack_pg; 1.59 + uint32_t img_start_addr, ustack_addr; 1.60 + struct intr_frame ifrm; 1.61 + 1.62 /* prepare the first process */ 1.63 - proc[1].id = 1; 1.64 - proc[1].parent = 0; 1.65 + p = proc + 1; 1.66 + p->id = 1; 1.67 + p->parent = 0; /* no parent for init */ 1.68 1.69 /* allocate a chunk of memory for the process image 1.70 * and copy the code of test_proc there. 1.71 - * (should be mapped at a fixed address) 1.72 */ 1.73 proc_size_pg = (test_proc_end - test_proc) / PGSIZE + 1; 1.74 if((img_start_pg = pgalloc(proc_size_pg, MEM_USER)) == -1) { 1.75 panic("failed to allocate space for the init process image\n"); 1.76 } 1.77 - img_start = (void*)PAGE_TO_ADDR(img_start_pg); 1.78 - memcpy(img_start, test_proc, proc_size_pg * PGSIZE); 1.79 - 1.80 - printf("copied init process at: %x\n", (unsigned int)img_start); 1.81 - 1.82 - /* instruction pointer at the beginning of the process image */ 1.83 - proc[1].ctx.instr_ptr = (uint32_t)img_start; 1.84 + img_start_addr = PAGE_TO_ADDR(img_start_pg); 1.85 + memcpy((void*)img_start_addr, test_proc, proc_size_pg * PGSIZE); 1.86 + printf("copied init process at: %x\n", img_start_addr); 1.87 1.88 /* allocate the first page of the process stack */ 1.89 stack_pg = ADDR_TO_PAGE(KMEM_START) - 1; 1.90 if(pgalloc_vrange(stack_pg, 1) == -1) { 1.91 panic("failed to allocate user stack page\n"); 1.92 } 1.93 - proc[1].ctx.stack_ptr = PAGE_TO_ADDR(stack_pg) + PGSIZE; 1.94 - proc[1].stack_start_pg = stack_pg; 1.95 + p->user_stack_pg = stack_pg; 1.96 1.97 - /* create the virtual address space for this process */ 1.98 - /*proc[1].ctx.pgtbl_paddr = clone_vm();*/ 1.99 + /* allocate a kernel stack for this process */ 1.100 + if((p->kern_stack_pg = pgalloc(KERN_STACK_SIZE / PGSIZE, MEM_KERNEL)) == -1) { 1.101 + panic("failed to allocate kernel stack for the init process\n"); 1.102 + } 1.103 + /* when switching from user space to kernel space, the ss0:esp0 from TSS 1.104 + * will be used to switch to the per-process kernel stack, so we need to 1.105 + * set it correctly before switching to user space. 1.106 + * tss->ss0 is already set in init_proc above. 1.107 + */ 1.108 + tss->esp0 = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE; 1.109 1.110 - /* we don't need the image and the stack in this address space */ 1.111 - /*unmap_page_range(img_start_pg, proc_size_pg); 1.112 - pgfree(img_start_pg, proc_size_pg); 1.113 1.114 - unmap_page(stack_pg); 1.115 - pgfree(stack_pg, 1);*/ 1.116 + /* now we need to fill in the fake interrupt stack frame */ 1.117 + memset(&ifrm, 0, sizeof ifrm); 1.118 + /* after the priviledge switch, this ss:esp will be used in userspace */ 1.119 + ifrm.esp = PAGE_TO_ADDR(stack_pg) + PGSIZE; 1.120 + ifrm.ss = selector(SEGM_UDATA, 3); 1.121 + /* instruction pointer at the beginning of the process image */ 1.122 + ifrm.regs.eip = img_start_addr; 1.123 + ifrm.cs = selector(SEGM_UCODE, 3); 1.124 + /* make sure the user will run with interrupts enabled */ 1.125 + ifrm.eflags = FLAGS_INTR_BIT; 1.126 + /* user data selectors should all be the same */ 1.127 + ifrm.ds = ifrm.es = ifrm.fs = ifrm.gs = ifrm.ss; 1.128 1.129 /* add it to the scheduler queues */ 1.130 - add_proc(1, STATE_RUNNABLE); 1.131 + add_proc(p->id, STATE_RUNNABLE); 1.132 1.133 - /* switch to the initial process, this never returns */ 1.134 - context_switch(1); 1.135 + /* execute a fake return from interrupt with the fake stack frame */ 1.136 + intr_ret(ifrm); 1.137 } 1.138 1.139 1.140 void context_switch(int pid) 1.141 { 1.142 - struct context *ctx; 1.143 - struct intr_frame ifrm; 1.144 - struct intr_frame *cur_frame = get_intr_frame(); 1.145 - 1.146 - assert(0); 1.147 + struct process *prev, *new; 1.148 1.149 if(cur_pid == pid) { 1.150 - assert(cur_frame); 1.151 - intr_ret(*cur_frame); 1.152 + return; /* nothing to be done */ 1.153 } 1.154 + prev = proc + cur_pid; 1.155 + new = proc + pid; 1.156 1.157 - ctx = &proc[pid].ctx; 1.158 - cur_pid = pid; 1.159 + /* push all registers onto the stack before switching stacks */ 1.160 + push_regs(); 1.161 1.162 - ifrm.inum = ifrm.err = 0; 1.163 + prev->ctx.stack_ptr = switch_stack(new->ctx.stack_ptr); 1.164 1.165 - ifrm.regs = ctx->regs; 1.166 - ifrm.eflags = ctx->flags | (1 << FLAGS_INTR_BIT); 1.167 + /* restore registers from the new stack */ 1.168 + pop_regs(); 1.169 1.170 - ifrm.eip = ctx->instr_ptr; 1.171 - ifrm.cs = selector(SEGM_KCODE, 0); /* XXX change this when we setup the TSS */ 1.172 - /*ifrm.regs.esp = ctx->stack_ptr;*/ /* ... until then... */ 1.173 - ifrm.esp = ctx->stack_ptr; /* this will only be used when we switch to userspace */ 1.174 - ifrm.ss = selector(SEGM_KDATA, 0); 1.175 + /* switch to the new process' address space */ 1.176 + set_pgdir_addr(new->ctx.pgtbl_paddr); 1.177 1.178 - /* switch to the vm of the process */ 1.179 - set_pgdir_addr(ctx->pgtbl_paddr); 1.180 - intr_ret(ifrm); 1.181 + /* make sure we'll return to the correct kernel stack next time 1.182 + * we enter from userspace 1.183 + */ 1.184 + tss->esp0 = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE; 1.185 } 1.186 1.187 int get_current_pid(void)