/* * This file contains all the functions used to manipulate the virtual address * spaces. It controls all of the system's paging and virtual memory from a low * level perspective. On initialisation, a page directory is allocated for the * kernel that identity maps everything. */ #include #include #include void copy_page_frame(uintptr_t src, uintptr_t dest); void page_fault_handler(struct InterruptFrame *frame); page_t zeroFrame; static page_dir_t kernelDir; /* Switch page directory */ void switch_dir(page_dir_t dir) { asm volatile("mov %0, %%cr3" :: "r" (dir)); } /* Get a page mapping */ page_t get_page(uintptr_t vaddr) { page_t *mappings = (void *) 0xFFC00000; page_table_t *tables = (void *) 0xFFFFF000; uintptr_t address = vaddr >> 12; uint32_t tbl = address / 1024; if (!(tables[tbl] & PDE_PRESENT)) return 0x00000000; return mappings[address]; } /* Set a page mapping */ void set_page(uintptr_t vaddr, page_t page) { page_t *mappings = (void *) 0xFFC00000; page_table_t *tables = (void *) 0xFFFFF000; uintptr_t address = vaddr >> 12; uint32_t tbl = address / 1024; if (!(tables[tbl] & PDE_PRESENT) && !page) return; /* Create table if not present */ if (!(tables[tbl] & PDE_PRESENT)) { tables[tbl] = alloc_frame() | PDE_PRESENT | PDE_WRITE; if (page & PTE_USER) tables[tbl] |= PDE_USER; memset(mappings + (tbl * 1024), 0, PAGE_SIZE); } mappings[address] = page; } /* Clone an entire 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_frame(); uint16_t tbl, pg; /* Temporarily link new paging structures into current directory */ page_table_t restore = oldTables[1022]; oldTables[1022] = dir | PDE_PRESENT | PDE_WRITE; flush_tlb((uintptr_t) newTables); /* Iterate tables */ for (tbl = 0; tbl < 1022; tbl++) { if (!(oldTables[tbl] & PDE_PRESENT)) continue; /* Link kernel tables */ if (tbl < 2 || tbl >= 1008) { newTables[tbl] = oldTables[tbl]; continue; } /* Copy everything else */ newTables[tbl] = alloc_frame() | PAGE_ATTR(oldTables[tbl]); oldTable = (page_t *) 0xFFC00000 + (tbl * 1024); newTable = (page_t *) 0xFF800000 + (tbl * 1024); flush_tlb((uintptr_t) newTable); for (pg = 0; pg < 1024; pg++) { if (!(oldTable[pg] & PTE_PRESENT)) { newTable[pg] = 0; continue; } /* Link the pages for Copy-On-Write */ oldTable[pg] &= ~PTE_WRITE; flush_tlb(((tbl * 1024) + pg) << 12); newTable[pg] = oldTable[pg]; } } newTables[1023] = oldTables[1022]; /* Unlink paging structures from current directory */ oldTables[1022] = restore; flush_tlb((uintptr_t) newTables); return dir; } /* Initialise paging */ void init_paging(void) { uint16_t tbl, pg; kernelDir = alloc_frame(); page_table_t *kernelTables = (page_table_t *) kernelDir; page_t *table; for (tbl = 0; tbl < 1024; tbl++) kernelTables[tbl] = 0x00000000 | PDE_WRITE; for (tbl = 0; tbl < 2; tbl++) { table = (page_t *) alloc_frame(); kernelTables[tbl] = ((page_table_t) table) | PDE_WRITE | PDE_PRESENT; for (pg = 0; pg < 1024; pg++) { /* Skip bottom page - catches NULL dereferences */ 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; /* * By mapping the page directory as the last page table, the page * directory entries are read as page table entries, and the page * tables become the pages. This means that each page contains the * contents of a page table, and the region in memory represented by the * last page table contains a contiguous list of all pages in memory. * The very last page contains the contents of the page directory * itself. This means that each virtual address space contains it's own * paging structures. */ /* Use kernel directory */ register_exception(14, page_fault_handler); cpu_load_paging(); zeroFrame = alloc_frame(); } /* Enable paging on the current CPU */ void cpu_load_paging(void) { switch_dir(kernelDir); asm volatile ( "movl %%cr0, %%eax;" "orl $0x80000000, %%eax;" "movl %%eax, %%cr0" ::: "%eax" ); }