Files
termOS/System/src/VM/VMM.c
T

259 lines
8.5 KiB
C

// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 0xKarinyash
#include <VM/VMM.h>
#include <VM/PMM.h>
#include <OS/OSPanic.h>
#include <OS/OSSpinlock.h>
#include <GDT.h>
#include <IDT.h>
#include <lib/String.h>
#include <types.h>
#include "bootinfo.h"
enum {
kVMUserStackTop = 0x70000000,
kVMUserStackSize = 0x4000
};
UInt64* gVMKernelPML4 = nullptr;
UInt64 gVMKernelPML4Physical = 0;
static OSSpinlock sVMVMMlock = {0};
static bool isInitialized = false;
extern UInt64 _kernel_start;
extern UInt64 _kernel_end;
extern UInt8* gVMPhycalMemoryBitmap;
extern UInt64 gVMPhycalMemoryBitmapSize;
extern HALGlobalDescriptorTable gHALGlobalDescriptorTable[];
extern HALInterruptsDescriptorTableEntry gHALInterruptsDescriptorTable[];
extern UInt8 gHALDoubleFaultStack[];
extern UInt8 stack_guard;
static UInt64* sVMGetVirtualTable(UInt64 phys) {
if (isInitialized) return (UInt64*)PHYS_TO_HHDM(phys);
return (UInt64*)phys;
}
static UInt64* sVMVirtualMemoryMapPageInternal(UInt64* pml4, UInt64 phys, UInt64 virt, UInt64 flags) {
UInt64 pt_idx = VMM_PT_INDEX(virt);
UInt64 pd_idx = VMM_PD_INDEX(virt);
UInt64 pdpt_idx = VMM_PDPT_INDEX(virt);
UInt64 pml4_idx = VMM_PML4_INDEX(virt);
UInt64* pml4_virt = pml4;
if (isInitialized) pml4_virt = (UInt64*)PHYS_TO_HHDM((UInt64)pml4);
UInt64 table_flags = PTE_PRESENT | PTE_RW | (flags & PTE_USER);
if (!(pml4_virt[pml4_idx] & PTE_PRESENT)) {
UInt64* addr = VMPhysicalMemoryAllocatePage();
if (!addr) return nullptr;
UInt64* addr_virt = sVMGetVirtualTable((UInt64)addr);
MemorySet(addr_virt, 0, kVMPageSize);
pml4_virt[pml4_idx] = (UInt64)addr | table_flags;
} else {
pml4_virt[pml4_idx] |= (flags & PTE_USER);
}
UInt64* pdpt = (UInt64*)PTE_GET_ADDR(pml4_virt[pml4_idx]);
UInt64* pdpt_virt = sVMGetVirtualTable((UInt64)pdpt);
if (!(pdpt_virt[pdpt_idx] & PTE_PRESENT)) {
UInt64* addr = VMPhysicalMemoryAllocatePage();
if (!addr) return nullptr;
UInt64* addr_virt = sVMGetVirtualTable((UInt64)addr);
MemorySet(addr_virt, 0, kVMPageSize);
pdpt_virt[pdpt_idx] = (UInt64)addr | table_flags;
} else {
pdpt_virt[pdpt_idx] |= (flags & PTE_USER);
}
UInt64* pd = (UInt64*)PTE_GET_ADDR(pdpt_virt[pdpt_idx]);
UInt64* pd_virt = sVMGetVirtualTable((UInt64)pd);
if (!(pd_virt[pd_idx] & PTE_PRESENT)) {
UInt64* addr = VMPhysicalMemoryAllocatePage();
if (!addr) return nullptr;
UInt64* addr_virt = sVMGetVirtualTable((UInt64)addr);
MemorySet(addr_virt, 0, kVMPageSize);
pd_virt[pd_idx] = (UInt64)addr | table_flags;
} else {
pd_virt[pd_idx] |= (flags & PTE_USER);
}
UInt64* pt = (UInt64*)PTE_GET_ADDR(pd_virt[pd_idx]);
UInt64* pt_virt = sVMGetVirtualTable((UInt64)pt);
pt_virt[pt_idx] = phys | flags;
__asm__ volatile("invlpg (%0)" :: "r" (virt) : "memory");
return (UInt64*)virt;
}
static void* sVMVirtualMemoryGetMappedPageInternal(UInt64* pml4, UInt64 virt) {
UInt64 pt_idx = VMM_PT_INDEX(virt);
UInt64 pd_idx = VMM_PD_INDEX(virt);
UInt64 pdpt_idx = VMM_PDPT_INDEX(virt);
UInt64 pml4_idx = VMM_PML4_INDEX(virt);
UInt64* pml4_virt = pml4;
if (isInitialized) pml4_virt = (UInt64*)PHYS_TO_HHDM((UInt64)pml4);
if (!(pml4_virt[pml4_idx] & PTE_PRESENT)) return nullptr;
UInt64* pdpt_virt = sVMGetVirtualTable(PTE_GET_ADDR(pml4_virt[pml4_idx]));
if (!(pdpt_virt[pdpt_idx] & PTE_PRESENT)) return nullptr;
UInt64* pd_virt = sVMGetVirtualTable(PTE_GET_ADDR(pdpt_virt[pdpt_idx]));
if (!(pd_virt[pd_idx] & PTE_PRESENT)) return nullptr;
UInt64* pt_virt = sVMGetVirtualTable(PTE_GET_ADDR(pd_virt[pd_idx]));
if (!(pt_virt[pt_idx] & PTE_PRESENT)) return nullptr;
return (void*)PHYS_TO_HHDM(PTE_GET_ADDR(pt_virt[pt_idx]));
}
static void sVMVirtualMemoryUnmapPageInternal(UInt64* pml4, UInt64 virt) {
UInt64 pt_idx = VMM_PT_INDEX(virt);
UInt64 pd_idx = VMM_PD_INDEX(virt);
UInt64 pdpt_idx = VMM_PDPT_INDEX(virt);
UInt64 pml4_idx = VMM_PML4_INDEX(virt);
UInt64* pml4_virt = pml4;
if (isInitialized) pml4_virt = (UInt64*)PHYS_TO_HHDM((UInt64)pml4);
if (!(pml4_virt[pml4_idx] & PTE_PRESENT)) return;
UInt64* pdpt_virt = sVMGetVirtualTable(PTE_GET_ADDR(pml4_virt[pml4_idx]));
if (!(pdpt_virt[pdpt_idx] & PTE_PRESENT)) return;
UInt64* pd_virt = sVMGetVirtualTable(PTE_GET_ADDR(pdpt_virt[pdpt_idx]));
if (!(pd_virt[pd_idx] & PTE_PRESENT)) return;
UInt64* pt_virt = sVMGetVirtualTable(PTE_GET_ADDR(pd_virt[pd_idx]));
pt_virt[pt_idx] = 0;
__asm__ volatile("invlpg (%0)" :: "r" (virt) : "memory");
}
UInt64* VMVirtualMemoryMapPage(UInt64* pml4, UInt64 phys, UInt64 virt, UInt64 flags) {
OSSpinlockState state;
OSSpinlockLockIRQ(&sVMVMMlock, &state);
UInt64* result = sVMVirtualMemoryMapPageInternal(pml4, phys, virt, flags);
OSSpinlockUnlockIRQ(&sVMVMMlock, &state);
return result;
}
void* VMVirtualMemoryGetOrAllocatePage(UInt64* pml4, UInt64 virt, UInt64 flags) {
OSSpinlockState state;
OSSpinlockLockIRQ(&sVMVMMlock, &state);
void* mappedPage = sVMVirtualMemoryGetMappedPageInternal(pml4, virt);
if (mappedPage) {
OSSpinlockUnlockIRQ(&sVMVMMlock, &state);
return mappedPage;
}
void* physicalPage = VMPhysicalMemoryAllocatePage();
if (!physicalPage) {
OSSpinlockUnlockIRQ(&sVMVMMlock, &state);
return nullptr;
}
UInt64* result = sVMVirtualMemoryMapPageInternal(pml4, (UInt64)physicalPage, virt, flags);
if (!result) {
OSSpinlockUnlockIRQ(&sVMVMMlock, &state);
return nullptr;
}
void* kernelVirtualAddress = (void*)PHYS_TO_HHDM((UInt64)physicalPage);
MemorySet(kernelVirtualAddress, 0, kVMPageSize);
OSSpinlockUnlockIRQ(&sVMVMMlock, &state);
return kernelVirtualAddress;
}
void VMVirtualMemoryUnmapPage(UInt64* pml4, UInt64 virt) {
OSSpinlockState state;
OSSpinlockLockIRQ(&sVMVMMlock, &state);
sVMVirtualMemoryUnmapPageInternal(pml4, virt);
OSSpinlockUnlockIRQ(&sVMVMMlock, &state);
}
void VMLoadCR3(UInt64 pml4_addr) {
__asm__ volatile ("mov %0, %%cr3" :: "r"(pml4_addr) : "memory");
}
void VMVirtualMemoryInitialize(Bootinfo* info) {
gVMKernelPML4Physical = (UInt64)VMPhysicalMemoryAllocatePage();
gVMKernelPML4 = (UInt64*)gVMKernelPML4Physical;
MemorySet(gVMKernelPML4, 0, kVMPageSize);
UInt64 k_virt_start = (UInt64)&_kernel_start;
UInt64 k_virt_end = (UInt64)&_kernel_end;
UInt64 fb_start = (UInt64)info->framebuffer.base;
UInt64 fb_end = fb_start + info->framebuffer.baseSize;
UInt64 fb_size = fb_end - fb_start;
UInt64 max_mem = VMPhysicalMemoryGetTotalMemorySize();
for (UInt64 i = 0; i < max_mem; i += kVMPageSize) VMVirtualMemoryMapPage(gVMKernelPML4, i, PHYS_TO_HHDM(i), PTE_PRESENT | PTE_RW);
for (UInt64 i = k_virt_start; i < k_virt_end; i += kVMPageSize) VMVirtualMemoryMapPage(gVMKernelPML4, KERNEL_VIRT_TO_PHYS(i), i, PTE_PRESENT | PTE_RW);
for (UInt64 i = 0; i < fb_size; i += kVMPageSize) VMVirtualMemoryMapPage(gVMKernelPML4, fb_start + i, FB_VIRT_BASE + i, PTE_PRESENT | PTE_RW);
VMVirtualMemoryUnmapPage(gVMKernelPML4, (UInt64)&stack_guard);
gVMPhycalMemoryBitmap = (UInt8*)PHYS_TO_HHDM((UInt64)gVMPhycalMemoryBitmap);
info->framebuffer.base = (UInt32*)FB_VIRT_BASE;
VMLoadCR3(gVMKernelPML4Physical);
isInitialized = true;
}
UInt64 VMVirtualMemoryCreateAddressSpace() {
OSSpinlockState state;
OSSpinlockLockIRQ(&sVMVMMlock, &state);
UInt64 phys = (UInt64)VMPhysicalMemoryAllocatePage();
if (!phys) {
OSSpinlockUnlockIRQ(&sVMVMMlock, &state);
return 0;
};
UInt64* virt = (UInt64*)PHYS_TO_HHDM(phys);
MemorySet(virt, 0, kVMPageSize);
UInt64* kernel_pml4_virt = sVMGetVirtualTable((UInt64)gVMKernelPML4);
for (UInt32 i = 256; i < 512; i++) {
virt[i] = kernel_pml4_virt[i];
}
OSSpinlockUnlockIRQ(&sVMVMMlock, &state);
return phys;
}
UInt64 VMGetCurrentCR3() {
UInt64 cr3;
__asm__ volatile("mov %%cr3, %0" : "=r"(cr3));
return cr3;
}
void VMVirtualMemorySetupUserStack(UInt64* pml4_phys) {
UInt64 stack_bottom = kVMUserStackTop - kVMUserStackSize;
for (UInt64 addr = stack_bottom; addr < kVMUserStackTop; addr += 4096) {
void* phys = VMPhysicalMemoryAllocatePage();
if (!phys) OSPanic("OOM in user stack setup");
MemorySet((void*)PHYS_TO_HHDM((UInt64)phys), 0, 4096);
VMVirtualMemoryMapPage((UInt64*)pml4_phys, (UInt64)phys, addr, PTE_PRESENT | PTE_RW | PTE_USER);
}
}