diff options
Diffstat (limited to 'arch/i386/kernel/paging.c')
-rw-r--r-- | arch/i386/kernel/paging.c | 116 |
1 files changed, 77 insertions, 39 deletions
diff --git a/arch/i386/kernel/paging.c b/arch/i386/kernel/paging.c index b2a1392..64afcf7 100644 --- a/arch/i386/kernel/paging.c +++ b/arch/i386/kernel/paging.c @@ -1,46 +1,80 @@ #include <kernel/paging.h> +#include <kernel/asm.h> +#include <kernel/pmem.h> +#include <kernel/panic.h> +#include <kernel/errno.h> #include <kernel/io.h> #include <kernel/syscall.h> #include <kernel/string.h> -extern uintptr_t _kernel_start; -extern uintptr_t _kernel_end; -static uintptr_t kstart = (uintptr_t)&_kernel_start; -static uintptr_t kend = (uintptr_t)&_kernel_end - 0xC0000000; - static uintptr_t page_directory[1024] __attribute__((aligned(PAGE_SIZE))); static uintptr_t first_page_table[1024] __attribute__((aligned(PAGE_SIZE))); -static uintptr_t second_page_table[1024] __attribute__((aligned(PAGE_SIZE))); static int paging_enabled = 0; -uint32_t* init_page_table(uint32_t flags) { - uint32_t *ret = kmalloc(sizeof(uint32_t)*1024); - for (int i = 0; i < 1024; i++) - ret[i] = flags; +int init_page_table(uintptr_t *pd, int index, int user) { + if (pd == NULL) + return 0; + + uintptr_t paddr = pfa_alloc(); + if (paddr == PFA_ALLOC_ERR) + return -ENOMEM; + + map_page(NULL, paddr, paddr, PD_PRES | PD_RW); + if (user) + pd[index] = paddr | PD_USR; + else + pd[index] = paddr; +} + +int init_page_directory(uintptr_t *pd, int user) { + uintptr_t paddr = pfa_alloc(); + if (paddr == PFA_ALLOC_ERR) + return -ENOMEM; + + 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; } -uint32_t* init_page_dir(uint32_t flags) { - uint32_t *ret = init_page_table(flags); - ret[0] = ((uintptr_t)&first_page_table - 0xC0000000) | 3; - ret[768] = ((uintptr_t)&second_page_table - 0xC0000000) | 3; +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); } 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; - second_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)&second_page_table - 0xC0000000) | 3; - for (uintptr_t i = kstart; i < kend; i += PAGE_SIZE) - map_page(page_directory, i, i + 0xC0000000, 0x003); + page_directory[768] = ((uintptr_t)&first_page_table - 0xC0000000) | 3; + for (uintptr_t i = KSTART; i < KEND; i += PAGE_SIZE) + map_page(page_directory, i, i + 0xC0000000, PD_PRES); enable_paging(((uintptr_t)&page_directory) - 0xC0000000); paging_enabled = 1; + + for (int i = 0; i < 1023; i++) { + if (i == 0 || i == 768) + continue; + init_page_table(page_directory, i, 0); + } return; } @@ -55,17 +89,21 @@ void page_fault_handler(struct regs *regs) { int reserved = errno & ERR_RESERVED; int ifetch = errno & ERR_INST; - uintptr_t first_free; - if (!present) - map_page(regs->cr3, fault_addr, fault_addr, PD_PRES | PD_RW | PD_USR); - if (user) - panic("Usermode attempted to read supervisor page"); + 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) - panic("Task wrote to read only page"); - if (reserved) - panic("Task overwrote reserved page bits"); - if (ifetch) - panic("Task paging instruction fetch failure"); + _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) { @@ -73,11 +111,11 @@ uintptr_t get_paddr(uintptr_t vaddr) { uint32_t ptindex = (uint32_t)vaddr >> 12 & 0x03FF; uint32_t *pd = (uint32_t*)0xFFFFF000; - if (*pd & PD_PRES != 1) - return NULL; + if ((*pd & PD_PRES) != 1) + return 0; uint32_t *pt = ((uint32_t*)0xFFC00000) + (0x400 * pdindex); - if (*pt & PD_PRES != 1) - return NULL; + if ((*pt & PD_PRES) != 1) + return 0; return (uintptr_t)((pt[ptindex] & ~0xFFF) + ((uint32_t)vaddr & 0xFFF)); } @@ -85,15 +123,15 @@ void map_page(uint32_t *pd, uintptr_t paddr, uintptr_t vaddr, uint32_t flags) { if (pd == NULL) pd = page_directory; - uintptr_t paddr_aligned = paddr & 0xFFFFF000; - uintptr_t vaddr_aligned = vaddr & 0xFFFFF000; + paddr = PGROUNDDN(paddr); + vaddr = PGROUNDDN(vaddr); - uintptr_t pdindex = vaddr_aligned >> 22; - uintptr_t ptindex = vaddr_aligned >> 12 & 0x03FF; + uintptr_t pdindex = vaddr >> 22; + uintptr_t ptindex = vaddr >> 12 & 0x03FF; - uintptr_t *pt = (pd[pdindex] + 0xC0000000 & 0xFFFFF000); + uintptr_t *pt = (uintptr_t*)((pd[pdindex] + 0xC0000000) & 0xFFFFF000); uintptr_t *pte = (uintptr_t*)(&pt[ptindex]); - *pte |= paddr_aligned | (flags & 0xFFF) | 0x01; + *pte |= paddr | (flags & 0xFFF) | 0x01; } void unmap_page(uint32_t *pd, uintptr_t vaddr) { @@ -103,7 +141,7 @@ void unmap_page(uint32_t *pd, uintptr_t vaddr) { uintptr_t pdindex = vaddr >> 22; uintptr_t ptindex = vaddr >> 12 & 0x03FF; - uintptr_t *pt = (pd[pdindex] + 0xC0000000 & 0xFFFFF000); + uintptr_t *pt = (uintptr_t*)((pd[pdindex] + 0xC0000000) & 0xFFFFF000); uintptr_t *pte = (uintptr_t*)(&pt[ptindex]); *pte &= 0; } |