diff --git a/ioapic.c b/ioapic.c
index aae49b5..a009ca8 100644
--- a/ioapic.c
+++ b/ioapic.c
@@ -1,85 +1,82 @@
+// The I/O APIC manages hardware interrupts for an SMP system.
+// http://www.intel.com/design/chipsets/datashts/29056601.pdf
+
 #include "types.h"
 #include "mp.h"
 #include "defs.h"
 #include "x86.h"
 #include "traps.h"
-#include "ioapic.h"
 
+#define IOAPIC  0xFEC00000   // Default physical address of IO APIC
+
+#define REG_ID     0x00  // Register index: ID
+#define REG_VER    0x01  // Register index: version
+#define REG_TABLE  0x10  // Redirection table base
+
+// The redirection table starts at REG_TABLE and uses
+// two registers to configure each interrupt.  
+// The first (low) register in a pair contains configuration bits.
+// The second (high) register contains a bitmask telling which
+// CPUs can serve that interrupt.
+#define INT_DISABLED   0x00100000  // Interrupt disabled
+#define INT_LEVEL      0x00008000  // Level-triggered (vs edge-)
+#define INT_ACTIVELOW  0x00002000  // Active low (vs high)
+#define INT_LOGICAL    0x00000800  // Destination is CPU id (vs APIC ID)
+
+volatile struct ioapic *ioapic;
+
+// IO APIC MMIO structure: write reg, then read or write data.
 struct ioapic {
-  uint ioregsel;  uint p01; uint p02; uint p03;
-  uint iowin;     uint p11; uint p12; uint p13;
+  uint reg;
+  uint pad[3];
+  uint data;
 };
 
-
-#define IOAPIC_REDTBL_LO(i)  (IOAPIC_REDTBL + (i) * 2)
-#define IOAPIC_REDTBL_HI(i)  (IOAPIC_REDTBL_LO(i) + 1)
-
 static uint
-ioapic_read(struct ioapic *io, int reg)
+ioapic_read(int reg)
 {
-  io->ioregsel = reg;
-  return io->iowin;
+  ioapic->reg = reg;
+  return ioapic->data;
 }
 
 static void
-ioapic_write(struct ioapic *io, int reg, uint val)
+ioapic_write(int reg, uint data)
 {
-  io->ioregsel = reg;
-  io->iowin = val;
+  ioapic->reg = reg;
+  ioapic->data = data;
 }
 
 void
 ioapic_init(void)
 {
-  struct ioapic *io;
-  uint l, h;
-  int nintr;
-  uchar id;
-  int i;
+  int i, id, maxintr;
 
   if(!ismp)
     return;
 
-  io = (struct ioapic*) IO_APIC_BASE;
-  l = ioapic_read(io, IOAPIC_VER);
-  nintr =  ((l & IOART_VER_MAXREDIR) >> MAXREDIRSHIFT) + 1;
-  id = ioapic_read(io, IOAPIC_ID) >> APIC_ID_SHIFT;
+  ioapic = (volatile struct ioapic*)IOAPIC;
+  maxintr = (ioapic_read(REG_VER) >> 16) & 0xFF;
+  id = ioapic_read(REG_ID) >> 24;
   if(id != ioapic_id)
     cprintf("ioapic_init: id isn't equal to ioapic_id; not a MP\n");
-  for(i = 0; i < nintr; i++) {
-    // active-hi and edge-triggered for ISA interrupts
-    // Assume that pin 0 on the first I/O APIC is an ExtINT pin.
-    // Assume that pins 1-15 are ISA interrupts
-    l = ioapic_read(io, IOAPIC_REDTBL_LO(i));
-    l = l & ~IOART_INTMASK;  // allow INTs
-    l |= IOART_INTMSET;
-    l = l & ~IOART_INTPOL;   // active hi
-    l = l & ~IOART_TRGRMOD;  // edgee triggered
-    l = l & ~IOART_DELMOD;   // fixed
-    l = l & ~IOART_DESTMOD;  // physical mode
-    l = l | (IRQ_OFFSET + i); // vector
-    ioapic_write(io, IOAPIC_REDTBL_LO(i), l);
-    h = ioapic_read(io, IOAPIC_REDTBL_HI(i));
-    h &= ~IOART_DEST;
-    ioapic_write(io, IOAPIC_REDTBL_HI(i), h);
+
+  // Mark all interrupts edge-triggered, active high, disabled,
+  // and not routed to any CPUs.
+  for(i = 0; i <= maxintr; i++){
+    ioapic_write(REG_TABLE+2*i, INT_DISABLED | (IRQ_OFFSET + i));
+    ioapic_write(REG_TABLE+2*i+1, 0);
   }
 }
 
 void
-ioapic_enable (int irq, int cpunum)
+ioapic_enable(int irq, int cpunum)
 {
-  uint l, h;
-  struct ioapic *io;
-  
   if(!ismp)
     return;
 
-  io = (struct ioapic*) IO_APIC_BASE;
-  l = ioapic_read(io, IOAPIC_REDTBL_LO(irq));
-  l = l & ~IOART_INTMASK;  // allow INTs
-  ioapic_write(io, IOAPIC_REDTBL_LO(irq), l);
-  h = ioapic_read(io, IOAPIC_REDTBL_HI(irq));
-  h &= ~IOART_DEST;
-  h |= (cpunum << APIC_ID_SHIFT);
-  ioapic_write(io, IOAPIC_REDTBL_HI(irq), h);
+  // Mark interrupt edge-triggered, active high,
+  // enabled, and routed to the given cpunum,
+  // which happens to be that cpu's APIC ID.
+  ioapic_write(REG_TABLE+2*irq, IRQ_OFFSET + irq);
+  ioapic_write(REG_TABLE+2*irq+1, cpunum << 24);
 }
diff --git a/ioapic.h b/ioapic.h
deleted file mode 100644
index a85f810..0000000
--- a/ioapic.h
+++ /dev/null
@@ -1,88 +0,0 @@
-#define IO_APIC_BASE   0xFEC00000   // Default phys addr of IO APIC
-#define IOAPIC_WINDOW        0x10   // Window register offset
-
-// Constants relating to APIC ID registers
-#define APIC_ID_MASK            0xff000000
-#define APIC_ID_SHIFT           24
-#define APIC_ID_CLUSTER         0xf0
-#define APIC_ID_CLUSTER_ID      0x0f
-#define APIC_MAX_CLUSTER        0xe
-#define APIC_MAX_INTRACLUSTER_ID 3
-#define APIC_ID_CLUSTER_SHIFT   4
-
-// Fields in VER
-#define APIC_VER_VERSION        0x000000ff
-#define APIC_VER_MAXLVT         0x00ff0000
-#define MAXLVTSHIFT             16
-
-// Indexes into IO APIC
-#define IOAPIC_ID               0x00
-#define IOAPIC_VER              0x01
-#define IOAPIC_ARB              0x02
-#define IOAPIC_REDTBL           0x10
-#define IOAPIC_REDTBL0          IOAPIC_REDTBL
-#define IOAPIC_REDTBL1          (IOAPIC_REDTBL+0x02)
-#define IOAPIC_REDTBL2          (IOAPIC_REDTBL+0x04)
-#define IOAPIC_REDTBL3          (IOAPIC_REDTBL+0x06)
-#define IOAPIC_REDTBL4          (IOAPIC_REDTBL+0x08)
-#define IOAPIC_REDTBL5          (IOAPIC_REDTBL+0x0a)
-#define IOAPIC_REDTBL6          (IOAPIC_REDTBL+0x0c)
-#define IOAPIC_REDTBL7          (IOAPIC_REDTBL+0x0e)
-#define IOAPIC_REDTBL8          (IOAPIC_REDTBL+0x10)
-#define IOAPIC_REDTBL9          (IOAPIC_REDTBL+0x12)
-#define IOAPIC_REDTBL10         (IOAPIC_REDTBL+0x14)
-#define IOAPIC_REDTBL11         (IOAPIC_REDTBL+0x16)
-#define IOAPIC_REDTBL12         (IOAPIC_REDTBL+0x18)
-#define IOAPIC_REDTBL13         (IOAPIC_REDTBL+0x1a)
-#define IOAPIC_REDTBL14         (IOAPIC_REDTBL+0x1c)
-#define IOAPIC_REDTBL15         (IOAPIC_REDTBL+0x1e)
-#define IOAPIC_REDTBL16         (IOAPIC_REDTBL+0x20)
-#define IOAPIC_REDTBL17         (IOAPIC_REDTBL+0x22)
-#define IOAPIC_REDTBL18         (IOAPIC_REDTBL+0x24)
-#define IOAPIC_REDTBL19         (IOAPIC_REDTBL+0x26)
-#define IOAPIC_REDTBL20         (IOAPIC_REDTBL+0x28)
-#define IOAPIC_REDTBL21         (IOAPIC_REDTBL+0x2a)
-#define IOAPIC_REDTBL22         (IOAPIC_REDTBL+0x2c)
-#define IOAPIC_REDTBL23         (IOAPIC_REDTBL+0x2e)
-
-// Fields in the IO APIC's redirection table entries
-#define IOART_DEST      APIC_ID_MASK    // broadcast addr: all APICs
-
-#define IOART_RESV      0x00fe0000      // reserved
-
-#define IOART_INTMASK   0x00010000      // R/W: INTerrupt mask
-#define IOART_INTMCLR   0x00000000      //       clear, allow INTs
-#define IOART_INTMSET   0x00010000      //       set, inhibit INTs
-
-#define IOART_TRGRMOD   0x00008000      // R/W: trigger mode
-#define IOART_TRGREDG   0x00000000      //       edge
-#define IOART_TRGRLVL   0x00008000      //       level
-
-#define IOART_REM_IRR   0x00004000      // RO: remote IRR
-
-#define IOART_INTPOL    0x00002000      // R/W: INT input pin polarity
-#define IOART_INTAHI    0x00000000      //      active high
-#define IOART_INTALO    0x00002000      //      active low
-
-#define IOART_DELIVS    0x00001000      // RO: delivery status
-
-#define IOART_DESTMOD   0x00000800      // R/W: destination mode
-#define IOART_DESTPHY   0x00000000      //      physical
-#define IOART_DESTLOG   0x00000800      //      logical
-
-#define IOART_DELMOD    0x00000700      // R/W: delivery mode
-#define IOART_DELFIXED  0x00000000      //       fixed
-#define IOART_DELLOPRI  0x00000100      //       lowest priority
-#define IOART_DELSMI    0x00000200      //       System Management INT
-#define IOART_DELRSV1   0x00000300      //       reserved
-#define IOART_DELNMI    0x00000400      //       NMI signal
-#define IOART_DELINIT   0x00000500      //       INIT signal
-#define IOART_DELRSV2   0x00000600      //       reserved
-#define IOART_DELEXINT  0x00000700      //       External INTerrupt
-
-#define IOART_INTVEC    0x000000ff      // R/W: INTerrupt vector field
-
-// Fields in VER
-#define IOART_VER_VERSION       0x000000ff
-#define IOART_VER_MAXREDIR      0x00ff0000
-#define MAXREDIRSHIFT           16
diff --git a/lapic.c b/lapic.c
index 7f2940c..6afc3b8 100644
--- a/lapic.c
+++ b/lapic.c
@@ -1,132 +1,91 @@
+// The local APIC manages internal (non-I/O) interrupts.
+// See Chapter 8 & Appendix C of Intel processor manual volume 3.
+
 #include "types.h"
-#include "mp.h"
 #include "defs.h"
 #include "param.h"
 #include "x86.h"
 #include "traps.h"
 #include "mmu.h"
 #include "proc.h"
-#include "lapic.h"
 
 // Local APIC registers, divided by 4 for use as uint[] indices.
 #define ID      (0x0020/4)   // ID
 #define VER     (0x0030/4)   // Version
 #define TPR     (0x0080/4)   // Task Priority
-#define APR     (0x0090/4)   // Arbitration Priority
-#define PPR     (0x00A0/4)   // Processor Priority
 #define EOI     (0x00B0/4)   // EOI
-#define LDR     (0x00D0/4)   // Logical Destination
-#define DFR     (0x00E0/4)   // Destination Format
 #define SVR     (0x00F0/4)   // Spurious Interrupt Vector
-#define ISR     (0x0100/4)   // Interrupt Status (8 registers)
-#define TMR     (0x0180/4)   // Trigger Mode (8 registers)
-#define IRR     (0x0200/4)   // Interrupt Request (8 registers)
+  #define ENABLE     0x00000100   // Unit Enable
 #define ESR     (0x0280/4)   // Error Status
 #define ICRLO   (0x0300/4)   // Interrupt Command
+  #define INIT       0x00000500   // INIT/RESET
+  #define STARTUP    0x00000600   // Startup IPI
+  #define DELIVS     0x00001000   // Delivery status
+  #define ASSERT     0x00004000   // Assert interrupt (vs deassert)
+  #define LEVEL      0x00008000   // Level triggered
+  #define BCAST      0x00080000   // Send to all APICs, including self.
 #define ICRHI   (0x0310/4)   // Interrupt Command [63:32]
 #define TIMER   (0x0320/4)   // Local Vector Table 0 (TIMER)
+  #define X1         0x0000000B   // divide counts by 1
+  #define PERIODIC   0x00020000   // Periodic
 #define PCINT   (0x0340/4)   // Performance Counter LVT
 #define LINT0   (0x0350/4)   // Local Vector Table 1 (LINT0)
 #define LINT1   (0x0360/4)   // Local Vector Table 2 (LINT1)
 #define ERROR   (0x0370/4)   // Local Vector Table 3 (ERROR)
+  #define MASKED     0x00010000   // Interrupt masked
 #define TICR    (0x0380/4)   // Timer Initial Count
 #define TCCR    (0x0390/4)   // Timer Current Count
 #define TDCR    (0x03E0/4)   // Timer Divide Configuration
 
-// SVR  
-#define ENABLE     0x00000100   // Unit Enable
-#define FOCUS      0x00000200   // Focus Processor Checking Disable
-
-// ICRLO
-// [14] IPI Trigger Mode Level (RW)
-#define DEASSERT   0x00000000   // Deassert level-sensitive interrupt
-#define ASSERT     0x00004000   // Assert level-sensitive interrupt
-
-// [17:16] Remote Read Status
-#define INVALID    0x00000000   // Invalid
-#define WAIT       0x00010000   // In-Progress
-#define VALID      0x00020000   // Valid
-
-// [19:18] Destination Shorthand
-#define FIELD      0x00000000   // No shorthand
-#define SELF       0x00040000   // Self is single destination
-#define ALLINC     0x00080000   // All including self
-#define ALLEXC     0x000C0000   // All Excluding self
-
-// ESR
-#define SENDCS     0x00000001   // Send CS Error
-#define RCVCS      0x00000002   // Receive CS Error
-#define SENDACCEPT 0x00000004   // Send Accept Error
-#define RCVACCEPT  0x00000008   // Receive Accept Error
-#define SENDVECTOR 0x00000020   // Send Illegal Vector
-#define RCVVECTOR  0x00000040   // Receive Illegal Vector
-#define REGISTER   0x00000080   // Illegal Register Address
-
-// [17] Timer Mode (RW)
-#define ONESHOT    0x00000000   // One-shot
-#define PERIODIC   0x00020000   // Periodic
-
-// [19:18] Timer Base (RW)
-#define CLKIN      0x00000000   // use CLKIN as input
-#define TMBASE     0x00040000   // use TMBASE
-#define DIVIDER    0x00080000   // use output of the divider
-
-#define X2         0x00000000   // divide by 2
-#define X4         0x00000001   // divide by 4
-#define X8         0x00000002   // divide by 8
-#define X16        0x00000003   // divide by 16
-#define X32        0x00000008   // divide by 32
-#define X64        0x00000009   // divide by 64
-#define X128       0x0000000A   // divide by 128
-#define X1         0x0000000B   // divide by 1
-
-//PAGEBREAK!
 volatile uint *lapic;  // Initialized in mp.c
 
+//PAGEBREAK!
 void
 lapic_init(int c)
 {
-  uint r, lvt;
-
   if(!lapic) 
     return;
 
-  lapic[DFR] = 0xFFFFFFFF;    // Set dst format register
-  r = (lapic[ID]>>24) & 0xFF; // Read APIC ID
-  lapic[LDR] = (1<<r) << 24;
-  lapic[TPR] = 0xFF;          // No interrupts for now
-
-  // Enable APIC
+  // Enable local APIC; set spurious interrupt vector.
   lapic[SVR] = ENABLE | (IRQ_OFFSET+IRQ_SPURIOUS);
 
-  // In virtual wire mode, set up the LINT0 and LINT1 as follows:
-  lapic[LINT0] = APIC_IMASK | APIC_EXTINT;
-  lapic[LINT1] = APIC_IMASK | APIC_NMI;
+  // The timer repeatedly counts down at bus frequency
+  // from lapic[TICR] and then issues an interrupt.  
+  // Lapic[TCCR] is the current counter value.
+  // If xv6 cared more about precise timekeeping, the
+  // values of TICR and TCCR would be calibrated using
+  // an external time source.
+  lapic[TDCR] = X1;
+  lapic[TICR] = 10000000;
+  lapic[TCCR] = 10000000;
+  lapic[TIMER] = PERIODIC | (IRQ_OFFSET + IRQ_TIMER);
 
-  lapic[EOI] = 0; // Ack any outstanding interrupts.
+  // Disable logical interrupt lines.
+  lapic[LINT0] = MASKED;
+  lapic[LINT1] = MASKED;
 
-  lvt = (lapic[VER]>>16) & 0xFF;
-  if(lvt >= 4)
-    lapic[PCINT] = APIC_IMASK;
+  // Disable performance counter overflow interrupts
+  // on machines that provide that interrupt entry.
+  if(((lapic[VER]>>16) & 0xFF) >= 4)
+    lapic[PCINT] = MASKED;
+
+  // Map error interrupt to IRQ_ERROR.
   lapic[ERROR] = IRQ_OFFSET+IRQ_ERROR;
-  lapic[ESR] = 0;
-  lapic[ESR];
 
-  // Issue an INIT Level De-Assert to synchronise arbitration ID's.
+  // Clear error status register (requires back-to-back writes).
+  lapic[ESR] = 0;
+  lapic[ESR] = 0;
+
+  // Ack any outstanding interrupts.
+  lapic[EOI] = 0;
+
+  // Send an Init Level De-Assert to synchronise arbitration ID's.
   lapic[ICRHI] = 0;
-  lapic[ICRLO] = ALLINC | APIC_LEVEL |
-                       DEASSERT | APIC_INIT;
-  while(lapic[ICRLO] & APIC_DELIVS)
+  lapic[ICRLO] = BCAST | INIT | LEVEL;
+  while(lapic[ICRLO] & DELIVS)
     ;
 
-  // Initialize the interrupt timer.
-  // On real hardware would need to do more XXX.
-  lapic[TDCR] = X1;
-  lapic[TIMER] = CLKIN | PERIODIC | (IRQ_OFFSET + IRQ_TIMER);
-  lapic[TCCR] = 10000000;
-  lapic[TICR] = 10000000;
-
-  // Enable interrupts on the APIC (but not on processor).
+  // Enable interrupts on the APIC (but not on the processor).
   lapic[TPR] = 0;
 }
 
@@ -146,22 +105,34 @@ lapic_eoi(void)
     lapic[EOI] = 0;
 }
 
+// Spin for a given number of microseconds.
+// On real hardware would want to tune this dynamically.
+static void
+microdelay(int us)
+{
+  volatile int j = 0;
+  
+  while(us-- > 0)
+    for(j=0; j<10000; j++);
+}
+
 // Start additional processor running bootstrap code at addr.
+// See Appendix B of MultiProcessor Specification.
 void
 lapic_startap(uchar apicid, uint addr)
 {
   int i;
   volatile int j = 0;
 
+  // Send INIT interrupt to reset other CPU.
   lapic[ICRHI] = apicid<<24;
-  lapic[ICRLO] = FIELD | APIC_LEVEL | ASSERT | APIC_INIT;
-  for(j=0; j<10000; j++);  // 200us
-  lapic[ICRLO] = FIELD | APIC_LEVEL | DEASSERT | APIC_INIT;
-  for(j=0; j<1000000; j++);  // 10ms
-
+  lapic[ICRLO] = INIT | LEVEL;
+  microdelay(10);
+  
+  // Send startup IPI (twice!) to enter bootstrap code.
   for(i = 0; i < 2; i++){
     lapic[ICRHI] = apicid<<24;
-    lapic[ICRLO] = FIELD | APIC_EDGE | APIC_STARTUP | (addr/4096);
+    lapic[ICRLO] = STARTUP | (addr>>12);
     for(j=0; j<10000; j++);  // 200us
   }
 }
diff --git a/main.c b/main.c
index 29a8d34..a8b3f62 100644
--- a/main.c
+++ b/main.c
@@ -12,6 +12,8 @@
 
 extern char edata[], end[];
 
+void bootothers(void);
+
 // Bootstrap processor starts running C code here.
 // This is called main0 not main so that it can have
 // a void return type.  Gcc can't handle functions named
@@ -37,7 +39,7 @@ main0(void)
   asm volatile("movl %0, %%ebp" : : "r" (cpus[bcpu].mpstack+MPSTACK));
 
   lapic_init(bcpu);
-  cprintf("\ncpu%d: starting xv6\n\n", cpu());
+  cprintf("\\ncpu%d: starting xv6\\n\\n", cpu());
 
   pinit();         // process table
   binit();         // buffer cache
@@ -51,7 +53,7 @@ main0(void)
   setupsegs(0);    // segments & TSS
   console_init();  // I/O devices & their interrupts
   ide_init();      // disk
-  mp_startthem();  // other CPUs
+  bootothers();    // boot other CPUs
   if(!ismp)
     pit8253_timerinit(); // uniprocessor timer
   userinit();      // first user process
@@ -67,7 +69,7 @@ main0(void)
 void
 mpmain(void)
 {
-  cprintf("cpu%d: starting\n", cpu());
+  cprintf("cpu%d: starting\\n", cpu());
   idtinit();
   lapic_init(cpu());
   setupsegs(0);
@@ -82,3 +84,29 @@ mpmain(void)
   scheduler();
 }
 
+void
+bootothers(void)
+{
+  extern uchar _binary_bootother_start[], _binary_bootother_size[];
+  uchar *code;
+  struct cpu *c;
+
+  // Write bootstrap code to unused memory at 0x7000.
+  code = (uchar*)0x7000;
+  memmove(code, _binary_bootother_start, (uint)_binary_bootother_size);
+
+  for(c = cpus; c < cpus+ncpu; c++){
+    if(c == cpus+cpu())  // We've started already.
+      continue;
+
+    // Set target %esp, %eip
+    *(void**)(code-4) = c->mpstack + MPSTACK;
+    *(void**)(code-8) = mpmain;
+    lapic_startap(c->apicid, (uint)code);
+
+    // Wait for cpu to get through bootstrap.
+    while(c->booted == 0)
+      ;
+  }
+}
+
diff --git a/mp.c b/mp.c
index 2cfea01..edd85df 100644
--- a/mp.c
+++ b/mp.c
@@ -1,3 +1,5 @@
+// http://developer.intel.com/design/pentium/datashts/24201606.pdf
+
 #include "types.h"
 #include "mp.h"
 #include "defs.h"
@@ -7,52 +9,39 @@
 #include "mmu.h"
 #include "proc.h"
 
-static char *buses[] = {
-  "CBUSI ",
-  "CBUSII",
-  "EISA  ",
-  "FUTURE",
-  "INTERN",
-  "ISA   ",
-  "MBI   ",
-  "MBII  ",
-  "MCA   ",
-  "MPI   ",
-  "MPSA  ",
-  "NUBUS ",
-  "PCI   ",
-  "PCMCIA",
-  "TC    ",
-  "VL    ",
-  "VME   ",
-  "XPRESS",
-  0,
-};
-
 struct cpu cpus[NCPU];
+static struct cpu *bcpu;
 int ismp;
 int ncpu;
 uchar ioapic_id;
 
-static struct cpu *bcpu;
-static struct mp *mp;  // The floating MP structure
-
-static struct mp*
-mp_scan(uchar *addr, int len)
+int
+mp_bcpu(void)
 {
-  uchar *e, *p, sum;
-  int i;
+  return bcpu-cpus;
+}
+
+static uchar
+sum(uchar *addr, int len)
+{
+  int i, sum;
+  
+  sum = 0;
+  for(i=0; i<len; i++)
+    sum += addr[i];
+  return sum;
+}
+
+// Look for an MP structure in the len bytes at addr.
+static struct mp*
+mp_search1(uchar *addr, int len)
+{
+  uchar *e, *p;
 
   e = addr+len;
-  for(p = addr; p < e; p += sizeof(struct mp)){
-    if(memcmp(p, "_MP_", 4))
-      continue;
-    sum = 0;
-    for(i = 0; i < sizeof(struct mp); i++)
-      sum += p[i];
-    if(sum == 0)
+  for(p = addr; p < e; p += sizeof(struct mp))
+    if(memcmp(p, "_MP_", 4) == 0 && sum(p, sizeof(struct mp)) == 0)
       return (struct mp*)p;
-  }
   return 0;
 }
 
@@ -68,110 +57,81 @@ mp_search(void)
   uint p;
   struct mp *mp;
 
-  bda = (uchar*) 0x400;
+  bda = (uchar*)0x400;
   if((p = (bda[0x0F]<<8)|bda[0x0E])){
-    if((mp = mp_scan((uchar*) p, 1024)))
+    if((mp = mp_search1((uchar*)p, 1024)))
       return mp;
   }else{
     p = ((bda[0x14]<<8)|bda[0x13])*1024;
-    if((mp = mp_scan((uchar*)p-1024, 1024)))
+    if((mp = mp_search1((uchar*)p-1024, 1024)))
       return mp;
   }
-  return mp_scan((uchar*)0xF0000, 0x10000);
+  return mp_search1((uchar*)0xF0000, 0x10000);
 }
 
-// Search for an MP configuration table. For now,
+// Search for an MP configuration table.  For now,
 // don't accept the default configurations (physaddr == 0).
 // Check for correct signature, calculate the checksum and,
 // if correct, check the version.
 // To do: check extended table checksum.
-static int
-mp_detect(void)
+static struct mpconf*
+mp_config(struct mp **pmp)
 {
-  struct mpctb *pcmp;
-  uchar *p, sum;
-  uint length;
+  struct mpconf *conf;
+  struct mp *mp;
 
   if((mp = mp_search()) == 0 || mp->physaddr == 0)
-    return -1;
-
-  pcmp = (struct mpctb*) mp->physaddr;
-  if(memcmp(pcmp, "PCMP", 4) != 0)
-    return -1;
-  if(pcmp->version != 1 && pcmp->version != 4)
-    return -1;
-
-  length = pcmp->length;
-  sum = 0;
-  for(p = (uchar*)pcmp; length; length--)
-    sum += *p++;
-  if(sum != 0)
-    return -1;
-
-  return 0;
+    return 0;
+  conf = (struct mpconf*)mp->physaddr;
+  if(memcmp(conf, "PCMP", 4) != 0)
+    return 0;
+  if(conf->version != 1 && conf->version != 4)
+    return 0;
+  if(sum((uchar*)conf, conf->length) != 0)
+    return 0;
+  *pmp = mp;
+  return conf;
 }
 
 void
 mp_init(void)
 {
-  int i;
   uchar *p, *e;
-  struct mpctb *mpctb;
-  struct mppe *proc;
-  struct mpbe *bus;
+  struct mp *mp;
+  struct mpconf *conf;
+  struct mpproc *proc;
   struct mpioapic *ioapic;
-  struct mpie *intr;
 
-  ncpu = 0;
-  if(mp_detect() < 0)
+  bcpu = &cpus[ncpu];
+  if((conf = mp_config(&mp)) == 0)
     return;
 
   ismp = 1;
+  lapic = (uint*)conf->lapicaddr;
 
-  // Run through the table saving information needed for starting
-  // application processors and initialising any I/O APICs. The table
-  // is guaranteed to be in order such that only one pass is necessary.
-
-  mpctb = (struct mpctb*)mp->physaddr;
-  lapic = (uint*)mpctb->lapicaddr;
-  p = (uchar*)mpctb + sizeof(*mpctb);
-  e = (uchar*)mpctb + mpctb->length;
-
-  while(p < e) {
+  for(p=(uchar*)(conf+1), e=(uchar*)conf+conf->length; p<e; ){
     switch(*p){
-    case MPPROCESSOR:
-      proc = (struct mppe*) p;
+    case MPPROC:
+      proc = (struct mpproc*)p;
       cpus[ncpu].apicid = proc->apicid;
-      if(proc->flags & MPBP) {
+      if(proc->flags & MPBOOT)
         bcpu = &cpus[ncpu];
-      }
       ncpu++;
-      p += sizeof(struct mppe);
-      continue;
-    case MPBUS:
-      bus = (struct mpbe*) p;
-      for(i = 0; buses[i]; i++){
-        if(strncmp(buses[i], bus->string, sizeof(bus->string)) == 0)
-          break;
-      }
-      p += sizeof(struct mpbe);
+      p += sizeof(struct mpproc);
       continue;
     case MPIOAPIC:
-      ioapic = (struct mpioapic*) p;
+      ioapic = (struct mpioapic*)p;
       ioapic_id = ioapic->apicno;
       p += sizeof(struct mpioapic);
       continue;
+    case MPBUS:
     case MPIOINTR:
-      intr = (struct mpie*) p;
-      p += sizeof(struct mpie);
+    case MPLINTR:
+      p += 8;
       continue;
     default:
-      cprintf("mp_init: unknown PCMP type 0x%x (e-p 0x%x)\n", *p, e-p);
-      while(p < e){
-        cprintf("%uX ", *p);
-        p++;
-      }
-      break;
+      cprintf("mp_init: unknown config type %x\n", *p);
+      panic("mp_init");
     }
   }
 
@@ -182,47 +142,3 @@ mp_init(void)
     outb(0x23, inb(0x23) | 1);  // Mask external interrupts.
   }
 }
-
-
-int
-mp_bcpu(void)
-{
-  if(ismp)
-    return bcpu-cpus;
-  return 0;
-}
-
-extern void mpmain(void);
-
-// Write bootstrap code to unused memory at 0x7000.
-#define APBOOTCODE 0x7000
-
-void
-mp_startthem(void)
-{
-  extern uchar _binary_bootother_start[], _binary_bootother_size[];
-  extern int main();
-  int c;
-
-  memmove((void*) APBOOTCODE,_binary_bootother_start,
-          (uint) _binary_bootother_size);
-
-  for(c = 0; c < ncpu; c++){
-    // Our current cpu has already started.
-    if(c == cpu())
-      continue;
-
-    // Set target %esp
-    *(uint*)(APBOOTCODE-4) = (uint) (cpus[c].mpstack) + MPSTACK;
-
-    // Set target %eip
-    *(uint*)(APBOOTCODE-8) = (uint)mpmain;
-
-    // Go!
-    lapic_startap(cpus[c].apicid, (uint)APBOOTCODE);
-
-    // Wait for cpu to get through bootstrap.
-    while(cpus[c].booted == 0)
-      ;
-  }
-}
diff --git a/mp.h b/mp.h
index b7c72d8..6453f65 100644
--- a/mp.h
+++ b/mp.h
@@ -1,4 +1,4 @@
-// See MultiProcessor Specification Version 1.[14].
+// See MultiProcessor Specification Version 1.[14]
 
 struct mp {             // floating pointer
   uchar signature[4];           // "_MP_"
@@ -11,7 +11,7 @@ struct mp {             // floating pointer
   uchar reserved[3];
 };
 
-struct mpctb {          // configuration table header
+struct mpconf {         // configuration table header
   uchar signature[4];           // "PCMP"
   ushort length;                // total table length
   uchar version;                // [14]
@@ -26,22 +26,17 @@ struct mpctb {          // configuration table header
   uchar reserved;
 };
 
-struct mppe {           // processor table entry
+struct mpproc {         // processor table entry
   uchar type;                   // entry type (0)
   uchar apicid;                 // local APIC id
   uchar version;                // local APIC verison
   uchar flags;                  // CPU flags
+    #define MPBOOT 0x02           // This proc is the bootstrap processor.
   uchar signature[4];           // CPU signature
   uint feature;                 // feature flags from CPUID instruction
   uchar reserved[8];
 };
 
-struct mpbe {           // bus table entry
-  uchar type;                   // entry type (1)
-  uchar busno;                  // bus id
-  char string[6];               // bus type string
-};
-
 struct mpioapic {       // I/O APIC table entry
   uchar type;                   // entry type (2)
   uchar apicno;                 // I/O APIC id
@@ -50,69 +45,10 @@ struct mpioapic {       // I/O APIC table entry
   uint *addr;                  // I/O APIC address
 };
 
-struct mpie {           // interrupt table entry
-  uchar type;                   // entry type ([34])
-  uchar intr;                   // interrupt type
-  ushort flags;                 // interrupt flag
-  uchar busno;                  // source bus id
-  uchar irq;                    // source bus irq
-  uchar apicno;                 // destination APIC id
-  uchar intin;                  // destination APIC [L]INTIN#
-};
+// Table entry types
+#define MPPROC    0x00  // One per processor
+#define MPBUS     0x01  // One per bus
+#define MPIOAPIC  0x02  // One per I/O APIC
+#define MPIOINTR  0x03  // One per bus interrupt source
+#define MPLINTR   0x04  // One per system interrupt source
 
-enum {                  // table entry types
-  MPPROCESSOR   = 0x00,         // one entry per processor
-  MPBUS = 0x01,                 // one entry per bus
-  MPIOAPIC = 0x02,              // one entry per I/O APIC
-  MPIOINTR = 0x03,              // one entry per bus interrupt source
-  MPLINTR = 0x04,               // one entry per system interrupt source
-
-  MPSASM = 0x80,
-  MPHIERARCHY   = 0x81,
-  MPCBASM = 0x82,
-
-                        // PCMPprocessor and PCMPioapic flags
-  MPEN = 0x01,                  // enabled
-  MPBP = 0x02,                  // bootstrap processor
-
-                        // PCMPiointr and PCMPlintr flags
-  MPPOMASK = 0x03,              // polarity conforms to bus specs
-  MPHIGH = 0x01,                // active high
-  MPLOW = 0x03,                 // active low
-  MPELMASK = 0x0C,              // trigger mode of APIC input signals
-  MPEDGE = 0x04,                // edge-triggered
-  MPLEVEL = 0x0C,               // level-triggered
-
-                        // PCMPiointr and PCMPlintr interrupt type
-  MPINT = 0x00,                 // vectored interrupt from APIC Rdt
-  MPNMI = 0x01,                 // non-maskable interrupt
-  MPSMI = 0x02,                 // system management interrupt
-  MPExtINT = 0x03,              // vectored interrupt from external PIC
-};
-
-// Common bits for
-//      I/O APIC Redirection Table Entry;
-//      Local APIC Local Interrupt Vector Table;
-//      Local APIC Inter-Processor Interrupt;
-//      Local APIC Timer Vector Table.
-enum {
-  APIC_FIXED     = 0x00000000,  // [10:8] Delivery Mode
-  APIC_LOWEST    = 0x00000100,  // Lowest priority
-  APIC_SMI       = 0x00000200,  // System Management Interrupt
-  APIC_RR        = 0x00000300,  // Remote Read
-  APIC_NMI       = 0x00000400,
-  APIC_INIT      = 0x00000500,  // INIT/RESET
-  APIC_STARTUP   = 0x00000600,  // Startup IPI
-  APIC_EXTINT    = 0x00000700,
-
-  APIC_PHYSICAL  = 0x00000000,  // [11] Destination Mode (RW)
-  APIC_LOGICAL   = 0x00000800,
-
-  APIC_DELIVS    = 0x00001000,  // [12] Delivery Status (RO)
-  APIC_HIGH      = 0x00000000,  // [13] Interrupt Input Pin Polarity (RW)
-  APIC_LOW       = 0x00002000,
-  APIC_REMOTEIRR = 0x00004000,  // [14] Remote IRR (RO)
-  APIC_EDGE      = 0x00000000,  // [15] Trigger Mode (RW)
-  APIC_LEVEL     = 0x00008000,
-  APIC_IMASK     = 0x00010000,  // [16] Interrupt Mask
-};