summaryrefslogblamecommitdiffstats
path: root/arch/x86_64/paging.c
blob: 29c4863b7b1ea76fb083377a1785b0049bdbfa4e (plain) (tree)
1
2
                   
                      

































































































































                                                                                        





                                                                                          

                                                                  
                                                             



















                                                                         
                               


     






















                                                         









































                                                                                              
                                             

                                                                            
#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_take4k();
        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_take4k();
        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_take4k();
    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));
}