Hi,
right now i use the following diff to poke around in the PCIe config
space. This diff enables
pcidump to read and write to a register. So far i used this mainly to
play with the Advanced
Error Reporting Capability some devices have.
$ pcidump 4:0:0:104
4:0:0: Broadcom BCM5754
0x0104: 0x00100000
This bit indicates an "Unsupported Request Error", the register
queried here is the
"Uncorrectable Error Status Register".
# pcidump 4:0:0:104:0x00100000
4:0:0: Broadcom BCM5754
0x0104: 0x00000000
pcidump shows the new value of the register after writing. By writing
a 1 to a status bit it
gets reset.
I implemented a check for the current securelevel because writing to
/dev/pci is only possible
for a securelevel smaller than 1.
I think this functionality can come in handy for people
writing/modifying device drivers.
Index: pcidump.8
===================================================================
--- pcidump.8 16 Jul 2013 11:13:34 -0000 1.12
+++ pcidump.8 27 Mar 2017 11:27:35 -0000
@@ -26,7 +26,7 @@
.Op Fl x | xx | xxx
.Op Fl d Ar pcidev
.Sm off
-.Op Ar bus : dev : func
+.Op Ar bus : dev : func [ : reg [ : val ] ]
.Sm on
.Nm pcidump
.Fl r Ar file
@@ -69,16 +69,29 @@ Shows a hexadecimal dump of the full PCI
Shows a hexadecimal dump of the PCIe extended config space.
.It Xo
.Sm off
-.Ar bus : dev : func
+.Ar bus : dev : func [ : reg [ : val ] ]
.Sm on
.Xc
Show information about the PCI device specified by the tuple given on
-the command line.
+the command line. If
+.Pa reg
+is used, the value of this register in the configuration space of
+.Pa func
+gets printed. If
+.Pa val
+is used, the register specified by
+.Pa reg
+will be loaded with the value specified by
+.Pa val .
If the
.Fl d
option is not given,
.Pa /dev/pci
is used.
+.It Xo
+.Xc
+The configuration space can only be written in a securelevel(7) lower
+than 1.
.El
.Sh FILES
.Bl -tag -width /dev/pci* -compact
@@ -86,7 +99,8 @@ is used.
Device files for accessing PCI domains.
.El
.Sh SEE ALSO
-.Xr pci 4
+.Xr pci 4 ,
+.Xr securelevel 7
.Sh HISTORY
The
.Nm
Index: pcidump.c
===================================================================
--- pcidump.c 25 Mar 2017 07:33:46 -0000 1.43
+++ pcidump.c 27 Mar 2017 11:24:10 -0000
@@ -19,6 +19,8 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/pciio.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
#include <stdio.h> /* need NULL for <dev/pci/*.h> */
@@ -37,19 +39,27 @@
#define PCIDEV "/dev/pci"
+#define PCI_CONFIG_SPACE_BEGIN 0x0
+#define PCIE_CONFIG_SPACE_END (PCIE_CONFIG_SPACE_SIZE - 1)
+#define PCI_CONFIG_ALIGNMENT 0x4
+#define REG_ALIGNMENT_OK(x) ((x) % PCI_CONFIG_ALIGNMENT ? 0 : 1)
+
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
__dead void usage(void);
+int get_securelevel(void);
void scanpcidomain(void);
-int probe(int, int, int);
+int probe(int, int, int, int, int);
+void chreg(int, int, int, int, int);
void dump(int, int, int);
void hexdump(int, int, int, int);
-const char *str2busdevfunc(const char *, int *, int *, int *);
+const char *str2busdevfunc(const char *, int *, int *, 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 *);
+int pci_write(int, int, int, u_int32_t, u_int32_t);
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);
@@ -67,7 +77,8 @@ usage(void)
extern char *__progname;
fprintf(stderr,
- "usage: %s [-v] [-x | -xx | -xxx] [-d pcidev] [bus:dev:func]\n"
+ "usage: %s [-v] [-x | -xx | -xxx] [-d pcidev]"
+ " [bus:dev:func[:reg[:val]]]\n"
" %s -r file [-d pcidev] bus:dev:func\n",
__progname, __progname);
exit(1);
@@ -139,7 +150,7 @@ int
main(int argc, char *argv[])
{
int nfuncs;
- int bus, dev, func;
+ int bus, dev, func, reg = -1, val = -1;
char pcidev[PATH_MAX] = PCIDEV;
char *romfile = NULL;
const char *errstr;
@@ -186,7 +197,10 @@ main(int argc, char *argv[])
dumpall = 0;
if (dumpall == 0) {
- pcifd = open(pcidev, O_RDONLY, 0777);
+ if (get_securelevel() < 1)
+ pcifd = open(pcidev, O_RDWR, 0777);
+ else
+ pcifd = open(pcidev, O_RDONLY, 0777);
if (pcifd == -1)
err(1, "%s", pcidev);
} else {
@@ -207,7 +221,7 @@ main(int argc, char *argv[])
}
if (argc == 1) {
- errstr = str2busdevfunc(argv[0], &bus, &dev, &func);
+ errstr = str2busdevfunc(argv[0], &bus, &dev, &func, ®, &val);
if (errstr != NULL)
errx(1, "\"%s\": %s", argv[0], errstr);
@@ -217,7 +231,7 @@ main(int argc, char *argv[])
else if (romfile)
error = dump_rom(bus, dev, func);
else
- error = probe(bus, dev, func);
+ error = probe(bus, dev, func, reg, val);
if (error != 0)
errc(1, error, "\"%s\"", argv[0]);
@@ -229,6 +243,25 @@ main(int argc, char *argv[])
return (0);
}
+int
+get_securelevel(void)
+{
+ int name[2], seclvl;
+ size_t len;
+
+ name[0] = CTL_KERN;
+ name[1] = KERN_SECURELVL;
+
+ len = sizeof(seclvl);
+
+ if (sysctl(name, 2, &seclvl, &len, NULL, 0) == -1) {
+ errx(1, "sysctl securelevel: %s", strerror(errno));
+ return (-1);
+ }
+
+ return seclvl;
+}
+
void
scanpcidomain(void)
{
@@ -239,17 +272,18 @@ scanpcidomain(void)
for (dev = 0; dev < 32; dev++) {
nfuncs = pci_nfuncs(bus, dev);
for (func = 0; func < nfuncs; func++) {
- probe(bus, dev, func);
+ probe(bus, dev, func, -1, -1);
}
}
}
}
const char *
-str2busdevfunc(const char *string, int *bus, int *dev, int *func)
+str2busdevfunc(const char *string, int *bus, int *dev, int *func, int *reg,
+ int *val)
{
const char *errstr;
- char b[80], *d, *f;
+ char b[80], *d, *f, *r, *v;
strlcpy(b, string, sizeof(b));
@@ -263,6 +297,15 @@ str2busdevfunc(const char *string, int *
return("function not specified");
*f++ = '\0';
+ r = strchr(f, ':');
+ if (r != NULL) {
+ *r++ = '\0';
+
+ v = strchr(r, ':');
+ if (v != NULL)
+ *v++ = '\0';
+ }
+
*bus = strtonum(b, 0, 255, &errstr);
if (errstr != NULL)
return (errstr);
@@ -273,11 +316,24 @@ str2busdevfunc(const char *string, int *
if (errstr != NULL)
return (errstr);
+ if (r != NULL) {
+ /* a non-PCIe function will return (u_int)-1 on a
+ * read above 0xfff */
+ if (sscanf(r, "%x", reg) == EOF
+ || *reg < PCI_CONFIG_SPACE_BEGIN
+ || *reg > PCIE_CONFIG_SPACE_END
+ || !REG_ALIGNMENT_OK(*reg))
+ return("invalid register");
+
+ if ((v != NULL) && (sscanf(v, "%x", val) == EOF))
+ return("invalid value");
+ }
+
return (NULL);
}
int
-probe(int bus, int dev, int func)
+probe(int bus, int dev, int func, int reg, int val)
{
u_int32_t id_reg;
const struct pci_known_vendor *pkv;
@@ -311,6 +367,8 @@ probe(int bus, int dev, int func)
(vendor == NULL) ? "unknown" : vendor,
(product == NULL) ? "unknown" : product);
+ if (reg > -1)
+ chreg(bus, dev, func, reg, val);
if (verbose)
dump(bus, dev, func);
if (hex > 0)
@@ -320,6 +378,25 @@ probe(int bus, int dev, int func)
}
void
+chreg(int bus, int dev, int func, int reg, int val)
+{
+ u_int32_t regval;
+ int tmp;
+
+ if (val > -1) {
+ if ((tmp = pci_write(bus, dev, func, reg, val)) != 0) {
+ err(1, "pci_write");
+ return;
+ }
+ }
+
+ if (pci_read(bus, dev, func, reg, ®val) != 0)
+ return;
+
+ printf("\t0x%04x: 0x%08x\n", reg, regval);
+}
+
+void
dump_pci_powerstate(int bus, int dev, int func, uint8_t ptr)
{
u_int32_t pmcsr;
@@ -800,6 +877,27 @@ pci_readmask(int bus, int dev, int func,
return (rv);
*val = io.pi_data;
+
+ return (0);
+}
+
+int
+pci_write(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;
+ io.pi_data = val;
+
+ rv = ioctl(pcifd, PCIOCWRITE, &io);
+ if (rv != 0)
+ return (rv);
return (0);
}