stos/amd64: mmu: Implement page mapping

Signed-off-by: Chloe M. <chloe@mensia.org>
This commit is contained in:
Chloe M.
2026-06-22 22:35:42 +00:00
parent 532e46c62f
commit 5b30974cb0
2 changed files with 226 additions and 0 deletions
+202
View File
@@ -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;
}
+24
View File
@@ -10,8 +10,18 @@
#define _HAL_MMU_H_ 1
#include <stdef.h>
#include <stapi/status.h>
#include <machine/vas.h>
#define PAGE_READONLY 0x00 /* Readonly */
#define PAGE_READONLY_X 0x02 /* Read/execute */
#define PAGE_READWRITE 0x03 /* Read/write */
#define PAGE_READWRITE_X 0x04 /* Read/write/execute */
typedef enum {
PAGESIZE_4K
} MMU_PAGESIZE;
/*
* Read the current virtual address space
*
@@ -26,4 +36,18 @@ VOID HalMmuReadVas(MMU_VAS *Result);
*/
VOID HalMmuWriteVas(MMU_VAS *Vas);
/*
* Map a single page of memory
*
* @Vas: Virtual address space to map within
* @Vma: Virtual memory address to map
* @Pma: Physical memory address to map
* @Flags: Flags to map with
* @PageSize: Pagesize to map
*/
ST_STATUS HalMmuMapSingle(
MMU_VAS *Vas, UPTR Vma, UPTR Pma,
USHORT Flags, MMU_PAGESIZE PageSize
);
#endif /* !_HAL_MMU_H_ */