kern

changeset 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
files src/asmops.h src/config.h src/intr-asm.S src/intr.h src/proc-asm.S src/proc.c src/proc.h src/segm-asm.S src/segm.c src/segm.h src/syscall.c
diffstat 11 files changed, 156 insertions(+), 60 deletions(-) [+]
line diff
     1.1 --- a/src/asmops.h	Mon Aug 08 09:53:10 2011 +0300
     1.2 +++ b/src/asmops.h	Sun Aug 14 16:57:23 2011 +0300
     1.3 @@ -13,6 +13,9 @@
     1.4  #define disable_intr() asm volatile("cli")
     1.5  #define halt_cpu() asm volatile("hlt")
     1.6  
     1.7 +#define push_regs() asm volatile("pusha");
     1.8 +#define pop_regs() asm volatile("popa");
     1.9 +
    1.10  #define inb(dest, port) asm volatile( \
    1.11  	"inb %1, %0\n\t" \
    1.12  	: "=a" ((unsigned char)(dest)) \
    1.13 @@ -42,4 +45,5 @@
    1.14  
    1.15  #define iodelay() outb(0, 0x80)
    1.16  
    1.17 +
    1.18  #endif	/* ASMOPS_H_ */
     2.1 --- a/src/config.h	Mon Aug 08 09:53:10 2011 +0300
     2.2 +++ b/src/config.h	Sun Aug 14 16:57:23 2011 +0300
     2.3 @@ -10,4 +10,7 @@
     2.4  /* allow automatic user stack growth by at most 1024 pages at a time (4mb) */
     2.5  #define USTACK_MAXGROW		1024
     2.6  
     2.7 +/* per-process kernel stack size (2 pages) */
     2.8 +#define KERN_STACK_SIZE		8192
     2.9 +
    2.10  #endif	/* _CONFIG_H_ */
     3.1 --- a/src/intr-asm.S	Mon Aug 08 09:53:10 2011 +0300
     3.2 +++ b/src/intr-asm.S	Sun Aug 14 16:57:23 2011 +0300
     3.3 @@ -1,3 +1,6 @@
     3.4 +#define ASM
     3.5 +#include "segm.h"
     3.6 +
     3.7  	.data
     3.8  	.align 4
     3.9  	.short 0
    3.10 @@ -68,10 +71,29 @@
    3.11   */
    3.12  	.extern dispatch_intr
    3.13  intr_entry_common:
    3.14 +	/* save the current data segment selectors */
    3.15 +	pushl %gs
    3.16 +	pushl %fs
    3.17 +	pushl %es
    3.18 +	pushl %ds
    3.19 +	/* save general purpose registers */
    3.20  	pusha
    3.21 +	/* if we entered from userspace ss and cs is set correctly, but
    3.22 +     * we must make sure all the other selectors are set to the
    3.23 +     * kernel data segment */
    3.24 +	mov $SEGM_KDATA, %ds
    3.25 +	mov $SEGM_KDATA, %es
    3.26 +	mov $SEGM_KDATA, %fs
    3.27 +	mov $SEGM_KDATA, %gs
    3.28  	call dispatch_intr
    3.29  intr_ret_local:
    3.30 +	/* restore general purpose registers */
    3.31  	popa
    3.32 +	/* restore data segment selectors */
    3.33 +	popl %ds
    3.34 +	popl %es
    3.35 +	popl %fs
    3.36 +	popl %gs
    3.37  	/* remove error code and intr num from stack */
    3.38  	add $8, %esp
    3.39  	iret
    3.40 @@ -93,5 +115,4 @@
    3.41  /* by including interrupts.h with ASM defined, the macros above
    3.42   * are expanded to generate all required interrupt entry points
    3.43   */
    3.44 -#define ASM
    3.45  #include <interrupts.h>
     4.1 --- a/src/intr.h	Mon Aug 08 09:53:10 2011 +0300
     4.2 +++ b/src/intr.h	Sun Aug 14 16:57:23 2011 +0300
     4.3 @@ -18,6 +18,8 @@
     4.4  struct intr_frame {
     4.5  	/* registers pushed by pusha in intr_entry_* */
     4.6  	struct registers regs;
     4.7 +	/* data segment selectors */
     4.8 +	uint32_t ds, es, fs, gs;
     4.9  	/* interrupt number and error code pushed in intr_entry_* */
    4.10  	uint32_t inum, err;
    4.11  	/* pushed by CPU during interrupt entry */
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/src/proc-asm.S	Sun Aug 14 16:57:23 2011 +0300
     5.3 @@ -0,0 +1,9 @@
     5.4 +	.text
     5.5 +	/* switch_stack(uint32_t new_stack)
     5.6 +     * switches to the new stack and returns the old stack pointer
     5.7 +     */
     5.8 +	.globl switch_stack
     5.9 +switch_stack:
    5.10 +	movl %esp, %eax
    5.11 +	movl 4(%esp), %esp
    5.12 +	ret
     6.1 --- a/src/proc.c	Mon Aug 08 09:53:10 2011 +0300
     6.2 +++ b/src/proc.c	Sun Aug 14 16:57:23 2011 +0300
     6.3 @@ -9,9 +9,13 @@
     6.4  #include "panic.h"
     6.5  #include "syscall.h"
     6.6  #include "sched.h"
     6.7 +#include "tss.h"
     6.8  
     6.9  #define	FLAGS_INTR_BIT	9
    6.10  
    6.11 +static void start_first_proc(void);
    6.12 +
    6.13 +
    6.14  /* defined in test_proc.S */
    6.15  void test_proc(void);
    6.16  void test_proc_end(void);
    6.17 @@ -19,90 +23,125 @@
    6.18  static struct process proc[MAX_PROC];
    6.19  static int cur_pid;
    6.20  
    6.21 +static struct task_state *tss;
    6.22 +
    6.23 +
    6.24  void init_proc(void)
    6.25  {
    6.26 -	int proc_size_pg, img_start_pg, stack_pg;
    6.27 -	void *img_start;
    6.28 -	cur_pid = 0;
    6.29 +	int tss_page;
    6.30  
    6.31 +	/* allocate a page for the task state segment, to make sure
    6.32 +	 * it doesn't cross page boundaries
    6.33 +	 */
    6.34 +	if((tss_page = pgalloc(1, MEM_KERNEL)) == -1) {
    6.35 +		panic("failed to allocate memory for the task state segment\n");
    6.36 +	}
    6.37 +	tss = (struct tss*)PAGE_TO_ADDR(tss_page);
    6.38 +
    6.39 +	/* the kernel stack segment never changes so we might as well set it now
    6.40 +	 * the only other thing that we use in the tss is the kernel stack pointer
    6.41 +	 * which is different for each process, and thus managed by context_switch
    6.42 +	 */
    6.43 +	memset(tss, 0, sizeof *tss);
    6.44 +	tss->ss0 = selector(SEGM_KDATA, 0);
    6.45 +
    6.46 +	set_tss((uint32_t)virt_to_phys(tss));
    6.47 +
    6.48 +	/* initialize system call handler (see syscall.c) */
    6.49  	init_syscall();
    6.50  
    6.51 +	start_first_proc(); /* XXX never returns */
    6.52 +}
    6.53 +
    6.54 +
    6.55 +static void start_first_proc(void)
    6.56 +{
    6.57 +	struct process *p;
    6.58 +	int proc_size_pg, img_start_pg, stack_pg;
    6.59 +	uint32_t img_start_addr, ustack_addr;
    6.60 +	struct intr_frame ifrm;
    6.61 +
    6.62  	/* prepare the first process */
    6.63 -	proc[1].id = 1;
    6.64 -	proc[1].parent = 0;
    6.65 +	p = proc + 1;
    6.66 +	p->id = 1;
    6.67 +	p->parent = 0;	/* no parent for init */
    6.68  
    6.69  	/* allocate a chunk of memory for the process image
    6.70  	 * and copy the code of test_proc there.
    6.71 -	 * (should be mapped at a fixed address)
    6.72  	 */
    6.73  	proc_size_pg = (test_proc_end - test_proc) / PGSIZE + 1;
    6.74  	if((img_start_pg = pgalloc(proc_size_pg, MEM_USER)) == -1) {
    6.75  		panic("failed to allocate space for the init process image\n");
    6.76  	}
    6.77 -	img_start = (void*)PAGE_TO_ADDR(img_start_pg);
    6.78 -	memcpy(img_start, test_proc, proc_size_pg * PGSIZE);
    6.79 -
    6.80 -	printf("copied init process at: %x\n", (unsigned int)img_start);
    6.81 -
    6.82 -	/* instruction pointer at the beginning of the process image */
    6.83 -	proc[1].ctx.instr_ptr = (uint32_t)img_start;
    6.84 +	img_start_addr = PAGE_TO_ADDR(img_start_pg);
    6.85 +	memcpy((void*)img_start_addr, test_proc, proc_size_pg * PGSIZE);
    6.86 +	printf("copied init process at: %x\n", img_start_addr);
    6.87  
    6.88  	/* allocate the first page of the process stack */
    6.89  	stack_pg = ADDR_TO_PAGE(KMEM_START) - 1;
    6.90  	if(pgalloc_vrange(stack_pg, 1) == -1) {
    6.91  		panic("failed to allocate user stack page\n");
    6.92  	}
    6.93 -	proc[1].ctx.stack_ptr = PAGE_TO_ADDR(stack_pg) + PGSIZE;
    6.94 -	proc[1].stack_start_pg = stack_pg;
    6.95 +	p->user_stack_pg = stack_pg;
    6.96  
    6.97 -	/* create the virtual address space for this process */
    6.98 -	/*proc[1].ctx.pgtbl_paddr = clone_vm();*/
    6.99 +	/* allocate a kernel stack for this process */
   6.100 +	if((p->kern_stack_pg = pgalloc(KERN_STACK_SIZE / PGSIZE, MEM_KERNEL)) == -1) {
   6.101 +		panic("failed to allocate kernel stack for the init process\n");
   6.102 +	}
   6.103 +	/* when switching from user space to kernel space, the ss0:esp0 from TSS
   6.104 +	 * will be used to switch to the per-process kernel stack, so we need to
   6.105 +	 * set it correctly before switching to user space.
   6.106 +	 * tss->ss0 is already set in init_proc above.
   6.107 +	 */
   6.108 +	tss->esp0 = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE;
   6.109  
   6.110 -	/* we don't need the image and the stack in this address space */
   6.111 -	/*unmap_page_range(img_start_pg, proc_size_pg);
   6.112 -	pgfree(img_start_pg, proc_size_pg);
   6.113  
   6.114 -	unmap_page(stack_pg);
   6.115 -	pgfree(stack_pg, 1);*/
   6.116 +	/* now we need to fill in the fake interrupt stack frame */
   6.117 +	memset(&ifrm, 0, sizeof ifrm);
   6.118 +	/* after the priviledge switch, this ss:esp will be used in userspace */
   6.119 +	ifrm.esp = PAGE_TO_ADDR(stack_pg) + PGSIZE;
   6.120 +	ifrm.ss = selector(SEGM_UDATA, 3);
   6.121 +	/* instruction pointer at the beginning of the process image */
   6.122 +	ifrm.regs.eip = img_start_addr;
   6.123 +	ifrm.cs = selector(SEGM_UCODE, 3);
   6.124 +	/* make sure the user will run with interrupts enabled */
   6.125 +	ifrm.eflags = FLAGS_INTR_BIT;
   6.126 +	/* user data selectors should all be the same */
   6.127 +	ifrm.ds = ifrm.es = ifrm.fs = ifrm.gs = ifrm.ss;
   6.128  
   6.129  	/* add it to the scheduler queues */
   6.130 -	add_proc(1, STATE_RUNNABLE);
   6.131 +	add_proc(p->id, STATE_RUNNABLE);
   6.132  
   6.133 -	/* switch to the initial process, this never returns */
   6.134 -	context_switch(1);
   6.135 +	/* execute a fake return from interrupt with the fake stack frame */
   6.136 +	intr_ret(ifrm);
   6.137  }
   6.138  
   6.139  
   6.140  void context_switch(int pid)
   6.141  {
   6.142 -	struct context *ctx;
   6.143 -	struct intr_frame ifrm;
   6.144 -	struct intr_frame *cur_frame = get_intr_frame();
   6.145 -
   6.146 -	assert(0);
   6.147 +	struct process *prev, *new;
   6.148  
   6.149  	if(cur_pid == pid) {
   6.150 -		assert(cur_frame);
   6.151 -		intr_ret(*cur_frame);
   6.152 +		return;	/* nothing to be done */
   6.153  	}
   6.154 +	prev = proc + cur_pid;
   6.155 +	new = proc + pid;
   6.156  
   6.157 -	ctx = &proc[pid].ctx;
   6.158 -	cur_pid = pid;
   6.159 +	/* push all registers onto the stack before switching stacks */
   6.160 +	push_regs();
   6.161  
   6.162 -	ifrm.inum = ifrm.err = 0;
   6.163 +	prev->ctx.stack_ptr = switch_stack(new->ctx.stack_ptr);
   6.164  
   6.165 -	ifrm.regs = ctx->regs;
   6.166 -	ifrm.eflags = ctx->flags | (1 << FLAGS_INTR_BIT);
   6.167 +	/* restore registers from the new stack */
   6.168 +	pop_regs();
   6.169  
   6.170 -	ifrm.eip = ctx->instr_ptr;
   6.171 -	ifrm.cs = selector(SEGM_KCODE, 0);	/* XXX change this when we setup the TSS */
   6.172 -	/*ifrm.regs.esp = ctx->stack_ptr;*/		/* ... until then... */
   6.173 -	ifrm.esp = ctx->stack_ptr;			/* this will only be used when we switch to userspace */
   6.174 -	ifrm.ss = selector(SEGM_KDATA, 0);
   6.175 +	/* switch to the new process' address space */
   6.176 +	set_pgdir_addr(new->ctx.pgtbl_paddr);
   6.177  
   6.178 -	/* switch to the vm of the process */
   6.179 -	set_pgdir_addr(ctx->pgtbl_paddr);
   6.180 -	intr_ret(ifrm);
   6.181 +	/* make sure we'll return to the correct kernel stack next time
   6.182 +	 * we enter from userspace
   6.183 +	 */
   6.184 +	tss->esp0 = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE;
   6.185  }
   6.186  
   6.187  int get_current_pid(void)
     7.1 --- a/src/proc.h	Mon Aug 08 09:53:10 2011 +0300
     7.2 +++ b/src/proc.h	Sun Aug 14 16:57:23 2011 +0300
     7.3 @@ -7,10 +7,10 @@
     7.4  #define MAX_PROC	128
     7.5  
     7.6  struct context {
     7.7 -	struct registers regs;	/* saved general purpose registers */
     7.8 -	uint32_t instr_ptr;		/* saved eip */
     7.9 +	/*struct registers regs;*/	/* saved general purpose registers */
    7.10 +	/*uint32_t instr_ptr;*/		/* saved eip */
    7.11  	uint32_t stack_ptr;		/* saved esp */
    7.12 -	uint32_t flags;			/* saved eflags */
    7.13 +	/*uint32_t flags;*/			/* saved eflags */
    7.14  	uint32_t pgtbl_paddr;	/* physical address of the page table */
    7.15  	/* TODO add FPU state */
    7.16  };
    7.17 @@ -31,7 +31,9 @@
    7.18  	/* extends of the process heap, increased by sbrk */
    7.19  
    7.20  	/* first page of the user stack, extends up to KMEM_START */
    7.21 -	int stack_start_pg;
    7.22 +	int user_stack_pg;
    7.23 +	/* first page of the kernel stack, (KERN_STACK_SIZE) */
    7.24 +	int kern_stack_pg;
    7.25  
    7.26  	struct context ctx;
    7.27  
     8.1 --- a/src/segm-asm.S	Mon Aug 08 09:53:10 2011 +0300
     8.2 +++ b/src/segm-asm.S	Sun Aug 14 16:57:23 2011 +0300
     8.3 @@ -37,3 +37,10 @@
     8.4  	movw %ax, (lim)
     8.5  	lgdt (lim)	
     8.6  	ret
     8.7 +
     8.8 +/* set_task_reg(uint16_t tss_selector)
     8.9 + * loads the TSS selector in the task register */
    8.10 +	.globl set_task_reg
    8.11 +set_task_reg:
    8.12 +	ltr 4(%esp)
    8.13 +	ret
     9.1 --- a/src/segm.c	Mon Aug 08 09:53:10 2011 +0300
     9.2 +++ b/src/segm.c	Sun Aug 14 16:57:23 2011 +0300
     9.3 @@ -21,8 +21,7 @@
     9.4  
     9.5  enum {TYPE_DATA, TYPE_CODE};
     9.6  
     9.7 -#define TSS_TYPE_BITS	0x900
     9.8 -#define TSS_BUSY		BIT_BUSY
     9.9 +#define TSS_TYPE_BITS	(BIT_ACCESSED | BIT_CODE)
    9.10  
    9.11  static void segm_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type);
    9.12  static void task_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, unsigned int busy);
    9.13 @@ -30,6 +29,7 @@
    9.14  /* these functions are implemented in segm-asm.S */
    9.15  void setup_selectors(uint16_t code, uint16_t data);
    9.16  void set_gdt(uint32_t addr, uint16_t limit);
    9.17 +void set_task_reg(uint16_t tss_selector);
    9.18  
    9.19  
    9.20  /* our global descriptor table */
    9.21 @@ -43,7 +43,6 @@
    9.22  	segm_desc(gdt + SEGM_KDATA, 0, 0xffffffff, 0, TYPE_DATA);
    9.23  	segm_desc(gdt + SEGM_UCODE, 0, 0xffffffff, 3, TYPE_CODE);
    9.24  	segm_desc(gdt + SEGM_UDATA, 0, 0xffffffff, 3, TYPE_DATA);
    9.25 -	task_desc(gdt + SEGM_TASK, 0, 0xffffffff, 3, TSS_BUSY);
    9.26  
    9.27  	set_gdt((uint32_t)gdt, sizeof gdt - 1);
    9.28  
    9.29 @@ -56,6 +55,12 @@
    9.30  	return (idx << 3) | (rpl & 3);
    9.31  }
    9.32  
    9.33 +void set_tss(uint32_t addr)
    9.34 +{
    9.35 +	task_desc(gdt + SEGM_TASK, 0, sizeof(struct tss) - 1, 3, TSS_BUSY);
    9.36 +	set_task_reg(selector(SEGM_TASK, 0));
    9.37 +}
    9.38 +
    9.39  static void segm_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type)
    9.40  {
    9.41  	desc->d[0] = limit & 0xffff; /* low order 16bits of limit */
    10.1 --- a/src/segm.h	Mon Aug 08 09:53:10 2011 +0300
    10.2 +++ b/src/segm.h	Sun Aug 14 16:57:23 2011 +0300
    10.3 @@ -7,9 +7,13 @@
    10.4  #define SEGM_UDATA	4
    10.5  #define SEGM_TASK	5
    10.6  
    10.7 +#ifndef ASM
    10.8  void init_segm(void);
    10.9  
   10.10  uint16_t selector(int idx, int rpl);
   10.11  
   10.12 +void set_tss(uint32_t addr);
   10.13 +#endif	/* ASM */
   10.14 +
   10.15  
   10.16  #endif	/* SEGM_H_ */
    11.1 --- a/src/syscall.c	Mon Aug 08 09:53:10 2011 +0300
    11.2 +++ b/src/syscall.c	Sun Aug 14 16:57:23 2011 +0300
    11.3 @@ -35,26 +35,26 @@
    11.4  		return;
    11.5  	}
    11.6  
    11.7 +	/* the return value goes into the interrupt frame copy of the user's eax
    11.8 +	 * so that it'll be restored into eax before returning to userland.
    11.9 +	 */
   11.10  	frm->regs.eax = sys_func[idx](frm->regs.ebx, frm->regs.ecx, frm->regs.edx, frm->regs.esi, frm->regs.edi);
   11.11 -	schedule();
   11.12  }
   11.13  
   11.14  static int sys_exit(int status)
   11.15  {
   11.16 +	printf("SYSCALL: exit\n");
   11.17  	return -1;	/* not implemented yet */
   11.18  }
   11.19  
   11.20  static int sys_hello(void)
   11.21  {
   11.22 -	/*printf("process %d says hello!\n", get_current_pid());*/
   11.23 +	printf("process %d says hello!\n", get_current_pid());
   11.24  	return 0;
   11.25  }
   11.26  
   11.27  static int sys_sleep(int sec)
   11.28  {
   11.29 -	int pid = get_current_pid();
   11.30 -	/*printf("process %d will sleep for %d sec\n", pid, sec);*/
   11.31 -	start_timer(sec * 1000, (timer_func_t)unblock_proc, (void*)pid);
   11.32 -	block_proc(pid);
   11.33 -	return 0;
   11.34 +	printf("SYSCALL: sleep\n");
   11.35 +	return -1;
   11.36  }