diff --git a/src/kernel/main.c b/src/kernel/main.c index ca6665c..e726d61 100644 --- a/src/kernel/main.c +++ b/src/kernel/main.c @@ -6,6 +6,7 @@ #include "drivers/serial.h" #include "fs/vfs.h" #include "mm/pmm.h" +#include "mm/vmm.h" #include "multiboot.h" extern uint8_t end; // Kernel end @@ -33,18 +34,15 @@ void __attribute__((cdecl)) kmain(multiboot_info_t *multiboot_info){ pmm_deinit_region(0x100000, (int)(&end) - 0x100000); // Deinit the section the kernel is in + // Initialize virtual memory + vmm_initialize(); + // Initialize drivers timer_initialize(); keyboard_initialize(); // Initialize FS vfs_initialize(); - - // PMM test code - uint32_t *p1 = (uint32_t *)pmm_alloc_block(); - printf("Allocated p1 at: 0x%x\n", p1); - uint32_t *p2 = (uint32_t *)pmm_alloc_block(); - printf("Allocated p2 at: 0x%x\n", p2); for(;;); } diff --git a/src/kernel/mm/pde.c b/src/kernel/mm/pde.c new file mode 100644 index 0000000..e14e15c --- /dev/null +++ b/src/kernel/mm/pde.c @@ -0,0 +1,34 @@ +#include "pde.h" + +void pd_entry_add_attr(pd_entry_t *entry, uint32_t attr){ + *entry |= attr; +} + +void pd_entry_del_attr(pd_entry_t *entry, uint32_t attr){ + *entry &= ~attr; +} + +void pd_entry_set_frame(pd_entry_t *entry, phys_addr frame){ + *entry &= ~PDE_FRAME; + *entry |= frame << 12; +} + +phys_addr pd_entry_get_frame(pd_entry_t entry){ + return entry >> 12; +} + +bool pd_entry_is_present(pd_entry_t entry){ + return entry & PDE_PRESENT; +} + +bool pd_entry_is_user(pd_entry_t entry){ + return entry & PDE_USER; +} + +bool pd_entry_is_4mb(pd_entry_t entry){ + return entry & PDE_4MB; +} + +bool pd_entry_is_writable(pd_entry_t entry){ + return entry & PDE_WRITABLE; +} diff --git a/src/kernel/mm/pde.h b/src/kernel/mm/pde.h new file mode 100644 index 0000000..6c9b04b --- /dev/null +++ b/src/kernel/mm/pde.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include "pmm.h" + +enum PDE_PAGE_FLAGS{ + PDE_PRESENT = 0x0001, + PDE_WRITABLE = 0x0002, + PDE_USER = 0x0004, + PDE_WRITETHROUGH = 0x0008, + PDE_CACHE_DISABLED = 0x0010, + PDE_ACCESSED = 0x0020, + PDE_DIRTY = 0x0040, + PDE_4MB = 0x0080, + PDE_CPU_GLOBAL = 0x0100, + PDE_LV4_GLOBAL = 0x0200, + PDE_FRAME = 0x7FFFF000, +}; + +typedef uint32_t pd_entry_t; + +void pd_entry_add_attr(pd_entry_t *entry, uint32_t attr); +void pd_entry_del_attr(pd_entry_t *entry, uint32_t attr); +void pd_entry_set_frame(pd_entry_t *entry, phys_addr frame); +phys_addr pd_entry_get_frame(pd_entry_t entry); +bool pd_entry_is_present(pd_entry_t entry); +bool pd_entry_is_user(pd_entry_t entry); +bool pd_entry_is_4mb(pd_entry_t entry); +bool pd_entry_is_writable(pd_entry_t entry); diff --git a/src/kernel/mm/pmm.asm b/src/kernel/mm/pmm.asm new file mode 100644 index 0000000..51db936 --- /dev/null +++ b/src/kernel/mm/pmm.asm @@ -0,0 +1,43 @@ +global pmm_paging_enable +pmm_paging_enable: + [bits 32] + ; New call frame + push ebp + mov ebp, esp + + mov eax, cr0 + cmp byte [esp + 4], 0 + jg .enable + jmp .disable +.enable: + or eax, 0x80000000 ; Set bit 31 + jmp .done +.disable: + and eax, 0x7FFFFFFF ; Clear bit 31 +.done: + mov cr0, eax + + ; Restore old call frame + mov esp, ebp + pop ebp + ret + +global pmm_is_paging +pmm_is_paging: + [bits 32] + mov eax, cr0 + and eax, 0x80000000 + ret + +global pmm_load_pdbr +pmm_load_pdbr: + [bits 32] + mov eax, [esp + 4] + mov cr3, eax + ret + +global pmm_get_pdbr +pmm_get_pdbr: + [bits 32] + mov eax, cr3 + ret diff --git a/src/kernel/mm/pmm.c b/src/kernel/mm/pmm.c index 9d4498a..dfb3fd0 100644 --- a/src/kernel/mm/pmm.c +++ b/src/kernel/mm/pmm.c @@ -62,7 +62,6 @@ void pmm_initialize(size_t mem_size, phys_addr bitmap){ g_pmm_max_blocks = (pmm_get_memory_size()*1024) / PMM_BLOCK_SIZE; g_pmm_used_blocks = g_pmm_max_blocks; - // FIXME memset(g_pmm_memory_map, 0xFF, pmm_get_block_count() / PMM_BLOCKS_PER_BYTE); } diff --git a/src/kernel/mm/pmm.h b/src/kernel/mm/pmm.h index 3ad6f88..be4e2c1 100644 --- a/src/kernel/mm/pmm.h +++ b/src/kernel/mm/pmm.h @@ -22,3 +22,7 @@ size_t pmm_get_memory_size(); uint32_t pmm_get_block_count(); uint32_t pmm_get_used_block_count(); uint32_t pmm_get_free_block_count(); +void __attribute__((cdecl)) pmm_paging_enable(bool enabled); +bool __attribute__((cdecl)) pmm_is_paging(); +void __attribute__((cdecl)) pmm_load_pdbr(phys_addr addr); +phys_addr __attribute__((cdecl)) pmm_get_pdbr(); diff --git a/src/kernel/mm/pte.c b/src/kernel/mm/pte.c new file mode 100644 index 0000000..774f05a --- /dev/null +++ b/src/kernel/mm/pte.c @@ -0,0 +1,26 @@ +#include "pte.h" + +void pt_entry_add_attr(pt_entry_t *entry, uint32_t attr){ + *entry |= attr; +} + +void pt_entry_del_attr(pt_entry_t *entry, uint32_t attr){ + *entry &= ~attr; +} + +void pt_entry_set_frame(pt_entry_t *entry, phys_addr frame){ + *entry &= ~PTE_FRAME; + *entry |= frame << 12; +} + +phys_addr pt_entry_get_frame(pt_entry_t entry){ + return entry >> 12; +} + +bool pt_entry_is_present(pt_entry_t entry){ + return entry & PTE_PRESENT; +} + +bool pt_entry_is_writable(pt_entry_t entry){ + return entry & PTE_WRITABLE; +} diff --git a/src/kernel/mm/pte.h b/src/kernel/mm/pte.h new file mode 100644 index 0000000..445abe0 --- /dev/null +++ b/src/kernel/mm/pte.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include "pmm.h" + +enum PTE_PAGE_FLAGS{ + PTE_PRESENT = 0x0001, + PTE_WRITABLE = 0x0002, + PTE_USER = 0x0004, + PTE_WRITETHROUGH = 0x0008, + PTE_NOT_CACHEABLE = 0x0010, + PTE_ACCESSED = 0x0020, + PTE_DIRTY = 0x0040, + PTE_PAT = 0x0080, + PTE_CPU_GLOBAL = 0x0100, + PTE_LV4_GLOBAL = 0x0200, + PTE_FRAME = 0x7FFFF000 +}; + +typedef uint32_t pt_entry_t; + +void pt_entry_add_attr(pt_entry_t *entry, uint32_t attr); +void pt_entry_del_attr(pt_entry_t *entry, uint32_t attr); +void pt_entry_set_frame(pt_entry_t *entry, phys_addr frame); +phys_addr pt_entry_get_frame(pt_entry_t entry); +bool pt_entry_is_present(pt_entry_t entry); +bool pt_entry_is_writable(pt_entry_t entry); diff --git a/src/kernel/mm/vmm.asm b/src/kernel/mm/vmm.asm new file mode 100644 index 0000000..e9883ae --- /dev/null +++ b/src/kernel/mm/vmm.asm @@ -0,0 +1,8 @@ +global vmm_flush_tlb_entry +vmm_flush_tlb_entry: + [bits 32] + cli + invlpg [esp + 4] + sti + + ret diff --git a/src/kernel/mm/vmm.c b/src/kernel/mm/vmm.c new file mode 100644 index 0000000..acb2887 --- /dev/null +++ b/src/kernel/mm/vmm.c @@ -0,0 +1,142 @@ +#include "vmm.h" +#include "pmm.h" +#include "../lib/memory.h" + +vmm_page_directory_t *g_vmm_current_dir = 0; +phys_addr g_vmm_current_pdbr = 0; + +void __attribute__((cdecl)) vmm_flush_tlb_entry(virt_addr addr); + +static inline pt_entry_t *vmm_ptable_lookup_entry(vmm_page_table_t *p, virt_addr addr){ + if(p) return &p->entries[PAGE_TABLE_INDEX(addr)]; + return 0; +} + +static inline pd_entry_t *vmm_pdirectory_lookup_entry(vmm_page_directory_t *p, virt_addr addr){ + if(p) return &p->entries[PAGE_DIRECTORY_INDEX(addr)]; + return 0; +} + +static inline bool vmm_switch_pdirectory(vmm_page_directory_t *dir){ + if(!dir) return false; + + g_vmm_current_dir = dir; + pmm_load_pdbr(g_vmm_current_pdbr); + return true; +} + +static inline vmm_page_directory_t *vmm_get_pdirectory(){ + return g_vmm_current_dir; +} + +bool vmm_alloc_page(pt_entry_t *entry){ + void *p = pmm_alloc_block(); + if(!p) return false; + + pt_entry_set_frame(entry, (phys_addr)p); + pt_entry_add_attr(entry, PTE_PRESENT); + + return true; +} + +void vmm_free_page(pt_entry_t *entry){ + void *p = (void *)pt_entry_get_frame(*entry); + if(p) pmm_free_block(p); + + pt_entry_del_attr(entry, PTE_PRESENT); +} + +void vmm_map_page(void *phys, void *virt){ + // Get page directory + vmm_page_directory_t *page_dir = vmm_get_pdirectory(); + + // Get page table + pd_entry_t *e = &page_dir->entries[PAGE_DIRECTORY_INDEX((uint32_t)virt)]; + if((*e & PTE_PRESENT) != PTE_PRESENT){ + // Must allocate + vmm_page_table_t *table = (vmm_page_table_t *)pmm_alloc_block(); + if(!table) return; + + // Clear page table + memset(table, 0, sizeof(vmm_page_table_t)); + + // Create a new entry + pd_entry_t *entry = &page_dir->entries[PAGE_DIRECTORY_INDEX((uint32_t)virt)]; + + // Map in the table + pd_entry_add_attr(entry, PDE_PRESENT); + pd_entry_add_attr(entry, PDE_WRITABLE); + pd_entry_set_frame(entry, (phys_addr)table); + } + + // Get table + vmm_page_table_t *table = (vmm_page_table_t *)PAGE_GET_PHYS_ADDR(e); + + // Get page + pt_entry_t *page = &table->entries[PAGE_TABLE_INDEX((uint32_t)virt)]; + + // Map it + pt_entry_set_frame(page, (phys_addr)phys); + pt_entry_add_attr(page, PTE_PRESENT); +} + +void vmm_initialize(){ + // Allocate default page table + vmm_page_table_t *table = (vmm_page_table_t *)pmm_alloc_block(); + if(!table) return; + + // Allocate 3GB page table + vmm_page_table_t *table2 = (vmm_page_table_t *)pmm_alloc_block(); + if(!table2) return; + + // Clear default table + memset(table, 0, sizeof(vmm_page_table_t)); + + // First 1MB are identity mapped + for(int i = 0, frame = 0x0, virt = 0x00000000; i<1024; i++, frame += 4096, virt += 4096){ + // Create a new page + pt_entry_t page = 0; + pt_entry_add_attr(&page, PTE_PRESENT); + pt_entry_set_frame(&page, frame); + + // Add the page to the page table + table2->entries[PAGE_TABLE_INDEX(virt)] = page; + } + + // Map 1MB to 3GB + for(int i = 0, frame = 0x100000, virt = 0xc0000000; i<1024; i++, frame += 4096, virt += 4096){ + // Create a new page + pt_entry_t page = 0; + pt_entry_add_attr(&page, PTE_PRESENT); + pt_entry_set_frame(&page, frame); + + // Add the page to the page table + table->entries[PAGE_TABLE_INDEX(virt)] = page; + } + + // Create default directory table + vmm_page_directory_t *dir = (vmm_page_directory_t *)pmm_alloc_blocks(3); + if(!dir) return; + + // Clear directory table and set it as current + memset(dir, 0, sizeof(vmm_page_directory_t)); + + pd_entry_t *entry = &dir->entries[PAGE_DIRECTORY_INDEX(0xc0000000)]; + pd_entry_add_attr(entry, PDE_PRESENT); + pd_entry_add_attr(entry, PDE_WRITABLE); + pd_entry_set_frame(entry, (phys_addr)table); + + pd_entry_t *entry2 = &dir->entries[PAGE_DIRECTORY_INDEX(0x00000000)]; + pd_entry_add_attr(entry2, PDE_PRESENT); + pd_entry_add_attr(entry2, PDE_WRITABLE); + pd_entry_set_frame(entry2, (phys_addr)table2); + + // Store current PDBR + g_vmm_current_pdbr = (phys_addr)&dir->entries; + + // Switch to our page directory + vmm_switch_pdirectory(dir); + + // Enable paging + pmm_paging_enable(true); +} diff --git a/src/kernel/mm/vmm.h b/src/kernel/mm/vmm.h new file mode 100644 index 0000000..0c2bab7 --- /dev/null +++ b/src/kernel/mm/vmm.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include "pte.h" +#include "pde.h" + +typedef uint32_t virt_addr; + +#define PAGES_PER_TABLE 1024 +#define PAGES_PER_DIR 1024 + +#define PAGE_DIRECTORY_INDEX(x) (((x) >> 22) & 0x3FF) +#define PAGE_TABLE_INDEX(x) (((x) >> 12) & 0x3FF) +#define PAGE_GET_PHYS_ADDR(x) (*(x) & ~0xFFF) + +#define PTABLE_ADDR_SPACE_SIZE 0x400000 +#define DTABLE_ADDR_SPACE_SIZE 0x100000000 + +#define PAGE_SIZE 4096 + +typedef struct{ + pt_entry_t entries[PAGES_PER_TABLE]; +} vmm_page_table_t; + +typedef struct{ + pd_entry_t entries[PAGES_PER_DIR]; +} vmm_page_directory_t; + +bool vmm_alloc_page(pt_entry_t *entry); +void vmm_free_page(pt_entry_t *entry); +void vmm_map_page(void *phys, void *virt); +void vmm_initialize();