121 lines
3.3 KiB
C
121 lines
3.3 KiB
C
#include "console.h"
|
|
#include "cpu/memlayout.h"
|
|
#include "cpu/x86.h"
|
|
#include "mem.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 | PDE_PS;
|
|
}
|
|
return kvm;
|
|
}
|
|
|
|
void kvmalloc() {
|
|
kvm = setupkvm();
|
|
switchkvm();
|
|
}
|
|
|
|
void switchkvm(void) {
|
|
lcr3(V2P(kvm)); // switch to the kernel page table
|
|
}
|
|
|
|
/* Before that point we worked with 4Mb kernel space huge pages
|
|
* But now we work with 4Kb userspace normal pages */
|
|
|
|
// 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) {
|
|
// we get an address from that entry of PgDir
|
|
// (that should point to physical memory of a page table),
|
|
// then we add KERNBASE, getting a pointer to table
|
|
// (which means the first table entry) in kernel virtual memory
|
|
pgtab = (pte_t *)P2V(PTE_ADDR(*pde));
|
|
} else {
|
|
if (!alloc)
|
|
return 0;
|
|
pgtab = (pte_t *)kalloc();
|
|
if (pgtab == 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.
|
|
// size might not be page-aligned.
|
|
static int mappages(pde_t *pgdir, void *va, uintptr_t size, uintptr_t pa,
|
|
int perm) {
|
|
if ((uintptr_t)va % PGSIZE != 0)
|
|
panic("Why??");
|
|
char *a = va;
|
|
char *last = (char *)PGROUNDDOWN(((uintptr_t)va) + size - 1);
|
|
for (;;) {
|
|
pte_t *pte = walkpgdir(pgdir, a, 1);
|
|
if (pte == 0)
|
|
return -1;
|
|
if (*pte & PTE_P)
|
|
panic("remap");
|
|
*pte = pa | perm | PTE_P;
|
|
if (a == last)
|
|
break;
|
|
a += PGSIZE;
|
|
pa += PGSIZE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// top may be not page-aligned
|
|
int allocuvm(pde_t *pgdir, uintptr_t base, uintptr_t top) {
|
|
if (base % PGSIZE != 0)
|
|
panic("You should at least consider killing yourself");
|
|
for (uintptr_t a = 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;
|
|
}
|
|
|
|
static void freept(pte_t *pt) {
|
|
for (int i = 0; i < NPTENTRIES; i++) {
|
|
if (pt[i] & PTE_P) {
|
|
kfree((char *)P2V(PTE_ADDR(pt[i])));
|
|
}
|
|
}
|
|
kfree((char *)pt);
|
|
}
|
|
|
|
void freevm(pde_t *pgdir) {
|
|
for (int i = 0; i < NPDENTRIES / 2; i++) {
|
|
if (pgdir[i] & PTE_P) {
|
|
freept((pte_t *)P2V(PTE_ADDR(pgdir[i])));
|
|
}
|
|
}
|
|
kfree(pgdir);
|
|
}
|