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, &reg, &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, &regval) != 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);
 }

Reply via email to