PERFORCE change 1190027 for review
John Baldwin
jhb at FreeBSD.org
Tue Jan 14 19:06:28 UTC 2014
http://p4web.freebsd.org/@@1190027?ac=10
Change 1190027 by jhb at jhb_jhbbsd on 2014/01/14 19:06:15
Add support for listing VPD fields in userland.
- Explicitly track the length of read-only VPD keyword values as
they are not always ASCII.
- Add an ioctl to fetch the list of VPD keywords for a given PCI
device.
- Add -V flag to dump VPD fields for each device.
- Allow the -l option to accept an optional selector so that
details are listed for a single device instead of all.
Affected files ...
.. //depot/projects/pci/sys/dev/pci/pci.c#49 edit
.. //depot/projects/pci/sys/dev/pci/pci_user.c#7 edit
.. //depot/projects/pci/sys/dev/pci/pcivar.h#15 edit
.. //depot/projects/pci/sys/sys/pciio.h#3 edit
.. //depot/projects/pci/usr.sbin/pciconf/pciconf.c#2 edit
Differences ...
==== //depot/projects/pci/sys/dev/pci/pci.c#49 (text+ko) ====
@@ -1011,7 +1011,7 @@
state = -2;
break;
}
- dflen = byte2;
+ cfg->vpd.vpd_ros[off].len = dflen = byte2;
if (dflen == 0 &&
strncmp(cfg->vpd.vpd_ros[off].keyword, "RV",
2) == 0) {
@@ -1205,6 +1205,17 @@
return (ENXIO);
}
+struct pcicfg_vpd *
+pci_fetch_vpd_list(device_t dev)
+{
+ struct pci_devinfo *dinfo = device_get_ivars(dev);
+ pcicfgregs *cfg = &dinfo->cfg;
+
+ if (!cfg->vpd.vpd_cached && cfg->vpd.vpd_reg != 0)
+ pci_read_vpd(device_get_parent(device_get_parent(dev)), cfg);
+ return (&cfg->vpd);
+}
+
/*
* Find the requested HyperTransport capability and return the offset
* in configuration space via the pointer provided. The function
==== //depot/projects/pci/sys/dev/pci/pci_user.c#7 (text+ko) ====
@@ -407,6 +407,89 @@
#endif /* PRE7_COMPAT */
static int
+pci_list_vpd(device_t dev, struct pci_list_vpd_io *lvio)
+{
+ struct pci_vpd_element vpd_element, *vpd_user;
+ struct pcicfg_vpd *vpd;
+ size_t len;
+ int error, i;
+
+ vpd = pci_fetch_vpd_list(dev);
+ if (vpd->vpd_reg == 0 || vpd->vpd_ident == NULL)
+ return (ENXIO);
+
+ /*
+ * Calculate the amount of space needed in the data buffer. An
+ * identifier element is always present followed by the read-only
+ * and read-write keywords.
+ */
+ len = sizeof(struct pci_vpd_element) + strlen(vpd->vpd_ident);
+ for (i = 0; i < vpd->vpd_rocnt; i++)
+ len += sizeof(struct pci_vpd_element) + vpd->vpd_ros[i].len;
+ for (i = 0; i < vpd->vpd_wcnt; i++)
+ len += sizeof(struct pci_vpd_element) + vpd->vpd_w[i].len;
+
+ if (lvio->plvi_len == 0) {
+ lvio->plvi_len = len;
+ return (0);
+ }
+ if (lvio->plvi_len < len) {
+ lvio->plvi_len = len;
+ return (ENOMEM);
+ }
+
+ /*
+ * Copyout the identifier string followed by each keyword and
+ * value.
+ */
+ vpd_user = lvio->plvi_data;
+ vpd_element.pve_keyword[0] = '\0';
+ vpd_element.pve_keyword[1] = '\0';
+ vpd_element.pve_flags = PVE_FLAG_IDENT;
+ vpd_element.pve_datalen = strlen(vpd->vpd_ident);
+ error = copyout(&vpd_element, vpd_user, sizeof(vpd_element));
+ if (error)
+ return (error);
+ error = copyout(vpd->vpd_ident, vpd_user->pve_data,
+ strlen(vpd->vpd_ident));
+ if (error)
+ return (error);
+ vpd_user = PVE_NEXT(vpd_user);
+ vpd_element.pve_flags = 0;
+ for (i = 0; i < vpd->vpd_rocnt; i++) {
+ vpd_element.pve_keyword[0] = vpd->vpd_ros[i].keyword[0];
+ vpd_element.pve_keyword[1] = vpd->vpd_ros[i].keyword[1];
+ vpd_element.pve_datalen = vpd->vpd_ros[i].len;
+ error = copyout(&vpd_element, vpd_user, sizeof(vpd_element));
+ if (error)
+ return (error);
+ error = copyout(vpd->vpd_ros[i].value, vpd_user->pve_data,
+ vpd->vpd_ros[i].len);
+ if (error)
+ return (error);
+ vpd_user = PVE_NEXT(vpd_user);
+ }
+ vpd_element.pve_flags = PVE_FLAG_RW;
+ for (i = 0; i < vpd->vpd_wcnt; i++) {
+ vpd_element.pve_keyword[0] = vpd->vpd_w[i].keyword[0];
+ vpd_element.pve_keyword[1] = vpd->vpd_w[i].keyword[1];
+ vpd_element.pve_datalen = vpd->vpd_w[i].len;
+ error = copyout(&vpd_element, vpd_user, sizeof(vpd_element));
+ if (error)
+ return (error);
+ error = copyout(vpd->vpd_w[i].value, vpd_user->pve_data,
+ vpd->vpd_w[i].len);
+ if (error)
+ return (error);
+ vpd_user = PVE_NEXT(vpd_user);
+ }
+ KASSERT((char *)vpd_user - (char *)lvio->plvi_data == len,
+ ("length mismatch"));
+ lvio->plvi_len = len;
+ return (0);
+}
+
+static int
pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td)
{
device_t pcidev, brdev;
@@ -417,6 +500,7 @@
struct pci_devinfo *dinfo;
struct pci_io *io;
struct pci_bar_io *bio;
+ struct pci_list_vpd_io *lvio;
struct pci_match_conf *pattern_buf;
struct pci_map *pm;
size_t confsz, iolen, pbufsz;
@@ -433,19 +517,29 @@
struct pci_match_conf_old *pattern_buf_old = NULL;
io_old = NULL;
+#endif
- if (!(flag & FWRITE) && cmd != PCIOCGETBAR &&
- cmd != PCIOCGETCONF && cmd != PCIOCGETCONF_OLD)
- return EPERM;
-#else
- if (!(flag & FWRITE) && cmd != PCIOCGETBAR && cmd != PCIOCGETCONF)
- return EPERM;
+ if (!(flag & FWRITE)) {
+ switch (cmd) {
+#ifdef PRE7_COMPAT
+#ifdef COMPAT_FREEBSD32
+ case PCIOCGETCONF_OLD32:
+#endif
+ case PCIOCGETCONF_OLD:
#endif
+ case PCIOCGETCONF:
+ case PCIOCGETBAR:
+ case PCIOCLISTVPD:
+ break;
+ default:
+ return (EPERM);
+ }
+ }
- switch(cmd) {
+ switch (cmd) {
#ifdef PRE7_COMPAT
#ifdef COMPAT_FREEBSD32
- case PCIOCGETCONF_OLD32:
+ case PCIOCGETCONF_OLD32:
cio32 = (struct pci_conf_io32 *)data;
cio = malloc(sizeof(struct pci_conf_io), M_TEMP, M_WAITOK);
cio->pat_buf_len = cio32->pat_buf_len;
@@ -466,7 +560,7 @@
cio = (struct pci_conf_io *)data;
}
- switch(cmd) {
+ switch (cmd) {
#ifdef PRE7_COMPAT
#ifdef COMPAT_FREEBSD32
case PCIOCGETCONF_OLD32:
@@ -912,6 +1006,22 @@
else
error = ENODEV;
break;
+ case PCIOCLISTVPD:
+ lvio = (struct pci_list_vpd_io *)data;
+
+ /*
+ * Assume that the user-level bus number is
+ * in fact the physical PCI bus number.
+ */
+ pcidev = pci_find_dbsf(lvio->plvi_sel.pc_domain,
+ lvio->plvi_sel.pc_bus, lvio->plvi_sel.pc_dev,
+ lvio->plvi_sel.pc_func);
+ if (pcidev == NULL) {
+ error = ENODEV;
+ break;
+ }
+ error = pci_list_vpd(pcidev, lvio);
+ break;
default:
error = ENOTTY;
break;
==== //depot/projects/pci/sys/dev/pci/pcivar.h#15 (text+ko) ====
@@ -57,6 +57,7 @@
struct vpd_readonly {
char keyword[2];
char *value;
+ int len;
};
struct vpd_write {
@@ -525,6 +526,7 @@
struct pci_map *pci_find_bar(device_t dev, int reg);
int pci_bar_enabled(device_t dev, struct pci_map *pm);
+struct pcicfg_vpd *pci_fetch_vpd_list(device_t dev);
#define VGA_PCI_BIOS_SHADOW_ADDR 0xC0000
#define VGA_PCI_BIOS_SHADOW_SIZE 131072
==== //depot/projects/pci/sys/sys/pciio.h#3 (text+ko) ====
@@ -116,10 +116,31 @@
uint64_t pbi_length; /* length of BAR */
};
+struct pci_vpd_element {
+ char pve_keyword[2];
+ uint8_t pve_flags;
+ uint8_t pve_datalen;
+ uint8_t pve_data[0];
+};
+
+#define PVE_FLAG_IDENT 0x01 /* Element is the string identifier */
+#define PVE_FLAG_RW 0x02 /* Element is read/write */
+
+#define PVE_NEXT(pve) \
+ ((struct pci_vpd_element *)((char *)(pve) + \
+ sizeof(struct pci_vpd_element) + (pve)->pve_datalen))
+
+struct pci_list_vpd_io {
+ struct pcisel plvi_sel; /* device to operate on */
+ size_t plvi_len; /* size of the data area */
+ struct pci_vpd_element *plvi_data;
+};
+
#define PCIOCGETCONF _IOWR('p', 5, struct pci_conf_io)
#define PCIOCREAD _IOWR('p', 2, struct pci_io)
#define PCIOCWRITE _IOWR('p', 3, struct pci_io)
#define PCIOCATTACHED _IOWR('p', 4, struct pci_io)
#define PCIOCGETBAR _IOWR('p', 6, struct pci_bar_io)
+#define PCIOCLISTVPD _IOWR('p', 7, struct pci_list_vpd_io)
#endif /* !_SYS_PCIIO_H_ */
==== //depot/projects/pci/usr.sbin/pciconf/pciconf.c#2 (text+ko) ====
@@ -67,9 +67,12 @@
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,7 +86,7 @@
usage(void)
{
fprintf(stderr, "%s\n%s\n%s\n%s\n",
- "usage: pciconf -l [-bcev]",
+ "usage: pciconf -l [-bcevV] [selector]",
" pciconf -a selector",
" pciconf -r [-b | -h] selector addr[:addr2]",
" pciconf -w [-b | -h] selector addr value");
@@ -95,13 +98,13 @@
{
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 +143,24 @@
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 +177,13 @@
}
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 +196,16 @@
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 +251,8 @@
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 +346,63 @@
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.
*/
More information about the p4-projects
mailing list