Compare commits

...

2 Commits

Author SHA1 Message Date
b360307f41 VMM: Make it so much gooder
There are still some fixes to be made, but I think this is already way cleaner than before.
2022-10-13 17:58:13 +02:00
9f2c9fb190 Kernel: Make Utilities be inline 2022-10-13 17:17:28 +02:00
4 changed files with 243 additions and 318 deletions

View File

@ -10,7 +10,6 @@ enum Flags
namespace VMM namespace VMM
{ {
void init(); // fetch page table from cr3 void init(); // fetch page table from cr3
void init(PageTable* cr3);
void map(uint64_t virtualAddress, uint64_t physicalAddress, int flags); void map(uint64_t virtualAddress, uint64_t physicalAddress, int flags);
void remap(uint64_t virtualAddress, int flags); void remap(uint64_t virtualAddress, int flags);
@ -18,4 +17,11 @@ namespace VMM
uint64_t getPhysical(uint64_t virtualAddress); uint64_t getPhysical(uint64_t virtualAddress);
uint64_t getFlags(uint64_t virtualAddress); uint64_t getFlags(uint64_t virtualAddress);
PageDirectoryEntry* find_pde(PageTable* root, uint64_t virtualAddress);
PageDirectoryEntry* create_pde_if_not_exists(PageTable* root, uint64_t virtualAddress);
void propagate_read_write(PageTable* root, uint64_t virtualAddress);
void propagate_user(PageTable* root, uint64_t virtualAddress);
void flush_tlb(uint64_t addr);
}; };

View File

@ -1,13 +1,37 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif
extern "C" uint64_t asm_get_rflags();
namespace Utilities namespace Utilities
{ {
uint64_t get_blocks_from_size(uint64_t blocksize, inline uint64_t get_blocks_from_size(uint64_t blocksize, uint64_t size)
uint64_t size); // Returns how many blocks of size blocksize does size occupy. {
return (size + (blocksize - 1)) / blocksize;
}
uint64_t get_rflags(); inline uint64_t get_rflags()
uint64_t get_top_of_stack(uint64_t bottom, uint64_t stack_pages); {
uint64_t round_down_to_nearest_page(uint64_t addr); return asm_get_rflags();
uint64_t round_up_to_nearest_page(uint64_t addr); }
inline uint64_t get_top_of_stack(uint64_t bottom, uint64_t stack_pages)
{
return bottom + (stack_pages * PAGE_SIZE) - sizeof(uintptr_t);
}
inline uint64_t round_down_to_nearest_page(uint64_t addr)
{
return addr - (addr % PAGE_SIZE);
}
inline uint64_t round_up_to_nearest_page(uint64_t addr)
{
if (addr % PAGE_SIZE) return addr + (PAGE_SIZE - (addr % PAGE_SIZE));
return addr;
}
} }

View File

@ -1,6 +1,10 @@
#define MODULE "vmm"
#include "memory/VMM.h" #include "memory/VMM.h"
#include "assert.h" #include "assert.h"
#include "log/Log.h"
#include "memory/PMM.h" #include "memory/PMM.h"
#include "misc/utils.h"
#include "std/string.h" #include "std/string.h"
// FIXME: There is a lot of duplicate code in this file. This should probably be refactored. // FIXME: There is a lot of duplicate code in this file. This should probably be refactored.
@ -12,322 +16,245 @@ void VMM::init()
asm volatile("mov %%cr3, %0" : "=r"(PML4)); asm volatile("mov %%cr3, %0" : "=r"(PML4));
} }
void VMM::init(PageTable* cr3)
{
PML4 = cr3;
}
void VMM::unmap(uint64_t virtualAddress) void VMM::unmap(uint64_t virtualAddress)
{ {
virtualAddress >>= 12; virtualAddress = Utilities::round_down_to_nearest_page(virtualAddress);
uint64_t P_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PT_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PD_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PDP_i = virtualAddress & 0x1ff;
PageDirectoryEntry PDE; PageDirectoryEntry* pde = find_pde(PML4, virtualAddress);
if (!pde) return; // Already unmapped
PDE = PML4->entries[PDP_i]; memset(pde, 0, sizeof(PageDirectoryEntry));
PageTable* PDP; flush_tlb(virtualAddress);
if (!PDE.Present)
{
return; // Already unmapped
}
else
{
if (PDE.LargerPages)
{
PDE.Present = false;
PDE.LargerPages = false;
PML4->entries[PDP_i] = PDE;
goto invalidate;
}
PDP = (PageTable*)((uint64_t)PDE.Address << 12);
}
PDE = PDP->entries[PD_i];
PageTable* PD;
if (!PDE.Present)
{
return; // Already unmapped
}
else
{
if (PDE.LargerPages)
{
PDE.Present = false;
PDE.LargerPages = false;
PDP->entries[PD_i] = PDE;
goto invalidate;
}
PD = (PageTable*)((uint64_t)PDE.Address << 12);
}
PDE = PD->entries[PT_i];
PageTable* PT;
if (!PDE.Present)
{
return; // Already unmapped
}
else
{
if (PDE.LargerPages)
{
PDE.LargerPages = false;
PDE.Present = false;
PD->entries[PT_i] = PDE;
goto invalidate;
}
PT = (PageTable*)((uint64_t)PDE.Address << 12);
}
PDE = PT->entries[P_i];
PDE.Present = false;
PT->entries[P_i] = PDE;
invalidate:
asm volatile("invlpg (%0)" : : "r"(virtualAddress) : "memory");
} }
uint64_t VMM::getPhysical(uint64_t virtualAddress) uint64_t VMM::getPhysical(uint64_t virtualAddress)
{ {
virtualAddress >>= 12; PageDirectoryEntry* pde = find_pde(PML4, Utilities::round_down_to_nearest_page(virtualAddress));
uint64_t P_i = virtualAddress & 0x1ff; if (!pde) return UINT64_MAX; // Not mapped
virtualAddress >>= 9;
uint64_t PT_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PD_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PDP_i = virtualAddress & 0x1ff;
PageDirectoryEntry PDE; return pde->Address << 12 | (virtualAddress % PAGE_SIZE);
PDE = PML4->entries[PDP_i];
PageTable* PDP;
if (!PDE.Present)
{
return UINT64_MAX; // Not mapped
}
else
{
if (PDE.LargerPages) return PDE.Address << 12 | (virtualAddress & PAGE_SIZE);
PDP = (PageTable*)((uint64_t)PDE.Address << 12);
}
PDE = PDP->entries[PD_i];
PageTable* PD;
if (!PDE.Present)
{
return UINT64_MAX; // Not mapped
}
else
{
if (PDE.LargerPages) return PDE.Address << 12 | (virtualAddress & PAGE_SIZE);
PD = (PageTable*)((uint64_t)PDE.Address << 12);
}
PDE = PD->entries[PT_i];
PageTable* PT;
if (!PDE.Present)
{
return UINT64_MAX; // Not mapped
}
else
{
if (PDE.LargerPages) return PDE.Address << 12 | (virtualAddress & PAGE_SIZE);
PT = (PageTable*)((uint64_t)PDE.Address << 12);
}
PDE = PT->entries[P_i];
if (!PDE.Present) return UINT64_MAX;
return PDE.Address << 12 | (virtualAddress & PAGE_SIZE);
} }
uint64_t VMM::getFlags(uint64_t virtualAddress) // FIXME: Add support for larger pages to getFlags. uint64_t VMM::getFlags(uint64_t virtualAddress)
{ {
virtualAddress >>= 12; PageDirectoryEntry* pde = find_pde(PML4, Utilities::round_down_to_nearest_page(virtualAddress));
uint64_t P_i = virtualAddress & 0x1ff; if (!pde) return 0; // Not mapped
virtualAddress >>= 9;
uint64_t PT_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PD_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PDP_i = virtualAddress & 0x1ff;
PageDirectoryEntry PDE;
PDE = PML4->entries[PDP_i];
PageTable* PDP;
if (!PDE.Present)
{
return 0; // Not mapped
}
else
{
if (PDE.LargerPages) goto read_flags;
PDP = (PageTable*)((uint64_t)PDE.Address << 12);
}
PDE = PDP->entries[PD_i];
PageTable* PD;
if (!PDE.Present)
{
return 0; // Not mapped
}
else
{
if (PDE.LargerPages) goto read_flags;
PD = (PageTable*)((uint64_t)PDE.Address << 12);
}
PDE = PD->entries[PT_i];
PageTable* PT;
if (!PDE.Present)
{
return 0; // Not mapped
}
else
{
if (PDE.LargerPages) goto read_flags;
PT = (PageTable*)((uint64_t)PDE.Address << 12);
}
PDE = PT->entries[P_i];
read_flags:
uint64_t flags = 0; uint64_t flags = 0;
if (PDE.UserSuper) flags |= User; if (pde->UserSuper) flags |= User;
if (PDE.ReadWrite) flags |= ReadWrite; if (pde->ReadWrite) flags |= ReadWrite;
return flags; return flags;
} }
void VMM::map(uint64_t virtualAddress, uint64_t physicalAddress, int flags) void VMM::map(uint64_t virtualAddress, uint64_t physicalAddress, int flags)
{ {
virtualAddress >>= 12; virtualAddress = Utilities::round_down_to_nearest_page(virtualAddress);
uint64_t P_i = virtualAddress & 0x1ff; PageDirectoryEntry* pde = find_pde(PML4, virtualAddress);
virtualAddress >>= 9; bool will_flush_tlb = true;
uint64_t PT_i = virtualAddress & 0x1ff; if (!pde)
virtualAddress >>= 9;
uint64_t PD_i = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t PDP_i = virtualAddress & 0x1ff;
PageDirectoryEntry PDE;
PDE = PML4->entries[PDP_i];
PageTable* PDP;
if (!PDE.Present)
{ {
PDP = (PageTable*)PMM::request_page(); pde = create_pde_if_not_exists(PML4, virtualAddress);
ASSERT(!(PMM_DID_FAIL(PDP))); will_flush_tlb = false;
memset(PDP, 0, PAGE_SIZE);
PDE.set_address((uint64_t)PDP);
PDE.Present = true;
PDE.ReadWrite = true;
if (flags & User) PDE.UserSuper = true;
PML4->entries[PDP_i] = PDE;
} }
else else if (pde->LargerPages)
{
if (PDE.LargerPages)
{ {
unmap(virtualAddress); unmap(virtualAddress);
PDE.LargerPages = false; pde = create_pde_if_not_exists(PML4, virtualAddress);
PDP = (PageTable*)PMM::request_page(); will_flush_tlb = false;
ASSERT(!(PMM_DID_FAIL(PDP)));
memset(PDP, 0, PAGE_SIZE);
PDE.set_address((uint64_t)PDP);
PDE.Present = true;
PDE.ReadWrite = true;
if (flags & User) PDE.UserSuper = true;
PML4->entries[PDP_i] = PDE;
}
PDP = (PageTable*)((uint64_t)PDE.Address << 12);
}
if ((flags & User) && !PDE.UserSuper)
{
PDE.UserSuper = true;
PML4->entries[PDP_i] = PDE;
} }
PDE = PDP->entries[PD_i]; pde->set_address(Utilities::round_down_to_nearest_page(physicalAddress));
PageTable* PD; if (flags & User) propagate_user(PML4, virtualAddress);
if (!PDE.Present) if (flags & ReadWrite) propagate_read_write(PML4, virtualAddress);
{ if (will_flush_tlb) flush_tlb(virtualAddress);
PD = (PageTable*)PMM::request_page(); }
ASSERT(!(PMM_DID_FAIL(PD)));
memset(PD, 0, PAGE_SIZE); PageDirectoryEntry* VMM::find_pde(PageTable* root, uint64_t virtualAddress)
PDE.set_address((uint64_t)PD); {
PDE.Present = true; virtualAddress >>= 12;
PDE.ReadWrite = true; uint64_t page_index = virtualAddress & 0x1ff;
if (flags & User) PDE.UserSuper = true; virtualAddress >>= 9;
PDP->entries[PD_i] = PDE; uint64_t pt_index = virtualAddress & 0x1ff;
} virtualAddress >>= 9;
else uint64_t pd_index = virtualAddress & 0x1ff;
{ virtualAddress >>= 9;
if (PDE.LargerPages) uint64_t pdp_index = virtualAddress & 0x1ff;
{ PageDirectoryEntry* pde;
unmap(virtualAddress); PageTable* pt;
PDE.LargerPages = false;
PD = (PageTable*)PMM::request_page(); pde = &root->entries[pdp_index]; // PML4
ASSERT(!(PMM_DID_FAIL(PD))); if (!pde->Present) return nullptr;
memset(PD, 0, PAGE_SIZE); else if (pde->LargerPages)
PDE.set_address((uint64_t)PD); return pde;
PDE.Present = true; else { pt = (PageTable*)((uint64_t)pde->Address << 12); }
PDE.ReadWrite = true;
if (flags & User) PDE.UserSuper = true; pde = &pt->entries[pd_index]; // PDP
PDP->entries[PD_i] = PDE; if (!pde->Present) return nullptr;
} else if (pde->LargerPages)
PD = (PageTable*)((uint64_t)PDE.Address << 12); return pde;
} else { pt = (PageTable*)((uint64_t)pde->Address << 12); }
if ((flags & User) && !PDE.UserSuper)
{ pde = &pt->entries[pt_index]; // PD
PDE.UserSuper = true; if (!pde->Present) return nullptr;
PDP->entries[PD_i] = PDE; else if (pde->LargerPages)
} return pde;
else { pt = (PageTable*)((uint64_t)pde->Address << 12); }
PDE = PD->entries[PT_i];
PageTable* PT; pde = &pt->entries[page_index]; // PT
if (!PDE.Present) if (!pde->Present) return nullptr;
{ return pde;
PT = (PageTable*)PMM::request_page(); }
ASSERT(!(PMM_DID_FAIL(PT)));
memset(PT, 0, PAGE_SIZE); PageDirectoryEntry* VMM::create_pde_if_not_exists(PageTable* root, uint64_t virtualAddress)
PDE.set_address((uint64_t)PT); {
PDE.Present = true; virtualAddress >>= 12;
PDE.ReadWrite = true; uint64_t page_index = virtualAddress & 0x1ff;
if (flags & User) PDE.UserSuper = true; virtualAddress >>= 9;
PD->entries[PT_i] = PDE; uint64_t pt_index = virtualAddress & 0x1ff;
} virtualAddress >>= 9;
else uint64_t pd_index = virtualAddress & 0x1ff;
{ virtualAddress >>= 9;
if (PDE.LargerPages) uint64_t pdp_index = virtualAddress & 0x1ff;
{ PageDirectoryEntry* pde;
unmap(virtualAddress); PageTable* pt;
PDE.LargerPages = false;
PT = (PageTable*)PMM::request_page(); pde = &root->entries[pdp_index]; // PML4
ASSERT(!(PMM_DID_FAIL(PT))); if (!pde->Present)
memset(PT, 0, PAGE_SIZE); {
PDE.set_address((uint64_t)PT); pt = (PageTable*)PMM::request_page();
PDE.Present = true; ASSERT(!(PMM_DID_FAIL(pt)));
PDE.ReadWrite = true; memset(pt, 0, PAGE_SIZE);
if (flags & User) PDE.UserSuper = true; pde->set_address((uint64_t)pt);
PD->entries[PT_i] = PDE; pde->Present = true;
} }
PT = (PageTable*)((uint64_t)PDE.Address << 12); else if (pde->LargerPages)
} return pde;
if ((flags & User) && !PDE.UserSuper) else { pt = (PageTable*)((uint64_t)pde->Address << 12); }
{
PDE.UserSuper = true; pde = &pt->entries[pd_index]; // PDP
PD->entries[PT_i] = PDE; if (!pde->Present)
} {
pt = (PageTable*)PMM::request_page();
PDE = PT->entries[P_i]; ASSERT(!(PMM_DID_FAIL(pt)));
PDE.Present = true; memset(pt, 0, PAGE_SIZE);
PDE.ReadWrite = flags & ReadWrite; pde->set_address((uint64_t)pt);
PDE.UserSuper = flags & User; pde->Present = true;
PDE.set_address(physicalAddress); }
PT->entries[P_i] = PDE; else if (pde->LargerPages)
return pde;
else { pt = (PageTable*)((uint64_t)pde->Address << 12); }
pde = &pt->entries[pt_index]; // PD
if (!pde->Present)
{
pt = (PageTable*)PMM::request_page();
ASSERT(!(PMM_DID_FAIL(pt)));
memset(pt, 0, PAGE_SIZE);
pde->set_address((uint64_t)pt);
pde->Present = true;
}
else if (pde->LargerPages)
return pde;
else { pt = (PageTable*)((uint64_t)pde->Address << 12); }
pde = &pt->entries[page_index]; // PT
if (!pde->Present) { pde->Present = true; }
return pde;
}
void VMM::propagate_read_write(PageTable* root, uint64_t virtualAddress)
{
virtualAddress >>= 12;
uint64_t page_index = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t pt_index = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t pd_index = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t pdp_index = virtualAddress & 0x1ff;
PageDirectoryEntry* pde;
PageTable* pt;
pde = &root->entries[pdp_index]; // PML4
if (!pde->Present) return;
else
{
pde->ReadWrite = true;
if (pde->LargerPages) return;
pt = (PageTable*)((uint64_t)pde->Address << 12);
}
pde = &pt->entries[pd_index]; // PDP
if (!pde->Present) return;
else
{
pde->ReadWrite = true;
if (pde->LargerPages) return;
pt = (PageTable*)((uint64_t)pde->Address << 12);
}
pde = &pt->entries[pt_index]; // PD
if (!pde->Present) return;
else
{
pde->ReadWrite = true;
if (pde->LargerPages) return;
pt = (PageTable*)((uint64_t)pde->Address << 12);
}
pde = &pt->entries[page_index];
if (!pde->Present) return;
else
pde->ReadWrite = true;
}
void VMM::propagate_user(PageTable* root, uint64_t virtualAddress)
{
virtualAddress >>= 12;
uint64_t page_index = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t pt_index = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t pd_index = virtualAddress & 0x1ff;
virtualAddress >>= 9;
uint64_t pdp_index = virtualAddress & 0x1ff;
PageDirectoryEntry* pde;
PageTable* pt;
pde = &root->entries[pdp_index]; // PML4
if (!pde->Present) return;
else
{
pde->UserSuper = true;
if (pde->LargerPages) return;
pt = (PageTable*)((uint64_t)pde->Address << 12);
}
pde = &pt->entries[pd_index]; // PDP
if (!pde->Present) return;
else
{
pde->UserSuper = true;
if (pde->LargerPages) return;
pt = (PageTable*)((uint64_t)pde->Address << 12);
}
pde = &pt->entries[pt_index]; // PD
if (!pde->Present) return;
else
{
pde->UserSuper = true;
if (pde->LargerPages) return;
pt = (PageTable*)((uint64_t)pde->Address << 12);
}
pde = &pt->entries[page_index];
if (!pde->Present) return;
else
pde->UserSuper = true;
}
void VMM::flush_tlb(uint64_t addr)
{
asm volatile("invlpg (%0)" : : "r"(addr) : "memory");
} }

View File

@ -1,32 +0,0 @@
#include "misc/utils.h"
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif
uint64_t Utilities::get_blocks_from_size(uint64_t blocksize, uint64_t size)
{
return (size + (blocksize - 1)) / blocksize;
}
extern "C" uint64_t asm_get_rflags();
uint64_t Utilities::get_rflags()
{
return asm_get_rflags();
}
uint64_t Utilities::get_top_of_stack(uint64_t bottom, uint64_t stack_pages)
{
return bottom + (stack_pages * PAGE_SIZE) - sizeof(uintptr_t);
}
uint64_t Utilities::round_down_to_nearest_page(uint64_t addr)
{
return addr - (addr % PAGE_SIZE);
}
uint64_t Utilities::round_up_to_nearest_page(uint64_t addr)
{
if (addr % PAGE_SIZE) return addr + (PAGE_SIZE - (addr % PAGE_SIZE));
return addr;
}