diff --git a/Makefile b/Makefile
index 91909d8..a0c901d 100644
--- a/Makefile
+++ b/Makefile
@@ -25,12 +25,13 @@ OBJS = \
 	trap.o\
 	uart.o\
 	vectors.o\
+	vm.o\
 
 # Cross-compiling (e.g., on Mac OS X)
-#TOOLPREFIX = i386-jos-elf-
+TOOLPREFIX = i386-jos-elf-
 
 # Using native tools (e.g., on X86 Linux)
-TOOLPREFIX = 
+#TOOLPREFIX = 
 
 CC = $(TOOLPREFIX)gcc
 AS = $(TOOLPREFIX)gas
diff --git a/bootasm.S b/bootasm.S
index 059cc1b..f6af255 100644
--- a/bootasm.S
+++ b/bootasm.S
@@ -88,5 +88,5 @@ gdt:
   SEG_ASM(STA_W, 0x0, 0xffffffff)         # data seg
 
 gdtdesc:
-  .word   (gdtdesc - gdt - 1)                            # sizeof(gdt) - 1
+  .word   (gdtdesc - gdt - 1)             # sizeof(gdt) - 1
   .long   gdt                             # address gdt
diff --git a/defs.h b/defs.h
index bcfab55..71dbb14 100644
--- a/defs.h
+++ b/defs.h
@@ -60,9 +60,10 @@ extern uchar    ioapicid;
 void            ioapicinit(void);
 
 // kalloc.c
+extern int      nfreemem;
 char*           kalloc(int);
 void            kfree(char*, int);
-void            kinit(void);
+void            kinit(char*,uint);
 
 // kbd.c
 void            kbdintr(void);
@@ -101,8 +102,6 @@ int             kill(int);
 void            pinit(void);
 void            procdump(void);
 void            scheduler(void) __attribute__((noreturn));
-void            ksegment(void);
-void            usegment(void);
 void            sleep(void*, struct spinlock*);
 void            userinit(void);
 int             wait(void);
@@ -111,6 +110,7 @@ void            yield(void);
 
 // swtch.S
 void            swtch(struct context**, struct context*);
+void            jstack(uint);
 
 // spinlock.c
 void            acquire(struct spinlock*);
@@ -152,6 +152,22 @@ void            uartinit(void);
 void            uartintr(void);
 void            uartputc(int);
 
+// vm.c
+#define PGROUNDUP(sz)  ((sz+PGSIZE-1) & ~(PGSIZE-1))
+void            pminit(void);
+void            swkstack(void);
+void            vminit(void);
+void            printpgdir(uint*);
+uint*           setupkvm(void);    // XXX need pde_t*
+char*           uva2ka(uint*, char*);
+int             allocuvm(uint*, char*, uint);  // XXX need pde_t*
+void            freevm(uint*);
+void            inituvm(uint*, char*, char*, uint);
+int             loaduvm(uint*, char*, struct inode *ip, uint, uint);
+uint*           copyuvm(uint*,uint);
+void            ksegment(void);
+void            loadvm(struct proc*);
+
 // number of elements in fixed-size array
 #define NELEM(x) (sizeof(x)/sizeof((x)[0]))
 
diff --git a/exec.c b/exec.c
index 17fab01..65de312 100644
--- a/exec.c
+++ b/exec.c
@@ -11,12 +11,13 @@ exec(char *path, char **argv)
 {
   char *mem, *s, *last;
   int i, argc, arglen, len, off;
-  uint sz, sp, argp;
+  uint sz, sp, spoffset, argp;
   struct elfhdr elf;
   struct inode *ip;
   struct proghdr ph;
+  pde_t *pgdir, *oldpgdir;
 
-  mem = 0;
+  pgdir = 0;
   sz = 0;
 
   if((ip = namei(path)) == 0)
@@ -29,37 +30,8 @@ exec(char *path, char **argv)
   if(elf.magic != ELF_MAGIC)
     goto bad;
 
-  // Compute memory size of new process.
-  // Program segments.
-  for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
-    if(readi(ip, (char*)&ph, off, sizeof(ph)) != sizeof(ph))
-      goto bad;
-    if(ph.type != ELF_PROG_LOAD)
-      continue;
-    if(ph.memsz < ph.filesz)
-      goto bad;
-    sz += ph.memsz;
-  }
-  
-  // Arguments.
-  arglen = 0;
-  for(argc=0; argv[argc]; argc++)
-    arglen += strlen(argv[argc]) + 1;
-  arglen = (arglen+3) & ~3;
-  sz += arglen;
-  sz += 4*(argc+1);  // argv data
-  sz += 4;  // argv
-  sz += 4;  // argc
-
-  // Stack.
-  sz += PAGE;
-  
-  // Allocate program memory.
-  sz = (sz+PAGE-1) & ~(PAGE-1);
-  mem = kalloc(sz);
-  if(mem == 0)
+  if (!(pgdir = setupkvm()))
     goto bad;
-  memset(mem, 0, sz);
 
   // Load program into memory.
   for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
@@ -67,37 +39,48 @@ exec(char *path, char **argv)
       goto bad;
     if(ph.type != ELF_PROG_LOAD)
       continue;
-    if(ph.va + ph.memsz < ph.va || ph.va + ph.memsz > sz)
-      goto bad;
     if(ph.memsz < ph.filesz)
       goto bad;
-    if(readi(ip, mem + ph.va, ph.offset, ph.filesz) != ph.filesz)
+    if (!allocuvm(pgdir, (char *)ph.va, ph.memsz))
+      goto bad;
+    sz += PGROUNDUP(ph.memsz);
+    if (!loaduvm(pgdir, (char *)ph.va, ip, ph.offset, ph.filesz))
       goto bad;
-    memset(mem + ph.va + ph.filesz, 0, ph.memsz - ph.filesz);
   }
   iunlockput(ip);
-  
-  // Initialize stack.
+
+  // Allocate and initialize stack at sz
+  if (!allocuvm(pgdir, (char *)sz, PGSIZE))
+    goto bad;
+  mem = uva2ka(pgdir, (char *)sz);
+  spoffset = sz;
+  sz += PGSIZE;
+
+  arglen = 0;
+  for(argc=0; argv[argc]; argc++)
+    arglen += strlen(argv[argc]) + 1;
+  arglen = (arglen+3) & ~3;
+
   sp = sz;
   argp = sz - arglen - 4*(argc+1);
 
   // Copy argv strings and pointers to stack.
-  *(uint*)(mem+argp + 4*argc) = 0;  // argv[argc]
+  *(uint*)(mem+argp-spoffset + 4*argc) = 0;  // argv[argc]
   for(i=argc-1; i>=0; i--){
     len = strlen(argv[i]) + 1;
     sp -= len;
-    memmove(mem+sp, argv[i], len);
-    *(uint*)(mem+argp + 4*i) = sp;  // argv[i]
+    memmove(mem+sp-spoffset, argv[i], len);
+    *(uint*)(mem+argp-spoffset + 4*i) = sp;  // argv[i]
   }
 
   // Stack frame for main(argc, argv), below arguments.
   sp = argp;
   sp -= 4;
-  *(uint*)(mem+sp) = argp;
+  *(uint*)(mem+sp-spoffset) = argp;
   sp -= 4;
-  *(uint*)(mem+sp) = argc;
+  *(uint*)(mem+sp-spoffset) = argc;
   sp -= 4;
-  *(uint*)(mem+sp) = 0xffffffff;   // fake return pc
+  *(uint*)(mem+sp-spoffset) = 0xffffffff;   // fake return pc
 
   // Save program name for debugging.
   for(last=s=path; *s; s++)
@@ -105,18 +88,25 @@ exec(char *path, char **argv)
       last = s+1;
   safestrcpy(proc->name, last, sizeof(proc->name));
 
-  // Commit to the new image.
-  kfree(proc->mem, proc->sz);
-  proc->mem = mem;
+  // Commit to the user image.
+  oldpgdir = proc->pgdir;
+  proc->pgdir = pgdir;
   proc->sz = sz;
   proc->tf->eip = elf.entry;  // main
   proc->tf->esp = sp;
-  usegment();
+
+  // printstack();
+
+  loadvm(proc);
+
+  freevm(oldpgdir);
+
+  // printstack(); 
+
   return 0;
 
  bad:
-  if(mem)
-    kfree(mem, sz);
+  freevm(pgdir);
   iunlockput(ip);
   return -1;
 }
diff --git a/file.c b/file.c
index 9b29d08..e10b824 100644
--- a/file.c
+++ b/file.c
@@ -116,7 +116,6 @@ filewrite(struct file *f, char *addr, int n)
     return pipewrite(f->pipe, addr, n);
   if(f->type == FD_INODE){
     ilock(f->ip);
-    cprintf("filewrite: %d\n", n);
     if((r = writei(f->ip, addr, f->off, n)) > 0)
       f->off += r;
     iunlock(f->ip);
diff --git a/forktest.c b/forktest.c
index 90cc38c..bb286e6 100644
--- a/forktest.c
+++ b/forktest.c
@@ -5,6 +5,8 @@
 #include "stat.h"
 #include "user.h"
 
+#define N  1000
+
 void
 printf(int fd, char *s, ...)
 {
@@ -18,7 +20,7 @@ forktest(void)
 
   printf(1, "fork test\n");
 
-  for(n=0; n<1000; n++){
+  for(n=0; n<N; n++){
     pid = fork();
     if(pid < 0)
       break;
@@ -26,8 +28,8 @@ forktest(void)
       exit();
   }
   
-  if(n == 1000){
-    printf(1, "fork claimed to work 1000 times!\n");
+  if(n == N){
+    printf(1, "fork claimed to work N times!\n", N);
     exit();
   }
   
diff --git a/ide.c b/ide.c
index c0eb80a..7b12aa0 100644
--- a/ide.c
+++ b/ide.c
@@ -147,8 +147,9 @@ iderw(struct buf *b)
   
   // Wait for request to finish.
   // Assuming will not sleep too long: ignore proc->killed.
-  while((b->flags & (B_VALID|B_DIRTY)) != B_VALID)
+  while((b->flags & (B_VALID|B_DIRTY)) != B_VALID) {
     sleep(b, &idelock);
+  }
 
   release(&idelock);
 }
diff --git a/kalloc.c b/kalloc.c
index 2730d57..200ea8d 100644
--- a/kalloc.c
+++ b/kalloc.c
@@ -8,6 +8,7 @@
 #include "types.h"
 #include "defs.h"
 #include "param.h"
+#include "mmu.h"
 #include "spinlock.h"
 
 struct run {
@@ -20,21 +21,28 @@ struct {
   struct run *freelist;
 } kmem;
 
+int nfreemem;
+
+static void
+printfreelist(void)
+{
+  struct run *r, **rp;
+  cprintf("freelist:\n");
+  for(rp=&kmem.freelist; (r=*rp) != 0; rp=&r->next){
+    cprintf("0x%x %d=0x%x\n", r, r->len, r->len);
+  }
+}
+
 // Initialize free list of physical pages.
 // This code cheats by just considering one megabyte of
 // pages after end.  Real systems would determine the
 // amount of memory available in the system and use it all.
 void
-kinit(void)
+kinit(char *p, uint len)
 {
-  extern char end[];
-  uint len;
-  char *p;
-
   initlock(&kmem.lock, "kmem");
-  p = (char*)(((uint)end + PAGE) & ~(PAGE-1));
-  len = 256*PAGE; // assume computer has 256 pages of RAM, 1 MB
-  cprintf("mem = %d\n", len);
+  cprintf("end 0x%x free = %d(0x%x)\n", p, len);
+  nfreemem = 0;
   kfree(p, len);
 }
 
@@ -47,19 +55,23 @@ kfree(char *v, int len)
 {
   struct run *r, *rend, **rp, *p, *pend;
 
-  if(len <= 0 || len % PAGE)
+  if(len <= 0 || len % PGSIZE)
     panic("kfree");
 
   // Fill with junk to catch dangling refs.
   memset(v, 1, len);
 
   acquire(&kmem.lock);
+  nfreemem += len;
   p = (struct run*)v;
   pend = (struct run*)(v + len);
   for(rp=&kmem.freelist; (r=*rp) != 0 && r <= pend; rp=&r->next){
     rend = (struct run*)((char*)r + r->len);
-    if(r <= p && p < rend)
+    if(r <= p && p < rend) {
+      cprintf("freeing a free page: r = 0x%x p = 0x%x rend = 0x%x\n", 
+	      r, p, rend);
       panic("freeing free page");
+    }
     if(rend == p){  // r before p: expand r to include p
       r->len += len;
       if(r->next && r->next == pend){  // r now next to r->next?
@@ -93,7 +105,7 @@ kalloc(int n)
   char *p;
   struct run *r, **rp;
 
-  if(n % PAGE || n <= 0)
+  if(n % PGSIZE || n <= 0)
     panic("kalloc");
 
   acquire(&kmem.lock);
@@ -103,6 +115,7 @@ kalloc(int n)
       p = (char*)r + r->len;
       if(r->len == 0)
         *rp = r->next;
+      nfreemem -= n;
       release(&kmem.lock);
       return p;
     }
@@ -112,3 +125,4 @@ kalloc(int n)
   cprintf("kalloc: out of memory\n");
   return 0;
 }
+
diff --git a/lapic.c b/lapic.c
index d2407b1..4fe4ace 100644
--- a/lapic.c
+++ b/lapic.c
@@ -48,6 +48,7 @@ lapicw(int index, int value)
 void
 lapicinit(int c)
 {
+  cprintf("lapicinit: %d 0x%x\n", c, lapic);
   if(!lapic) 
     return;
 
diff --git a/main.c b/main.c
index 60cd1b3..319aad9 100644
--- a/main.c
+++ b/main.c
@@ -19,20 +19,27 @@ main(void)
   ioapicinit();    // another interrupt controller
   consoleinit();   // I/O devices & their interrupts
   uartinit();      // serial port
-cprintf("cpus %p cpu %p\n", cpus, cpu);
-  cprintf("\ncpu%d: starting xv6\n\n", cpu->id);
+  pminit();        // physical memory for kernel
+  jkstack();       // Jump to mainc on a proper-allocated kernel stack 
+}
 
-  kinit();         // physical memory allocator
+void
+mainc(void)
+{
+  cprintf("cpus %p cpu %p\n", cpus, cpu);
+  cprintf("\ncpu%d: starting xv6\n\n", cpu->id);
+  vminit();        // virtual memory
   pinit();         // process table
   tvinit();        // trap vectors
   binit();         // buffer cache
   fileinit();      // file table
   iinit();         // inode cache
   ideinit();       // disk
+  cprintf("ismp: %d\n", ismp);
   if(!ismp)
     timerinit();   // uniprocessor timer
   userinit();      // first user process
-  bootothers();    // start other processors
+  // bootothers();    // start other processors  XXX fix where to boot from
 
   // Finish setting up this processor in mpmain.
   mpmain();
@@ -43,9 +50,12 @@ cprintf("cpus %p cpu %p\n", cpus, cpu);
 static void
 mpmain(void)
 {
-  if(cpunum() != mpbcpu())
+  if(cpunum() != mpbcpu()) {
+    ksegment();
+    cprintf("other cpu\n");
+    vminit();
     lapicinit(cpunum());
-  ksegment();
+  }
   cprintf("cpu%d: mpmain\n", cpu->id);
   idtinit();
   xchg(&cpu->booted, 1);
@@ -74,11 +84,15 @@ bootothers(void)
     stack = kalloc(KSTACKSIZE);
     *(void**)(code-4) = stack + KSTACKSIZE;
     *(void**)(code-8) = mpmain;
+    cprintf("lapicstartap\n");
     lapicstartap(c->id, (uint)code);
+    cprintf("lapicstartap done\n");
 
     // Wait for cpu to get through bootstrap.
     while(c->booted == 0)
       ;
+
+    cprintf("lapicstartap booted\n");
   }
 }
 
diff --git a/mmu.h b/mmu.h
index 364d05b..76d5ce7 100644
--- a/mmu.h
+++ b/mmu.h
@@ -62,6 +62,8 @@ struct segdesc {
 #define STA_R       0x2     // Readable (executable segments)
 #define STA_A       0x1     // Accessed
 
+// 
+
 // System segment type bits
 #define STS_T16A    0x1     // Available 16-bit TSS
 #define STS_LDT     0x2     // Local Descriptor Table
@@ -76,6 +78,92 @@ struct segdesc {
 #define STS_IG32    0xE     // 32-bit Interrupt Gate
 #define STS_TG32    0xF     // 32-bit Trap Gate
 
+
+// A linear address 'la' has a three-part structure as follows:
+//
+// +--------10------+-------10-------+---------12----------+
+// | Page Directory |   Page Table   | Offset within Page  |
+// |      Index     |      Index     |                     |
+// +----------------+----------------+---------------------+
+//  \--- PDX(la) --/ \--- PTX(la) --/ \---- PGOFF(la) ----/
+//  \----------- PPN(la) -----------/
+//
+// The PDX, PTX, PGOFF, and PPN macros decompose linear addresses as shown.
+// To construct a linear address la from PDX(la), PTX(la), and PGOFF(la),
+// use PGADDR(PDX(la), PTX(la), PGOFF(la)).
+
+// page number field of address
+#define PPN(la)		(((uint) (la)) >> PTXSHIFT)
+#define VPN(la)		PPN(la)		// used to index into vpt[]
+
+// page directory index
+#define PDX(la)		((((uint) (la)) >> PDXSHIFT) & 0x3FF)
+#define VPD(la)		PDX(la)		// used to index into vpd[]
+
+// page table index
+#define PTX(la)		((((uint) (la)) >> PTXSHIFT) & 0x3FF)
+
+// offset in page
+#define PGOFF(la)	(((uint) (la)) & 0xFFF)
+
+// construct linear address from indexes and offset
+#define PGADDR(d, t, o)	((uint) ((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))
+
+// mapping from physical addresses to virtual addresses is the identity one
+// (really linear addresses, but we map linear to physical also directly)
+#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 PTSIZE		(PGSIZE*NPTENTRIES) // bytes mapped by a page directory entry
+#define PTSHIFT		22		// log2(PTSIZE)
+
+#define PTXSHIFT	12		// offset of PTX in a linear address
+#define PDXSHIFT	22		// offset of PDX in a linear address
+
+// Page table/directory entry flags.
+#define PTE_P		0x001	// Present
+#define PTE_W		0x002	// Writeable
+#define PTE_U		0x004	// User
+#define PTE_PWT		0x008	// Write-Through
+#define PTE_PCD		0x010	// Cache-Disable
+#define PTE_A		0x020	// Accessed
+#define PTE_D		0x040	// Dirty
+#define PTE_PS		0x080	// Page Size
+#define PTE_MBZ		0x180	// Bits must be zero
+
+// The PTE_AVAIL bits aren't used by the kernel or interpreted by the
+// hardware, so user processes are allowed to set them arbitrarily.
+#define PTE_AVAIL	0xE00	// Available for software use
+
+// Only flags in PTE_USER may be used in system calls.
+#define PTE_USER	(PTE_AVAIL | PTE_P | PTE_W | PTE_U)
+
+// Address in page table or page directory entry
+#define PTE_ADDR(pte)	((uint) (pte) & ~0xFFF)
+
+typedef uint pte_t;
+typedef uint pde_t;
+
+// Control Register flags
+#define CR0_PE		0x00000001	// Protection Enable
+#define CR0_MP		0x00000002	// Monitor coProcessor
+#define CR0_EM		0x00000004	// Emulation
+#define CR0_TS		0x00000008	// Task Switched
+#define CR0_ET		0x00000010	// Extension Type
+#define CR0_NE		0x00000020	// Numeric Errror
+#define CR0_WP		0x00010000	// Write Protect
+#define CR0_AM		0x00040000	// Alignment Mask
+#define CR0_NW		0x20000000	// Not Writethrough
+#define CR0_CD		0x40000000	// Cache Disable
+#define CR0_PG		0x80000000	// Paging
+
+
 // PAGEBREAK: 40
 // Task state segment format
 struct taskstate {
diff --git a/mp.c b/mp.c
index e1edf24..d2f828a 100644
--- a/mp.c
+++ b/mp.c
@@ -39,6 +39,7 @@ mpsearch1(uchar *addr, int len)
 {
   uchar *e, *p;
 
+  cprintf("mpsearch1 0x%x %d\n", addr, len);
   e = addr+len;
   for(p = addr; p < e; p += sizeof(struct mp))
     if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0)
diff --git a/param.h b/param.h
index 34edf95..c1959d1 100644
--- a/param.h
+++ b/param.h
@@ -1,5 +1,5 @@
 #define NPROC        64  // maximum number of processes
-#define PAGE       4096  // granularity of user-space memory allocation
+#define PAGE       4096  // conveniently chosen to be equal to PGSIZE
 #define KSTACKSIZE PAGE  // size of per-process kernel stack
 #define NCPU          8  // maximum number of CPUs
 #define NOFILE       16  // open files per process
diff --git a/proc.c b/proc.c
index 669331e..a38a9e6 100644
--- a/proc.c
+++ b/proc.c
@@ -60,39 +60,6 @@ procdump(void)
   }
 }
 
-// Set up CPU's kernel segment descriptors.
-// Run once at boot time on each CPU.
-void
-ksegment(void)
-{
-  struct cpu *c;
-
-  c = &cpus[cpunum()];
-  c->gdt[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0x100000 + 64*1024-1, 0);
-  c->gdt[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
-  c->gdt[SEG_KCPU] = SEG(STA_W, &c->cpu, 8, 0);
-  lgdt(c->gdt, sizeof(c->gdt));
-  loadgs(SEG_KCPU << 3);
-  
-  // Initialize cpu-local storage.
-  cpu = c;
-  proc = 0;
-}
-
-// Set up CPU's segment descriptors and current process task state.
-void
-usegment(void)
-{
-  pushcli();
-  cpu->gdt[SEG_UCODE] = SEG(STA_X|STA_R, proc->mem, proc->sz-1, DPL_USER);
-  cpu->gdt[SEG_UDATA] = SEG(STA_W, proc->mem, proc->sz-1, DPL_USER);
-  cpu->gdt[SEG_TSS] = SEG16(STS_T32A, &cpu->ts, sizeof(cpu->ts)-1, 0);
-  cpu->gdt[SEG_TSS].s = 0;
-  cpu->ts.ss0 = SEG_KDATA << 3;
-  cpu->ts.esp0 = (uint)proc->kstack + KSTACKSIZE;
-  ltr(SEG_TSS << 3);
-  popcli();
-}
 
 //PAGEBREAK: 32
 // Look in the process table for an UNUSED proc.
@@ -149,20 +116,19 @@ userinit(void)
   
   p = allocproc();
   initproc = p;
-
-  // Initialize memory from initcode.S
-  p->sz = PAGE;
-  p->mem = kalloc(p->sz);
-  memset(p->mem, 0, p->sz);
-  memmove(p->mem, _binary_initcode_start, (int)_binary_initcode_size);
-
+  if (!(p->pgdir = setupkvm()))
+    panic("userinit: out of memory?");
+  if (!allocuvm(p->pgdir, 0x0, (int)_binary_initcode_size))
+    panic("userinit: out of memory?");
+  inituvm(p->pgdir, 0x0, _binary_initcode_start, (int)_binary_initcode_size);
+  p->sz = PGROUNDUP((int)_binary_initcode_size);
   memset(p->tf, 0, sizeof(*p->tf));
   p->tf->cs = (SEG_UCODE << 3) | DPL_USER;
   p->tf->ds = (SEG_UDATA << 3) | DPL_USER;
   p->tf->es = p->tf->ds;
   p->tf->ss = p->tf->ds;
   p->tf->eflags = FL_IF;
-  p->tf->esp = p->sz;
+  p->tf->esp = PGSIZE;
   p->tf->eip = 0;  // beginning of initcode.S
 
   safestrcpy(p->name, "initcode", sizeof(p->name));
@@ -176,17 +142,10 @@ userinit(void)
 int
 growproc(int n)
 {
-  char *newmem;
-
-  newmem = kalloc(proc->sz + n);
-  if(newmem == 0)
+  if (!allocuvm(proc->pgdir, (char *)proc->sz, n))
     return -1;
-  memmove(newmem, proc->mem, proc->sz);
-  memset(newmem + proc->sz, 0, n);
-  kfree(proc->mem, proc->sz);
-  proc->mem = newmem;
   proc->sz += n;
-  usegment();
+  loadvm(proc);
   return 0;
 }
 
@@ -204,14 +163,13 @@ fork(void)
     return -1;
 
   // Copy process state from p.
-  np->sz = proc->sz;
-  if((np->mem = kalloc(np->sz)) == 0){
+  if (!(np->pgdir = copyuvm(proc->pgdir, proc->sz))) {
     kfree(np->kstack, KSTACKSIZE);
     np->kstack = 0;
     np->state = UNUSED;
     return -1;
   }
-  memmove(np->mem, proc->mem, np->sz);
+  np->sz = proc->sz;
   np->parent = proc;
   *np->tf = *proc->tf;
 
@@ -225,7 +183,7 @@ fork(void)
  
   pid = np->pid;
   np->state = RUNNABLE;
-
+  safestrcpy(np->name, proc->name, sizeof(proc->name));
   return pid;
 }
 
@@ -256,7 +214,7 @@ scheduler(void)
       // to release ptable.lock and then reacquire it
       // before jumping back to us.
       proc = p;
-      usegment();
+      loadvm(p);
       p->state = RUNNING;
       swtch(&cpu->scheduler, proc->context);
 
@@ -284,7 +242,6 @@ sched(void)
     panic("sched running");
   if(readeflags()&FL_IF)
     panic("sched interruptible");
-
   intena = cpu->intena;
   swtch(&proc->context, cpu->scheduler);
   cpu->intena = intena;
@@ -455,9 +412,10 @@ wait(void)
       if(p->state == ZOMBIE){
         // Found one.
         pid = p->pid;
-        kfree(p->mem, p->sz);
         kfree(p->kstack, KSTACKSIZE);
+	freevm(p->pgdir);
         p->state = UNUSED;
+	p->kstack = 0;
         p->pid = 0;
         p->parent = 0;
         p->name[0] = 0;
diff --git a/proc.h b/proc.h
index ebf4f2d..ebc42f1 100644
--- a/proc.h
+++ b/proc.h
@@ -30,8 +30,8 @@ enum procstate { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };
 
 // Per-process state
 struct proc {
-  char *mem;                   // Start of process memory (kernel address)
   uint sz;                     // Size of process memory (bytes)
+  pde_t* pgdir;                // linear address of proc's pgdir
   char *kstack;                // Bottom of kernel stack for this process
   enum procstate state;        // Process state
   volatile int pid;            // Process ID
diff --git a/sh.c b/sh.c
index 100bbdc..e8d65f0 100644
--- a/sh.c
+++ b/sh.c
@@ -330,7 +330,7 @@ parsecmd(char *s)
 {
   char *es;
   struct cmd *cmd;
-  
+
   es = s + strlen(s);
   cmd = parseline(&s, es);
   peek(&s, es, "");
@@ -363,7 +363,7 @@ struct cmd*
 parsepipe(char **ps, char *es)
 {
   struct cmd *cmd;
-  
+
   cmd = parseexec(ps, es);
   if(peek(ps, es, "|")){
     gettoken(ps, es, 0, 0);
@@ -420,6 +420,7 @@ parseexec(char **ps, char *es)
   int tok, argc;
   struct execcmd *cmd;
   struct cmd *ret;
+  int *x = (int *) peek;
   
   if(peek(ps, es, "("))
     return parseblock(ps, es);
diff --git a/spinlock.c b/spinlock.c
index c3ea730..54e4eb5 100644
--- a/spinlock.c
+++ b/spinlock.c
@@ -71,7 +71,7 @@ getcallerpcs(void *v, uint pcs[])
   
   ebp = (uint*)v - 2;
   for(i = 0; i < 10; i++){
-    if(ebp == 0 || ebp == (uint*)0xffffffff)
+    if(ebp == 0 || ebp < 0x100000 || ebp == (uint*)0xffffffff)
       break;
     pcs[i] = ebp[1];     // saved %eip
     ebp = (uint*)ebp[0]; // saved %ebp
diff --git a/swtch.S b/swtch.S
index 8751317..49efdf9 100644
--- a/swtch.S
+++ b/swtch.S
@@ -26,3 +26,11 @@ swtch:
   popl %ebx
   popl %ebp
   ret
+
+# Jump on a new stack, fake C calling conventions
+.globl jstack
+jstack:	
+  movl 4(%esp), %esp
+  subl $16, %esp	# space for arguments
+  movl $0, %ebp	        # terminate functions that follow ebp's
+  call mainc	        # continue at mainc
diff --git a/syscall.c b/syscall.c
index 110a872..2785c0a 100644
--- a/syscall.c
+++ b/syscall.c
@@ -18,10 +18,12 @@ fetchint(struct proc *p, uint addr, int *ip)
 {
   if(addr >= p->sz || addr+4 > p->sz)
     return -1;
-  *ip = *(int*)(p->mem + addr);
+  *ip = *(int*)(addr);
   return 0;
 }
 
+// XXX should we copy the string?
+
 // Fetch the nul-terminated string at addr from process p.
 // Doesn't actually copy the string - just sets *pp to point at it.
 // Returns length of string, not including nul.
@@ -32,8 +34,10 @@ fetchstr(struct proc *p, uint addr, char **pp)
 
   if(addr >= p->sz)
     return -1;
-  *pp = p->mem + addr;
-  ep = p->mem + p->sz;
+  // *pp = p->mem + addr;
+  // ep = p->mem + p->sz;
+  *pp = (char **) addr;
+  ep = p->sz;
   for(s = *pp; s < ep; s++)
     if(*s == 0)
       return s - *pp;
@@ -44,7 +48,8 @@ fetchstr(struct proc *p, uint addr, char **pp)
 int
 argint(int n, int *ip)
 {
-  return fetchint(proc, proc->tf->esp + 4 + 4*n, ip);
+  int x = fetchint(proc, proc->tf->esp + 4 + 4*n, ip);
+  return x;
 }
 
 // Fetch the nth word-sized system call argument as a pointer
@@ -59,7 +64,8 @@ argptr(int n, char **pp, int size)
     return -1;
   if((uint)i >= proc->sz || (uint)i+size >= proc->sz)
     return -1;
-  *pp = proc->mem + i;
+  // *pp = proc->mem + i;   // XXXXX
+  *pp = (char *) i;   // XXXXX
   return 0;
 }
 
diff --git a/sysfile.c b/sysfile.c
index 3eec766..6b8eef4 100644
--- a/sysfile.c
+++ b/sysfile.c
@@ -264,7 +264,6 @@ sys_open(void)
 
   if(argstr(0, &path) < 0 || argint(1, &omode) < 0)
     return -1;
-
   if(omode & O_CREATE){
     if((ip = create(path, T_FILE, 0, 0)) == 0)
       return -1;
@@ -291,7 +290,6 @@ sys_open(void)
   f->off = 0;
   f->readable = !(omode & O_WRONLY);
   f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
-
   return fd;
 }
 
@@ -350,8 +348,9 @@ sys_exec(void)
   int i;
   uint uargv, uarg;
 
-  if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0)
+  if(argstr(0, &path) < 0 || argint(1, (int*)&uargv) < 0) {
     return -1;
+  }
   memset(argv, 0, sizeof(argv));
   for(i=0;; i++){
     if(i >= NELEM(argv))
diff --git a/trap.c b/trap.c
index 86ce052..1f35708 100644
--- a/trap.c
+++ b/trap.c
@@ -78,13 +78,14 @@ trap(struct trapframe *tf)
   default:
     if(proc == 0 || (tf->cs&3) == 0){
       // In kernel, it must be our mistake.
-      cprintf("unexpected trap %d from cpu %d eip %x\n",
-              tf->trapno, cpu->id, tf->eip);
+      cprintf("unexpected trap %d from cpu %d eip %x (cr2=0x%x)\n",
+              tf->trapno, cpu->id, tf->eip, rcr2());
       panic("trap");
     }
     // In user space, assume process misbehaved.
-    cprintf("pid %d %s: trap %d err %d on cpu %d eip %x -- kill proc\n",
-            proc->pid, proc->name, tf->trapno, tf->err, cpu->id, tf->eip);
+    cprintf("pid %d %s: trap %d err %d on cpu %d eip 0x%x addr 0x%x--kill proc\n",
+            proc->pid, proc->name, tf->trapno, tf->err, cpu->id, tf->eip, 
+	    rcr2());
     proc->killed = 1;
   }
 
diff --git a/x86.h b/x86.h
index fcd3062..986e1b0 100644
--- a/x86.h
+++ b/x86.h
@@ -121,6 +121,61 @@ sti(void)
   asm volatile("sti");
 }
 
+static inline void lcr0(uint val)
+{
+  asm volatile("movl %0,%%cr0" : : "r" (val));
+}
+
+static inline uint rcr0(void)
+{
+  uint val;
+  asm volatile("movl %%cr0,%0" : "=r" (val));
+  return val;
+}
+
+static inline uint rcr2(void)
+{
+  uint val;
+  asm volatile("movl %%cr2,%0" : "=r" (val));
+  return val;
+}
+
+static inline void lcr3(uint val) 
+{
+  asm volatile("movl %0,%%cr3" : : "r" (val));
+}
+
+static inline uint rcr3(void)
+{
+  uint val;
+  asm volatile("movl %%cr3,%0" : "=r" (val));
+  return val;
+}
+
+static inline void lebp(uint val)
+{
+  asm volatile("movl %0,%%ebp" : : "r" (val));
+}
+
+static inline uint rebp(void)
+{
+  uint val;
+  asm volatile("movl %%ebp,%0" : "=r" (val));
+  return val;
+}
+
+static inline void lesp(uint val)
+{
+  asm volatile("movl %0,%%esp" : : "r" (val));
+}
+
+static inline uint resp(void)
+{
+  uint val;
+  asm volatile("movl %%esp,%0" : "=r" (val));
+  return val;
+}
+
 //PAGEBREAK: 36
 // Layout of the trap frame built on the stack by the
 // hardware and by trapasm.S, and passed to trap().