#include "mp.h"
-_MP_ *_mp_;
+static PCMP *pcmp;
-static _MP_*
-mpscan(uchar *addr, int len)
+static char* buses[] = {
+ "CBUSI ",
+ "CBUSII",
+ "EISA ",
+ "FUTURE",
+ "INTERN",
+ "ISA ",
+ "MBI ",
+ "MBII ",
+ "MCA ",
+ "MPI ",
+ "MPSA ",
+ "NUBUS ",
+ "PCI ",
+ "PCMCIA",
+ "TC ",
+ "VL ",
+ "VME ",
+ "XPRESS",
+ 0,
+};
+
+static Bus*
+mpgetbus(int busno)
{
- uchar *e, *p, sum;
+ Bus *bus;
+
+ for(bus = mpbus; bus; bus = bus->next)
+ if(bus->busno == busno)
+ return bus;
+
+ print("mpgetbus: can't find bus %d\n", busno);
+ return 0;
+}
+
+static Apic*
+mkprocessor(PCMPprocessor* p)
+{
+ static int machno = 1;
+ int apicno;
+ Apic *apic;
+
+ apicno = p->apicno;
+ if(!(p->flags & PcmpEN) || apicno > MaxAPICNO || mpapic[apicno] != nil)
+ return 0;
+
+ if((apic = xalloc(sizeof(Apic))) == nil)
+ panic("mkprocessor: no memory for Apic");
+ apic->type = PcmpPROCESSOR;
+ apic->apicno = apicno;
+ apic->flags = p->flags;
+ apic->lintr[0] = ApicIMASK;
+ apic->lintr[1] = ApicIMASK;
+ if(p->flags & PcmpBP)
+ apic->machno = 0;
+ else
+ apic->machno = machno++;
+ mpapic[apicno] = apic;
+
+ return apic;
+}
+
+static Bus*
+mkbus(PCMPbus* p)
+{
+ Bus *bus;
int i;
- e = addr+len;
- for(p = addr; p < e; p += sizeof(_MP_)){
- if(memcmp(p, "_MP_", 4))
- continue;
- sum = 0;
- for(i = 0; i < sizeof(_MP_); i++)
- sum += p[i];
- if(sum == 0)
- return (_MP_*)p;
+ for(i = 0; buses[i]; i++)
+ if(strncmp(buses[i], p->string, sizeof(p->string)) == 0)
+ break;
+ if(buses[i] == 0)
+ return 0;
+
+ if((bus = xalloc(sizeof(Bus))) == nil)
+ panic("mkbus: no memory for Bus");
+ if(mpbus)
+ mpbuslast->next = bus;
+ else
+ mpbus = bus;
+ mpbuslast = bus;
+
+ bus->type = i;
+ bus->busno = p->busno;
+ if(bus->type == BusEISA){
+ bus->po = PcmpLOW;
+ bus->el = PcmpLEVEL;
+ if(mpeisabus != -1)
+ print("mkbus: more than one EISA bus\n");
+ mpeisabus = bus->busno;
}
- return 0;
+ else if(bus->type == BusPCI){
+ bus->po = PcmpLOW;
+ bus->el = PcmpLEVEL;
+ }
+ else if(bus->type == BusISA){
+ bus->po = PcmpHIGH;
+ bus->el = PcmpEDGE;
+ if(mpisabus != -1)
+ print("mkbus: more than one ISA bus\n");
+ mpisabus = bus->busno;
+ }
+ else{
+ bus->po = PcmpHIGH;
+ bus->el = PcmpEDGE;
+ }
+
+ return bus;
}
-static _MP_*
-mpsearch(void)
+static Apic*
+mkioapic(PCMPioapic* p)
{
- uchar *bda;
- ulong p;
- _MP_ *mp;
+ void *va;
+ int apicno;
+ Apic *apic;
+ apicno = p->apicno;
+ if(!(p->flags & PcmpEN) || apicno > MaxAPICNO || mpioapic[apicno] != nil)
+ return 0;
/*
- * Search for the MP Floating Pointer Structure:
- * 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.
+ * Map the I/O APIC.
*/
- bda = KADDR(0x400);
- if((p = (bda[0x0F]<<8)|bda[0x0E])){
- if(mp = mpscan(KADDR(p<<4), 1024))
- return mp;
+ if((va = vmap(p->addr, 1024)) == nil)
+ return 0;
+ if((apic = xalloc(sizeof(Apic))) == nil)
+ panic("mkioapic: no memory for Apic");
+ apic->type = PcmpIOAPIC;
+ apic->apicno = apicno;
+ apic->addr = va;
+ apic->paddr = p->addr;
+ apic->flags = p->flags;
+ mpioapic[apicno] = apic;
+
+ return apic;
+}
+
+static Aintr*
+mkiointr(PCMPintr* p)
+{
+ Bus *bus;
+ Aintr *aintr;
+ PCMPintr* pcmpintr;
+
+ /*
+ * According to the MultiProcessor Specification, a destination
+ * I/O APIC of 0xFF means the signal is routed to all I/O APICs.
+ * It's unclear how that can possibly be correct so treat it as
+ * an error for now.
+ */
+ if(p->apicno > MaxAPICNO || mpioapic[p->apicno] == nil)
+ return 0;
+
+ if((bus = mpgetbus(p->busno)) == 0)
+ return 0;
+
+ if((aintr = xalloc(sizeof(Aintr))) == nil)
+ panic("mkiointr: no memory for Aintr");
+ aintr->intr = p;
+
+ if(0)
+ print("mkiointr: type %d intr type %d flags %#o "
+ "bus %d irq %d apicno %d intin %d\n",
+ p->type, p->intr, p->flags,
+ p->busno, p->irq, p->apicno, p->intin);
+ /*
+ * Hack for Intel SR1520ML motherboard, which BIOS describes
+ * the i82575 dual ethernet controllers incorrectly.
+ */
+ if(memcmp(pcmp->product, "INTEL X38MLST ", 20) == 0){
+ if(p->busno == 1 && p->intin == 16 && p->irq == 1){
+ if((pcmpintr = xalloc(sizeof(PCMPintr))) == nil)
+ panic("iointr: no memory for PCMPintr");
+ memmove(pcmpintr, p, sizeof(PCMPintr));
+ print("mkiointr: %20.20s bus %d intin %d irq %d\n",
+ (char*)pcmp->product,
+ pcmpintr->busno, pcmpintr->intin,
+ pcmpintr->irq);
+ pcmpintr->intin = 17;
+ aintr->intr = pcmpintr;
+ }
+ }
+ aintr->apic = mpioapic[p->apicno];
+ aintr->next = bus->aintr;
+ bus->aintr = aintr;
+
+ return aintr;
+}
+
+static int
+mklintr(PCMPintr* p)
+{
+ Apic *apic;
+ Bus *bus;
+ int i, intin, v;
+
+ /*
+ * The offsets of vectors for LINT[01] are known to be
+ * 0 and 1 from the local APIC vector space at VectorLAPIC.
+ */
+ if((bus = mpgetbus(p->busno)) == 0)
+ return 0;
+ intin = p->intin;
+
+ /*
+ * Pentium Pros have problems if LINT[01] are set to ExtINT
+ * so just bag it, SMP mode shouldn't need ExtINT anyway.
+ */
+ if(p->intr == PcmpExtINT || p->intr == PcmpNMI)
+ v = ApicIMASK;
+ else
+ v = mpintrinit(bus, p, VectorLAPIC+intin, p->irq);
+
+ if(p->apicno == 0xFF){
+ for(i=0; i<=MaxAPICNO; i++){
+ if((apic = mpapic[i]) == nil)
+ continue;
+ if(apic->flags & PcmpEN)
+ apic->lintr[intin] = v;
+ }
}
else{
- p = ((bda[0x14]<<8)|bda[0x13])*1024;
- if(mp = mpscan(KADDR(p-1024), 1024))
- return mp;
+ if(apic = mpapic[p->apicno])
+ if(apic->flags & PcmpEN)
+ apic->lintr[intin] = v;
}
- return mpscan(KADDR(0xF0000), 0x10000);
+
+ return v;
+}
+
+static void
+dumpmp(uchar *p, uchar *e)
+{
+ int i;
+
+ for(i = 0; p < e; p++) {
+ if((i % 16) == 0) print("*mp%d=", i/16);
+ print("%.2x ", *p);
+ if((++i % 16) == 0) print("\n");
+ }
+ if((i % 16) != 0) print("\n");
+}
+
+
+static void
+mpoverride(uchar** newp, uchar** e)
+{
+ int size, i, j;
+ char buf[20];
+ uchar* p;
+ char* s;
+
+ size = strtol(getconf("*mp"), 0, 0);
+ if(size <= 0) panic("mpoverride: invalid size in *mp");
+ *newp = p = xalloc(size);
+ if(p == nil) panic("mpoverride: can't allocate memory");
+ *e = p + size;
+ for(i = 0; ; i++){
+ snprint(buf, sizeof buf, "*mp%d", i);
+ s = getconf(buf);
+ if(s == nil) break;
+ while(*s){
+ j = strtol(s, &s, 16);
+ if(*s && *s != ' ' || j < 0 || j > 0xff) panic("mpoverride: invalid entry in %s", buf);
+ if(p >= *e) panic("mpoverride: overflow in %s", buf);
+ *p++ = j;
+ }
+ }
+ if(p != *e) panic("mpoverride: size doesn't match");
+}
+
+static void
+pcmpinit(void)
+{
+ uchar *p, *e;
+ Apic *apic;
+ void *va;
+
+ /*
+ * Map the local APIC.
+ */
+ va = vmap(pcmp->lapicbase, 1024);
+
+ print("LAPIC: %.8lux %#p\n", pcmp->lapicbase, va);
+ if(va == nil)
+ panic("pcmpinit: cannot map lapic %.8lux", pcmp->lapicbase);
+
+ p = ((uchar*)pcmp)+PCMPsz;
+ e = ((uchar*)pcmp)+pcmp->length;
+ if(getconf("*dumpmp") != nil)
+ dumpmp(p, e);
+ if(getconf("*mp") != nil)
+ mpoverride(&p, &e);
+
+ /*
+ * 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.
+ */
+ while(p < e) switch(*p){
+ default:
+ print("pcmpinit: unknown PCMP type 0x%uX (e-p 0x%zuX)\n",
+ *p, e-p);
+ while(p < e){
+ print("%uX ", *p);
+ p++;
+ }
+ break;
+
+ case PcmpPROCESSOR:
+ if(apic = mkprocessor((PCMPprocessor*)p)){
+ apic->addr = va;
+ apic->paddr = pcmp->lapicbase;
+ }
+ p += PCMPprocessorsz;
+ continue;
+
+ case PcmpBUS:
+ mkbus((PCMPbus*)p);
+ p += PCMPbussz;
+ continue;
+
+ case PcmpIOAPIC:
+ if(apic = mkioapic((PCMPioapic*)p))
+ ioapicinit(apic, apic->apicno);
+ p += PCMPioapicsz;
+ continue;
+
+ case PcmpIOINTR:
+ mkiointr((PCMPintr*)p);
+ p += PCMPintrsz;
+ continue;
+
+ case PcmpLINTR:
+ mklintr((PCMPintr*)p);
+ p += PCMPintrsz;
+ continue;
+ }
+
+ /*
+ * Ininitalize local APIC and start application processors.
+ */
+ mpinit();
+}
+
+static void
+mpreset(void)
+{
+ /* stop application processors */
+ mpshutdown();
+
+ /* do generic reset */
+ archreset();
}
static int identify(void);
PCArch archmp = {
.id= "_MP_",
.ident= identify,
-.reset= mpshutdown,
-.intrinit= mpinit,
+.reset= mpreset,
+.intrinit= pcmpinit,
.intrenable= mpintrenable,
.intron= lapicintron,
.introff= lapicintroff,
identify(void)
{
char *cp;
- PCMP *pcmp;
- uchar *p, sum;
- ulong length;
+ _MP_ *_mp_;
+ ulong pa, len;
- if((cp = getconf("*nomp")) != nil && strtol(cp, 0, 0) != 0)
+ if((cp = getconf("*nomp")) != nil && strcmp(cp, "0") != 0)
return 1;
/*
* if correct, check the version.
* To do: check extended table checksum.
*/
- if((_mp_ = mpsearch()) == 0 || _mp_->physaddr == 0)
+ if((_mp_ = sigsearch("_MP_", _MP_sz)) == nil || _mp_->physaddr == 0)
return 1;
- pcmp = KADDR(_mp_->physaddr);
- if(memcmp(pcmp, "PCMP", 4))
+ len = PCMPsz;
+ pa = _mp_->physaddr;
+ if(pa + len-1 < pa)
return 1;
- length = pcmp->length;
- sum = 0;
- for(p = (uchar*)pcmp; length; length--)
- sum += *p++;
-
- if(sum || (pcmp->version != 1 && pcmp->version != 4))
+ memreserve(pa, len);
+ if((pcmp = vmap(pa, len)) == nil)
+ return 1;
+ if(pcmp->length < PCMPsz
+ || pa + pcmp->length-1 < pa
+ || memcmp(pcmp, "PCMP", 4) != 0
+ || (pcmp->version != 1 && pcmp->version != 4)){
+Bad:
+ vunmap(pcmp, len);
+ pcmp = nil;
return 1;
-
- if(cpuserver && m->havetsc)
- archmp.fastclock = tscticks;
- return 0;
-}
-
-Lock mpsynclock;
-
-void
-syncclock(void)
-{
- uvlong x;
-
- if(arch->fastclock != tscticks)
- return;
-
- if(m->machno == 0){
- wrmsr(0x10, 0);
- m->tscticks = 0;
- } else {
- x = MACHP(0)->tscticks;
- while(x == MACHP(0)->tscticks)
- ;
- wrmsr(0x10, MACHP(0)->tscticks);
- cycles(&m->tscticks);
}
-}
+ len = pcmp->length;
+ memreserve(pa, len);
+ vunmap(pcmp, PCMPsz);
+ if((pcmp = vmap(pa, len)) == nil)
+ return 1;
-uvlong
-tscticks(uvlong *hz)
-{
- if(hz != nil)
- *hz = m->cpuhz;
+ if(checksum(pcmp, len) != 0)
+ goto Bad;
- cycles(&m->tscticks); /* Uses the rdtsc instruction */
- return m->tscticks;
+ if(m->havetsc && getconf("*notsc") == nil)
+ archmp.fastclock = tscticks;
+
+ return 0;
}