Author: jhb
Date: Mon Feb 17 22:19:49 2014
New Revision: 262134
URL: http://svnweb.freebsd.org/changeset/base/262134

Log:
  MFC 260926:
  Add support for displaying VPD for PCI devices via pciconf.
  - Store the length of each read-only VPD value since not all values are
    guaranteed to be ASCII values (though most are).
  - Add a new pciio ioctl to fetch VPD for a single PCI device.  The values
    are returned as a list of variable length records, one for the device
    name and each keyword.
  - Add a new -V flag to pciconf's list mode which displays VPD data for
    each device.

Modified:
  stable/9/sys/dev/pci/pci.c
  stable/9/sys/dev/pci/pci_user.c
  stable/9/sys/dev/pci/pcivar.h
  stable/9/sys/sys/pciio.h
  stable/9/usr.sbin/pciconf/pciconf.8
  stable/9/usr.sbin/pciconf/pciconf.c
Directory Properties:
  stable/9/sys/   (props changed)
  stable/9/sys/dev/   (props changed)
  stable/9/sys/sys/   (props changed)
  stable/9/usr.sbin/pciconf/   (props changed)

Changes in other areas also in this revision:
Modified:
  stable/10/sys/dev/pci/pci.c
  stable/10/sys/dev/pci/pci_user.c
  stable/10/sys/dev/pci/pcivar.h
  stable/10/sys/sys/pciio.h
  stable/10/usr.sbin/pciconf/pciconf.8
  stable/10/usr.sbin/pciconf/pciconf.c
  stable/8/sys/dev/pci/pci.c
  stable/8/sys/dev/pci/pci_user.c
  stable/8/sys/dev/pci/pcivar.h
  stable/8/sys/sys/pciio.h
  stable/8/usr.sbin/pciconf/pciconf.8
  stable/8/usr.sbin/pciconf/pciconf.c
Directory Properties:
  stable/10/   (props changed)
  stable/8/sys/   (props changed)
  stable/8/sys/dev/   (props changed)
  stable/8/sys/dev/pci/   (props changed)
  stable/8/sys/sys/   (props changed)
  stable/8/usr.sbin/pciconf/   (props changed)

Modified: stable/9/sys/dev/pci/pci.c
==============================================================================
--- stable/9/sys/dev/pci/pci.c  Mon Feb 17 22:06:52 2014        (r262133)
+++ stable/9/sys/dev/pci/pci.c  Mon Feb 17 22:19:49 2014        (r262134)
@@ -995,7 +995,7 @@ pci_read_vpd(device_t pcib, pcicfgregs *
                                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) {
@@ -1196,6 +1196,17 @@ pci_get_vpd_readonly_method(device_t dev
        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 extended capability and return the offset in
  * configuration space via the pointer provided. The function returns

Modified: stable/9/sys/dev/pci/pci_user.c
==============================================================================
--- stable/9/sys/dev/pci/pci_user.c     Mon Feb 17 22:06:52 2014        
(r262133)
+++ stable/9/sys/dev/pci/pci_user.c     Mon Feb 17 22:19:49 2014        
(r262134)
@@ -407,6 +407,89 @@ pci_conf_match_old32(struct pci_match_co
 #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 @@ pci_ioctl(struct cdev *dev, u_long cmd, 
        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 @@ pci_ioctl(struct cdev *dev, u_long cmd, 
        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 @@ pci_ioctl(struct cdev *dev, u_long cmd, 
                cio = (struct pci_conf_io *)data;
        }
 
-       switch(cmd) {
+       switch (cmd) {
 #ifdef PRE7_COMPAT
 #ifdef COMPAT_FREEBSD32
        case PCIOCGETCONF_OLD32:
@@ -912,6 +1006,22 @@ getconfexit:
                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;

Modified: stable/9/sys/dev/pci/pcivar.h
==============================================================================
--- stable/9/sys/dev/pci/pcivar.h       Mon Feb 17 22:06:52 2014        
(r262133)
+++ stable/9/sys/dev/pci/pcivar.h       Mon Feb 17 22:19:49 2014        
(r262134)
@@ -57,6 +57,7 @@ struct pci_map {
 struct vpd_readonly {
     char       keyword[2];
     char       *value;
+    int                len;
 };
 
 struct vpd_write {
@@ -489,6 +490,7 @@ extern uint32_t     pci_generation;
 
 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

Modified: stable/9/sys/sys/pciio.h
==============================================================================
--- stable/9/sys/sys/pciio.h    Mon Feb 17 22:06:52 2014        (r262133)
+++ stable/9/sys/sys/pciio.h    Mon Feb 17 22:19:49 2014        (r262134)
@@ -116,10 +116,31 @@ struct pci_bar_io {
        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_ */

Modified: stable/9/usr.sbin/pciconf/pciconf.8
==============================================================================
--- stable/9/usr.sbin/pciconf/pciconf.8 Mon Feb 17 22:06:52 2014        
(r262133)
+++ stable/9/usr.sbin/pciconf/pciconf.8 Mon Feb 17 22:19:49 2014        
(r262134)
@@ -33,7 +33,7 @@
 .Nd diagnostic utility for the PCI bus
 .Sh SYNOPSIS
 .Nm
-.Fl l Oo Fl bcev Oc Op Ar device
+.Fl l Oo Fl bcevV Oc Op Ar device
 .Nm
 .Fl a Ar device
 .Nm
@@ -182,6 +182,28 @@ 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

Modified: stable/9/usr.sbin/pciconf/pciconf.c
==============================================================================
--- stable/9/usr.sbin/pciconf/pciconf.c Mon Feb 17 22:06:52 2014        
(r262133)
+++ stable/9/usr.sbin/pciconf/pciconf.c Mon Feb 17 22:19:49 2014        
(r262134)
@@ -71,8 +71,9 @@ 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(const char *name, int verbose, int bars, int caps,
-    int errors);
+    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);
@@ -86,7 +87,7 @@ static void
 usage(void)
 {
        fprintf(stderr, "%s\n%s\n%s\n%s\n",
-               "usage: pciconf -l [-bcev] [device]",
+               "usage: pciconf -l [-bcevV] [device]",
                "       pciconf -a device",
                "       pciconf -r [-b | -h] device addr[:addr2]",
                "       pciconf -w [-b | -h] device addr value");
@@ -98,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;
@@ -143,6 +144,10 @@ main(int argc, char **argv)
                        verbose = 1;
                        break;
 
+               case 'V':
+                       vpd = 1;
+                       break;
+
                default:
                        usage();
                }
@@ -156,7 +161,7 @@ main(int argc, char **argv)
 
        if (listmode) {
                list_devs(optind + 1 == argc ? argv[optind] : NULL, verbose,
-                   bars, caps, errors);
+                   bars, caps, errors, vpd);
        } else if (attachedmode) {
                chkattached(argv[optind]);
        } else if (readmode) {
@@ -173,7 +178,8 @@ main(int argc, char **argv)
 }
 
 static void
-list_devs(const char *name, 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;
@@ -246,6 +252,8 @@ list_devs(const char *name, int verbose,
                                list_caps(fd, p);
                        if (errors)
                                list_errors(fd, p);
+                       if (vpd)
+                               list_vpd(fd, p);
                }
        } while (pc.status == PCI_GETCONF_MORE_DEVS);
 
@@ -339,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.
  */
_______________________________________________
svn-src-stable-9@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-stable-9
To unsubscribe, send any mail to "svn-src-stable-9-unsubscr...@freebsd.org"

Reply via email to