The branch main has been updated by jhb:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=e63fbcc69402d4a1e8681a95710f0ec01a6837b4

commit e63fbcc69402d4a1e8681a95710f0ec01a6837b4
Author:     Timo Völker <[email protected]>
AuthorDate: 2026-06-24 19:57:34 +0000
Commit:     John Baldwin <[email protected]>
CommitDate: 2026-06-24 19:57:34 +0000

    pciconf: Add option to write into a BAR region
    
    Add option -W that allows to write into a BAR region.
    
    Also, add an option -R that allows to read from a BAR region that
    works similar to -r that reads from the PCI config space.
    
    Reviewed by:    bcr (manpages), jhb
    Differential Revision:  https://reviews.freebsd.org/D55915
---
 usr.sbin/pciconf/pciconf.8 |  31 ++++++--
 usr.sbin/pciconf/pciconf.c | 180 ++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 187 insertions(+), 24 deletions(-)

diff --git a/usr.sbin/pciconf/pciconf.8 b/usr.sbin/pciconf/pciconf.8
index 7958a538f3d9..acb5fc4d039d 100644
--- a/usr.sbin/pciconf/pciconf.8
+++ b/usr.sbin/pciconf/pciconf.8
@@ -23,7 +23,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd March 10, 2026
+.Dd April 24, 2026
 .Dt PCICONF 8
 .Os
 .Sh NAME
@@ -42,6 +42,10 @@
 .Fl w Oo Fl b | h Oc Ar device addr value
 .Nm
 .Fl D Oo Fl b | h | x Oc Ar device bar Op Ar start Op Ns Ar count
+.Nm
+.Fl R Oo Fl b | h | x Oc Ar device bar start Ns Op : Ns Ar end
+.Nm
+.Fl W Oo Fl b | h | x Oc Ar device bar offset value
 .Sh DESCRIPTION
 The
 .Nm
@@ -341,7 +345,8 @@ of device
 .Pp
 The
 .Fl D
-option request a dump of the specified BAR.
+option requests a dump of the BAR specified by its offset in config space
+.Ar bar .
 Dump is performed to the standard output, raw register values
 are written.
 Use
@@ -353,9 +358,25 @@ Optionally, the
 and
 .Ar count
 of the registers dumped can be specified, in multiple of the operation width,
-see next paragraph.
+see below.
+.Pp
+The
+.Fl R
+option reads from the BAR specified by its offset in config space
+.Ar bar .
+It starts reading at byte offset
+.Ar start .
+The optional last argument
+.Ar end
+specifies a range to read.
+The
+.Fl W
+option writes the
+.Ar value
+to the BAR at byte offset
+.Ar offset .
 .Pp
-For read, write, and dump operations, the flags
+For read, write, BAR dump, BAR read, and BAR write operations, the flags
 .Fl b ,
 .Fl h ,
 and
@@ -369,7 +390,7 @@ indicates a halfword (two-byte) operation.
 indicates a quadword (four-byte) operation.
 The default is to read or
 write a longword (four bytes).
-The quadword mode is only valid for BAR dump.
+The quadword mode is only valid for BAR dump, BAR read, and BAR write.
 .Sh ENVIRONMENT
 PCI vendor and device information is read from
 .Pa /usr/local/share/pciids/pci.ids .
diff --git a/usr.sbin/pciconf/pciconf.c b/usr.sbin/pciconf/pciconf.c
index e2a3beeba6f7..f3c6a413826f 100644
--- a/usr.sbin/pciconf/pciconf.c
+++ b/usr.sbin/pciconf/pciconf.c
@@ -91,6 +91,10 @@ static void writeit(const char *, const char *, const char 
*, int);
 static void chkattached(const char *);
 static void dump_bar(const char *name, const char *reg, const char *bar_start,
     const char *bar_count, int width, int verbose);
+static void read_bar(const char *name, const char *bar, const char *start_end,
+    int width);
+static void write_bar(const char *name, const char *bar, const char 
*offset_str,
+    const char *value_str, int width);
 
 static int exitstatus = 0;
 
@@ -104,7 +108,9 @@ usage(void)
                "       pciconf -a device\n"
                "       pciconf -r [-b | -h] device addr[:addr2]\n"
                "       pciconf -w [-b | -h] device addr value\n"
-               "       pciconf -D [-b | -h | -x] device bar [start [count]]"
+               "       pciconf -D [-b | -h | -x] device bar [start [count]]\n"
+               "       pciconf -R [-b | -h | -x] device bar start[:end]\n"
+               "       pciconf -W [-b | -h | -x] device bar offset value"
                "\n");
        exit(1);
 }
@@ -113,14 +119,15 @@ int
 main(int argc, char **argv)
 {
        int c, width;
-       enum { NONE, LIST, TREE, READ, WRITE, ATTACHED, DUMPBAR } mode;
+       enum { NONE, LIST, TREE, READ, WRITE, ATTACHED, DUMPBAR, READBAR,
+           WRITEBAR } mode;
        int compact, bars, bridge, caps, errors, verbose, vpd;
 
        mode = NONE;
        compact = bars = bridge = caps = errors = verbose = vpd = 0;
        width = 4;
 
-       while ((c = getopt(argc, argv, "aBbcDehlrtwVvx")) != -1) {
+       while ((c = getopt(argc, argv, "aBbcDehlRrtWwVvx")) != -1) {
                switch(c) {
                case 'a':
                        mode = ATTACHED;
@@ -157,6 +164,10 @@ main(int argc, char **argv)
                        mode = LIST;
                        break;
 
+               case 'R':
+                       mode = READBAR;
+                       break;
+
                case 'r':
                        mode = READ;
                        break;
@@ -164,6 +175,11 @@ main(int argc, char **argv)
                case 't':
                        mode = TREE;
                        break;
+
+               case 'W':
+                       mode = WRITEBAR;
+                       break;
+
                case 'w':
                        mode = WRITE;
                        break;
@@ -221,6 +237,18 @@ main(int argc, char **argv)
                    optind + 3 < argc ? argv[optind + 3] : NULL, 
                    width, verbose);
                break;
+       case READBAR:
+               if (optind + 3 != argc)
+                       usage();
+               read_bar(argv[optind], argv[optind + 1], argv[optind + 2],
+                   width);
+               break;
+       case WRITEBAR:
+               if (optind + 4 != argc)
+                       usage();
+               write_bar(argv[optind], argv[optind + 1], argv[optind + 2],
+                   argv[optind + 3], width);
+               break;
        default:
                usage();
        }
@@ -1365,6 +1393,36 @@ chkattached(const char *name)
        close(fd);
 }
 
+static void
+map_bar(const char *name, const char *reg, int flags, struct pci_bar_mmap *pbm)
+{
+       int fd;
+       char *el;
+
+       memset(pbm, 0, sizeof(struct pci_bar_mmap));
+       pbm->pbm_sel = getsel(name);
+       pbm->pbm_reg = strtoul(reg, &el, 0);
+       if (*reg == '\0' || *el != '\0')
+               errx(1, "Invalid bar specification %s", reg);
+       pbm->pbm_memattr = VM_MEMATTR_DEVICE;
+       pbm->pbm_flags = flags;
+
+       fd = open(_PATH_DEVPCI, O_RDWR, 0);
+       if (fd < 0)
+               err(1, "%s", _PATH_DEVPCI);
+
+       if (ioctl(fd, PCIOCBARMMAP, pbm) < 0)
+               err(1, "ioctl(PCIOCBARMMAP)");
+
+       close(fd);
+}
+
+static void
+unmap_bar(struct pci_bar_mmap *pbm)
+{
+       munmap(pbm->pbm_map_base, pbm->pbm_map_length);
+}
+
 static void
 dump_bar(const char *name, const char *reg, const char *bar_start,
     const char *bar_count, int width, int verbose)
@@ -1376,7 +1434,6 @@ dump_bar(const char *name, const char *reg, const char 
*bar_start,
        uint64_t *dx, a, start, count;
        char *el;
        size_t res;
-       int fd;
 
        start = 0;
        if (bar_start != NULL) {
@@ -1393,19 +1450,7 @@ dump_bar(const char *name, const char *reg, const char 
*bar_start,
                            bar_count);
        }
 
-       pbm.pbm_sel = getsel(name);
-       pbm.pbm_reg = strtoul(reg, &el, 0);
-       if (*reg == '\0' || *el != '\0')
-               errx(1, "Invalid bar specification %s", reg);
-       pbm.pbm_flags = 0;
-       pbm.pbm_memattr = VM_MEMATTR_DEVICE;
-
-       fd = open(_PATH_DEVPCI, O_RDWR, 0);
-       if (fd < 0)
-               err(1, "%s", _PATH_DEVPCI);
-
-       if (ioctl(fd, PCIOCBARMMAP, &pbm) < 0)
-               err(1, "ioctl(PCIOCBARMMAP)");
+       map_bar(name, reg, 0, &pbm);
 
        if (count == 0)
                count = pbm.pbm_bar_length / width;
@@ -1475,6 +1520,103 @@ dump_bar(const char *name, const char *reg, const char 
*bar_start,
                errx(1, "invalid access width");
        }
 
-       munmap((void *)pbm.pbm_map_base, pbm.pbm_map_length);
-       close(fd);
+       unmap_bar(&pbm);
+}
+
+union tunion {
+       uint8_t one;
+       uint16_t two;
+       uint32_t four;
+       uint64_t eight;
+       uint8_t bytes[8];
+};
+
+static void
+read_bar(const char *name, const char *bar, const char *start_end, int width)
+{
+       struct pci_bar_mmap pbm;
+       long rstart, rend, r;
+       char *end;
+       int items_per_line, i;
+
+       rend = rstart = strtol(start_end, &end, 0);
+       if (*end == ':') {
+               end++;
+               rend = strtol(end, NULL, 0);
+       }
+
+       map_bar(name, bar, 0, &pbm);
+
+       items_per_line = 16 / width;
+       for (i = 1, r = rstart; r <= rend; i++, r += width) {
+               uintptr_t addr;
+               union tunion t;
+               int j;
+
+               addr = (uintptr_t)pbm.pbm_map_base + pbm.pbm_bar_off + r;
+               switch (width) {
+               case 1:
+                       t.one = *((uint8_t *)addr);
+                       break;
+               case 2:
+                       t.two = *((uint16_t *)addr);
+                       break;
+               case 4:
+                       t.four = *((uint32_t *)addr);
+                       break;
+               case 8:
+                       t.eight = *((uint64_t *)addr);
+                       break;
+               default:
+                       errx(1, "invalid read width");
+               }
+               for (j = 0; j < width; j++) {
+                       printf("%02x", t.bytes[j]);
+               }
+               /* Use a double space in the middle when outputting bytes. */
+               if (width == 1 && (i % 16) == 8)
+                       putchar(' ');
+               putchar(i % items_per_line == 0 ? '\n' : ' ');
+       }
+       if (i % items_per_line != 1)
+               putchar('\n');
+
+       unmap_bar(&pbm);
+}
+
+static void
+write_bar(const char *name, const char *bar, const char *offset_str,
+    const char *value_str, int width)
+{
+       struct pci_bar_mmap pbm;
+       uint64_t offset, value;
+       uintptr_t addr;
+       char *el;
+
+       offset = strtoul(offset_str, &el, 0);
+       if (*el != '\0')
+               errx(1, "Invalid bar offset specification %s", offset_str);
+       value = strtoul(value_str, (char **)0, 0); /* XXX error check */
+
+       map_bar(name, bar, PCIIO_BAR_MMAP_RW, &pbm);
+
+       addr = (uintptr_t)pbm.pbm_map_base + pbm.pbm_bar_off + offset;
+       switch (width) {
+       case 1:
+               *((uint8_t *)addr) = value;
+               break;
+       case 2:
+               *((uint16_t *)addr) = value;
+               break;
+       case 4:
+               *((uint32_t *)addr) = value;
+               break;
+       case 8:
+               *((uint64_t *)addr) = value;
+               break;
+       default:
+               errx(1, "invalid write width");
+       }
+
+       unmap_bar(&pbm);
 }

Reply via email to