// 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]; int ncpu; uchar ioapicid; 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; int ismp; struct mp *mp; struct mpconf *conf; struct mpproc *proc; struct mpioapic *ioapic; if ((conf = mpconfig(&mp)) == 0) { panic("Expect to run on an SMP"); } 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 < NCPU) { cpus[ncpu].apicid = proc->apicid; // apicid may differ from 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: ismp = 0; break; } } if (!ismp) { panic("Didn't find a suitable machine"); } 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. } }