#include "paging.h"
#include "interrupt.h"
#include <stddef.h>
#include "lib/jove.h"
#include "io/log.h"
#include "lib/string.h"
#include "boot/boot.h"
extern void *_kernel_end;
PAGEALIGN static uint64_t s_kernel_initial_pml4[512];
PAGEALIGN static uint64_t s_kernel_initial_pml3[2][512];
PAGEALIGN static uint64_t s_kernel_initial_pml2[2][512];
PAGEALIGN static uint64_t s_kernel_initial_pml1[2][512];
static struct PageDirectory s_kernel_initial_pd;
struct PageDirectory *mem_current_pd;
physptr_t
mem_linear_tophys_koffset(uintptr_t virt)
{
return (virt - boot_kernel_virtual_base) + boot_kernel_physical_address;
}
uintptr_t
mem_phys_tolinear(physptr_t phys)
{
return (uintptr_t)(phys + 0xFFFF800000000000ULL);
}
static size_t
s_paging_pmle(size_t l, uintptr_t addr)
{
size_t shift = (12 + (9 * l));
return (addr & (0x1FFULL << shift)) >> shift;
}
static union PageEntry*
s_paging_fetch_table(union PageEntry *pt, size_t l, uintptr_t virt)
{
size_t pmle = s_paging_pmle(l, virt);
union PageEntry entry = pt[pmle];
bool entry_new = false;
if(!entry.p) {
entry_new = true;
entry.value = mem_phys_alloc(1);
entry.p = 1;
entry.rw = 1;
entry.us = 1;
pt[pmle] = entry;
}
union PageEntry *table = (union PageEntry*)(mem_phys_tolinear(entry.paddr << 12));
if(entry_new) memset(table, 0, PAGESIZE);
return table;
}
static union PageEntry*
s_paging_get_table(union PageEntry *pt, size_t l, uintptr_t virt)
{
if(pt == NULL) return NULL;
size_t pmle = s_paging_pmle(l, virt);
union PageEntry entry = pt[pmle];
if(!entry.p) return NULL;
return (union PageEntry*)(mem_phys_tolinear(entry.paddr << 12));
}
physptr_t
mem_linear_tophys(uintptr_t virt)
{
struct PageDirectory *pd = mem_current_pd;
union PageEntry *pml3 = s_paging_get_table(pd->pml4_vaddr, 3, virt);
union PageEntry *pml2 = s_paging_get_table(pd->pml4_vaddr, 3, virt);
union PageEntry *pml1 = s_paging_get_table(pd->pml4_vaddr, 3, virt);
if(pml1 == NULL) return 0;
size_t pml1i = s_paging_pmle(0, virt);
if(!pml1[pml1i].p) return 0;
return pml1[pml1i].paddr << 12;
}
bool
mem_check_ptr(const void *ptr)
{
if(ptr == NULL) return false;
return mem_linear_tophys((uintptr_t)ptr) != 0;
}
void
mem_paging_map4k(struct PageDirectory *pd, physptr_t phys, uintptr_t virt, uint8_t flg)
{
union PageEntry *pml3 = s_paging_fetch_table(pd->pml4_vaddr, 3, virt);
union PageEntry *pml2 = s_paging_fetch_table(pml3, 2, virt);
union PageEntry *pml1 = s_paging_fetch_table(pml2, 1, virt);
size_t pml1e = s_paging_pmle(0, virt);
pml1[pml1e] = (union PageEntry) {
.p = (flg & 1) > 0,
.rw = (flg & 2) > 0,
.us = (flg & 4) > 0,
.paddr = phys >> 12
};
}
union PageEntry
mem_paging_fetch4k(struct PageDirectory *pd, uintptr_t virt)
{
union PageEntry *pml3 = s_paging_fetch_table(pd->pml4_vaddr, 3, virt);
union PageEntry *pml2 = s_paging_fetch_table(pml3, 2, virt);
union PageEntry *pml1 = s_paging_fetch_table(pml2, 1, virt);
return pml1[s_paging_pmle(0, virt)];
}
void
mem_pd_ensure_4k(struct PageDirectory *pd, uintptr_t virt, uint8_t flg)
{
union PageEntry pml1e = mem_paging_fetch4k(pd, virt);
if(!pml1e.p) {
uintptr_t phys = mem_phys_alloc(1);
mem_paging_map4k(pd, phys, virt, flg);
}
}
void
mem_pd_ensure_range(struct PageDirectory *pd, uintptr_t from, uintptr_t to, uint8_t flg)
{
from &= ~0xFFF;
for(; from < to; from += PAGESIZE)
mem_pd_ensure_4k(pd, from, flg);
}
void
mem_ensure_range_for(void *pd, uintptr_t from, uintptr_t to, bool rw, bool user)
{
mem_pd_ensure_range((struct PageDirectory*)pd, from, to, 1 | (rw << 1) | (user << 2));
}
void
mem_ensure_range(uintptr_t from, uintptr_t to, bool rw, bool user)
{
mem_ensure_range_for(mem_current_pd, from, to, rw, user);
}
void mem_pd_new(struct PageDirectory *pd)
{
physptr_t pml4p = mem_phys_alloc(1);
union PageEntry *pml4 = (union PageEntry*)mem_phys_tolinear(pml4p);
memset(pml4, 0, PAGESIZE);
memcpy(&pml4[256], &pd->pml4_vaddr[256], PAGESIZE / 2);
*pd = (struct PageDirectory){
.pml4_vaddr = pml4,
.pml4_paddr = pml4p,
.references = 1
};
}
void mem_pd_clone(struct PageDirectory *pd, struct PageDirectory *parent)
{
mem_pd_new(pd);
for(size_t i = 0; i < 256; i++) {
//TODO: Impl pd cloning
}
}
struct Registers*
s_pagefault_handler(struct Registers *state)
{
extern uint64_t __isr_err;
uintptr_t fault_addr = 0;
__asm__ volatile("movq %%cr2, %0": "=r"(fault_addr));
bool present = __isr_err & 1;
bool write = __isr_err & 2;
bool user = __isr_err & 4;
bool fetch = __isr_err & 16;
klogf("Page fault at %016X\n", fault_addr);
klogf("%s %s from a %s address\n",
user ? "user" : "kernel",
write ? "wrote" : "read",
present ? "present" : "non-present");
kpanic("Unhandled page fault at %016X\n", state->ip);
return state;
}
void
mem_paging_setup(void)
{
memset(s_kernel_initial_pml4, 0, PAGESIZE);
memset(s_kernel_initial_pml3, 0, 2 * PAGESIZE);
memset(s_kernel_initial_pml2, 0, 2 * PAGESIZE);
memset(s_kernel_initial_pml1, 0, PAGESIZE);
s_kernel_initial_pd = (struct PageDirectory){
.pml4_vaddr = (union PageEntry*)&s_kernel_initial_pml4,
.pml4_paddr = mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml4),
.references = 1
};
mem_current_pd = &s_kernel_initial_pd;
/* Map first few GiBs */
s_kernel_initial_pml4[256] =
mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml3[0])
| 3;
s_kernel_initial_pml3[0][0] =
mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml2[0])
| 3;
for(int i = 0; i < 512; i++) {
s_kernel_initial_pml2[0][i] = (i * (PAGESIZE * 512)) | 0x80 | 3;
}
size_t kernel_pml4e = (boot_kernel_virtual_base >> (39));
size_t kernel_pml3e = (boot_kernel_virtual_base >> (30)) % 512;
size_t kernel_pml2e = (boot_kernel_virtual_base >> (21)) % 512;
size_t kernel_npages = ((((uintptr_t)&_kernel_end) - boot_kernel_virtual_base) >> 12) + 1;
klogf("Kernel has %i pages\n", kernel_npages);
/* Map kernel pages */
s_kernel_initial_pml4[511] =
mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml3[1]) | 3;
s_kernel_initial_pml3[1][kernel_pml3e] =
mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml2[1]) | 3;
s_kernel_initial_pml2[1][kernel_pml2e] =
mem_linear_tophys_koffset((uintptr_t)&s_kernel_initial_pml1[0]) | 3;
for(int i = 0; i < kernel_npages; i++) {
s_kernel_initial_pml1[0][i] = (i * PAGESIZE) + boot_kernel_physical_address | 3;
}
int_set_handler(14, s_pagefault_handler);
__asm__ volatile("mov %0, %%cr3":: "r"(s_kernel_initial_pd.pml4_paddr));
}