- 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$
.\"
.Dd June 1, 2012
.Dd January 20, 2014
.Dt PCICONF 8
.Os
.Sh NAME
@ -33,13 +33,13 @@
.Nd diagnostic utility for the PCI bus
.Sh SYNOPSIS
.Nm
.Fl l Op Fl bcev
.Fl l Oo Fl bcev Oc Op Ar device
.Nm
.Fl a Ar selector
.Fl a Ar device
.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
.Fl w Oo Fl b | h Oc Ar selector addr value
.Fl w Oo Fl b | h Oc Ar device addr value
.Sh DESCRIPTION
The
.Nm
@ -54,7 +54,9 @@ normally only the super-user.
.Pp
With the
.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
foo0@pci0:0:4:0: class=0x010000 card=0x00000000 chip=0x000f1000 rev=0x01 \
hdr=0x00
@ -65,16 +67,14 @@ hdr=0x00
.Ed
.Pp
The first column gives the
device name, unit number, and
.Ar selector .
If there is no device configured in the kernel for the
driver name, unit number, and selector .
If there is no driver attached to the
.Tn PCI
device in question, the device name will be
device in question, the driver name will be
.Dq none .
Unit numbers for unconfigured devices start at zero and are incremented for
each unconfigured device that is encountered.
The
.Ar selector
Unit numbers for detached devices start at zero and are incremented for
each detached device that is encountered.
The selector
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
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
vendor, device, class and subclass identification strings for each device.
.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
.Nm
except for
.Fl l
require a
.Ar selector
of the form
.Ar device .
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 \&: \
Ns Va function Ns ,
.Li pci Ns Va bus Ns \&: Ns Va device Ns \&: Ns Va function Ns , or
.Li pci Ns Va bus Ns \&: Ns Va device Ns .
In case of an abridged form, omitted selector components are assumed to be 0.
Ns Va function Ns
.It
.Li pci Ns Va bus Ns \&: Ns Va device Ns \&: Ns Va function Ns
.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
will be ignored; this is so that the first column in the output of
.Nm

View File

@ -35,6 +35,7 @@ static const char rcsid[] =
#include <sys/types.h>
#include <sys/fcntl.h>
#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <inttypes.h>
@ -67,8 +68,10 @@ struct pci_vendor_info
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_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 const char *guess_class(struct pci_conf *p);
static const char *guess_subclass(struct pci_conf *p);
@ -83,10 +86,10 @@ static void
usage(void)
{
fprintf(stderr, "%s\n%s\n%s\n%s\n",
"usage: pciconf -l [-bcev]",
" pciconf -a selector",
" pciconf -r [-b | -h] selector addr[:addr2]",
" pciconf -w [-b | -h] selector addr value");
"usage: pciconf -l [-bcev] [device]",
" pciconf -a device",
" pciconf -r [-b | -h] device addr[:addr2]",
" pciconf -w [-b | -h] device addr value");
exit (1);
}
@ -145,14 +148,15 @@ main(int argc, char **argv)
}
}
if ((listmode && optind != argc)
if ((listmode && optind >= argc + 1)
|| (writemode && optind + 3 != argc)
|| (readmode && optind + 2 != argc)
|| (attachedmode && optind + 1 != argc))
usage();
if (listmode) {
list_devs(verbose, bars, caps, errors);
list_devs(optind + 1 == argc ? argv[optind] : NULL, verbose,
bars, caps, errors);
} else if (attachedmode) {
chkattached(argv[optind]);
} else if (readmode) {
@ -169,11 +173,12 @@ main(int argc, char **argv)
}
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;
struct pci_conf_io pc;
struct pci_conf conf[255], *p;
struct pci_match_conf patterns[1];
int none_count = 0;
if (verbose)
@ -186,6 +191,16 @@ list_devs(int verbose, int bars, int caps, int errors)
bzero(&pc, sizeof(struct pci_conf_io));
pc.match_buf_len = sizeof(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 {
if (ioctl(fd, PCIOCGETCONF, &pc) == -1)
@ -557,7 +572,61 @@ read_config(int fd, struct pcisel *sel, long reg, int width)
}
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 *epbase;
@ -595,6 +664,20 @@ getsel(const char *str)
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
readone(int fd, struct pcisel *sel, long reg, int width)
{