diff options
author | cinap_lenrek <cinap_lenrek@gmx.de> | 2012-06-17 23:12:19 +0200 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@gmx.de> | 2012-06-17 23:12:19 +0200 |
commit | a47521a3ede91ef06bf7938722c490893d5458f6 (patch) | |
tree | 9337bcdee5c2cea704a9a28652abb5b873947c32 /sys/src/9/pc/archmp.c | |
parent | 9cb66f310b16b51526fd3f5480b69a54589e229e (diff) |
experimental acpi support for apic irq routing
Diffstat (limited to 'sys/src/9/pc/archmp.c')
-rw-r--r-- | sys/src/9/pc/archmp.c | 392 |
1 files changed, 355 insertions, 37 deletions
diff --git a/sys/src/9/pc/archmp.c b/sys/src/9/pc/archmp.c index 1df3c3fbc..7d72e0462 100644 --- a/sys/src/9/pc/archmp.c +++ b/sys/src/9/pc/archmp.c @@ -7,7 +7,352 @@ #include "mp.h" -_MP_ *_mp_; +static PCMP *pcmp; + +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) +{ + 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; + + 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; + } + 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 Apic* +mkioapic(PCMPioapic* p) +{ + void *va; + int apicno; + Apic *apic; + + apicno = p->apicno; + if(!(p->flags & PcmpEN) || apicno > MaxAPICNO || mpioapic[apicno] != nil) + return 0; + /* + * Map the I/O APIC. + */ + 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{ + if(apic = mpapic[p->apicno]) + if(apic->flags & PcmpEN) + apic->lintr[intin] = v; + } + + 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 = atoi(getconf("*mp")); + 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 %.8lux\n", pcmp->lapicbase, (ulong)va); + if(va == nil) + panic("pcmpinit: cannot map lapic %.8lux", pcmp->lapicbase); + + p = ((uchar*)pcmp)+sizeof(PCMP); + 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%luX)\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 += sizeof(PCMPprocessor); + continue; + + case PcmpBUS: + mkbus((PCMPbus*)p); + p += sizeof(PCMPbus); + continue; + + case PcmpIOAPIC: + if(apic = mkioapic((PCMPioapic*)p)) + ioapicinit(apic, apic->apicno); + p += sizeof(PCMPioapic); + continue; + + case PcmpIOINTR: + mkiointr((PCMPintr*)p); + p += sizeof(PCMPintr); + continue; + + case PcmpLINTR: + mklintr((PCMPintr*)p); + p += sizeof(PCMPintr); + continue; + } + + /* + * Ininitalize local APIC and start application processors. + */ + mpinit(); +} static _MP_* mpscan(uchar *addr, int len) @@ -60,7 +405,7 @@ PCArch archmp = { .id= "_MP_", .ident= identify, .reset= mpshutdown, -.intrinit= mpinit, +.intrinit= pcmpinit, .intrenable= mpintrenable, .intron= lapicintron, .introff= lapicintroff, @@ -72,7 +417,7 @@ static int identify(void) { char *cp; - PCMP *pcmp; + _MP_ *_mp_; uchar *p, sum; ulong length; @@ -90,50 +435,23 @@ identify(void) return 1; pcmp = KADDR(_mp_->physaddr); - if(memcmp(pcmp, "PCMP", 4)) + if(memcmp(pcmp, "PCMP", 4)){ + pcmp = nil; return 1; + } length = pcmp->length; sum = 0; for(p = (uchar*)pcmp; length; length--) sum += *p++; - if(sum || (pcmp->version != 1 && pcmp->version != 4)) + if(sum || (pcmp->version != 1 && pcmp->version != 4)){ + 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); - } -} - -uvlong -tscticks(uvlong *hz) -{ - if(hz != nil) - *hz = m->cpuhz; - - cycles(&m->tscticks); /* Uses the rdtsc instruction */ - return m->tscticks; + return 0; } |