Orion
Barry Importing existing Orion kernel d41a53c (2 years, 4 months ago)diff --git a/mem/paging.c b/mem/paging.c new file mode 100644 index 0000000..b44e843 --- /dev/null +++ b/mem/paging.c @@ -0,0 +1,228 @@ +/* + * This file contains all functions used to manipulate the virtual address + * spaces. It has a static Kernel page directory and table, which it uses to + * initialsed an identity-paged environment for the Kernel to work in. This is + * enough for the heap to function in. The file also exposes several functions + * that allow a page directory to be manipulated and have pages added and moved. + * These functions are used by other components of the Kernel - mostly the heap + * and IPC. There are also functions to create new and destroy existing page + * directories. The paging system also implements features like copy-on-write. + */ + +#include <stdint.h> +#include <sys/mman.h> +#include "frame.h" +#include "heap.h" +#include "mem.h" +#include "paging.h" +#include "../vfs/vfs.h" +#include "../proc/proc.h" +#include "../io.h" +#include "../screen.h" + +Spinlock quickPageLock; + +page_table_t kernelDir; +page_t zeroFrame; + +void enable_paging(void); +void disable_paging(void); +void copy_page_frame(void *src, void *dest); + +/* Switch page directory */ +static void +switch_dir(page_dir_t dir) +{ + asm volatile("mov %0, %%cr3" :: "r" (dir)); +} + +/* Allocate a page a frame */ +void +alloc_page(page_t *page, uint16_t flags, page_t frame) +{ + page_t *mappings = (void *) 0xFFC00000; + page_table_t *tables = (void *) 0xFFFFF000; + if ((tables[(page - mappings) / 1024] & PDE_PRESENT) == 0) + return; + if (*page & 0xFFFFF000) + return; + + if (frame == (page_t) -1) + frame = alloc_frames(1); + if (frame == (page_t) -1) + return; + + *page = frame | flags; + flush_tlb((page - mappings) << 12); +} + +/* Release a page's frame */ +void +free_page(page_t *page) +{ + page_t *mappings = (void *) 0xFFC00000; + page_table_t *tables = (void *) 0xFFFFF000; + if ((tables[(page - mappings) / 1024] & PDE_PRESENT) == 0) + return; + if ((*page & 0xFFFFF000) == 0) + return; + + free_frame(*page & 0xFFFFF000); + *page = 0x00000000; + flush_tlb((page - mappings) << 12); +} + +/* Get Page Table Entry from virtual address */ +page_t * +get_page(void *addr) +{ + page_t *mappings = (void *) 0xFFC00000; + page_table_t *tables = (void *) 0xFFFFF000; + uint32_t address = (uint32_t) addr >> 12; + uint32_t tbl = address / 1024; + /* Create table not present */ + if ((tables[tbl] & PDE_PRESENT) == 0) { + tables[tbl] = alloc_frames(1) + | PDE_PRESENT | PDE_WRITE | PDE_USER; + memset((void *) mappings + (tbl * 0x1000), 0, 0x1000); + } + return &mappings[address]; +} + +/* Clone a page directory */ +page_dir_t +clone_dir(void) +{ + page_table_t *oldTables = (void *) 0xFFFFF000; + page_table_t *newTables = (void *) 0xFFFFE000; + page_t *oldTable, *newTable; + page_dir_t dir = alloc_frames(1); + uint16_t i, tbl, pg; + + /* Temporarily link new paging structures into current directory */ + page_table_t restore = oldTables[1022]; + oldTables[1022] = dir | PDE_PRESENT | PDE_WRITE; + for (i = 0; i < 1024; i++) + flush_tlb((uintptr_t) newTables + (0x1000 * i)); + + /* Iterate tables */ + for (tbl = 0; tbl < 1022; tbl++) { + if ((oldTables[tbl] & PDE_PRESENT) == 0) + continue; + + /* Link Kernel tables */ + if (tbl < 2 || tbl >= 1008) { /* TODO: define kernel mem */ + newTables[tbl] = oldTables[tbl]; + continue; + } + + /* Copy everything else */ + newTables[tbl] = alloc_frames(1) | PG_ATTR(oldTables[tbl]); + oldTable = (page_t *) 0xFFC00000 + (tbl * 1024); + newTable = (page_t *) 0xFF800000 + (tbl * 1024); + for (pg = 0; pg < 1024; pg++) { + if ((oldTable[pg] & PTE_PRESENT) == 0) { + newTable[pg] = 0; + continue; + } + + /* Copy-On-Write behaviour */ + if (tbl < 960) { + oldTable[pg] &= ~PTE_WRITE; + flush_tlb((uintptr_t) (((tbl * 1024) + pg) << 12)); + newTable[pg] = oldTable[pg]; + } else { + newTable[pg] = alloc_frames(1) | PG_ATTR(oldTable[pg]); + copy_page_frame((void *) PG_ADDR(oldTable[pg]), + (void *) PG_ADDR(newTable[pg])); + } + /* FIXME */ + } + } + newTables[1023] = oldTables[1022]; + + /* Unlink paging structures */ + oldTables[1022] = restore; + for (i = 0; i < 1024; i++) + flush_tlb((uintptr_t) newTables + (0x1000 * i)); + + return dir; +} + +/* Free all (copied) pages in the current directory */ +void +clean_dir(void) +{ + page_t *mappings = (void *) 0xFFC00000; + page_table_t *tables = (void *) 0xFFFFF000; + page_t *pages; + uint16_t tbl, pg; + for (tbl = 2; tbl < 1008; tbl++) { + if ((tables[tbl] & PDE_PRESENT) == 0) + continue; + pages = mappings + (tbl * 1024); + for (pg = 0; pg < 1024; pg++) { + if ((pages[pg] & PDE_PRESENT) == 0) + continue; + free_page(pages + pg); + } + } +} + +/* Quickly map a page frame into view for temporary use */ +page_t +quick_page(uintptr_t frame) +{ + page_t *mappings = (void *) 0xFFC00000; + page_t old; + old = mappings[2047]; + mappings[2047] = PG_ADDR(frame) | PG_ATTR(old); + flush_tlb(0x7FF000); + return PG_ADDR(old); +} + +/* Initialise paging */ +void +init_paging(void) +{ + zeroFrame = alloc_frames(1); + memset((void *) zeroFrame, 0, 0x1000); + + uint16_t tbl, pg; + page_t *table; + page_table_t *kernelTables; + kernelDir = alloc_frames(1); + kernelTables = (page_table_t *) kernelDir; + for (tbl = 0; tbl < 1024; tbl++) + kernelTables[tbl] = 0x00000000 | PDE_WRITE; + for (tbl = 0; tbl < 2; tbl++) { + table = (void *) alloc_frames(1); + kernelTables[tbl] = ((page_table_t) table) + | PDE_WRITE | PDE_PRESENT; + for (pg = 0; pg < 1024; pg++) { + if (!tbl && !pg) + continue; + table[pg] = (((tbl * 1024) + pg) << 12) + | PTE_WRITE | PTE_PRESENT | PTE_GLOBAL; + } + } + /* Map the directory into itself */ + kernelTables[1023] = kernelDir | PDE_WRITE | PDE_PRESENT; + + /* Use Kernel directory */ + switch_dir(kernelDir); + register_exception(14, early_page_fault_handler); + enable_paging(); + + /* Identity page the APIC registers */ + *get_page((void *) lapicPtr) = lapicPtr + | PTE_PRESENT | PTE_WRITE | PTE_GLOBAL; + *get_page((void *) ioapicPtr) = ioapicPtr + | PTE_PRESENT | PTE_WRITE | PTE_GLOBAL; + + /* Allocate Kernel stack */ + uintptr_t stk; + for (stk = 0xF0400000; stk < 0xF0800000; stk += 0x1000) + alloc_page(get_page((void *) stk), + PTE_PRESENT | PTE_WRITE | PTE_USER, -1); +}