blob: 263ecd506ca57eadcc2944e978e2a35d305c3799 [file] [log] [blame] [edit]
#ifdef __SHARC__
#pragma nosharc
#define SINIT(x) x
#endif
/* See COPYRIGHT for copyright information. */
#include <arch/x86.h>
#include <arch/arch.h>
#include <arch/mmu.h>
#include <arch/apic.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>
// These variables are set in i386_vm_init()
pde_t* boot_pgdir; // Virtual address of boot time page directory
physaddr_t RO boot_cr3; // Physical address of boot time page directory
// Global descriptor table.
//
// The kernel and user segments are identical (except for the DPL).
// To load the SS register, the CPL must equal the DPL. Thus,
// we must duplicate the segments for the user and the kernel.
//
segdesc_t gdt_in_c[] =
{
// 0x0 - unused (always faults -- for trapping NULL far pointers)
SEG_NULL,
// 0x8 - kernel code segment
[GD_KT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 0),
// 0x10 - kernel data segment
[GD_KD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 0),
// 0x18 - user code segment
[GD_UT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 3),
// 0x20 - user data segment
[GD_UD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 3),
// 0x28 - tss, initialized in idt_init()
[GD_TSS >> 3] = SEG_NULL,
// 0x30 - LDT, set per-process
[GD_LDT >> 3] = SEG_NULL
};
/* Want gdt to be a pointer, not an array type (can replace it more easily) */
segdesc_t *gdt = gdt_in_c;
pseudodesc_t gdt_pd = {
sizeof(gdt_in_c) - 1, (unsigned long) gdt_in_c
};
// --------------------------------------------------------------
// Set up initial memory mappings and turn on MMU.
// --------------------------------------------------------------
static void check_boot_pgdir(bool pse);
//
// Map [la, la+size) of linear address space to physical [pa, pa+size)
// in the page table rooted at pgdir. Size is a multiple of PGSIZE.
// Use permission bits perm|PTE_P for the entries.
//
// To map with Jumbos, set PTE_PS in perm
static void
boot_map_segment(pde_t *COUNT(NPDENTRIES) pgdir, uintptr_t la, size_t size, physaddr_t pa, int perm)
{
uintptr_t i;
pte_t *pte;
// la can be page unaligned, but weird things will happen
// unless pa has the same offset. pa always truncates any
// possible offset. will warn. size can be weird too.
if (PGOFF(la)) {
warn("la not page aligned in boot_map_segment!");
size += PGOFF(la);
}
if (perm & PTE_PS) {
if (JPGOFF(la) || JPGOFF(pa))
panic("Tried to map a Jumbo page at an unaligned address!");
// need to index with i instead of la + size, in case of wrap-around
for (i = 0; i < size; i += JPGSIZE, la += JPGSIZE, pa += JPGSIZE) {
pte = pgdir_walk(pgdir, (void*)la, 2);
assert(pte);
*pte = PTE_ADDR(pa) | PTE_P | perm;
}
} else {
for (i = 0; i < size; i += PGSIZE, la += PGSIZE, pa += PGSIZE) {
pte = pgdir_walk(pgdir, (void*)la, 1);
assert(pte);
if (*pte & PTE_PS)
// if we start using the extra flag for PAT, which we aren't,
// this will warn, since PTE_PS and PTE_PAT are the same....
warn("Possibly attempting to map a regular page into a Jumbo PDE");
*pte = PTE_ADDR(pa) | PTE_P | perm;
}
}
}
// Set up a two-level page table:
// boot_pgdir is its linear (virtual) address of the root
// boot_cr3 is the physical adresss of the root
// Then turn on paging. Then effectively turn off segmentation.
// (i.e., the segment base addrs are set to zero).
//
// This function only sets up the kernel part of the address space
// (ie. addresses >= ULIM). The user part of the address space
// will be setup later.
//
// From UWLIM to ULIM, the user is allowed to read but not write.
// Above ULIM the user cannot read (or write).
void
vm_init(void)
{
pde_t* pgdir;
uint32_t cr0, edx;
size_t n;
bool pse;
pse = enable_pse();
if (pse)
cprintf("PSE capability detected.\n");
// we paniced earlier if we don't support PGE. turn it on now.
// it's used in boot_map_segment, which covers all of the mappings that are
// the same for all address spaces. and also for the VPT mapping below.
lcr4(rcr4() | CR4_PGE);
// set up mtrr's for core0. other cores will do the same later
setup_default_mtrrs(0);
/*
* PSE status:
* - can walk and set up boot_map_segments with jumbos but can't
* insert yet. need to look at the page_dir and friends.
* - anything related to a single struct page still can't handle
* jumbos. will need to think about and adjust Page functions
* - do we want to store info like this in the struct page? or just check
* by walking the PTE
* - when we alloc a page, and we want it to be 4MB, we'll need
* to have contiguous memory, etc
* - there's a difference between having 4MB page table entries
* and having 4MB Page tracking structs. changing the latter will
* break a lot of things
* - showmapping and friends work on a 4KB granularity, but map to the
* correct entries
* - need to not insert / boot_map a single page into an area that is
* already holding a jumbo page. will need to break the jumbo up so that
* we can then insert the lone page. currently warns.
* - some inherent issues with the pgdir_walks returning a PTE, and we
* don't know whether it is a jumbo (PDE) or a regular PTE.
*/
//////////////////////////////////////////////////////////////////////
// create initial page directory.
pgdir = kpage_zalloc_addr();
assert(pgdir);
boot_pgdir = pgdir;
boot_cr3 = PADDR(pgdir);
// helpful if you want to manually walk with kvm / bochs
//printk("pgdir va = %p, pgdir pa = %p\n\n", pgdir, PADDR(pgdir));
//////////////////////////////////////////////////////////////////////
// Recursively insert PD in itself as a page table, to form
// a virtual page table at virtual address VPT.
// (For now, you don't have understand the greater purpose of the
// following two lines. Unless you are eagle-eyed, in which case you
// should already know.)
// Permissions: kernel RW, user NONE, Global Page
pgdir[PDX(VPT)] = PADDR(pgdir) | PTE_W | PTE_P | PTE_G;
// same for UVPT
// Permissions: kernel R, user R, Global Page
pgdir[PDX(UVPT)] = PADDR(pgdir) | PTE_U | PTE_P | PTE_G;
//////////////////////////////////////////////////////////////////////
// Map all of physical memory at KERNBASE.
// Ie. the VA range [KERNBASE, 2^32) should map to
// the PA range [0, 2^32 - KERNBASE)
// We might not have 2^32 - KERNBASE bytes of physical memory, but
// we just set up the mapping anyway.
// Permissions: kernel RW, user NONE
// Your code goes here:
// this maps all of the possible phys memory
// note the use of unsigned underflow to get size = 0x40000000
//boot_map_segment(pgdir, KERNBASE, -KERNBASE, 0, PTE_W);
// but this only maps what is available, and saves memory. every 4MB of
// mapped memory requires a 2nd level page: 2^10 entries, each covering 2^12
// need to modify tests below to account for this
if (pse) {
// map the first 4MB as regular entries, to support different MTRRs
boot_map_segment(pgdir, KERNBASE, JPGSIZE, 0, PTE_W | PTE_G);
boot_map_segment(pgdir, KERNBASE + JPGSIZE, max_paddr - JPGSIZE, JPGSIZE,
PTE_W | PTE_G | PTE_PS);
} else
boot_map_segment(pgdir, KERNBASE, max_paddr, 0, PTE_W | PTE_G);
// APIC mapping: using PAT (but not *the* PAT flag) to make these type UC
// IOAPIC
boot_map_segment(pgdir, IOAPIC_BASE, APIC_SIZE, IOAPIC_PBASE,
PTE_PCD | PTE_PWT | PTE_W | PTE_G);
// Local APIC
boot_map_segment(pgdir, LAPIC_BASE, APIC_SIZE, LAPIC_PBASE,
PTE_PCD | PTE_PWT | PTE_W | PTE_G);
// Check that the initial page directory has been set up correctly.
check_boot_pgdir(pse);
//////////////////////////////////////////////////////////////////////
// On x86, segmentation maps a VA to a LA (linear addr) and
// paging maps the LA to a PA. I.e. VA => LA => PA. If paging is
// turned off the LA is used as the PA. Note: there is no way to
// turn off segmentation. The closest thing is to set the base
// address to 0, so the VA => LA mapping is the identity.
// Current mapping: VA KERNBASE+x => PA x.
// (segmentation base=-KERNBASE and paging is off)
// From here on down we must maintain this VA KERNBASE + x => PA x
// mapping, even though we are turning on paging and reconfiguring
// segmentation.
// Map VA 0:4MB same as VA KERNBASE, i.e. to PA 0:4MB.
// (Limits our kernel to <4MB)
/* They mean linear address 0:4MB, and the kernel < 4MB is only until
* segmentation is turned off.
* once we turn on paging, segmentation is still on, so references to
* KERNBASE+x will get mapped to linear address x, which we need to make
* sure can map to phys addr x, until we can turn off segmentation and
* KERNBASE+x maps to LA KERNBASE+x, which maps to PA x, via paging
*/
pgdir[0] = pgdir[PDX(KERNBASE)];
// Install page table.
lcr3(boot_cr3);
// Turn on paging.
cr0 = rcr0();
// CD and NW should already be on, but just in case these turn on caching
cr0 |= CR0_PE|CR0_PG|CR0_AM|CR0_WP|CR0_NE|CR0_MP;
cr0 &= ~(CR0_TS|CR0_EM|CR0_CD|CR0_NW);
lcr0(cr0);
// Current mapping: KERNBASE+x => x => x.
// (x < 4MB so uses paging pgdir[0])
// Reload all segment registers.
asm volatile("lgdt gdt_pd");
asm volatile("movw %%ax,%%gs" :: "a" (GD_UD|3));
asm volatile("movw %%ax,%%fs" :: "a" (GD_UD|3));
asm volatile("movw %%ax,%%es" :: "a" (GD_KD));
asm volatile("movw %%ax,%%ds" :: "a" (GD_KD));
asm volatile("movw %%ax,%%ss" :: "a" (GD_KD));
asm volatile("ljmp %0,$1f\n 1:\n" :: "i" (GD_KT)); // reload cs
asm volatile("lldt %%ax" :: "a" (0));
// Final mapping: KERNBASE+x => KERNBASE+x => x.
// This mapping was only used after paging was turned on but
// before the segment registers were reloaded.
pgdir[0] = 0;
// Flush the TLB for good measure, to kill the pgdir[0] mapping.
tlb_flush_global();
}
void x86_cleanup_bootmem(void)
{
#define trampoline_pg 0x00001000UL
// Remove the mapping of the page used by the trampoline
page_remove(boot_pgdir, (void*)trampoline_pg);
// Remove the page table used for that mapping
pagetable_remove(boot_pgdir, (void*)trampoline_pg);
}
//
// Checks that the kernel part of virtual address space
// has been setup roughly correctly(by i386_vm_init()).
//
// This function doesn't test every corner case,
// in fact it doesn't test the permission bits at all,
// but it is a pretty good sanity check.
//
static physaddr_t check_va2pa(pde_t *COUNT(NPDENTRIES) pgdir, uintptr_t va);
static void
check_boot_pgdir(bool pse)
{
uint32_t i, n;
pde_t *pgdir, pte;
pgdir = boot_pgdir;
// check phys mem
//for (i = 0; KERNBASE + i != 0; i += PGSIZE)
// adjusted check to account for only mapping avail mem
if (pse)
for (i = 0; i < max_paddr; i += JPGSIZE)
assert(check_va2pa(pgdir, KERNBASE + i) == i);
else
for (i = 0; i < max_paddr; i += PGSIZE)
assert(check_va2pa(pgdir, KERNBASE + i) == i);
// check for zero/non-zero in PDEs
for (i = 0; i < NPDENTRIES; i++) {
switch (i) {
case PDX(VPT):
case PDX(UVPT):
case PDX(LAPIC_BASE): // LAPIC mapping. TODO: remove when MTRRs are up
assert(pgdir[i]);
break;
default:
//if (i >= PDX(KERNBASE))
// adjusted check to account for only mapping avail mem
// and you can't KADDR maxpa (just above legal range)
// max_paddr can be up to maxpa, so assume the worst
if (i >= PDX(KERNBASE) && i <= PDX(KADDR(max_paddr-1)))
assert(pgdir[i]);
else
assert(pgdir[i] == 0);
break;
}
}
/* check permissions
* user read-only. check for user and write, should be only user
* eagle-eyed viewers should be able to explain the extra cases.
* for the mongoose-eyed, remember that weird shit happens when you loop
* through UVPT. Specifically, you can't loop once, then look at a jumbo
* page that is kernel only. That's the end of the page table for you, so
* having a U on the entry doesn't make sense. Thus we check for a jumbo
* page, and special case it. This will happen at 0xbf701000. Why is this
* magical? Get your eagle glasses and figure it out. */
for (i = UWLIM; i < ULIM; i+=PGSIZE) {
pte = get_va_perms(pgdir, (void*SAFE)TC(i));
if (pte & PTE_P) {
if (i == UVPT+(VPT >> 10))
continue;
if (*pgdir_walk(pgdir, (void*SAFE)TC(i), 0) & PTE_PS) {
assert((pte & PTE_U) != PTE_U);
assert((pte & PTE_W) != PTE_W);
} else {
assert((pte & PTE_U) == PTE_U);
assert((pte & PTE_W) != PTE_W);
}
}
}
// kernel read-write.
for (i = ULIM; i <= KERNBASE + max_paddr - PGSIZE; i+=PGSIZE) {
pte = get_va_perms(pgdir, (void*SAFE)TC(i));
if ((pte & PTE_P) && (i != VPT+(UVPT>>10))) {
assert((pte & PTE_U) != PTE_U);
assert((pte & PTE_W) == PTE_W);
}
}
// special mappings
pte = get_va_perms(pgdir, (void*SAFE)TC(UVPT+(VPT>>10)));
assert((pte & PTE_U) != PTE_U);
assert((pte & PTE_W) != PTE_W);
// note this means the kernel cannot directly manipulate this virtual address
// convince yourself this isn't a big deal, eagle-eyes!
pte = get_va_perms(pgdir, (void*SAFE)TC(VPT+(UVPT>>10)));
assert((pte & PTE_U) != PTE_U);
assert((pte & PTE_W) != PTE_W);
cprintf("check_boot_pgdir() succeeded!\n");
}
// This function returns the physical address of the page containing 'va',
// defined by the page directory 'pgdir'. The hardware normally performs
// this functionality for us! We define our own version to help check
// the check_boot_pgdir() function; it shouldn't be used elsewhere.
static physaddr_t
check_va2pa(pde_t *COUNT(NPDENTRIES) _pgdir, uintptr_t va)
{
pte_t *COUNT(NPTENTRIES) p;
pde_t *COUNT(1) pgdir;
pgdir = &_pgdir[PDX(va)];
if (!(*pgdir & PTE_P))
return ~0;
if (*pgdir & PTE_PS)
return PTE_ADDR(*pgdir);
p = (pte_t*COUNT(NPTENTRIES)) KADDR(PTE_ADDR(*pgdir));
if (!(p[PTX(va)] & PTE_P))
return ~0;
return PTE_ADDR(p[PTX(va)]);
}
/*
* Remove the second level page table associated with virtual address va.
* Will 0 out the PDE for that page table.
* Panics if the page table has any present entries.
* This should be called rarely and with good cause.
* Currently errors if the PDE is jumbo or not present.
*/
error_t pagetable_remove(pde_t *pgdir, void *va)
{
pde_t* the_pde = &pgdir[PDX(va)];
if (!(*the_pde & PTE_P) || (*the_pde & PTE_PS))
return -EFAULT;
pte_t* page_table = (pde_t*COUNT(NPTENTRIES))KADDR(PTE_ADDR(*the_pde));
for (int i = 0; i < NPTENTRIES; i++)
if (page_table[i] & PTE_P)
panic("Page table not empty during attempted removal!");
*the_pde = 0;
page_decref(pa2page(PADDR(page_table)));
return 0;
}
// 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.
//
// Hint: you can turn a Page * into the physical address of the
// page it refers to with page2pa() from kern/pmap.h.
//
// Supports returning jumbo (4MB PSE) PTEs. To create with a jumbo, pass in 2.
pte_t*
pgdir_walk(pde_t *pgdir, const void *SNT va, int create)
{
pde_t* the_pde = &pgdir[PDX(va)];
page_t *new_table;
if (*the_pde & PTE_P) {
if (*the_pde & PTE_PS)
return (pte_t*)the_pde;
return &((pde_t*COUNT(NPTENTRIES))KADDR(PTE_ADDR(*the_pde)))[PTX(va)];
}
if (!create)
return NULL;
if (create == 2) {
if (JPGOFF(va))
panic("Attempting to find a Jumbo PTE at an unaligned VA!");
*the_pde = PTE_PS | PTE_P;
return (pte_t*)the_pde;
}
if (kpage_alloc(&new_table))
return NULL;
memset(page2kva(new_table), 0, PGSIZE);
/* storing our ref to new_table in the PTE */
*the_pde = (pde_t)page2pa(new_table) | PTE_P | PTE_W | PTE_U;
return &((pde_t*COUNT(NPTENTRIES))KADDR(PTE_ADDR(*the_pde)))[PTX(va)];
}
/* Returns the effective permissions for PTE_U, PTE_W, and PTE_P on a given
* virtual address. Note we need to consider the composition of every PTE in
* the page table walk. */
int get_va_perms(pde_t *pgdir, const void *SNT va)
{
pde_t the_pde = pgdir[PDX(va)];
pte_t the_pte;
if (!(the_pde & PTE_P))
return 0;
if (the_pde & PTE_PS)
return the_pde & (PTE_U | PTE_W | PTE_P);
the_pte = ((pde_t*COUNT(NPTENTRIES))KADDR(PTE_ADDR(the_pde)))[PTX(va)];
if (!(the_pte & PTE_P))
return 0;
return the_pte & the_pde & (PTE_U | PTE_W | PTE_P);
}
void
page_check(void)
{
page_t *pp, *pp0, *pp1, *pp2;
page_list_t fl[1024];
pte_t *ptep;
// should be able to allocate three pages
pp0 = pp1 = pp2 = 0;
assert(kpage_alloc(&pp0) == 0);
assert(kpage_alloc(&pp1) == 0);
assert(kpage_alloc(&pp2) == 0);
assert(pp0);
assert(pp1 && pp1 != pp0);
assert(pp2 && pp2 != pp1 && pp2 != pp0);
// temporarily steal the rest of the free pages
for(int i=0; i<llc_cache->num_colors; i++) {
fl[i] = colored_page_free_list[i];
LIST_INIT(&colored_page_free_list[i]);
}
// should be no free memory
assert(kpage_alloc(&pp) == -ENOMEM);
// Fill pp1 with bogus data and check for invalid tlb entries
memset(page2kva(pp1), 0xFFFFFFFF, PGSIZE);
// there is no page allocated at address 0
assert(page_lookup(boot_pgdir, (void *) 0x0, &ptep) == NULL);
// there is no free memory, so we can't allocate a page table
assert(page_insert(boot_pgdir, pp1, 0x0, 0) < 0);
// free pp0 and try again: pp0 should be used for page table
page_decref(pp0);
assert(page_insert(boot_pgdir, pp1, 0x0, 0) == 0);
tlb_invalidate(boot_pgdir, 0x0);
// DEP Should have shot down invalid TLB entry - let's check
{ TRUSTEDBLOCK
int *x = 0x0;
assert(*x == 0xFFFFFFFF);
}
assert(PTE_ADDR(boot_pgdir[0]) == page2pa(pp0));
assert(check_va2pa(boot_pgdir, 0x0) == page2pa(pp1));
assert(kref_refcnt(&pp1->pg_kref) == 2);
assert(kref_refcnt(&pp0->pg_kref) == 1);
// should be able to map pp2 at PGSIZE because pp0 is already allocated for page table
assert(page_insert(boot_pgdir, pp2, (void*SNT) PGSIZE, 0) == 0);
assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
assert(kref_refcnt(&pp2->pg_kref) == 2);
// Make sure that pgdir_walk returns a pointer to the pte and
// not the table or some other garbage
{
pte_t *p = (pte_t*COUNT(NPTENTRIES))KADDR(PTE_ADDR(boot_pgdir[PDX(PGSIZE)]));
assert(pgdir_walk(boot_pgdir, (void *SNT)PGSIZE, 0) == &p[PTX(PGSIZE)]);
}
// should be no free memory
assert(kpage_alloc(&pp) == -ENOMEM);
// should be able to map pp2 at PGSIZE because it's already there
assert(page_insert(boot_pgdir, pp2, (void*SNT) PGSIZE, PTE_U) == 0);
assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
assert(kref_refcnt(&pp2->pg_kref) == 2);
// Make sure that we actually changed the permission on pp2 when we re-mapped it
{
pte_t *p = pgdir_walk(boot_pgdir, (void*SNT)PGSIZE, 0);
assert(((*p) & PTE_U) == PTE_U);
}
// pp2 should NOT be on the free list
// could happen if ref counts are handled sloppily in page_insert
assert(kpage_alloc(&pp) == -ENOMEM);
// should not be able to map at PTSIZE because need free page for page table
assert(page_insert(boot_pgdir, pp0, (void*SNT) PTSIZE, 0) < 0);
// insert pp1 at PGSIZE (replacing pp2)
assert(page_insert(boot_pgdir, pp1, (void*SNT) PGSIZE, 0) == 0);
// should have pp1 at both 0 and PGSIZE, pp2 nowhere, ...
assert(check_va2pa(boot_pgdir, 0) == page2pa(pp1));
assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
// ... and ref counts should reflect this
assert(kref_refcnt(&pp1->pg_kref) == 3);
assert(kref_refcnt(&pp2->pg_kref) == 1);
// pp2 should be returned by page_alloc
page_decref(pp2); /* should free it */
assert(kpage_alloc(&pp) == 0 && pp == pp2);
// unmapping pp1 at 0 should keep pp1 at PGSIZE
page_remove(boot_pgdir, 0x0);
assert(check_va2pa(boot_pgdir, 0x0) == ~0);
assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp1));
assert(kref_refcnt(&pp1->pg_kref) == 2);
assert(kref_refcnt(&pp2->pg_kref) == 1);
// unmapping pp1 at PGSIZE should free it
page_remove(boot_pgdir, (void*SNT) PGSIZE);
assert(check_va2pa(boot_pgdir, 0x0) == ~0);
assert(check_va2pa(boot_pgdir, PGSIZE) == ~0);
assert(kref_refcnt(&pp1->pg_kref) == 1);
assert(kref_refcnt(&pp2->pg_kref) == 1);
page_decref(pp1);
// so it should be returned by page_alloc
assert(kpage_alloc(&pp) == 0 && pp == pp1);
// should be no free memory
assert(kpage_alloc(&pp) == -ENOMEM);
// forcibly take pp0 back
assert(PTE_ADDR(boot_pgdir[0]) == page2pa(pp0));
boot_pgdir[0] = 0;
assert(kref_refcnt(&pp0->pg_kref) == 1);
// Catch invalid pointer addition in pgdir_walk - i.e. pgdir + PDX(va)
{
// Give back pp0 for a bit
page_decref(pp0);
void *SNT va = (void *SNT)((PGSIZE * NPDENTRIES) + PGSIZE);
pte_t *p2 = pgdir_walk(boot_pgdir, va, 1);
pte_t *p = (pte_t*COUNT(NPTENTRIES))KADDR(PTE_ADDR(boot_pgdir[PDX(va)]));
assert(p2 == &p[PTX(va)]);
// Clean up again
boot_pgdir[PDX(va)] = 0;
}
// give free list back
for(int i=0; i<llc_cache->num_colors; i++)
colored_page_free_list[i] = fl[i];
// free the pages we took
page_decref(pp0);
page_decref(pp1);
page_decref(pp2);
assert(!kref_refcnt(&pp0->pg_kref));
assert(!kref_refcnt(&pp1->pg_kref));
assert(!kref_refcnt(&pp2->pg_kref));
cprintf("page_check() succeeded!\n");
}
/* Walks len bytes from start, executing 'callback' on every PTE, passing it a
* specific VA and whatever arg is passed in. Note, this cannot handle jumbo
* pages. */
int env_user_mem_walk(env_t* e, void* start, size_t len,
mem_walk_callback_t callback, void* arg)
{
pte_t *pt;
uint32_t pdeno, pteno;
physaddr_t pa;
assert((uintptr_t)start % PGSIZE == 0 && len % PGSIZE == 0);
uintptr_t end = (uintptr_t)start+len;
uint32_t pdeno_start = PDX(start);
uint32_t pdeno_end = PDX(ROUNDUP(end,PTSIZE));
/* concerned about overflow. this should catch it for now, given the above
* assert. */
assert((len == 0) || (pdeno_start < pdeno_end));
for (pdeno = pdeno_start; pdeno < pdeno_end; pdeno++) {
if (!(e->env_pgdir[pdeno] & PTE_P))
continue;
/* find the pa and a pointer to the page table */
pa = PTE_ADDR(e->env_pgdir[pdeno]);
pt = (pte_t*COUNT(NPTENTRIES)) KADDR(pa);
/* figure out where we start and end within the page table */
uint32_t pteno_start = (pdeno == pdeno_start ? PTX(start) : 0);
uint32_t pteno_end = (pdeno == pdeno_end - 1 && PTX(end) != 0 ?
PTX(end) : NPTENTRIES );
int ret;
for (pteno = pteno_start; pteno < pteno_end; pteno++) {
if((ret = callback(e, &pt[pteno], PGADDR(pdeno, pteno, 0), arg)))
return ret;
}
}
return 0;
}
/* Frees (decrefs) all pages of the process's page table, including the page
* directory. Does not free the memory that is actually mapped. */
void env_pagetable_free(env_t* e)
{
static_assert(UVPT % PTSIZE == 0);
assert(e->env_cr3 != rcr3());
for(uint32_t pdeno = 0; pdeno < PDX(UVPT); pdeno++)
{
// only look at mapped page tables
if (!(e->env_pgdir[pdeno] & PTE_P))
continue;
// find the pa and va of the page table
physaddr_t pa = PTE_ADDR(e->env_pgdir[pdeno]);
// free the page table itself
e->env_pgdir[pdeno] = 0;
page_decref(pa2page(pa));
}
// free the page directory
physaddr_t pa = e->env_cr3;
e->env_cr3 = 0;
page_decref(pa2page(pa));
tlbflush();
}