kern
changeset 57:437360696883
I think we're done for now. two processes seem to be scheduled and switched just fine, fork seems to work (NO CoW YET!)
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Tue, 16 Aug 2011 03:26:53 +0300 |
parents | 0be4615594df |
children | 64a574189824 |
files | src/panic.c src/proc-asm.S src/proc.c src/proc.h src/sched.c src/syscall.c src/syscall.h src/test_proc.S src/timer.c src/vm.c src/vm.h |
diffstat | 11 files changed, 255 insertions(+), 31 deletions(-) [+] |
line diff
1.1 --- a/src/panic.c Mon Aug 15 06:17:58 2011 +0300 1.2 +++ b/src/panic.c Tue Aug 16 03:26:53 2011 +0300 1.3 @@ -18,12 +18,15 @@ 1.4 { 1.5 va_list ap; 1.6 struct all_registers regs; 1.7 + uint32_t eip; 1.8 1.9 disable_intr(); 1.10 1.11 memset(®s, 0, sizeof regs); 1.12 get_regs(®s); 1.13 1.14 + eip = get_caller_instr_ptr(); 1.15 + 1.16 printf("~~~~~ kernel panic ~~~~~\n"); 1.17 va_start(ap, fmt); 1.18 vprintf(fmt, ap); 1.19 @@ -32,7 +35,7 @@ 1.20 printf("\nRegisters:\n"); 1.21 printf("eax: %x, ebx: %x, ecx: %x, edx: %x\n", regs.eax, regs.ebx, regs.ecx, regs.edx); 1.22 printf("esp: %x, ebp: %x, esi: %x, edi: %x\n", regs.esp, regs.ebp, regs.esi, regs.edi); 1.23 - printf("eflags: %x\n", regs.eflags); 1.24 + printf("eip: %x, eflags: %x\n", eip, regs.eflags); 1.25 printf("cr0: %x, cr1: %x, cr2: %x, cr3: %x\n", regs.cr0, regs.cr1, regs.cr2, regs.cr3); 1.26 printf("cs: %x (%d|%d)\n", regs.cs, regs.cs >> 3, regs.cs & 3); 1.27 printf("ss: %x (%d|%d)\n", regs.ss, regs.ss >> 3, regs.ss & 3);
2.1 --- a/src/proc-asm.S Mon Aug 15 06:17:58 2011 +0300 2.2 +++ b/src/proc-asm.S Tue Aug 16 03:26:53 2011 +0300 2.3 @@ -1,9 +1,51 @@ 2.4 .text 2.5 - /* switch_stack(uint32_t new_stack) 2.6 - * switches to the new stack and returns the old stack pointer 2.7 + /* switch_stack(uint32_t new_stack, uint32_t *old_stack_ptr) 2.8 + * switches to the new stack and returns the old stack pointer, which is 2.9 + * also copied to the address passed as the second argument. 2.10 */ 2.11 .globl switch_stack 2.12 switch_stack: 2.13 - movl %esp, %eax 2.14 - movl 4(%esp), %esp 2.15 + movl %esp, %eax /* old stack in eax */ 2.16 + movl 8(%esp), %edx 2.17 + cmpl $0, %edx /* if old_stack_ptr is null, skip ahead */ 2.18 + jz oldp_is_null 2.19 + movl %eax, (%edx) /* otherwise *old_stack_ptr = eax */ 2.20 +oldp_is_null: 2.21 + movl 4(%esp), %esp /* set the new stack */ 2.22 ret 2.23 + 2.24 + /* get_instr_stack_ptr(uint32_t *eip, uint32_t *esp) 2.25 + * returns the current instruction and stack pointers at the same 2.26 + * point in execution, so that a newly-forked process with these 2.27 + * values will just return from this function and continue on. 2.28 + */ 2.29 + .globl get_instr_stack_ptr 2.30 +get_instr_stack_ptr: 2.31 + call get_instr_ptr 2.32 + movl %eax, 4(%esp) 2.33 + movl %esp, 8(%esp) 2.34 + ret 2.35 + 2.36 + /* get_instr_ptr(void) 2.37 + * returns the address of the next instruction after the call to this function 2.38 + */ 2.39 + .globl get_instr_ptr 2.40 +get_instr_ptr: 2.41 + movl (%esp), %eax 2.42 + ret 2.43 + 2.44 + /* get_caller_instr_ptr(void) 2.45 + * returns the address of the next instruction after the call to the function that 2.46 + * called this function. 2.47 + * NOTE: will only work properly when called from a function that uses ebp to point 2.48 + * to its stack frame, which means all of the C functions but pretty much none of 2.49 + * our assembly functions. 2.50 + */ 2.51 + .globl get_caller_instr_ptr 2.52 +get_caller_instr_ptr: 2.53 + movl 4(%ebp), %eax 2.54 + ret 2.55 + 2.56 + .globl just_forked 2.57 +just_forked: 2.58 + call intr_ret
3.1 --- a/src/proc.c Mon Aug 15 06:17:58 2011 +0300 3.2 +++ b/src/proc.c Tue Aug 16 03:26:53 2011 +0300 3.3 @@ -1,6 +1,7 @@ 3.4 #include <stdio.h> 3.5 #include <string.h> 3.6 #include <assert.h> 3.7 +#include <errno.h> 3.8 #include "config.h" 3.9 #include "proc.h" 3.10 #include "tss.h" 3.11 @@ -17,7 +18,8 @@ 3.12 static void start_first_proc(void); 3.13 3.14 /* defined in proc-asm.S */ 3.15 -uint32_t switch_stack(uint32_t new_stack); 3.16 +uint32_t switch_stack(uint32_t new_stack, uint32_t *old_stack); 3.17 +void just_forked(void); 3.18 3.19 /* defined in test_proc.S */ 3.20 void test_proc(void); 3.21 @@ -62,7 +64,6 @@ 3.22 start_first_proc(); /* XXX never returns */ 3.23 } 3.24 3.25 - 3.26 static void start_first_proc(void) 3.27 { 3.28 struct process *p; 3.29 @@ -134,6 +135,66 @@ 3.30 intr_ret(ifrm); 3.31 } 3.32 3.33 +int fork(void) 3.34 +{ 3.35 + int i, pid; 3.36 + struct process *p, *parent; 3.37 + 3.38 + disable_intr(); 3.39 + 3.40 + /* find a free process slot */ 3.41 + /* TODO don't search up to MAX_PROC if uid != 0 */ 3.42 + pid = -1; 3.43 + for(i=1; i<MAX_PROC; i++) { 3.44 + if(proc[i].id == 0) { 3.45 + pid = i; 3.46 + break; 3.47 + } 3.48 + } 3.49 + 3.50 + if(pid == -1) { 3.51 + /* process table full */ 3.52 + return -EAGAIN; 3.53 + } 3.54 + 3.55 + 3.56 + p = proc + pid; 3.57 + parent = get_current_proc(); 3.58 + 3.59 + /* allocate a kernel stack for the new process */ 3.60 + if((p->kern_stack_pg = pgalloc(KERN_STACK_SIZE / PGSIZE, MEM_KERNEL)) == -1) { 3.61 + return -EAGAIN; 3.62 + } 3.63 + p->ctx.stack_ptr = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE; 3.64 + 3.65 + /* we need to copy the current interrupt frame to the new kernel stack so 3.66 + * that the new process will return to the same point as the parent, just 3.67 + * after the fork syscall. 3.68 + */ 3.69 + p->ctx.stack_ptr -= sizeof(struct intr_frame); 3.70 + memcpy((void*)p->ctx.stack_ptr, get_intr_frame(), sizeof(struct intr_frame)); 3.71 + /* child's return from fork returns 0 */ 3.72 + ((struct intr_frame*)p->ctx.stack_ptr)->regs.eax = 0; 3.73 + 3.74 + /* XXX describe */ 3.75 + p->ctx.stack_ptr -= 4; 3.76 + *(uint32_t*)p->ctx.stack_ptr = (uint32_t)just_forked; 3.77 + 3.78 + /* initialize the rest of the process structure */ 3.79 + p->id = pid; 3.80 + p->parent = parent->id; 3.81 + p->next = p->prev = 0; 3.82 + 3.83 + /* will be copied on write */ 3.84 + p->user_stack_pg = parent->user_stack_pg; 3.85 + 3.86 + p->ctx.pgtbl_paddr = clone_vm(CLONE_COW); 3.87 + 3.88 + /* done, now let's add it to the scheduler runqueue */ 3.89 + add_proc(p->id); 3.90 + 3.91 + return pid; 3.92 +} 3.93 3.94 void context_switch(int pid) 3.95 { 3.96 @@ -147,13 +208,7 @@ 3.97 new = proc + pid; 3.98 3.99 if(last_pid != pid) { 3.100 - /* push all registers onto the stack before switching stacks */ 3.101 - push_regs(); 3.102 - 3.103 - prev->ctx.stack_ptr = switch_stack(new->ctx.stack_ptr); 3.104 - 3.105 - /* restore registers from the new stack */ 3.106 - pop_regs(); 3.107 + set_current_pid(new->id); 3.108 3.109 /* switch to the new process' address space */ 3.110 set_pgdir_addr(new->ctx.pgtbl_paddr); 3.111 @@ -162,9 +217,21 @@ 3.112 * we enter from userspace 3.113 */ 3.114 tss->esp0 = PAGE_TO_ADDR(new->kern_stack_pg) + KERN_STACK_SIZE; 3.115 + 3.116 + /* push all registers onto the stack before switching stacks */ 3.117 + push_regs(); 3.118 + 3.119 + /* XXX: when switching to newly forked processes this switch_stack call 3.120 + * WILL NOT RETURN HERE. It will return to just_forked instead. So the 3.121 + * rest of this function will not run. 3.122 + */ 3.123 + switch_stack(new->ctx.stack_ptr, &prev->ctx.stack_ptr); 3.124 + 3.125 + /* restore registers from the new stack */ 3.126 + pop_regs(); 3.127 + } else { 3.128 + set_current_pid(new->id); 3.129 } 3.130 - 3.131 - set_current_pid(new->id); 3.132 } 3.133 3.134
4.1 --- a/src/proc.h Mon Aug 15 06:17:58 2011 +0300 4.2 +++ b/src/proc.h Tue Aug 16 03:26:53 2011 +0300 4.3 @@ -45,6 +45,8 @@ 4.4 4.5 void init_proc(void); 4.6 4.7 +int fork(void); 4.8 + 4.9 void context_switch(int pid); 4.10 4.11 void set_current_pid(int pid); 4.12 @@ -52,4 +54,9 @@ 4.13 struct process *get_current_proc(void); 4.14 struct process *get_process(int pid); 4.15 4.16 +/* defined in proc-asm.S */ 4.17 +uint32_t get_instr_ptr(void); 4.18 +uint32_t get_caller_instr_ptr(void); 4.19 +void get_instr_stack_ptr(uint32_t *iptr, uint32_t *sptr); 4.20 + 4.21 #endif /* PROC_H_ */
5.1 --- a/src/sched.c Mon Aug 15 06:17:58 2011 +0300 5.2 +++ b/src/sched.c Tue Aug 16 03:26:53 2011 +0300 5.3 @@ -136,6 +136,8 @@ 5.4 5.5 set_current_pid(0); 5.6 5.7 + printf("idle loop is running\n"); 5.8 + 5.9 /* make sure interrupts are enabled before halting */ 5.10 while(EMPTY(&runq)) { 5.11 enable_intr();
6.1 --- a/src/syscall.c Mon Aug 15 06:17:58 2011 +0300 6.2 +++ b/src/syscall.c Tue Aug 16 03:26:53 2011 +0300 6.3 @@ -12,12 +12,16 @@ 6.4 static int sys_exit(int status); 6.5 static int sys_hello(void); 6.6 static int sys_sleep(int sec); 6.7 +static int sys_fork(void); 6.8 +static int sys_getpid(void); 6.9 6.10 void init_syscall(void) 6.11 { 6.12 sys_func[SYS_EXIT] = sys_exit; 6.13 sys_func[SYS_HELLO] = sys_hello; 6.14 sys_func[SYS_SLEEP] = sys_sleep; 6.15 + sys_func[SYS_FORK] = sys_fork; 6.16 + sys_func[SYS_GETPID] = sys_getpid; 6.17 6.18 interrupt(SYSCALL_INT, syscall); 6.19 } 6.20 @@ -56,8 +60,19 @@ 6.21 static int sys_sleep(int sec) 6.22 { 6.23 printf("process %d will sleep for %d seconds\n", get_current_pid(), sec); 6.24 - 6.25 - sleep(sec * 1000); 6.26 - 6.27 + sleep(sec * 1000); /* timer.c */ 6.28 return 0; 6.29 } 6.30 + 6.31 +static int sys_fork(void) 6.32 +{ 6.33 + printf("process %d is forking\n", get_current_pid()); 6.34 + return fork(); /* proc.c */ 6.35 +} 6.36 + 6.37 +static int sys_getpid(void) 6.38 +{ 6.39 + int pid = get_current_pid(); 6.40 + printf("process %d getpid\n", pid); 6.41 + return pid; 6.42 +}
7.1 --- a/src/syscall.h Mon Aug 15 06:17:58 2011 +0300 7.2 +++ b/src/syscall.h Tue Aug 16 03:26:53 2011 +0300 7.3 @@ -7,8 +7,10 @@ 7.4 #define SYS_EXIT 0 7.5 #define SYS_HELLO 1 7.6 #define SYS_SLEEP 2 7.7 +#define SYS_FORK 3 7.8 +#define SYS_GETPID 4 7.9 7.10 -#define NUM_SYSCALLS 3 7.11 +#define NUM_SYSCALLS 5 7.12 7.13 #ifndef ASM 7.14 void init_syscall(void);
8.1 --- a/src/test_proc.S Mon Aug 15 06:17:58 2011 +0300 8.2 +++ b/src/test_proc.S Tue Aug 16 03:26:53 2011 +0300 8.3 @@ -4,16 +4,28 @@ 8.4 .text 8.5 .globl test_proc 8.6 test_proc: 8.7 + /* fork another process */ 8.8 + movl $SYS_FORK, %eax 8.9 + int $SYSCALL_INT 8.10 + 8.11 +infloop: 8.12 /* --- print a message --- */ 8.13 movl $SYS_HELLO, %eax 8.14 int $SYSCALL_INT 8.15 8.16 + /* --- call getpid --- */ 8.17 + movl $SYS_GETPID, %eax 8.18 + int $SYSCALL_INT 8.19 + /* copy the pid to ebx to use as a sleep interval */ 8.20 + movl %eax, %ebx 8.21 + shl $1, %ebx 8.22 + 8.23 /* --- sleep for 5 seconds --- */ 8.24 movl $SYS_SLEEP, %eax 8.25 - movl $3, %ebx 8.26 + /*movl $3, %ebx*/ 8.27 int $SYSCALL_INT 8.28 8.29 - jmp test_proc 8.30 + jmp infloop 8.31 8.32 .globl test_proc_end 8.33 test_proc_end:
9.1 --- a/src/timer.c Mon Aug 15 06:17:58 2011 +0300 9.2 +++ b/src/timer.c Tue Aug 16 03:26:53 2011 +0300 9.3 @@ -136,6 +136,7 @@ 9.4 static void intr_handler(int inum) 9.5 { 9.6 int istate; 9.7 + struct process *p; 9.8 9.9 nticks++; 9.10 9.11 @@ -159,7 +160,12 @@ 9.12 } 9.13 } 9.14 9.15 - /* call the scheduler to decide if it's time to switch processes */ 9.16 + /* decrement the process' ticks_left and call the scheduler to decide if 9.17 + * it's time to switch processes 9.18 + */ 9.19 + if((p = get_current_proc())) { 9.20 + p->ticks_left--; 9.21 + } 9.22 schedule(); 9.23 9.24 set_intr_state(istate);
10.1 --- a/src/vm.c Mon Aug 15 06:17:58 2011 +0300 10.2 +++ b/src/vm.c Tue Aug 16 03:26:53 2011 +0300 10.3 @@ -566,7 +566,6 @@ 10.4 /*printf("free_node\n");*/ 10.5 } 10.6 10.7 -#if 0 10.8 /* clone_vm makes a copy of the current page tables, thus duplicating the 10.9 * virtual address space. 10.10 * 10.11 @@ -574,11 +573,14 @@ 10.12 * we don't want to diplicate the page tables, just point all page directory 10.13 * entries to the same set of page tables. 10.14 * 10.15 + * If "cow" is non-zero it also marks the shared user-space pages as 10.16 + * read-only, to implement copy-on-write. 10.17 + * 10.18 * Returns the physical address of the new page directory. 10.19 */ 10.20 -uint32_t clone_vm(void) 10.21 +uint32_t clone_vm(int cow) 10.22 { 10.23 - int i, dirpg, tblpg, kstart_dirent; 10.24 + int i, j, dirpg, tblpg, kstart_dirent; 10.25 uint32_t paddr; 10.26 uint32_t *ndir, *ntbl; 10.27 10.28 @@ -597,7 +599,8 @@ 10.29 ntbl = (uint32_t*)PAGE_TO_ADDR(tblpg); 10.30 10.31 /* we will allocate physical pages and map them to this virtual page 10.32 - * as needed in the loop below. 10.33 + * as needed in the loop below. we don't need the physical page allocated 10.34 + * by pgalloc. 10.35 */ 10.36 free_phys_page(virt_to_phys((uint32_t)ntbl)); 10.37 10.38 @@ -606,10 +609,18 @@ 10.39 /* user space */ 10.40 for(i=0; i<kstart_dirent; i++) { 10.41 if(pgdir[i] & PG_PRESENT) { 10.42 + /* first go through all the entries of the existing 10.43 + * page table and unset the writable bits. 10.44 + */ 10.45 + for(j=0; j<1024; j++) { 10.46 + PGTBL(i)[j] &= ~(uint32_t)PG_WRITABLE; 10.47 + } 10.48 + 10.49 + /* allocate a page table for the clone */ 10.50 paddr = alloc_phys_page(); 10.51 - map_page(tblpg, ADDR_TO_PAGE(paddr), PG_USER | PG_WRITABLE); 10.52 10.53 /* copy the page table */ 10.54 + map_page(tblpg, ADDR_TO_PAGE(paddr), 0); 10.55 memcpy(ntbl, PGTBL(i), PGSIZE); 10.56 10.57 /* set the new page directory entry */ 10.58 @@ -624,9 +635,12 @@ 10.59 ndir[i] = pgdir[i]; 10.60 } 10.61 10.62 + /* we just changed all the page protection bits, so we need to flush the TLB */ 10.63 + flush_tlb(); 10.64 + 10.65 paddr = virt_to_phys((uint32_t)ndir); 10.66 10.67 - /* unmap before freeing to avoid deallocating the physical pages */ 10.68 + /* unmap before freeing the virtual pages, to avoid deallocating the physical pages */ 10.69 unmap_page(dirpg); 10.70 unmap_page(tblpg); 10.71 10.72 @@ -635,7 +649,50 @@ 10.73 10.74 return paddr; 10.75 } 10.76 -#endif 10.77 + 10.78 +int get_page_bit(int pgnum, uint32_t bit, int wholepath) 10.79 +{ 10.80 + int tidx = PAGE_TO_PGTBL(pgnum); 10.81 + int tent = PAGE_TO_PGTBL_PG(pgnum); 10.82 + uint32_t *pgtbl = PGTBL(tidx); 10.83 + 10.84 + if(wholepath) { 10.85 + if((pgdir[tidx] & bit) == 0) { 10.86 + return 0; 10.87 + } 10.88 + } 10.89 + 10.90 + return pgtbl[tent] & bit; 10.91 +} 10.92 + 10.93 +void set_page_bit(int pgnum, uint32_t bit, int wholepath) 10.94 +{ 10.95 + int tidx = PAGE_TO_PGTBL(pgnum); 10.96 + int tent = PAGE_TO_PGTBL_PG(pgnum); 10.97 + uint32_t *pgtbl = PGTBL(tidx); 10.98 + 10.99 + if(wholepath) { 10.100 + pgdir[tidx] |= bit; 10.101 + } 10.102 + pgtbl[tent] |= bit; 10.103 + 10.104 + flush_tlb_page(pgnum); 10.105 +} 10.106 + 10.107 +void clear_page_bit(int pgnum, uint32_t bit, int wholepath) 10.108 +{ 10.109 + int tidx = PAGE_TO_PGTBL(pgnum); 10.110 + int tent = PAGE_TO_PGTBL_PG(pgnum); 10.111 + uint32_t *pgtbl = PGTBL(tidx); 10.112 + 10.113 + if(wholepath) { 10.114 + pgdir[tidx] &= ~bit; 10.115 + } 10.116 + 10.117 + pgtbl[tent] &= ~bit; 10.118 + 10.119 + flush_tlb_page(pgnum); 10.120 +} 10.121 10.122 10.123 void dbg_print_vm(int area)
11.1 --- a/src/vm.h Mon Aug 15 06:17:58 2011 +0300 11.2 +++ b/src/vm.h Tue Aug 16 03:26:53 2011 +0300 11.3 @@ -36,6 +36,13 @@ 11.4 #define PAGE_TO_PGTBL(x) ((uint32_t)(x) >> 10) 11.5 #define PAGE_TO_PGTBL_PG(x) ((uint32_t)(x) & 0x3ff) 11.6 11.7 +/* argument to clone_vm */ 11.8 +#define CLONE_SHARED 0 11.9 +#define CLONE_COW 1 11.10 + 11.11 +/* last argument to *_page_bit */ 11.12 +#define PAGE_ONLY 0 11.13 +#define WHOLE_PATH 1 11.14 11.15 void init_vm(void); 11.16 11.17 @@ -57,7 +64,11 @@ 11.18 int pgalloc_vrange(int start, int num); 11.19 void pgfree(int start, int num); 11.20 11.21 -uint32_t clone_vm(void); 11.22 +uint32_t clone_vm(int cow); 11.23 + 11.24 +int get_page_bit(int pgnum, uint32_t bit, int wholepath); 11.25 +void set_page_bit(int pgnum, uint32_t bit, int wholepath); 11.26 +void clear_page_bit(int pgnum, uint32_t bit, int wholepath); 11.27 11.28 void dbg_print_vm(int area); 11.29