summaryrefslogblamecommitdiffstats
path: root/arch/x86_64/pagedirectory.c
blob: cdfffeba72e79707c5ae71b1b164737e0dcf2123 (plain) (tree)









































































































































                                                                                          
#include "arch/page.h"
#include "arch/processor.h"
#include "klib/rbtree.h"
#include "boot.h"
#include "memory.h"
#include "string.h"
#include "print.h"

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 page_directory_t s_kernel_initial_pd;

static rbtree_t s_page_directories;
static intmax_t s_next_pdid = 0;

page_directory_t*
pd_new()
{
    page_directory_t newpd = {
        .id = s_next_pdid++,
        .phys = pm_alloc(1),
    };
    newpd.pml = (void*)pm_tovirt(newpd.phys);
    for(size_t i = 256; i < 512; i++) {
        pmle_t *kpe = &(s_kernel_initial_pd.pml)[i];
        pmle_t *ppe = &(newpd.pml)[i];
        *ppe = *kpe;
    }

    return rbtree_insert(&s_page_directories, newpd.id, &newpd);
}

static void
s_pd_dup_pml(pmle_t *src, pmle_t *dest, size_t l, size_t i)
{
    pmle_t srce = src[i];
    if(!srce.p) return;

    dest[i] = srce;
    dest[i].paddr = pm_alloc(1);
    pmle_t dste = dest[i];

    pmle_t *srct = (pmle_t*)pm_tovirt(srce.paddr << PAGE_SHIFT);
    pmle_t *dstt = (pmle_t*)pm_tovirt(dste.paddr << PAGE_SHIFT);

    if(l == 0) {
        memcpy(dstt, srct, PAGE_SIZE);
        return;
    }

    for(i = 0; i < 512; i++) {
        dstt[i] = srct[i];
        if(!srct[i].p) continue;
        s_pd_dup_pml(srct, dstt, l - 1, i);
    }
}

page_directory_t*
pd_dup(page_directory_t *pd)
{
    page_directory_t *newpd = pd_new();
    for(size_t i = 0; i < 256; i++) {
        s_pd_dup_pml(pd->pml, newpd->pml, 3, i);
    }
    return newpd;
}

page_directory_t*
pd_get(pdid_t pdid)
{
    return rbtree_find(&s_page_directories, pdid);
}

void 
pd_switch(page_directory_t *pd)
{
    processor_t *pc = processor_current();
    if(pc->pd == pd) return;
    pc->pd = pd;
    __asm__ volatile("movq %0, %%cr3":: "r"(pd->phys));
}

void
vm_setup_early(void)
{
    memset(s_kernel_initial_pml4, 0, PAGE_SIZE);
    memset(s_kernel_initial_pml3, 0, 2 * PAGE_SIZE);
    memset(s_kernel_initial_pml2, 0, 2 * PAGE_SIZE);
    memset(s_kernel_initial_pml1, 0, PAGE_SIZE);
    s_kernel_initial_pd = (page_directory_t){
        .phys = vm_tophys_koff((uintptr_t)&s_kernel_initial_pml4),
        .pml = (pmle_t*)&s_kernel_initial_pml4,
        .id = s_next_pdid++
    };
    processor_current()->pd = &s_kernel_initial_pd;

    /* Map first few GiBs */
    s_kernel_initial_pml4[256] =
        vm_tophys_koff((uintptr_t)&s_kernel_initial_pml3[0])
        | 3;
    s_kernel_initial_pml3[0][0] =
        vm_tophys_koff((uintptr_t)&s_kernel_initial_pml2[0])
        | 3;
    for(int i = 0; i < 512; i++) {
        s_kernel_initial_pml2[0][i] = (i * (PAGE_SIZE * 512)) | 0x80 | 3;
    }

    size_t kernel_pml3e = (_kernel_virtual_base >> (30)) % 512;
    size_t kernel_pml2e = (_kernel_virtual_base >> (21)) % 512;
    size_t kernel_npages = ((((uintptr_t)&_kernel_end) - _kernel_virtual_base) >> 12) + 1;
    klogf("Kernel has %i pages\n", kernel_npages);

    /* Map kernel pages */
    s_kernel_initial_pml4[511] =
        vm_tophys_koff((uintptr_t)&s_kernel_initial_pml3[1]) | 3;
    s_kernel_initial_pml3[1][kernel_pml3e] =
        vm_tophys_koff((uintptr_t)&s_kernel_initial_pml2[1]) | 3;
    s_kernel_initial_pml2[1][kernel_pml2e] =
        vm_tophys_koff((uintptr_t)&s_kernel_initial_pml1[0]) | 3;
    for(size_t i = 0; i < kernel_npages; i++) {
        s_kernel_initial_pml1[0][i] = (i * PAGE_SIZE) + boot_kernel_physical_address | 3;
    }
 
    extern int_state_t *_pagefault_handler(int_state_t*);
    int_handler_set(14, _pagefault_handler);
    __asm__ volatile("mov %0, %%cr3":: "r"(s_kernel_initial_pd.phys));
}

void
vm_setup(void)
{
    rbtree_new(&s_page_directories, page_directory_t);

    processor_t *proc = processor_current();
    pd_switch(pd_new());
}