From ffc782f8740027d21793c37c6094ebed06d1dfd2 Mon Sep 17 00:00:00 2001 From: Danny Holman Date: Wed, 29 May 2024 00:26:59 -0500 Subject: arch: i386: fix several bugs in paging subsystem Fix several triple-faulting bugs in the paging initialization routines. These include causing a page fault during physical memory manager initialization, causing a page fault during paging initialization and other double-faulting and triple-faulting bugs. Signed-off-by: Danny Holman --- arch/i386/include/kernel/paging.h | 14 +-- arch/i386/kernel/kmalloc.c | 3 +- arch/i386/kernel/multiboot.c | 19 ++-- arch/i386/kernel/paging.c | 192 ++++++++++++++++++-------------------- arch/i386/kernel/pmem.c | 2 +- 5 files changed, 108 insertions(+), 122 deletions(-) diff --git a/arch/i386/include/kernel/paging.h b/arch/i386/include/kernel/paging.h index e949e32..5999986 100644 --- a/arch/i386/include/kernel/paging.h +++ b/arch/i386/include/kernel/paging.h @@ -3,7 +3,6 @@ #include #include -#include #include #define PD_PRES 0x0001 @@ -26,15 +25,18 @@ #define PGROUNDUP(size) (((size)+PAGE_SIZE-1) & ~(PAGE_SIZE-1)) #define PGROUNDDN(size) (((size)) & ~(PAGE_SIZE-1)) -int init_page_directory(uintptr_t *pd, int user); -void enable_paging(uintptr_t pd_addr); +#define GET_PADDR(x) (((uint32_t)(x)) - 0xC0000000) +#define GET_VADDR(x) (((uint32_t)(x)) + 0xC0000000) -void paging_init(void); -void page_fault_handler(struct regs *regs); +#define GET_PDX(x) (((uintptr_t)(x) >> 22) & 0x3FF) +#define GET_PTX(x) (((uintptr_t)(x) >> 12) & 0x3FF) -uint32_t get_vaddr(uintptr_t paddr); +uintptr_t* init_page_table(void); +void paging_init(void); void map_page(uint32_t *pd, uintptr_t paddr, uintptr_t vaddr, uint32_t flags); void unmap_page(uint32_t *pd, uintptr_t vaddr); +void page_fault_handler(struct isr_frame *frame); + #endif diff --git a/arch/i386/kernel/kmalloc.c b/arch/i386/kernel/kmalloc.c index 183545a..8118ecd 100644 --- a/arch/i386/kernel/kmalloc.c +++ b/arch/i386/kernel/kmalloc.c @@ -1,8 +1,7 @@ -#include +#include #include #include #include -#include #include #include diff --git a/arch/i386/kernel/multiboot.c b/arch/i386/kernel/multiboot.c index 87b5e32..f56910a 100644 --- a/arch/i386/kernel/multiboot.c +++ b/arch/i386/kernel/multiboot.c @@ -10,6 +10,7 @@ #include #include #include +#include extern void kernel_main(char *cmdline); extern uintptr_t *kpgdir; @@ -19,22 +20,20 @@ void i386_entry(uint32_t mboot_magic, struct mboot_info *header, uintptr_t *entr fb_init(); if (mboot_magic != MBOOT_LOADER_MAGIC) { - kprintf("NOT BOOTED WITH MULTIBOOT BOOTLOADER\n"); - kprintf("RESET PC!\n"); disable_ints(); - while (1); + panic("Not booted with multiboot bootloader"); } - map_page(NULL, (uintptr_t)header, (uintptr_t)GET_VADDR(header), PD_PRES); + map_page(NULL, (uintptr_t)header, (uintptr_t)header, PD_PRES); if (!(header->flags >> 6 & 0x1)) { - kprintf("NO MEMORY MAP FROM BOOTLOADER\n"); - kprintf("RESET PC!\n"); disable_ints(); - while (1); + panic("Physical memory map not provided by bootloader"); } struct mboot_info hcopy; + char cmdcopy[1024]; memcpy(&hcopy, header, sizeof(struct mboot_info)); - unmap_page(NULL, (uintptr_t)header); + map_page(NULL, (uintptr_t)header->cmdline, (uintptr_t)header->cmdline, PD_PRES); + strcpy(cmdcopy, header->cmdline); pfa_init(&hcopy); paging_init(); @@ -44,8 +43,8 @@ void i386_entry(uint32_t mboot_magic, struct mboot_info *header, uintptr_t *entr pic_remap(); timer_init(); - //enable_ints(); - kernel_main((char*)header->cmdline); + enable_ints(); + kernel_main(cmdcopy); while (1); } diff --git a/arch/i386/kernel/paging.c b/arch/i386/kernel/paging.c index 64afcf7..05cd04c 100644 --- a/arch/i386/kernel/paging.c +++ b/arch/i386/kernel/paging.c @@ -2,146 +2,132 @@ #include #include #include -#include -#include #include -#include +#include +#include +#include -static uintptr_t page_directory[1024] __attribute__((aligned(PAGE_SIZE))); -static uintptr_t first_page_table[1024] __attribute__((aligned(PAGE_SIZE))); +uintptr_t *kpgdir = NULL; +static uintptr_t kernel_pd[1024] __attribute__((aligned(PAGE_SIZE))); +static uintptr_t kernel_pt[1024] __attribute__((aligned(PAGE_SIZE))); +static uintptr_t accnt_pt[1024] __attribute__((aligned(PAGE_SIZE))); static int paging_enabled = 0; -int init_page_table(uintptr_t *pd, int index, int user) { - if (pd == NULL) - return 0; +void _copy_kernel_maps(uintptr_t *new_pd) { + int pdx = GET_PDX(GET_VADDR(KSTART)); + new_pd[pdx] = GET_PADDR(kernel_pt) | PD_PRES | PD_RW; + new_pd[832] = GET_PADDR(accnt_pt) | PD_PRES | PD_RW; +} - uintptr_t paddr = pfa_alloc(); - if (paddr == PFA_ALLOC_ERR) - return -ENOMEM; +uintptr_t* init_page_table(void) { + uintptr_t *ret = (uintptr_t*)pfa_alloc(); + if ((uintptr_t)ret == PFA_ALLOC_ERR) + return NULL; - map_page(NULL, paddr, paddr, PD_PRES | PD_RW); - if (user) - pd[index] = paddr | PD_USR; - else - pd[index] = paddr; + map_page(NULL, (uintptr_t)ret, (uintptr_t)ret+0x20000000, PD_PRES | PD_RW); + memset((char*)ret, 0, PAGE_SIZE); + unmap_page(NULL, (uintptr_t)ret); + return ret; } -int init_page_directory(uintptr_t *pd, int user) { - uintptr_t paddr = pfa_alloc(); - if (paddr == PFA_ALLOC_ERR) - return -ENOMEM; +uintptr_t* init_page_directory(void) { + uintptr_t *ret = (uintptr_t*)pfa_alloc(); + if ((uintptr_t)ret == PFA_ALLOC_ERR) + return NULL; - map_page(NULL, paddr, pd, PD_PRES | PD_RW); - for (int i = 0; i < 1023; i++) - init_page_table(pd, i, 0); - pd[1023] = paddr | 3; - return paddr; -} + map_page(NULL, (uintptr_t)ret, (uintptr_t)ret, PD_PRES | PD_RW); + memset(ret, 0, PAGE_SIZE); + _copy_kernel_maps(ret); + unmap_page(NULL, (uintptr_t)ret); -static void _alloc_new_page(uint32_t *cr3, uintptr_t vaddr, int user, int rw) { - uintptr_t paddr = pfa_alloc(); - if (paddr == PFA_ALLOC_ERR) - panic("Cannot allocate physical page"); - - uint32_t flags = PD_PRES; - if (rw == 1) - flags |= PD_RW; - if (user == 1) - flags |= PD_USR; - map_page(cr3, paddr, vaddr, flags); + return ret; } void paging_init(void) { if (paging_enabled == 1) return; - - for (int i = 0; i < 1024; i++) { - page_directory[i] = PD_RW; - first_page_table[i] = PD_RW; - } - - page_directory[1023] = ((uintptr_t)&page_directory - 0xC0000000) | 3; - page_directory[0] = ((uintptr_t)&first_page_table - 0xC0000000) | 3; - page_directory[768] = ((uintptr_t)&first_page_table - 0xC0000000) | 3; + + _copy_kernel_maps(kernel_pd); for (uintptr_t i = KSTART; i < KEND; i += PAGE_SIZE) - map_page(page_directory, i, i + 0xC0000000, PD_PRES); + map_page(kernel_pd, i, GET_VADDR(i), PD_PRES); - enable_paging(((uintptr_t)&page_directory) - 0xC0000000); + map_page(kernel_pd, 0xB8000, 0xC03FF000, PD_PRES | PD_RW); + kpgdir = kernel_pd; + enable_paging(GET_PADDR(kpgdir)); paging_enabled = 1; - - for (int i = 0; i < 1023; i++) { - if (i == 0 || i == 768) - continue; - init_page_table(page_directory, i, 0); - } return; } -void page_fault_handler(struct regs *regs) { - uintptr_t errno = regs->isr_err; - - uintptr_t fault_addr; - __asm__ volatile("movl %%cr2, %0" : "=r"(fault_addr)); - int present = errno & ERR_PRESENT; - int rw = errno & ERR_RW; - int user = errno & ERR_USER; - int reserved = errno & ERR_RESERVED; - int ifetch = errno & ERR_INST; - - if (reserved || ifetch) - panic("Unknown error in page fault"); - if (present && user) - panic("User process generated protection fault"); // TODO: this shouldn't panic - if (present && !user) - panic("Kernel process generated protection fault"); - - if (user && rw) - _alloc_new_page((uint32_t*)regs->cr3, fault_addr, 1, 1); - if (user && !rw) - _alloc_new_page((uint32_t*)regs->cr3, fault_addr, 1, 0); - if (rw) - _alloc_new_page((uint32_t*)regs->cr3, fault_addr, 0, 1); - if (!rw) - _alloc_new_page((uint32_t*)regs->cr3, fault_addr, 0, 0); -} - -uintptr_t get_paddr(uintptr_t vaddr) { - uint32_t pdindex = (uint32_t)vaddr >> 22; - uint32_t ptindex = (uint32_t)vaddr >> 12 & 0x03FF; - - uint32_t *pd = (uint32_t*)0xFFFFF000; - if ((*pd & PD_PRES) != 1) - return 0; - uint32_t *pt = ((uint32_t*)0xFFC00000) + (0x400 * pdindex); - if ((*pt & PD_PRES) != 1) - return 0; - return (uintptr_t)((pt[ptindex] & ~0xFFF) + ((uint32_t)vaddr & 0xFFF)); -} - void map_page(uint32_t *pd, uintptr_t paddr, uintptr_t vaddr, uint32_t flags) { if (pd == NULL) - pd = page_directory; + pd = kpgdir; paddr = PGROUNDDN(paddr); vaddr = PGROUNDDN(vaddr); - uintptr_t pdindex = vaddr >> 22; - uintptr_t ptindex = vaddr >> 12 & 0x03FF; + uintptr_t pdindex = GET_PDX(vaddr); + uintptr_t ptindex = GET_PTX(vaddr); + + uintptr_t *pt = (uintptr_t*)(GET_VADDR(pd[pdindex]) & 0xFFFFF000); + if (pd[pdindex] == 0) + pd[pdindex] = (uintptr_t)init_page_table(); - uintptr_t *pt = (uintptr_t*)((pd[pdindex] + 0xC0000000) & 0xFFFFF000); uintptr_t *pte = (uintptr_t*)(&pt[ptindex]); *pte |= paddr | (flags & 0xFFF) | 0x01; + invlpg((void*)vaddr); } void unmap_page(uint32_t *pd, uintptr_t vaddr) { if (pd == NULL) - pd = page_directory; + pd = kpgdir; + + uintptr_t pdindex = GET_PDX(vaddr); + uintptr_t ptindex = GET_PTX(vaddr); - uintptr_t pdindex = vaddr >> 22; - uintptr_t ptindex = vaddr >> 12 & 0x03FF; + uintptr_t *pt = (uintptr_t*)(GET_VADDR(pd[pdindex]) & 0xFFFFF000); + if (pd[pdindex] == 0) + return; - uintptr_t *pt = (uintptr_t*)((pd[pdindex] + 0xC0000000) & 0xFFFFF000); uintptr_t *pte = (uintptr_t*)(&pt[ptindex]); *pte &= 0; + invlpg((void*)vaddr); +} + +void page_fault_handler(struct isr_frame *frame) { + uintptr_t fault_addr; + __asm__ volatile("movl %%cr2, %0" : "=r"(fault_addr)); + + switch (frame->isr_err) { + case 0: + map_page((uintptr_t*)GET_VADDR(frame->cr3), pfa_alloc(), fault_addr, PD_PRES); + break; + case 1: + panic("Kernel process caused protection fault on read\n"); + break; + case 2: + map_page((uintptr_t*)GET_VADDR(frame->cr3), pfa_alloc(), fault_addr, PD_PRES | PD_RW); + break; + case 3: + panic("Kernel process caused protection fault on write\n"); + break; + case 4: + map_page((uintptr_t*)GET_VADDR(frame->cr3), pfa_alloc(), fault_addr, PD_PRES | PD_USR); + break; + case 5: + // TODO: instead of panicking, kill process + panic("User process caused protection fault on read\n"); + break; + case 6: + map_page((uintptr_t*)GET_VADDR(frame->cr3), pfa_alloc(), fault_addr, PD_PRES | PD_RW | PD_USR); + break; + case 7: + // TODO: see case 101 + panic("USER process caused protection fault on write\n"); + break; + default: + kprintf("Unknown paging error occured on address %x\n", fault_addr); + panic("Paging error"); + break; + } } diff --git a/arch/i386/kernel/pmem.c b/arch/i386/kernel/pmem.c index e59c80b..0e53507 100644 --- a/arch/i386/kernel/pmem.c +++ b/arch/i386/kernel/pmem.c @@ -25,7 +25,7 @@ uintptr_t pfa_alloc(void) { if (ret == NULL) return PFA_ALLOC_ERR; - struct pfa_page *temp_map = (struct pfa_page*)0x1000; + struct pfa_page *temp_map = (struct pfa_page*)0xD0000000; map_page(NULL, (uintptr_t)ret, (uintptr_t)temp_map, PD_PRES | PD_RW); memset((char*)temp_map, 0, PAGE_SIZE); unmap_page(NULL, (uintptr_t)temp_map); -- cgit v1.2.3