kern

diff src/proc.c @ 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
line diff
     1.1 --- a/src/proc.c	Mon Aug 08 09:53:10 2011 +0300
     1.2 +++ b/src/proc.c	Sun Aug 14 16:57:23 2011 +0300
     1.3 @@ -9,9 +9,13 @@
     1.4  #include "panic.h"
     1.5  #include "syscall.h"
     1.6  #include "sched.h"
     1.7 +#include "tss.h"
     1.8  
     1.9  #define	FLAGS_INTR_BIT	9
    1.10  
    1.11 +static void start_first_proc(void);
    1.12 +
    1.13 +
    1.14  /* defined in test_proc.S */
    1.15  void test_proc(void);
    1.16  void test_proc_end(void);
    1.17 @@ -19,90 +23,125 @@
    1.18  static struct process proc[MAX_PROC];
    1.19  static int cur_pid;
    1.20  
    1.21 +static struct task_state *tss;
    1.22 +
    1.23 +
    1.24  void init_proc(void)
    1.25  {
    1.26 -	int proc_size_pg, img_start_pg, stack_pg;
    1.27 -	void *img_start;
    1.28 -	cur_pid = 0;
    1.29 +	int tss_page;
    1.30  
    1.31 +	/* allocate a page for the task state segment, to make sure
    1.32 +	 * it doesn't cross page boundaries
    1.33 +	 */
    1.34 +	if((tss_page = pgalloc(1, MEM_KERNEL)) == -1) {
    1.35 +		panic("failed to allocate memory for the task state segment\n");
    1.36 +	}
    1.37 +	tss = (struct tss*)PAGE_TO_ADDR(tss_page);
    1.38 +
    1.39 +	/* the kernel stack segment never changes so we might as well set it now
    1.40 +	 * the only other thing that we use in the tss is the kernel stack pointer
    1.41 +	 * which is different for each process, and thus managed by context_switch
    1.42 +	 */
    1.43 +	memset(tss, 0, sizeof *tss);
    1.44 +	tss->ss0 = selector(SEGM_KDATA, 0);
    1.45 +
    1.46 +	set_tss((uint32_t)virt_to_phys(tss));
    1.47 +
    1.48 +	/* initialize system call handler (see syscall.c) */
    1.49  	init_syscall();
    1.50  
    1.51 +	start_first_proc(); /* XXX never returns */
    1.52 +}
    1.53 +
    1.54 +
    1.55 +static void start_first_proc(void)
    1.56 +{
    1.57 +	struct process *p;
    1.58 +	int proc_size_pg, img_start_pg, stack_pg;
    1.59 +	uint32_t img_start_addr, ustack_addr;
    1.60 +	struct intr_frame ifrm;
    1.61 +
    1.62  	/* prepare the first process */
    1.63 -	proc[1].id = 1;
    1.64 -	proc[1].parent = 0;
    1.65 +	p = proc + 1;
    1.66 +	p->id = 1;
    1.67 +	p->parent = 0;	/* no parent for init */
    1.68  
    1.69  	/* allocate a chunk of memory for the process image
    1.70  	 * and copy the code of test_proc there.
    1.71 -	 * (should be mapped at a fixed address)
    1.72  	 */
    1.73  	proc_size_pg = (test_proc_end - test_proc) / PGSIZE + 1;
    1.74  	if((img_start_pg = pgalloc(proc_size_pg, MEM_USER)) == -1) {
    1.75  		panic("failed to allocate space for the init process image\n");
    1.76  	}
    1.77 -	img_start = (void*)PAGE_TO_ADDR(img_start_pg);
    1.78 -	memcpy(img_start, test_proc, proc_size_pg * PGSIZE);
    1.79 -
    1.80 -	printf("copied init process at: %x\n", (unsigned int)img_start);
    1.81 -
    1.82 -	/* instruction pointer at the beginning of the process image */
    1.83 -	proc[1].ctx.instr_ptr = (uint32_t)img_start;
    1.84 +	img_start_addr = PAGE_TO_ADDR(img_start_pg);
    1.85 +	memcpy((void*)img_start_addr, test_proc, proc_size_pg * PGSIZE);
    1.86 +	printf("copied init process at: %x\n", img_start_addr);
    1.87  
    1.88  	/* allocate the first page of the process stack */
    1.89  	stack_pg = ADDR_TO_PAGE(KMEM_START) - 1;
    1.90  	if(pgalloc_vrange(stack_pg, 1) == -1) {
    1.91  		panic("failed to allocate user stack page\n");
    1.92  	}
    1.93 -	proc[1].ctx.stack_ptr = PAGE_TO_ADDR(stack_pg) + PGSIZE;
    1.94 -	proc[1].stack_start_pg = stack_pg;
    1.95 +	p->user_stack_pg = stack_pg;
    1.96  
    1.97 -	/* create the virtual address space for this process */
    1.98 -	/*proc[1].ctx.pgtbl_paddr = clone_vm();*/
    1.99 +	/* allocate a kernel stack for this process */
   1.100 +	if((p->kern_stack_pg = pgalloc(KERN_STACK_SIZE / PGSIZE, MEM_KERNEL)) == -1) {
   1.101 +		panic("failed to allocate kernel stack for the init process\n");
   1.102 +	}
   1.103 +	/* when switching from user space to kernel space, the ss0:esp0 from TSS
   1.104 +	 * will be used to switch to the per-process kernel stack, so we need to
   1.105 +	 * set it correctly before switching to user space.
   1.106 +	 * tss->ss0 is already set in init_proc above.
   1.107 +	 */
   1.108 +	tss->esp0 = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE;
   1.109  
   1.110 -	/* we don't need the image and the stack in this address space */
   1.111 -	/*unmap_page_range(img_start_pg, proc_size_pg);
   1.112 -	pgfree(img_start_pg, proc_size_pg);
   1.113  
   1.114 -	unmap_page(stack_pg);
   1.115 -	pgfree(stack_pg, 1);*/
   1.116 +	/* now we need to fill in the fake interrupt stack frame */
   1.117 +	memset(&ifrm, 0, sizeof ifrm);
   1.118 +	/* after the priviledge switch, this ss:esp will be used in userspace */
   1.119 +	ifrm.esp = PAGE_TO_ADDR(stack_pg) + PGSIZE;
   1.120 +	ifrm.ss = selector(SEGM_UDATA, 3);
   1.121 +	/* instruction pointer at the beginning of the process image */
   1.122 +	ifrm.regs.eip = img_start_addr;
   1.123 +	ifrm.cs = selector(SEGM_UCODE, 3);
   1.124 +	/* make sure the user will run with interrupts enabled */
   1.125 +	ifrm.eflags = FLAGS_INTR_BIT;
   1.126 +	/* user data selectors should all be the same */
   1.127 +	ifrm.ds = ifrm.es = ifrm.fs = ifrm.gs = ifrm.ss;
   1.128  
   1.129  	/* add it to the scheduler queues */
   1.130 -	add_proc(1, STATE_RUNNABLE);
   1.131 +	add_proc(p->id, STATE_RUNNABLE);
   1.132  
   1.133 -	/* switch to the initial process, this never returns */
   1.134 -	context_switch(1);
   1.135 +	/* execute a fake return from interrupt with the fake stack frame */
   1.136 +	intr_ret(ifrm);
   1.137  }
   1.138  
   1.139  
   1.140  void context_switch(int pid)
   1.141  {
   1.142 -	struct context *ctx;
   1.143 -	struct intr_frame ifrm;
   1.144 -	struct intr_frame *cur_frame = get_intr_frame();
   1.145 -
   1.146 -	assert(0);
   1.147 +	struct process *prev, *new;
   1.148  
   1.149  	if(cur_pid == pid) {
   1.150 -		assert(cur_frame);
   1.151 -		intr_ret(*cur_frame);
   1.152 +		return;	/* nothing to be done */
   1.153  	}
   1.154 +	prev = proc + cur_pid;
   1.155 +	new = proc + pid;
   1.156  
   1.157 -	ctx = &proc[pid].ctx;
   1.158 -	cur_pid = pid;
   1.159 +	/* push all registers onto the stack before switching stacks */
   1.160 +	push_regs();
   1.161  
   1.162 -	ifrm.inum = ifrm.err = 0;
   1.163 +	prev->ctx.stack_ptr = switch_stack(new->ctx.stack_ptr);
   1.164  
   1.165 -	ifrm.regs = ctx->regs;
   1.166 -	ifrm.eflags = ctx->flags | (1 << FLAGS_INTR_BIT);
   1.167 +	/* restore registers from the new stack */
   1.168 +	pop_regs();
   1.169  
   1.170 -	ifrm.eip = ctx->instr_ptr;
   1.171 -	ifrm.cs = selector(SEGM_KCODE, 0);	/* XXX change this when we setup the TSS */
   1.172 -	/*ifrm.regs.esp = ctx->stack_ptr;*/		/* ... until then... */
   1.173 -	ifrm.esp = ctx->stack_ptr;			/* this will only be used when we switch to userspace */
   1.174 -	ifrm.ss = selector(SEGM_KDATA, 0);
   1.175 +	/* switch to the new process' address space */
   1.176 +	set_pgdir_addr(new->ctx.pgtbl_paddr);
   1.177  
   1.178 -	/* switch to the vm of the process */
   1.179 -	set_pgdir_addr(ctx->pgtbl_paddr);
   1.180 -	intr_ret(ifrm);
   1.181 +	/* make sure we'll return to the correct kernel stack next time
   1.182 +	 * we enter from userspace
   1.183 +	 */
   1.184 +	tss->esp0 = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE;
   1.185  }
   1.186  
   1.187  int get_current_pid(void)