95 lines
2.3 KiB
C
95 lines
2.3 KiB
C
#include "mem.h"
|
|
#include "cpu/memlayout.h"
|
|
#include "cpu/x86.h"
|
|
#include "console.h"
|
|
|
|
pde_t *kvm;
|
|
|
|
pde_t *setupkvm(void) {
|
|
pde_t *kvm = kalloc();
|
|
memset(kvm, 0, PGSIZE);
|
|
|
|
// Map physical memory to KERNBASE..KERNBASE+PHYSTOP
|
|
for (uintptr_t pa = 0; pa < PHYSTOP; pa += 4 << 20) {
|
|
uintptr_t va = KERNBASE + pa;
|
|
kvm[PDX(va)] = pa | PTE_P | PTE_W | PTE_PS;
|
|
}
|
|
return kvm;
|
|
}
|
|
|
|
void kvmalloc() {
|
|
kvm = setupkvm();
|
|
switchkvm();
|
|
}
|
|
|
|
void switchkvm(void)
|
|
{
|
|
lcr3(V2P(kvm)); // switch to the kernel page table
|
|
}
|
|
|
|
// Return the address of the PTE in page table pgdir
|
|
// that corresponds to virtual address va. If alloc!=0,
|
|
// create any required page table pages.
|
|
static pte_t *
|
|
walkpgdir(pde_t *pgdir, const void *va, int alloc)
|
|
{
|
|
pde_t *pde;
|
|
pte_t *pgtab;
|
|
|
|
pde = &pgdir[PDX(va)];
|
|
if (*pde & PTE_P) {
|
|
pgtab = (pte_t *)P2V(PTE_ADDR(*pde));
|
|
} else {
|
|
if (!alloc || (pgtab = (pte_t *)kalloc()) == 0)
|
|
return 0;
|
|
// Make sure all those PTE_P bits are zero.
|
|
memset(pgtab, 0, PGSIZE);
|
|
// The permissions here are overly generous, but they can
|
|
// be further restricted by the permissions in the page table
|
|
// entries, if necessary.
|
|
*pde = V2P(pgtab) | PTE_P | PTE_W | PTE_U;
|
|
}
|
|
return &pgtab[PTX(va)];
|
|
}
|
|
|
|
// Create PTEs for virtual addresses starting at va that refer to
|
|
// physical addresses starting at pa. va and size might not
|
|
// be page-aligned.
|
|
static int
|
|
mappages(pde_t *pgdir, void *va, uintptr_t size, uintptr_t pa, int perm)
|
|
{
|
|
char *a, *last;
|
|
pte_t *pte;
|
|
|
|
a = (char *)PGROUNDDOWN((uintptr_t)va);
|
|
last = (char *)PGROUNDDOWN(((uintptr_t)va) + size - 1);
|
|
for (;;)
|
|
{
|
|
if ((pte = walkpgdir(pgdir, a, 1)) == 0)
|
|
return -1;
|
|
if (*pte & PTE_P)
|
|
panic("remap");
|
|
*pte = pa | perm | PTE_P;
|
|
if (a == last)
|
|
break;
|
|
a += PGSIZE;
|
|
pa += PGSIZE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int allocuvm(pde_t *pgdir, uintptr_t base, uintptr_t top) {
|
|
for (uintptr_t a = PGROUNDUP(base); a < top; a += PGSIZE) {
|
|
char *pa = kalloc();
|
|
if (pa == 0) {
|
|
return -1;
|
|
}
|
|
memset(pa, 0, PGSIZE);
|
|
if (mappages(pgdir, (void*)a, PGSIZE, V2P(pa), PTE_W | PTE_U) < 0) {
|
|
kfree(pa);
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|