diff options
| author | Simon J. Gerraty <sjg@FreeBSD.org> | 2014-04-28 07:50:45 +0000 |
|---|---|---|
| committer | Simon J. Gerraty <sjg@FreeBSD.org> | 2014-04-28 07:50:45 +0000 |
| commit | 3b8f08459569bf0faa21473e5cec2491e95c9349 (patch) | |
| tree | 80f45dd81ca716bcd7ca9674581e1fc40b93cd34 /usr.sbin/pciconf | |
| parent | 9d2ab4a62d6733c45958627ac113bdbd818d1e2a (diff) | |
| parent | b2ba55951383498f252746f618d513139da06e8e (diff) | |
Notes
Diffstat (limited to 'usr.sbin/pciconf')
| -rw-r--r-- | usr.sbin/pciconf/cap.c | 8 | ||||
| -rw-r--r-- | usr.sbin/pciconf/pciconf.8 | 80 | ||||
| -rw-r--r-- | usr.sbin/pciconf/pciconf.c | 172 |
3 files changed, 227 insertions, 33 deletions
diff --git a/usr.sbin/pciconf/cap.c b/usr.sbin/pciconf/cap.c index d7296a33f18d..bdcbe1d0079b 100644 --- a/usr.sbin/pciconf/cap.c +++ b/usr.sbin/pciconf/cap.c @@ -410,7 +410,7 @@ aspm_string(uint8_t aspm) static void cap_express(int fd, struct pci_conf *p, uint8_t ptr) { - uint32_t cap; + uint32_t cap, cap2; uint16_t ctl, flags, sta; flags = read_config(fd, &p->pc_sel, ptr + PCIER_FLAGS, 2); @@ -452,6 +452,7 @@ cap_express(int fd, struct pci_conf *p, uint8_t ptr) if (flags & PCIEM_FLAGS_IRQ) printf(" IRQ %d", (flags & PCIEM_FLAGS_IRQ) >> 9); cap = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CAP, 4); + cap2 = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CAP2, 4); ctl = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CTL, 2); printf(" max data %d(%d)", MAX_PAYLOAD((ctl & PCIEM_CTL_MAX_PAYLOAD) >> 5), @@ -474,6 +475,11 @@ cap_express(int fd, struct pci_conf *p, uint8_t ptr) printf(" ASPM %s(%s)", aspm_string(ctl & PCIEM_LINK_CTL_ASPMC), aspm_string((cap & PCIEM_LINK_CAP_ASPM) >> 10)); } + if ((cap2 & PCIEM_CAP2_ARI) != 0) { + ctl = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CTL2, 4); + printf(" ARI %s", + (ctl & PCIEM_CTL2_ARI) ? "enabled" : "disabled"); + } } static void diff --git a/usr.sbin/pciconf/pciconf.8 b/usr.sbin/pciconf/pciconf.8 index b416abc54096..e0caf1fcb5bc 100644 --- a/usr.sbin/pciconf/pciconf.8 +++ b/usr.sbin/pciconf/pciconf.8 @@ -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 bcevV 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,58 @@ 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 +.Fl V +option is supplied, +.Nm +will list any vital product data +.Pq VPD +provided by each device. +Each VPD keyword is enumerated via a line in the following format: +.Bd -literal + VPD ro PN = '110114640C0 ' +.Ed +.Pp +The first string after the +.Dq Li VPD +prefix indicates if the keyword is read-only +.Dq ro +or read-write +.Dq rw . +The second string provides the keyword name. +The text after the the equals sign lists the value of the keyword which is +usually an ASCII string. +.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 diff --git a/usr.sbin/pciconf/pciconf.c b/usr.sbin/pciconf/pciconf.c index 58ffddd9cfab..0177dc98702f 100644 --- a/usr.sbin/pciconf/pciconf.c +++ b/usr.sbin/pciconf/pciconf.c @@ -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,9 +68,12 @@ 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, int vpd); static void list_verbose(struct pci_conf *p); +static void list_vpd(int fd, struct pci_conf *p); static const char *guess_class(struct pci_conf *p); static const char *guess_subclass(struct pci_conf *p); static int load_vendors(void); @@ -83,10 +87,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 [-bcevV] [device]", + " pciconf -a device", + " pciconf -r [-b | -h] device addr[:addr2]", + " pciconf -w [-b | -h] device addr value"); exit (1); } @@ -95,13 +99,13 @@ main(int argc, char **argv) { int c; int listmode, readmode, writemode, attachedmode; - int bars, caps, errors, verbose; + int bars, caps, errors, verbose, vpd; int byte, isshort; listmode = readmode = writemode = attachedmode = 0; - bars = caps = errors = verbose = byte = isshort = 0; + bars = caps = errors = verbose = vpd = byte = isshort = 0; - while ((c = getopt(argc, argv, "abcehlrwv")) != -1) { + while ((c = getopt(argc, argv, "abcehlrwvV")) != -1) { switch(c) { case 'a': attachedmode = 1; @@ -140,19 +144,24 @@ main(int argc, char **argv) verbose = 1; break; + case 'V': + vpd = 1; + break; + default: usage(); } } - 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, vpd); } else if (attachedmode) { chkattached(argv[optind]); } else if (readmode) { @@ -169,11 +178,13 @@ 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 vpd) { 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 +197,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) @@ -231,6 +252,8 @@ list_devs(int verbose, int bars, int caps, int errors) list_caps(fd, p); if (errors) list_errors(fd, p); + if (vpd) + list_vpd(fd, p); } } while (pc.status == PCI_GETCONF_MORE_DEVS); @@ -324,6 +347,63 @@ list_verbose(struct pci_conf *p) printf(" subclass = %s\n", dp); } +static void +list_vpd(int fd, struct pci_conf *p) +{ + struct pci_list_vpd_io list; + struct pci_vpd_element *vpd, *end; + + list.plvi_sel = p->pc_sel; + list.plvi_len = 0; + list.plvi_data = NULL; + if (ioctl(fd, PCIOCLISTVPD, &list) < 0 || list.plvi_len == 0) + return; + + list.plvi_data = malloc(list.plvi_len); + if (ioctl(fd, PCIOCLISTVPD, &list) < 0) { + free(list.plvi_data); + return; + } + + vpd = list.plvi_data; + end = (struct pci_vpd_element *)((char *)vpd + list.plvi_len); + for (; vpd < end; vpd = PVE_NEXT(vpd)) { + if (vpd->pve_flags == PVE_FLAG_IDENT) { + printf(" VPD ident = '%.*s'\n", + (int)vpd->pve_datalen, vpd->pve_data); + continue; + } + + /* Ignore the checksum keyword. */ + if (!(vpd->pve_flags & PVE_FLAG_RW) && + memcmp(vpd->pve_keyword, "RV", 2) == 0) + continue; + + /* Ignore remaining read-write space. */ + if (vpd->pve_flags & PVE_FLAG_RW && + memcmp(vpd->pve_keyword, "RW", 2) == 0) + continue; + + /* Handle extended capability keyword. */ + if (!(vpd->pve_flags & PVE_FLAG_RW) && + memcmp(vpd->pve_keyword, "CP", 2) == 0) { + printf(" VPD ro CP = ID %02x in map 0x%x[0x%x]\n", + (unsigned int)vpd->pve_data[0], + PCIR_BAR((unsigned int)vpd->pve_data[1]), + (unsigned int)vpd->pve_data[3] << 8 | + (unsigned int)vpd->pve_data[2]); + continue; + } + + /* Remaining keywords should all have ASCII values. */ + printf(" VPD %s %c%c = '%.*s'\n", + vpd->pve_flags & PVE_FLAG_RW ? "rw" : "ro", + vpd->pve_keyword[0], vpd->pve_keyword[1], + (int)vpd->pve_datalen, vpd->pve_data); + } + free(list.plvi_data); +} + /* * This is a direct cut-and-paste from the table in sys/dev/pci/pci.c. */ @@ -557,7 +637,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 +729,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) { |
