blob: 4ce640c27a29d29ec5a72336e6dabb2fbd61b80f [file] [log] [blame] [edit]
/* See COPYRIGHT for copyright information. */
#include <arch/arch.h>
#include <arch/mmu.h>
#include <error.h>
#include <sys/queue.h>
#include <atomic.h>
#include <string.h>
#include <assert.h>
#include <pmap.h>
#include <kclock.h>
#include <env.h>
#include <stdio.h>
#include <kmalloc.h>
#include <page_alloc.h>
pde_t* boot_pgdir; // Virtual address of boot time page directory
physaddr_t boot_cr3; // Physical address of boot time page directory
// --------------------------------------------------------------
// Set up initial memory mappings and turn on MMU.
// --------------------------------------------------------------
void
vm_init(void)
{
// we already set up our page tables before jumping
// into the kernel, so there's not much going on here
extern pte_t l1pt[NPTENTRIES];
boot_pgdir = l1pt;
boot_cr3 = PADDR(boot_pgdir);
}
// Given 'pgdir', a pointer to a page directory, pgdir_walk returns
// a pointer to the page table entry (PTE) for linear address 'va'.
// This requires walking the two-level page table structure.
//
// If the relevant page table doesn't exist in the page directory, then:
// - If create == 0, pgdir_walk returns NULL.
// - Otherwise, pgdir_walk tries to allocate a new page table
// with page_alloc. If this fails, pgdir_walk returns NULL.
// - Otherwise, pgdir_walk returns a pointer into the new page table.
//
// This is boot_pgdir_walk, but using page_alloc() instead of boot_alloc().
// Unlike boot_pgdir_walk, pgdir_walk can fail.
pte_t*
pgdir_walk(pde_t *pgdir, const void *SNT va, int create)
{
pte_t* ppte;
pte_t* pt;
pt = pgdir;
for(int i = 0; i < NPTLEVELS-1; i++)
{
// this code relies upon the fact that all page tables are the same size
uintptr_t idx = (uintptr_t)va >> (L1PGSHIFT - i*(L1PGSHIFT-L2PGSHIFT));
idx = idx & (NPTENTRIES-1);
ppte = &pt[idx];
if(*ppte & PTE_E)
return ppte;
if(!(*ppte & PTE_T))
{
if(!create)
return NULL;
page_t *new_table;
if(kpage_alloc(&new_table))
return NULL;
memset(page2kva(new_table), 0, PGSIZE);
*ppte = PTD(page2pa(new_table));
}
pt = (pte_t*)KADDR(PTD_ADDR(*ppte));
}
uintptr_t idx = (uintptr_t)va >> (L1PGSHIFT - (NPTLEVELS-1)*(L1PGSHIFT-L2PGSHIFT));
idx = idx & (NPTENTRIES-1);
return &pt[idx];
}
/* Returns the effective permissions for PTE_U, PTE_W, and PTE_P on a given
* virtual address. */
int get_va_perms(pde_t *pgdir, const void *SNT va)
{
pte_t* pte = pgdir_walk(pgdir, va, 0);
return pte == NULL ? 0 : (*pte & (PTE_PERM | PTE_E));
}
void
page_check(void)
{
}