123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "apic.h"
- #include "io.h"
- typedef struct Rbus Rbus;
- typedef struct Rdt Rdt;
- struct Rbus {
- Rbus *next;
- int devno;
- Rdt *rdt;
- };
- struct Rdt {
- Apic *apic;
- int intin;
- uint32_t lo;
- int ref; /* could map to multiple busses */
- int enabled; /* times enabled */
- };
- enum { /* IOAPIC registers */
- Ioregsel = 0x00, /* indirect register address */
- Iowin = 0x04, /* indirect register data */
- Ioipa = 0x08, /* IRQ Pin Assertion */
- Ioeoi = 0x10, /* EOI */
- Ioapicid = 0x00, /* Identification */
- Ioapicver = 0x01, /* Version */
- Ioapicarb = 0x02, /* Arbitration */
- Ioabcfg = 0x03, /* Boot Coniguration */
- Ioredtbl = 0x10, /* Redirection Table */
- };
- static Rdt rdtarray[Nrdt];
- static int nrdtarray;
- static int gsib;
- static Rbus* rdtbus[Nbus];
- static Rdt* rdtvecno[IdtMAX+1];
- static Lock idtnolock;
- static int idtno = IdtIOAPIC;
- Apic xioapic[Napic];
- static void
- rtblget(Apic* apic, int sel, uint32_t* hi, uint32_t* lo)
- {
- sel = Ioredtbl + 2*sel;
- *(apic->addr+Ioregsel) = sel+1;
- *hi = *(apic->addr+Iowin);
- *(apic->addr+Ioregsel) = sel;
- *lo = *(apic->addr+Iowin);
- }
- static void
- rtblput(Apic* apic, int sel, uint32_t hi, uint32_t lo)
- {
- sel = Ioredtbl + 2*sel;
- *(apic->addr+Ioregsel) = sel+1;
- *(apic->addr+Iowin) = hi;
- *(apic->addr+Ioregsel) = sel;
- *(apic->addr+Iowin) = lo;
- }
- Rdt*
- rdtlookup(Apic *apic, int intin)
- {
- int i;
- Rdt *r;
- for(i = 0; i < nrdtarray; i++){
- r = rdtarray + i;
- if(apic == r->apic && intin == r->intin)
- return r;
- }
- return nil;
- }
- void
- ioapicintrinit(int busno, int apicno, int intin, int devno, uint32_t lo)
- {
- Rbus *rbus;
- Rdt *rdt;
- Apic *apic;
- if(busno >= Nbus || apicno >= Napic || nrdtarray >= Nrdt)
- return;
- apic = &xioapic[apicno];
- if(!apic->useable || intin >= apic->nrdt)
- return;
- rdt = rdtlookup(apic, intin);
- if(rdt == nil){
- rdt = &rdtarray[nrdtarray++];
- rdt->apic = apic;
- rdt->intin = intin;
- rdt->lo = lo;
- }else{
- if(lo != rdt->lo){
- print("mutiple irq botch bus %d %d/%d/%d lo %d vs %d\n",
- busno, apicno, intin, devno, lo, rdt->lo);
- return;
- }
- DBG("dup rdt %d %d %d %d %.8ux\n", busno, apicno, intin, devno, lo);
- }
- rdt->ref++;
- rbus = malloc(sizeof *rbus);
- rbus->rdt = rdt;
- rbus->devno = devno;
- rbus->next = rdtbus[busno];
- rdtbus[busno] = rbus;
- }
- void
- ioapicinit(int id, uintptr_t pa)
- {
- Apic *apic;
- /*
- * Mark the IOAPIC useable if it has a good ID
- * and the registers can be mapped.
- */
- if(id >= Napic)
- return;
- apic = &xioapic[id];
- if(apic->useable || (apic->addr = vmap(pa, 1024)) == nil)
- return;
- apic->useable = 1;
- /*
- * Initialise the I/O APIC.
- * The MultiProcessor Specification says it is the
- * responsibility of the O/S to set the APIC ID.
- */
- lock(apic);
- *(apic->addr+Ioregsel) = Ioapicver;
- apic->nrdt = ((*(apic->addr+Iowin)>>16) & 0xff) + 1;
- apic->gsib = gsib;
- gsib += apic->nrdt;
- *(apic->addr+Ioregsel) = Ioapicid;
- *(apic->addr+Iowin) = id<<24;
- unlock(apic);
- }
- void
- ioapicdump(void)
- {
- int i, n;
- Rbus *rbus;
- Rdt *rdt;
- Apic *apic;
- uint32_t hi, lo;
- if(!DBGFLG)
- return;
- for(i = 0; i < Napic; i++){
- apic = &xioapic[i];
- if(!apic->useable || apic->addr == 0)
- continue;
- print("ioapic %d addr %#p nrdt %d gsib %d\n",
- i, apic->addr, apic->nrdt, apic->gsib);
- for(n = 0; n < apic->nrdt; n++){
- lock(apic);
- rtblget(apic, n, &hi, &lo);
- unlock(apic);
- print(" rdt %2.2d %#8.8ux %#8.8ux\n", n, hi, lo);
- }
- }
- for(i = 0; i < Nbus; i++){
- if((rbus = rdtbus[i]) == nil)
- continue;
- print("iointr bus %d:\n", i);
- for(; rbus != nil; rbus = rbus->next){
- rdt = rbus->rdt;
- print(" apic %ld devno %#ux (%d %d) intin %d lo %#ux ref %d\n",
- rdt->apic-xioapic, rbus->devno, rbus->devno>>2,
- rbus->devno & 0x03, rdt->intin, rdt->lo, rdt->ref);
- }
- }
- }
- void
- ioapiconline(void)
- {
- int i;
- Apic *apic;
- for(apic = xioapic; apic < &xioapic[Napic]; apic++){
- if(!apic->useable || apic->addr == nil)
- continue;
- for(i = 0; i < apic->nrdt; i++){
- lock(apic);
- rtblput(apic, i, 0, Im);
- unlock(apic);
- }
- }
- ioapicdump();
- }
- static int dfpolicy = 0;
- static void
- ioapicintrdd(uint32_t* hi, uint32_t* lo)
- {
- int i;
- static int df;
- static Lock dflock;
- /*
- * Set delivery mode (lo) and destination field (hi),
- * according to interrupt routing policy.
- */
- /*
- * The bulk of this code was written ~1995, when there was
- * one architecture and one generation of hardware, the number
- * of CPUs was up to 4(8) and the choices for interrupt routing
- * were physical, or flat logical (optionally with lowest
- * priority interrupt). Logical mode hasn't scaled well with
- * the increasing number of packages/cores/threads, so the
- * fall-back is to physical mode, which works across all processor
- * generations, both AMD and Intel, using the APIC and xAPIC.
- *
- * Interrupt routing policy can be set here.
- */
- switch(dfpolicy){
- default: /* noise core 0 */
- *hi = sys->machptr[0]->apicno<<24;
- break;
- case 1: /* round-robin */
- /*
- * Assign each interrupt to a different CPU on a round-robin
- * Some idea of the packages/cores/thread topology would be
- * useful here, e.g. to not assign interrupts to more than one
- * thread in a core. But, as usual, Intel make that an onerous
- * task.
- */
- lock(&dflock);
- for(;;){
- i = df++;
- if(df >= sys->nmach+1)
- df = 0;
- if(sys->machptr[i] == nil || !sys->machptr[i]->online)
- continue;
- i = sys->machptr[i]->apicno;
- if(xlapic[i].useable && xlapic[i].addr == 0)
- break;
- }
- unlock(&dflock);
-
- *hi = i<<24;
- break;
- }
- *lo |= Pm|MTf;
- }
- int
- nextvec(void)
- {
- uint vecno;
- lock(&idtnolock);
- vecno = idtno;
- idtno = (idtno+8) % IdtMAX;
- if(idtno < IdtIOAPIC)
- idtno += IdtIOAPIC;
- unlock(&idtnolock);
- return vecno;
- }
- static int
- msimask(Vkey *v, int mask)
- {
- Pcidev *p;
- p = pcimatchtbdf(v->tbdf);
- if(p == nil)
- return -1;
- return pcimsimask(p, mask);
- }
- static int
- intrenablemsi(Vctl* v, Pcidev *p)
- {
- uint vno, lo, hi;
- uint64_t msivec;
- vno = nextvec();
- lo = IPlow | TMedge | vno;
- ioapicintrdd(&hi, &lo);
- if(lo & Lm)
- lo |= MTlp;
- msivec = (uint64_t)hi<<32 | lo;
- if(pcimsienable(p, msivec) == -1)
- return -1;
- v->isr = apicisr;
- v->eoi = apiceoi;
- v->vno = vno;
- v->type = "msi";
- v->mask = msimask;
- DBG("msiirq: %T: enabling %.16llux %s irq %d vno %d\n", p->tbdf, msivec, v->name, v->irq, vno);
- return vno;
- }
- int
- disablemsi(Vctl* v, Pcidev *p)
- {
- if(p == nil)
- return -1;
- return pcimsimask(p, 1);
- }
- int
- ioapicintrenable(Vctl* v)
- {
- Rbus *rbus;
- Rdt *rdt;
- uint32_t hi, lo;
- int busno, devno, vecno;
- /*
- * Bridge between old and unspecified new scheme,
- * the work in progress...
- */
- if(v->tbdf == BUSUNKNOWN){
- if(v->irq >= IrqLINT0 && v->irq <= MaxIrqLAPIC){
- if(v->irq != IrqSPURIOUS)
- v->isr = apiceoi;
- v->type = "lapic";
- return v->irq;
- }
- else{
- /*
- * Legacy ISA.
- * Make a busno and devno using the
- * ISA bus number and the irq.
- */
- extern int mpisabusno;
- if(mpisabusno == -1)
- panic("no ISA bus allocated");
- busno = mpisabusno;
- devno = v->irq<<2;
- }
- }
- else if(BUSTYPE(v->tbdf) == BusPCI){
- /*
- * PCI.
- * Make a devno from BUSDNO(tbdf) and pcidev->intp.
- */
- Pcidev *pcidev;
- busno = BUSBNO(v->tbdf);
- if((pcidev = pcimatchtbdf(v->tbdf)) == nil)
- panic("no PCI dev for tbdf %#8.8ux\n", v->tbdf);
- if((vecno = intrenablemsi(v, pcidev)) != -1)
- return vecno;
- disablemsi(v, pcidev);
- if((devno = pcicfgr8(pcidev, PciINTP)) == 0)
- panic("no INTP for tbdf %#8.8ux\n", v->tbdf);
- devno = BUSDNO(v->tbdf)<<2|(devno-1);
- DBG("ioapicintrenable: tbdf %#8.8ux busno %d devno %d\n",
- v->tbdf, busno, devno);
- }
- else{
- SET(busno); SET(devno);
- panic("unknown tbdf %#8.8ux\n", v->tbdf);
- }
- rdt = nil;
- for(rbus = rdtbus[busno]; rbus != nil; rbus = rbus->next)
- if(rbus->devno == devno){
- rdt = rbus->rdt;
- break;
- }
- if(rdt == nil){
- extern int mpisabusno;
- /*
- * First crack in the smooth exterior of the new code:
- * some BIOS make an MPS table where the PCI devices are
- * just defaulted to ISA.
- * Rewrite this to be cleaner.
- */
- if((busno = mpisabusno) == -1)
- return -1;
- devno = v->irq<<2;
- for(rbus = rdtbus[busno]; rbus != nil; rbus = rbus->next)
- if(rbus->devno == devno){
- rdt = rbus->rdt;
- break;
- }
- DBG("isa: tbdf %#8.8ux busno %d devno %d %#p\n",
- v->tbdf, busno, devno, rdt);
- }
- if(rdt == nil)
- return -1;
- /*
- * Second crack:
- * what to do about devices that intrenable/intrdisable frequently?
- * 1) there is no ioapicdisable yet;
- * 2) it would be good to reuse freed vectors.
- * Oh bugger.
- */
- /*
- * This is a low-frequency event so just lock
- * the whole IOAPIC to initialise the RDT entry
- * rather than putting a Lock in each entry.
- */
- lock(rdt->apic);
- DBG("%T: %ld/%d/%d (%d)\n", v->tbdf, rdt->apic - xioapic, rbus->devno, rdt->intin, devno);
- if((rdt->lo & 0xff) == 0){
- vecno = nextvec();
- rdt->lo |= vecno;
- rdtvecno[vecno] = rdt;
- }else
- DBG("%T: mutiple irq bus %d dev %d\n", v->tbdf, busno, devno);
- rdt->enabled++;
- lo = (rdt->lo & ~Im);
- ioapicintrdd(&hi, &lo);
- rtblput(rdt->apic, rdt->intin, hi, lo);
- vecno = lo & 0xff;
- unlock(rdt->apic);
- DBG("busno %d devno %d hi %#8.8ux lo %#8.8ux vecno %d\n",
- busno, devno, hi, lo, vecno);
- v->isr = apicisr;
- v->eoi = apiceoi;
- v->vno = vecno;
- v->type = "ioapic";
- return vecno;
- }
- int
- ioapicintrdisable(int vecno)
- {
- Rdt *rdt;
- /*
- * FOV. Oh dear. This isn't very good.
- * Fortunately rdtvecno[vecno] is static
- * once assigned.
- * Must do better.
- *
- * What about any pending interrupts?
- */
- if(vecno < 0 || vecno > MaxVectorAPIC){
- panic("ioapicintrdisable: vecno %d out of range", vecno);
- return -1;
- }
- if((rdt = rdtvecno[vecno]) == nil){
- panic("ioapicintrdisable: vecno %d has no rdt", vecno);
- return -1;
- }
- lock(rdt->apic);
- rdt->enabled--;
- if(rdt->enabled == 0)
- rtblput(rdt->apic, rdt->intin, 0, rdt->lo);
- unlock(rdt->apic);
- return 0;
- }
|