git: e63fbcc69402 - main - pciconf: Add option to write into a BAR region

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Wed, 24 Jun 2026 20:04:53 UTC
The branch main has been updated by jhb:

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

commit e63fbcc69402d4a1e8681a95710f0ec01a6837b4
Author:     Timo Völker <timo.voelker@fh-muenster.de>
AuthorDate: 2026-06-24 19:57:34 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
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);
 }