Files
86Box-probing-tools/pcireg/pcireg.c
2025-04-19 18:17:42 -03:00

1756 lines
55 KiB
C

/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box Probing Tools distribution.
*
* PCI bus and configuration space probing tool.
*
*
*
* Authors: RichardG, <richardg867@gmail.com>
*
* Copyright 2021 RichardG.
*
* ┌──────────────────────────────────────────────────────────────┐
* │ This file is UTF-8 encoded. If this text is surrounded by │
* │ garbage, please tell your editor to open this file as UTF-8. │
* └──────────────────────────────────────────────────────────────┘
*/
#ifdef __POSIX_UEFI__
# include <uefi.h>
#else
# include <inttypes.h>
# include <malloc.h>
# include <stdio.h>
# include <stdint.h>
# include <string.h>
# include <stdlib.h>
# if defined(__DOS__) || defined(__PMODEW__)
# include <dos.h>
# include <i86.h>
# endif
#endif
#include "lh5_extract.h"
#include "clib_pci.h"
#include "clib_std.h"
#include "clib_sys.h"
#include "clib_term.h"
static const char *command_flags[] = {
"I/O",
"Mem",
"Master",
"Special",
"Invalidate",
"Palette",
"Parity",
"Wait",
"SErr",
"FastBack",
"DisableINTx",
NULL,
NULL,
NULL,
NULL,
NULL,
};
static const char *status_flags[] = {
NULL,
NULL,
NULL,
"IRQ",
"CapList",
"66MHz",
"UDF",
"FastBack",
"ParityErr1",
NULL,
NULL,
"TargetAbort",
"TargetAbortAck",
"MasterAbort",
"SErr",
"ParityErr2"
};
static const char *devsel[] = {
"Fast",
"Medium",
"Slow",
"Invalid"
};
static const char *bridge_flags[] = {
"Parity",
"SErr",
"ISA",
"VGA",
NULL,
"MasterAbort",
"SecReset",
"FastB2B",
"PriMTimeout",
"SecMTimeout",
"TimeoutStat",
"TimeoutSErr",
NULL,
NULL,
NULL,
NULL
};
static int term_width;
#if defined(__DOS__) || defined(__PMODEW__)
static union REGS regs;
static struct SREGS seg_regs;
# pragma pack(push, 1)
static struct PACKED {
uint32_t edi, esi, ebp, unused, ebx, edx, ecx, eax;
uint16_t flags, es, ds, fs, gs, ip, cs, sp, ss;
} dpmi_regs;
# pragma pack(pop)
#endif
static int pciids_cur_vendor = -1;
static int pciids_cur_device = -1;
static char *pciids_lookup;
#ifdef PCI_LIB_VERSION
static char pciids_buf[256];
#endif
#pragma pack(push, 1)
static struct PACKED {
uint16_t vendor_id;
uint32_t devices_offset;
uint32_t string_offset;
} *pciids_vendor = NULL;
static struct PACKED {
uint16_t device_id;
uint32_t subdevices_offset;
uint32_t string_offset;
} *pciids_device = NULL;
static struct PACKED {
uint16_t subvendor_id;
uint16_t subdevice_id;
uint32_t string_offset;
} *pciids_subdevice = NULL;
static struct PACKED {
uint8_t class_id;
uint32_t string_offset;
} *pciids_class = NULL;
static struct PACKED {
uint8_t class_id;
uint8_t subclass_id;
uint32_t string_offset;
} *pciids_subclass = NULL;
static struct PACKED {
uint8_t class_id;
uint8_t subclass_id;
uint8_t progif_id;
uint32_t string_offset;
} *pciids_progif = NULL;
static char *pciids_string = NULL;
#if defined(__DOS__) || defined(__PMODEW__)
typedef struct {
uint8_t bus, dev;
struct {
uint8_t link;
uint16_t bitmap;
} ints[4];
uint8_t slot, reserved;
} irq_routing_entry_t;
typedef struct {
uint16_t len;
union {
struct {
uint16_t offset, segment;
} data_ptr;
irq_routing_entry_t entry[1];
};
} irq_routing_table_t;
#endif
#pragma pack(pop)
static int
pciids_open_database(void **ptr, char id)
{
FILE *f;
size_t pos;
unsigned int header_size;
unsigned int original_size;
unsigned int packed_size;
uint8_t header[128];
char *filename;
char target_filename[13];
unsigned short crc;
unsigned char method;
uint8_t *buf = NULL;
/* No action is required if the database is already loaded. */
if (*ptr)
return 0;
fflush(stdout);
/* Generate target filename. */
strcpy(target_filename, "PCIIDS_@.BIN");
target_filename[7] = id;
/* Open archive or uncompressed database, and stop if none could be opened. */
f = fopen("PCIIDS.LHA", "r" FOPEN_BINARY);
if (!f) {
f = fopen(target_filename, "r" FOPEN_BINARY);
if (!f)
return 1;
fseek(f, 0, SEEK_END);
original_size = packed_size = ftell(f);
fseek(f, 0, SEEK_SET);
method = '0';
goto found;
}
/* Go through archive. */
while (!feof(f)) {
/* Read LHA header. */
pos = ftell(f);
if (!fread(header, sizeof(header), 1, f))
break;
/* Parse LHA header. */
header_size = LH5HeaderParse(header, sizeof(header), &original_size, &packed_size, &filename, &crc, &method);
if (!header_size)
break; /* invalid header */
/* Check filename. */
crc = !strcmp(filename, target_filename);
free(filename);
if (crc) {
found:
/* Allocate buffers for the compressed and decompressed data. */
*ptr = malloc(original_size);
if (!*ptr)
goto fail;
buf = (method != '0') ? malloc(packed_size) : *ptr;
if (!buf)
goto fail;
/* Read and optionally decompress data. */
fseek(f, pos + header_size, SEEK_SET);
if (!fread(buf, packed_size, 1, f))
goto fail;
if ((method != '0') && (LH5Decode(buf, packed_size, *ptr, original_size) == -1))
goto fail;
/* All done, close archive. */
fclose(f);
if (method != '0')
free(buf);
return 0;
}
/* Move on to the next header. */
fseek(f, pos + header_size + packed_size, SEEK_SET);
}
fail:
/* Entry not found or read/decompression failed. */
printf("PCI ID database %c decompression failed\n", id);
fclose(f);
if (*ptr) {
free(*ptr);
*ptr = NULL;
}
if (buf)
free(buf);
return 1;
}
static char *
pciids_read_string(uint32_t offset)
{
/* Return nothing if the string offset is invalid. */
if (offset == 0xffffffff)
return NULL;
/* Open database if required. */
if (pciids_open_database((void **) &pciids_string, 'T'))
return NULL;
return &pciids_string[offset];
}
static int
find_vendor(uint16_t vendor_id)
{
/* Open database if required. */
if (pciids_open_database((void **) &pciids_vendor, 'V'))
return 0;
/* Go through vendor entries until the ID is matched or overtaken. */
for (pciids_cur_vendor = 0; pciids_vendor[pciids_cur_vendor].vendor_id < vendor_id; pciids_cur_vendor++)
;
/* Return 1 if the ID was matched, 0 otherwise. */
return pciids_vendor[pciids_cur_vendor].vendor_id == vendor_id;
}
static char *
pciids_get_vendor(uint16_t vendor_id)
{
/* Find vendor ID in the database, and return its name if found. */
if (find_vendor(vendor_id))
return pciids_read_string(pciids_vendor[pciids_cur_vendor].string_offset);
#ifdef PCI_LIB_VERSION
/* Find vendor ID in the system pci.ids. */
pciids_cur_vendor = -1;
pciids_lookup = pci_lookup_name(pacc, pciids_buf, sizeof(pciids_buf), PCI_LOOKUP_VENDOR | PCI_LOOKUP_NO_NUMBERS, vendor_id);
#else
pciids_lookup = NULL;
#endif
/* Return nothing if the vendor ID was not found. */
return pciids_lookup;
}
static char *
pciids_get_device(uint16_t vendor_id, uint16_t device_id)
{
/* Must be preceded by a call to {find|get}_vendor to establish the vendor ID! */
/* Open database if required. */
if ((pciids_cur_vendor < 0) || pciids_open_database((void **) &pciids_device, 'D'))
goto no_device_db;
/* Go through device entries until the ID is matched or overtaken. */
for (pciids_cur_device = pciids_vendor[pciids_cur_vendor].devices_offset; pciids_device[pciids_cur_device].device_id < device_id; pciids_cur_device++)
;
/* Return the device name if found. */
if (pciids_device[pciids_cur_device].device_id == device_id)
return pciids_read_string(pciids_device[pciids_cur_device].string_offset);
no_device_db:
#ifdef PCI_LIB_VERSION
/* Find device ID in the system pci.ids. */
pciids_cur_device = -1;
pciids_lookup = pci_lookup_name(pacc, pciids_buf, sizeof(pciids_buf), PCI_LOOKUP_DEVICE | PCI_LOOKUP_NO_NUMBERS, vendor_id, device_id);
#else
pciids_lookup = NULL;
#endif
/* Return nothing if the device ID was not found. */
return pciids_lookup;
}
static char *
pciids_get_subdevice(uint16_t vendor_id, uint16_t device_id, uint16_t subvendor_id, uint16_t subdevice_id)
{
/* Must be preceded by calls to {find|get}_vendor and get_device to establish the vendor/device ID! */
int i;
/* Open database if required. */
if ((pciids_cur_device < 0) || pciids_open_database((void **) &pciids_subdevice, 'S'))
goto no_subdevice_db;
/* Go through subdevice entries until the ID is matched or overtaken. */
for (i = pciids_device[pciids_cur_device].subdevices_offset; (pciids_subdevice[i].subvendor_id < subvendor_id) || (pciids_subdevice[i].subdevice_id < subdevice_id); i++)
;
/* Return the subdevice name if found. */
if ((pciids_subdevice[i].subvendor_id == subvendor_id) && (pciids_subdevice[i].subdevice_id == subdevice_id))
return pciids_read_string(pciids_subdevice[i].string_offset);
no_subdevice_db:
#ifdef PCI_LIB_VERSION
/* Find subdevice ID in the system pci.ids. */
pciids_lookup = pci_lookup_name(pacc, pciids_buf, sizeof(pciids_buf), PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_DEVICE | PCI_LOOKUP_NO_NUMBERS, vendor_id, device_id, subvendor_id, subdevice_id);
#else
pciids_lookup = NULL;
#endif
/* Return nothing if the subdevice ID was not found. */
return pciids_lookup;
}
static char *
pciids_get_class(uint8_t class_id)
{
int i;
/* Open database if required. */
if (pciids_open_database((void **) &pciids_class, 'C'))
goto no_class_db;
/* Go through class entries until the ID is matched or overtaken. */
for (i = 0; pciids_class[i].class_id < class_id; i++)
;
/* Return the class name if found. */
if (pciids_class[i].class_id == class_id)
return pciids_read_string(pciids_class[i].string_offset);
no_class_db:
#ifdef PCI_LIB_VERSION
/* Find class ID in the system pci.ids. */
pciids_lookup = pci_lookup_name(pacc, pciids_buf, sizeof(pciids_buf), PCI_LOOKUP_CLASS | PCI_LOOKUP_NO_NUMBERS, (class_id << 8) | 0x80); /* no way to look up by class without subclass, but several classes repeat their name in subclass 80 */
#else
pciids_lookup = NULL;
#endif
/* Return nothing if the class ID was not found. */
return pciids_lookup;
}
static char *
pciids_get_subclass(uint8_t class_id, uint8_t subclass_id)
{
int i;
/* Open database if required. */
if (pciids_open_database((void **) &pciids_subclass, 'U'))
goto no_subclass_db;
/* Go through subclass entries until the ID is matched or overtaken. */
for (i = 0; (pciids_subclass[i].class_id < class_id) || (pciids_subclass[i].subclass_id < subclass_id); i++)
;
/* Return the subclass name if found. */
if ((pciids_subclass[i].class_id == class_id) && (pciids_subclass[i].subclass_id == subclass_id))
return pciids_read_string(pciids_subclass[i].string_offset);
no_subclass_db:
#ifdef PCI_LIB_VERSION
/* Find subclass ID in the system pci.ids. */
pciids_lookup = pci_lookup_name(pacc, pciids_buf, sizeof(pciids_buf), PCI_LOOKUP_CLASS | PCI_LOOKUP_NO_NUMBERS, (class_id << 8) | subclass_id);
#else
pciids_lookup = NULL;
#endif
/* Return nothing if the subclass ID was not found. */
return pciids_lookup;
}
static char *
pciids_get_progif(uint8_t class_id, uint8_t subclass_id, uint8_t progif_id)
{
int i;
/* Open database if required. */
if (pciids_open_database((void **) &pciids_progif, 'P'))
goto no_progif_db;
/* Go through programming interface entries until the ID is matched or overtaken. */
for (i = 0; (pciids_progif[i].class_id < class_id) || (pciids_progif[i].subclass_id < subclass_id) || (pciids_progif[i].progif_id < progif_id); i++)
;
/* Return the programming interface name if found. */
if ((pciids_progif[i].class_id == class_id) && (pciids_progif[i].subclass_id == subclass_id) && (pciids_progif[i].progif_id == progif_id))
return pciids_read_string(pciids_progif[i].string_offset);
no_progif_db:
#ifdef PCI_LIB_VERSION
/* Find programming interface ID in the system pci.ids. */
pciids_lookup = pci_lookup_name(pacc, pciids_buf, sizeof(pciids_buf), PCI_LOOKUP_PROGIF | PCI_LOOKUP_NO_NUMBERS, (class_id << 8) | subclass_id, progif_id);
#else
pciids_lookup = NULL;
#endif
/* Return nothing if the programming interface ID was not found. */
return pciids_lookup;
}
static int
dump_regs(uint8_t bus, uint8_t dev, uint8_t func, uint8_t start_reg, char sz)
{
int i, width, flags, bar_id;
char buf[16];
uint8_t cur_reg, regs[256], dev_type, bar_reg;
multi_t reg_val;
FILE *f;
/* Align the starting register. */
start_reg &= 0xfc;
/* Generate dump file name. */
sprintf(buf, "PCI%02X%02X%d.BIN", bus, dev, func);
/* Get terminal size. */
term_width = term_get_size_x();
/* Size character '.' indicates a quiet dump for scan_bus. */
if (sz != '.') {
/* Print banner message. */
printf("Dumping registers [%02X:FF] from PCI bus %02X device %02X function %d\n\n", start_reg,
bus, dev, func);
/* Print column headers. */
printf(" ");
switch (sz) {
case 'd':
case 'l':
width = 40;
for (i = 0x0; i <= 0xf; i += 4) {
/* Add spacing at the halfway point. */
if (i == 0x8)
putchar(' ');
printf(" %X", i);
}
break;
case 'w':
width = 44;
for (i = 0x0; i <= 0xf; i += 2) {
if (i == 0x8)
putchar(' ');
printf(" %X", i);
}
break;
default:
width = 52;
for (i = 0x0; i <= 0xf; i++) {
if (i == 0x8)
putchar(' ');
printf(" %X", i);
}
break;
}
/* Move on to the next line if the terminal didn't already do that for us. */
if (width < term_width)
putchar('\n');
} else {
/* Print dump file name now. */
printf("Dumping registers to %s", buf);
}
cur_reg = 0;
do {
/* Print row header. */
if (sz != '.')
printf("%02X:", cur_reg);
/* Iterate through register dwords on this range. */
do {
/* Add spacing at the halfway point. */
if ((sz != '.') && ((cur_reg & 0x0f) == 0x08))
putchar(' ');
/* Are we supposed to read this register? */
if (cur_reg < start_reg) {
/* No, read it as 0 for the dump. */
reg_val.u32 = 0;
/* Print dashes. */
switch (sz) {
case '.':
break;
case 'd':
case 'l':
printf(" --------");
break;
case 'w':
printf(" ---- ----");
break;
default:
printf(" -- -- -- --");
break;
}
} else {
/* Yes, read dword value. */
#ifdef DEBUG
reg_val.u32 = pci_cf8(bus, dev, func, cur_reg);
#else
reg_val.u32 = pci_readl(bus, dev, func, cur_reg);
#endif
/* Print the value as bytes/words/dword. */
switch (sz) {
case '.':
break;
case 'd':
case 'l':
printf(" %04X%04X", reg_val.u16[1], reg_val.u16[0]);
break;
case 'w':
printf(" %04X %04X", reg_val.u16[0], reg_val.u16[1]);
break;
default:
printf(" %02X %02X %02X %02X", reg_val.u8[0], reg_val.u8[1], reg_val.u8[2], reg_val.u8[3]);
break;
}
}
/* Save value to dump array. */
*((uint32_t *) &regs[cur_reg]) = reg_val.u32;
/* Move on to the next dword. */
cur_reg += 4;
} while (cur_reg & 0x0f);
if (sz != '.') {
/* Move on to the next line if the terminal didn't already do that for us. */
if (width < term_width)
putchar('\n');
}
} while (cur_reg);
/* Print dump file name. */
if (sz != '.')
printf("\nSaving dump to %s\n", buf);
/* Write dump file. */
f = fopen(buf, "wb");
if (!f) {
if (sz != '.')
printf("File creation failed\n");
return 1;
}
if (fwrite(regs, sizeof(regs), 1, f) < 1) {
fclose(f);
if (sz != '.')
printf("File write failed\n");
return 1;
}
fclose(f);
if (sz == '.') {
/* Clear the dump file name printed earlier. */
width = strlen(buf) + 21;
for (i = 0; i < width; i++)
putchar('\b');
for (i = 0; i < width; i++)
putchar(' ');
for (i = 0; i < width; i++)
putchar('\b');
}
return 0;
}
static uint16_t
scan_bus(uint8_t bus, unsigned int nesting, char *nesting_buf, char dump, char *buf)
{
int i, j;
char *temp;
uint8_t dev;
uint8_t func;
uint8_t header_type;
uint8_t is_last = 0;
multi_t dev_id, dev_rev_class;
uint16_t ret = 0;
/* Perform a recursive scan to determine the highest device. */
if (buf)
ret = scan_bus(bus, nesting, NULL, dump, NULL);
/* Iterate through devices. */
for (dev = 0; dev < pci_device_count; dev++) {
/* Iterate through functions. */
for (func = 0; func < 8; func++) {
/* Read vendor/device ID. */
#ifdef DEBUG
if ((bus < DEBUG) && (dev <= bus) && (func == 0)) {
dev_id.u16[0] = rand();
dev_id.u16[1] = rand();
} else {
dev_id.u32 = 0xffffffff;
}
#else
dev_id.u32 = pci_readl(bus, dev, func, 0x00);
#endif
i = (dev << 8) | func;
is_last = i >= ret;
/* Report a valid ID. */
if (dev_id.u32 && (dev_id.u32 != 0xffffffff)) {
if (!buf) {
ret = i;
goto next_func;
}
/* Print device address and IDs. */
printf(" %02X %02X %d [%04X:%04X] ", bus, dev, func,
dev_id.u16[0], dev_id.u16[1]);
/* Clear vendor/device name buffer while adding nested bus spacing if required. */
i = term_width - 24;
if (bus != 0) {
printf("%s%s", nesting_buf, is_last ? "└─" : "├─");
i -= nesting << 1;
}
buf[0] = '\0';
/* Read revision and class ID. */
#ifdef DEBUG
dev_rev_class.u16[0] = rand();
dev_rev_class.u16[1] = rand();
#else
dev_rev_class.u32 = pci_readl(bus, dev, func, 0x08);
#endif
/* Look up vendor name in the PCI ID database. */
temp = pciids_get_vendor(dev_id.u16[0]);
if (temp) {
/* Print vendor name. */
i -= printf("%s ", temp);
/* Look up device name. */
temp = pciids_get_device(dev_id.u16[0], dev_id.u16[1]);
if (temp)
strcpy(&buf[strlen(buf)], temp);
else /* name not found */
goto unknown_device;
} else {
/* Vendor name not found. */
i -= printf("[Unknown] ");
unknown_device: /* Look up class ID. */
temp = pciids_get_subclass(dev_rev_class.u8[3], dev_rev_class.u8[2]);
if (temp)
sprintf(&buf[strlen(buf)], "[%s]", temp);
else /* name not found */
sprintf(&buf[strlen(buf)], "[Class %02X:%02X:%02X]", dev_rev_class.u8[3], dev_rev_class.u8[2], dev_rev_class.u8[1]);
}
/* Limit buffer to screen width, then print it with the revision ID. */
j = i - strlen(buf);
if (j >= 9) {
temp = "%s (rev %02X)";
} else if (j == 8) {
temp = "%s (rv %02X)";
} else if (j == 7) {
temp = "%s (r %02X)";
} else if (j == 6) {
temp = "%s (r%02X)";
} else if (j == 5) {
temp = "%s (%02X)";
} else {
temp = "%s(%02X)";
if (j < 4)
strcpy(&buf[i - 5], "");
}
i -= printf(temp, buf, dev_rev_class.u8[0]);
/* Move on to the next line if the terminal didn't already do that for us. */
if (i > 0)
putchar('\n');
/* Dump registers if requested. */
if (dump)
dump_regs(bus, dev, func, 0, '.');
} else {
/* Stop or move on to the next function if there's nothing here. */
if (func)
continue;
else
break;
}
next_func:
/* Read header type. */
#ifdef DEBUG
header_type = (bus < (DEBUG - 1)) ? 0x01 : 0x00;
#else
header_type = pci_readb(bus, dev, func, 0x0e);
#endif
/* If this is a bridge, mark that we should probe its bus. */
if (buf && (header_type & 0x7f)) {
/* Read bus number. */
#ifdef DEBUG
j = bus + 1;
#else
j = pci_readb(bus, dev, func, 0x19);
#endif
/* Scan the secondary bus with an added nesting layer. */
i = strlen(nesting_buf);
if (nesting > 0)
sprintf(&nesting_buf[i], is_last ? " " : "");
scan_bus(j, nesting + 1, nesting_buf, dump, buf);
nesting_buf[i] = '\0';
}
/* If we're at the first function, stop if this is not a multi-function device. */
if ((func == 0) && !(header_type & 0x80))
break;
}
}
return ret;
}
static int
scan_buses(char dump)
{
int i;
char *buf;
char *nesting_buf;
/* Initialize buffers. */
buf = malloc(256);
buf[0] = '\0';
nesting_buf = malloc(256);
nesting_buf[0] = '\0';
/* Get terminal size. */
term_width = term_get_size_x();
/* Print header. */
printf("Bus Dev Fun [VeID:DeID] Device\n");
for (i = 0; i < term_width; i++)
printf("");
/* Scan the root bus. */
scan_bus(0, 0, nesting_buf, dump, buf);
/* Clean up. */
free(buf);
free(nesting_buf);
return 0;
}
static void
info_flags_helper(uint16_t bitfield, const char **table)
{
/* Small helper function for printing PCI command/status flags. */
int i, j;
/* Check each bit. */
j = 0;
for (i = 0; i < 16; i++) {
/* Ignore if there is no table entry. */
if (!table[i])
continue;
/* Print flag name in [brackets] if set. */
if (bitfield & (1 << i))
printf(" [%s]", table[i]);
else
printf(" %s ", table[i]);
/* Add line break and indentation after the 7th item. */
if (++j == 7) {
printf("\n ");
j = 0;
}
}
}
static int
dump_info(uint8_t bus, uint8_t dev, uint8_t func)
{
char *temp;
int i, j;
uint8_t header_type, subsys_reg, num_bars, exprom_reg;
multi_t reg_val;
/* Print banner message. */
printf("Displaying information for PCI bus %02X device %02X function %d\n",
bus, dev, func);
/* Read vendor/device ID, and stop if it's invalid. */
#ifdef DEBUG
reg_val.u32 = 0x05711106;
#else
reg_val.u32 = pci_readl(bus, dev, func, 0x00);
if (!reg_val.u32 || (reg_val.u32 == 0xffffffff)) {
printf("\nNo device appears to exist here. (vendor:device %04X:%04X)\n",
reg_val.u16[0], reg_val.u16[1]);
return 1;
}
#endif
/* Print vendor ID. */
printf("\nVendor: [%04X] ", reg_val.u16[0]);
/* Print vendor name if found. */
temp = pciids_get_vendor(reg_val.u16[0]);
printf("%s", temp ? temp : "[Unknown]");
/* Print device ID. */
printf("\nDevice: [%04X] ", reg_val.u16[1]);
/* Print device name if found. */
temp = pciids_get_device(i = reg_val.u16[0], j = reg_val.u16[1]);
if (temp)
printf("%s", temp ? temp : "[Unknown]");
/* Read header type. We'll be using it a lot. */
header_type = pci_readb(bus, dev, func, 0x0e);
/* Determine the locations of common registers for this header type. */
switch (header_type & 0x7f) {
case 0x00: /* standard */
subsys_reg = 0x2c;
num_bars = 6;
exprom_reg = 0x30;
break;
case 0x01: /* PCI bridge */
subsys_reg = 0xff;
num_bars = 2;
exprom_reg = 0x38;
break;
case 0x02: /* CardBus bridge */
subsys_reg = 0x40;
num_bars = 0;
exprom_reg = 0xff;
break;
default: /* others */
subsys_reg = 0xff;
num_bars = 0;
exprom_reg = 0xff;
break;
}
if (subsys_reg != 0xff) {
/* Read subsystem ID and print it if valid. */
reg_val.u32 = pci_readl(bus, dev, func, subsys_reg);
if (reg_val.u32 && (reg_val.u32 != 0xffffffff)) {
/* Print subvendor ID. */
printf("\nSubvendor: [%04X] ", reg_val.u16[0]);
/* Print subvendor name if found. */
temp = pciids_get_vendor(reg_val.u16[0]);
printf("%s", temp ? temp : "[Unknown]");
/* Print subdevice ID. */
printf("\nSubdevice: [%04X] ", reg_val.u16[1]);
/* Print subdevice ID if found. */
temp = pciids_get_subdevice(i, j, reg_val.u16[0], reg_val.u16[1]);
printf("%s", temp ? temp : "[Unknown]");
}
}
/* Read command and status. */
reg_val.u32 = pci_readl(bus, dev, func, 0x04);
/* Print command and status flags. */
printf("\n\nCommand:");
info_flags_helper(reg_val.u16[0], command_flags);
printf("\n Status:");
info_flags_helper(reg_val.u16[1], status_flags);
printf(" DEVSEL[%s]", devsel[(reg_val.u16[1] >> 9) & 3]);
/* Print bridge flags if this is a bridge. */
if ((header_type & 0x7f) == 0x01) {
printf("\n Bridge:");
info_flags_helper(pci_readw(bus, dev, func, 0x3e), bridge_flags);
}
/* 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("\nClass: [%02X] ", reg_val.u8[3]);
/* Print class name if found. */
temp = pciids_get_class(reg_val.u8[3]);
printf("%s", temp ? temp : "[Unknown]");
/* Print subclass ID. */
printf("\n [%02X] ", reg_val.u8[2]);
/* Print subclass name if found. */
temp = pciids_get_subclass(reg_val.u8[3], reg_val.u8[2]);
printf("%s", temp ? temp : "[Unknown]");
/* Print programming interface ID. */
printf("\n [%02X] ", reg_val.u8[1]);
/* Print programming interface name if found. */
temp = pciids_get_progif(reg_val.u8[3], reg_val.u8[2], reg_val.u8[1]);
printf("%s", temp ? temp : "[Unknown]");
/* Read latency, grant and interrupt line. */
reg_val.u32 = pci_readl(bus, dev, func, 0x3c);
/* Print interrupt if present. */
if (reg_val.u16[0] && (reg_val.u8[0] != 0xff))
printf("\nInterrupt: INT%c (IRQ %d)", '@' + (reg_val.u8[1] & 15), reg_val.u8[0]);
/* Print latency and grant if available. */
if ((header_type & 0x7f) == 0x00) {
#ifdef FMT_FLOAT_SUPPORTED
printf("\nMin Grant: %.0f us at 33 MHz", reg_val.u8[2] * 0.25);
printf("\nMax Latency: %.0f us at 33 MHz", reg_val.u8[3] * 0.25);
#else
printf("\nMin Grant: %d.%d us at 33 MHz", reg_val.u8[2] >> 2, 25 * (reg_val.u8[2] & 3));
printf("\nMax Latency: %d.%d us at 33 MHz", reg_val.u8[3] >> 2, 25 * (reg_val.u8[2] & 3));
#endif
}
/* Read and print BARs. */
j = 0;
for (i = 0; i < num_bars; i++) {
/* 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. */
if (!j) {
putchar('\n');
j = 1;
}
printf("\nBAR %d: ", i);
/* Print BAR type, address and properties. */
if (reg_val.u8[0] & 1) {
printf("I/O at %04X", reg_val.u16[0] & 0xfffc);
} else {
printf("Memory at ");
switch (reg_val.u32 & 0x00000006) {
case 0x00:
printf("%08X (32-bit", reg_val.u32 & 0xfffffff0);
break;
case 0x02:
printf("%05X (below 1 MB", reg_val.u32 & 0xfffffff0);
break;
case 0x04:
/* Next BAR has the upper 32 bits. */
printf("%08X'%08X (64-bit", pci_readl(bus, dev, func, 0x14 + (i++ << 2)), reg_val.u32 & 0xfffffff0);
break;
case 0x06:
printf("%08X (invalid location", reg_val.u32 & 0xfffffff0);
break;
}
printf(", ");
if (!(reg_val.u8[0] & 0x08))
printf("not ");
printf("prefetchable)");
}
}
if ((header_type & 0x7f) == 0x01) {
/* Read and print PCI bridge specific registers. */
putchar('\n');
/* Read and print bus numbers. */
reg_val.u32 = pci_readl(bus, dev, func, 0x18);
printf("\nPCI bus: Primary[%02X] Secondary[%02X] Subordinate[%02X]", reg_val.u8[0], reg_val.u8[1], reg_val.u8[2]);
/* Read and print I/O range. */
reg_val.u16[0] = pci_readw(bus, dev, func, 0x1c);
printf("\n I/O: ");
if (reg_val.u8[0] & 1)
printf("%04X%04X-%04X%04X (32-bit)", pci_readw(bus, dev, func, 0x30), (reg_val.u8[0] & 0xf0) << 8, pci_readw(bus, dev, func, 0x32), reg_val.u16[0] | 0x0fff);
else
printf("%04X-%04X (16-bit)", (reg_val.u8[0] & 0xf0) << 8, reg_val.u16[0] | 0x0fff);
/* Read and print MMIO memory range. */
reg_val.u32 = pci_readl(bus, dev, func, 0x20);
printf("\n Memory: %08X-%08X (32-bit, not prefetchable)\n ", (reg_val.u32 & 0x0000fff0) << 16, reg_val.u32 | 0x000fffff);
/* Read and print prefetchable memory range. */
reg_val.u32 = pci_readl(bus, dev, func, 0x24);
if (reg_val.u16[0] & 1)
printf("%08X'%08X-%08X'%08X (64-bit", pci_readl(bus, dev, func, 0x28), (reg_val.u32 & 0x0000fff0) << 16, pci_readl(bus, dev, func, 0x2c), reg_val.u32 | 0x000fffff);
else
printf("%08X-%08X (32-bit", (reg_val.u32 & 0x0000fff0) << 16, reg_val.u32 | 0x000fffff);
printf(", prefetchable)");
}
if (exprom_reg != 0xff) {
/* Read and print expansion ROM. */
reg_val.u32 = pci_readl(bus, dev, func, exprom_reg);
if (reg_val.u32 && (reg_val.u32 != 0xffffffff))
printf("\nExpansion ROM: %08X (%sabled)", reg_val.u32 & 0xfffffffe, (reg_val.u8[0] & 1) ? "en" : "dis");
}
printf("\n");
return 0;
}
#if defined(__DOS__) || defined(__PMODEW__)
static int
comp_irq_routing_entry(const void *elem1, const void *elem2)
{
irq_routing_entry_t *a = (irq_routing_entry_t *) elem1;
irq_routing_entry_t *b = (irq_routing_entry_t *) elem2;
return comp_ui8(&a->dev, &b->dev);
}
static int
comp_irq_routing_link(const void *elem1, const void *elem2)
{
uint8_t a = *((uint8_t *) elem1);
uint8_t b = *((uint8_t *) elem2);
/* IRQ link values are highly chipset-dependent. For VIA and ALi chipsets,
we can get away with rotating the values left by 1 to get an ascending
order. Naturally, there are exceptions, but this should cover most cases. */
/* Special case for ALi M1489 AMIBIOS 6, actual order unconfirmed. */
if (a == 0x89)
a = 0xc1; /* immediately after 0x41 */
if (b == 0x89)
b = 0xc1;
/* Perform rotation. */
a = (a << 1) | (a >> 7);
b = (b << 1) | (b >> 7);
return comp_ui8(&a, &b);
}
static int
free_realmode(uint16_t segment)
{
memset(&regs, 0, sizeof(regs));
regs.w.ax = 0x0101;
regs.w.dx = segment;
int386(0x31, &regs, &regs);
return !regs.w.cflag;
}
#define STEERING_TABLE_PIR 1
#define STEERING_TABLE_86BOX 2
static int
dump_steering_table(uint8_t mode)
{
int i, j, entries;
uint8_t irq_bitmap[256], temp[4];
uint16_t buf_size, table_segment, dev_class;
irq_routing_table_t far *far_table;
irq_routing_table_t *table;
irq_routing_entry_t *entry;
if (mode & STEERING_TABLE_PIR) {
/* Search for PIR table. */
uint32_t *p = (uint32_t *) 0xf0000;
for (; (int) p <= 0xfffff; p += 4) { /* every 16 bytes */
if (*p == ('$' | ('P' << 8) | ('I' << 16) | ('R' << 24)))
break;
}
if ((int) p > 0xfffff) {
printf("$PIR table not found in BIOS segment.\n");
retry_pir:
printf("Try again without -m\n");
return 1;
}
/* Move data to a near buffer while converting to the PCI BIOS format. */
i = (((uint16_t *) p)[3] - 32) >> 4; /* byte 6 */
buf_size = sizeof(irq_routing_table_t) + (sizeof(irq_routing_entry_t) * (i - 1)); /* subtract the single entry in the base struct */
table = malloc(buf_size);
if (!table) {
printf("Failed to allocate %d local bytes.\n", buf_size);
goto retry_pir;
}
table->len = i * sizeof(irq_routing_entry_t);
memcpy(&table->entry[0], &p[8], table->len); /* byte 32 */
} else {
/* Allocate real mode memory buffer for PCI BIOS. */
buf_size = 1024;
retry_buf:
memset(&regs, 0, sizeof(regs));
regs.w.ax = 0x0100;
regs.w.bx = (buf_size + 15) >> 4;
int386(0x31, &regs, &regs);
if (regs.w.cflag) {
printf("Failed to allocate %d bytes. (AX=%04X)\n", (buf_size + 15) & ~0xf, regs.w.ax);
retry_pcibios:
printf("Try again with -m\n");
return 1;
}
table_segment = regs.w.ax;
far_table = (irq_routing_table_t far *) MK_FP(regs.w.dx, 0);
/* Specify where the IRQ routing information will be placed. */
far_table->len = buf_size;
far_table->data_ptr.segment = table_segment;
far_table->data_ptr.offset = 2; /* hardcoded! */
/* Call PCI BIOS to fetch IRQ routing information. */
memset(&dpmi_regs, 0, sizeof(dpmi_regs));
dpmi_regs.eax = 0xb10e;
dpmi_regs.ebx = 0x0000;
dpmi_regs.es = table_segment;
dpmi_regs.edi = 0x0000;
dpmi_regs.ds = 0xf000;
segread(&seg_regs);
regs.w.ax = 0x0300;
regs.w.bx = 0x001a;
regs.w.cx = 0x0000;
seg_regs.es = FP_SEG(&dpmi_regs);
regs.x.edi = FP_OFF(&dpmi_regs);
regs.w.cflag = 0;
int386x(0x31, &regs, &regs, &seg_regs);
if (regs.w.cflag) {
printf("DPMI call failed. (AX=%04X)\n", regs.w.ax);
goto retry_pcibios;
}
/* Check for any returned error. */
i = (dpmi_regs.eax >> 8) & 0xff;
if ((i == 0x59) && (far_table->len > buf_size)) {
/* Re-allocate buffer with the requested size. */
buf_size = far_table->len;
printf("PCI BIOS claims %d bytes for table entries, ", buf_size);
free_realmode(table_segment);
if (buf_size >= 65530) {
printf("which looks invalid.\n");
goto retry_pcibios;
}
printf("retrying...\n");
buf_size += 2;
goto retry_buf;
} else if (i) {
/* Something else went wrong. */
printf("PCI BIOS call failed. (AH=%02X)\n", i);
free_realmode(table_segment);
goto retry_pcibios;
}
/* Move data to a near buffer. */
table = malloc(buf_size);
if (!table) {
printf("Failed to allocate %d local bytes.\n", buf_size);
free_realmode(table_segment);
goto retry_pcibios;
}
_fmemcpy(table, far_table, buf_size);
free_realmode(table_segment);
}
/* Get terminal size. */
term_width = term_get_size_x();
/* Start output according to the selected mode. */
if (mode & STEERING_TABLE_86BOX) {
/* Stop if no entries were found. */
entries = table->len / sizeof(table->entry[0]);
if (!entries) {
printf("/* No entries found! */\n");
free(table);
return 1;
}
/* Sort table entries by device number for a tidier generated code. */
entry = &table->entry[0];
qsort(entry, entries, sizeof(table->entry[0]), comp_irq_routing_entry);
/* Check for and add missing northbridge steering entries. */
i = 0;
for (; entries > 0; entries--) {
/* Make sure we are on the root bus. */
if (entry->bus == 0) {
if ((entry->dev >> 3) == 0x00)
i |= 1;
else if ((entry->dev >> 3) == 0x01)
i |= 2;
}
entry++;
}
/* Assume device 00 is the northbridge if it has a host bridge class. */
if (!(i & 1)) {
dev_class = pci_readw(0x00, 0x00, 0, 0x0a);
if (dev_class == 0x0600)
printf("pci_register_slot(0x00, PCI_CARD_NORTHBRIDGE, 1, 2, 3, 4);\n");
}
/* Assume device 01 is the AGP bridge if it has a PCI bridge class. */
if (!(i & 2)) {
dev_class = pci_readw(0x00, 0x01, 0, 0x0a);
if (dev_class == 0x0604)
printf("pci_register_slot(0x01, PCI_CARD_AGPBRIDGE, 1, 2, 3, 4);\n");
}
/* Identify INTx# link value mapping by gathering all known values. */
memset(irq_bitmap, 0, sizeof(irq_bitmap));
entry = &table->entry[0];
entries = table->len / sizeof(table->entry[0]);
while (entries-- > 0) {
/* Ignore non-root buses. */
if (!entry->bus) {
/* Populate each IRQ link value. */
for (j = 0; j < 4; j++)
irq_bitmap[entry->ints[j].link] = entry->ints[j].link;
}
/* Move on to the next entry. */
entry++;
}
/* Sort link value entries. */
qsort(irq_bitmap, sizeof(irq_bitmap), sizeof(irq_bitmap[0]), comp_irq_routing_link);
/* Jump to the first non-zero link value entry. */
for (i = 0; (i < sizeof(irq_bitmap)) && (!irq_bitmap[i]); i++)
;
/* Warn if the mapping might not be completely valid. */
j = sizeof(irq_bitmap) - i;
if (j % 4)
printf("/* WARNING: %d IRQ link mappings found */\n", j);
/* Copy link value entries to temporary array. */
if (j > sizeof(temp))
j = sizeof(temp);
memcpy(temp, &irq_bitmap[i], j);
/* Fill in mapping entries. */
memset(irq_bitmap, 0, sizeof(irq_bitmap));
for (i = 0; i < j; i++) {
if (temp[i])
irq_bitmap[temp[i]] = i + 1;
}
} else {
/* Print header. */
printf("┌ Location ─┐┌ INT Lines ┐┌ Steerable IRQs ───────── (INTA=1 B=2 C=4 D=8) ┐\n");
printf("│Slt Bus Dev││A1 B2 C3 D4││ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15│\n");
printf("┴───────────┴┴───────────┴┴───────────────────────────────────────────────┴");
for (i = 75; i < term_width; i++)
putchar('');
}
/* Print entries until the end of the table. */
entries = table->len / sizeof(table->entry[0]);
entry = &table->entry[0];
while (entries-- > 0) {
/* Correct device number. */
entry->dev >>= 3;
/* Print entry according to the selected mode. */
if (mode & STEERING_TABLE_86BOX) {
/* Ignore non-root buses. */
if (entry->bus)
goto next_entry;
/* Print device ID. */
printf("pci_register_slot(0x%02X, PCI_CARD_", entry->dev);
/* Determine and print slot type. */
if (entry->dev == 0) {
printf("NORTHBRIDGE,");
} else {
/* Read device class. */
dev_class = pci_readw(0x00, entry->dev, 0, 0x0a);
/* Determine slot type by location and class. */
if ((entry->dev == 1) && (dev_class == 0x0604)) {
printf("AGPBRIDGE, ");
} else if (dev_class == 0x0601) { /* ISA bridge */
printf("SOUTHBRIDGE,");
} else if (entry->slot == 0) {
/* Very likely to be an onboard device. */
if ((dev_class & 0xff00) == 0x0300)
printf("VIDEO, ");
else if (dev_class == 0x0101)
printf("IDE, ");
else if (dev_class == 0x0100)
printf("SCSI, ");
else if ((dev_class == 0x0401) || (dev_class == 0x0403))
printf("SOUND, ");
else if (dev_class == 0x0703)
printf("MODEM, ");
else if ((dev_class & 0xff00) == 0x0200)
printf("NETWORK, ");
else if (dev_class == 0x0700)
printf("UART, ");
else if (dev_class == 0x0c03)
printf("USB, ");
else
goto normal;
} else {
normal:
printf("NORMAL, ");
}
}
/* Print INTx# IRQ routing entries. */
for (i = 0; i < 4; i++) {
if (i)
putchar(',');
printf(" %d", irq_bitmap[entry->ints[i].link]);
}
/* Finish line with a comment containing the slot number if present. */
printf(");");
if (entry->slot)
printf(" /* Slot %02X */", entry->slot);
else
printf(" /* Onboard */");
} else {
/* Print slot, bus and device. */
printf(" %02X %02X %02X ", entry->slot, entry->bus, entry->dev);
/* Clear IRQ bitmap. */
memset(irq_bitmap, 0, 16);
/* Print INTx# line values, while populating the IRQ bitmap. */
for (i = 0; i < (sizeof(entry->ints) / sizeof(entry->ints[0])); i++) {
printf(" %02X", entry->ints[i].link);
/* Set this INTx# line's bit on the bitmap entries for IRQs where it can be placed. */
for (j = 0; j < 16; j++)
irq_bitmap[j] |= ((entry->ints[i].bitmap >> j) & 1) << i;
}
/* Print IRQ bitmap. */
putchar(' ');
for (i = 0; i < 16; i++)
printf(" %X", irq_bitmap[i]);
}
/* Move on to the next entry. */
putchar('\n');
next_entry:
entry++;
}
free(table);
return 0;
}
#endif
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;
multi_t reg_val;
/* Write a byte, word or dword depending on the input value's length. */
data_port = 0xcfc;
switch (strlen(val)) {
case 1:
case 2:
/* Byte input value. */
parse_hex_u8(val, &reg_val.u8[0]);
/* Print banner message. */
printf("Writing %02X to PCI bus %02X device %02X function %d register [%02X]\n",
reg_val.u8[0],
bus, dev, func, reg);
/* Write byte value to register. */
pci_writeb(bus, dev, func, reg, reg_val.u8[0]);
printf("Written!\n");
/* Read the register's byte value back. */
#ifdef DEBUG
reg_val.u32 = pci_cf8(bus, dev, func, reg);
reg_val.u8[0] = reg_val.u8[reg & 3];
#else
reg_val.u8[0] = pci_readb(bus, dev, func, reg);
#endif
printf("Readback: %02X\n", reg_val.u8[0]);
break;
case 3:
case 4:
/* Word input value. */
parse_hex_u16(val, &reg_val.u16[0]);
/* Print banner message. */
printf("Writing %04X to PCI bus %02X device %02X function %d registers [%02X:%02X]\n",
reg_val.u16[0],
bus, dev, func, reg | 1, reg & 0xfe);
/* Write word value to register. */
pci_writew(bus, dev, func, reg, reg_val.u16[0]);
printf("Written!\n");
/* Read the register's word value back. */
#ifdef DEBUG
reg_val.u32 = pci_cf8(bus, dev, func, reg);
reg_val.u16[0] = reg_val.u16[(reg >> 1) & 1];
#else
reg_val.u16[0] = pci_readw(bus, dev, func, reg);
#endif
printf("Readback: %04X / %02X %02X\n",
reg_val.u16[0],
reg_val.u8[0], reg_val.u8[1]);
break;
default:
/* Dword input value. */
parse_hex_u32(val, &reg_val.u32);
/* Print banner message. */
printf("Writing %04X%04X to PCI bus %02X device %02X function %d registers [%02X:%02X]\n",
reg_val.u16[1], reg_val.u16[0],
bus, dev, func, reg | 3, reg & 0xfc);
/* Write dword value to register. */
pci_writel(bus, dev, func, reg, reg_val.u32);
printf("Written!\n");
/* Read the register's dword value back. */
#ifdef DEBUG
reg_val.u32 = pci_cf8(bus, dev, func, reg);
#else
reg_val.u32 = pci_readl(bus, dev, func, reg);
#endif
printf("Readback: %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]);
break;
}
return 0;
}
int
main(int argc, char **argv)
{
int hexargc, i;
char *ch;
uint8_t hexargv[8], bus, dev, func, reg;
uint32_t cf8;
/* Disable stdout buffering. */
term_unbuffer_stdout();
/* Print usage if there are too few parameters or if the first one looks invalid. */
if ((argc <= 1) || (strlen(argv[1]) < 2) || ((argv[1][0] != '-') && (argv[1][0] != '/'))) {
ch = strrchr(argv[0], '\\');
if (!(ch++)) {
ch = strrchr(argv[0], '/');
if (!(ch++))
ch = argv[0];
}
usage:
printf("%s -s [-d]\n", ch);
printf("∟ Display all devices on the PCI bus. Specify -d to dump registers as well.\n");
#if defined(__DOS__) || defined(__PMODEW__)
printf("\n");
printf("%s -t [-m] [-8]\n", ch);
printf("∟ Display BIOS IRQ steering table. Specify -m to look for a Microsoft $PIR\n");
printf(" table instead of calling PCI BIOS. Specify -8 to display as 86Box code.\n");
#endif
printf("\n");
printf("%s -i [bus] device [function]\n", ch);
printf("∟ Show information about the specified device.\n");
printf("\n");
printf("%s -r [bus] device [function] register\n", ch);
printf("∟ Read the specified register.\n");
printf("\n");
printf("%s -w [bus] device [function] register value\n", ch);
printf("∟ Write byte, word or dword to the specified register.\n");
printf("\n");
printf("%s {-d|-dw|-dl} [bus] device [function [register]]\n", ch);
printf("∟ Dump registers as bytes (-d), words (-dw) or dwords (-dl). Optionally\n");
printf(" specify the register to start from (requires bus to be specified as well).\n");
printf("\n");
printf("All numeric parameters should be specified in hexadecimal (without 0x prefix).\n");
printf("{bus device function register} can be substituted for a single port CF8h dword.\n");
printf("Register dumps are saved to PCIbbddf.BIN where bb=bus, dd=device, f=function.");
term_final_linebreak();
return 1;
}
#ifdef DEBUG
pci_mechanism = 1;
#else
/* Initialize PCI, and exit in case of failure. */
if (!pci_init())
return 1;
#endif
/* Convert the first parameter to lowercase. */
ch = argv[1];
while (*ch) {
if ((*ch >= 'A') && (*ch <= 'Z'))
*ch += 32;
ch++;
}
/* Interpret parameters. */
if (argv[1][1] == 's') {
/* Bus scan only asks for a single optional parameter. */
if ((argc >= 3) && (strlen(argv[2]) > 1))
return scan_buses(argv[2][1]);
else
return scan_buses('\0');
}
#if defined(__DOS__) || defined(__PMODEW__)
else if (argv[1][1] == 't') {
/* Steering table display asks for optional parameters. */
reg = 0;
for (i = 2; i < argc; i++) {
if (argv[i][0] == '\0')
continue;
else if (argv[i][1] == '8')
reg |= STEERING_TABLE_86BOX;
else if (argv[i][1] == 'm')
reg |= STEERING_TABLE_PIR;
}
return dump_steering_table(reg);
}
#endif
else if ((argc >= 3) && (strlen(argv[1]) > 1)) {
/* Read second parameter as a dword. */
if (parse_hex_u32(argv[2], &cf8)) {
/* Initialize default bus/device/function/register values. */
bus = dev = func = reg = 0;
/* Determine if the second parameter's value is a port CF8h dword. */
hexargc = 0;
if (cf8 & 0xffffff00) {
/* Break the CF8h dword's MSBs down into individual parameters. */
hexargv[hexargc++] = (cf8 >> 16) & 0xff;
hexargv[hexargc++] = (cf8 >> 11) & 31;
hexargv[hexargc++] = (cf8 >> 8) & 7;
}
hexargv[hexargc++] = cf8;
/* Read parameters until the end is reached or an invalid hex value is found. */
for (i = 3; (i < argc) && (i < (sizeof(hexargv) - 1)); i++) {
if (!parse_hex_u8(argv[i], &hexargv[hexargc++]))
break;
}
} else {
/* Print usage if the second parameter is an invalid hex value. */
goto usage;
}
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 */
case 3:
func = hexargv[2];
dev = hexargv[1];
bus = hexargv[0];
break;
case 2:
func = hexargv[1];
/* fall-through */
case 1:
dev = hexargv[0];
break;
case -1:
break;
default:
goto usage;
}
/* Start the operation. */
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')
hexargc -= 1;
/* Process parameters for read/write operations. */
switch (hexargc) {
case 4:
reg = hexargv[3];
func = hexargv[2];
dev = hexargv[1];
bus = hexargv[0];
break;
case 3:
reg = hexargv[2];
/* fall-through */
case 2:
func = hexargv[1];
/* fall-through */
case 1:
dev = hexargv[0];
break;
case -1:
break;
default:
printf("unknown hexargc %d\n", hexargc);
goto usage;
}
/* Start the operation. */
switch (argv[1][1]) {
case 'r':
/* Start read. */
return read_reg(bus, dev, func, reg);
case 'w':
/* Start write. */
return write_reg(bus, dev, func, reg, argv[2 + hexargc]);
default:
/* Print usage if an unknown parameter was specified. */
goto usage;
}
}
} else {
/* Print usage if a single parameter was specified. */
goto usage;
}
return 1;
}