svn commit: r262134 - in stable: 10/sys/dev/pci 10/sys/sys 10/usr.sbin/pciconf 8/sys/dev/pci 8/sys/sys 8/usr.sbin/pciconf 9/sys/dev/pci 9/sys/sys 9/usr.sbin/pciconf

John Baldwin jhb at FreeBSD.org
Mon Feb 17 22:19:53 UTC 2014


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.
  */


More information about the svn-src-stable-9 mailing list