kern

annotate src/proc.c @ 56:0be4615594df

finally, runqueues, blocking, waking up, idle loop etc, all seem to work fine on a single user process... Next up: try forking another one :)
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 15 Aug 2011 06:17:58 +0300
parents 88a6c4e192f9
children 437360696883
rev   line source
nuclear@52 1 #include <stdio.h>
nuclear@47 2 #include <string.h>
nuclear@52 3 #include <assert.h>
nuclear@55 4 #include "config.h"
nuclear@42 5 #include "proc.h"
nuclear@42 6 #include "tss.h"
nuclear@45 7 #include "vm.h"
nuclear@47 8 #include "segm.h"
nuclear@47 9 #include "intr.h"
nuclear@47 10 #include "panic.h"
nuclear@51 11 #include "syscall.h"
nuclear@51 12 #include "sched.h"
nuclear@54 13 #include "tss.h"
nuclear@47 14
nuclear@55 15 #define FLAGS_INTR_BIT (1 << 9)
nuclear@47 16
nuclear@54 17 static void start_first_proc(void);
nuclear@54 18
nuclear@55 19 /* defined in proc-asm.S */
nuclear@55 20 uint32_t switch_stack(uint32_t new_stack);
nuclear@54 21
nuclear@47 22 /* defined in test_proc.S */
nuclear@47 23 void test_proc(void);
nuclear@47 24 void test_proc_end(void);
nuclear@42 25
nuclear@42 26 static struct process proc[MAX_PROC];
nuclear@56 27
nuclear@56 28 /* cur_pid: pid of the currently executing process.
nuclear@56 29 * when we're in the idle process cur_pid will be 0.
nuclear@56 30 * last_pid: pid of the last real process that was running, this should
nuclear@56 31 * never become 0. Essentially this defines the active kernel stack.
nuclear@56 32 */
nuclear@56 33 static int cur_pid, last_pid;
nuclear@42 34
nuclear@54 35 static struct task_state *tss;
nuclear@54 36
nuclear@54 37
nuclear@42 38 void init_proc(void)
nuclear@42 39 {
nuclear@54 40 int tss_page;
nuclear@51 41
nuclear@54 42 /* allocate a page for the task state segment, to make sure
nuclear@54 43 * it doesn't cross page boundaries
nuclear@54 44 */
nuclear@54 45 if((tss_page = pgalloc(1, MEM_KERNEL)) == -1) {
nuclear@54 46 panic("failed to allocate memory for the task state segment\n");
nuclear@54 47 }
nuclear@55 48 tss = (struct task_state*)PAGE_TO_ADDR(tss_page);
nuclear@54 49
nuclear@54 50 /* the kernel stack segment never changes so we might as well set it now
nuclear@54 51 * the only other thing that we use in the tss is the kernel stack pointer
nuclear@54 52 * which is different for each process, and thus managed by context_switch
nuclear@54 53 */
nuclear@54 54 memset(tss, 0, sizeof *tss);
nuclear@54 55 tss->ss0 = selector(SEGM_KDATA, 0);
nuclear@54 56
nuclear@55 57 set_tss((uint32_t)tss);
nuclear@54 58
nuclear@54 59 /* initialize system call handler (see syscall.c) */
nuclear@51 60 init_syscall();
nuclear@42 61
nuclear@54 62 start_first_proc(); /* XXX never returns */
nuclear@54 63 }
nuclear@54 64
nuclear@54 65
nuclear@54 66 static void start_first_proc(void)
nuclear@54 67 {
nuclear@54 68 struct process *p;
nuclear@54 69 int proc_size_pg, img_start_pg, stack_pg;
nuclear@55 70 uint32_t img_start_addr;
nuclear@54 71 struct intr_frame ifrm;
nuclear@54 72
nuclear@42 73 /* prepare the first process */
nuclear@54 74 p = proc + 1;
nuclear@54 75 p->id = 1;
nuclear@54 76 p->parent = 0; /* no parent for init */
nuclear@42 77
nuclear@55 78 p->ticks_left = TIMESLICE_TICKS;
nuclear@55 79 p->next = p->prev = 0;
nuclear@55 80
nuclear@55 81 /* the first process may keep this existing page table */
nuclear@55 82 p->ctx.pgtbl_paddr = get_pgdir_addr();
nuclear@55 83
nuclear@42 84 /* allocate a chunk of memory for the process image
nuclear@42 85 * and copy the code of test_proc there.
nuclear@42 86 */
nuclear@51 87 proc_size_pg = (test_proc_end - test_proc) / PGSIZE + 1;
nuclear@45 88 if((img_start_pg = pgalloc(proc_size_pg, MEM_USER)) == -1) {
nuclear@45 89 panic("failed to allocate space for the init process image\n");
nuclear@45 90 }
nuclear@54 91 img_start_addr = PAGE_TO_ADDR(img_start_pg);
nuclear@54 92 memcpy((void*)img_start_addr, test_proc, proc_size_pg * PGSIZE);
nuclear@54 93 printf("copied init process at: %x\n", img_start_addr);
nuclear@47 94
nuclear@47 95 /* allocate the first page of the process stack */
nuclear@47 96 stack_pg = ADDR_TO_PAGE(KMEM_START) - 1;
nuclear@47 97 if(pgalloc_vrange(stack_pg, 1) == -1) {
nuclear@47 98 panic("failed to allocate user stack page\n");
nuclear@47 99 }
nuclear@54 100 p->user_stack_pg = stack_pg;
nuclear@52 101
nuclear@54 102 /* allocate a kernel stack for this process */
nuclear@54 103 if((p->kern_stack_pg = pgalloc(KERN_STACK_SIZE / PGSIZE, MEM_KERNEL)) == -1) {
nuclear@54 104 panic("failed to allocate kernel stack for the init process\n");
nuclear@54 105 }
nuclear@54 106 /* when switching from user space to kernel space, the ss0:esp0 from TSS
nuclear@54 107 * will be used to switch to the per-process kernel stack, so we need to
nuclear@54 108 * set it correctly before switching to user space.
nuclear@54 109 * tss->ss0 is already set in init_proc above.
nuclear@54 110 */
nuclear@54 111 tss->esp0 = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE;
nuclear@45 112
nuclear@45 113
nuclear@54 114 /* now we need to fill in the fake interrupt stack frame */
nuclear@54 115 memset(&ifrm, 0, sizeof ifrm);
nuclear@54 116 /* after the priviledge switch, this ss:esp will be used in userspace */
nuclear@54 117 ifrm.esp = PAGE_TO_ADDR(stack_pg) + PGSIZE;
nuclear@54 118 ifrm.ss = selector(SEGM_UDATA, 3);
nuclear@54 119 /* instruction pointer at the beginning of the process image */
nuclear@55 120 ifrm.eip = img_start_addr;
nuclear@54 121 ifrm.cs = selector(SEGM_UCODE, 3);
nuclear@54 122 /* make sure the user will run with interrupts enabled */
nuclear@54 123 ifrm.eflags = FLAGS_INTR_BIT;
nuclear@54 124 /* user data selectors should all be the same */
nuclear@54 125 ifrm.ds = ifrm.es = ifrm.fs = ifrm.gs = ifrm.ss;
nuclear@42 126
nuclear@51 127 /* add it to the scheduler queues */
nuclear@55 128 add_proc(p->id);
nuclear@55 129
nuclear@56 130 /* make it current */
nuclear@56 131 set_current_pid(p->id);
nuclear@42 132
nuclear@54 133 /* execute a fake return from interrupt with the fake stack frame */
nuclear@54 134 intr_ret(ifrm);
nuclear@42 135 }
nuclear@42 136
nuclear@47 137
nuclear@47 138 void context_switch(int pid)
nuclear@42 139 {
nuclear@56 140 static struct process *prev, *new;
nuclear@49 141
nuclear@55 142 assert(get_intr_state() == 0);
nuclear@56 143 assert(pid > 0);
nuclear@56 144 assert(last_pid > 0);
nuclear@55 145
nuclear@56 146 prev = proc + last_pid;
nuclear@54 147 new = proc + pid;
nuclear@52 148
nuclear@56 149 if(last_pid != pid) {
nuclear@56 150 /* push all registers onto the stack before switching stacks */
nuclear@56 151 push_regs();
nuclear@47 152
nuclear@56 153 prev->ctx.stack_ptr = switch_stack(new->ctx.stack_ptr);
nuclear@47 154
nuclear@56 155 /* restore registers from the new stack */
nuclear@56 156 pop_regs();
nuclear@47 157
nuclear@56 158 /* switch to the new process' address space */
nuclear@56 159 set_pgdir_addr(new->ctx.pgtbl_paddr);
nuclear@47 160
nuclear@56 161 /* make sure we'll return to the correct kernel stack next time
nuclear@56 162 * we enter from userspace
nuclear@56 163 */
nuclear@56 164 tss->esp0 = PAGE_TO_ADDR(new->kern_stack_pg) + KERN_STACK_SIZE;
nuclear@56 165 }
nuclear@56 166
nuclear@56 167 set_current_pid(new->id);
nuclear@56 168 }
nuclear@56 169
nuclear@56 170
nuclear@56 171 void set_current_pid(int pid)
nuclear@56 172 {
nuclear@56 173 cur_pid = pid;
nuclear@56 174 if(pid > 0) {
nuclear@56 175 last_pid = pid;
nuclear@56 176 }
nuclear@47 177 }
nuclear@51 178
nuclear@51 179 int get_current_pid(void)
nuclear@51 180 {
nuclear@51 181 return cur_pid;
nuclear@51 182 }
nuclear@51 183
nuclear@51 184 struct process *get_current_proc(void)
nuclear@51 185 {
nuclear@56 186 return cur_pid > 0 ? &proc[cur_pid] : 0;
nuclear@51 187 }
nuclear@51 188
nuclear@51 189 struct process *get_process(int pid)
nuclear@51 190 {
nuclear@51 191 return &proc[pid];
nuclear@51 192 }