diff options
Diffstat (limited to 'kernel/mm/virtual_mm')
-rw-r--r-- | kernel/mm/virtual_mm/virtual_mm.c | 104 |
1 files changed, 101 insertions, 3 deletions
diff --git a/kernel/mm/virtual_mm/virtual_mm.c b/kernel/mm/virtual_mm/virtual_mm.c index 6350c11..0f55950 100644 --- a/kernel/mm/virtual_mm/virtual_mm.c +++ b/kernel/mm/virtual_mm/virtual_mm.c @@ -18,6 +18,8 @@ #include <stdint.h> +#include <kernel/halt.h> + #include <libk/stdio.h> #include <mm/physical_mm.h> @@ -26,8 +28,7 @@ extern uint32_t kernel_start; extern uint32_t kernel_end; -uint32_t initial_page_directory[PAGE_DIRECTORY_SIZE]; -uint32_t initial_page_table[PAGE_TABLE_SIZE]; +uint32_t *current_page_directory = 0; bool virtual_mm_allocate_page(uint32_t *pt_entry) @@ -75,5 +76,102 @@ virtual_mm_lookup_directory(uint32_t *page_directory, uint32_t virtual_addr) ALWAYS_INLINE void virtual_mm_load_page_directory(uint32_t *page_directory) { - __asm__("movl %0, %%cr0" ::"r"(page_directory)); + __asm__("movl %0, %%eax" ::"r"(page_directory) : "%eax"); + /* For some reason, that colon is required; else the assembler thinks that + * %eax doesn't exist :^) */ + __asm__("movl %%eax, %%cr3" ::: "%eax"); +} + +bool +virtual_mm_switch_page_directory(uint32_t *page_directory) +{ + if (!page_directory) + return false; + current_page_directory = page_directory; + virtual_mm_load_page_directory(page_directory); + + return true; +} + +/* TODO: I have absolutely _no_ idea if this works */ +void +virtual_mm_flush_tlb_entry(uint32_t *virtual_addr) +{ + __asm__("cli;" + "invlpg (%0);" + "sti" ::"r"(virtual_addr)); +} + +bool +virtual_mm_map_page(uint32_t *physical_addr, uint32_t *virtual_addr) +{ + uint32_t *pd_entry + = ¤t_page_directory[PAGE_DIRECTORY_INDEX((uint32_t) virtual_addr)]; + + /* If the page directory entry isn't present */ + if (!PTE_IS_PRESENT(*pd_entry)) { + uint32_t *page_table = physical_mm_allocate_block(); + if (!page_table) + return false; + + *pd_entry = 0; + ADD_ATTRIB(pd_entry, SET_PDE_PRESENT(1)); + ADD_ATTRIB(pd_entry, SET_PDE_WRITABLE(1)); + ADD_ATTRIB(pd_entry, SET_PDE_FRAME((uint32_t) page_table)); + } + + uint32_t *page_table = (uint32_t *) GET_PDE_FRAME(pd_entry); + uint32_t *pt_entry = &page_table[PAGE_TABLE_INDEX((uint32_t) virtual_addr)]; + *pt_entry = 0; + + ADD_ATTRIB(pt_entry, SET_PTE_PRESENT(1)); + ADD_ATTRIB(pt_entry, SET_PTE_FRAME((uint32_t) physical_addr)); + + return true; +} + +ALWAYS_INLINE static void +virtual_mm_enable_paging(void) +{ + __asm__("movl %%cr0, %%eax;" + "orl $0x80000000, %%eax;" + "movl %%eax, %%cr0" :: + : "eax"); +} + +void +virtual_mm_initialize(void) +{ + uint32_t *table = physical_mm_allocate_block(); + if (!table) + ASSERT_NOT_REACHED(); + + for (uint32_t i = 0; i < 1024; i++) + table[i] = 0; + + /* Identity map the first 4MiB (maps 4KiB 1024 times) */ + for (uint32_t i = 0, physical_addr = 0, virtual_addr = 0; i < 1024; + i++, physical_addr += 4096, virtual_addr += 4096) { + uint32_t page = 0; + ADD_ATTRIB(&page, SET_PTE_PRESENT(1)); + ADD_ATTRIB(&page, SET_PTE_FRAME(physical_addr)); + + table[PAGE_TABLE_INDEX(virtual_addr)] = page; + } + + uint32_t *page_directory = physical_mm_allocate_block(); + if (!page_directory) + ASSERT_NOT_REACHED(); + + for (uint32_t i = 0; i < 1024; i++) + page_directory[i] = 0; + + uint32_t *pd_entry = &page_directory[PAGE_DIRECTORY_INDEX(0)]; + ADD_ATTRIB(pd_entry, SET_PDE_PRESENT(1)); + ADD_ATTRIB(pd_entry, SET_PDE_WRITABLE(1)); + ADD_ATTRIB(pd_entry, SET_PDE_FRAME((uint32_t) table)); + + current_page_directory = page_directory; + virtual_mm_switch_page_directory(current_page_directory); + virtual_mm_enable_paging(); } |