# HG changeset patch # User John Tsiombikas # Date 1313330243 -10800 # Node ID 4eaecb14fe31ca6e36f73c99573b063d88bde952 # Parent 23abbeea4d5f72f2ce8632935f16b6db783ace2e bringing the task switching thing into shape with proper per-process kernel stacks and shit diff -r 23abbeea4d5f -r 4eaecb14fe31 src/asmops.h --- a/src/asmops.h Mon Aug 08 09:53:10 2011 +0300 +++ b/src/asmops.h Sun Aug 14 16:57:23 2011 +0300 @@ -13,6 +13,9 @@ #define disable_intr() asm volatile("cli") #define halt_cpu() asm volatile("hlt") +#define push_regs() asm volatile("pusha"); +#define pop_regs() asm volatile("popa"); + #define inb(dest, port) asm volatile( \ "inb %1, %0\n\t" \ : "=a" ((unsigned char)(dest)) \ @@ -42,4 +45,5 @@ #define iodelay() outb(0, 0x80) + #endif /* ASMOPS_H_ */ diff -r 23abbeea4d5f -r 4eaecb14fe31 src/config.h --- a/src/config.h Mon Aug 08 09:53:10 2011 +0300 +++ b/src/config.h Sun Aug 14 16:57:23 2011 +0300 @@ -10,4 +10,7 @@ /* allow automatic user stack growth by at most 1024 pages at a time (4mb) */ #define USTACK_MAXGROW 1024 +/* per-process kernel stack size (2 pages) */ +#define KERN_STACK_SIZE 8192 + #endif /* _CONFIG_H_ */ diff -r 23abbeea4d5f -r 4eaecb14fe31 src/intr-asm.S --- a/src/intr-asm.S Mon Aug 08 09:53:10 2011 +0300 +++ b/src/intr-asm.S Sun Aug 14 16:57:23 2011 +0300 @@ -1,3 +1,6 @@ +#define ASM +#include "segm.h" + .data .align 4 .short 0 @@ -68,10 +71,29 @@ */ .extern dispatch_intr intr_entry_common: + /* save the current data segment selectors */ + pushl %gs + pushl %fs + pushl %es + pushl %ds + /* save general purpose registers */ pusha + /* if we entered from userspace ss and cs is set correctly, but + * we must make sure all the other selectors are set to the + * kernel data segment */ + mov $SEGM_KDATA, %ds + mov $SEGM_KDATA, %es + mov $SEGM_KDATA, %fs + mov $SEGM_KDATA, %gs call dispatch_intr intr_ret_local: + /* restore general purpose registers */ popa + /* restore data segment selectors */ + popl %ds + popl %es + popl %fs + popl %gs /* remove error code and intr num from stack */ add $8, %esp iret @@ -93,5 +115,4 @@ /* by including interrupts.h with ASM defined, the macros above * are expanded to generate all required interrupt entry points */ -#define ASM #include diff -r 23abbeea4d5f -r 4eaecb14fe31 src/intr.h --- a/src/intr.h Mon Aug 08 09:53:10 2011 +0300 +++ b/src/intr.h Sun Aug 14 16:57:23 2011 +0300 @@ -18,6 +18,8 @@ struct intr_frame { /* registers pushed by pusha in intr_entry_* */ struct registers regs; + /* data segment selectors */ + uint32_t ds, es, fs, gs; /* interrupt number and error code pushed in intr_entry_* */ uint32_t inum, err; /* pushed by CPU during interrupt entry */ diff -r 23abbeea4d5f -r 4eaecb14fe31 src/proc-asm.S --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/proc-asm.S Sun Aug 14 16:57:23 2011 +0300 @@ -0,0 +1,9 @@ + .text + /* switch_stack(uint32_t new_stack) + * switches to the new stack and returns the old stack pointer + */ + .globl switch_stack +switch_stack: + movl %esp, %eax + movl 4(%esp), %esp + ret diff -r 23abbeea4d5f -r 4eaecb14fe31 src/proc.c --- a/src/proc.c Mon Aug 08 09:53:10 2011 +0300 +++ b/src/proc.c Sun Aug 14 16:57:23 2011 +0300 @@ -9,9 +9,13 @@ #include "panic.h" #include "syscall.h" #include "sched.h" +#include "tss.h" #define FLAGS_INTR_BIT 9 +static void start_first_proc(void); + + /* defined in test_proc.S */ void test_proc(void); void test_proc_end(void); @@ -19,90 +23,125 @@ static struct process proc[MAX_PROC]; static int cur_pid; +static struct task_state *tss; + + void init_proc(void) { - int proc_size_pg, img_start_pg, stack_pg; - void *img_start; - cur_pid = 0; + int tss_page; + /* allocate a page for the task state segment, to make sure + * it doesn't cross page boundaries + */ + if((tss_page = pgalloc(1, MEM_KERNEL)) == -1) { + panic("failed to allocate memory for the task state segment\n"); + } + tss = (struct tss*)PAGE_TO_ADDR(tss_page); + + /* the kernel stack segment never changes so we might as well set it now + * the only other thing that we use in the tss is the kernel stack pointer + * which is different for each process, and thus managed by context_switch + */ + memset(tss, 0, sizeof *tss); + tss->ss0 = selector(SEGM_KDATA, 0); + + set_tss((uint32_t)virt_to_phys(tss)); + + /* initialize system call handler (see syscall.c) */ init_syscall(); + start_first_proc(); /* XXX never returns */ +} + + +static void start_first_proc(void) +{ + struct process *p; + int proc_size_pg, img_start_pg, stack_pg; + uint32_t img_start_addr, ustack_addr; + struct intr_frame ifrm; + /* prepare the first process */ - proc[1].id = 1; - proc[1].parent = 0; + p = proc + 1; + p->id = 1; + p->parent = 0; /* no parent for init */ /* allocate a chunk of memory for the process image * and copy the code of test_proc there. - * (should be mapped at a fixed address) */ proc_size_pg = (test_proc_end - test_proc) / PGSIZE + 1; if((img_start_pg = pgalloc(proc_size_pg, MEM_USER)) == -1) { panic("failed to allocate space for the init process image\n"); } - img_start = (void*)PAGE_TO_ADDR(img_start_pg); - memcpy(img_start, test_proc, proc_size_pg * PGSIZE); - - printf("copied init process at: %x\n", (unsigned int)img_start); - - /* instruction pointer at the beginning of the process image */ - proc[1].ctx.instr_ptr = (uint32_t)img_start; + img_start_addr = PAGE_TO_ADDR(img_start_pg); + memcpy((void*)img_start_addr, test_proc, proc_size_pg * PGSIZE); + printf("copied init process at: %x\n", img_start_addr); /* allocate the first page of the process stack */ stack_pg = ADDR_TO_PAGE(KMEM_START) - 1; if(pgalloc_vrange(stack_pg, 1) == -1) { panic("failed to allocate user stack page\n"); } - proc[1].ctx.stack_ptr = PAGE_TO_ADDR(stack_pg) + PGSIZE; - proc[1].stack_start_pg = stack_pg; + p->user_stack_pg = stack_pg; - /* create the virtual address space for this process */ - /*proc[1].ctx.pgtbl_paddr = clone_vm();*/ + /* allocate a kernel stack for this process */ + if((p->kern_stack_pg = pgalloc(KERN_STACK_SIZE / PGSIZE, MEM_KERNEL)) == -1) { + panic("failed to allocate kernel stack for the init process\n"); + } + /* when switching from user space to kernel space, the ss0:esp0 from TSS + * will be used to switch to the per-process kernel stack, so we need to + * set it correctly before switching to user space. + * tss->ss0 is already set in init_proc above. + */ + tss->esp0 = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE; - /* we don't need the image and the stack in this address space */ - /*unmap_page_range(img_start_pg, proc_size_pg); - pgfree(img_start_pg, proc_size_pg); - unmap_page(stack_pg); - pgfree(stack_pg, 1);*/ + /* now we need to fill in the fake interrupt stack frame */ + memset(&ifrm, 0, sizeof ifrm); + /* after the priviledge switch, this ss:esp will be used in userspace */ + ifrm.esp = PAGE_TO_ADDR(stack_pg) + PGSIZE; + ifrm.ss = selector(SEGM_UDATA, 3); + /* instruction pointer at the beginning of the process image */ + ifrm.regs.eip = img_start_addr; + ifrm.cs = selector(SEGM_UCODE, 3); + /* make sure the user will run with interrupts enabled */ + ifrm.eflags = FLAGS_INTR_BIT; + /* user data selectors should all be the same */ + ifrm.ds = ifrm.es = ifrm.fs = ifrm.gs = ifrm.ss; /* add it to the scheduler queues */ - add_proc(1, STATE_RUNNABLE); + add_proc(p->id, STATE_RUNNABLE); - /* switch to the initial process, this never returns */ - context_switch(1); + /* execute a fake return from interrupt with the fake stack frame */ + intr_ret(ifrm); } void context_switch(int pid) { - struct context *ctx; - struct intr_frame ifrm; - struct intr_frame *cur_frame = get_intr_frame(); - - assert(0); + struct process *prev, *new; if(cur_pid == pid) { - assert(cur_frame); - intr_ret(*cur_frame); + return; /* nothing to be done */ } + prev = proc + cur_pid; + new = proc + pid; - ctx = &proc[pid].ctx; - cur_pid = pid; + /* push all registers onto the stack before switching stacks */ + push_regs(); - ifrm.inum = ifrm.err = 0; + prev->ctx.stack_ptr = switch_stack(new->ctx.stack_ptr); - ifrm.regs = ctx->regs; - ifrm.eflags = ctx->flags | (1 << FLAGS_INTR_BIT); + /* restore registers from the new stack */ + pop_regs(); - ifrm.eip = ctx->instr_ptr; - ifrm.cs = selector(SEGM_KCODE, 0); /* XXX change this when we setup the TSS */ - /*ifrm.regs.esp = ctx->stack_ptr;*/ /* ... until then... */ - ifrm.esp = ctx->stack_ptr; /* this will only be used when we switch to userspace */ - ifrm.ss = selector(SEGM_KDATA, 0); + /* switch to the new process' address space */ + set_pgdir_addr(new->ctx.pgtbl_paddr); - /* switch to the vm of the process */ - set_pgdir_addr(ctx->pgtbl_paddr); - intr_ret(ifrm); + /* make sure we'll return to the correct kernel stack next time + * we enter from userspace + */ + tss->esp0 = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE; } int get_current_pid(void) diff -r 23abbeea4d5f -r 4eaecb14fe31 src/proc.h --- a/src/proc.h Mon Aug 08 09:53:10 2011 +0300 +++ b/src/proc.h Sun Aug 14 16:57:23 2011 +0300 @@ -7,10 +7,10 @@ #define MAX_PROC 128 struct context { - struct registers regs; /* saved general purpose registers */ - uint32_t instr_ptr; /* saved eip */ + /*struct registers regs;*/ /* saved general purpose registers */ + /*uint32_t instr_ptr;*/ /* saved eip */ uint32_t stack_ptr; /* saved esp */ - uint32_t flags; /* saved eflags */ + /*uint32_t flags;*/ /* saved eflags */ uint32_t pgtbl_paddr; /* physical address of the page table */ /* TODO add FPU state */ }; @@ -31,7 +31,9 @@ /* extends of the process heap, increased by sbrk */ /* first page of the user stack, extends up to KMEM_START */ - int stack_start_pg; + int user_stack_pg; + /* first page of the kernel stack, (KERN_STACK_SIZE) */ + int kern_stack_pg; struct context ctx; diff -r 23abbeea4d5f -r 4eaecb14fe31 src/segm-asm.S --- a/src/segm-asm.S Mon Aug 08 09:53:10 2011 +0300 +++ b/src/segm-asm.S Sun Aug 14 16:57:23 2011 +0300 @@ -37,3 +37,10 @@ movw %ax, (lim) lgdt (lim) ret + +/* set_task_reg(uint16_t tss_selector) + * loads the TSS selector in the task register */ + .globl set_task_reg +set_task_reg: + ltr 4(%esp) + ret diff -r 23abbeea4d5f -r 4eaecb14fe31 src/segm.c --- a/src/segm.c Mon Aug 08 09:53:10 2011 +0300 +++ b/src/segm.c Sun Aug 14 16:57:23 2011 +0300 @@ -21,8 +21,7 @@ enum {TYPE_DATA, TYPE_CODE}; -#define TSS_TYPE_BITS 0x900 -#define TSS_BUSY BIT_BUSY +#define TSS_TYPE_BITS (BIT_ACCESSED | BIT_CODE) static void segm_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type); static void task_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, unsigned int busy); @@ -30,6 +29,7 @@ /* these functions are implemented in segm-asm.S */ void setup_selectors(uint16_t code, uint16_t data); void set_gdt(uint32_t addr, uint16_t limit); +void set_task_reg(uint16_t tss_selector); /* our global descriptor table */ @@ -43,7 +43,6 @@ segm_desc(gdt + SEGM_KDATA, 0, 0xffffffff, 0, TYPE_DATA); segm_desc(gdt + SEGM_UCODE, 0, 0xffffffff, 3, TYPE_CODE); segm_desc(gdt + SEGM_UDATA, 0, 0xffffffff, 3, TYPE_DATA); - task_desc(gdt + SEGM_TASK, 0, 0xffffffff, 3, TSS_BUSY); set_gdt((uint32_t)gdt, sizeof gdt - 1); @@ -56,6 +55,12 @@ return (idx << 3) | (rpl & 3); } +void set_tss(uint32_t addr) +{ + task_desc(gdt + SEGM_TASK, 0, sizeof(struct tss) - 1, 3, TSS_BUSY); + set_task_reg(selector(SEGM_TASK, 0)); +} + static void segm_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type) { desc->d[0] = limit & 0xffff; /* low order 16bits of limit */ diff -r 23abbeea4d5f -r 4eaecb14fe31 src/segm.h --- a/src/segm.h Mon Aug 08 09:53:10 2011 +0300 +++ b/src/segm.h Sun Aug 14 16:57:23 2011 +0300 @@ -7,9 +7,13 @@ #define SEGM_UDATA 4 #define SEGM_TASK 5 +#ifndef ASM void init_segm(void); uint16_t selector(int idx, int rpl); +void set_tss(uint32_t addr); +#endif /* ASM */ + #endif /* SEGM_H_ */ diff -r 23abbeea4d5f -r 4eaecb14fe31 src/syscall.c --- a/src/syscall.c Mon Aug 08 09:53:10 2011 +0300 +++ b/src/syscall.c Sun Aug 14 16:57:23 2011 +0300 @@ -35,26 +35,26 @@ return; } + /* the return value goes into the interrupt frame copy of the user's eax + * so that it'll be restored into eax before returning to userland. + */ frm->regs.eax = sys_func[idx](frm->regs.ebx, frm->regs.ecx, frm->regs.edx, frm->regs.esi, frm->regs.edi); - schedule(); } static int sys_exit(int status) { + printf("SYSCALL: exit\n"); return -1; /* not implemented yet */ } static int sys_hello(void) { - /*printf("process %d says hello!\n", get_current_pid());*/ + printf("process %d says hello!\n", get_current_pid()); return 0; } static int sys_sleep(int sec) { - int pid = get_current_pid(); - /*printf("process %d will sleep for %d sec\n", pid, sec);*/ - start_timer(sec * 1000, (timer_func_t)unblock_proc, (void*)pid); - block_proc(pid); - return 0; + printf("SYSCALL: sleep\n"); + return -1; }