aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/pciconf
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/pciconf')
-rw-r--r--usr.sbin/pciconf/pciconf.839
-rw-r--r--usr.sbin/pciconf/pciconf.c182
2 files changed, 194 insertions, 27 deletions
diff --git a/usr.sbin/pciconf/pciconf.8 b/usr.sbin/pciconf/pciconf.8
index f0f47b8aabf9..eb2e038d7e82 100644
--- a/usr.sbin/pciconf/pciconf.8
+++ b/usr.sbin/pciconf/pciconf.8
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd November 23, 2015
+.Dd June 14, 2018
.Dt PCICONF 8
.Os
.Sh NAME
@@ -40,6 +40,8 @@
.Fl r Oo Fl b | h Oc Ar device addr Ns Op : Ns Ar addr2
.Nm
.Fl w Oo Fl b | h Oc Ar device addr value
+.Nm
+.Fl D Oo Fl b | h | x Oc Ar device addr Op start Ns Op : Ns Ar count
.Sh DESCRIPTION
The
.Nm
@@ -305,17 +307,38 @@ into a configuration space register at byte offset
.Ar addr
of device
.Ar selector .
-For both operations, the flags
-.Fl b
+.Pp
+The
+.Fl D
+option request a dump of the specified BAR.
+Dump is performed to the standard output, raw register values
+are written.
+Use
+.Xr hexdump 1
+to convert them to human-readable dump,
+or redirect into a file to save the snapshot of the device state.
+Optionally, the
+.Ar start
and
-.Fl h
+.Ar count
+of the registers dumped can be specified, in multiple of the operation width,
+see next paragraph.
+.Pp
+For read, write, and dump operations, the flags
+.Fl b ,
+.Fl h ,
+and
+.Fl x
select the width of the operation;
.Fl b
indicates a byte operation, and
.Fl h
indicates a halfword (two-byte) operation.
+.Fl x
+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.
.Sh ENVIRONMENT
PCI vendor and device information is read from
.Pa /usr/local/share/pciids/pci.ids .
@@ -368,3 +391,11 @@ to provide the device with a driver KLD, and reading of configuration space
registers may cause a failure in badly designed
.Tn PCI
chips.
+.Pp
+There is currently no way to specify the caching mode for the mapping
+established by the
+.Fl D
+option,
+.Nm
+always uses uncached access.
+This is fine for control register BARs.
diff --git a/usr.sbin/pciconf/pciconf.c b/usr.sbin/pciconf/pciconf.c
index 692eae395603..47b8f96422de 100644
--- a/usr.sbin/pciconf/pciconf.c
+++ b/usr.sbin/pciconf/pciconf.c
@@ -34,6 +34,13 @@ static const char rcsid[] =
#include <sys/types.h>
#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/pciio.h>
+#include <sys/queue.h>
+
+#include <vm/vm.h>
+
+#include <dev/pci/pcireg.h>
#include <assert.h>
#include <ctype.h>
@@ -44,10 +51,6 @@ static const char rcsid[] =
#include <stdio.h>
#include <string.h>
#include <unistd.h>
-#include <sys/pciio.h>
-#include <sys/queue.h>
-
-#include <dev/pci/pcireg.h>
#include "pathnames.h"
#include "pciconf.h"
@@ -82,32 +85,37 @@ static int load_vendors(void);
static void readit(const char *, const char *, int);
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 int exitstatus = 0;
static void
usage(void)
{
- fprintf(stderr, "%s\n%s\n%s\n%s\n",
- "usage: pciconf -l [-BbcevV] [device]",
- " pciconf -a device",
- " pciconf -r [-b | -h] device addr[:addr2]",
- " pciconf -w [-b | -h] device addr value");
- exit (1);
+
+ fprintf(stderr, "%s",
+ "usage: pciconf -l [-BbcevV] [device]\n"
+ " 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]]"
+ "\n");
+ exit(1);
}
int
main(int argc, char **argv)
{
- int c;
- int listmode, readmode, writemode, attachedmode;
+ int c, width;
+ int listmode, readmode, writemode, attachedmode, dumpbarmode;
int bars, bridge, caps, errors, verbose, vpd;
- int byte, isshort;
- listmode = readmode = writemode = attachedmode = 0;
- bars = bridge = caps = errors = verbose = vpd = byte = isshort = 0;
+ listmode = readmode = writemode = attachedmode = dumpbarmode = 0;
+ bars = bridge = caps = errors = verbose = vpd= 0;
+ width = 4;
- while ((c = getopt(argc, argv, "aBbcehlrwVv")) != -1) {
+ while ((c = getopt(argc, argv, "aBbcDehlrwVv")) != -1) {
switch(c) {
case 'a':
attachedmode = 1;
@@ -119,19 +127,23 @@ main(int argc, char **argv)
case 'b':
bars = 1;
- byte = 1;
+ width = 1;
break;
case 'c':
caps = 1;
break;
+ case 'D':
+ dumpbarmode = 1;
+ break;
+
case 'e':
errors = 1;
break;
case 'h':
- isshort = 1;
+ width = 2;
break;
case 'l':
@@ -154,6 +166,10 @@ main(int argc, char **argv)
vpd = 1;
break;
+ case 'x':
+ width = 8;
+ break;
+
default:
usage();
}
@@ -162,7 +178,9 @@ main(int argc, char **argv)
if ((listmode && optind >= argc + 1)
|| (writemode && optind + 3 != argc)
|| (readmode && optind + 2 != argc)
- || (attachedmode && optind + 1 != argc))
+ || (attachedmode && optind + 1 != argc)
+ || (dumpbarmode && (optind + 2 > argc || optind + 4 < argc))
+ || (width == 8 && !dumpbarmode))
usage();
if (listmode) {
@@ -171,16 +189,20 @@ main(int argc, char **argv)
} else if (attachedmode) {
chkattached(argv[optind]);
} else if (readmode) {
- readit(argv[optind], argv[optind + 1],
- byte ? 1 : isshort ? 2 : 4);
+ readit(argv[optind], argv[optind + 1], width);
} else if (writemode) {
writeit(argv[optind], argv[optind + 1], argv[optind + 2],
- byte ? 1 : isshort ? 2 : 4);
+ width);
+ } else if (dumpbarmode) {
+ dump_bar(argv[optind], argv[optind + 1],
+ optind + 2 < argc ? argv[optind + 2] : NULL,
+ optind + 3 < argc ? argv[optind + 3] : NULL,
+ width, verbose);
} else {
usage();
}
- return exitstatus;
+ return (exitstatus);
}
static void
@@ -1027,3 +1049,117 @@ chkattached(const char *name)
printf("%s: %s%s\n", name, pi.pi_data == 0 ? "not " : "", "attached");
close(fd);
}
+
+static void
+dump_bar(const char *name, const char *reg, const char *bar_start,
+ const char *bar_count, int width, int verbose)
+{
+ struct pci_bar_mmap pbm;
+ uint32_t *dd;
+ uint16_t *dh;
+ uint8_t *db;
+ uint64_t *dx, a, start, count;
+ char *el;
+ size_t res;
+ int fd;
+
+ start = 0;
+ if (bar_start != NULL) {
+ start = strtoul(bar_start, &el, 0);
+ if (*el != '\0')
+ errx(1, "Invalid bar start specification %s",
+ bar_start);
+ }
+ count = 0;
+ if (bar_count != NULL) {
+ count = strtoul(bar_count, &el, 0);
+ if (*el != '\0')
+ errx(1, "Invalid count specification %s",
+ 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_UNCACHEABLE; /* XXX */
+
+ 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)");
+
+ if (count == 0)
+ count = pbm.pbm_bar_length / width;
+ if (start + count < start || (start + count) * width < (uint64_t)width)
+ errx(1, "(start + count) x width overflow");
+ if ((start + count) * width > pbm.pbm_bar_length) {
+ if (start * width > pbm.pbm_bar_length)
+ count = 0;
+ else
+ count = (pbm.pbm_bar_length - start * width) / width;
+ }
+ if (verbose) {
+ fprintf(stderr,
+ "Dumping pci%d:%d:%d:%d BAR %x mapped base %p "
+ "off %#x length %#jx from %#jx count %#jx in %d-bytes\n",
+ pbm.pbm_sel.pc_domain, pbm.pbm_sel.pc_bus,
+ pbm.pbm_sel.pc_dev, pbm.pbm_sel.pc_func,
+ pbm.pbm_reg, pbm.pbm_map_base, pbm.pbm_bar_off,
+ pbm.pbm_bar_length, start, count, width);
+ }
+ switch (width) {
+ case 1:
+ db = (uint8_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base +
+ pbm.pbm_bar_off + start * width);
+ for (a = 0; a < count; a += width, db++) {
+ res = fwrite(db, width, 1, stdout);
+ if (res != 1) {
+ errx(1, "error writing to stdout");
+ break;
+ }
+ }
+ break;
+ case 2:
+ dh = (uint16_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base +
+ pbm.pbm_bar_off + start * width);
+ for (a = 0; a < count; a += width, dh++) {
+ res = fwrite(dh, width, 1, stdout);
+ if (res != 1) {
+ errx(1, "error writing to stdout");
+ break;
+ }
+ }
+ break;
+ case 4:
+ dd = (uint32_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base +
+ pbm.pbm_bar_off + start * width);
+ for (a = 0; a < count; a += width, dd++) {
+ res = fwrite(dd, width, 1, stdout);
+ if (res != 1) {
+ errx(1, "error writing to stdout");
+ break;
+ }
+ }
+ break;
+ case 8:
+ dx = (uint64_t *)(uintptr_t)((uintptr_t)pbm.pbm_map_base +
+ pbm.pbm_bar_off + start * width);
+ for (a = 0; a < count; a += width, dx++) {
+ res = fwrite(dx, width, 1, stdout);
+ if (res != 1) {
+ errx(1, "error writing to stdout");
+ break;
+ }
+ }
+ break;
+ default:
+ errx(1, "invalid access width");
+ }
+
+ munmap((void *)pbm.pbm_map_base, pbm.pbm_map_length);
+ close(fd);
+}