Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 2 Feb 2009 19:54:16 +0000 (UTC)
From:      John Baldwin <jhb@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r188018 - in head: sys/dev/pci usr.sbin/pciconf
Message-ID:  <200902021954.n12JsGT2005768@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jhb
Date: Mon Feb  2 19:54:16 2009
New Revision: 188018
URL: http://svn.freebsd.org/changeset/base/188018

Log:
  - Add a new ioctl to /dev/pci to fetch details on an individual BAR of a
    device.  The details include the current value of the BAR (including all
    the flag bits and the current base address), its length, and whether or not
    it is enabled.  Since this operation is not invasive, non-root users are
    allowed to use it (unlike manual config register access which requires
    root).  The intention is that userland apps (such as Xorg) will use this
    interface rather than dangerously frobbing the BARs from userland to
    obtain this information.
  - Add a new sub-mode to the 'list' mode of pciconf.  The -b flag when used
    with -l will now list all the active BARs for each device.
  
  MFC after:	1 month

Modified:
  head/sys/dev/pci/pci_user.c
  head/sys/dev/pci/pcireg.h
  head/usr.sbin/pciconf/pciconf.8
  head/usr.sbin/pciconf/pciconf.c

Modified: head/sys/dev/pci/pci_user.c
==============================================================================
--- head/sys/dev/pci/pci_user.c	Mon Feb  2 19:22:53 2009	(r188017)
+++ head/sys/dev/pci/pci_user.c	Mon Feb  2 19:54:16 2009	(r188018)
@@ -307,7 +307,10 @@ pci_ioctl(struct cdev *dev, u_long cmd, 
 	struct pci_conf_io *cio;
 	struct pci_devinfo *dinfo;
 	struct pci_io *io;
+	struct pci_bar_io *bio;
 	struct pci_match_conf *pattern_buf;
+	struct resource_list_entry *rle;
+	uint32_t value;
 	size_t confsz, iolen, pbufsz;
 	int error, ionum, i, num_patterns;
 #ifdef PRE7_COMPAT
@@ -319,11 +322,11 @@ pci_ioctl(struct cdev *dev, u_long cmd, 
 	io_old = NULL;
 	pattern_buf_old = NULL;
 
-	if (!(flag & FWRITE) &&
-	    (cmd != PCIOCGETCONF && cmd != PCIOCGETCONF_OLD))
+	if (!(flag & FWRITE) && cmd != PCIOCGETBAR &&
+	    cmd != PCIOCGETCONF && cmd != PCIOCGETCONF_OLD)
 		return EPERM;
 #else
-	if (!(flag & FWRITE) && cmd != PCIOCGETCONF)
+	if (!(flag & FWRITE) && cmd != PCIOCGETBAR && cmd != PCIOCGETCONF)
 		return EPERM;
 #endif
 
@@ -669,6 +672,70 @@ getconfexit:
 		}
 		break;
 
+	case PCIOCGETBAR:
+		bio = (struct pci_bar_io *)data;
+
+		/*
+		 * Assume that the user-level bus number is
+		 * in fact the physical PCI bus number.
+		 */
+		pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain,
+		    bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev,
+		    bio->pbi_sel.pc_func);
+		if (pcidev == NULL) {
+			error = ENODEV;
+			break;
+		}
+		dinfo = device_get_ivars(pcidev);
+		
+		/*
+		 * Look for a resource list entry matching the requested BAR.
+		 *
+		 * XXX: This will not find BARs that are not initialized, but
+		 * maybe that is ok?
+		 */
+		rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY,
+		    bio->pbi_reg);
+		if (rle == NULL)
+			rle = resource_list_find(&dinfo->resources,
+			    SYS_RES_IOPORT, bio->pbi_reg);
+		if (rle == NULL || rle->res == NULL) {
+			error = EINVAL;
+			break;
+		}
+
+		/*
+		 * Ok, we have a resource for this BAR.  Read the lower
+		 * 32 bits to get any flags.
+		 */
+		value = pci_read_config(pcidev, bio->pbi_reg, 4);
+		if (PCI_BAR_MEM(value)) {
+			if (rle->type != SYS_RES_MEMORY) {
+				error = EINVAL;
+				break;
+			}
+			value &= ~PCIM_BAR_MEM_BASE;
+		} else {
+			if (rle->type != SYS_RES_IOPORT) {
+				error = EINVAL;
+				break;
+			}
+			value &= ~PCIM_BAR_IO_BASE;
+		}
+		bio->pbi_base = rman_get_start(rle->res) | value;
+		bio->pbi_length = rman_get_size(rle->res);
+
+		/*
+		 * Check the command register to determine if this BAR
+		 * is enabled.
+		 */
+		value = pci_read_config(pcidev, PCIR_COMMAND, 2);
+		if (rle->type == SYS_RES_MEMORY)
+			bio->pbi_enabled = (value & PCIM_CMD_MEMEN) != 0;
+		else
+			bio->pbi_enabled = (value & PCIM_CMD_PORTEN) != 0;
+		error = 0;
+		break;
 	default:
 		error = ENOTTY;
 		break;

Modified: head/sys/dev/pci/pcireg.h
==============================================================================
--- head/sys/dev/pci/pcireg.h	Mon Feb  2 19:22:53 2009	(r188017)
+++ head/sys/dev/pci/pcireg.h	Mon Feb  2 19:54:16 2009	(r188018)
@@ -117,7 +117,7 @@
 
 #define	PCIR_BARS	0x10
 #define	PCIR_BAR(x)		(PCIR_BARS + (x) * 4)
-#define	PCI_MAX_BAR_0		5	/* Number of standard bars */
+#define	PCIR_MAX_BAR_0		5
 #define	PCI_RID2BAR(rid)	(((rid) - PCIR_BARS) / 4)
 #define	PCI_BAR_IO(x)		(((x) & PCIM_BAR_SPACE) == PCIM_BAR_IO_SPACE)
 #define	PCI_BAR_MEM(x)		(((x) & PCIM_BAR_SPACE) == PCIM_BAR_MEM_SPACE)
@@ -158,6 +158,7 @@
 
 /* config registers for header type 1 (PCI-to-PCI bridge) devices */
 
+#define	PCIR_MAX_BAR_1	1
 #define	PCIR_SECSTAT_1	0x1e
 
 #define	PCIR_PRIBUS_1	0x18
@@ -188,6 +189,7 @@
 
 /* config registers for header type 2 (CardBus) devices */
 
+#define	PCIR_MAX_BAR_2	0
 #define	PCIR_CAP_PTR_2	0x14
 #define	PCIR_SECSTAT_2	0x16
 

Modified: head/usr.sbin/pciconf/pciconf.8
==============================================================================
--- head/usr.sbin/pciconf/pciconf.8	Mon Feb  2 19:22:53 2009	(r188017)
+++ head/usr.sbin/pciconf/pciconf.8	Mon Feb  2 19:54:16 2009	(r188018)
@@ -33,7 +33,7 @@
 .Nd diagnostic utility for the PCI bus
 .Sh SYNOPSIS
 .Nm
-.Fl l Op Fl cv
+.Fl l Op Fl bcv
 .Nm
 .Fl a Ar selector
 .Nm
@@ -112,6 +112,32 @@ device, which contains several (similar 
 one chip.
 .Pp
 If the
+.Fl b
+option is supplied,
+.Nm
+will list any base address registers
+.Pq BARs
+that are assigned resources for each device.
+Each BAR will be enumerated via a line in the following format:
+.Bd -literal
+    bar   [10] = type Memory, range 32, base 0xda060000, size 131072, enabled
+.Ed
+.Pp
+The first value after the
+.Dq Li bar
+prefix in the square brackets is the offset of the BAR in config space in
+hexadecimal.
+The type of a BAR is one of
+.Dq Memory ,
+.Dq Prefetchable Memory ,
+or
+.Dq I/O Port .
+The range indicates the maximum address the BAR decodes.
+The base and size indicate the start and length of the BAR's address window,
+respectively.
+Finally, the last flag indicates if the BAR is enabled or disabled.
+.Pp
+If the
 .Fl c
 option is supplied,
 .Nm

Modified: head/usr.sbin/pciconf/pciconf.c
==============================================================================
--- head/usr.sbin/pciconf/pciconf.c	Mon Feb  2 19:22:53 2009	(r188017)
+++ head/usr.sbin/pciconf/pciconf.c	Mon Feb  2 19:54:16 2009	(r188018)
@@ -37,6 +37,7 @@ static const char rcsid[] =
 
 #include <ctype.h>
 #include <err.h>
+#include <inttypes.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -66,7 +67,8 @@ struct pci_vendor_info
 
 TAILQ_HEAD(,pci_vendor_info)	pci_vendors;
 
-static void list_devs(int verbose, int caps);
+static void list_bars(int fd, struct pci_conf *p);
+static void list_devs(int verbose, int bars, int caps);
 static void list_verbose(struct pci_conf *p);
 static const char *guess_class(struct pci_conf *p);
 static const char *guess_subclass(struct pci_conf *p);
@@ -81,7 +83,7 @@ static void
 usage(void)
 {
 	fprintf(stderr, "%s\n%s\n%s\n%s\n",
-		"usage: pciconf -l [-cv]",
+		"usage: pciconf -l [-bcv]",
 		"       pciconf -a selector",
 		"       pciconf -r [-b | -h] selector addr[:addr2]",
 		"       pciconf -w [-b | -h] selector addr value");
@@ -92,10 +94,10 @@ int
 main(int argc, char **argv)
 {
 	int c;
-	int listmode, readmode, writemode, attachedmode, caps, verbose;
+	int listmode, readmode, writemode, attachedmode, bars, caps, verbose;
 	int byte, isshort;
 
-	listmode = readmode = writemode = attachedmode = caps = verbose = byte = isshort = 0;
+	listmode = readmode = writemode = attachedmode = bars = caps = verbose = byte = isshort = 0;
 
 	while ((c = getopt(argc, argv, "abchlrwv")) != -1) {
 		switch(c) {
@@ -104,6 +106,7 @@ main(int argc, char **argv)
 			break;
 
 		case 'b':
+			bars = 1;
 			byte = 1;
 			break;
 
@@ -143,7 +146,7 @@ main(int argc, char **argv)
 		usage();
 
 	if (listmode) {
-		list_devs(verbose, caps);
+		list_devs(verbose, bars, caps);
 	} else if (attachedmode) {
 		chkattached(argv[optind],
 		    byte ? 1 : isshort ? 2 : 4);
@@ -161,7 +164,7 @@ main(int argc, char **argv)
 }
 
 static void
-list_devs(int verbose, int caps)
+list_devs(int verbose, int bars, int caps)
 {
 	int fd;
 	struct pci_conf_io pc;
@@ -217,6 +220,8 @@ list_devs(int verbose, int caps)
 			    p->pc_revid, p->pc_hdr);
 			if (verbose)
 				list_verbose(p);
+			if (bars)
+				list_bars(fd, p);
 			if (caps)
 				list_caps(fd, p);
 		}
@@ -226,6 +231,64 @@ list_devs(int verbose, int caps)
 }
 
 static void
+list_bars(int fd, struct pci_conf *p)
+{
+	struct pci_bar_io bar;
+	uint64_t base;
+	const char *type;
+	int i, range, max;
+
+	switch (p->pc_hdr & PCIM_HDRTYPE) {
+	case PCIM_HDRTYPE_NORMAL:
+		max = PCIR_MAX_BAR_0;
+		break;
+	case PCIM_HDRTYPE_BRIDGE:
+		max = PCIR_MAX_BAR_1;
+		break;
+	case PCIM_HDRTYPE_CARDBUS:
+		max = PCIR_MAX_BAR_2;
+		break;
+	default:
+		return;
+	}
+
+	for (i = 0; i <= max; i++) {
+		bar.pbi_sel = p->pc_sel;
+		bar.pbi_reg = PCIR_BAR(i);
+		if (ioctl(fd, PCIOCGETBAR, &bar) < 0)
+			continue;
+		if (PCI_BAR_IO(bar.pbi_base)) {
+			type = "I/O Port";
+			range = 32;
+			base = bar.pbi_base & PCIM_BAR_IO_BASE;
+		} else {
+			if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH)
+				type = "Prefetchable Memory";
+			else
+				type = "Memory";
+			switch (bar.pbi_base & PCIM_BAR_MEM_TYPE) {
+			case PCIM_BAR_MEM_32:
+				range = 32;
+				break;
+			case PCIM_BAR_MEM_1MB:
+				range = 20;
+				break;
+			case PCIM_BAR_MEM_64:
+				range = 64;
+				break;
+			default:
+				range = -1;
+			}
+			base = bar.pbi_base & ~((uint64_t)0xf);
+		}
+		printf("    bar   [%02x] = type %s, range %2d, base %#jx, ",
+		    PCIR_BAR(i), type, range, (uintmax_t)base);
+		printf("size %2d, %s\n", (int)bar.pbi_length,
+		    bar.pbi_enabled ? "enabled" : "disabled");
+	}
+}
+
+static void
 list_verbose(struct pci_conf *p)
 {
 	struct pci_vendor_info	*vi;



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