- Allow PCI devices that are attached to a driver to be identified by their

device name instead of just the selector.
- Accept an optional device argument to -l to restrict the output to only
  listing details about a single device.  This is mostly useful in
  conjunction with other flags like -e or -c to allow a user to query
  details about a single device.

MFC after:	1 week
This commit is contained in:
John Baldwin 2014-01-20 15:51:02 +00:00
parent 3443b2bc31
commit c9341dd343
2 changed files with 130 additions and 29 deletions

View File

@ -25,7 +25,7 @@
.\" .\"
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd June 1, 2012 .Dd January 20, 2014
.Dt PCICONF 8 .Dt PCICONF 8
.Os .Os
.Sh NAME .Sh NAME
@ -33,13 +33,13 @@
.Nd diagnostic utility for the PCI bus .Nd diagnostic utility for the PCI bus
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Fl l Op Fl bcev .Fl l Oo Fl bcev Oc Op Ar device
.Nm .Nm
.Fl a Ar selector .Fl a Ar device
.Nm .Nm
.Fl r Oo Fl b | h Oc Ar selector addr Ns Op : Ns Ar addr2 .Fl r Oo Fl b | h Oc Ar device addr Ns Op : Ns Ar addr2
.Nm .Nm
.Fl w Oo Fl b | h Oc Ar selector addr value .Fl w Oo Fl b | h Oc Ar device addr value
.Sh DESCRIPTION .Sh DESCRIPTION
The The
.Nm .Nm
@ -54,7 +54,9 @@ normally only the super-user.
.Pp .Pp
With the With the
.Fl l .Fl l
option, it lists all devices found by the boot probe in the following format: option,
.Nm
lists PCI devices in the following format:
.Bd -literal .Bd -literal
foo0@pci0:0:4:0: class=0x010000 card=0x00000000 chip=0x000f1000 rev=0x01 \ foo0@pci0:0:4:0: class=0x010000 card=0x00000000 chip=0x000f1000 rev=0x01 \
hdr=0x00 hdr=0x00
@ -65,16 +67,14 @@ hdr=0x00
.Ed .Ed
.Pp .Pp
The first column gives the The first column gives the
device name, unit number, and driver name, unit number, and selector .
.Ar selector . If there is no driver attached to the
If there is no device configured in the kernel for the
.Tn PCI .Tn PCI
device in question, the device name will be device in question, the driver name will be
.Dq none . .Dq none .
Unit numbers for unconfigured devices start at zero and are incremented for Unit numbers for detached devices start at zero and are incremented for
each unconfigured device that is encountered. each detached device that is encountered.
The The selector
.Ar selector
is in a form which may directly be used for the other forms of the command. is in a form which may directly be used for the other forms of the command.
The second column is the class code, with the class byte printed as two The second column is the class code, with the class byte printed as two
hex digits, followed by the sub-class and the interface bytes. hex digits, followed by the sub-class and the interface bytes.
@ -182,18 +182,36 @@ option is supplied,
will attempt to load the vendor/device information database, and print will attempt to load the vendor/device information database, and print
vendor, device, class and subclass identification strings for each device. vendor, device, class and subclass identification strings for each device.
.Pp .Pp
If the optional
.Ar device
argument is given with the
.Fl l
flag,
.Nm
will only list details about a single device instead of all devices.
.Pp
All invocations of All invocations of
.Nm .Nm
except for except for
.Fl l .Fl l
require a require a
.Ar selector .Ar device .
of the form The device can be identified either by a device name if the device is
attached to a driver or by a selector.
Selectors identify a PCI device by its address in PCI config space and
can take one of the following forms:
.Pp
.Bl -bullet -offset indent -compact
.It
.Li pci Ns Va domain Ns \&: Ns Va bus Ns \&: Ns Va device Ns \&: \ .Li pci Ns Va domain Ns \&: Ns Va bus Ns \&: Ns Va device Ns \&: \
Ns Va function Ns , Ns Va function Ns
.Li pci Ns Va bus Ns \&: Ns Va device Ns \&: Ns Va function Ns , or .It
.Li pci Ns Va bus Ns \&: Ns Va device Ns . .Li pci Ns Va bus Ns \&: Ns Va device Ns \&: Ns Va function Ns
In case of an abridged form, omitted selector components are assumed to be 0. .It
.Li pci Ns Va bus Ns \&: Ns Va device Ns
.El
.Pp
In the case of an abridged form, omitted selector components are assumed to be 0.
An optional leading device name followed by @ and an optional final colon An optional leading device name followed by @ and an optional final colon
will be ignored; this is so that the first column in the output of will be ignored; this is so that the first column in the output of
.Nm .Nm

View File

@ -35,6 +35,7 @@ static const char rcsid[] =
#include <sys/types.h> #include <sys/types.h>
#include <sys/fcntl.h> #include <sys/fcntl.h>
#include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <err.h> #include <err.h>
#include <inttypes.h> #include <inttypes.h>
@ -67,8 +68,10 @@ struct pci_vendor_info
TAILQ_HEAD(,pci_vendor_info) pci_vendors; TAILQ_HEAD(,pci_vendor_info) pci_vendors;
static struct pcisel getsel(const char *str);
static void list_bars(int fd, struct pci_conf *p); static void list_bars(int fd, struct pci_conf *p);
static void list_devs(int verbose, int bars, int caps, int errors); static void list_devs(const char *name, int verbose, int bars, int caps,
int errors);
static void list_verbose(struct pci_conf *p); static void list_verbose(struct pci_conf *p);
static const char *guess_class(struct pci_conf *p); static const char *guess_class(struct pci_conf *p);
static const char *guess_subclass(struct pci_conf *p); static const char *guess_subclass(struct pci_conf *p);
@ -83,10 +86,10 @@ static void
usage(void) usage(void)
{ {
fprintf(stderr, "%s\n%s\n%s\n%s\n", fprintf(stderr, "%s\n%s\n%s\n%s\n",
"usage: pciconf -l [-bcev]", "usage: pciconf -l [-bcev] [device]",
" pciconf -a selector", " pciconf -a device",
" pciconf -r [-b | -h] selector addr[:addr2]", " pciconf -r [-b | -h] device addr[:addr2]",
" pciconf -w [-b | -h] selector addr value"); " pciconf -w [-b | -h] device addr value");
exit (1); exit (1);
} }
@ -145,14 +148,15 @@ main(int argc, char **argv)
} }
} }
if ((listmode && optind != argc) if ((listmode && optind >= argc + 1)
|| (writemode && optind + 3 != argc) || (writemode && optind + 3 != argc)
|| (readmode && optind + 2 != argc) || (readmode && optind + 2 != argc)
|| (attachedmode && optind + 1 != argc)) || (attachedmode && optind + 1 != argc))
usage(); usage();
if (listmode) { if (listmode) {
list_devs(verbose, bars, caps, errors); list_devs(optind + 1 == argc ? argv[optind] : NULL, verbose,
bars, caps, errors);
} else if (attachedmode) { } else if (attachedmode) {
chkattached(argv[optind]); chkattached(argv[optind]);
} else if (readmode) { } else if (readmode) {
@ -169,11 +173,12 @@ main(int argc, char **argv)
} }
static void static void
list_devs(int verbose, int bars, int caps, int errors) list_devs(const char *name, int verbose, int bars, int caps, int errors)
{ {
int fd; int fd;
struct pci_conf_io pc; struct pci_conf_io pc;
struct pci_conf conf[255], *p; struct pci_conf conf[255], *p;
struct pci_match_conf patterns[1];
int none_count = 0; int none_count = 0;
if (verbose) if (verbose)
@ -186,6 +191,16 @@ list_devs(int verbose, int bars, int caps, int errors)
bzero(&pc, sizeof(struct pci_conf_io)); bzero(&pc, sizeof(struct pci_conf_io));
pc.match_buf_len = sizeof(conf); pc.match_buf_len = sizeof(conf);
pc.matches = conf; pc.matches = conf;
if (name != NULL) {
bzero(&patterns, sizeof(patterns));
patterns[0].pc_sel = getsel(name);
patterns[0].flags = PCI_GETCONF_MATCH_DOMAIN |
PCI_GETCONF_MATCH_BUS | PCI_GETCONF_MATCH_DEV |
PCI_GETCONF_MATCH_FUNC;
pc.num_patterns = 1;
pc.pat_buf_len = sizeof(patterns);
pc.patterns = patterns;
}
do { do {
if (ioctl(fd, PCIOCGETCONF, &pc) == -1) if (ioctl(fd, PCIOCGETCONF, &pc) == -1)
@ -557,7 +572,61 @@ read_config(int fd, struct pcisel *sel, long reg, int width)
} }
static struct pcisel static struct pcisel
getsel(const char *str) getdevice(const char *name)
{
struct pci_conf_io pc;
struct pci_conf conf[1];
struct pci_match_conf patterns[1];
char *cp;
int fd;
fd = open(_PATH_DEVPCI, O_RDONLY, 0);
if (fd < 0)
err(1, "%s", _PATH_DEVPCI);
bzero(&pc, sizeof(struct pci_conf_io));
pc.match_buf_len = sizeof(conf);
pc.matches = conf;
bzero(&patterns, sizeof(patterns));
/*
* The pattern structure requires the unit to be split out from
* the driver name. Walk backwards from the end of the name to
* find the start of the unit.
*/
if (name[0] == '\0')
err(1, "Empty device name");
cp = strchr(name, '\0');
assert(cp != NULL && cp != name);
cp--;
while (cp != name && isdigit(cp[-1]))
cp--;
if (cp == name)
errx(1, "Invalid device name");
if ((size_t)(cp - name) + 1 > sizeof(patterns[0].pd_name))
errx(1, "Device name i2s too long");
memcpy(patterns[0].pd_name, name, cp - name);
patterns[0].pd_unit = strtol(cp, &cp, 10);
assert(*cp == '\0');
patterns[0].flags = PCI_GETCONF_MATCH_NAME | PCI_GETCONF_MATCH_UNIT;
pc.num_patterns = 1;
pc.pat_buf_len = sizeof(patterns);
pc.patterns = patterns;
if (ioctl(fd, PCIOCGETCONF, &pc) == -1)
err(1, "ioctl(PCIOCGETCONF)");
if (pc.status != PCI_GETCONF_LAST_DEVICE &&
pc.status != PCI_GETCONF_MORE_DEVS)
errx(1, "error returned from PCIOCGETCONF ioctl");
close(fd);
if (pc.num_matches == 0)
errx(1, "Device not found");
return (conf[0].pc_sel);
}
static struct pcisel
parsesel(const char *str)
{ {
char *ep = strchr(str, '@'); char *ep = strchr(str, '@');
char *epbase; char *epbase;
@ -595,6 +664,20 @@ getsel(const char *str)
return sel; return sel;
} }
static struct pcisel
getsel(const char *str)
{
/*
* No device names contain colons and selectors always contain
* at least one colon.
*/
if (strchr(str, ':') == NULL)
return (getdevice(str));
else
return (parsesel(str));
}
static void static void
readone(int fd, struct pcisel *sel, long reg, int width) readone(int fd, struct pcisel *sel, long reg, int width)
{ {