From 1812a79554ea857594b7b57a9851d2de40354b7c Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Wed, 27 Jan 1999 14:52:53 +0000 Subject: [PATCH] Added `bus mapping mode' (-M) which scans the whole configuration space to find devices hiding behind misconfigured or misdesigned bus bridges. This is intended only for debugging purposes and it's available only to root as it can crash several well-known buggy chips. --- ChangeLog | 16 ++++ lib/access.c | 3 +- lib/generic.c | 6 +- lspci.c | 208 ++++++++++++++++++++++++++++++++++++++++---------- lspci.man | 9 +++ 5 files changed, 197 insertions(+), 45 deletions(-) diff --git a/ChangeLog b/ChangeLog index 03a77e2..19c3edd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,21 @@ +Wed Jan 27 14:59:16 1999 Martin Mares + + * lspci.c: Implemented bus mapping mode (-M). + + * lspci.c (scan_devices): Split to scan_devices() and scan_device(). + (show): Split to show() and show_device(). + + * lib/access.c (pci_init): When a->method == PCI_ACCESS_AUTO, + set it to the real access method afterwards. + +Mon Jan 25 23:46:13 1999 Martin Mares + + * lib/generic.c (pci_generic_fill_info): If in buscentric mode, + don't check PCI_COMMAND for I/O and memory enables. + Mon Jan 25 21:28:49 1999 Martin Mares + * Makefile: Added target `release' which substitutes new version number to .spec, .lsm and README. Also rewrote target `dist'. diff --git a/lib/access.c b/lib/access.c index 343abd8..986b54a 100644 --- a/lib/access.c +++ b/lib/access.c @@ -1,5 +1,5 @@ /* - * $Id: access.c,v 1.2 1999/01/24 21:35:35 mj Exp $ + * $Id: access.c,v 1.3 1999/01/27 14:53:02 mj Exp $ * * The PCI Library -- User Access * @@ -139,6 +139,7 @@ pci_init(struct pci_access *a) { a->debug("...OK\n"); a->methods = pci_methods[i]; + a->method = i; break; } a->debug("...No.\n"); diff --git a/lib/generic.c b/lib/generic.c index 11dc121..b586c95 100644 --- a/lib/generic.c +++ b/lib/generic.c @@ -1,5 +1,5 @@ /* - * $Id: generic.c,v 1.2 1999/01/25 22:10:45 geert Exp $ + * $Id: generic.c,v 1.3 1999/01/27 14:53:03 mj Exp $ * * The PCI Library -- Generic Direct Access Functions * @@ -115,10 +115,10 @@ pci_generic_fill_info(struct pci_dev *d, int flags) d->base_addr[i] = x; if (x & PCI_BASE_ADDRESS_SPACE_IO) { - if (!(cmd & PCI_COMMAND_IO)) + if (!a->buscentric && !(cmd & PCI_COMMAND_IO)) d->base_addr[i] = 0; } - else if (cmd & PCI_COMMAND_MEMORY) + else if (a->buscentric || (cmd & PCI_COMMAND_MEMORY)) { if ((x & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64) { diff --git a/lspci.c b/lspci.c index 45390d9..e5ff280 100644 --- a/lspci.c +++ b/lspci.c @@ -1,5 +1,5 @@ /* - * $Id: lspci.c,v 1.20 1999/01/24 21:38:47 mj Exp $ + * $Id: lspci.c,v 1.21 1999/01/27 14:52:55 mj Exp $ * * Linux PCI Utilities -- List All PCI Devices * @@ -24,8 +24,9 @@ static int show_hex; /* Show contents of config space as hexadecimal numbers * static struct pci_filter filter; /* Device filter */ static int show_tree; /* Show bus tree */ static int machine_readable; /* Generate machine-readable output */ +static int map_mode; /* Bus mapping mode enabled */ -static char options[] = "nvbxs:d:ti:mg" GENERIC_OPTIONS ; +static char options[] = "nvbxs:d:ti:mgM" GENERIC_OPTIONS ; static char help_msg[] = "\ Usage: lspci []\n\ @@ -38,7 +39,8 @@ Usage: lspci []\n\ -d []:[]\tShow only selected devices\n\ -t\t\tShow bus tree\n\ -m\t\tProduce machine-readable output\n\ --i \tUse specified ID database instead of %s\n" +-i \tUse specified ID database instead of %s\n\ +-M\t\tEnable `bus mapping' mode (dangerous; root only)\n" GENERIC_HELP ; @@ -71,35 +73,45 @@ struct device { static struct device *first_dev; +static struct device * +scan_device(struct pci_dev *p) +{ + int how_much = (show_hex > 2) ? 256 : 64; + struct device *d; + + if (!pci_filter_match(&filter, p)) + return NULL; + d = xmalloc(sizeof(struct device)); + bzero(d, sizeof(*d)); + d->dev = p; + if (!pci_read_block(p, 0, d->config, how_much)) + die("Unable to read %d bytes of configuration space.", how_much); + if (how_much < 128 && (d->config[PCI_HEADER_TYPE] & 0x7f) == PCI_HEADER_TYPE_CARDBUS) + { + /* For cardbus bridges, we need to fetch 64 bytes more to get the full standard header... */ + if (!pci_read_block(p, 0, d->config+64, 64)) + die("Unable to read cardbus bridge extension data."); + how_much = 128; + } + d->config_cnt = how_much; + pci_setup_cache(p, d->config, d->config_cnt); + pci_fill_info(p, PCI_FILL_IDENT | PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE); + return d; +} + static void scan_devices(void) { struct device *d; - int how_much = (show_hex > 2) ? 256 : 64; struct pci_dev *p; pci_scan_bus(pacc); for(p=pacc->devices; p; p=p->next) - { - if (!pci_filter_match(&filter, p)) - continue; - d = xmalloc(sizeof(struct device)); - d->next = first_dev; - first_dev = d; - d->dev = p; - if (!pci_read_block(p, 0, d->config, how_much)) - die("Unable to read %d bytes of configuration space.", how_much); - if (how_much < 128 && (d->config[PCI_HEADER_TYPE] & 0x7f) == PCI_HEADER_TYPE_CARDBUS) - { - /* For cardbus bridges, we need to fetch 64 bytes more to get the full standard header... */ - if (!pci_read_block(p, 0, d->config+64, 64)) - die("Unable to read cardbus bridge extension data."); - how_much = 128; - } - d->config_cnt = how_much; - pci_setup_cache(p, d->config, d->config_cnt); - pci_fill_info(p, PCI_FILL_IDENT | PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE); - } + if (d = scan_device(p)) + { + d->next = first_dev; + first_dev = d; + } } static int @@ -718,24 +730,28 @@ show_machine(struct device *d) } } +static void +show_device(struct device *d) +{ + if (machine_readable) + show_machine(d); + else if (verbose) + show_verbose(d); + else + show_terse(d); + if (show_hex) + show_hex_dump(d); + if (verbose || show_hex) + putchar('\n'); +} + static void show(void) { struct device *d; for(d=first_dev; d; d=d->next) - { - if (machine_readable) - show_machine(d); - else if (verbose) - show_verbose(d); - else - show_terse(d); - if (show_hex) - show_hex_dump(d); - if (verbose || show_hex) - putchar('\n'); - } + show_device(d); } /* Tree output */ @@ -978,6 +994,108 @@ show_forest(void) show_tree_bridge(&host_bridge, line, line); } +/* Bus mapping mode */ + +struct bus_bridge { + struct bus_bridge *next; + byte first, last; +}; + +struct bus_info { + byte exists; + byte covered; + struct bus_bridge *bridges; +}; + +static struct bus_info *bus_info; + +static void +map_bridge(struct bus_info *bi, struct device *d, int np, int ns, int nl) +{ + struct bus_bridge *b = xmalloc(sizeof(struct bus_bridge)); + struct pci_dev *p = d->dev; + int prim = get_conf_byte(d, np); + + b->next = bi->bridges; + bi->bridges = b; + b->first = get_conf_byte(d, ns); + b->last = get_conf_byte(d, nl); + printf("## %02x.%02x:%d is a bridge from %02x to %02x-%02x\n", + p->bus, p->dev, p->func, prim, b->first, b->last); + if (prim != p->bus) + printf("!!! Bridge points to invalid primary bus.\n"); + if (b->first > b->last) + { + printf("!!! Bridge points to invalid bus range.\n"); + b->last = b->first; + } +} + +static void +do_map_bus(int bus) +{ + int dev, func; + int verbose = pacc->debugging; + struct bus_info *bi = bus_info + bus; + struct device *d; + + if (verbose) + printf("Mapping bus %02x\n", bus); + for(dev = 0; dev < 32; dev++) + if (filter.slot < 0 || filter.slot == dev) + { + for(func = 0; func < 8; func++) + if (filter.func < 0 || filter.func == func) + { + struct pci_dev *p = pci_get_dev(pacc, bus, dev, func); + u16 vendor = pci_read_word(p, PCI_VENDOR_ID); + if (vendor && vendor != 0xffff) + { + if (verbose) + printf("Discovered device %02x:%02x.%d\n", bus, dev, func); + bi->exists = 1; + if (d = scan_device(p)) + { + show_device(d); + switch (get_conf_byte(d, PCI_HEADER_TYPE) & 0x7f) + { + case PCI_HEADER_TYPE_BRIDGE: + map_bridge(bi, d, PCI_PRIMARY_BUS, PCI_SECONDARY_BUS, PCI_SUBORDINATE_BUS); + break; + case PCI_HEADER_TYPE_CARDBUS: + map_bridge(bi, d, PCI_CB_PRIMARY_BUS, PCI_CB_CARD_BUS, PCI_CB_SUBORDINATE_BUS); + break; + } + free(d); + } + else if (verbose) + printf("But it was filtered out.\n"); + } + pci_free_dev(p); + } + } +} + +static void +map_the_bus(void) +{ + if (pacc->method == PCI_ACCESS_PROC_BUS_PCI || + pacc->method == PCI_ACCESS_DUMP) + printf("WARNING: Bus mapping can be reliable only with direct hardware access enabled.\n\n"); + else if (!check_root()) + die("Only root can map the bus."); + bus_info = xmalloc(sizeof(struct bus_info) * 256); + bzero(bus_info, sizeof(struct bus_info) * 256); + if (filter.bus >= 0) + do_map_bus(filter.bus); + else + { + int bus; + for(bus=0; bus<256; bus++) + do_map_bus(bus); + } +} + /* Main */ int @@ -1029,6 +1147,9 @@ main(int argc, char **argv) case 'm': machine_readable++; break; + case 'M': + map_mode++; + break; default: if (parse_generic_option(i, pacc, optarg)) break; @@ -1040,12 +1161,17 @@ main(int argc, char **argv) goto bad; pci_init(pacc); - scan_devices(); - sort_them(); - if (show_tree) - show_forest(); + if (map_mode) + map_the_bus(); else - show(); + { + scan_devices(); + sort_them(); + if (show_tree) + show_forest(); + else + show(); + } pci_cleanup(pacc); return 0; diff --git a/lspci.man b/lspci.man index 6021d92..dd91ce6 100644 --- a/lspci.man +++ b/lspci.man @@ -85,6 +85,15 @@ as directory containing PCI bus information instead of /proc/bus/pci. .B -m Dump PCI device data in machine readable form (both normal and verbose format supported) for easy parsing by scripts. +.TP +.B -M +Invoke bus mapping mode which scans the bus extensively to find all devices including +those behind misconfigured bridges etc. Please note that this is intended only for +debugging and as it can crash the machine (only in case of buggy devices, but +unfortunately these happen to exist), it's available only to root. Also using +-M on PCI access methods which don't directly touch the hardware has no +sense since the results are (modulo bugs in lspci) identical to normal listing +modes. .SH PCILIB OPTIONS The PCI utilities use PCILIB (a portable library providing platform-independent -- 2.39.2