kern

annotate 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
rev   line source
nuclear@52 1 #include <stdio.h>
nuclear@47 2 #include <string.h>
nuclear@52 3 #include <assert.h>
nuclear@42 4 #include "proc.h"
nuclear@42 5 #include "tss.h"
nuclear@45 6 #include "vm.h"
nuclear@47 7 #include "segm.h"
nuclear@47 8 #include "intr.h"
nuclear@47 9 #include "panic.h"
nuclear@51 10 #include "syscall.h"
nuclear@51 11 #include "sched.h"
nuclear@54 12 #include "tss.h"
nuclear@47 13
nuclear@52 14 #define FLAGS_INTR_BIT 9
nuclear@47 15
nuclear@54 16 static void start_first_proc(void);
nuclear@54 17
nuclear@54 18
nuclear@47 19 /* defined in test_proc.S */
nuclear@47 20 void test_proc(void);
nuclear@47 21 void test_proc_end(void);
nuclear@42 22
nuclear@42 23 static struct process proc[MAX_PROC];
nuclear@42 24 static int cur_pid;
nuclear@42 25
nuclear@54 26 static struct task_state *tss;
nuclear@54 27
nuclear@54 28
nuclear@42 29 void init_proc(void)
nuclear@42 30 {
nuclear@54 31 int tss_page;
nuclear@51 32
nuclear@54 33 /* allocate a page for the task state segment, to make sure
nuclear@54 34 * it doesn't cross page boundaries
nuclear@54 35 */
nuclear@54 36 if((tss_page = pgalloc(1, MEM_KERNEL)) == -1) {
nuclear@54 37 panic("failed to allocate memory for the task state segment\n");
nuclear@54 38 }
nuclear@54 39 tss = (struct tss*)PAGE_TO_ADDR(tss_page);
nuclear@54 40
nuclear@54 41 /* the kernel stack segment never changes so we might as well set it now
nuclear@54 42 * the only other thing that we use in the tss is the kernel stack pointer
nuclear@54 43 * which is different for each process, and thus managed by context_switch
nuclear@54 44 */
nuclear@54 45 memset(tss, 0, sizeof *tss);
nuclear@54 46 tss->ss0 = selector(SEGM_KDATA, 0);
nuclear@54 47
nuclear@54 48 set_tss((uint32_t)virt_to_phys(tss));
nuclear@54 49
nuclear@54 50 /* initialize system call handler (see syscall.c) */
nuclear@51 51 init_syscall();
nuclear@42 52
nuclear@54 53 start_first_proc(); /* XXX never returns */
nuclear@54 54 }
nuclear@54 55
nuclear@54 56
nuclear@54 57 static void start_first_proc(void)
nuclear@54 58 {
nuclear@54 59 struct process *p;
nuclear@54 60 int proc_size_pg, img_start_pg, stack_pg;
nuclear@54 61 uint32_t img_start_addr, ustack_addr;
nuclear@54 62 struct intr_frame ifrm;
nuclear@54 63
nuclear@42 64 /* prepare the first process */
nuclear@54 65 p = proc + 1;
nuclear@54 66 p->id = 1;
nuclear@54 67 p->parent = 0; /* no parent for init */
nuclear@42 68
nuclear@42 69 /* allocate a chunk of memory for the process image
nuclear@42 70 * and copy the code of test_proc there.
nuclear@42 71 */
nuclear@51 72 proc_size_pg = (test_proc_end - test_proc) / PGSIZE + 1;
nuclear@45 73 if((img_start_pg = pgalloc(proc_size_pg, MEM_USER)) == -1) {
nuclear@45 74 panic("failed to allocate space for the init process image\n");
nuclear@45 75 }
nuclear@54 76 img_start_addr = PAGE_TO_ADDR(img_start_pg);
nuclear@54 77 memcpy((void*)img_start_addr, test_proc, proc_size_pg * PGSIZE);
nuclear@54 78 printf("copied init process at: %x\n", img_start_addr);
nuclear@47 79
nuclear@47 80 /* allocate the first page of the process stack */
nuclear@47 81 stack_pg = ADDR_TO_PAGE(KMEM_START) - 1;
nuclear@47 82 if(pgalloc_vrange(stack_pg, 1) == -1) {
nuclear@47 83 panic("failed to allocate user stack page\n");
nuclear@47 84 }
nuclear@54 85 p->user_stack_pg = stack_pg;
nuclear@52 86
nuclear@54 87 /* allocate a kernel stack for this process */
nuclear@54 88 if((p->kern_stack_pg = pgalloc(KERN_STACK_SIZE / PGSIZE, MEM_KERNEL)) == -1) {
nuclear@54 89 panic("failed to allocate kernel stack for the init process\n");
nuclear@54 90 }
nuclear@54 91 /* when switching from user space to kernel space, the ss0:esp0 from TSS
nuclear@54 92 * will be used to switch to the per-process kernel stack, so we need to
nuclear@54 93 * set it correctly before switching to user space.
nuclear@54 94 * tss->ss0 is already set in init_proc above.
nuclear@54 95 */
nuclear@54 96 tss->esp0 = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE;
nuclear@45 97
nuclear@45 98
nuclear@54 99 /* now we need to fill in the fake interrupt stack frame */
nuclear@54 100 memset(&ifrm, 0, sizeof ifrm);
nuclear@54 101 /* after the priviledge switch, this ss:esp will be used in userspace */
nuclear@54 102 ifrm.esp = PAGE_TO_ADDR(stack_pg) + PGSIZE;
nuclear@54 103 ifrm.ss = selector(SEGM_UDATA, 3);
nuclear@54 104 /* instruction pointer at the beginning of the process image */
nuclear@54 105 ifrm.regs.eip = img_start_addr;
nuclear@54 106 ifrm.cs = selector(SEGM_UCODE, 3);
nuclear@54 107 /* make sure the user will run with interrupts enabled */
nuclear@54 108 ifrm.eflags = FLAGS_INTR_BIT;
nuclear@54 109 /* user data selectors should all be the same */
nuclear@54 110 ifrm.ds = ifrm.es = ifrm.fs = ifrm.gs = ifrm.ss;
nuclear@42 111
nuclear@51 112 /* add it to the scheduler queues */
nuclear@54 113 add_proc(p->id, STATE_RUNNABLE);
nuclear@42 114
nuclear@54 115 /* execute a fake return from interrupt with the fake stack frame */
nuclear@54 116 intr_ret(ifrm);
nuclear@42 117 }
nuclear@42 118
nuclear@47 119
nuclear@47 120 void context_switch(int pid)
nuclear@42 121 {
nuclear@54 122 struct process *prev, *new;
nuclear@49 123
nuclear@52 124 if(cur_pid == pid) {
nuclear@54 125 return; /* nothing to be done */
nuclear@52 126 }
nuclear@54 127 prev = proc + cur_pid;
nuclear@54 128 new = proc + pid;
nuclear@52 129
nuclear@54 130 /* push all registers onto the stack before switching stacks */
nuclear@54 131 push_regs();
nuclear@47 132
nuclear@54 133 prev->ctx.stack_ptr = switch_stack(new->ctx.stack_ptr);
nuclear@47 134
nuclear@54 135 /* restore registers from the new stack */
nuclear@54 136 pop_regs();
nuclear@47 137
nuclear@54 138 /* switch to the new process' address space */
nuclear@54 139 set_pgdir_addr(new->ctx.pgtbl_paddr);
nuclear@47 140
nuclear@54 141 /* make sure we'll return to the correct kernel stack next time
nuclear@54 142 * we enter from userspace
nuclear@54 143 */
nuclear@54 144 tss->esp0 = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE;
nuclear@47 145 }
nuclear@51 146
nuclear@51 147 int get_current_pid(void)
nuclear@51 148 {
nuclear@51 149 return cur_pid;
nuclear@51 150 }
nuclear@51 151
nuclear@51 152 struct process *get_current_proc(void)
nuclear@51 153 {
nuclear@51 154 return cur_pid ? &proc[cur_pid] : 0;
nuclear@51 155 }
nuclear@51 156
nuclear@51 157 struct process *get_process(int pid)
nuclear@51 158 {
nuclear@51 159 return &proc[pid];
nuclear@51 160 }