# HG changeset patch # User John Tsiombikas # Date 1313454413 -10800 # Node ID 437360696883e439e3c229f071d43802c9dc0d96 # Parent 0be4615594df271f76be24c33321877284a95846 I think we're done for now. two processes seem to be scheduled and switched just fine, fork seems to work (NO CoW YET!) diff -r 0be4615594df -r 437360696883 src/panic.c --- a/src/panic.c Mon Aug 15 06:17:58 2011 +0300 +++ b/src/panic.c Tue Aug 16 03:26:53 2011 +0300 @@ -18,12 +18,15 @@ { va_list ap; struct all_registers regs; + uint32_t eip; disable_intr(); memset(®s, 0, sizeof regs); get_regs(®s); + eip = get_caller_instr_ptr(); + printf("~~~~~ kernel panic ~~~~~\n"); va_start(ap, fmt); vprintf(fmt, ap); @@ -32,7 +35,7 @@ printf("\nRegisters:\n"); printf("eax: %x, ebx: %x, ecx: %x, edx: %x\n", regs.eax, regs.ebx, regs.ecx, regs.edx); printf("esp: %x, ebp: %x, esi: %x, edi: %x\n", regs.esp, regs.ebp, regs.esi, regs.edi); - printf("eflags: %x\n", regs.eflags); + printf("eip: %x, eflags: %x\n", eip, regs.eflags); printf("cr0: %x, cr1: %x, cr2: %x, cr3: %x\n", regs.cr0, regs.cr1, regs.cr2, regs.cr3); printf("cs: %x (%d|%d)\n", regs.cs, regs.cs >> 3, regs.cs & 3); printf("ss: %x (%d|%d)\n", regs.ss, regs.ss >> 3, regs.ss & 3); diff -r 0be4615594df -r 437360696883 src/proc-asm.S --- a/src/proc-asm.S Mon Aug 15 06:17:58 2011 +0300 +++ b/src/proc-asm.S Tue Aug 16 03:26:53 2011 +0300 @@ -1,9 +1,51 @@ .text - /* switch_stack(uint32_t new_stack) - * switches to the new stack and returns the old stack pointer + /* switch_stack(uint32_t new_stack, uint32_t *old_stack_ptr) + * switches to the new stack and returns the old stack pointer, which is + * also copied to the address passed as the second argument. */ .globl switch_stack switch_stack: - movl %esp, %eax - movl 4(%esp), %esp + movl %esp, %eax /* old stack in eax */ + movl 8(%esp), %edx + cmpl $0, %edx /* if old_stack_ptr is null, skip ahead */ + jz oldp_is_null + movl %eax, (%edx) /* otherwise *old_stack_ptr = eax */ +oldp_is_null: + movl 4(%esp), %esp /* set the new stack */ ret + + /* get_instr_stack_ptr(uint32_t *eip, uint32_t *esp) + * returns the current instruction and stack pointers at the same + * point in execution, so that a newly-forked process with these + * values will just return from this function and continue on. + */ + .globl get_instr_stack_ptr +get_instr_stack_ptr: + call get_instr_ptr + movl %eax, 4(%esp) + movl %esp, 8(%esp) + ret + + /* get_instr_ptr(void) + * returns the address of the next instruction after the call to this function + */ + .globl get_instr_ptr +get_instr_ptr: + movl (%esp), %eax + ret + + /* get_caller_instr_ptr(void) + * returns the address of the next instruction after the call to the function that + * called this function. + * NOTE: will only work properly when called from a function that uses ebp to point + * to its stack frame, which means all of the C functions but pretty much none of + * our assembly functions. + */ + .globl get_caller_instr_ptr +get_caller_instr_ptr: + movl 4(%ebp), %eax + ret + + .globl just_forked +just_forked: + call intr_ret diff -r 0be4615594df -r 437360696883 src/proc.c --- a/src/proc.c Mon Aug 15 06:17:58 2011 +0300 +++ b/src/proc.c Tue Aug 16 03:26:53 2011 +0300 @@ -1,6 +1,7 @@ #include #include #include +#include #include "config.h" #include "proc.h" #include "tss.h" @@ -17,7 +18,8 @@ static void start_first_proc(void); /* defined in proc-asm.S */ -uint32_t switch_stack(uint32_t new_stack); +uint32_t switch_stack(uint32_t new_stack, uint32_t *old_stack); +void just_forked(void); /* defined in test_proc.S */ void test_proc(void); @@ -62,7 +64,6 @@ start_first_proc(); /* XXX never returns */ } - static void start_first_proc(void) { struct process *p; @@ -134,6 +135,66 @@ intr_ret(ifrm); } +int fork(void) +{ + int i, pid; + struct process *p, *parent; + + disable_intr(); + + /* find a free process slot */ + /* TODO don't search up to MAX_PROC if uid != 0 */ + pid = -1; + for(i=1; ikern_stack_pg = pgalloc(KERN_STACK_SIZE / PGSIZE, MEM_KERNEL)) == -1) { + return -EAGAIN; + } + p->ctx.stack_ptr = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE; + + /* we need to copy the current interrupt frame to the new kernel stack so + * that the new process will return to the same point as the parent, just + * after the fork syscall. + */ + p->ctx.stack_ptr -= sizeof(struct intr_frame); + memcpy((void*)p->ctx.stack_ptr, get_intr_frame(), sizeof(struct intr_frame)); + /* child's return from fork returns 0 */ + ((struct intr_frame*)p->ctx.stack_ptr)->regs.eax = 0; + + /* XXX describe */ + p->ctx.stack_ptr -= 4; + *(uint32_t*)p->ctx.stack_ptr = (uint32_t)just_forked; + + /* initialize the rest of the process structure */ + p->id = pid; + p->parent = parent->id; + p->next = p->prev = 0; + + /* will be copied on write */ + p->user_stack_pg = parent->user_stack_pg; + + p->ctx.pgtbl_paddr = clone_vm(CLONE_COW); + + /* done, now let's add it to the scheduler runqueue */ + add_proc(p->id); + + return pid; +} void context_switch(int pid) { @@ -147,13 +208,7 @@ new = proc + pid; if(last_pid != pid) { - /* push all registers onto the stack before switching stacks */ - push_regs(); - - prev->ctx.stack_ptr = switch_stack(new->ctx.stack_ptr); - - /* restore registers from the new stack */ - pop_regs(); + set_current_pid(new->id); /* switch to the new process' address space */ set_pgdir_addr(new->ctx.pgtbl_paddr); @@ -162,9 +217,21 @@ * we enter from userspace */ tss->esp0 = PAGE_TO_ADDR(new->kern_stack_pg) + KERN_STACK_SIZE; + + /* push all registers onto the stack before switching stacks */ + push_regs(); + + /* XXX: when switching to newly forked processes this switch_stack call + * WILL NOT RETURN HERE. It will return to just_forked instead. So the + * rest of this function will not run. + */ + switch_stack(new->ctx.stack_ptr, &prev->ctx.stack_ptr); + + /* restore registers from the new stack */ + pop_regs(); + } else { + set_current_pid(new->id); } - - set_current_pid(new->id); } diff -r 0be4615594df -r 437360696883 src/proc.h --- a/src/proc.h Mon Aug 15 06:17:58 2011 +0300 +++ b/src/proc.h Tue Aug 16 03:26:53 2011 +0300 @@ -45,6 +45,8 @@ void init_proc(void); +int fork(void); + void context_switch(int pid); void set_current_pid(int pid); @@ -52,4 +54,9 @@ struct process *get_current_proc(void); struct process *get_process(int pid); +/* defined in proc-asm.S */ +uint32_t get_instr_ptr(void); +uint32_t get_caller_instr_ptr(void); +void get_instr_stack_ptr(uint32_t *iptr, uint32_t *sptr); + #endif /* PROC_H_ */ diff -r 0be4615594df -r 437360696883 src/sched.c --- a/src/sched.c Mon Aug 15 06:17:58 2011 +0300 +++ b/src/sched.c Tue Aug 16 03:26:53 2011 +0300 @@ -136,6 +136,8 @@ set_current_pid(0); + printf("idle loop is running\n"); + /* make sure interrupts are enabled before halting */ while(EMPTY(&runq)) { enable_intr(); diff -r 0be4615594df -r 437360696883 src/syscall.c --- a/src/syscall.c Mon Aug 15 06:17:58 2011 +0300 +++ b/src/syscall.c Tue Aug 16 03:26:53 2011 +0300 @@ -12,12 +12,16 @@ static int sys_exit(int status); static int sys_hello(void); static int sys_sleep(int sec); +static int sys_fork(void); +static int sys_getpid(void); void init_syscall(void) { sys_func[SYS_EXIT] = sys_exit; sys_func[SYS_HELLO] = sys_hello; sys_func[SYS_SLEEP] = sys_sleep; + sys_func[SYS_FORK] = sys_fork; + sys_func[SYS_GETPID] = sys_getpid; interrupt(SYSCALL_INT, syscall); } @@ -56,8 +60,19 @@ static int sys_sleep(int sec) { printf("process %d will sleep for %d seconds\n", get_current_pid(), sec); - - sleep(sec * 1000); - + sleep(sec * 1000); /* timer.c */ return 0; } + +static int sys_fork(void) +{ + printf("process %d is forking\n", get_current_pid()); + return fork(); /* proc.c */ +} + +static int sys_getpid(void) +{ + int pid = get_current_pid(); + printf("process %d getpid\n", pid); + return pid; +} diff -r 0be4615594df -r 437360696883 src/syscall.h --- a/src/syscall.h Mon Aug 15 06:17:58 2011 +0300 +++ b/src/syscall.h Tue Aug 16 03:26:53 2011 +0300 @@ -7,8 +7,10 @@ #define SYS_EXIT 0 #define SYS_HELLO 1 #define SYS_SLEEP 2 +#define SYS_FORK 3 +#define SYS_GETPID 4 -#define NUM_SYSCALLS 3 +#define NUM_SYSCALLS 5 #ifndef ASM void init_syscall(void); diff -r 0be4615594df -r 437360696883 src/test_proc.S --- a/src/test_proc.S Mon Aug 15 06:17:58 2011 +0300 +++ b/src/test_proc.S Tue Aug 16 03:26:53 2011 +0300 @@ -4,16 +4,28 @@ .text .globl test_proc test_proc: + /* fork another process */ + movl $SYS_FORK, %eax + int $SYSCALL_INT + +infloop: /* --- print a message --- */ movl $SYS_HELLO, %eax int $SYSCALL_INT + /* --- call getpid --- */ + movl $SYS_GETPID, %eax + int $SYSCALL_INT + /* copy the pid to ebx to use as a sleep interval */ + movl %eax, %ebx + shl $1, %ebx + /* --- sleep for 5 seconds --- */ movl $SYS_SLEEP, %eax - movl $3, %ebx + /*movl $3, %ebx*/ int $SYSCALL_INT - jmp test_proc + jmp infloop .globl test_proc_end test_proc_end: diff -r 0be4615594df -r 437360696883 src/timer.c --- a/src/timer.c Mon Aug 15 06:17:58 2011 +0300 +++ b/src/timer.c Tue Aug 16 03:26:53 2011 +0300 @@ -136,6 +136,7 @@ static void intr_handler(int inum) { int istate; + struct process *p; nticks++; @@ -159,7 +160,12 @@ } } - /* call the scheduler to decide if it's time to switch processes */ + /* decrement the process' ticks_left and call the scheduler to decide if + * it's time to switch processes + */ + if((p = get_current_proc())) { + p->ticks_left--; + } schedule(); set_intr_state(istate); diff -r 0be4615594df -r 437360696883 src/vm.c --- a/src/vm.c Mon Aug 15 06:17:58 2011 +0300 +++ b/src/vm.c Tue Aug 16 03:26:53 2011 +0300 @@ -566,7 +566,6 @@ /*printf("free_node\n");*/ } -#if 0 /* clone_vm makes a copy of the current page tables, thus duplicating the * virtual address space. * @@ -574,11 +573,14 @@ * we don't want to diplicate the page tables, just point all page directory * entries to the same set of page tables. * + * If "cow" is non-zero it also marks the shared user-space pages as + * read-only, to implement copy-on-write. + * * Returns the physical address of the new page directory. */ -uint32_t clone_vm(void) +uint32_t clone_vm(int cow) { - int i, dirpg, tblpg, kstart_dirent; + int i, j, dirpg, tblpg, kstart_dirent; uint32_t paddr; uint32_t *ndir, *ntbl; @@ -597,7 +599,8 @@ ntbl = (uint32_t*)PAGE_TO_ADDR(tblpg); /* we will allocate physical pages and map them to this virtual page - * as needed in the loop below. + * as needed in the loop below. we don't need the physical page allocated + * by pgalloc. */ free_phys_page(virt_to_phys((uint32_t)ntbl)); @@ -606,10 +609,18 @@ /* user space */ for(i=0; i> 10) #define PAGE_TO_PGTBL_PG(x) ((uint32_t)(x) & 0x3ff) +/* argument to clone_vm */ +#define CLONE_SHARED 0 +#define CLONE_COW 1 + +/* last argument to *_page_bit */ +#define PAGE_ONLY 0 +#define WHOLE_PATH 1 void init_vm(void); @@ -57,7 +64,11 @@ int pgalloc_vrange(int start, int num); void pgfree(int start, int num); -uint32_t clone_vm(void); +uint32_t clone_vm(int cow); + +int get_page_bit(int pgnum, uint32_t bit, int wholepath); +void set_page_bit(int pgnum, uint32_t bit, int wholepath); +void clear_page_bit(int pgnum, uint32_t bit, int wholepath); void dbg_print_vm(int area);