stos/amd64: mmu: Implement page mapping
Signed-off-by: Chloe M. <chloe@mensia.org>
This commit is contained in:
@@ -7,9 +7,186 @@
|
||||
*/
|
||||
|
||||
#include <hal/mmu.h>
|
||||
#include <hal/page.h>
|
||||
#include <machine/vas.h>
|
||||
#include <mm/pmm.h>
|
||||
#include <mm/vmm.h>
|
||||
#include <stdef.h>
|
||||
|
||||
/*
|
||||
* Page-Table Entry (PTE) flags
|
||||
*
|
||||
* See Intel SDM Vol 3A, Section 4.5, Table 4-19
|
||||
*/
|
||||
#define PTE_ADDR_MASK 0x000FFFFFFFFFF000
|
||||
#define PTE_P BIT(0) /* Present */
|
||||
#define PTE_RW BIT(1) /* Writable */
|
||||
#define PTE_US BIT(2) /* User r/w allowed */
|
||||
#define PTE_PWT BIT(3) /* Page-level write-through */
|
||||
#define PTE_PCD BIT(4) /* Page-level cache disable */
|
||||
#define PTE_ACC BIT(5) /* Accessed */
|
||||
#define PTE_DIRTY BIT(6) /* Dirty (written-to page) */
|
||||
#define PTE_PS BIT(7) /* Page size */
|
||||
#define PTE_GLOBAL BIT(8) /* Global; sticky */
|
||||
#define PTE_NX BIT(63) /* Execute-disable */
|
||||
|
||||
/* 57-bit linear addresses */
|
||||
#define CR4_LA57 BIT(12)
|
||||
|
||||
/*
|
||||
* Valid page levels
|
||||
*/
|
||||
typedef enum {
|
||||
PAGE_LEVEL_PML1,
|
||||
PAGE_LEVEL_PML2,
|
||||
PAGE_LEVEL_PML3,
|
||||
PAGE_LEVEL_PML4,
|
||||
PAGE_LEVEL_PML5
|
||||
} PAGE_LEVEL;
|
||||
|
||||
/*
|
||||
* Convert protection flags to PTE flags
|
||||
*/
|
||||
static inline UQUAD
|
||||
MmuProtToPte(UCHAR Prot)
|
||||
{
|
||||
switch (Prot) {
|
||||
case PAGE_READONLY:
|
||||
return PTE_P | PTE_NX;
|
||||
case PAGE_READWRITE:
|
||||
return PTE_P | PTE_RW | PTE_NX;
|
||||
case PAGE_READWRITE_X:
|
||||
return PTE_P | PTE_RW;
|
||||
case PAGE_READONLY_X:
|
||||
return PTE_P;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the given pagesize is valid
|
||||
*
|
||||
* @PageSize: Pagesize to check
|
||||
*/
|
||||
static inline BOOLEAN
|
||||
MmuPageSizeValid(MMU_PAGESIZE PageSize)
|
||||
{
|
||||
switch (PageSize) {
|
||||
case PAGESIZE_4K:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Obtain the toplevel index
|
||||
*/
|
||||
static inline PAGE_LEVEL
|
||||
MmuTopLevel(VOID)
|
||||
{
|
||||
UPTR Cr4;
|
||||
|
||||
ASMV(
|
||||
"mov %%cr4, %0"
|
||||
: "=r" (Cr4)
|
||||
:
|
||||
: "memory"
|
||||
);
|
||||
|
||||
return ISSET(Cr4, CR4_LA57)
|
||||
? PAGE_LEVEL_PML5
|
||||
: PAGE_LEVEL_PML4;
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalidate an entry from the TLB
|
||||
*
|
||||
* @Vma: Virtual memory address to flush
|
||||
*/
|
||||
static inline VOID
|
||||
MmuInvlpg(UPTR Vma)
|
||||
{
|
||||
ASMV(
|
||||
"invlpg (%0)"
|
||||
:
|
||||
: "r" (Vma)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Obtain the page level index
|
||||
*
|
||||
* @Vma: Virtual memory address
|
||||
* @Level: Page level
|
||||
*/
|
||||
static inline UQUAD
|
||||
MmuLevelIndex(UPTR Vma, PAGE_LEVEL Level)
|
||||
{
|
||||
switch (Level) {
|
||||
case PAGE_LEVEL_PML1: return (Vma >> 12) & 0x1FF;
|
||||
case PAGE_LEVEL_PML2: return (Vma >> 21) & 0x1FF;
|
||||
case PAGE_LEVEL_PML3: return (Vma >> 30) & 0x1FF;
|
||||
case PAGE_LEVEL_PML4: return (Vma >> 39) & 0x1FF;
|
||||
case PAGE_LEVEL_PML5: return (Vma >> 48) & 0x1FF;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract a page table entry at a specific level
|
||||
*
|
||||
* @Vas: Virtual address space containing top-level
|
||||
* @Vma: Virtual memory address used as key
|
||||
* @Level: Level to extract
|
||||
* @Alloc: If true, allocate unset entries
|
||||
*/
|
||||
static UPTR *
|
||||
MmuExtractLevel(MMU_VAS *Vas, UPTR Vma, PAGE_LEVEL Level, BOOLEAN Alloc)
|
||||
{
|
||||
MM_PFN AllocPfn;
|
||||
UPTR Pma, *PageMap;
|
||||
PAGE_LEVEL CurrentLevel;
|
||||
USHORT Index;
|
||||
|
||||
if (Vas == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CurrentLevel = MmuTopLevel();
|
||||
PageMap = PMA_TO_VMA((Vas->Cr3 & PTE_ADDR_MASK));
|
||||
|
||||
while (CurrentLevel > Level) {
|
||||
Index = MmuLevelIndex(Vma, CurrentLevel);
|
||||
|
||||
if (ISSET(PageMap[Index], PTE_P)) {
|
||||
Pma = PageMap[Index] & PTE_ADDR_MASK;
|
||||
PageMap = PMA_TO_VMA(Pma);
|
||||
--CurrentLevel;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Alloc) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AllocPfn = MmRequestFrame();
|
||||
if (AllocPfn == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Pma = AllocPfn << LOG2_PAGESIZE;
|
||||
PageMap[Index] = Pma | (PTE_P | PTE_RW | PTE_US);
|
||||
|
||||
PageMap = PMA_TO_VMA(Pma);
|
||||
--CurrentLevel;
|
||||
}
|
||||
|
||||
return PageMap;
|
||||
}
|
||||
|
||||
VOID
|
||||
HalMmuReadVas(MMU_VAS *Result)
|
||||
{
|
||||
@@ -39,3 +216,28 @@ HalMmuWriteVas(MMU_VAS *Vas)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
ST_STATUS
|
||||
HalMmuMapSingle(MMU_VAS *Vas, UPTR Vma, UPTR Pma, USHORT Flags, MMU_PAGESIZE PagSize)
|
||||
{
|
||||
UPTR *PageTable;
|
||||
USIZE PteFlags;
|
||||
USHORT Index;
|
||||
|
||||
if (Vas == NULL) {
|
||||
return STATUS_INVALID_PARAM;
|
||||
}
|
||||
|
||||
PageTable = MmuExtractLevel(Vas, Vma, PAGE_LEVEL_PML1, true);
|
||||
if (PageTable == NULL) {
|
||||
return STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
PteFlags = MmuProtToPte(Flags);
|
||||
Index = MmuLevelIndex(Vma, PAGE_LEVEL_PML1);
|
||||
|
||||
/* Map the entry and flush the TLB */
|
||||
PageTable[Index] = Pma | PteFlags;
|
||||
MmuInvlpg(Vma);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user