#include "arch/x86_64/page.h"
#include "klib/rbtree.h"
#include "arch/processor.h"
#include "string.h"
#include "jove.h"
#include "memory.h"
#include "print.h"
extern void *_kernel_end;
struct PageDirectoryListEntry {
struct PageDirectoryListEntry *next;
page_directory_t pd;
};
struct PageStateCache {
page_directory_t *pd;
size_t pmli[4];
pmle_t *pml[4];
} s_state_cache;
const uintptr_t USERLAND_MEMORY_BASE = KiB;
const uintptr_t USERLAND_MEMORY_LIMIT = 0x00007FFFFFFFFFFFULL;
const uintptr_t PHYSMAP_MEMORY_BASE = 0xFFFF800000000000ULL;
const uintptr_t PHYSMAP_MEMORY_LIMIT = PHYSMAP_MEMORY_BASE + (1 * GiB);
const uintptr_t KERNEL_MEMORY_BASE = 0xFFFF800000000000ULL;
const uintptr_t KERNEL_MEMORY_LIMIT = 0xFFFFFFFFFFFFFFFFULL;
static size_t
s_paging_pmli(size_t l, uintptr_t addr)
{
size_t shift = (PAGE_SHIFT + (9 * l));
return (addr & (0x1FFULL << shift)) >> shift;
}
static pmle_t*
s_paging_fetch_table(pmle_t *pml, size_t l, uintptr_t virt)
{
size_t pmli = s_paging_pmli(l, virt);
if(s_state_cache.pmli[l] == pmli && s_state_cache.pml[l] != NULL)
return s_state_cache.pml[l];
pmle_t entry = pml[pmli];
bool entry_new = false;
if(!entry.p) {
entry_new = true;
entry.value = pm_alloc(1);
entry.p = 1;
entry.rw = 1;
entry.us = 1;
pml[pmli] = entry;
}
pmle_t *table = (pmle_t*)(pm_tovirt(entry.paddr << PAGE_SHIFT));
if(entry_new) memset(table, 0, PAGE_SIZE);
s_state_cache.pmli[l] = pmli;
s_state_cache.pml[l] = table;
return table;
}
static void
s_paging_cache_tables(page_directory_t *pd, uintptr_t virt)
{
pmle_t *pml4 = pd->pml;
if(s_state_cache.pd != pd) memset(&s_state_cache, 0, sizeof(s_state_cache));
pmle_t *pml3 = s_paging_fetch_table(pml4, 3, virt);
pmle_t *pml2 = s_paging_fetch_table(pml3, 2, virt);
pmle_t *pml1 = s_paging_fetch_table(pml2, 1, virt);
}
static pmle_t*
s_paging_get_table(pmle_t *pt, size_t l, uintptr_t virt)
{
if(pt == NULL) return NULL;
size_t pmli = s_paging_pmli(l, virt);
if(s_state_cache.pmli[l] == pmli && s_state_cache.pml[l] != NULL)
return s_state_cache.pml[l];
pmle_t entry = pt[pmli];
if(!entry.p) return NULL;
return (pmle_t*)(pm_tovirt(entry.paddr << PAGE_SHIFT));
}
page_mapping_t
vm_pd_mapping_get(page_directory_t *pd, uintptr_t addr)
{
spinlock_acquire(pd->lock);
page_mapping_t mapping = { 0 };
pmle_t *pml4 = pd->pml;
if(s_state_cache.pd != pd) memset(&s_state_cache, 0, sizeof(s_state_cache));
pmle_t *pml3 = s_paging_get_table(pml4, 3, addr);
pmle_t *pml2 = s_paging_get_table(pml3, 2, addr);
pmle_t *pml1 = s_paging_get_table(pml2, 1, addr);
if(pml1 == NULL) goto release_return;
size_t pml1i = s_paging_pmli(0, addr);
pmle_t pml1e = pml1[pml1i];
mapping = (page_mapping_t) {
.phys = (pml1e.paddr << PAGE_SHIFT) & ~PAGE_MASK,
.pf = {
.present = pml1e.p,
.writeable = pml1e.rw,
.useraccess = pml1e.us,
.executable = !pml1e.xd
}
};
release_return:
spinlock_release(pd->lock);
return mapping;
}
page_mapping_t vm_mapping_get(uintptr_t addr)
{ return vm_pd_mapping_get(pd_current(), addr); }
void
mem_set_mapping_as(page_directory_t *pd, page_mapping_t mapping, uintptr_t virt)
{
spinlock_acquire(pd->lock);
s_paging_cache_tables(pd, virt);
pmle_t *pml1 = s_state_cache.pml[0];
size_t pml1i = s_paging_pmli(0, virt);
pml1[pml1i] = (pmle_t) {
.p = mapping.pf.present,
.rw = mapping.pf.writeable,
.us = mapping.pf.useraccess,
.xd = !mapping.pf.executable,
.paddr = mapping.phys >> PAGE_SHIFT
};
spinlock_release(pd->lock);
}
void mem_set_mapping(page_mapping_t mapping, uintptr_t virt)
{ mem_set_mapping_as(pd_current(), mapping, virt); }
void
vm_pd_ensure(page_directory_t *pd, uintptr_t from, uintptr_t to, page_flags_t flg)
{
spinlock_acquire(pd->lock);
from &= ~(PAGE_SIZE - 1);
to += (to % PAGE_SIZE > 0 ? (PAGE_SIZE - (to % PAGE_SIZE)) : 0);
if(to < from) to = from;
size_t pages = (to - from) >> PAGE_SHIFT;
if(pages == 0) pages = 1;
for(size_t i = 0; i < pages; i++) {
uintptr_t waddr = from + (i << PAGE_SHIFT);
s_paging_cache_tables(pd, waddr);
pmle_t *pml1 = s_state_cache.pml[1];
size_t pml1i = s_paging_pmli(0, waddr);
if(!pml1[pml1i].p) {
physptr_t phys = pm_alloc(1);
pml1[pml1i] = (pmle_t) {
.p = flg.present,
.rw = flg.writeable,
.us = flg.useraccess,
.xd = !flg.executable,
.paddr = phys >> PAGE_SHIFT
};
}
}
spinlock_release(pd->lock);
}
void vm_ensure(uintptr_t from, uintptr_t to, page_flags_t flg)
{ vm_pd_ensure(pd_current(), from, to, flg); }