From f67c0b7e20ef0816a2d9047fd20346e3ede98b75 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Fri, 13 Feb 2015 17:23:29 +0100 Subject: Prepare for user memory management and usermode code --- Makefile | 21 +++++++++ run_bochs_debug.sh | 3 +- run_qemu.sh | 3 +- run_qemu_debug.sh | 3 +- src/kernel/core/paging.c | 103 +++++++++++++++++++++++++++----------------- src/kernel/core/region.c | 4 +- src/kernel/core/thread.c | 17 +++++--- src/kernel/include/paging.h | 8 +++- src/kernel/include/region.h | 6 +-- src/kernel/include/thread.h | 6 +++ 10 files changed, 118 insertions(+), 56 deletions(-) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8649df2 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +DIRS = src/common src/kernel + +all: + for dir in $(DIRS); do \ + $(MAKE) -C $$dir; \ + done + +rebuild: + for dir in $(DIRS); do \ + $(MAKE) -C $$dir rebuild; \ + done + +clean: + for dir in $(DIRS); do \ + $(MAKE) -C $$dir clean; \ + done + +mrproper: + for dir in $(DIRS); do \ + $(MAKE) -C $$dir mrproper; \ + done diff --git a/run_bochs_debug.sh b/run_bochs_debug.sh index 335b810..7bc7d1c 100755 --- a/run_bochs_debug.sh +++ b/run_bochs_debug.sh @@ -2,7 +2,6 @@ cd `dirname $0` -make -C src/common || exit 1 -make -C src/kernel || exit 1 +make || exit 1 ./make_cdrom.sh bochs -f bochs_debug.cfg -q diff --git a/run_qemu.sh b/run_qemu.sh index 59f1f19..7f7d264 100755 --- a/run_qemu.sh +++ b/run_qemu.sh @@ -2,6 +2,5 @@ cd `dirname $0` -make -C src/common || exit 1 -make -C src/kernel || exit 1 +make || exit 1 qemu-system-i386 -kernel src/kernel/kernel.bin -serial stdio -m 16 diff --git a/run_qemu_debug.sh b/run_qemu_debug.sh index 9e84613..16c0311 100755 --- a/run_qemu_debug.sh +++ b/run_qemu_debug.sh @@ -2,7 +2,6 @@ cd `dirname $0` -make -C src/common || exit 1 -make -C src/kernel || exit 1 +make || exit 1 qemu-system-i386 -kernel src/kernel/kernel.bin -serial stdio -s -S & (sleep 0.1; gdb src/kernel/kernel.bin -x gdb_cmd) diff --git a/src/kernel/core/paging.c b/src/kernel/core/paging.c index 44e00fe..3bc36ea 100644 --- a/src/kernel/core/paging.c +++ b/src/kernel/core/paging.c @@ -53,48 +53,73 @@ void page_fault_handler(registers_t *regs) { void* vaddr; asm volatile("movl %%cr2, %0":"=r"(vaddr)); - if ((size_t)vaddr >= K_HIGHHALF_ADDR) { - uint32_t pt = PT_OF_ADDR(vaddr); + bool is_user = ((regs->err_code & PF_USER_BIT) != 0); - if (current_pd != &kernel_pd && current_pd->page[pt] != kernel_pd.page[pt]) { - current_pd->page[pt] = kernel_pd.page[pt]; - invlpg(¤t_pt[pt]); - return; - } - if (regs->eflags & EFLAGS_IF) asm volatile("sti"); // from now on we are preemptible - - if (vaddr >= (void*)&kernel_stack_protector && vaddr < (void*)&kernel_stack_protector + PAGE_SIZE) { - dbg_printf("Kernel stack overflow at 0x%p\n", vaddr); - PANIC("Kernel stack overflow."); - } + if (is_user) { + if (regs->eflags & EFLAGS_IF) asm volatile("sti"); + // remark : sti should always be executed, it is stupid to run user code with interrupts disabled - if ((size_t)vaddr >= PD_MIRROR_ADDR) { - dbg_printf("Fault on access to mirrorred PD at 0x%p\n", vaddr); - dbg_print_region_info(); - PANIC("Unhandled kernel space page fault"); - } - - region_info_t *i = find_region(vaddr); - if (i == 0) { - dbg_printf("Kernel pagefault in non-existing region at 0x%p\n", vaddr); - dbg_dump_registers(regs); - PANIC("Unhandled kernel space page fault"); + if ((size_t)vaddr >= K_HIGHHALF_ADDR) { + ASSERT(current_thread->kmem_violation_handler != 0); + current_thread->kmem_violation_handler(regs); + } else { + ASSERT(current_thread->usermem_pf_handler != 0); + current_thread->usermem_pf_handler(get_current_pagedir(), regs, vaddr); } - if (i->pf == 0) { - dbg_printf("Kernel pagefault in region with no handler at 0x%p\n", vaddr); + } else { + //TODO: instead of panicing, we should try to recover from the exception (?) + if ((size_t)vaddr < PAGE_SIZE) { + dbg_printf("Null pointer dereference in kernel code (0x%p)\n", vaddr); dbg_dump_registers(regs); - dbg_print_region_info(); - PANIC("Unhandled kernel space page fault"); + PANIC("Null pointer dereference in kernel code."); + } else if ((size_t)vaddr < K_HIGHHALF_ADDR) { + if (current_thread->usermem_pf_handler == 0) { + dbg_printf("Userspace page fault at 0x%p, no handler declared\n", vaddr); + dbg_dump_registers(regs); + PANIC("Unhandled userspace page fault"); + } + + // userspace PF handlers should always be preemptible + if (regs->eflags & EFLAGS_IF) asm volatile("sti"); + current_thread->usermem_pf_handler(get_current_pagedir(), regs, vaddr); + } else { + uint32_t pt = PT_OF_ADDR(vaddr); + + if (current_pd != &kernel_pd && current_pd->page[pt] != kernel_pd.page[pt]) { + current_pd->page[pt] = kernel_pd.page[pt]; + invlpg(¤t_pt[pt]); + return; + } + + // from now on we are preemptible + if (regs->eflags & EFLAGS_IF) asm volatile("sti"); + + if (vaddr >= (void*)&kernel_stack_protector && vaddr < (void*)&kernel_stack_protector + PAGE_SIZE) { + dbg_printf("Kernel stack overflow at 0x%p\n", vaddr); + dbg_dump_registers(regs); + PANIC("Kernel stack overflow."); + } + + if ((size_t)vaddr >= PD_MIRROR_ADDR) { + dbg_printf("Fault on access to mirrorred PD at 0x%p\n", vaddr); + dbg_print_region_info(); + PANIC("Unhandled kernel space page fault"); + } + + region_info_t *i = find_region(vaddr); + if (i == 0) { + dbg_printf("Kernel pagefault in non-existing region at 0x%p\n", vaddr); + dbg_dump_registers(regs); + PANIC("Unhandled kernel space page fault"); + } + if (i->pf == 0) { + dbg_printf("Kernel pagefault in region with no handler at 0x%p\n", vaddr); + dbg_dump_registers(regs); + dbg_print_region_info(); + PANIC("Unhandled kernel space page fault"); + } + i->pf(get_current_pagedir(), i, vaddr); } - i->pf(get_current_pagedir(), i, vaddr); - } else { - if (regs->eflags & EFLAGS_IF) asm volatile("sti"); // userspace PF handlers should always be preemptible - - dbg_printf("Userspace page fault at 0x%p\n", vaddr); - dbg_dump_registers(regs); - PANIC("Unhandled userspace page fault"); - // not handled yet - // TODO } } @@ -237,7 +262,7 @@ pagedir_t *create_pagedir() { pd = (pagedir_t*)malloc(sizeof(pagedir_t)); if (pd == 0) goto error; - temp = region_alloc(PAGE_SIZE, 0, 0); + temp = region_alloc(PAGE_SIZE, "Temporary pagedir mapping", 0); if (temp == 0) goto error; bool map_ok = pd_map_page(temp, pd_phys, true); @@ -262,7 +287,7 @@ pagedir_t *create_pagedir() { return pd; - error: +error: if (pd_phys != 0) frame_free(pd_phys, 1); if (pd != 0) free(pd); if (temp != 0) region_free(temp); diff --git a/src/kernel/core/region.c b/src/kernel/core/region.c index c14f129..c2c00a9 100644 --- a/src/kernel/core/region.c +++ b/src/kernel/core/region.c @@ -238,7 +238,7 @@ void region_free(void* addr) { mutex_unlock(&ra_mutex); } -static void* region_alloc_inner(size_t size, char* type, page_fault_handler_t pf, bool use_reserve) { +static void* region_alloc_inner(size_t size, char* type, kernel_pf_handler_t pf, bool use_reserve) { size = PAGE_ALIGN_UP(size); for (descriptor_t *i = first_free_region_by_size; i != 0; i = i->free.first_bigger) { @@ -282,7 +282,7 @@ static void* region_alloc_inner(size_t size, char* type, page_fault_handler_t pf return 0; //No big enough block found } -void* region_alloc(size_t size, char* type, page_fault_handler_t pf) { +void* region_alloc(size_t size, char* type, kernel_pf_handler_t pf) { void* result = 0; mutex_lock(&ra_mutex); diff --git a/src/kernel/core/thread.c b/src/kernel/core/thread.c index 3f25add..9d11da2 100644 --- a/src/kernel/core/thread.c +++ b/src/kernel/core/thread.c @@ -107,10 +107,7 @@ static void run_thread(void (*entry)(void*), void* data) { asm volatile("sti"); entry(data); - current_thread->state = T_STATE_FINISHED; - // TODO : add job for deleting the thread, or whatever - yield(); // expected never to return! - ASSERT(false); + exit(); } thread_t *new_thread(entry_t entry, void* data) { thread_t *t = (thread_t*)malloc(sizeof(thread_t)); @@ -147,7 +144,10 @@ thread_t *new_thread(entry_t entry, void* data) { t->current_pd_d = get_kernel_pagedir(); - t->proc = 0; // used by L1 functions + // used by user processes + t->proc = 0; + t->usermem_pf_handler = 0; + t->kmem_violation_handler = 0; return t; } @@ -196,6 +196,13 @@ void pause() { resume_interrupts(st); } +void exit() { + current_thread->state = T_STATE_FINISHED; + // TODO : add job for deleting the thread, or whatever + yield(); // expected never to return! + ASSERT(false); +} + void resume_thread(thread_t *thread, bool run_at_once) { bool st = disable_interrupts(); diff --git a/src/kernel/include/paging.h b/src/kernel/include/paging.h index d42ec52..56fa7fa 100644 --- a/src/kernel/include/paging.h +++ b/src/kernel/include/paging.h @@ -3,10 +3,16 @@ #include #include +// Bits in the error code for page fault +#define PF_PRESENT_BIT (1<<0) +#define PF_WRITE_BIT (1<<1) +#define PF_USER_BIT (1<<2) +#define PF_RSVD_WRITE_BIT (1<<3) +#define PF_OPFETCH_BIT (1<<4) + struct page_directory; typedef struct page_directory pagedir_t; - void paging_setup(void* kernel_data_end); pagedir_t *get_current_pagedir(); diff --git a/src/kernel/include/region.h b/src/kernel/include/region.h index 1fef582..a390e06 100644 --- a/src/kernel/include/region.h +++ b/src/kernel/include/region.h @@ -8,18 +8,18 @@ #include struct region_info; -typedef void (*page_fault_handler_t)(pagedir_t *pd, struct region_info *r, void* addr); +typedef void (*kernel_pf_handler_t)(pagedir_t *pd, struct region_info *r, void* addr); typedef struct region_info { void* addr; size_t size; char* type; - page_fault_handler_t pf; + kernel_pf_handler_t pf; } region_info_t; void region_allocator_init(void* kernel_data_end); -void* region_alloc(size_t size, char* type, page_fault_handler_t pf); // returns 0 on error +void* region_alloc(size_t size, char* type, kernel_pf_handler_t pf); // returns 0 on error region_info_t *find_region(void* addr); void region_free(void* addr); diff --git a/src/kernel/include/thread.h b/src/kernel/include/thread.h index b5fa50d..011067f 100644 --- a/src/kernel/include/thread.h +++ b/src/kernel/include/thread.h @@ -3,6 +3,7 @@ #include #include #include +#include #define T_STATE_RUNNING 1 #define T_STATE_PAUSED 2 @@ -12,6 +13,8 @@ #define TASK_SWITCH_FREQUENCY 50 // in herz +typedef void (*user_pf_handler_t)(pagedir_t *pd, registers_t *regs, void* addr); + typedef struct saved_context { uint32_t *esp; void (*eip)(); @@ -27,6 +30,8 @@ typedef struct thread { region_info_t *stack_region; struct process *proc; // process : L1 data structure + user_pf_handler_t usermem_pf_handler; // page fault in user memory + isr_handler_t kmem_violation_handler; // page fault in kernel memory accessed by user code (violation) struct thread *next_in_queue; } thread_t; @@ -40,6 +45,7 @@ extern thread_t *current_thread; void yield(); void pause(); +void exit(); void resume_thread(thread_t *thread, bool run_at_once); -- cgit v1.2.3