static int tbdffmt(Fmt* fmt) { char *p; int l, r; uint type, tbdf; if((p = malloc(READSTR)) == nil) return fmtstrcpy(fmt, "(tbdfconv)"); switch(fmt->r){ case 'T': tbdf = va_arg(fmt->args, uint); type = BUSTYPE(tbdf); if(type < nelem(bustypes)) l = snprint(p, READSTR, bustypes[type]); else l = snprint(p, READSTR, "%d", type); snprint(p+l, READSTR-l, ".%d.%d.%d", BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf)); break; default: snprint(p, READSTR, "(tbdfconv)"); break; } r = fmtstrcpy(fmt, p); free(p); return r; }
void pcishowdev(Pcidev* t) { int i; char intpin = 'x'; /* intpin numbers can range from 1 to 8. */ intpin = "xABCDEFGHxxxxxxx"[t->intp&0xf]; print("%d %2d/%d %.2x %.2x %.2x %.4x %.4x %c %3d ", BUSBNO(t->tbdf), BUSDNO(t->tbdf), BUSFNO(t->tbdf), t->ccrb, t->ccru, t->ccrp, t->vid, t->did, intpin, t->intl); for(i = 0; i < nelem(t->mem); i++) { if(t->mem[i].size == 0) continue; print("%d:%.8llx %d ", i, t->mem[i].bar, t->mem[i].size); } if(t->ioa.bar || t->ioa.size) print("ioa:%.8llx %d ", t->ioa.bar, t->ioa.size); if(t->mema.bar || t->mema.size) if(t->bridge) print("->%d", BUSBNO(t->bridge->tbdf)); print("\n"); }
static int mpintrenablex(Vctl* v, int tbdf) { Bus *bus; Aintr *aintr; Apic *apic; Pcidev *pcidev; int bno, dno, hi, irq, lo, n, type, vno; char *typenm; /* * Find the bus. */ type = BUSTYPE(tbdf); bno = BUSBNO(tbdf); dno = BUSDNO(tbdf); if(type == BusISA) bno = mpisabus; vno = -1; for(bus = mpbus; bus != nil; bus = bus->next){ if(bus->type != type) continue; if(bus->busno == bno) break; } if(bus == nil){ typenm = type < 0 || type >= nelem(buses)? "": buses[type]; print("mpintrenablex: can't find bus type %d (%s) for irq %d " "%s busno %d\n", type, typenm, v->irq, v->name, bno); return -1; } /* * For PCI devices the interrupt pin (INT[ABCD]) and device * number are encoded into the entry irq field, so create something * to match on. The interrupt pin used by the device has to be * obtained from the PCI config space. */ if(bus->type == BusPCI){ pcidev = pcimatchtbdf(tbdf); if(pcidev != nil && (n = pcicfgr8(pcidev, PciINTP)) != 0) irq = (dno<<2)|(n-1); else irq = -1; //print("pcidev %#uX: irq %#uX v->irq %#uX\n", tbdf, irq, v->irq); } else irq = v->irq; /* * Find a matching interrupt entry from the list of interrupts * attached to this bus. */ for(aintr = bus->aintr; aintr; aintr = aintr->next){ if(aintr->intr->irq != irq) continue; if (0) { PCMPintr* p = aintr->intr; print("mpintrenablex: bus %d intin %d irq %d\n", p->busno, p->intin, p->irq); } /* * Check if already enabled. Multifunction devices may share * INT[A-D]# so, if already enabled, check the polarity matches * and the trigger is level. * * Should check the devices differ only in the function number, * but that can wait for the planned enable/disable rewrite. * The RDT read here is safe for now as currently interrupts * are never disabled once enabled. */ apic = aintr->apic; ioapicrdtr(apic, aintr->intr->intin, 0, &lo); if(!(lo & ApicIMASK)){ vno = lo & 0xFF; //print("%s vector %d (!imask)\n", v->name, vno); n = mpintrinit(bus, aintr->intr, vno, v->irq); n |= ApicPHYSICAL; /* no-op */ lo &= ~(ApicRemoteIRR|ApicDELIVS); if(n != lo || !(n & ApicLEVEL)){ print("mpintrenable: multiple botch irq%d, tbdf %uX, lo %8.8uX, n %8.8uX\n", v->irq, tbdf, lo, n); return -1; } break; } /* * With the APIC a unique vector can be assigned to each * request to enable an interrupt. There are two reasons this * is a good idea: * 1) to prevent lost interrupts, no more than 2 interrupts * should be assigned per block of 16 vectors (there is an * in-service entry and a holding entry for each priority * level and there is one priority level per block of 16 * interrupts). * 2) each input pin on the IOAPIC will receive a different * vector regardless of whether the devices on that pin use * the same IRQ as devices on another pin. */ vno = VectorAPIC + (incref(&mpvnoref)-1)*8; //print("%s vector %d (imask)\n", v->name, vno); if(vno > MaxVectorAPIC){ print("mpintrenable: vno %d, irq %d, tbdf %uX\n", vno, v->irq, tbdf); return -1; } hi = mpintrcpu()<<24; lo = mpintrinit(bus, aintr->intr, vno, v->irq); //print("lo 0x%uX: busno %d intr %d vno %d irq %d elcr 0x%uX\n", // lo, bus->busno, aintr->intr->irq, vno, // v->irq, i8259elcr); if(lo & ApicIMASK) return -1; lo |= ApicPHYSICAL; /* no-op */ if((apic->flags & PcmpEN) && apic->type == PcmpIOAPIC) ioapicrdtw(apic, aintr->intr->intin, hi, lo); //else // print("lo not enabled 0x%uX %d\n", // apic->flags, apic->type); break; } if (aintr) { v->isr = lapicisr; v->eoi = lapiceoi; } return vno; }
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; }