#include #include #include #include #include #include #include #include #include #include /* ------------------------------------------------------------- */ #define BASEPATH "/sys/bus/pci/devices" void *mapbar(char *dev, int bar, uint32_t offset) { char filename[128]; struct stat st; uint8_t *map; int fd; snprintf(filename, sizeof(filename), "%s/%s/resource%d", BASEPATH, dev, bar); fd = open(filename, O_RDONLY); if (-1 == fd) { fprintf(stderr, "open %s: %s", filename, strerror(errno)); return NULL; } fstat(fd, &st); map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (MAP_FAILED == map) { fprintf(stderr, "mmap %s: %s", filename, strerror(errno)); return NULL; } close(fd); return map + offset; } uint32_t readsys(char *dev, char *file) { char filename[128], content[128]; FILE *fp; snprintf(filename, sizeof(filename), "%s/%s/%s", BASEPATH, dev, file); fp = fopen(filename, "r"); if (NULL == fp) return 0; fgets(content, sizeof(content), fp); fclose(fp); return strtol(content, NULL, 0); } /* ------------------------------------------------------------- */ static const char *xhci_capname[] = { [ 0 ] = "Reserved", [ 1 ] = "USB Legacy Support", [ 2 ] = "Supported Protocol", [ 3 ] = "Extended Power Management", [ 4 ] = "I/O Virtualization", [ 5 ] = "Message Interrupt", [ 6 ] = "Local Memory", [ 7 ... 9 ] = "Reserved", [ 10 ] = "USB Debug Capability", [ 11 ... 16 ] = "Reserved", [ 17 ] = "Extended Message Interrupt", [ 18 ... 191 ] = "Reserved", [ 192 ... 255 ] = "Vendor Defined", }; void xhcidump(char *dev, uint32_t offset) { uint32_t *map, val; int i, n, ext = 0, off, cid; map = mapbar(dev, 0, offset); if (!map) return; n = map[0] & 0xff; if (n > 0x20) n = 0x20; printf(" xhci base caps\n"); for (i = 0; i * 4 < n; i++) { val = map[i]; printf(" 0x%04x: 0x%08x", i * 4, val); switch (i * 4) { case 0x00: printf(" - version %x.%02x, caplength 0x%x", (val >> 24) & 0xff, (val >> 16) & 0xff, val & 0xff); break; case 0x04: printf(" - ports %d, intrs %d, slots %d", (val >> 24) & 0xff, (val >> 8) & 0x3ff, val & 0xff); break; case 0x08: printf(" - erst max 0x%x, ist 0x%x", (val >> 4) & 0x0f, val & 0x0f); break; case 0x0c: printf(" - u2 lat 0x%x, u1 lat 0x%x", (val >> 16) & 0xffff, val & 0xff); break; case 0x10: ext = (val >> 16) & 0xffff; printf(" - extptr 0x%x, maxpsasize 0x%x%s%s%s%s%s%s%s%s", ext, (val >> 12) & 0xf, (val & 0x80) ? ", nss" : "", (val & 0x40) ? ", ltc" : "", (val & 0x20) ? ", lhrc" : "", (val & 0x10) ? ", pind" : "", (val & 0x08) ? ", ppc" : "", (val & 0x04) ? ", csz" : "", (val & 0x02) ? ", bnc" : "", (val & 0x01) ? ", ac64" : ""); break; case 0x14: printf(" - doorbell offset 0x%x", val); break; case 0x18: printf(" - runtime offset 0x%x", val); break; default: break; } printf("\n"); } while (ext) { printf(" xhci ext cap @ 0x%x\n", ext*4); val = map[ext]; off = val >> 8 & 0xff; cid = val & 0xff; printf(" 0x%04x: 0x%08x - next 0x%x, cap 0x%x [ %s ]\n", ext*4, val, off, cid, xhci_capname[cid]); switch (cid) { case 2: printf(" - version %x.%02x\n", (val >> 24) & 0xff, (val >> 16) & 0xff); val = map[ext+1]; printf(" 0x%04x: 0x%08x - name %c%c%c%c\n", ext*4+4, val, val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff); val = map[ext+2]; printf(" 0x%04x: 0x%08x - psic 0x%x, port count %d, port offset %d\n", ext*4+8, val, (val >> 28) & 0xf, (val >> 8) & 0xff, val & 0xff); break; } if (off) { ext += off; } else { ext = 0; } } return; } /* ------------------------------------------------------------- */ void ehcidump(char *dev, uint32_t offset) { uint32_t *map, val; int i, n; map = mapbar(dev, 0, offset); if (!map) return; n = map[0] & 0xff; if (n > 0x10) n = 0x10; printf(" ehci caps\n"); for (i = 0; i * 4 < n; i++) { val = map[i]; printf(" 0x%04x: 0x%08x", i * 4, val); switch (i * 4) { case 0x00: printf(" - version %x.%02x, caplength 0x%x", (val >> 24) & 0xff, (val >> 16) & 0xff, val & 0xff); break; case 0x04: printf(" - dbg port %d%s, companion %d ctl %d prt%s, ports %d", (val >> 20) & 0x0f, (val >> 16) & 0x01 ? ", pi" : "", (val >> 12) & 0x0f, (val >> 8) & 0x0f, (val >> 4) & 0x01 ? ", ppc" : "", (val >> 0) & 0x07); break; case 0x08: printf(" - extptr 0x%x, iso thresh %d%s%s%s", (val >> 8) & 0xff, (val >> 4) & 0x0f, (val >> 2) & 0x01 ? ", aspc" : "", (val >> 1) & 0x01 ? ", pflf" : "", (val >> 0) & 0x01 ? ", 64ac" : ""); break; default: break; } printf("\n"); } return; } /* ------------------------------------------------------------- */ void ahcidump(char *dev) { uint32_t *map, val; int ports,i; map = mapbar(dev, 5, 0); if (!map) return; printf(" ahci caps\n"); val = map[0]; ports = (val >> 0) & 0x1f; printf(" 0x%04x: 0x%08x", 0, val); printf(" - maxports: %d\n", ports); val = map[3]; printf(" 0x%04x: 0x%08x", 3*4, val); printf(" - port map:"); for (i = 0; i < ports+1; i++) { if (val & (1 << i)) { printf(" %d", i); } else { printf(" -"); } } printf("\n"); val = map[4]; printf(" 0x%04x: 0x%08x", 4*4, val); printf(" - ahci ver: %x.%x%x\n", (val >> 16) & 0xffff, (val >> 8) & 0xff, (val >> 0) & 0xff); return; } /* ------------------------------------------------------------- */ int main(int argc, char *argv[]) { unsigned long addr0; struct dirent *entry; uint32_t class, vendor, device; DIR *dir; dir = opendir(BASEPATH); while ((entry = readdir(dir)) != NULL) { if (entry->d_name[0] == '.') continue; if (argc > 1 && !strstr(entry->d_name, argv[1])) continue; class = readsys(entry->d_name, "class"); vendor = readsys(entry->d_name, "vendor"); device = readsys(entry->d_name, "device"); addr0 = readsys(entry->d_name, "resource"); switch (class) { case 0x0c0300: printf("uhci @ %s %04x:%04x\n", entry->d_name, vendor, device); break; case 0x0c0310: printf("ohci @ %s %04x:%04x\n", entry->d_name, vendor, device); break; case 0x0c0320: printf("ehci @ %s %04x:%04x\n", entry->d_name, vendor, device); printf(" bar0 @ 0x%lx\n", addr0); ehcidump(entry->d_name, addr0 & 0xfff); break; case 0x0c0330: printf("xhci @ %s %04x:%04x\n", entry->d_name, vendor, device); printf(" bar0 @ 0x%lx\n", addr0); xhcidump(entry->d_name, addr0 & 0xfff); break; case 0x010601: printf("ahci @ %s %04x:%04x\n", entry->d_name, vendor, device); if (1) { ahcidump(entry->d_name); } break; } } return 0; }