291 lines
7.4 KiB
C++
291 lines
7.4 KiB
C++
#define MODULE "vmm"
|
|
|
|
#include "memory/VMM.h"
|
|
#include "assert.h"
|
|
#include "log/Log.h"
|
|
#include "memory/PMM.h"
|
|
#include "misc/utils.h"
|
|
#include "std/string.h"
|
|
#include "utils/Addresses.h"
|
|
#include "utils/Registers.h"
|
|
|
|
static PageTable* kernel_pml4;
|
|
static PageTable* current_pml4;
|
|
static AddressSpace* user_address_space;
|
|
|
|
void VMM::switch_back_to_kernel_address_space()
|
|
{
|
|
if (current_pml4 != kernel_pml4) { current_pml4 = kernel_pml4; }
|
|
}
|
|
|
|
void VMM::switch_to_user_address_space(AddressSpace& space)
|
|
{
|
|
user_address_space = &space;
|
|
current_pml4 = user_address_space->get_pml4();
|
|
}
|
|
|
|
void VMM::switch_to_previous_user_address_space()
|
|
{
|
|
current_pml4 = user_address_space->get_pml4();
|
|
}
|
|
|
|
void VMM::enter_syscall_context()
|
|
{
|
|
if (current_pml4 != kernel_pml4)
|
|
{
|
|
current_pml4 = kernel_pml4;
|
|
apply_address_space();
|
|
switch_to_previous_user_address_space();
|
|
}
|
|
}
|
|
|
|
void VMM::exit_syscall_context()
|
|
{
|
|
if (current_pml4 != user_address_space->get_pml4()) { switch_to_previous_user_address_space(); }
|
|
apply_address_space();
|
|
}
|
|
|
|
void VMM::apply_address_space()
|
|
{
|
|
write_cr3(current_pml4);
|
|
}
|
|
|
|
bool VMM::is_using_kernel_address_space()
|
|
{
|
|
return current_pml4 == kernel_pml4;
|
|
}
|
|
|
|
void VMM::init()
|
|
{
|
|
kernel_pml4 = (PageTable*)read_cr3();
|
|
current_pml4 = kernel_pml4;
|
|
}
|
|
|
|
void VMM::unmap(uint64_t vaddr)
|
|
{
|
|
vaddr = round_down_to_nearest_page(vaddr);
|
|
|
|
PageDirectoryEntry* pde = find_pde(current_pml4, vaddr);
|
|
if (!pde) return; // Already unmapped
|
|
|
|
memset(pde, 0, sizeof(PageDirectoryEntry));
|
|
flush_tlb(vaddr);
|
|
}
|
|
|
|
void VMM::remap(uint64_t vaddr, int flags)
|
|
{
|
|
vaddr = round_down_to_nearest_page(vaddr);
|
|
|
|
PageDirectoryEntry* pde = find_pde(current_pml4, vaddr);
|
|
if (!pde) return; // Not mapped
|
|
|
|
if (flags & User) propagate_user(current_pml4, vaddr);
|
|
else
|
|
pde->user = false;
|
|
if (flags & ReadWrite) propagate_read_write(current_pml4, vaddr);
|
|
else
|
|
pde->read_write = false;
|
|
flush_tlb(vaddr);
|
|
}
|
|
|
|
uint64_t VMM::get_physical(uint64_t vaddr)
|
|
{
|
|
PageDirectoryEntry* pde = find_pde(current_pml4, round_down_to_nearest_page(vaddr));
|
|
if (!pde) return UINT64_MAX; // Not mapped
|
|
|
|
return pde->get_address() | (vaddr % PAGE_SIZE);
|
|
}
|
|
|
|
uint64_t VMM::get_flags(uint64_t vaddr)
|
|
{
|
|
PageDirectoryEntry* pde = find_pde(current_pml4, round_down_to_nearest_page(vaddr));
|
|
if (!pde) return 0; // Not mapped
|
|
|
|
uint64_t flags = 0;
|
|
if (pde->user) flags |= User;
|
|
if (pde->read_write) flags |= ReadWrite;
|
|
return flags;
|
|
}
|
|
|
|
void VMM::map(uint64_t vaddr, uint64_t paddr, int flags)
|
|
{
|
|
vaddr = round_down_to_nearest_page(vaddr);
|
|
PageDirectoryEntry* pde = find_pde(current_pml4, vaddr);
|
|
bool will_flush_tlb = true;
|
|
if (!pde)
|
|
{
|
|
pde = create_pde_if_not_exists(current_pml4, vaddr);
|
|
will_flush_tlb = false;
|
|
}
|
|
else if (pde->larger_pages)
|
|
{
|
|
unmap(vaddr);
|
|
pde = create_pde_if_not_exists(current_pml4, vaddr);
|
|
}
|
|
|
|
pde->set_address(round_down_to_nearest_page(paddr));
|
|
if (flags & User) propagate_user(current_pml4, vaddr);
|
|
else
|
|
pde->user = false;
|
|
if (flags & ReadWrite) propagate_read_write(current_pml4, vaddr);
|
|
else
|
|
pde->read_write = false;
|
|
if (will_flush_tlb) flush_tlb(vaddr);
|
|
}
|
|
|
|
PageDirectoryEntry* VMM::find_pde(PageTable* root, uint64_t vaddr)
|
|
{
|
|
uint64_t page_index;
|
|
PageDirectoryEntry* pde;
|
|
PageTable* pt = root;
|
|
|
|
uint64_t indexes[3];
|
|
|
|
decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
|
|
|
|
for (int i = 0; i < 3;
|
|
i++) // Walk through the page map level 4, page directory pointer, and page directory to find the page table.
|
|
{
|
|
pde = &pt->entries[indexes[i]];
|
|
if (!pde->present) return nullptr;
|
|
else if (pde->larger_pages)
|
|
return pde;
|
|
else { pt = (PageTable*)pde->get_address(); }
|
|
}
|
|
|
|
pde = &pt->entries[page_index]; // PT
|
|
if (!pde->present) return nullptr;
|
|
return pde;
|
|
}
|
|
|
|
PageDirectoryEntry* VMM::create_pde_if_not_exists(PageTable* root, uint64_t vaddr)
|
|
{
|
|
uint64_t page_index;
|
|
PageDirectoryEntry* pde;
|
|
PageTable* pt = root;
|
|
|
|
uint64_t indexes[3];
|
|
|
|
decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
|
|
|
|
auto pde_create_if_not_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;
|
|
};
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
pde = &pt->entries[indexes[i]];
|
|
if (!pde->present) { pde_create_if_not_present(); }
|
|
else if (pde->larger_pages)
|
|
return pde;
|
|
else { pt = (PageTable*)pde->get_address(); }
|
|
}
|
|
|
|
pde = &pt->entries[page_index];
|
|
if (!pde->present) { pde->present = true; }
|
|
return pde;
|
|
}
|
|
|
|
void VMM::propagate_read_write(PageTable* root, uint64_t vaddr)
|
|
{
|
|
uint64_t page_index;
|
|
PageDirectoryEntry* pde;
|
|
PageTable* pt = root;
|
|
|
|
uint64_t indexes[3];
|
|
|
|
decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
pde = &pt->entries[indexes[i]];
|
|
if (!pde->present) return;
|
|
else
|
|
{
|
|
pde->read_write = true;
|
|
if (pde->larger_pages) return;
|
|
pt = (PageTable*)pde->get_address();
|
|
}
|
|
}
|
|
|
|
pde = &pt->entries[page_index];
|
|
if (!pde->present) return;
|
|
else
|
|
pde->read_write = true;
|
|
}
|
|
|
|
void VMM::propagate_user(PageTable* root, uint64_t vaddr)
|
|
{
|
|
uint64_t page_index;
|
|
PageDirectoryEntry* pde;
|
|
PageTable* pt = root;
|
|
|
|
uint64_t indexes[3];
|
|
|
|
decompose_vaddr(vaddr, page_index, indexes[2], indexes[1], indexes[0]);
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
pde = &pt->entries[indexes[i]];
|
|
if (!pde->present) return;
|
|
else
|
|
{
|
|
pde->user = true;
|
|
if (pde->larger_pages) return;
|
|
pt = (PageTable*)pde->get_address();
|
|
}
|
|
}
|
|
|
|
pde = &pt->entries[page_index];
|
|
if (!pde->present) return;
|
|
else
|
|
pde->user = true;
|
|
}
|
|
|
|
void VMM::flush_tlb(uint64_t addr)
|
|
{
|
|
asm volatile("invlpg (%0)" : : "r"(addr) : "memory");
|
|
}
|
|
|
|
void VMM::decompose_vaddr(uint64_t vaddr, uint64_t& page_index, uint64_t& pt_index, uint64_t& pd_index,
|
|
uint64_t& pdp_index)
|
|
{
|
|
vaddr >>= 12;
|
|
page_index = vaddr & 0x1ff;
|
|
vaddr >>= 9;
|
|
pt_index = vaddr & 0x1ff;
|
|
vaddr >>= 9;
|
|
pd_index = vaddr & 0x1ff;
|
|
vaddr >>= 9;
|
|
pdp_index = vaddr & 0x1ff;
|
|
}
|
|
|
|
uint64_t VMM::recompose_vaddr(uint64_t page_index, uint64_t pt_index, uint64_t pd_index, uint64_t pdp_index)
|
|
{
|
|
return pdp_index << 39 | pd_index << 30 | pt_index << 21 | page_index << 12;
|
|
}
|
|
|
|
void VMM::install_kernel_page_directory_into_address_space(AddressSpace& space)
|
|
{
|
|
PageTable* space_pml4 = space.get_pml4();
|
|
PageTable* kernel_last_pdp = (PageTable*)kernel_pml4->entries[511].get_address();
|
|
PageTable* kernel_last_pd = (PageTable*)kernel_last_pdp->entries[511].get_address();
|
|
|
|
PageTable* space_last_pdp = (PageTable*)PMM::request_page();
|
|
|
|
PageDirectoryEntry& space_last_pdp_pde = space_pml4->entries[511];
|
|
|
|
space_last_pdp_pde.present = true;
|
|
space_last_pdp_pde.read_write = true;
|
|
space_last_pdp_pde.set_address((uint64_t)space_last_pdp);
|
|
|
|
PageDirectoryEntry& space_last_pd_pde = space_last_pdp->entries[511];
|
|
|
|
space_last_pd_pde.present = true;
|
|
space_last_pd_pde.read_write = true;
|
|
space_last_pd_pde.set_address((uint64_t)kernel_last_pd);
|
|
} |