diff --git a/lib/wlib.c b/lib/wlib.c index 4c5a222..a890586 100644 --- a/lib/wlib.c +++ b/lib/wlib.c @@ -19,6 +19,15 @@ #include "wlib.h" +int +comp_ui8(const void *elem1, const void *elem2) +{ + uint8_t a = *((uint8_t *) elem1); + uint8_t b = *((uint8_t *) elem2); + return ((a < b) ? -1 : ((a > b) ? 1 : 0)); +} + + uint32_t pci_cf8(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg) { diff --git a/lib/wlib.h b/lib/wlib.h index 8429090..02664eb 100644 --- a/lib/wlib.h +++ b/lib/wlib.h @@ -68,8 +68,10 @@ void outl(uint16_t port, uint32_t data); modify [ax cx]; #endif +/* Comparator functions. */ +extern int comp_ui8(const void *elem1, const void *elem2); -/* PCI functions. */ +/* PCI I/O functions. */ extern uint32_t pci_cf8(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg); extern uint8_t pci_readb(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg); extern uint16_t pci_readw(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg); diff --git a/pcireg/PCIIDS.BIN b/pcireg/PCIIDS.BIN index c525d9e..d7dd273 100644 Binary files a/pcireg/PCIIDS.BIN and b/pcireg/PCIIDS.BIN differ diff --git a/pcireg/PCIREG.EXE b/pcireg/PCIREG.EXE index d96df53..0fcd7bc 100644 Binary files a/pcireg/PCIREG.EXE and b/pcireg/PCIREG.EXE differ diff --git a/pcireg/README.md b/pcireg/README.md index da62377..3d6a254 100644 --- a/pcireg/README.md +++ b/pcireg/README.md @@ -11,6 +11,9 @@ PCIREG -s [-d] PCIREG -t [-8] ∟ Display BIOS IRQ steering table. Specify -8 to display as 86Box code. +PCIREG -i [bus] device [function] +∟ Show information about the specified device. + PCIREG -r [bus] device [function] register ∟ Read the specified register. diff --git a/pcireg/pciids.py b/pcireg/pciids.py index 04cd1f3..c6d3511 100644 --- a/pcireg/pciids.py +++ b/pcireg/pciids.py @@ -23,82 +23,192 @@ def main(): pciutil.load_pci_db() # Start databases. - vendor_db = b'' - device_db = b'' - class_db = b'' - string_db = struct.pack('> 16, '')).encode('cp437', 'ignore')[:256] - if not vendor: - continue - elif last_vendor != vendor: - if last_vendor != None and not device_has_ffff: - device_db += end_entry + # Check if the vendor ID has changed. + if (pci_id >> 16) != current_vendor_id: + # Add termination device entry if one isn't already present. + if current_vendor_id != None and not device_has_termination: + device_db += struct.pack('> 16 + + # Store the device entries offset for this vendor. + vendor_devices_offset[current_vendor_id] = len(device_db) + + # Enumerate this device's subdevices. + subdevice_db_pos = len(subdevice_db) + subdevice_has_termination = False + for pci_subid in sorted(pciutil._pci_subdevices.get(pci_id, {})): + # Store a null device entries offset for this vendor if one isn't already present. + subvendor_id = (pci_subid >> 16) & 0xffff + if subvendor_id not in vendor_devices_offset: + vendor_devices_offset[subvendor_id] = None + + # Look up subdevice ID. + subdevice = pciutil.clean_device(pciutil._pci_subdevices[pci_id][pci_subid]).encode('cp437', 'ignore')[:256] + + # Add to string database if a valid result was found. + if subdevice: + string_db_pos = string_db_lookup.get(subdevice, None) + if string_db_pos == None: + string_db_pos = string_db_lookup[subdevice] = len(string_db) + string_db += struct.pack('> 16, len(device_db), string_db_pos) - vendor_has_ffff = (pci_id >> 16) == 0xffff + # Add to vendor database. + devices_offset = vendor_devices_offset.get(vendor_id, None) + if devices_offset == None: + devices_offset = 0xffffffff + if string_db_pos != 0xffffffff and devices_offset != 0xffffffff: + vendor_db += struct.pack('> 16) & 0xff, pci_subclass & 0xff, string_db_pos) + subclass_has_termination = pci_subclass == 0xffff - # Add ffff end entry to the databases if required. - if not device_has_ffff: - device_db += end_entry - if not vendor_has_ffff: - vendor_db += end_entry - if not class_has_ffff: - class_db += end_entry + # Enumerate progif IDs. + print('Enumerating progifs...') + for pci_progif in sorted(pciutil._pci_progifs): + # Look up class-subclass-progif ID. + progif_name = pciutil._pci_progifs[pci_progif].encode('cp437', 'ignore')[:256] - # Write binary file. + # Add to string database if a valid result was found. + if progif_name: + string_db_pos = string_db_lookup.get(progif_name, None) + if string_db_pos == None: + string_db_pos = string_db_lookup[progif_name] = len(string_db) + string_db += struct.pack('> 16) & 0xff, (pci_progif >> 8) & 0xff, pci_progif & 0xff, string_db_pos) + progif_has_termination = pci_progif == 0xffffff + + # Create binary file. print('Writing binary database...') f = open('PCIIDS.BIN', 'wb') - f.write(struct.pack('> 9) & 3]); + + /* Read revision and class ID. */ + reg_val.u32 = pci_readl(bus, dev, func, 0x08); + + /* Print revision. */ + printf("\n\nRevision: %02X", reg_val.u8[0]); + + /* Print class ID. */ + printf("\n\nClass: [%02X] ", reg_val.u8[3]); + + /* Print class name if found. */ + temp = pciids_get_class(reg_val.u8[3]); + if (temp) { + printf(temp); + free(temp); + } else { + printf("[Unknown]"); + } + + /* Print subclass ID. */ + printf("\n [%02X] "); + + /* Print subclass name if found. */ + temp = pciids_get_subclass(reg_val.u8[3], reg_val.u8[2]); + if (temp) { + printf(temp); + free(temp); + } else { + printf("[Unknown]"); + } + + /* Print programming interface ID. */ + printf("\n [%02X] "); + + /* Print programming interface name if found. */ + temp = pciids_get_progif(reg_val.u8[3], reg_val.u8[2], reg_val.u8[1]); + if (temp) { + printf(temp); + free(temp); + } else { + printf("[Unknown]"); + } + + /* Read and print BARs. */ + for (i = 0; i < num_bars; i++) { + if (i == 0) + putchar('\n'); + + /* Read BAR. */ + reg_val.u32 = pci_readl(bus, dev, func, 0x10 + (i << 2)); + + /* Move on to the next BAR if this one doesn't appear to be valid. */ + if (!reg_val.u32 || (reg_val.u32 == 0xffffffff)) + continue; + + /* Print BAR index. */ + printf("\nBAR %d: ", i); + + /* Print BAR type and address. */ + if (i & 1) + printf("I/O at %04X", reg_val.u16[0] & 0xfffc); + else + printf("Memory at %04X%04X", reg_val.u16[1], reg_val.u16[0] & 0xfff0); + + } + + printf("\n"); return 0; } -int -comp_ui8(const void *elem1, const void *elem2) -{ - uint8_t a = *((uint8_t *) elem1); - uint8_t b = *((uint8_t *) elem2); - return ((a < b) ? -1 : ((a > b) ? 1 : 0)); -} - - -int +static int comp_irq_routing_entry(const void *elem1, const void *elem2) { irq_routing_entry_t *a = (irq_routing_entry_t *) elem1; @@ -771,8 +1190,8 @@ comp_irq_routing_entry(const void *elem1, const void *elem2) } -int -show_steering_table(char mode) +static int +dump_steering_table(char mode) { int i, j, entries; uint8_t irq_bitmap[256], temp[4]; @@ -997,7 +1416,33 @@ next_entry: } -int +static int +read_reg(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg) +{ + multi_t reg_val; + + /* Print banner message. */ + printf("Reading from PCI bus %02X device %02X function %d registers [%02X:%02X]\n", + bus, dev, func, reg | 3, reg & 0xfc); + + /* Read dword value from register. */ +#ifdef DEBUG + reg_val.u32 = pci_cf8(bus, dev, func, reg); +#else + reg_val.u32 = pci_readl(bus, dev, func, reg); +#endif + + /* Print value as a dword and bytes. */ + printf("Value: %04X%04X / %04X %04X / %02X %02X %02X %02X\n", + reg_val.u16[1], reg_val.u16[0], + reg_val.u16[0], reg_val.u16[1], + reg_val.u8[0], reg_val.u8[1], reg_val.u8[2], reg_val.u8[3]); + + return 0; +} + + +static int write_reg(uint8_t bus, uint8_t dev, uint8_t func, uint8_t reg, char *val) { uint16_t data_port; @@ -1111,6 +1556,9 @@ usage: printf("PCIREG -t [-8]\n"); printf("∟ Display BIOS IRQ steering table. Specify -8 to display as 86Box code.\n"); printf("\n"); + printf("PCIREG -i [bus] device [function]\n"); + printf("∟ Show information about the specified device.\n"); + printf("\n"); printf("PCIREG -r [bus] device [function] register\n"); printf("∟ Read the specified register.\n"); printf("\n"); @@ -1155,9 +1603,9 @@ usage: } else if (argv[1][1] == 't') { /* Steering table display only requires a single optional parameter. */ if ((argc >= 3) && (strlen(argv[2]) > 1)) - return show_steering_table(argv[2][1]); + return dump_steering_table(argv[2][1]); else - return show_steering_table('\0'); + return dump_steering_table('\0'); } else if ((argc >= 3) && (strlen(argv[1]) > 1)) { /* Read second parameter as a dword. */ nonhex = 0; @@ -1186,10 +1634,13 @@ usage: goto usage; } - if (argv[1][1] == 'd') { - /* Process parameters for a register dump. */ + if ((argv[1][1] == 'd') || (argv[1][1] == 'i')) { + /* Process parameters for a register or information dump. */ switch (hexargc) { case 4: + /* Specifying a register is not valid on an information dump. */ + if (argv[1][1] == 'i') + goto usage; reg = hexargv[3]; /* fall-through */ @@ -1215,7 +1666,15 @@ usage: } /* Start the operation. */ - return dump_regs(bus, dev, func, reg, argv[1][2]); + switch (argv[1][1]) { + case 'd': + /* Start register dump. */ + return dump_regs(bus, dev, func, reg, argv[1][2]); + + case 'i': + /* Start information dump. */ + return dump_info(bus, dev, func); + } } else { /* Subtract value parameter from a write operation. */ if (argv[1][1] == 'w') diff --git a/pcireg/pciutil.py b/pcireg/pciutil.py index 4e6bf7a..5e2e2d9 100644 --- a/pcireg/pciutil.py +++ b/pcireg/pciutil.py @@ -18,17 +18,15 @@ import re, urllib.request clean_device_abbr = [ - ('100Base-T', 'FE'), - ('100Base-TX', 'FE'), + ('100Base-TX?', 'FE'), ('1000Base-T', 'GbE'), - ('Acceleration', 'Accel.'), - ('Accelerator', 'Accel.'), + ('Accelerat(?:ion|or)', 'Accel.'), ('Alert on LAN', 'AoL'), ('Chipset Family', 'Chipset'), ('Chipset Graphics', 'iGPU'), ('Connection', 'Conn.'), ('DECchip', ''), - ('Dual Port', '2-port'), + ('Dual (Lane|Port)', '2-\\2'), ('Fast Ethernet', 'FE'), ('Fibre Channel', 'FC'), ('Function', 'Func.'), @@ -42,25 +40,21 @@ clean_device_abbr = [ ('Input/Output', 'I/O'), ('Integrated ([^\s]+) Graphics', '\\2 iGPU'), # VIA CLE266 ('Integrated Graphics', 'iGPU'), - ('([0-9]) lane', '\\2-lane'), + ('([0-9]) (lane|port)', '\\2-\\3'), ('Local Area Network', 'LAN'), ('Low Pin Count', 'LPC'), ('Memory Controller Hub', 'MCH'), - ('Network Adapter', 'NIC'), - ('Network (?:Interface )?Card', 'NIC'), - ('Network (?:Interface )?Controller', 'NIC'), + ('Network (?:Interface )?(?:Adapter|Card|Controller)', 'NIC'), ('NVM Express', 'NVMe'), ('Parallel ATA', 'PATA'), - ('PCI-E', 'PCIe'), - ('PCI Express', 'PCIe'), - ('PCI[- ]to[- ]PCI', 'PCI-PCI'), + ('PCI(?:-E| Express)', 'PCIe'), + ('([^- ]+)[- ]to[- ]([^- ]+)', '\\2-\\3'), ('Platform Controller Hub', 'PCH'), - ('([0-9]) port', '\\2-port'), ('Processor Graphics', 'iGPU'), - ('Quad Port', '4-port'), + ('Quad (Lane|Port)', '4-\\2'), ('Serial ATA', 'SATA'), ('Serial Attached SCSI', 'SAS'), - ('Single Port', '1-port'), + ('Single (Lane|Port)', '1-\\2'), ('USB ?([0-9])\\.0', 'USB\\2'), ('USB ?([0-9])\\.[0-9] ?Gen([0-9x]+)', 'USB\\2.\\3'), ('USB ?([0-9]\\.[0-9])', 'USB\\2'), @@ -69,7 +63,7 @@ clean_device_abbr = [ ('Wireless LAN', 'WLAN'), ] clean_device_bit_pattern = re.compile('''( |^|\(|\[|\{|/)(?:([0-9]{1,4}) )?(?:(K)(?:ilo)?|(M)(?:ega)?|(G)(?:iga)?)bit( |$|\)|\]|\})''', re.I) -clean_device_group_pattern = re.compile('''\\\\([0-9]+)''') +clean_device_doubleabbr_pattern = re.compile('''( |^|\(|\[|\{|/)([^ \(\[\{/]+) (?: |\(|\[|\{|/)\\2(?: |\)|\]|\})( |$|\)|\]|\})''') clean_device_suffix_pattern = re.compile(''' (?:Adapter|Card|Device|(?:Host )?Controller)( (?: [0-9#]+)?|$|\)|\]|\})''', re.I) clean_vendor_abbr_pattern = re.compile(''' \[([^\]]+)\]''') clean_vendor_suffix_pattern = re.compile(''' (?:Semiconductors?|(?:Micro)?electronics?|Interactive|Technolog(?:y|ies)|(?:Micro)?systems|Computer(?: works)?|Products|Group|and subsidiaries|of(?: America)?|Co(?:rp(?:oration)?|mpany)?|Inc|LLC|Ltd|GmbH|AB|AG|SA|(?:\(|\[|\{).*)$''', re.I) @@ -92,6 +86,7 @@ clean_vendor_final = { _clean_device_abbr_cache = [] _pci_vendors = {} _pci_devices = {} +_pci_subdevices = {} _pci_classes = {} _pci_subclasses = {} _pci_progifs = {} @@ -103,14 +98,15 @@ def clean_device(device, vendor=None): if not _clean_device_abbr_cache: for pattern, replace in clean_device_abbr: _clean_device_abbr_cache.append(( - re.compile('''( |^|\(|\[|\{|/)''' + pattern + '''( |$|\)|\]|\})''', re.I), - '\\g<1>' + replace + '\\g<' + str(1 + len(clean_device_group_pattern.findall(pattern))) + '>', + re.compile('''(?P |^|\(|\[|\{|/)''' + pattern + '''(?P |$|\)|\]|\})''', re.I), + '\\g' + replace + '\\g', )) # Apply patterns. device = clean_device_bit_pattern.sub('\\1\\2\\3\\4\\5bit\\6', device) for pattern, replace in _clean_device_abbr_cache: device = pattern.sub(replace, device) + device = clean_device_doubleabbr_pattern.sub('\\1\\2\\3', device) device = clean_device_suffix_pattern.sub('\\1', device) # Remove duplicate vendor ID. @@ -193,6 +189,11 @@ def load_pci_db(): elif line[1] != 9: # device device = (vendor << 16) | int(line[1:5], 16) _pci_devices[device] = line[7:-1].decode('utf8', 'ignore') + else: # subdevice + subdevice = (int(line[2:6], 16) << 16) | int(line[7:11], 16) + if device not in _pci_subdevices: + _pci_subdevices[device] = {} + _pci_subdevices[device][subdevice] = line[13:-1].decode('utf8', 'ignore') f.close()