Virtual memory manager

This commit is contained in:
Arnau Camprubí 2022-07-23 01:42:19 +02:00
pare 1d64e83647
commit 3ff5e1fd08
S'han modificat 11 arxius amb 351 adicions i 7 eliminacions

Veure arxiu

@ -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(;;);
}

34
src/kernel/mm/pde.c Normal file
Veure arxiu

@ -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;
}

30
src/kernel/mm/pde.h Normal file
Veure arxiu

@ -0,0 +1,30 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#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);

43
src/kernel/mm/pmm.asm Normal file
Veure arxiu

@ -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

Veure arxiu

@ -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);
}

Veure arxiu

@ -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();

26
src/kernel/mm/pte.c Normal file
Veure arxiu

@ -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;
}

27
src/kernel/mm/pte.h Normal file
Veure arxiu

@ -0,0 +1,27 @@
#pragma once
#include <stdint.h>
#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);

8
src/kernel/mm/vmm.asm Normal file
Veure arxiu

@ -0,0 +1,8 @@
global vmm_flush_tlb_entry
vmm_flush_tlb_entry:
[bits 32]
cli
invlpg [esp + 4]
sti
ret

142
src/kernel/mm/vmm.c Normal file
Veure arxiu

@ -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);
}

33
src/kernel/mm/vmm.h Normal file
Veure arxiu

@ -0,0 +1,33 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#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();