Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 14 Jan 2014 19:06:27 GMT
From:      John Baldwin <jhb@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 1190027 for review
Message-ID:  <201401141906.s0EJ6Rt6021781@skunkworks.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://p4web.freebsd.org/@@1190027?ac=10

Change 1190027 by jhb@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.
  */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201401141906.s0EJ6Rt6021781>