text: // Multiprocessor support
  // Search memory for MP description structures.
  // http://developer.intel.com/design/pentium/datashts/24201606.pdf
  
  #include "types.h"
  #include "defs.h"
  #include "param.h"
  #include "memlayout.h"
  #include "mp.h"
  #include "x86.h"
  #include "mmu.h"
  #include "proc.h"
  
  struct cpu cpus[NCPU];
  static struct cpu *bcpu;
  int ismp;
  int ncpu;
  uchar ioapicid;
  
  int
  mpbcpu(void) {
  	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 *
  mpsearch1(uint a, int len) {
  	uchar *e, *p, *addr;
  
  	addr = p2v(a);
  	e = addr + len;
  
  	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;
  }
  
  // Search for the MP Floating Pointer Structure, which according to the
  // spec is in one of the following three locations:
  // 1) in the first KB of the EBDA;
  // 2) in the last KB of system base memory;
  // 3) in the BIOS ROM between 0xE0000 and 0xFFFFF.
  static struct mp *
  mpsearch(void) {
  	uchar *bda;
  	uint p;
  	struct mp *mp;
  
  	bda = (uchar *) P2V(0x400);
  
  	if ((p = ((bda[0x0F] << 8) | bda[0x0E]) << 4)) {
  		if ((mp = mpsearch1(p, 1024))) {
  			return mp;
  		}
  	} else {
  		p = ((bda[0x14] << 8) | bda[0x13]) * 1024;
  
  		if ((mp = mpsearch1(p - 1024, 1024))) {
  			return mp;
  		}
  	}
  
  	return mpsearch1(0xF0000, 0x10000);
  }
  
  // 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 struct mpconf *
  mpconfig(struct mp **pmp) {
  	struct mpconf *conf;
  	struct mp *mp;
  
  	if ((mp = mpsearch()) == 0 || mp->physaddr == 0) {
  		return 0;
  	}
  
  	conf = (struct mpconf *) p2v((uint) 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
  mpinit(void) {
  	uchar *p, *e;
  	struct mp *mp;
  	struct mpconf *conf;
  	struct mpproc *proc;
  	struct mpioapic *ioapic;
  
  	bcpu = &cpus[0];
  
  	if ((conf = mpconfig(&mp)) == 0) {
  		return;
  	}
  
  	ismp = 1;
  	lapic = (uint *)conf->lapicaddr;
  
  	for (p = (uchar *)(conf + 1), e = (uchar *)conf + conf->length; p < e;) {
  		switch (*p) {
  		case MPPROC:
  			proc = (struct mpproc *)p;
  
  			if (ncpu != proc->apicid) {
  				cprintf("mpinit: ncpu=%d apicid=%d\n", ncpu, proc->apicid);
  				ismp = 0;
  			}
  
  			if (proc->flags & MPBOOT) {
  				bcpu = &cpus[ncpu];
  			}
  
  			cpus[ncpu].id = ncpu;
  			ncpu++;
  			p += sizeof(struct mpproc);
  			continue;
  
  		case MPIOAPIC:
  			ioapic = (struct mpioapic *)p;
  			ioapicid = ioapic->apicno;
  			p += sizeof(struct mpioapic);
  			continue;
  
  		case MPBUS:
  		case MPIOINTR:
  		case MPLINTR:
  			p += 8;
  			continue;
  
  		default:
  			cprintf("mpinit: unknown config type %x\n", *p);
  			ismp = 0;
  		}
  	}
  
  	if (!ismp) {
  		// Didn't like what we found; fall back to no MP.
  		ncpu = 1;
  		lapic = 0;
  		ioapicid = 0;
  		return;
  	}
  
  	if (mp->imcrp) {
  		// Bochs doesn't support IMCR, so this doesn't run on Bochs.
  		// But it would on real hardware.
  		outb(0x22, 0x70);   // Select IMCR
  		outb(0x23, inb(0x23) | 1);  // Mask external interrupts.
  	}
  }
  