// VirtMem.c
// Property of Aperture Research Inc (c) 2012-2013
// Licensed under Creative Commons Attribution 3.0 Unported.
// Sets up the paging structures left by our bootstrap in (boot.s).
#include <stdlib.h>
using namespace Kernel;
using namespace Library;
namespace Kernel {
namespace HAL {
namespace VMem
{
TPML *CurrentPML4T;
bool IsPaging;
#define I_RECURSIVE_SLOT (511ULL)
// Convert an address into array index of a structure
// E.G. int index = I_PML4_INDEX(0xFFFFFFFFFFFFFFFF); // index = 511
#define I_PML4_INDEX(addr) ((((uintptr_t)(addr))>>39) & 511)
#define I_PDPT_INDEX(addr) ((((uintptr_t)(addr))>>30) & 511)
#define I_PD_INDEX(addr) ((((uintptr_t)(addr))>>21) & 511)
#define I_PT_INDEX(addr) ((((uintptr_t)(addr))>>12) & 511)
// Base address for paging structures
#define KADDR_I_PT (0xFFFF000000000000ULL + (I_RECURSIVE_SLOT<<39))
#define KADDR_I_PD (KADDR_I_PT + (I_RECURSIVE_SLOT<<30))
#define KADDR_I_PDPT (KADDR_I_PD + (I_RECURSIVE_SLOT<<21))
#define KADDR_I_PML4 (KADDR_I_PDPT + (I_RECURSIVE_SLOT<<12))
// Structures for given address, for example
// uint64_t* pt = I_PT(addr)
// uint64_t physical_addr = pt[I_PT_INDEX(addr)];
#define I_PML4(addr) ((uint64_t*)KADDR_I_PML4)
#define I_PDPT(addr) ((uint64_t*)(KADDR_I_PDPT + (((addr)>>27) & 0x00001FF000)))
#define I_PD(addr) ((uint64_t*)(KADDR_I_PD + (((addr)>>18) & 0x003FFFF000)))
#define I_PT(addr) ((uint64_t*)(KADDR_I_PT + (((addr)>>9) & 0x7FFFFFF000)))
void Initialise()
{
TPML* const OriginalPml4 = (TPML* const)0x1000;
// Put the physical address (We know it's 0x1000, since it's a constant) of the PML4T into the second last entry of itself, so it appears as a PDPT.
// This means that subsequently, the address of a PD would reside in one of 512 PDs in our 'PDPT' (PML4T in disguise), and so on
// for the PD and PTs.
OriginalPml4->Entry[I_RECURSIVE_SLOT] = (uint64_t)OriginalPml4 | I_Present | I_ReadWrite;
CurrentPML4T = OriginalPml4;
IsPaging = 1;
}
void SwitchPML4T(TPML *PML4T)
{
asm volatile("mov %0, %%cr3" :: "r"(PML4T));
}
bool MapAddr(uint64_t VirtAddr, uint64_t PhysAddr, uint64_t Flags)
{
// Access PhysAddr using VirtAddr.
VirtAddr &= I_AlignMask;
PhysAddr &= I_AlignMask;
// // First, find out which page we will need.
uint64_t PageTableIndex = I_PT_INDEX(VirtAddr);
uint64_t PageDirectoryIndex = I_PD_INDEX(VirtAddr);
uint64_t PageDirectoryPointerTableIndex = I_PDPT_INDEX(VirtAddr);
uint64_t PML4TIndex = I_PML4_INDEX(VirtAddr);
// Because these are indexes into structures, we need to use MODULO '%'
// To change them to relative indexes, not absolute ones.
if(PML4TIndex == I_RECURSIVE_SLOT)
{
// We can't map 510, we need that for our recursive mapping.
HALT("Tried to map to PML4[510]! (RESTRICTED, KERNEL USE)");
}
// Now we know where all the stuff is at, let's start mapping.
// First, we check if the desired PDPT is present:
if(!(CurrentPML4T->Entry[PML4TIndex]) & I_Present)
{
// It's not, so we have to set it to present.
CurrentPML4T->Entry[PML4TIndex] = ((uint64_t)(HAL::AllocatePage()) & I_AlignMask) | 0x03;
// So we can access this later, put the address of the PDPT we just defined into the 'PML4T'.
TPML *PML4 = (TPML*)(CurrentPML4T->Entry[I_RECURSIVE_SLOT] & I_AlignMask);
PML4->Entry[PML4TIndex] = CurrentPML4T->Entry[PML4TIndex] | I_Present | I_ReadWrite;
invlpg(CurrentPML4T);
}
// Continue with our business here.
// Check the PD:
TPML *PDPT = (TPML*)(CurrentPML4T->Entry[PML4TIndex] & I_AlignMask);
if(!(PDPT->Entry[PageDirectoryPointerTableIndex]) & I_Present)
{
// uint64_t temp = HAL::PMem::AllocatePage_NoMap();
PDPT->Entry[PageDirectoryPointerTableIndex] = ((uint64_t)(HAL::AllocatePage()) & I_AlignMask) | 0x03;
// Put the address of the PD into the PDPT.
TPML *PML4 = (TPML*)(CurrentPML4T->Entry[I_RECURSIVE_SLOT] & I_AlignMask);
TPML *PML3 = (TPML*)(PML4->Entry[PML4TIndex] & I_AlignMask);
PML3->Entry[PageDirectoryPointerTableIndex] = PDPT->Entry[PageDirectoryPointerTableIndex] | I_Present | I_ReadWrite;
invlpg(PDPT);
}
// Next, we must check if the Page Table is present:
TPML *PageDirectory = (TPML*)(PDPT->Entry[PageDirectoryPointerTableIndex] & I_AlignMask);
if(!(PageDirectory->Entry[PageDirectoryIndex]) & I_Present)
{
PageDirectory->Entry[PageDirectoryIndex] = ((uint64_t)(HAL::AllocatePage()) & I_AlignMask) | 0x03;
// PageDirectory->Entry[PageDirectoryIndex] &= I_AlignMask | 0x03;
// Put the address of the PT into the PD.
TPML *PML4 = (TPML*)(CurrentPML4T->Entry[I_RECURSIVE_SLOT] & I_AlignMask);
TPML *PML3 = (TPML*)(PML4->Entry[PML4TIndex] & I_AlignMask);
TPML *PML2 = (TPML*)(PML3->Entry[PageDirectoryPointerTableIndex] & I_AlignMask);
PML2->Entry[PageDirectoryIndex] = PageDirectory->Entry[PageDirectoryIndex] | I_Present | I_ReadWrite;
invlpg(PageDirectory);
}
bool r = true;
TPML *PageTable = (TPML*)(PageDirectory->Entry[PageDirectoryIndex] & I_AlignMask);
if(PageTable->Entry[PageTableIndex])
r = false;
PageTable->Entry[PageTableIndex] = (PhysAddr & I_AlignMask) | Flags;
invlpg(PageTable);
// Flush TLB.
asm volatile("mov %cr3, %rax; mov %rax, %cr3");
return r;
}
void UnMapAddr(uint64_t VirtAddr)
{
// First, find out which page we will need.
uint64_t PageTableIndex = VirtAddr / 0x1000;
// Next, which Page Table does that page reside in?
// Let's find out:
uint64_t PageDirectoryIndex = PageTableIndex / 512;
// Page Directory:
uint64_t PageDirectoryPointerTableIndex = PageDirectoryIndex / 512;
// Finally, which PDPT is it in?
uint64_t PML4TIndex = PageDirectoryPointerTableIndex / 512;
// Because these are indexes into structures, we need to use MODULO '%'
// To change them to relative indexes, not absolute ones.
PageTableIndex %= 512;
PageDirectoryIndex %= 512;
PageDirectoryPointerTableIndex %= 512;
TPML *PML = (TPML*)(CurrentPML4T->Entry[510] & I_AlignMask);
if(PML)
{
TPML *PDPT = (TPML*)(PML->Entry[PML4TIndex] & I_AlignMask);
if(PDPT)
{
TPML *PageDirectory = (TPML*)(PDPT->Entry[PageDirectoryPointerTableIndex] & I_AlignMask);
if(PageDirectory)
{
TPML *PageTable = (TPML*)(PageDirectory->Entry[PageDirectoryIndex] & I_AlignMask);
PageTable->Entry[PageTableIndex] = 0;
asm volatile("invlpg (%0)" : : "a" (VirtAddr));
}
else
{
return;
}
}
else
{
return;
}
}
else
{
return;
}
}
void MapRegion(uint64_t VirtAddr, uint64_t PhysAddr, uint64_t LengthInPages, uint64_t Flags)
{
for(uint64_t i = 0; i < LengthInPages; i++)
{
MapAddr(VirtAddr + (i * 0x1000), PhysAddr + (i * 0x1000), Flags);
}
}
void UnMapRegion(uint64_t VirtAddr, uint64_t LengthInPages)
{
for(uint64_t i = 0; i < LengthInPages; i++)
{
UnMapAddr(VirtAddr + (i * 0x1000));
}
}
uint64_t GetMapping(uint64_t VirtAddr)
{
// First, find out which page we will need.
uint64_t PageTableIndex = VirtAddr / 0x1000;
// Next, which Page Table does that page reside in?
// Let's find out:
uint64_t PageDirectoryIndex = PageTableIndex / 512;
// Page Directory:
uint64_t PageDirectoryPointerTableIndex = PageDirectoryIndex / 512;
// Finally, which PDPT is it in?
uint64_t PML4TIndex = PageDirectoryPointerTableIndex / 512;
// Because these are indexes into structures, we need to use MODULO '%'
// To change them to relative indexes, not absolute ones.
PML4TIndex %= 512;
PageTableIndex %= 512;
PageDirectoryIndex %= 512;
PageDirectoryPointerTableIndex %= 512;
TPML *PML = (TPML*)(CurrentPML4T->Entry[510] & I_AlignMask);
if(PML)
{
TPML *PDPT = (TPML*)(PML->Entry[PML4TIndex] & I_AlignMask);
if(PDPT)
{
TPML *PageDirectory = (TPML*)(PDPT->Entry[PageDirectoryPointerTableIndex] & I_AlignMask);
if(PageDirectory)
{
TPML *PageTable = (TPML*)(PageDirectory->Entry[PageDirectoryIndex] & I_AlignMask);
return (uint64_t)(PageTable->Entry[PageTableIndex] & I_AlignMask);
}
else
{
return 0;
}
}
else
{
return 0;
}
}
else
{
return 0;
}
}
uint64_t SearchPhysicalMapping(uint64_t PhysAddr)
{
// Search the page tables for the physical mapping.
// PML4
for(int i = 0; i < 512; i++)
{
// Check if there's anything in there; if not, skip early
if(!CurrentPML4T->Entry[i])
{
continue;
}
// PDPT
for(int f = 0; f < 512; f++)
{
if(!((TPML*)(CurrentPML4T->Entry[i]))->Entry[f])
{
continue;
}
// PD
for(int x = 0; x < 512; x++)
{
if(!((TPML*)((TPML*)(CurrentPML4T->Entry[i]))->Entry[f])->Entry[x])
{
continue;
}
// PT
for(int y = 0; y < 512; y++)
{
// Search the entries!
TPML *PML4 = (TPML*)(CurrentPML4T->Entry[i] & I_AlignMask);
TPML *PML3 = (TPML*)(PML4->Entry[f] & I_AlignMask);
TPML *PML2 = (TPML*)(PML3->Entry[x] & I_AlignMask);
return PML2->Entry[y];
}
}
}
}
return 0;
}
bool GetPagingFlag()
{
return IsPaging;
}
}
}
}