kern
changeset 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 |
files | src/asmops.h src/config.h src/intr-asm.S src/intr.h src/proc-asm.S src/proc.c src/proc.h src/segm-asm.S src/segm.c src/segm.h src/syscall.c |
diffstat | 11 files changed, 156 insertions(+), 60 deletions(-) [+] |
line diff
1.1 --- a/src/asmops.h Mon Aug 08 09:53:10 2011 +0300 1.2 +++ b/src/asmops.h Sun Aug 14 16:57:23 2011 +0300 1.3 @@ -13,6 +13,9 @@ 1.4 #define disable_intr() asm volatile("cli") 1.5 #define halt_cpu() asm volatile("hlt") 1.6 1.7 +#define push_regs() asm volatile("pusha"); 1.8 +#define pop_regs() asm volatile("popa"); 1.9 + 1.10 #define inb(dest, port) asm volatile( \ 1.11 "inb %1, %0\n\t" \ 1.12 : "=a" ((unsigned char)(dest)) \ 1.13 @@ -42,4 +45,5 @@ 1.14 1.15 #define iodelay() outb(0, 0x80) 1.16 1.17 + 1.18 #endif /* ASMOPS_H_ */
2.1 --- a/src/config.h Mon Aug 08 09:53:10 2011 +0300 2.2 +++ b/src/config.h Sun Aug 14 16:57:23 2011 +0300 2.3 @@ -10,4 +10,7 @@ 2.4 /* allow automatic user stack growth by at most 1024 pages at a time (4mb) */ 2.5 #define USTACK_MAXGROW 1024 2.6 2.7 +/* per-process kernel stack size (2 pages) */ 2.8 +#define KERN_STACK_SIZE 8192 2.9 + 2.10 #endif /* _CONFIG_H_ */
3.1 --- a/src/intr-asm.S Mon Aug 08 09:53:10 2011 +0300 3.2 +++ b/src/intr-asm.S Sun Aug 14 16:57:23 2011 +0300 3.3 @@ -1,3 +1,6 @@ 3.4 +#define ASM 3.5 +#include "segm.h" 3.6 + 3.7 .data 3.8 .align 4 3.9 .short 0 3.10 @@ -68,10 +71,29 @@ 3.11 */ 3.12 .extern dispatch_intr 3.13 intr_entry_common: 3.14 + /* save the current data segment selectors */ 3.15 + pushl %gs 3.16 + pushl %fs 3.17 + pushl %es 3.18 + pushl %ds 3.19 + /* save general purpose registers */ 3.20 pusha 3.21 + /* if we entered from userspace ss and cs is set correctly, but 3.22 + * we must make sure all the other selectors are set to the 3.23 + * kernel data segment */ 3.24 + mov $SEGM_KDATA, %ds 3.25 + mov $SEGM_KDATA, %es 3.26 + mov $SEGM_KDATA, %fs 3.27 + mov $SEGM_KDATA, %gs 3.28 call dispatch_intr 3.29 intr_ret_local: 3.30 + /* restore general purpose registers */ 3.31 popa 3.32 + /* restore data segment selectors */ 3.33 + popl %ds 3.34 + popl %es 3.35 + popl %fs 3.36 + popl %gs 3.37 /* remove error code and intr num from stack */ 3.38 add $8, %esp 3.39 iret 3.40 @@ -93,5 +115,4 @@ 3.41 /* by including interrupts.h with ASM defined, the macros above 3.42 * are expanded to generate all required interrupt entry points 3.43 */ 3.44 -#define ASM 3.45 #include <interrupts.h>
4.1 --- a/src/intr.h Mon Aug 08 09:53:10 2011 +0300 4.2 +++ b/src/intr.h Sun Aug 14 16:57:23 2011 +0300 4.3 @@ -18,6 +18,8 @@ 4.4 struct intr_frame { 4.5 /* registers pushed by pusha in intr_entry_* */ 4.6 struct registers regs; 4.7 + /* data segment selectors */ 4.8 + uint32_t ds, es, fs, gs; 4.9 /* interrupt number and error code pushed in intr_entry_* */ 4.10 uint32_t inum, err; 4.11 /* pushed by CPU during interrupt entry */
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/src/proc-asm.S Sun Aug 14 16:57:23 2011 +0300 5.3 @@ -0,0 +1,9 @@ 5.4 + .text 5.5 + /* switch_stack(uint32_t new_stack) 5.6 + * switches to the new stack and returns the old stack pointer 5.7 + */ 5.8 + .globl switch_stack 5.9 +switch_stack: 5.10 + movl %esp, %eax 5.11 + movl 4(%esp), %esp 5.12 + ret
6.1 --- a/src/proc.c Mon Aug 08 09:53:10 2011 +0300 6.2 +++ b/src/proc.c Sun Aug 14 16:57:23 2011 +0300 6.3 @@ -9,9 +9,13 @@ 6.4 #include "panic.h" 6.5 #include "syscall.h" 6.6 #include "sched.h" 6.7 +#include "tss.h" 6.8 6.9 #define FLAGS_INTR_BIT 9 6.10 6.11 +static void start_first_proc(void); 6.12 + 6.13 + 6.14 /* defined in test_proc.S */ 6.15 void test_proc(void); 6.16 void test_proc_end(void); 6.17 @@ -19,90 +23,125 @@ 6.18 static struct process proc[MAX_PROC]; 6.19 static int cur_pid; 6.20 6.21 +static struct task_state *tss; 6.22 + 6.23 + 6.24 void init_proc(void) 6.25 { 6.26 - int proc_size_pg, img_start_pg, stack_pg; 6.27 - void *img_start; 6.28 - cur_pid = 0; 6.29 + int tss_page; 6.30 6.31 + /* allocate a page for the task state segment, to make sure 6.32 + * it doesn't cross page boundaries 6.33 + */ 6.34 + if((tss_page = pgalloc(1, MEM_KERNEL)) == -1) { 6.35 + panic("failed to allocate memory for the task state segment\n"); 6.36 + } 6.37 + tss = (struct tss*)PAGE_TO_ADDR(tss_page); 6.38 + 6.39 + /* the kernel stack segment never changes so we might as well set it now 6.40 + * the only other thing that we use in the tss is the kernel stack pointer 6.41 + * which is different for each process, and thus managed by context_switch 6.42 + */ 6.43 + memset(tss, 0, sizeof *tss); 6.44 + tss->ss0 = selector(SEGM_KDATA, 0); 6.45 + 6.46 + set_tss((uint32_t)virt_to_phys(tss)); 6.47 + 6.48 + /* initialize system call handler (see syscall.c) */ 6.49 init_syscall(); 6.50 6.51 + start_first_proc(); /* XXX never returns */ 6.52 +} 6.53 + 6.54 + 6.55 +static void start_first_proc(void) 6.56 +{ 6.57 + struct process *p; 6.58 + int proc_size_pg, img_start_pg, stack_pg; 6.59 + uint32_t img_start_addr, ustack_addr; 6.60 + struct intr_frame ifrm; 6.61 + 6.62 /* prepare the first process */ 6.63 - proc[1].id = 1; 6.64 - proc[1].parent = 0; 6.65 + p = proc + 1; 6.66 + p->id = 1; 6.67 + p->parent = 0; /* no parent for init */ 6.68 6.69 /* allocate a chunk of memory for the process image 6.70 * and copy the code of test_proc there. 6.71 - * (should be mapped at a fixed address) 6.72 */ 6.73 proc_size_pg = (test_proc_end - test_proc) / PGSIZE + 1; 6.74 if((img_start_pg = pgalloc(proc_size_pg, MEM_USER)) == -1) { 6.75 panic("failed to allocate space for the init process image\n"); 6.76 } 6.77 - img_start = (void*)PAGE_TO_ADDR(img_start_pg); 6.78 - memcpy(img_start, test_proc, proc_size_pg * PGSIZE); 6.79 - 6.80 - printf("copied init process at: %x\n", (unsigned int)img_start); 6.81 - 6.82 - /* instruction pointer at the beginning of the process image */ 6.83 - proc[1].ctx.instr_ptr = (uint32_t)img_start; 6.84 + img_start_addr = PAGE_TO_ADDR(img_start_pg); 6.85 + memcpy((void*)img_start_addr, test_proc, proc_size_pg * PGSIZE); 6.86 + printf("copied init process at: %x\n", img_start_addr); 6.87 6.88 /* allocate the first page of the process stack */ 6.89 stack_pg = ADDR_TO_PAGE(KMEM_START) - 1; 6.90 if(pgalloc_vrange(stack_pg, 1) == -1) { 6.91 panic("failed to allocate user stack page\n"); 6.92 } 6.93 - proc[1].ctx.stack_ptr = PAGE_TO_ADDR(stack_pg) + PGSIZE; 6.94 - proc[1].stack_start_pg = stack_pg; 6.95 + p->user_stack_pg = stack_pg; 6.96 6.97 - /* create the virtual address space for this process */ 6.98 - /*proc[1].ctx.pgtbl_paddr = clone_vm();*/ 6.99 + /* allocate a kernel stack for this process */ 6.100 + if((p->kern_stack_pg = pgalloc(KERN_STACK_SIZE / PGSIZE, MEM_KERNEL)) == -1) { 6.101 + panic("failed to allocate kernel stack for the init process\n"); 6.102 + } 6.103 + /* when switching from user space to kernel space, the ss0:esp0 from TSS 6.104 + * will be used to switch to the per-process kernel stack, so we need to 6.105 + * set it correctly before switching to user space. 6.106 + * tss->ss0 is already set in init_proc above. 6.107 + */ 6.108 + tss->esp0 = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE; 6.109 6.110 - /* we don't need the image and the stack in this address space */ 6.111 - /*unmap_page_range(img_start_pg, proc_size_pg); 6.112 - pgfree(img_start_pg, proc_size_pg); 6.113 6.114 - unmap_page(stack_pg); 6.115 - pgfree(stack_pg, 1);*/ 6.116 + /* now we need to fill in the fake interrupt stack frame */ 6.117 + memset(&ifrm, 0, sizeof ifrm); 6.118 + /* after the priviledge switch, this ss:esp will be used in userspace */ 6.119 + ifrm.esp = PAGE_TO_ADDR(stack_pg) + PGSIZE; 6.120 + ifrm.ss = selector(SEGM_UDATA, 3); 6.121 + /* instruction pointer at the beginning of the process image */ 6.122 + ifrm.regs.eip = img_start_addr; 6.123 + ifrm.cs = selector(SEGM_UCODE, 3); 6.124 + /* make sure the user will run with interrupts enabled */ 6.125 + ifrm.eflags = FLAGS_INTR_BIT; 6.126 + /* user data selectors should all be the same */ 6.127 + ifrm.ds = ifrm.es = ifrm.fs = ifrm.gs = ifrm.ss; 6.128 6.129 /* add it to the scheduler queues */ 6.130 - add_proc(1, STATE_RUNNABLE); 6.131 + add_proc(p->id, STATE_RUNNABLE); 6.132 6.133 - /* switch to the initial process, this never returns */ 6.134 - context_switch(1); 6.135 + /* execute a fake return from interrupt with the fake stack frame */ 6.136 + intr_ret(ifrm); 6.137 } 6.138 6.139 6.140 void context_switch(int pid) 6.141 { 6.142 - struct context *ctx; 6.143 - struct intr_frame ifrm; 6.144 - struct intr_frame *cur_frame = get_intr_frame(); 6.145 - 6.146 - assert(0); 6.147 + struct process *prev, *new; 6.148 6.149 if(cur_pid == pid) { 6.150 - assert(cur_frame); 6.151 - intr_ret(*cur_frame); 6.152 + return; /* nothing to be done */ 6.153 } 6.154 + prev = proc + cur_pid; 6.155 + new = proc + pid; 6.156 6.157 - ctx = &proc[pid].ctx; 6.158 - cur_pid = pid; 6.159 + /* push all registers onto the stack before switching stacks */ 6.160 + push_regs(); 6.161 6.162 - ifrm.inum = ifrm.err = 0; 6.163 + prev->ctx.stack_ptr = switch_stack(new->ctx.stack_ptr); 6.164 6.165 - ifrm.regs = ctx->regs; 6.166 - ifrm.eflags = ctx->flags | (1 << FLAGS_INTR_BIT); 6.167 + /* restore registers from the new stack */ 6.168 + pop_regs(); 6.169 6.170 - ifrm.eip = ctx->instr_ptr; 6.171 - ifrm.cs = selector(SEGM_KCODE, 0); /* XXX change this when we setup the TSS */ 6.172 - /*ifrm.regs.esp = ctx->stack_ptr;*/ /* ... until then... */ 6.173 - ifrm.esp = ctx->stack_ptr; /* this will only be used when we switch to userspace */ 6.174 - ifrm.ss = selector(SEGM_KDATA, 0); 6.175 + /* switch to the new process' address space */ 6.176 + set_pgdir_addr(new->ctx.pgtbl_paddr); 6.177 6.178 - /* switch to the vm of the process */ 6.179 - set_pgdir_addr(ctx->pgtbl_paddr); 6.180 - intr_ret(ifrm); 6.181 + /* make sure we'll return to the correct kernel stack next time 6.182 + * we enter from userspace 6.183 + */ 6.184 + tss->esp0 = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE; 6.185 } 6.186 6.187 int get_current_pid(void)
7.1 --- a/src/proc.h Mon Aug 08 09:53:10 2011 +0300 7.2 +++ b/src/proc.h Sun Aug 14 16:57:23 2011 +0300 7.3 @@ -7,10 +7,10 @@ 7.4 #define MAX_PROC 128 7.5 7.6 struct context { 7.7 - struct registers regs; /* saved general purpose registers */ 7.8 - uint32_t instr_ptr; /* saved eip */ 7.9 + /*struct registers regs;*/ /* saved general purpose registers */ 7.10 + /*uint32_t instr_ptr;*/ /* saved eip */ 7.11 uint32_t stack_ptr; /* saved esp */ 7.12 - uint32_t flags; /* saved eflags */ 7.13 + /*uint32_t flags;*/ /* saved eflags */ 7.14 uint32_t pgtbl_paddr; /* physical address of the page table */ 7.15 /* TODO add FPU state */ 7.16 }; 7.17 @@ -31,7 +31,9 @@ 7.18 /* extends of the process heap, increased by sbrk */ 7.19 7.20 /* first page of the user stack, extends up to KMEM_START */ 7.21 - int stack_start_pg; 7.22 + int user_stack_pg; 7.23 + /* first page of the kernel stack, (KERN_STACK_SIZE) */ 7.24 + int kern_stack_pg; 7.25 7.26 struct context ctx; 7.27
8.1 --- a/src/segm-asm.S Mon Aug 08 09:53:10 2011 +0300 8.2 +++ b/src/segm-asm.S Sun Aug 14 16:57:23 2011 +0300 8.3 @@ -37,3 +37,10 @@ 8.4 movw %ax, (lim) 8.5 lgdt (lim) 8.6 ret 8.7 + 8.8 +/* set_task_reg(uint16_t tss_selector) 8.9 + * loads the TSS selector in the task register */ 8.10 + .globl set_task_reg 8.11 +set_task_reg: 8.12 + ltr 4(%esp) 8.13 + ret
9.1 --- a/src/segm.c Mon Aug 08 09:53:10 2011 +0300 9.2 +++ b/src/segm.c Sun Aug 14 16:57:23 2011 +0300 9.3 @@ -21,8 +21,7 @@ 9.4 9.5 enum {TYPE_DATA, TYPE_CODE}; 9.6 9.7 -#define TSS_TYPE_BITS 0x900 9.8 -#define TSS_BUSY BIT_BUSY 9.9 +#define TSS_TYPE_BITS (BIT_ACCESSED | BIT_CODE) 9.10 9.11 static void segm_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type); 9.12 static void task_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, unsigned int busy); 9.13 @@ -30,6 +29,7 @@ 9.14 /* these functions are implemented in segm-asm.S */ 9.15 void setup_selectors(uint16_t code, uint16_t data); 9.16 void set_gdt(uint32_t addr, uint16_t limit); 9.17 +void set_task_reg(uint16_t tss_selector); 9.18 9.19 9.20 /* our global descriptor table */ 9.21 @@ -43,7 +43,6 @@ 9.22 segm_desc(gdt + SEGM_KDATA, 0, 0xffffffff, 0, TYPE_DATA); 9.23 segm_desc(gdt + SEGM_UCODE, 0, 0xffffffff, 3, TYPE_CODE); 9.24 segm_desc(gdt + SEGM_UDATA, 0, 0xffffffff, 3, TYPE_DATA); 9.25 - task_desc(gdt + SEGM_TASK, 0, 0xffffffff, 3, TSS_BUSY); 9.26 9.27 set_gdt((uint32_t)gdt, sizeof gdt - 1); 9.28 9.29 @@ -56,6 +55,12 @@ 9.30 return (idx << 3) | (rpl & 3); 9.31 } 9.32 9.33 +void set_tss(uint32_t addr) 9.34 +{ 9.35 + task_desc(gdt + SEGM_TASK, 0, sizeof(struct tss) - 1, 3, TSS_BUSY); 9.36 + set_task_reg(selector(SEGM_TASK, 0)); 9.37 +} 9.38 + 9.39 static void segm_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type) 9.40 { 9.41 desc->d[0] = limit & 0xffff; /* low order 16bits of limit */
10.1 --- a/src/segm.h Mon Aug 08 09:53:10 2011 +0300 10.2 +++ b/src/segm.h Sun Aug 14 16:57:23 2011 +0300 10.3 @@ -7,9 +7,13 @@ 10.4 #define SEGM_UDATA 4 10.5 #define SEGM_TASK 5 10.6 10.7 +#ifndef ASM 10.8 void init_segm(void); 10.9 10.10 uint16_t selector(int idx, int rpl); 10.11 10.12 +void set_tss(uint32_t addr); 10.13 +#endif /* ASM */ 10.14 + 10.15 10.16 #endif /* SEGM_H_ */
11.1 --- a/src/syscall.c Mon Aug 08 09:53:10 2011 +0300 11.2 +++ b/src/syscall.c Sun Aug 14 16:57:23 2011 +0300 11.3 @@ -35,26 +35,26 @@ 11.4 return; 11.5 } 11.6 11.7 + /* the return value goes into the interrupt frame copy of the user's eax 11.8 + * so that it'll be restored into eax before returning to userland. 11.9 + */ 11.10 frm->regs.eax = sys_func[idx](frm->regs.ebx, frm->regs.ecx, frm->regs.edx, frm->regs.esi, frm->regs.edi); 11.11 - schedule(); 11.12 } 11.13 11.14 static int sys_exit(int status) 11.15 { 11.16 + printf("SYSCALL: exit\n"); 11.17 return -1; /* not implemented yet */ 11.18 } 11.19 11.20 static int sys_hello(void) 11.21 { 11.22 - /*printf("process %d says hello!\n", get_current_pid());*/ 11.23 + printf("process %d says hello!\n", get_current_pid()); 11.24 return 0; 11.25 } 11.26 11.27 static int sys_sleep(int sec) 11.28 { 11.29 - int pid = get_current_pid(); 11.30 - /*printf("process %d will sleep for %d sec\n", pid, sec);*/ 11.31 - start_timer(sec * 1000, (timer_func_t)unblock_proc, (void*)pid); 11.32 - block_proc(pid); 11.33 - return 0; 11.34 + printf("SYSCALL: sleep\n"); 11.35 + return -1; 11.36 }