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(&regs, 0, sizeof regs);
    1.12  	get_regs(&regs);
    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