diff --git a/Makefile b/Makefile
index f67c88c..d52c976 100644
--- a/Makefile
+++ b/Makefile
@@ -109,7 +109,7 @@ initcode: initcode.S
 	$(OBJDUMP) -S initcode.o > initcode.asm
 
 kernel: $(OBJS) multiboot.o data.o bootother initcode
-	$(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernel multiboot.o data.o $(OBJS) -b binary initcode bootother
+	$(LD) $(LDFLAGS) -T kernel.ld -e multiboot_entry -o kernel multiboot.o data.o $(OBJS) -b binary initcode bootother
 	$(OBJDUMP) -S kernel > kernel.asm
 	$(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym
 
@@ -200,7 +200,7 @@ QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \
 ifndef CPUS
 CPUS := 2
 endif
-QEMUOPTS = -hdb fs.img xv6.img -smp $(CPUS)
+QEMUOPTS = -hdb fs.img xv6.img -smp $(CPUS) -m 512
 
 qemu: fs.img xv6.img
 	$(QEMU) -serial mon:stdio $(QEMUOPTS)
diff --git a/bootmain.c b/bootmain.c
index 7cd469f..fa2725d 100644
--- a/bootmain.c
+++ b/bootmain.c
@@ -8,6 +8,7 @@
 #include "types.h"
 #include "elf.h"
 #include "x86.h"
+#include "memlayout.h"
 
 #define SECTSIZE  512
 
@@ -19,7 +20,7 @@ bootmain(void)
   struct elfhdr *elf;
   struct proghdr *ph, *eph;
   void (*entry)(void);
-  uchar* va;
+  uchar* pa;
 
   elf = (struct elfhdr*)0x10000;  // scratch space
 
@@ -34,15 +35,15 @@ bootmain(void)
   ph = (struct proghdr*)((uchar*)elf + elf->phoff);
   eph = ph + elf->phnum;
   for(; ph < eph; ph++){
-    va = (uchar*)ph->va;
-    readseg(va, ph->filesz, ph->offset);
+    pa = (uchar*)ph->pa;
+    readseg(pa, ph->filesz, ph->offset);
     if(ph->memsz > ph->filesz)
-      stosb(va + ph->filesz, 0, ph->memsz - ph->filesz);
+      stosb(pa + ph->filesz, 0, ph->memsz - ph->filesz);
   }
 
   // Call the entry point from the ELF header.
   // Does not return!
-  entry = (void(*)(void))(elf->entry);
+  entry = (void(*)(void))(elf->entry & 0xFFFFFF);
   entry();
 }
 
diff --git a/bootother.S b/bootother.S
index 37b899b..2c97207 100644
--- a/bootother.S
+++ b/bootother.S
@@ -1,4 +1,5 @@
 #include "asm.h"
+#include "memlayout.h"
 
 # Each non-boot CPU ("AP") is started up in response to a STARTUP
 # IPI from the boot CPU.  Section B.4.2 of the Multi-Processor
@@ -24,6 +25,8 @@
 
 #define CR0_PE    1
 
+#define RELOC1(x) ((x) + KERNBASE)    // same as V2P, but without casts
+
 .code16           
 .globl start
 start:
@@ -40,7 +43,7 @@ start:
   movl    %eax, %cr0
 
 //PAGEBREAK!
-  ljmp    $(SEG_KCODE<<3), $start32
+  ljmpl    $(SEG_KCODE<<3), $(start32+KERNBASE)
 
 .code32
 start32:
@@ -53,10 +56,10 @@ start32:
   movw    %ax, %gs
 
   # switch to the stack allocated by bootothers()
-  movl    start-4, %esp
+  movl    RELOC1(start-4), %esp
 
   # call mpmain()
-  call	*(start-8)
+  call	*(RELOC1(start)-8)
 
   movw    $0x8a00, %ax
   movw    %ax, %dx
@@ -69,8 +72,9 @@ spin:
 .p2align 2
 gdt:
   SEG_NULLASM
-  SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)
-  SEG_ASM(STA_W, 0x0, 0xffffffff)
+  SEG_ASM(STA_X|STA_R, -KERNBASE, 0xffffffff)
+  SEG_ASM(STA_W, -KERNBASE, 0xffffffff)
+
 
 gdtdesc:
   .word   (gdtdesc - gdt - 1)
diff --git a/console.c b/console.c
index 27649e6..394176b 100644
--- a/console.c
+++ b/console.c
@@ -9,6 +9,7 @@
 #include "spinlock.h"
 #include "fs.h"
 #include "file.h"
+#include "memlayout.h"
 #include "mmu.h"
 #include "proc.h"
 #include "x86.h"
@@ -60,6 +61,9 @@ cprintf(char *fmt, ...)
   if(locking)
     acquire(&cons.lock);
 
+  if (fmt == 0)
+    panic("null fmt");
+
   argp = (uint*)(void*)(&fmt + 1);
   state = 0;
   for(i = 0; (c = fmt[i] & 0xff) != 0; i++){
@@ -121,7 +125,7 @@ panic(char *s)
 //PAGEBREAK: 50
 #define BACKSPACE 0x100
 #define CRTPORT 0x3d4
-static ushort *crt = (ushort*)0xb8000;  // CGA memory
+static ushort *crt = (ushort*)P2V(0xb8000);  // CGA memory
 
 static void
 cgaputc(int c)
diff --git a/defs.h b/defs.h
index bbe4ae4..54ea14d 100644
--- a/defs.h
+++ b/defs.h
@@ -62,6 +62,7 @@ extern uchar    ioapicid;
 void            ioapicinit(void);
 
 // kalloc.c
+char*           pgalloc(void);
 char*           kalloc(void);
 void            kfree(char*);
 void            kinit(void);
@@ -160,6 +161,7 @@ void            uartintr(void);
 void            uartputc(int);
 
 // vm.c
+void            pginit(char* (*alloc)());
 void            seginit(void);
 void            kvmalloc(void);
 void            vmenable(void);
diff --git a/exec.c b/exec.c
index 05f80f8..9186cfb 100644
--- a/exec.c
+++ b/exec.c
@@ -1,5 +1,6 @@
 #include "types.h"
 #include "param.h"
+#include "memlayout.h"
 #include "mmu.h"
 #include "proc.h"
 #include "defs.h"
diff --git a/ide.c b/ide.c
index 53293a7..054bb76 100644
--- a/ide.c
+++ b/ide.c
@@ -3,6 +3,7 @@
 #include "types.h"
 #include "defs.h"
 #include "param.h"
+#include "memlayout.h"
 #include "mmu.h"
 #include "proc.h"
 #include "x86.h"
diff --git a/kalloc.c b/kalloc.c
index bf1616a..83ed7e8 100644
--- a/kalloc.c
+++ b/kalloc.c
@@ -5,6 +5,7 @@
 #include "types.h"
 #include "defs.h"
 #include "param.h"
+#include "memlayout.h"
 #include "mmu.h"
 #include "spinlock.h"
 
@@ -18,6 +19,20 @@ struct {
 } kmem;
 
 extern char end[]; // first address after kernel loaded from ELF file
+char *newend;
+
+// simple page allocator to get off the ground during boot
+char *
+pgalloc(void)
+{
+  if (newend == 0)
+    newend = end;
+
+  void *p = (void*)PGROUNDUP((uint)newend);
+  memset(p, 0, PGSIZE);
+  newend = newend + PGSIZE;
+  return p;
+}
 
 // Initialize free list of physical pages.
 void
@@ -26,8 +41,8 @@ kinit(void)
   char *p;
 
   initlock(&kmem.lock, "kmem");
-  p = (char*)PGROUNDUP((uint)end);
-  for(; p + PGSIZE <= (char*)PHYSTOP; p += PGSIZE)
+  p = (char*)PGROUNDUP((uint)newend);
+  for(; p + PGSIZE <= (char*)p2v(PHYSTOP); p += PGSIZE)
     kfree(p);
 }
 
@@ -41,7 +56,7 @@ kfree(char *v)
 {
   struct run *r;
 
-  if((uint)v % PGSIZE || v < end || (uint)v >= PHYSTOP) 
+  if((uint)v % PGSIZE || v < end || v2p(v) >= PHYSTOP) 
     panic("kfree");
 
   // Fill with junk to catch dangling refs.
@@ -67,6 +82,7 @@ kalloc(void)
   if(r)
     kmem.freelist = r->next;
   release(&kmem.lock);
+  cprintf("kalloc: 0x%x\n", r);
   return (char*)r;
 }
 
diff --git a/main.c b/main.c
index a27c4ff..d596cec 100644
--- a/main.c
+++ b/main.c
@@ -1,6 +1,7 @@
 #include "types.h"
 #include "defs.h"
 #include "param.h"
+#include "memlayout.h"
 #include "mmu.h"
 #include "proc.h"
 #include "x86.h"
@@ -9,6 +10,8 @@ static void bootothers(void);
 static void mpmain(void);
 void jmpkstack(void)  __attribute__((noreturn));
 void mainc(void);
+static volatile int newpgdir;
+
 
 // Bootstrap processor starts running C code here.
 // Allocate a real stack and switch to it, first
@@ -16,6 +19,7 @@ void mainc(void);
 int
 main(void)
 {
+  pginit(pgalloc);
   mpinit();        // collect info about this machine
   lapicinit(mpbcpu());
   seginit();       // set up segments
@@ -46,7 +50,6 @@ mainc(void)
   ioapicinit();    // another interrupt controller
   consoleinit();   // I/O devices & their interrupts
   uartinit();      // serial port
-  kvmalloc();      // initialize the kernel page table
   pinit();         // process table
   tvinit();        // trap vectors
   binit();         // buffer cache
@@ -57,25 +60,35 @@ mainc(void)
     timerinit();   // uniprocessor timer
   userinit();      // first user process
   bootothers();    // start other processors
-
+  kvmalloc();      // new kernel page table wo. bottom mapped
+  newpgdir = 1;
   // Finish setting up this processor in mpmain.
   mpmain();
 }
 
+// Common CPU setup code.
+// Bootstrap CPU comes here from mainc().
+// Other CPUs jump here from bootother.S.
+static void
+mpboot(void)
+{
+  vmenable();        // turn on paging
+  seginit();
+  lapicinit(cpunum());
+  mpmain();
+}
+
 // Common CPU setup code.
 // Bootstrap CPU comes here from mainc().
 // Other CPUs jump here from bootother.S.
 static void
 mpmain(void)
 {
-  if(cpunum() != mpbcpu()){
-    seginit();
-    lapicinit(cpunum());
-  }
-  vmenable();        // turn on paging
   cprintf("cpu%d: starting\n", cpu->id);
   idtinit();       // load idt register
   xchg(&cpu->booted, 1); // tell bootothers() we're up
+  while (!newpgdir) ;  // wait until we have new page dir
+  switchkvm();     // switch to new page dir
   scheduler();     // start running processes
 }
 
@@ -91,7 +104,7 @@ bootothers(void)
   // Write bootstrap code to unused memory at 0x7000.
   // The linker has placed the image of bootother.S in
   // _binary_bootother_start.
-  code = (uchar*)0x7000;
+  code = p2v(0x7000);
   memmove(code, _binary_bootother_start, (uint)_binary_bootother_size);
 
   for(c = cpus; c < cpus+ncpu; c++){
@@ -103,9 +116,9 @@ bootothers(void)
     // its first instruction.
     stack = kalloc();
     *(void**)(code-4) = stack + KSTACKSIZE;
-    *(void**)(code-8) = mpmain;
+    *(void**)(code-8) = mpboot;
 
-    lapicstartap(c->id, (uint)code);
+    lapicstartap(c->id, v2p(code));
 
     // Wait for cpu to finish mpmain()
     while(c->booted == 0)
diff --git a/mmu.h b/mmu.h
index 2d88a52..86879d2 100644
--- a/mmu.h
+++ b/mmu.h
@@ -106,18 +106,10 @@ struct segdesc {
 // construct linear address from indexes and offset
 #define PGADDR(d, t, o)	((uint)((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))
 
-// turn a kernel linear address into a physical address.
-// all of the kernel data structures have linear and
-// physical addresses that are equal.
-#define PADDR(a)       ((uint)(a))
-
 // Page directory and page table constants.
 #define NPDENTRIES	1024		// page directory entries per page directory
 #define NPTENTRIES	1024		// page table entries per page table
 
-#define PGSIZE		4096		// bytes mapped by a page
-#define PGSHIFT		12		// log2(PGSIZE)
-
 #define PTXSHIFT	12		// offset of PTX in a linear address
 #define PDXSHIFT	22		// offset of PDX in a linear address
 
diff --git a/mp.c b/mp.c
index 5ab348e..b1e2813 100644
--- a/mp.c
+++ b/mp.c
@@ -5,6 +5,7 @@
 #include "types.h"
 #include "defs.h"
 #include "param.h"
+#include "memlayout.h"
 #include "mp.h"
 #include "x86.h"
 #include "mmu.h"
@@ -39,6 +40,7 @@ mpsearch1(uchar *addr, int len)
 {
   uchar *e, *p;
 
+  addr = p2v((uint) addr);
   e = addr+len;
   for(p = addr; p < e; p += sizeof(struct mp))
     if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0)
@@ -83,7 +85,7 @@ mpconfig(struct mp **pmp)
 
   if((mp = mpsearch()) == 0 || mp->physaddr == 0)
     return 0;
-  conf = (struct mpconf*)mp->physaddr;
+  conf = (struct mpconf*) p2v((uint) mp->physaddr);
   if(memcmp(conf, "PCMP", 4) != 0)
     return 0;
   if(conf->version != 1 && conf->version != 4)
diff --git a/multiboot.S b/multiboot.S
index 2579b6d..84ab638 100644
--- a/multiboot.S
+++ b/multiboot.S
@@ -15,6 +15,9 @@
 # }
 
 #include "asm.h"
+#include "memlayout.h"
+
+#define RELOC(x) ((x) - KERNBASE)    // same as V2P, but without casts
 
 #define STACK 4096
 
@@ -42,7 +45,7 @@ multiboot_header:
 # boot loader - bootasm.S - sets up.
 .globl multiboot_entry
 multiboot_entry:
-  lgdt gdtdesc
+  lgdt RELOC(gdtdesc)
   ljmp $(SEG_KCODE<<3), $mbstart32
 
 mbstart32:
@@ -65,11 +68,11 @@ spin:
 .p2align 2                                # force 4 byte alignment
 gdt:
   SEG_NULLASM                             # null seg
-  SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)   # code seg
-  SEG_ASM(STA_W, 0x0, 0xffffffff)         # data seg
+  SEG_ASM(STA_X|STA_R, -KERNBASE, 0xffffffff)   # code seg
+  SEG_ASM(STA_W, -KERNBASE, 0xffffffff)         # data seg
 
 gdtdesc:
   .word   (gdtdesc - gdt - 1)             # sizeof(gdt) - 1
-  .long   gdt                             # address gdt
+  .long   RELOC(gdt)                      # address gdt
 
 .comm stack, STACK
diff --git a/param.h b/param.h
index ab1b9fe..03c05f9 100644
--- a/param.h
+++ b/param.h
@@ -7,8 +7,6 @@
 #define NINODE       50  // maximum number of active i-nodes
 #define NDEV         10  // maximum major device number
 #define ROOTDEV       1  // device number of file system root disk
-#define USERTOP  0xA0000 // end of user address space
-#define PHYSTOP  0x1000000 // use phys mem up to here as free pool
 #define MAXARG       32  // max exec arguments
 #define LOGSIZE      10  // size of log
 
diff --git a/proc.c b/proc.c
index eb334d0..626d03e 100644
--- a/proc.c
+++ b/proc.c
@@ -1,6 +1,7 @@
 #include "types.h"
 #include "defs.h"
 #include "param.h"
+#include "memlayout.h"
 #include "mmu.h"
 #include "x86.h"
 #include "proc.h"
diff --git a/spinlock.c b/spinlock.c
index e668598..a16621c 100644
--- a/spinlock.c
+++ b/spinlock.c
@@ -4,6 +4,7 @@
 #include "defs.h"
 #include "param.h"
 #include "x86.h"
+#include "memlayout.h"
 #include "mmu.h"
 #include "proc.h"
 #include "spinlock.h"
@@ -71,7 +72,7 @@ getcallerpcs(void *v, uint pcs[])
   
   ebp = (uint*)v - 2;
   for(i = 0; i < 10; i++){
-    if(ebp == 0 || ebp < (uint*)0x100000 || ebp == (uint*)0xffffffff)
+    if(ebp == 0 || ebp < (uint*)KERNBASE || ebp == (uint*)0xffffffff)
       break;
     pcs[i] = ebp[1];     // saved %eip
     ebp = (uint*)ebp[0]; // saved %ebp
diff --git a/syscall.c b/syscall.c
index ce50a59..b848716 100644
--- a/syscall.c
+++ b/syscall.c
@@ -1,6 +1,7 @@
 #include "types.h"
 #include "defs.h"
 #include "param.h"
+#include "memlayout.h"
 #include "mmu.h"
 #include "proc.h"
 #include "x86.h"
diff --git a/sysproc.c b/sysproc.c
index efaa372..2a92c48 100644
--- a/sysproc.c
+++ b/sysproc.c
@@ -2,6 +2,7 @@
 #include "x86.h"
 #include "defs.h"
 #include "param.h"
+#include "memlayout.h"
 #include "mmu.h"
 #include "proc.h"
 
diff --git a/trap.c b/trap.c
index 6651f8e..3f80145 100644
--- a/trap.c
+++ b/trap.c
@@ -1,6 +1,7 @@
 #include "types.h"
 #include "defs.h"
 #include "param.h"
+#include "memlayout.h"
 #include "mmu.h"
 #include "proc.h"
 #include "x86.h"
diff --git a/vm.c b/vm.c
index 1fe64d2..b14a863 100644
--- a/vm.c
+++ b/vm.c
@@ -2,20 +2,71 @@
 #include "types.h"
 #include "defs.h"
 #include "x86.h"
+#include "memlayout.h"
 #include "mmu.h"
 #include "proc.h"
 #include "elf.h"
 
 extern char data[];  // defined in data.S
-
 static pde_t *kpgdir;  // for use in scheduler()
+struct segdesc gdt[NSEGS];
 
-// Allocate one page table for the machine for the kernel address
-// space for scheduler processes.
-void
-kvmalloc(void)
+
+// page map for during boot
+// XXX build a static page table in assembly
+static void
+pgmap(void *va, void *last, uint pa)
 {
-  kpgdir = setupkvm();
+  pde_t *pde;
+  pte_t *pgtab;
+  pte_t *pte;
+
+  for(;;){
+    pde = &kpgdir[PDX(va)];
+    pde_t pdev = *pde;
+    if (pdev == 0) {
+      pgtab = (pte_t *) pgalloc();
+      *pde = v2p(pgtab) | PTE_P | PTE_W;
+    } else {
+      pgtab = (pte_t*)p2v(PTE_ADDR(pdev));
+    }
+    pte = &pgtab[PTX(va)];
+    *pte = pa | PTE_W | PTE_P;
+    if(va == last)
+      break;
+    va += PGSIZE;
+    pa += PGSIZE;
+  }
+}
+
+// set up a page table to get off the ground
+void
+pginit(char* (*alloc)(void))
+{
+  uint cr0;
+
+  kpgdir = (pde_t *) alloc();
+  pgmap((void *) 0, (void *) PHYSTOP, 0);    // map pa 0 at va 0
+  pgmap((void *) KERNBASE, (void *) (KERNBASE+PHYSTOP), 0);   // map pa 0 at va KERNBASE
+  pgmap((void*)0xFE000000, 0, 0xFE000000);
+
+  switchkvm(); // load kpgdir into cr3
+
+  cr0 = rcr0();
+  cr0 |= CR0_PG;
+  lcr0(cr0);   // paging on
+
+  // new gdt
+  gdt[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, 0);
+  gdt[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
+  lgdt((void *)v2p(gdt), sizeof(gdt));
+  loadgs(SEG_KDATA << 3);
+  loadfs(SEG_KDATA << 3);
+  loades(SEG_KDATA << 3);
+  loadds(SEG_KDATA << 3);
+  loadss(SEG_KDATA << 3);
+
+  __asm volatile("ljmp %0,$1f\n 1:\n" :: "i" (SEG_KCODE << 3));  // reload cs
 }
 
 // Set up CPU's kernel segment descriptors.
@@ -57,7 +108,7 @@ walkpgdir(pde_t *pgdir, const void *va, int create)
 
   pde = &pgdir[PDX(va)];
   if(*pde & PTE_P){
-    pgtab = (pte_t*)PTE_ADDR(*pde);
+    pgtab = (pte_t*)p2v(PTE_ADDR(*pde));
   } else {
     if(!create || (pgtab = (pte_t*)kalloc()) == 0)
       return 0;
@@ -66,7 +117,7 @@ walkpgdir(pde_t *pgdir, const void *va, int create)
     // The permissions here are overly generous, but they can
     // be further restricted by the permissions in the page table 
     // entries, if necessary.
-    *pde = PADDR(pgtab) | PTE_P | PTE_W | PTE_U;
+    *pde = v2p(pgtab) | PTE_P | PTE_W | PTE_U;
   }
   return &pgtab[PTX(va)];
 }
@@ -105,29 +156,30 @@ mappages(pde_t *pgdir, void *la, uint size, uint pa, int perm)
 // page protection bits prevent it from using anything other
 // than its memory.
 // 
+//
 // setupkvm() and exec() set up every page table like this:
-//   0..640K          : user memory (text, data, stack, heap)
-//   640K..1M         : mapped direct (for IO space)
-//   1M..end          : mapped direct (for the kernel's text and data)
-//   end..PHYSTOP     : mapped direct (kernel heap and user pages)
+//   0..KERNBASE      : user memory (text, data, stack, heap), mapped to some phys mem
+//   KERNBASE+640K..KERNBASE+1M: mapped to 640K..1M
+//   KERNBASE+1M..KERNBASE+end : mapped to 1M..end
+//   KERNBASE+end..KERBASE+PHYSTOP     : mapped to end..PHYSTOP (free memory)
 //   0xfe000000..0    : mapped direct (devices such as ioapic)
 //
 // The kernel allocates memory for its heap and for user memory
 // between kernend and the end of physical memory (PHYSTOP).
 // The virtual address space of each user program includes the kernel
-// (which is inaccessible in user mode).  The user program addresses
-// range from 0 till 640KB (USERTOP), which where the I/O hole starts
-// (both in physical memory and in the kernel's virtual address
-// space).
+// (which is inaccessible in user mode).  The user program sits in
+// the bottom of the address space, and the kernel at the top at KERNBASE.
 static struct kmap {
-  void *p;
-  void *e;
+  void *l;
+  uint p;
+  uint e;
   int perm;
 } kmap[] = {
-  {(void*)USERTOP,    (void*)0x100000, PTE_W},  // I/O space
-  {(void*)0x100000,   data,            0    },  // kernel text, rodata
-  {data,              (void*)PHYSTOP,  PTE_W},  // kernel data, memory
-  {(void*)0xFE000000, 0,               PTE_W},  // device mappings
+  { (void *)IOSPACEB, IOSPACEB, IOSPACEE, PTE_W},  // I/O space
+  { P2V(IOSPACEB), IOSPACEB, IOSPACEE, PTE_W},  // I/O space
+  { (void *)KERNLINK, V2P(KERNLINK), V2P(data),  0},  // kernel text, rodata
+  { data, V2P(data), PHYSTOP,  PTE_W},  // kernel data, memory
+  { (void*)0xFE000000, 0xFE000000, 0, PTE_W},  // device mappings
 };
 
 // Set up kernel part of a page table.
@@ -142,12 +194,21 @@ setupkvm(void)
   memset(pgdir, 0, PGSIZE);
   k = kmap;
   for(k = kmap; k < &kmap[NELEM(kmap)]; k++)
-    if(mappages(pgdir, k->p, k->e - k->p, (uint)k->p, k->perm) < 0)
+    if(mappages(pgdir, k->l, k->e - k->p, (uint)k->p, k->perm) < 0)
       return 0;
 
   return pgdir;
 }
 
+// Allocate one page table for the machine for the kernel address
+// space for scheduler processes.
+void
+kvmalloc(void)
+{
+  kpgdir = setupkvm();
+  switchkvm();
+}
+
 // Turn on paging.
 void
 vmenable(void)
@@ -158,6 +219,16 @@ vmenable(void)
   cr0 = rcr0();
   cr0 |= CR0_PG;
   lcr0(cr0);
+
+ struct cpu *c = &cpus[0];
+  lgdt((void *)v2p((void *)(c->gdt)), sizeof(c->gdt));
+  loadgs(SEG_KCPU << 3);
+  loadfs(SEG_KDATA << 3);
+  loades(SEG_KDATA << 3);
+  loadds(SEG_KDATA << 3);
+  loadss(SEG_KDATA << 3);
+
+  __asm volatile("ljmp %0,$1f\n 1:\n" :: "i" (SEG_KCODE << 3));  // reload cs
 }
 
 // Switch h/w page table register to the kernel-only page table,
@@ -165,7 +236,7 @@ vmenable(void)
 void
 switchkvm(void)
 {
-  lcr3(PADDR(kpgdir));   // switch to the kernel page table
+  lcr3(v2p(kpgdir));   // switch to the kernel page table
 }
 
 // Switch TSS and h/w page table to correspond to process p.
@@ -180,7 +251,7 @@ switchuvm(struct proc *p)
   ltr(SEG_TSS << 3);
   if(p->pgdir == 0)
     panic("switchuvm: no pgdir");
-  lcr3(PADDR(p->pgdir));  // switch to new address space
+  lcr3(v2p(p->pgdir));  // switch to new address space
   popcli();
 }
 
@@ -195,7 +266,7 @@ inituvm(pde_t *pgdir, char *init, uint sz)
     panic("inituvm: more than a page");
   mem = kalloc();
   memset(mem, 0, PGSIZE);
-  mappages(pgdir, 0, PGSIZE, PADDR(mem), PTE_W|PTE_U);
+  mappages(pgdir, 0, PGSIZE, v2p(mem), PTE_W|PTE_U);
   memmove(mem, init, sz);
 }
 
@@ -245,7 +316,7 @@ allocuvm(pde_t *pgdir, uint oldsz, uint newsz)
       return 0;
     }
     memset(mem, 0, PGSIZE);
-    mappages(pgdir, (char*)a, PGSIZE, PADDR(mem), PTE_W|PTE_U);
+    mappages(pgdir, (char*)a, PGSIZE, v2p(mem), PTE_W|PTE_U);
   }
   return newsz;
 }
@@ -289,7 +360,7 @@ freevm(pde_t *pgdir)
   deallocuvm(pgdir, USERTOP, 0);
   for(i = 0; i < NPDENTRIES; i++){
     if(pgdir[i] & PTE_P)
-      kfree((char*)PTE_ADDR(pgdir[i]));
+      kfree(p2v(PTE_ADDR(pgdir[i])));
   }
   kfree((char*)pgdir);
 }
@@ -315,7 +386,7 @@ copyuvm(pde_t *pgdir, uint sz)
     if((mem = kalloc()) == 0)
       goto bad;
     memmove(mem, (char*)pa, PGSIZE);
-    if(mappages(d, (void*)i, PGSIZE, PADDR(mem), PTE_W|PTE_U) < 0)
+    if(mappages(d, (void*)i, PGSIZE, v2p(mem), PTE_W|PTE_U) < 0)
       goto bad;
   }
   return d;
diff --git a/x86.h b/x86.h
index 5a59cc2..828d5bc 100644
--- a/x86.h
+++ b/x86.h
@@ -96,6 +96,30 @@ loadgs(ushort v)
   asm volatile("movw %0, %%gs" : : "r" (v));
 }
 
+static inline void
+loadfs(ushort v)
+{
+  __asm volatile("movw %0, %%fs" : : "r" (v));
+}
+
+static inline void
+loades(ushort v)
+{
+  __asm volatile("movw %0, %%es" : : "r" (v));
+}
+
+static inline void
+loadds(ushort v)
+{
+  __asm volatile("movw %0, %%ds" : : "r" (v));
+}
+
+static inline void
+loadss(ushort v)
+{
+  __asm volatile("movw %0, %%ss" : : "r" (v));
+}
+
 static inline uint
 rebp(void)
 {