mirror of
https://github.com/openbsd/src.git
synced 2024-12-22 16:42:56 -08:00
883 lines
21 KiB
C
883 lines
21 KiB
C
/* $OpenBSD: pcidump.c,v 1.49 2019/02/19 21:32:42 dlg Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2006, 2007 David Gwynne <loki@animata.net>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/pciio.h>
|
|
|
|
#include <stdio.h> /* need NULL for dev/pci/ headers */
|
|
|
|
#include <dev/pci/pcireg.h>
|
|
#include <dev/pci/pcidevs.h>
|
|
#include <dev/pci/pcidevs_data.h>
|
|
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <paths.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
|
|
#define PCIDEV "/dev/pci"
|
|
|
|
#ifndef nitems
|
|
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
|
|
#endif
|
|
|
|
__dead void usage(void);
|
|
void scanpcidomain(void);
|
|
int probe(int, int, int);
|
|
void dump(int, int, int);
|
|
void hexdump(int, int, int, int);
|
|
const char *str2busdevfunc(const char *, int *, int *, int *);
|
|
int pci_nfuncs(int, int);
|
|
int pci_read(int, int, int, u_int32_t, u_int32_t *);
|
|
int pci_readmask(int, int, int, u_int32_t, u_int32_t *);
|
|
void dump_bars(int, int, int, int);
|
|
void dump_caplist(int, int, int, u_int8_t);
|
|
void dump_pci_powerstate(int, int, int, uint8_t);
|
|
void dump_pcie_linkspeed(int, int, int, uint8_t);
|
|
void print_pcie_ls(uint8_t);
|
|
int dump_rom(int, int, int);
|
|
int dump_vga_bios(void);
|
|
|
|
void dump_type0(int bus, int dev, int func);
|
|
void dump_type1(int bus, int dev, int func);
|
|
void dump_type2(int bus, int dev, int func);
|
|
|
|
__dead void
|
|
usage(void)
|
|
{
|
|
extern char *__progname;
|
|
|
|
fprintf(stderr,
|
|
"usage: %s [-v] [-x | -xx | -xxx] [-d pcidev] [bus:dev:func]\n"
|
|
" %s -r file [-d pcidev] bus:dev:func\n",
|
|
__progname, __progname);
|
|
exit(1);
|
|
}
|
|
|
|
int pcifd;
|
|
int romfd;
|
|
int verbose = 0;
|
|
int hex = 0;
|
|
int size = 64;
|
|
|
|
const char *pci_capnames[] = {
|
|
"Reserved",
|
|
"Power Management",
|
|
"AGP",
|
|
"Vital Product Data (VPD)",
|
|
"Slot Identification",
|
|
"Message Signalled Interrupts (MSI)",
|
|
"CompactPCI Hot Swap",
|
|
"PCI-X",
|
|
"AMD LDT/HT",
|
|
"Vendor Specific",
|
|
"Debug Port",
|
|
"CompactPCI Central Resource Control",
|
|
"PCI Hot-Plug",
|
|
"PCI-PCI",
|
|
"AGP8",
|
|
"Secure",
|
|
"PCI Express",
|
|
"Extended Message Signalled Interrupts (MSI-X)",
|
|
"SATA",
|
|
"PCI Advanced Features"
|
|
};
|
|
|
|
const char *pci_enhanced_capnames[] = {
|
|
"Unknown",
|
|
"Advanced Error Reporting",
|
|
"Virtual Channel Capability",
|
|
"Device Serial Number",
|
|
"Power Budgeting",
|
|
"Root Complex Link Declaration",
|
|
"Root Complex Internal Link Control",
|
|
"Root Complex Event Collector",
|
|
"Multi-Function VC Capability",
|
|
"Virtual Channel Capability",
|
|
"Root Complex/Root Bridge",
|
|
"Vendor-Specific",
|
|
"Config Access",
|
|
"Access Control Services",
|
|
"Alternate Routing ID",
|
|
"Address Translation Services",
|
|
"Single Root I/O Virtualization",
|
|
"Multi Root I/O Virtualization",
|
|
"Multicast",
|
|
"Page Request Interface",
|
|
"Reserved for AMD",
|
|
"Resizable BAR",
|
|
"Dynamic Power Allocation",
|
|
"TPH Requester",
|
|
"Latency Tolerance Reporting",
|
|
"Secondary PCIe Capability",
|
|
"Protocol Multiplexing",
|
|
"Process Address Space ID",
|
|
"LN Requester",
|
|
"Downstream Port Containment",
|
|
"L1 PM",
|
|
"Precision Time Measurement",
|
|
};
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
int nfuncs;
|
|
int bus, dev, func;
|
|
char pcidev[PATH_MAX] = PCIDEV;
|
|
char *romfile = NULL;
|
|
const char *errstr;
|
|
int c, error = 0, dumpall = 1, domid = 0;
|
|
|
|
while ((c = getopt(argc, argv, "d:r:vx")) != -1) {
|
|
switch (c) {
|
|
case 'd':
|
|
strlcpy(pcidev, optarg, sizeof(pcidev));
|
|
dumpall = 0;
|
|
break;
|
|
case 'r':
|
|
romfile = optarg;
|
|
dumpall = 0;
|
|
break;
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
case 'x':
|
|
hex++;
|
|
break;
|
|
default:
|
|
usage();
|
|
}
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc > 1 || (romfile && argc != 1))
|
|
usage();
|
|
|
|
if (romfile) {
|
|
romfd = open(romfile, O_WRONLY|O_CREAT|O_TRUNC, 0777);
|
|
if (romfd == -1)
|
|
err(1, "%s", romfile);
|
|
}
|
|
|
|
if (hex > 1)
|
|
size = 256;
|
|
if (hex > 2)
|
|
size = 4096;
|
|
|
|
if (argc == 1)
|
|
dumpall = 0;
|
|
|
|
if (dumpall == 0) {
|
|
pcifd = open(pcidev, O_RDONLY, 0777);
|
|
if (pcifd == -1)
|
|
err(1, "%s", pcidev);
|
|
} else {
|
|
for (;;) {
|
|
snprintf(pcidev, 16, "/dev/pci%d", domid++);
|
|
pcifd = open(pcidev, O_RDONLY, 0777);
|
|
if (pcifd == -1) {
|
|
if (errno == ENXIO || errno == ENOENT) {
|
|
return 0;
|
|
} else {
|
|
err(1, "%s", pcidev);
|
|
}
|
|
}
|
|
printf("Domain %s:\n", pcidev);
|
|
scanpcidomain();
|
|
close(pcifd);
|
|
}
|
|
}
|
|
|
|
if (argc == 1) {
|
|
errstr = str2busdevfunc(argv[0], &bus, &dev, &func);
|
|
if (errstr != NULL)
|
|
errx(1, "\"%s\": %s", argv[0], errstr);
|
|
|
|
nfuncs = pci_nfuncs(bus, dev);
|
|
if (nfuncs == -1 || func > nfuncs)
|
|
error = ENXIO;
|
|
else if (romfile)
|
|
error = dump_rom(bus, dev, func);
|
|
else
|
|
error = probe(bus, dev, func);
|
|
|
|
if (error != 0)
|
|
errc(1, error, "\"%s\"", argv[0]);
|
|
} else {
|
|
printf("Domain %s:\n", pcidev);
|
|
scanpcidomain();
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
scanpcidomain(void)
|
|
{
|
|
int nfuncs;
|
|
int bus, dev, func;
|
|
|
|
for (bus = 0; bus < 256; bus++) {
|
|
for (dev = 0; dev < 32; dev++) {
|
|
nfuncs = pci_nfuncs(bus, dev);
|
|
for (func = 0; func < nfuncs; func++) {
|
|
probe(bus, dev, func);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const char *
|
|
str2busdevfunc(const char *string, int *bus, int *dev, int *func)
|
|
{
|
|
const char *errstr;
|
|
char b[80], *d, *f;
|
|
|
|
strlcpy(b, string, sizeof(b));
|
|
|
|
d = strchr(b, ':');
|
|
if (d == NULL)
|
|
return("device not specified");
|
|
*d++ = '\0';
|
|
|
|
f = strchr(d, ':');
|
|
if (f == NULL)
|
|
return("function not specified");
|
|
*f++ = '\0';
|
|
|
|
*bus = strtonum(b, 0, 255, &errstr);
|
|
if (errstr != NULL)
|
|
return (errstr);
|
|
*dev = strtonum(d, 0, 31, &errstr);
|
|
if (errstr != NULL)
|
|
return (errstr);
|
|
*func = strtonum(f, 0, 7, &errstr);
|
|
if (errstr != NULL)
|
|
return (errstr);
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
int
|
|
probe(int bus, int dev, int func)
|
|
{
|
|
u_int32_t id_reg;
|
|
const struct pci_known_vendor *pkv;
|
|
const struct pci_known_product *pkp;
|
|
const char *vendor = NULL, *product = NULL;
|
|
|
|
if (pci_read(bus, dev, func, PCI_ID_REG, &id_reg) != 0)
|
|
return (errno);
|
|
|
|
if (PCI_VENDOR(id_reg) == PCI_VENDOR_INVALID ||
|
|
PCI_VENDOR(id_reg) == 0)
|
|
return (ENXIO);
|
|
|
|
for (pkv = pci_known_vendors; pkv->vendorname != NULL; pkv++) {
|
|
if (pkv->vendor == PCI_VENDOR(id_reg)) {
|
|
vendor = pkv->vendorname;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (vendor != NULL) {
|
|
for (pkp = pci_known_products; pkp->productname != NULL; pkp++)
|
|
if (pkp->vendor == PCI_VENDOR(id_reg) &&
|
|
pkp->product == PCI_PRODUCT(id_reg)) {
|
|
product = pkp->productname;
|
|
break;
|
|
}
|
|
}
|
|
|
|
printf(" %d:%d:%d: %s %s\n", bus, dev, func,
|
|
(vendor == NULL) ? "unknown" : vendor,
|
|
(product == NULL) ? "unknown" : product);
|
|
|
|
if (verbose)
|
|
dump(bus, dev, func);
|
|
if (hex > 0)
|
|
hexdump(bus, dev, func, size);
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
dump_pci_powerstate(int bus, int dev, int func, uint8_t ptr)
|
|
{
|
|
u_int32_t pmcsr;
|
|
|
|
if (pci_read(bus, dev, func, ptr + PCI_PMCSR, &pmcsr) != 0)
|
|
return;
|
|
|
|
printf("\t\tState: D%d", pmcsr & PCI_PMCSR_STATE_MASK);
|
|
if (pmcsr & PCI_PMCSR_PME_EN)
|
|
printf(" PME# enabled");
|
|
if (pmcsr & PCI_PMCSR_PME_STATUS)
|
|
printf(" PME# asserted");
|
|
printf("\n");
|
|
}
|
|
|
|
void
|
|
print_pcie_ls(uint8_t speed)
|
|
{
|
|
if (speed & 4)
|
|
printf("8.0");
|
|
else if (speed & 2)
|
|
printf("5.0");
|
|
else if (speed & 1)
|
|
printf("2.5");
|
|
else
|
|
printf("unknown (%d)", speed);
|
|
}
|
|
|
|
void
|
|
dump_pcie_linkspeed(int bus, int dev, int func, uint8_t ptr)
|
|
{
|
|
u_int32_t lcap, sreg, lcap2 = 0, xcap;
|
|
u_int8_t cwidth, cspeed, swidth, sspeed;
|
|
|
|
if (pci_read(bus, dev, func, ptr + PCI_PCIE_XCAP, &xcap) != 0)
|
|
return;
|
|
|
|
if (PCI_PCIE_XCAP_VER(xcap) >= 2) {
|
|
if (pci_read(bus, dev, func, ptr + PCI_PCIE_LCAP2, &lcap2) != 0)
|
|
lcap2 = 0;
|
|
else
|
|
cspeed = (lcap2 & 0x0e) >> 1;
|
|
}
|
|
|
|
if (pci_read(bus, dev, func, ptr + PCI_PCIE_LCAP, &lcap) != 0)
|
|
return;
|
|
if (lcap2 == 0)
|
|
cspeed = lcap & 0x0f;
|
|
|
|
if (pci_read(bus, dev, func, ptr + PCI_PCIE_LCSR, &sreg) != 0)
|
|
return;
|
|
sreg = sreg >> 16;
|
|
|
|
cwidth = (lcap >> 4) & 0x3f;
|
|
if (cwidth == 0)
|
|
return;
|
|
|
|
swidth = (sreg >> 4) & 0x3f;
|
|
sspeed = sreg & 0x0f;
|
|
|
|
printf("\t Link Speed: ");
|
|
print_pcie_ls(sspeed);
|
|
printf(" / ");
|
|
print_pcie_ls(cspeed);
|
|
|
|
printf(" GT/s Link Width: x%d / x%d\n", swidth, cwidth);
|
|
}
|
|
|
|
void
|
|
dump_pcie_enhanced_caplist(int bus, int dev, int func)
|
|
{
|
|
u_int32_t reg;
|
|
u_int32_t capidx;
|
|
u_int16_t ptr;
|
|
u_int16_t ecap;
|
|
|
|
ptr = PCI_PCIE_ECAP;
|
|
|
|
do {
|
|
if (pci_read(bus, dev, func, ptr, ®) != 0)
|
|
return;
|
|
|
|
if (PCI_PCIE_ECAP_ID(reg) == 0xffff &&
|
|
PCI_PCIE_ECAP_NEXT(reg) == PCI_PCIE_ECAP_LAST)
|
|
return;
|
|
|
|
ecap = PCI_PCIE_ECAP_ID(reg);
|
|
if (ecap >= nitems(pci_enhanced_capnames))
|
|
capidx = 0;
|
|
else
|
|
capidx = ecap;
|
|
|
|
printf("\t0x%04x: Enhanced Capability 0x%02x: ", ptr, ecap);
|
|
printf("%s\n", pci_enhanced_capnames[capidx]);
|
|
|
|
ptr = PCI_PCIE_ECAP_NEXT(reg);
|
|
|
|
} while (ptr != PCI_PCIE_ECAP_LAST);
|
|
}
|
|
|
|
void
|
|
dump_caplist(int bus, int dev, int func, u_int8_t ptr)
|
|
{
|
|
u_int32_t reg;
|
|
u_int8_t cap;
|
|
|
|
if (pci_read(bus, dev, func, PCI_COMMAND_STATUS_REG, ®) != 0)
|
|
return;
|
|
if (!(reg & PCI_STATUS_CAPLIST_SUPPORT))
|
|
return;
|
|
|
|
if (pci_read(bus, dev, func, ptr, ®) != 0)
|
|
return;
|
|
ptr = PCI_CAPLIST_PTR(reg);
|
|
while (ptr != 0) {
|
|
if (pci_read(bus, dev, func, ptr, ®) != 0)
|
|
return;
|
|
cap = PCI_CAPLIST_CAP(reg);
|
|
printf("\t0x%04x: Capability 0x%02x: ", ptr, cap);
|
|
if (cap >= nitems(pci_capnames))
|
|
cap = 0;
|
|
printf("%s\n", pci_capnames[cap]);
|
|
switch (cap) {
|
|
case PCI_CAP_PWRMGMT:
|
|
dump_pci_powerstate(bus, dev, func, ptr);
|
|
break;
|
|
case PCI_CAP_PCIEXPRESS:
|
|
dump_pcie_linkspeed(bus, dev, func, ptr);
|
|
dump_pcie_enhanced_caplist(bus, dev, func);
|
|
break;
|
|
}
|
|
ptr = PCI_CAPLIST_NEXT(reg);
|
|
}
|
|
}
|
|
|
|
void
|
|
dump_bars(int bus, int dev, int func, int end)
|
|
{
|
|
const char *memtype;
|
|
u_int64_t mem;
|
|
u_int64_t mask;
|
|
u_int32_t reg, reg1;
|
|
int bar;
|
|
|
|
for (bar = PCI_MAPREG_START; bar < end; bar += 0x4) {
|
|
if (pci_read(bus, dev, func, bar, ®) != 0 ||
|
|
pci_readmask(bus, dev, func, bar, ®1) != 0)
|
|
warn("unable to read PCI_MAPREG 0x%02x", bar);
|
|
|
|
printf("\t0x%04x: BAR ", bar);
|
|
|
|
if (reg == 0 && reg1 == 0) {
|
|
printf("empty (%08x)\n", reg);
|
|
continue;
|
|
}
|
|
|
|
switch (PCI_MAPREG_TYPE(reg)) {
|
|
case PCI_MAPREG_TYPE_MEM:
|
|
printf("mem ");
|
|
if (PCI_MAPREG_MEM_PREFETCHABLE(reg))
|
|
printf("prefetchable ");
|
|
|
|
memtype = "32bit 1m";
|
|
switch (PCI_MAPREG_MEM_TYPE(reg)) {
|
|
case PCI_MAPREG_MEM_TYPE_32BIT:
|
|
memtype = "32bit";
|
|
case PCI_MAPREG_MEM_TYPE_32BIT_1M:
|
|
printf("%s ", memtype);
|
|
|
|
printf("addr: 0x%08x/0x%08x\n",
|
|
PCI_MAPREG_MEM_ADDR(reg),
|
|
PCI_MAPREG_MEM_SIZE(reg1));
|
|
|
|
break;
|
|
case PCI_MAPREG_MEM_TYPE_64BIT:
|
|
mem = reg;
|
|
mask = reg1;
|
|
bar += 0x04;
|
|
if (pci_read(bus, dev, func, bar, ®) != 0 ||
|
|
pci_readmask(bus, dev, func, bar, ®1) != 0)
|
|
warn("unable to read 0x%02x", bar);
|
|
|
|
mem |= (u_int64_t)reg << 32;
|
|
mask |= (u_int64_t)reg1 << 32;
|
|
|
|
printf("64bit addr: 0x%016llx/0x%08llx\n",
|
|
PCI_MAPREG_MEM64_ADDR(mem),
|
|
PCI_MAPREG_MEM64_SIZE(mask));
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case PCI_MAPREG_TYPE_IO:
|
|
printf("io addr: 0x%08x/0x%04x\n",
|
|
PCI_MAPREG_IO_ADDR(reg),
|
|
PCI_MAPREG_IO_SIZE(reg1));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
dump_type0(int bus, int dev, int func)
|
|
{
|
|
u_int32_t reg;
|
|
|
|
dump_bars(bus, dev, func, PCI_MAPREG_END);
|
|
|
|
if (pci_read(bus, dev, func, PCI_CARDBUS_CIS_REG, ®) != 0)
|
|
warn("unable to read PCI_CARDBUS_CIS_REG");
|
|
printf("\t0x%04x: Cardbus CIS: %08x\n", PCI_CARDBUS_CIS_REG, reg);
|
|
|
|
if (pci_read(bus, dev, func, PCI_SUBSYS_ID_REG, ®) != 0)
|
|
warn("unable to read PCI_SUBSYS_ID_REG");
|
|
printf("\t0x%04x: Subsystem Vendor ID: %04x Product ID: %04x\n",
|
|
PCI_SUBSYS_ID_REG, PCI_VENDOR(reg), PCI_PRODUCT(reg));
|
|
|
|
if (pci_read(bus, dev, func, PCI_ROM_REG, ®) != 0)
|
|
warn("unable to read PCI_ROM_REG");
|
|
printf("\t0x%04x: Expansion ROM Base Address: %08x\n",
|
|
PCI_ROM_REG, reg);
|
|
|
|
if (pci_read(bus, dev, func, 0x38, ®) != 0)
|
|
warn("unable to read 0x38 (reserved)");
|
|
printf("\t0x%04x: %08x\n", 0x38, reg);
|
|
|
|
if (pci_read(bus, dev, func, PCI_INTERRUPT_REG, ®) != 0)
|
|
warn("unable to read PCI_INTERRUPT_REG");
|
|
printf("\t0x%04x: Interrupt Pin: %02x Line: %02x Min Gnt: %02x"
|
|
" Max Lat: %02x\n", PCI_INTERRUPT_REG, PCI_INTERRUPT_PIN(reg),
|
|
PCI_INTERRUPT_LINE(reg), PCI_MIN_GNT(reg), PCI_MAX_LAT(reg));
|
|
}
|
|
|
|
void
|
|
dump_type1(int bus, int dev, int func)
|
|
{
|
|
u_int32_t reg;
|
|
|
|
dump_bars(bus, dev, func, PCI_MAPREG_PPB_END);
|
|
|
|
if (pci_read(bus, dev, func, PCI_PRIBUS_1, ®) != 0)
|
|
warn("unable to read PCI_PRIBUS_1");
|
|
printf("\t0x%04x: Primary Bus: %d Secondary Bus: %d "
|
|
"Subordinate Bus: %d \n\t Secondary Latency Timer: %02x\n",
|
|
PCI_PRIBUS_1, (reg >> 0) & 0xff, (reg >> 8) & 0xff,
|
|
(reg >> 16) & 0xff, (reg >> 24) & 0xff);
|
|
|
|
if (pci_read(bus, dev, func, PCI_IOBASEL_1, ®) != 0)
|
|
warn("unable to read PCI_IOBASEL_1");
|
|
printf("\t0x%04x: I/O Base: %02x I/O Limit: %02x "
|
|
"Secondary Status: %04x\n", PCI_IOBASEL_1, (reg >> 0 ) & 0xff,
|
|
(reg >> 8) & 0xff, (reg >> 16) & 0xffff);
|
|
|
|
if (pci_read(bus, dev, func, PCI_MEMBASE_1, ®) != 0)
|
|
warn("unable to read PCI_MEMBASE_1");
|
|
printf("\t0x%04x: Memory Base: %04x Memory Limit: %04x\n",
|
|
PCI_MEMBASE_1, (reg >> 0) & 0xffff, (reg >> 16) & 0xffff);
|
|
|
|
if (pci_read(bus, dev, func, PCI_PMBASEL_1, ®) != 0)
|
|
warn("unable to read PCI_PMBASEL_1");
|
|
printf("\t0x%04x: Prefetch Memory Base: %04x "
|
|
"Prefetch Memory Limit: %04x\n", PCI_PMBASEL_1,
|
|
(reg >> 0) & 0xffff, (reg >> 16) & 0xffff);
|
|
|
|
#undef PCI_PMBASEH_1
|
|
#define PCI_PMBASEH_1 0x28
|
|
if (pci_read(bus, dev, func, PCI_PMBASEH_1, ®) != 0)
|
|
warn("unable to read PCI_PMBASEH_1");
|
|
printf("\t0x%04x: Prefetch Memory Base Upper 32 Bits: %08x\n",
|
|
PCI_PMBASEH_1, reg);
|
|
|
|
#undef PCI_PMLIMITH_1
|
|
#define PCI_PMLIMITH_1 0x2c
|
|
if (pci_read(bus, dev, func, PCI_PMLIMITH_1, ®) != 0)
|
|
warn("unable to read PCI_PMLIMITH_1");
|
|
printf("\t0x%04x: Prefetch Memory Limit Upper 32 Bits: %08x\n",
|
|
PCI_PMLIMITH_1, reg);
|
|
|
|
#undef PCI_IOBASEH_1
|
|
#define PCI_IOBASEH_1 0x30
|
|
if (pci_read(bus, dev, func, PCI_IOBASEH_1, ®) != 0)
|
|
warn("unable to read PCI_IOBASEH_1");
|
|
printf("\t0x%04x: I/O Base Upper 16 Bits: %04x "
|
|
"I/O Limit Upper 16 Bits: %04x\n", PCI_IOBASEH_1,
|
|
(reg >> 0) & 0xffff, (reg >> 16) & 0xffff);
|
|
|
|
#define PCI_PPB_ROM_REG 0x38
|
|
if (pci_read(bus, dev, func, PCI_PPB_ROM_REG, ®) != 0)
|
|
warn("unable to read PCI_PPB_ROM_REG");
|
|
printf("\t0x%04x: Expansion ROM Base Address: %08x\n",
|
|
PCI_PPB_ROM_REG, reg);
|
|
|
|
if (pci_read(bus, dev, func, PCI_INTERRUPT_REG, ®) != 0)
|
|
warn("unable to read PCI_INTERRUPT_REG");
|
|
printf("\t0x%04x: Interrupt Pin: %02x Line: %02x "
|
|
"Bridge Control: %04x\n",
|
|
PCI_INTERRUPT_REG, PCI_INTERRUPT_PIN(reg),
|
|
PCI_INTERRUPT_LINE(reg), reg >> 16);
|
|
}
|
|
|
|
void
|
|
dump_type2(int bus, int dev, int func)
|
|
{
|
|
u_int32_t reg;
|
|
|
|
if (pci_read(bus, dev, func, PCI_MAPREG_START, ®) != 0)
|
|
warn("unable to read PCI_MAPREG\n");
|
|
printf("\t0x%04x: Cardbus Control Registers Base Address: %08x\n",
|
|
PCI_MAPREG_START, reg);
|
|
|
|
if (pci_read(bus, dev, func, PCI_PRIBUS_2, ®) != 0)
|
|
warn("unable to read PCI_PRIBUS_2");
|
|
printf("\t0x%04x: Primary Bus: %d Cardbus Bus: %d "
|
|
"Subordinate Bus: %d \n\t Cardbus Latency Timer: %02x\n",
|
|
PCI_PRIBUS_2, (reg >> 0) & 0xff, (reg >> 8) & 0xff,
|
|
(reg >> 16) & 0xff, (reg >> 24) & 0xff);
|
|
|
|
if (pci_read(bus, dev, func, PCI_MEMBASE0_2, ®) != 0)
|
|
warn("unable to read PCI_MEMBASE0_2\n");
|
|
printf("\t0x%04x: Memory Base 0: %08x\n", PCI_MEMBASE0_2, reg);
|
|
|
|
if (pci_read(bus, dev, func, PCI_MEMLIMIT0_2, ®) != 0)
|
|
warn("unable to read PCI_MEMLIMIT0_2\n");
|
|
printf("\t0x%04x: Memory Limit 0: %08x\n", PCI_MEMLIMIT0_2, reg);
|
|
|
|
if (pci_read(bus, dev, func, PCI_MEMBASE1_2, ®) != 0)
|
|
warn("unable to read PCI_MEMBASE1_2\n");
|
|
printf("\t0x%04x: Memory Base 1: %08x\n", PCI_MEMBASE1_2, reg);
|
|
|
|
if (pci_read(bus, dev, func, PCI_MEMLIMIT1_2, ®) != 0)
|
|
warn("unable to read PCI_MEMLIMIT1_2\n");
|
|
printf("\t0x%04x: Memory Limit 1: %08x\n", PCI_MEMLIMIT1_2, reg);
|
|
|
|
if (pci_read(bus, dev, func, PCI_IOBASE0_2, ®) != 0)
|
|
warn("unable to read PCI_IOBASE0_2\n");
|
|
printf("\t0x%04x: I/O Base 0: %08x\n", PCI_IOBASE0_2, reg);
|
|
|
|
if (pci_read(bus, dev, func, PCI_IOLIMIT0_2, ®) != 0)
|
|
warn("unable to read PCI_IOLIMIT0_2\n");
|
|
printf("\t0x%04x: I/O Limit 0: %08x\n", PCI_IOLIMIT0_2, reg);
|
|
|
|
if (pci_read(bus, dev, func, PCI_IOBASE1_2, ®) != 0)
|
|
warn("unable to read PCI_IOBASE1_2\n");
|
|
printf("\t0x%04x: I/O Base 1: %08x\n", PCI_IOBASE1_2, reg);
|
|
|
|
if (pci_read(bus, dev, func, PCI_IOLIMIT1_2, ®) != 0)
|
|
warn("unable to read PCI_IOLIMIT1_2\n");
|
|
printf("\t0x%04x: I/O Limit 1: %08x\n", PCI_IOLIMIT1_2, reg);
|
|
|
|
if (pci_read(bus, dev, func, PCI_INTERRUPT_REG, ®) != 0)
|
|
warn("unable to read PCI_INTERRUPT_REG");
|
|
printf("\t0x%04x: Interrupt Pin: %02x Line: %02x "
|
|
"Bridge Control: %04x\n",
|
|
PCI_INTERRUPT_REG, PCI_INTERRUPT_PIN(reg),
|
|
PCI_INTERRUPT_LINE(reg), reg >> 16);
|
|
|
|
if (pci_read(bus, dev, func, PCI_SUBVEND_2, ®) != 0)
|
|
warn("unable to read PCI_SUBVEND_2");
|
|
printf("\t0x%04x: Subsystem Vendor ID: %04x Product ID: %04x\n",
|
|
PCI_SUBVEND_2, PCI_VENDOR(reg), PCI_PRODUCT(reg));
|
|
|
|
if (pci_read(bus, dev, func, PCI_PCCARDIF_2, ®) != 0)
|
|
warn("unable to read PCI_PCCARDIF_2\n");
|
|
printf("\t0x%04x: 16-bit Legacy Mode Base Address: %08x\n",
|
|
PCI_PCCARDIF_2, reg);
|
|
}
|
|
|
|
void
|
|
dump(int bus, int dev, int func)
|
|
{
|
|
u_int32_t reg;
|
|
u_int8_t capptr = PCI_CAPLISTPTR_REG;
|
|
|
|
if (pci_read(bus, dev, func, PCI_ID_REG, ®) != 0)
|
|
warn("unable to read PCI_ID_REG");
|
|
printf("\t0x%04x: Vendor ID: %04x Product ID: %04x\n", PCI_ID_REG,
|
|
PCI_VENDOR(reg), PCI_PRODUCT(reg));
|
|
|
|
if (pci_read(bus, dev, func, PCI_COMMAND_STATUS_REG, ®) != 0)
|
|
warn("unable to read PCI_COMMAND_STATUS_REG");
|
|
printf("\t0x%04x: Command: %04x Status: %04x\n",
|
|
PCI_COMMAND_STATUS_REG, reg & 0xffff, (reg >> 16) & 0xffff);
|
|
|
|
if (pci_read(bus, dev, func, PCI_CLASS_REG, ®) != 0)
|
|
warn("unable to read PCI_CLASS_REG");
|
|
printf("\t0x%04x: Class: %02x Subclass: %02x Interface: %02x "
|
|
"Revision: %02x\n", PCI_CLASS_REG, PCI_CLASS(reg),
|
|
PCI_SUBCLASS(reg), PCI_INTERFACE(reg), PCI_REVISION(reg));
|
|
|
|
if (pci_read(bus, dev, func, PCI_BHLC_REG, ®) != 0)
|
|
warn("unable to read PCI_BHLC_REG");
|
|
printf("\t0x%04x: BIST: %02x Header Type: %02x Latency Timer: %02x "
|
|
"Cache Line Size: %02x\n", PCI_BHLC_REG, PCI_BIST(reg),
|
|
PCI_HDRTYPE(reg), PCI_LATTIMER(reg), PCI_CACHELINE(reg));
|
|
|
|
switch (PCI_HDRTYPE_TYPE(reg)) {
|
|
case 2:
|
|
dump_type2(bus, dev, func);
|
|
capptr = PCI_CARDBUS_CAPLISTPTR_REG;
|
|
break;
|
|
case 1:
|
|
dump_type1(bus, dev, func);
|
|
break;
|
|
case 0:
|
|
dump_type0(bus, dev, func);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
dump_caplist(bus, dev, func, capptr);
|
|
}
|
|
|
|
void
|
|
hexdump(int bus, int dev, int func, int size)
|
|
{
|
|
u_int32_t reg;
|
|
int i;
|
|
|
|
for (i = 0; i < size; i += 4) {
|
|
if (pci_read(bus, dev, func, i, ®) != 0) {
|
|
if (errno == EINVAL)
|
|
return;
|
|
warn("unable to read 0x%02x", i);
|
|
}
|
|
|
|
if ((i % 16) == 0)
|
|
printf("\t0x%04x:", i);
|
|
printf(" %08x", reg);
|
|
|
|
if ((i % 16) == 12)
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
int
|
|
pci_nfuncs(int bus, int dev)
|
|
{
|
|
u_int32_t hdr;
|
|
|
|
if (pci_read(bus, dev, 0, PCI_BHLC_REG, &hdr) != 0)
|
|
return (-1);
|
|
|
|
return (PCI_HDRTYPE_MULTIFN(hdr) ? 8 : 1);
|
|
}
|
|
|
|
int
|
|
pci_read(int bus, int dev, int func, u_int32_t reg, u_int32_t *val)
|
|
{
|
|
struct pci_io io;
|
|
int rv;
|
|
|
|
bzero(&io, sizeof(io));
|
|
io.pi_sel.pc_bus = bus;
|
|
io.pi_sel.pc_dev = dev;
|
|
io.pi_sel.pc_func = func;
|
|
io.pi_reg = reg;
|
|
io.pi_width = 4;
|
|
|
|
rv = ioctl(pcifd, PCIOCREAD, &io);
|
|
if (rv != 0)
|
|
return (rv);
|
|
|
|
*val = io.pi_data;
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
pci_readmask(int bus, int dev, int func, u_int32_t reg, u_int32_t *val)
|
|
{
|
|
struct pci_io io;
|
|
int rv;
|
|
|
|
bzero(&io, sizeof(io));
|
|
io.pi_sel.pc_bus = bus;
|
|
io.pi_sel.pc_dev = dev;
|
|
io.pi_sel.pc_func = func;
|
|
io.pi_reg = reg;
|
|
io.pi_width = 4;
|
|
|
|
rv = ioctl(pcifd, PCIOCREADMASK, &io);
|
|
if (rv != 0)
|
|
return (rv);
|
|
|
|
*val = io.pi_data;
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
dump_rom(int bus, int dev, int func)
|
|
{
|
|
struct pci_rom rom;
|
|
u_int32_t cr, addr;
|
|
|
|
if (pci_read(bus, dev, func, PCI_ROM_REG, &addr) != 0 ||
|
|
pci_read(bus, dev, func, PCI_CLASS_REG, &cr) != 0)
|
|
return (errno);
|
|
|
|
if (addr == 0 && PCI_CLASS(cr) == PCI_CLASS_DISPLAY &&
|
|
PCI_SUBCLASS(cr) == PCI_SUBCLASS_DISPLAY_VGA)
|
|
return dump_vga_bios();
|
|
|
|
bzero(&rom, sizeof(rom));
|
|
rom.pr_sel.pc_bus = bus;
|
|
rom.pr_sel.pc_dev = dev;
|
|
rom.pr_sel.pc_func = func;
|
|
if (ioctl(pcifd, PCIOCGETROMLEN, &rom))
|
|
return (errno);
|
|
|
|
rom.pr_rom = malloc(rom.pr_romlen);
|
|
if (rom.pr_rom == NULL)
|
|
return (ENOMEM);
|
|
|
|
if (ioctl(pcifd, PCIOCGETROM, &rom))
|
|
return (errno);
|
|
|
|
if (write(romfd, rom.pr_rom, rom.pr_romlen) == -1)
|
|
return (errno);
|
|
|
|
return (0);
|
|
}
|
|
|
|
#define VGA_BIOS_ADDR 0xc0000
|
|
#define VGA_BIOS_LEN 0x10000
|
|
|
|
int
|
|
dump_vga_bios(void)
|
|
{
|
|
#if defined(__amd64__) || defined(__i386__)
|
|
void *bios;
|
|
int fd;
|
|
|
|
fd = open(_PATH_MEM, O_RDONLY, 0777);
|
|
if (fd == -1)
|
|
err(1, "%s", _PATH_MEM);
|
|
|
|
bios = malloc(VGA_BIOS_LEN);
|
|
if (bios == NULL)
|
|
return (ENOMEM);
|
|
|
|
if (pread(fd, bios, VGA_BIOS_LEN, VGA_BIOS_ADDR) == -1)
|
|
err(1, "%s", _PATH_MEM);
|
|
|
|
if (write(romfd, bios, VGA_BIOS_LEN) == -1) {
|
|
free(bios);
|
|
return (errno);
|
|
}
|
|
|
|
free(bios);
|
|
|
|
return (0);
|
|
#else
|
|
return (ENODEV);
|
|
#endif
|
|
}
|