Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ void uartputc(int);
// vm.c
void seginit(void);
void switchuvm(struct proc*);
pde_t* setupkvm(void);
void kvmalloc(void);
void switchkvm(void);

// number of elements in fixed-size array
#define NELEM(x) (sizeof(x)/sizeof((x)[0]))
5 changes: 3 additions & 2 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ int
main(void)
{
kinit(end, P2V(PHYSTOP)); // phys page allocator
consoleinit(); // console hardware (moved up so early panics are visible)
uartinit(); // serial port (moved up so early panics are visible)
kvmalloc(); // kernel page table
mpinit(); // detect other processors
lapicinit(); // interrupt controller
picinit(); // disable pic
ioapicinit(); // another interrupt controller
consoleinit(); // console hardware
uartinit(); // serial port
ideinit(); // disk
tvinit(); // trap vectors
binit(); // buffer cache
Expand Down
29 changes: 29 additions & 0 deletions mmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,32 @@ struct gatedesc {
}

#endif

// Page directory and page table constants.
#define NPDENTRIES 1024 // # directory entries per page directory
#define NPTENTRIES 1024 // # PTEs per page table

#define PTXSHIFT 12 // offset of PTX in a linear address
#define PDXSHIFT 22 // offset of PDX in a linear address

// page directory index
#define PDX(va) (((uint)(va) >> PDXSHIFT) & 0x3FF)

// page table index
#define PTX(va) (((uint)(va) >> PTXSHIFT) & 0x3FF)

#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1))

// Page table/directory entry flags.
#define PTE_P 0x001 // Present
#define PTE_W 0x002 // Writeable
#define PTE_U 0x004 // User
#define PTE_PS 0x080 // Page Size

// Address in page table or page directory entry
#define PTE_ADDR(pte) ((uint)(pte) & ~0xFFF)
#define PTE_FLAGS(pte) ((uint)(pte) & 0xFFF)

#ifndef __ASSEMBLER__
typedef uint pte_t;
#endif
115 changes: 115 additions & 0 deletions vm.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
#include "param.h"
#include "types.h"
#include "memlayout.h"
#include "asm.h"
#include "mmu.h"
#include "proc.h"
#include "defs.h"
#include "x86.h"
#include "elf.h"

// x86 hardware page tables are 4KB-granular. The rest of this branch still
// uses the 1MB segmentation PGSIZE (see mmu.h) for process layout and kalloc,
// so override PGSIZE locally here for the page-table math. kalloc still
// returns 1MB chunks; we only use the first 4KB of each as a page
// directory / page table, which is wasteful but structurally correct.
#undef PGSIZE
#define PGSIZE 4096

extern char data[]; // defined by kernel.ld
pde_t *kpgdir; // for use in scheduler()

// Set up CPU's kernel segment descriptors.
// Run once on entry on each CPU.
Expand Down Expand Up @@ -46,4 +59,106 @@ switchuvm(struct proc *p)
mycpu()->ts.iomb = (ushort) 0xFFFF;
ltr(SEG_TSS << 3);
popcli();
}

// 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, uint size, uint pa, int perm)
{
char *a, *last;
pte_t *pte;

a = (char*)PGROUNDDOWN((uint)va);
last = (char*)PGROUNDDOWN(((uint)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;
}

// This table defines the kernel's mappings, which are present in
// every process's page table.
static struct kmap {
void *virt;
uint phys_start;
uint phys_end;
int perm;
} kmap[] = {
{ (void*)KERNBASE, 0, EXTMEM, PTE_W}, // I/O space
{ (void*)KERNLINK, V2P(KERNLINK), V2P(data), 0}, // kern text+rodata
{ (void*)data, V2P(data), PHYSTOP, PTE_W}, // kern data+memory
{ (void*)DEVSPACE, DEVSPACE, 0, PTE_W}, // more devices
};

// Set up kernel part of a page table.
pde_t*
setupkvm(void)
{
pde_t *pgdir;
struct kmap *k;

if((pgdir = (pde_t*)kalloc()) == 0)
return 0;
memset(pgdir, 0, PGSIZE);
if (P2V(PHYSTOP) > (void*)DEVSPACE)
panic("PHYSTOP too high");
for(k = kmap; k < &kmap[NELEM(kmap)]; k++)
if(mappages(pgdir, k->virt, k->phys_end - k->phys_start,
(uint)k->phys_start, k->perm) < 0) {
kfree((char*)pgdir);
return 0;
}
return pgdir;
}

// Switch h/w page table register to the kernel-only page table,
// for when no process is running.
void
switchkvm(void)
{
lcr3(V2P(kpgdir)); // switch to the kernel page table
}

// Allocate one page table for the machine for the kernel address
// space for scheduler processes.
void
kvmalloc(void)
{
kpgdir = setupkvm();
switchkvm();
}
6 changes: 6 additions & 0 deletions x86.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ rcr2(void)
return val;
}

static inline void
lcr3(uint val)
{
asm volatile("movl %0,%%cr3" : : "r" (val));
}

static inline void
noop(void)
{
Expand Down