int mpintrenable(Vctl* v) { int irq, tbdf, vno; /* * If the bus is known, try it. * BUSUNKNOWN is given both by [E]ISA devices and by * interrupts local to the processor (local APIC, coprocessor * breakpoint and page-fault). */ tbdf = v->tbdf; if(tbdf != BUSUNKNOWN && (vno = mpintrenablex(v, tbdf)) != -1) return vno; irq = v->irq; if(irq >= IrqLINT0 && irq <= MaxIrqLAPIC){ if(irq != IrqSPURIOUS) v->isr = lapiceoi; return VectorPIC+irq; } if(irq < 0 || irq > MaxIrqPIC){ print("mpintrenable: irq %d out of range\n", irq); return -1; } /* * Either didn't find it or have to try the default buses * (ISA and EISA). This hack is due to either over-zealousness * or laziness on the part of some manufacturers. * * The MP configuration table on some older systems * (e.g. ASUS PCI/E-P54NP4) has an entry for the EISA bus * but none for ISA. It also has the interrupt type and * polarity set to 'default for this bus' which wouldn't * be compatible with ISA. */ if(mpeisabus != -1){ vno = mpintrenablex(v, MKBUS(BusEISA, 0, 0, 0)); if(vno != -1) return vno; } if(mpisabus != -1){ vno = mpintrenablex(v, MKBUS(BusISA, 0, 0, 0)); if(vno != -1) return vno; } print("mpintrenable: out of choices eisa %d isa %d tbdf %#ux irq %d\n", mpeisabus, mpisabus, v->tbdf, v->irq); return -1; }
static int32_t irqmapwrite(Chan* c, void *buf, int32_t n, int64_t offset) { int acpiirq(uint32_t tbdf, int irq); int t, b, d, f, irq; int *p[] = {&t, &b, &d, &f, &irq}; Cmdbuf *cb; cb = parsecmd(buf, n); if (cb->nf < nelem(p)) error("iprqmapwrite t b d f irq"); for(int i = 0; i < nelem(p); i++) *p[i] = strtoul(cb->f[i], 0, 0); acpiirq(MKBUS(t, b, d, f), irq); return -1; }
static int32_t acpiwrite(Chan *c, void *a, int32_t n, int64_t off) { Mach *m = machp(); Cmdtab *ct; Cmdbuf *cb; Reg *r; uint rno, fun, dev, bus, i; if(c->qid.path == Qio){ if(reg == nil) error("region not configured"); return regio(reg, a, n, off, 1); } if(c->qid.path != Qctl) error(Eperm); cb = parsecmd(a, n); if(waserror()){ free(cb); nexterror(); } ct = lookupcmd(cb, ctls, nelem(ctls)); DBG("acpi ctl %s\n", cb->f[0]); switch(ct->index){ case CMregion: r = reg; if(r == nil){ r = smalloc(sizeof(Reg)); r->name = nil; } kstrdup(&r->name, cb->f[1]); r->spc = acpiregid(cb->f[2]); if(r->spc < 0){ free(r); reg = nil; error("bad region type"); } if(r->spc == Rpcicfg || r->spc == Rpcibar){ rno = r->base>>Rpciregshift & Rpciregmask; fun = r->base>>Rpcifunshift & Rpcifunmask; dev = r->base>>Rpcidevshift & Rpcidevmask; bus = r->base>>Rpcibusshift & Rpcibusmask; r->tbdf = MKBUS(BusPCI, bus, dev, fun); r->base = rno; /* register ~ our base addr */ } r->base = strtoull(cb->f[3], nil, 0); r->len = strtoull(cb->f[4], nil, 0); r->accsz = strtoul(cb->f[5], nil, 0); if(r->accsz < 1 || r->accsz > 4){ free(r); reg = nil; error("bad region access size"); } reg = r; DBG("region %s %s %llux %llux sz%d", r->name, acpiregstr(r->spc), r->base, r->len, r->accsz); break; case CMgpe: i = strtoul(cb->f[1], nil, 0); if(i >= ngpes) error("gpe out of range"); kstrdup(&gpes[i].obj, cb->f[2]); DBG("gpe %d %s\n", i, gpes[i].obj); setgpeen(i, 1); break; default: panic("acpi: unknown ctl"); }
static int pcilscan(int bno, char *path, Pcidev** list) { Pcidev *p, *head, *tail; int dno, fno, i, hdt, l, maxfno, maxubn, sbn, tbdf, ubn, capoff; maxubn = bno; head = nil; tail = nil; for(dno = 0; dno <= Maxdev; dno++){ maxfno = 0; for(fno = 0; fno <= maxfno; fno++){ /* * For this possible device, form the * bus+device+function triplet needed to address it * and try to read the vendor and device ID. * If successful, allocate a device struct and * start to fill it in with some useful information * from the device's configuration space. */ tbdf = MKBUS(BusPCI, bno, dno, fno); l = pcicfgrw(tbdf, PciVID, 0, Read, 4); if(l == 0xFFFFFFFF || l == 0) continue; p = malloc(sizeof(*p)); p->caplist = nil; p->capidx = nil; p->capcnt = 0; p->tbdf = tbdf; p->vid = l; p->did = l>>16; p->path = path; if(pcilist != nil) pcitail->list = p; else pcilist = p; pcitail = p; p->pcr = pcicfgr16(p, PciPCR); p->rid = pcicfgr8(p, PciRID); p->ccrp = pcicfgr8(p, PciCCRp); p->ccru = pcicfgr8(p, PciCCRu); p->ccrb = pcicfgr8(p, PciCCRb); p->cls = pcicfgr8(p, PciCLS); p->ltr = pcicfgr8(p, PciLTR); p->intl = pcicfgr8(p, PciINTL); p->intp = pcicfgr8(p, PciINTP); /* * If the device is a multi-function device adjust the * loop count so all possible functions are checked. */ hdt = pcicfgr8(p, PciHDT); if(hdt & 0x80) maxfno = Maxfn; /* * If appropriate, read the base address registers * and work out the sizes. */ switch(p->ccrb) { default: if((hdt & 0x7F) != 0) break; for(i = 0; i < nelem(p->mem); i++) { p->mem[i].bar = pcicfgr32(p, PciBAR0+4*i); p->mem[i].size = pcibarsize(p, PciBAR0+4*i); } break; /* * Some virtio-pci devices have ccrb == 0x00, their BARs and * sizes also should be picked here. */ case 0x05: /* memory controller */ case 0x06: /* bridge device */ break; } /* * Try to gather PCI capabilities. If the offset of capabilities * in the config area cannot be found, skip this step. For simplicity, * capabilities will be linked in a LIFO so we don't deal with list * heads and tails. */ capoff = pcicapoff(p); int off = capoff; while(capoff != -1) { off = pcicfgr8(p, off); if((off < 0x40) || (off & 3)) break; off &= ~3; Pcicap *c = malloc(sizeof(*c)); c->dev = p; c->link = p->caplist; p->caplist = c; p->capcnt++; c->vndr = pcicfgr8(p, off + PciCapVndr); c->caplen = pcicfgr8(p, off + PciCapLen); c->type = pcicfgr8(p, off + PciCapType); c->bar = pcicfgr8(p, off + PciCapBar); c->offset = pcicfgr32(p, off + PciCapOff); c->length = pcicfgr32(p, off + PciCapLength); off++; } if(p->capcnt > 0) { p->capidx = malloc(p->capcnt * sizeof(Pcicap *)); Pcicap *pcp = p->caplist; for(int pix = 0; ; pix++) { p->capidx[pix] = pcp; pcp = pcp->link; if(pcp == nil) break; } } if(head != nil) tail->link = p; else head = p; tail = p; } } *list = head; for(p = head; p != nil; p = p->link) { /* * Find PCI-PCI bridges and recursively descend the tree. */ if(p->ccrb != 0x06 || p->ccru != 0x04) continue; /* * If the secondary or subordinate bus number is not * initialised try to do what the PCI BIOS should have * done and fill in the numbers as the tree is descended. * On the way down the subordinate bus number is set to * the maximum as it's not known how many buses are behind * this one; the final value is set on the way back up. */ sbn = pcicfgr8(p, PciSBN); ubn = pcicfgr8(p, PciUBN); if(sbn == 0 || ubn == 0) { print("%T: unconfigured bridge\n", p->tbdf); sbn = maxubn+1; /* * Make sure memory, I/O and master enables are * off, set the primary, secondary and subordinate * bus numbers and clear the secondary status before * attempting to scan the secondary bus. * * Initialisation of the bridge should be done here. */ pcicfgw32(p, PciPCR, 0xFFFF0000); pcicfgw32(p, PciPBN, Maxbus<<16 | sbn<<8 | bno); pcicfgw16(p, PciSPSR, 0xFFFF); char *bus = mallocz(256, 1); snprint(bus, 256, "%s/%d.%d.0", path, BUSBNO(p->tbdf), BUSDNO(p->tbdf)); maxubn = pcilscan(sbn, bus, &p->bridge); pcicfgw32(p, PciPBN, maxubn<<16 | sbn<<8 | bno); } else { /* * You can't go back. * This shouldn't be possible, but the * Iwill DK8-HTX seems to have subordinate * bus numbers which get smaller on the * way down. Need to look more closely at * this. */ if(ubn > maxubn) { maxubn = ubn; } char *bus = mallocz(256, 1); snprint(bus, 256, "%s/%d.%d.0", path, BUSBNO(p->tbdf), BUSDNO(p->tbdf)); pcilscan(sbn, bus, &p->bridge); } } return maxubn; }
static void pcicfginit(void) { int fd, i, j, n, bno, dno, fno; char buf[1024], *base, *s; Pcidev *p; Dir *d; trace("pcicfginit\n"); if((fd = open(base = "/dev/pci", OREAD)) < 0) if((fd = open(base = "#$/pci", OREAD)) < 0) return; n = dirreadall(fd, &d); close(fd); for(i=0; i<n; i++) { if(strstr(d[i].name, "ctl") == nil) continue; strncpy(buf, d[i].name, sizeof(buf)); bno = strtoul(buf, &s, 10); dno = strtoul(s+1, &s, 10); fno = strtoul(s+1, nil, 10); p = mallocz(sizeof(*p), 1); p->tbdf = MKBUS(BusPCI, bno, dno, fno); sprint(buf, "%s/%d.%d.%draw", base, bno, dno, fno); if((p->rawfd = open(buf, ORDWR)) < 0){ free(p); continue; } sprint(buf, "%s/%d.%d.%dctl", base, bno, dno, fno); if((fd = open(buf, OREAD)) < 0){ close(p->rawfd); free(p); continue; } if((j = read(fd, buf, sizeof(buf)-1)) <= 0){ close(p->rawfd); close(fd); free(p); continue; } buf[j] = 0; close(fd); p->ccrb = strtol(buf, nil, 16); p->ccru = strtol(buf + 3, nil, 16); p->vid = strtol(buf + 9, &s, 16); p->did = strtol(s + 1, &s, 16); p->intl = strtol(s + 1, &s, 10); p->rid = pcicfgr8(p, PciRID); trace("%d.%d.%d: did=%X vid=%X rid=%X intl=%d ccru=%X\n", bno, dno, fno, p->did, p->vid, p->rid, p->intl, p->ccru); while(*s == ' '){ j = strtol(s+1, &s, 10); if(j < 0 || j >= nelem(p->mem)) break; p->mem[j].bar = strtoul(s+1, &s, 16); p->mem[j].size = strtoul(s+1, &s, 10); trace("\tmem[%d] = %p %d\n", j, p->mem[j].bar, p->mem[j].size); } if(pcilist != nil) pcitail->list = p; else pcilist = p; pcitail = p; } }