diff --git a/defs.h b/defs.h
index d0750de35f2331dff985242fcc0414c5c090a4ae..d6d68adfa61357ec89201bec9c9fb513f9bd4ced 100644
--- a/defs.h
+++ b/defs.h
@@ -73,6 +73,7 @@ int             cpunum(void);
 extern volatile uint*    lapic;
 void            lapiceoi(void);
 void            lapicinit(int);
+void            lapic_tlbflush(uint);
 void            lapicstartap(uchar, uint);
 void            microdelay(int);
 
@@ -156,6 +157,8 @@ void            uartputc(int);
 #define PGROUNDUP(sz)  ((sz+PGSIZE-1) & ~(PGSIZE-1))
 void            pminit(void);
 void            ksegment(void);
+void            kvmalloc(void);
+void            loadkvm(void);
 void            vminit(void);
 void            jkstack();
 void            printstack(void);
diff --git a/exec.c b/exec.c
index 65de3120e8ace188f61d8f92cdb6294660fa276e..6ded59f8d62c721e94d4971424b6344133f6c344 100644
--- a/exec.c
+++ b/exec.c
@@ -95,14 +95,10 @@ exec(char *path, char **argv)
   proc->tf->eip = elf.entry;  // main
   proc->tf->esp = sp;
 
-  // printstack();
-
-  loadvm(proc);
+  loadvm(proc); 
 
   freevm(oldpgdir);
 
-  // printstack(); 
-
   return 0;
 
  bad:
diff --git a/lapic.c b/lapic.c
index 4fe4ace7bd95ff4c6add286303e095ff40aa2757..c1cd7f30eb98aed16862e87c6830db6e913275c2 100644
--- a/lapic.c
+++ b/lapic.c
@@ -20,8 +20,11 @@
   #define STARTUP    0x00000600   // Startup IPI
   #define DELIVS     0x00001000   // Delivery status
   #define ASSERT     0x00004000   // Assert interrupt (vs deassert)
+  #define DEASSERT   0x00000000
   #define LEVEL      0x00008000   // Level triggered
   #define BCAST      0x00080000   // Send to all APICs, including self.
+  #define BUSY       0x00001000
+  #define FIXED      0x00000000
 #define ICRHI   (0x0310/4)   // Interrupt Command [63:32]
 #define TIMER   (0x0320/4)   // Local Vector Table 0 (TIMER)
   #define X1         0x0000000B   // divide counts by 1
@@ -44,6 +47,27 @@ lapicw(int index, int value)
   lapic[ID];  // wait for write to finish, by reading
 }
 
+static uint
+lapicr(uint off)
+{
+  return lapic[off];
+}
+
+static int
+apic_icr_wait()
+{
+    uint i = 100000;
+    while ((lapicr(ICRLO) & BUSY) != 0) {
+        nop_pause();
+        i--;
+        if (i == 0) {
+            cprintf("apic_icr_wait: wedged?\n");
+            return -1;
+        }
+    }
+    return 0;
+}
+
 //PAGEBREAK!
 void
 lapicinit(int c)
@@ -128,6 +152,22 @@ microdelay(int us)
 }
 
 
+// Send IPI
+void
+lapic_ipi(int cpu, int ino)
+{
+  lapicw(ICRHI, cpu << 24);
+  lapicw(ICRLO, FIXED | DEASSERT | ino);
+  if (apic_icr_wait() < 0)
+    panic("lapic_ipi: icr_wait failure");
+}
+
+void
+lapic_tlbflush(uint cpu)
+{
+  lapic_ipi(cpu, T_TLBFLUSH);
+}
+
 #define IO_RTC  0x70
 
 // Start additional processor running bootstrap code at addr.
diff --git a/main.c b/main.c
index 319aad961ce372aa30fab2a707fb4247bbd222cc..78cd3340300f4d94487ec9498cd7f21335bb2e65 100644
--- a/main.c
+++ b/main.c
@@ -6,13 +6,14 @@
 #include "x86.h"
 
 static void bootothers(void);
-static void mpmain(void) __attribute__((noreturn));
+static void mpmain(void);
+void jkstack(void) __attribute__((noreturn));
 
 // Bootstrap processor starts running C code here.
 int
 main(void)
 {
-  mpinit(); // collect info about this machine
+  mpinit();        // collect info about this machine
   lapicinit(mpbcpu());
   ksegment();
   picinit();       // interrupt controller
@@ -28,18 +29,17 @@ mainc(void)
 {
   cprintf("cpus %p cpu %p\n", cpus, cpu);
   cprintf("\ncpu%d: starting xv6\n\n", cpu->id);
-  vminit();        // virtual memory
+  kvmalloc();      // allocate the kernel page table
   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  XXX fix where to boot from
+  bootothers();    // start other processors
 
   // Finish setting up this processor in mpmain.
   mpmain();
@@ -53,13 +53,12 @@ mpmain(void)
   if(cpunum() != mpbcpu()) {
     ksegment();
     cprintf("other cpu\n");
-    vminit();
     lapicinit(cpunum());
   }
+  vminit();        // Run with paging on each processor
   cprintf("cpu%d: mpmain\n", cpu->id);
   idtinit();
   xchg(&cpu->booted, 1);
-
   cprintf("cpu%d: scheduling\n", cpu->id);
   scheduler();
 }
@@ -72,10 +71,10 @@ bootothers(void)
   struct cpu *c;
   char *stack;
 
-  // Write bootstrap code to unused memory at 0x7000.
-  code = (uchar*)0x7000;
+  // Write bootstrap code to unused memory at 0x7000.  The linker has
+  // placed the start of bootother.S there.
+  code = (uchar *) 0x7000;
   memmove(code, _binary_bootother_start, (uint)_binary_bootother_size);
-
   for(c = cpus; c < cpus+ncpu; c++){
     if(c == cpus+cpunum())  // We've started already.
       continue;
@@ -84,15 +83,11 @@ 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/proc.c b/proc.c
index a38a9e60fac4058ac565d121d7345e002073a006..3e28200cf2163fd2659ae3d2a3a846f9fbc6762e 100644
--- a/proc.c
+++ b/proc.c
@@ -242,6 +242,7 @@ sched(void)
     panic("sched running");
   if(readeflags()&FL_IF)
     panic("sched interruptible");
+  loadkvm();    // Switch to the kernel page table
   intena = cpu->intena;
   swtch(&proc->context, cpu->scheduler);
   cpu->intena = intena;
diff --git a/trap.c b/trap.c
index 1f35708e706f4551d6a119bbf729ca486bb8a0ba..f0f016f1427abbb625b669a558b92093ab690921 100644
--- a/trap.c
+++ b/trap.c
@@ -73,6 +73,10 @@ trap(struct trapframe *tf)
             cpu->id, tf->cs, tf->eip);
     lapiceoi();
     break;
+  case T_TLBFLUSH:
+    lapiceoi();
+    lcr3(rcr3());
+    break;
    
   //PAGEBREAK: 13
   default:
diff --git a/traps.h b/traps.h
index f450c2df9c6b332007145fd2f71449b713e3fe03..4422d745e3acb0e41f9e79593c2119fe52048362 100644
--- a/traps.h
+++ b/traps.h
@@ -24,7 +24,8 @@
 
 // These are arbitrarily chosen, but with care not to overlap
 // processor defined exceptions or interrupt vectors.
-#define T_SYSCALL       64     // system call
+#define T_SYSCALL       64      // system call
+#define T_TLBFLUSH      65      // flush TLB
 #define T_DEFAULT      500      // catchall
 
 #define T_IRQ0          32      // IRQ 0 corresponds to int T_IRQ
diff --git a/x86.h b/x86.h
index 986e1b0042370cfe9747d6f655ebf40f35b9030b..b9fa8b8ac4455561495838cabeb84c48ba4a861d 100644
--- a/x86.h
+++ b/x86.h
@@ -176,6 +176,11 @@ static inline uint resp(void)
   return val;
 }
 
+static inline void nop_pause(void)
+{
+  asm volatile("pause" : :);
+}
+
 //PAGEBREAK: 36
 // Layout of the trap frame built on the stack by the
 // hardware and by trapasm.S, and passed to trap().