2026-02-03 21:28:15 +00:00
|
|
|
#include <mango/compiler.h>
|
2024-11-02 11:31:51 +00:00
|
|
|
#include <mango/memblock.h>
|
2026-02-03 21:28:15 +00:00
|
|
|
#include <mango/pmap.h>
|
2024-11-02 11:31:51 +00:00
|
|
|
#include <mango/printk.h>
|
|
|
|
|
#include <mango/status.h>
|
2026-02-03 21:28:15 +00:00
|
|
|
#include <mango/types.h>
|
|
|
|
|
#include <mango/vm.h>
|
2023-02-06 20:50:28 +00:00
|
|
|
|
|
|
|
|
/* some helpful datasize constants */
|
2026-02-03 21:28:15 +00:00
|
|
|
#define C_1GiB 0x40000000ULL
|
|
|
|
|
#define C_2GiB (2 * C_1GiB)
|
2023-02-06 20:50:28 +00:00
|
|
|
|
2026-02-03 21:28:15 +00:00
|
|
|
#define BAD_INDEX ((unsigned int)-1)
|
2023-02-06 20:50:28 +00:00
|
|
|
#define PTR_TO_ENTRY(x) (((x) & ~VM_PAGE_MASK) | PTE_PRESENT | PTE_RW)
|
|
|
|
|
#define ENTRY_TO_PTR(x) ((x) & ~VM_PAGE_MASK)
|
|
|
|
|
|
2026-02-03 21:28:15 +00:00
|
|
|
#define PFN(x) ((x) >> VM_PAGE_SHIFT)
|
2023-02-06 20:50:28 +00:00
|
|
|
|
|
|
|
|
static int can_use_gbpages = 0;
|
|
|
|
|
static pmap_t kernel_pmap;
|
|
|
|
|
|
2023-04-12 20:17:11 +01:00
|
|
|
static size_t ps_size(enum page_size ps)
|
2023-02-06 20:50:28 +00:00
|
|
|
{
|
|
|
|
|
switch (ps) {
|
|
|
|
|
case PS_4K:
|
|
|
|
|
return 0x1000;
|
|
|
|
|
case PS_2M:
|
|
|
|
|
return 0x200000;
|
|
|
|
|
case PS_1G:
|
|
|
|
|
return 0x40000000;
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-03 21:28:15 +00:00
|
|
|
static pmap_t alloc_pmap(void)
|
2023-02-06 20:50:28 +00:00
|
|
|
{
|
2023-04-12 20:17:11 +01:00
|
|
|
struct pml4t *p = kzalloc(sizeof *p, 0);
|
2023-02-06 20:50:28 +00:00
|
|
|
return vm_virt_to_phys(p);
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-12 20:17:11 +01:00
|
|
|
static pte_t make_pte(pfn_t pfn, enum vm_prot prot, enum page_size size)
|
2023-02-06 20:50:28 +00:00
|
|
|
{
|
|
|
|
|
pte_t v = pfn;
|
|
|
|
|
|
|
|
|
|
switch (size) {
|
|
|
|
|
case PS_1G:
|
2026-02-03 21:28:15 +00:00
|
|
|
/* pfn_t is in terms of 4KiB pages, convert to 1GiB page frame
|
|
|
|
|
* number */
|
2023-02-06 20:50:28 +00:00
|
|
|
pfn >>= 18;
|
|
|
|
|
v = (pfn & 0x3FFFFF) << 30;
|
|
|
|
|
break;
|
|
|
|
|
case PS_2M:
|
2026-02-03 21:28:15 +00:00
|
|
|
/* pfn_t is in terms of 4KiB pages, convert to 2MiB page frame
|
|
|
|
|
* number */
|
2023-02-06 20:50:28 +00:00
|
|
|
pfn >>= 9;
|
|
|
|
|
v = (pfn & 0x7FFFFFFF) << 21;
|
|
|
|
|
break;
|
|
|
|
|
case PS_4K:
|
|
|
|
|
v = (pfn & 0xFFFFFFFFFF) << 12;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v |= PTE_PRESENT;
|
|
|
|
|
|
|
|
|
|
if (size != PS_4K) {
|
|
|
|
|
v |= PTE_PAGESIZE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (prot & VM_PROT_WRITE) {
|
|
|
|
|
v |= PTE_RW;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-08 15:53:06 +01:00
|
|
|
if (prot & VM_PROT_NOCACHE) {
|
|
|
|
|
v |= PTE_NOCACHE;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-06 20:50:28 +00:00
|
|
|
if (!(prot & VM_PROT_EXEC)) {
|
2023-02-07 12:09:36 +00:00
|
|
|
v |= PTE_NX;
|
2023-02-06 20:50:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((prot & VM_PROT_USER) && !(prot & VM_PROT_SVR)) {
|
|
|
|
|
v |= PTE_USR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return v;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void delete_ptab(phys_addr_t pt)
|
|
|
|
|
{
|
|
|
|
|
if (pt & PTE_PAGESIZE) {
|
|
|
|
|
/* this entry points to a hugepage, nothing to delete */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pt &= ~VM_PAGE_MASK;
|
|
|
|
|
if (!pt) {
|
2023-02-07 12:10:05 +00:00
|
|
|
/* physical address of 0x0, nothing to delete */
|
2023-02-06 20:50:28 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2023-02-07 15:38:18 +00:00
|
|
|
|
2023-04-12 20:17:11 +01:00
|
|
|
struct ptab *ptab = vm_phys_to_virt(pt);
|
2023-02-06 20:50:28 +00:00
|
|
|
kfree(ptab);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void delete_pdir(phys_addr_t pd)
|
|
|
|
|
{
|
|
|
|
|
if (pd & PTE_PAGESIZE) {
|
|
|
|
|
/* this entry points to a hugepage, nothing to delete */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pd &= ~0x1FFFFFULL;
|
2023-02-07 12:10:05 +00:00
|
|
|
if (!pd) {
|
|
|
|
|
/* physical address of 0x0, nothing to delete */
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-02-07 15:38:18 +00:00
|
|
|
|
2023-04-12 20:17:11 +01:00
|
|
|
struct pdir *pdir = vm_phys_to_virt(pd);
|
2023-02-06 20:50:28 +00:00
|
|
|
for (int i = 0; i < 512; i++) {
|
|
|
|
|
if (pdir->p_pages[i] & PTE_PAGESIZE) {
|
|
|
|
|
/* this is a hugepage, there is nothing to delete */
|
|
|
|
|
continue;
|
2023-02-07 15:38:18 +00:00
|
|
|
}
|
2023-02-06 20:50:28 +00:00
|
|
|
|
|
|
|
|
delete_ptab(pdir->p_entries[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
kfree(pdir);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-03 21:28:15 +00:00
|
|
|
static kern_status_t do_pmap_add(
|
|
|
|
|
pmap_t pmap,
|
|
|
|
|
void *p,
|
|
|
|
|
pfn_t pfn,
|
|
|
|
|
enum vm_prot prot,
|
|
|
|
|
enum page_size size)
|
2023-02-06 20:50:28 +00:00
|
|
|
{
|
|
|
|
|
uintptr_t pv = (uintptr_t)p;
|
2026-02-03 21:28:15 +00:00
|
|
|
unsigned int pml4t_index = BAD_INDEX, pdpt_index = BAD_INDEX,
|
|
|
|
|
pd_index = BAD_INDEX, pt_index = BAD_INDEX;
|
2023-02-06 20:50:28 +00:00
|
|
|
|
|
|
|
|
switch (size) {
|
|
|
|
|
case PS_4K:
|
|
|
|
|
pml4t_index = (pv >> 39) & 0x1FF;
|
|
|
|
|
pdpt_index = (pv >> 30) & 0x1FF;
|
|
|
|
|
pd_index = (pv >> 21) & 0x1FF;
|
|
|
|
|
pt_index = (pv >> 12) & 0x1FF;
|
|
|
|
|
break;
|
|
|
|
|
case PS_2M:
|
|
|
|
|
pml4t_index = (pv >> 39) & 0x1FF;
|
|
|
|
|
pdpt_index = (pv >> 30) & 0x1FF;
|
|
|
|
|
pd_index = (pv >> 21) & 0x1FF;
|
|
|
|
|
break;
|
|
|
|
|
case PS_1G:
|
|
|
|
|
if (!can_use_gbpages) {
|
|
|
|
|
return KERN_UNSUPPORTED;
|
|
|
|
|
}
|
|
|
|
|
pml4t_index = (pv >> 39) & 0x1FF;
|
|
|
|
|
pdpt_index = (pv >> 30) & 0x1FF;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return KERN_INVALID_ARGUMENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 1. get PML4T (mandatory) */
|
2023-04-12 20:17:11 +01:00
|
|
|
struct pml4t *pml4t = vm_phys_to_virt(ENTRY_TO_PTR(pmap));
|
2023-02-06 20:50:28 +00:00
|
|
|
if (!pml4t) {
|
|
|
|
|
return KERN_INVALID_ARGUMENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 2. traverse PML4T, get PDPT (mandatory) */
|
2023-04-12 20:17:11 +01:00
|
|
|
struct pdpt *pdpt = NULL;
|
2023-02-06 20:50:28 +00:00
|
|
|
if (!pml4t->p_entries[pml4t_index]) {
|
|
|
|
|
pdpt = kzalloc(sizeof *pdpt, 0);
|
2026-02-03 21:28:15 +00:00
|
|
|
pml4t->p_entries[pml4t_index]
|
|
|
|
|
= PTR_TO_ENTRY(vm_virt_to_phys(pdpt));
|
2023-02-06 20:50:28 +00:00
|
|
|
} else {
|
2026-02-03 21:28:15 +00:00
|
|
|
pdpt = vm_phys_to_virt(
|
|
|
|
|
ENTRY_TO_PTR(pml4t->p_entries[pml4t_index]));
|
2023-02-06 20:50:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* if we're mapping a 1GiB page, we stop here */
|
|
|
|
|
if (size == PS_1G) {
|
|
|
|
|
if (pdpt->p_entries[pdpt_index] != 0) {
|
2023-02-07 12:10:12 +00:00
|
|
|
/* this slot points to a pdir, delete it.
|
2026-02-03 21:28:15 +00:00
|
|
|
if this slot points to a hugepage, this does nothing
|
|
|
|
|
*/
|
2023-02-06 20:50:28 +00:00
|
|
|
delete_pdir(pdpt->p_entries[pdpt_index]);
|
|
|
|
|
}
|
2023-02-07 15:38:18 +00:00
|
|
|
|
2023-02-06 20:50:28 +00:00
|
|
|
pdpt->p_pages[pdpt_index] = make_pte(pfn, prot, size);
|
|
|
|
|
|
|
|
|
|
return KERN_OK;
|
|
|
|
|
}
|
2023-02-07 15:38:18 +00:00
|
|
|
|
2023-02-06 20:50:28 +00:00
|
|
|
/* 3. traverse PDPT, get PDIR (optional, 4K and 2M only) */
|
2023-04-12 20:17:11 +01:00
|
|
|
struct pdir *pdir = NULL;
|
2026-02-03 21:28:15 +00:00
|
|
|
if (!pdpt->p_entries[pdpt_index]
|
|
|
|
|
|| pdpt->p_pages[pdpt_index] & PTE_PAGESIZE) {
|
2023-02-06 20:50:28 +00:00
|
|
|
/* entry is null, or points to a hugepage */
|
|
|
|
|
pdir = kzalloc(sizeof *pdir, 0);
|
2026-02-03 21:28:15 +00:00
|
|
|
pdpt->p_entries[pdpt_index]
|
|
|
|
|
= PTR_TO_ENTRY(vm_virt_to_phys(pdir));
|
2023-02-06 20:50:28 +00:00
|
|
|
} else {
|
2026-02-03 21:28:15 +00:00
|
|
|
pdir = vm_phys_to_virt(
|
|
|
|
|
ENTRY_TO_PTR(pdpt->p_entries[pdpt_index]));
|
2023-02-06 20:50:28 +00:00
|
|
|
}
|
|
|
|
|
|
2023-02-07 12:10:12 +00:00
|
|
|
/* if we're mapping a 2MiB page, we stop here */
|
2023-02-06 20:50:28 +00:00
|
|
|
if (size == PS_2M) {
|
|
|
|
|
if (pdir->p_entries[pd_index] != 0) {
|
|
|
|
|
/* this slot points to a ptab, delete it.
|
2026-02-03 21:28:15 +00:00
|
|
|
if this slot points to a hugepage, this does nothing
|
|
|
|
|
*/
|
2023-02-06 20:50:28 +00:00
|
|
|
delete_ptab(pdir->p_entries[pd_index]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pdir->p_pages[pd_index] = make_pte(pfn, prot, size);
|
|
|
|
|
return KERN_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 4. traverse PDIR, get PTAB (optional, 4K only) */
|
2023-04-12 20:17:11 +01:00
|
|
|
struct ptab *ptab = NULL;
|
2026-02-03 21:28:15 +00:00
|
|
|
if (!pdir->p_entries[pd_index]
|
|
|
|
|
|| pdir->p_pages[pd_index] & PTE_PAGESIZE) {
|
2023-02-06 20:50:28 +00:00
|
|
|
/* entry is null, or points to a hugepage */
|
|
|
|
|
ptab = kzalloc(sizeof *ptab, 0);
|
|
|
|
|
pdir->p_entries[pd_index] = PTR_TO_ENTRY(vm_virt_to_phys(ptab));
|
|
|
|
|
} else {
|
|
|
|
|
ptab = vm_phys_to_virt(ENTRY_TO_PTR(pdir->p_entries[pd_index]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ptab->p_pages[pt_index] = make_pte(pfn, prot, size);
|
|
|
|
|
return KERN_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-09 19:50:22 +00:00
|
|
|
pmap_t get_kernel_pmap(void)
|
|
|
|
|
{
|
|
|
|
|
return kernel_pmap;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-06 20:50:28 +00:00
|
|
|
void pmap_bootstrap(void)
|
|
|
|
|
{
|
|
|
|
|
can_use_gbpages = gigabyte_pages();
|
2026-02-03 21:28:15 +00:00
|
|
|
printk("pmap: gigabyte pages %sabled",
|
|
|
|
|
can_use_gbpages == 1 ? "en" : "dis");
|
2023-02-09 21:38:06 +00:00
|
|
|
enable_nx();
|
|
|
|
|
printk("pmap: NX protection enabled");
|
2023-02-06 20:50:28 +00:00
|
|
|
|
2023-04-12 20:17:11 +01:00
|
|
|
enum page_size hugepage = PS_2M;
|
2023-02-06 20:50:28 +00:00
|
|
|
if (can_use_gbpages) {
|
|
|
|
|
hugepage = PS_1G;
|
|
|
|
|
}
|
|
|
|
|
size_t hugepage_sz = ps_size(hugepage);
|
|
|
|
|
|
2023-02-07 12:10:12 +00:00
|
|
|
kernel_pmap = alloc_pmap();
|
2023-02-06 20:50:28 +00:00
|
|
|
|
2023-02-07 12:10:12 +00:00
|
|
|
/* map 2GiB at the end of the address space to
|
|
|
|
|
replace the mapping created by start_32 and allow access to
|
|
|
|
|
the kernel and memblock-allocated data. */
|
2023-02-06 20:50:28 +00:00
|
|
|
uintptr_t vbase = VM_KERNEL_VOFFSET;
|
|
|
|
|
for (size_t i = 0; i < C_2GiB; i += hugepage_sz) {
|
2026-02-03 21:28:15 +00:00
|
|
|
do_pmap_add(
|
|
|
|
|
kernel_pmap,
|
|
|
|
|
(void *)(vbase + i),
|
|
|
|
|
PFN(i),
|
|
|
|
|
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXEC
|
|
|
|
|
| VM_PROT_SVR,
|
|
|
|
|
hugepage);
|
2023-02-06 20:50:28 +00:00
|
|
|
}
|
|
|
|
|
|
2023-02-07 15:38:18 +00:00
|
|
|
phys_addr_t pmem_limit = 0x0;
|
2023-04-12 20:17:11 +01:00
|
|
|
struct memblock_iter it;
|
2026-02-03 21:28:15 +00:00
|
|
|
for_each_mem_range(&it, 0x00, UINTPTR_MAX)
|
|
|
|
|
{
|
2023-02-07 15:38:18 +00:00
|
|
|
if (it.it_limit > pmem_limit) {
|
|
|
|
|
pmem_limit = it.it_limit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vbase = VM_PAGEMAP_BASE;
|
|
|
|
|
for (size_t i = 0; i < pmem_limit; i += hugepage_sz) {
|
2026-02-03 21:28:15 +00:00
|
|
|
do_pmap_add(
|
|
|
|
|
kernel_pmap,
|
|
|
|
|
(void *)(vbase + i),
|
|
|
|
|
PFN(i),
|
|
|
|
|
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_SVR
|
|
|
|
|
| VM_PROT_NOCACHE,
|
|
|
|
|
hugepage);
|
2023-02-07 15:38:18 +00:00
|
|
|
}
|
|
|
|
|
|
2023-02-06 20:50:28 +00:00
|
|
|
pmap_switch(kernel_pmap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pmap_t pmap_create(void)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void pmap_destroy(pmap_t pmap)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-03 21:28:15 +00:00
|
|
|
kern_status_t pmap_add(
|
|
|
|
|
pmap_t pmap,
|
|
|
|
|
void *p,
|
|
|
|
|
pfn_t pfn,
|
|
|
|
|
enum vm_prot prot,
|
|
|
|
|
enum pmap_flags flags)
|
2023-02-06 20:50:28 +00:00
|
|
|
{
|
2023-05-01 18:12:07 +01:00
|
|
|
enum page_size ps = PS_4K;
|
|
|
|
|
if (flags & PMAP_HUGEPAGE) {
|
|
|
|
|
ps = PS_2M;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return do_pmap_add(pmap, p, pfn, prot, ps);
|
2023-02-06 20:50:28 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-03 21:28:15 +00:00
|
|
|
kern_status_t pmap_add_block(
|
|
|
|
|
pmap_t pmap,
|
|
|
|
|
void *p,
|
|
|
|
|
pfn_t pfn,
|
|
|
|
|
size_t len,
|
|
|
|
|
enum vm_prot prot,
|
|
|
|
|
enum pmap_flags flags)
|
2023-02-06 20:50:28 +00:00
|
|
|
{
|
|
|
|
|
return KERN_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
kern_status_t pmap_remove(pmap_t pmap, void *p)
|
|
|
|
|
{
|
|
|
|
|
return KERN_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
kern_status_t pmap_remove_range(pmap_t pmap, void *p, size_t len)
|
|
|
|
|
{
|
|
|
|
|
return KERN_OK;
|
|
|
|
|
}
|