[flashrom] [PATCH RFC] Access PCI BARs through sysfs

Ed Swierk eswierk at skyportsystems.com
Thu Feb 26 01:57:42 CET 2015


Various kernel options are known to interfere with flashrom's attempts to
access physical memory through /dev/mem. Some (like CONFIG_STRICT_DEVMEM)
are important security measures that cannot be disabled without recompiling
the kernel, and are likely to be used more widely over time.

However, complete access to physical memory is unnecessary if the SPI
interface can be accessed by mapping a BAR resource file in the PCI device
directory in sysfs.

I added a function pcidev_mapbar() that maps a PCI BAR resource through
sysfs if possible; otherwise it falls back to the existing
pcidev_mapbar()/rphysmap() method.

I modified several programmers to call pcidev_mapbar().  I also removed
the rget_io_perms() call, since obtaining IO privileges is unnecessary when
using the sysfs access method (the whole point of the patch!).  Instead,
rget_io_perms() is now called automatically by pcidev_init() only when
using some access method other than sysfs.

Several programmers (like satamv) require access to other memory or IO
regions as well as PCI BARs.  There's little point in converting these,
since the programmer still requires IO privileges.  And the it8212
programmer tries to enable the ROM BAR before accessing it; I have no
idea if sysfs can handle dynamically-appearing BARs, so I left that
programmer alone.

Running flashrom on a Linux system with CONFIG_STRICT_DEVMEM=y, I can now
successfully program the SPI flash on an Intel 10G NIC using the
nicintel_spi programmer.  I have not tested any other programmers or
system configurations.

Signed-off-by: Ed Swierk <eswierk at skyportsystems.com>

Index: drkaiser.c
===================================================================
--- drkaiser.c	(revision 1885)
+++ drkaiser.c	(working copy)
@@ -59,27 +59,20 @@
 int drkaiser_init(void)
 {
 	struct pci_dev *dev = NULL;
-	uint32_t addr;
 
-	if (rget_io_perms())
-		return 1;
-
 	dev = pcidev_init(drkaiser_pcidev, PCI_BASE_ADDRESS_2);
 	if (!dev)
 		return 1;
 
-	addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_2);
-	if (!addr)
+	/* Map 128kB flash memory window. */
+	drkaiser_bar = pcidev_mapbar(dev, "Dr. Kaiser PC-Waechter flash memory",
+				     PCI_BASE_ADDRESS_2, 0, DRKAISER_MEMMAP_SIZE);
+	if (drkaiser_bar == ERROR_PTR)
 		return 1;
 
 	/* Write magic register to enable flash write. */
 	rpci_write_word(dev, PCI_MAGIC_DRKAISER_ADDR, PCI_MAGIC_DRKAISER_VALUE);
 
-	/* Map 128kB flash memory window. */
-	drkaiser_bar = rphysmap("Dr. Kaiser PC-Waechter flash memory", addr, DRKAISER_MEMMAP_SIZE);
-	if (drkaiser_bar == ERROR_PTR)
-		return 1;
-
 	max_rom_decode.parallel = 128 * 1024;
 	register_par_master(&par_master_drkaiser, BUS_PARALLEL);
 
Index: gfxnvidia.c
===================================================================
--- gfxnvidia.c	(revision 1885)
+++ gfxnvidia.c	(working copy)
@@ -82,21 +82,12 @@
 	struct pci_dev *dev = NULL;
 	uint32_t reg32;
 
-	if (rget_io_perms())
-		return 1;
-
 	dev = pcidev_init(gfx_nvidia, PCI_BASE_ADDRESS_0);
 	if (!dev)
 		return 1;
 
-	uint32_t io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
-	if (!io_base_addr)
-		return 1;
-
-	io_base_addr += 0x300000;
-	msg_pinfo("Detected NVIDIA I/O base address: 0x%x.\n", io_base_addr);
-
-	nvidia_bar = rphysmap("NVIDIA", io_base_addr, GFXNVIDIA_MEMMAP_SIZE);
+	nvidia_bar = pcidev_mapbar(dev, "NVIDIA", PCI_BASE_ADDRESS_0, 0,
+				   GFXNVIDIA_MEMMAP_SIZE);
 	if (nvidia_bar == ERROR_PTR)
 		return 1;
 
Index: nicintel.c
===================================================================
--- nicintel.c	(revision 1885)
+++ nicintel.c	(working copy)
@@ -62,32 +62,19 @@
 int nicintel_init(void)
 {
 	struct pci_dev *dev = NULL;
-	uintptr_t addr;
 
-	/* Needed only for PCI accesses on some platforms.
-	 * FIXME: Refactor that into get_mem_perms/rget_io_perms/get_pci_perms?
-	 */
-	if (rget_io_perms())
-		return 1;
-
 	/* FIXME: BAR2 is not available if the device uses the CardBus function. */
 	dev = pcidev_init(nics_intel, PCI_BASE_ADDRESS_2);
 	if (!dev)
 		return 1;
 
-	addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_2);
-	if (!addr)
-		return 1;
-
-	nicintel_bar = rphysmap("Intel NIC flash", addr, NICINTEL_MEMMAP_SIZE);
+	nicintel_bar = pcidev_mapbar(dev, "Intel NIC flash", PCI_BASE_ADDRESS_2,
+				     0, NICINTEL_MEMMAP_SIZE);
 	if (nicintel_bar == ERROR_PTR)
 		return 1;
 
-	addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
-	if (!addr)
-		return 1;
-
-	nicintel_control_bar = rphysmap("Intel NIC control/status reg", addr, NICINTEL_CONTROL_MEMMAP_SIZE);
+	nicintel_control_bar = pcidev_mapbar(dev, "Intel NIC control/status reg", PCI_BASE_ADDRESS_0,
+					     0, NICINTEL_CONTROL_MEMMAP_SIZE);
 	if (nicintel_control_bar == ERROR_PTR)
 		return 1;
 
Index: nicintel_eeprom.c
===================================================================
--- nicintel_eeprom.c	(revision 1885)
+++ nicintel_eeprom.c	(working copy)
@@ -295,18 +295,12 @@
 
 int nicintel_ee_init(void)
 {
-	if (rget_io_perms())
-		return 1;
-
 	struct pci_dev *dev = pcidev_init(nics_intel_ee, PCI_BASE_ADDRESS_0);
 	if (!dev)
 		return 1;
 
-	uint32_t io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
-	if (!io_base_addr)
-		return 1;
-
-	nicintel_eebar = rphysmap("Intel Gigabit NIC w/ SPI EEPROM", io_base_addr, MEMMAP_SIZE);
+	nicintel_eebar = pcidev_mapbar(dev, "Intel Gigabit NIC w/ SPI EEPROM", PCI_BASE_ADDRESS_0,
+				       0, MEMMAP_SIZE);
 	nicintel_pci = dev;
 	if (dev->device_id != UNPROG_DEVICE) {
 		uint32_t eec = pci_mmio_readl(nicintel_eebar + EEC);
Index: nicintel_spi.c
===================================================================
--- nicintel_spi.c	(revision 1885)
+++ nicintel_spi.c	(working copy)
@@ -187,23 +187,16 @@
 	struct pci_dev *dev = NULL;
 	uint32_t tmp;
 
-	if (rget_io_perms())
-		return 1;
-
 	dev = pcidev_init(nics_intel_spi, PCI_BASE_ADDRESS_0);
 	if (!dev)
 		return 1;
 
-	uint32_t io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
-	if (!io_base_addr)
-		return 1;
-
 	if (dev->device_id < 0x10d8) {
-		nicintel_spibar = rphysmap("Intel Gigabit NIC w/ SPI flash", io_base_addr,
-					   MEMMAP_SIZE);
+		nicintel_spibar = pcidev_mapbar(dev, "Intel Gigabit NIC w/ SPI flash",
+						PCI_BASE_ADDRESS_0, 0, MEMMAP_SIZE);
 	} else {
-		nicintel_spibar = rphysmap("Intel 10 Gigabit NIC w/ SPI flash", io_base_addr + 0x10000,
-					   MEMMAP_SIZE);
+		nicintel_spibar = pcidev_mapbar(dev, "Intel 10 Gigabit NIC w/ SPI flash",
+						PCI_BASE_ADDRESS_0, 0x10000, MEMMAP_SIZE);
 	}
 	if (nicintel_spibar == ERROR_PTR)
 		return 1;
Index: ogp_spi.c
===================================================================
--- ogp_spi.c	(revision 1885)
+++ ogp_spi.c	(working copy)
@@ -125,18 +125,11 @@
 	}
 	free(type);
 
-	if (rget_io_perms())
-		return 1;
-
 	dev = pcidev_init(ogp_spi, PCI_BASE_ADDRESS_0);
 	if (!dev)
 		return 1;
 
-	uint32_t io_base_addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
-	if (!io_base_addr)
-		return 1;
-
-	ogp_spibar = rphysmap("OGP registers", io_base_addr, 4096);
+	ogp_spibar = pcidev_mapbar(dev, "OGP registers", PCI_BASE_ADDRESS_0, 0, 4096);
 	if (ogp_spibar == ERROR_PTR)
 		return 1;
 
Index: pcidev.c
===================================================================
--- pcidev.c	(revision 1885)
+++ pcidev.c	(working copy)
@@ -25,6 +25,12 @@
 #include "programmer.h"
 #include "hwaccess.h"
 
+#if !defined(__DJGPP__) && !defined(__LIBPAYLOAD__)
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/mman.h>
+#endif
+
 struct pci_access *pacc;
 
 enum pci_bartype {
@@ -174,6 +180,15 @@
 	pci_init(pacc);         /* Initialize the PCI library */
 	if (register_shutdown(pcidev_shutdown, NULL))
 		return 1;
+#if !defined(__DJGPP__) && !defined(__LIBPAYLOAD__)
+	if (pacc->method != PCI_ACCESS_SYS_BUS_PCI) {
+#else
+	if (1) {
+#endif
+		/* Acquire IO permissions if SYS_BUS_PCI access method is unavailable. */
+		if (rget_io_perms())
+			return 1;
+	}
 	pci_scan_bus(pacc);     /* We want to get the list of devices */
 	return 0;
 }
@@ -252,6 +267,73 @@
 	return found_dev;
 }
 
+#if !defined(__DJGPP__) && !defined(__LIBPAYLOAD__)
+static void *pcidev_mapbar_sysfs(struct pci_dev *dev, const char *descr, int bar, off_t offset, size_t length)
+{
+	void *addr;
+	static char path[PATH_MAX];
+	static char *file;
+	int fd;
+
+
+	switch (bar) {
+	case PCI_BASE_ADDRESS_0:
+		file = "resource0";
+		break;
+	case PCI_BASE_ADDRESS_1:
+		file = "resource1";
+		break;
+	case PCI_BASE_ADDRESS_2:
+		file = "resource2";
+		break;
+	case PCI_BASE_ADDRESS_3:
+		file = "resource3";
+		break;
+	case PCI_BASE_ADDRESS_4:
+		file = "resource4";
+		break;
+	case PCI_BASE_ADDRESS_5:
+		file = "resource5";
+		break;
+	case PCI_ROM_ADDRESS:
+		file = "rom";
+		break;
+	default:
+		msg_perr("Error accessing %s: invalid PCI bar 0x%x\n", descr, bar);
+		return MAP_FAILED;
+	}
+	snprintf(path, PATH_MAX, "%s/devices/%04x:%02x:%02x.%d/%s", pci_get_param(pacc, "sysfs.path"),
+		 dev->domain, dev->bus, dev->dev, dev->func, file);
+
+	fd = open(path, O_RDWR | O_SYNC);
+	if (fd < 0) {
+		msg_perr("Error accessing %s: open(%s): %s\n", descr, path, strerror(errno));
+		return MAP_FAILED;
+	}
+
+	addr = mmap(NULL, length, PROT_WRITE | PROT_READ, MAP_SHARED, fd, offset);
+	if (addr == MAP_FAILED) {
+		msg_perr("Error accessing %s: mmap(%s): %s\n", descr, path, strerror(errno));
+		return MAP_FAILED;
+	}
+
+	return addr;
+}
+#endif
+
+void *pcidev_mapbar(struct pci_dev *dev, const char *descr, int bar, off_t offset, size_t length)
+{
+#if !defined(__DJGPP__) && !defined(__LIBPAYLOAD__)
+	if (pacc->method == PCI_ACCESS_SYS_BUS_PCI)
+		return pcidev_mapbar_sysfs(dev, descr, bar, offset, length);
+#endif
+
+	uint32_t baraddr = pcidev_readbar(dev, bar);
+	if (!baraddr)
+		return MAP_FAILED;
+	return rphysmap(descr, baraddr + offset, length);
+}
+
 enum pci_write_type {
 	pci_write_type_byte,
 	pci_write_type_word,
Index: programmer.h
===================================================================
--- programmer.h	(revision 1885)
+++ programmer.h	(working copy)
@@ -187,6 +187,7 @@
 int pci_init_common(void);
 uintptr_t pcidev_readbar(struct pci_dev *dev, int bar);
 struct pci_dev *pcidev_init(const struct dev_entry *devs, int bar);
+void *pcidev_mapbar(struct pci_dev *dev, const char *descr, int bar, off_t offset, size_t length);
 /* rpci_write_* are reversible writes. The original PCI config space register
  * contents will be restored on shutdown.
  */
Index: satasii.c
===================================================================
--- satasii.c	(revision 1885)
+++ satasii.c	(working copy)
@@ -71,12 +71,7 @@
 int satasii_init(void)
 {
 	struct pci_dev *dev = NULL;
-	uint32_t addr;
-	uint16_t reg_offset;
 
-	if (rget_io_perms())
-		return 1;
-
 	dev = pcidev_init(satas_sii, PCI_BASE_ADDRESS_0);
 	if (!dev)
 		return 1;
@@ -84,21 +79,14 @@
 	id = dev->device_id;
 
 	if ((id == 0x3132) || (id == 0x3124)) {
-		addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_0);
-		if (!addr)
-			return 1;
-		reg_offset = 0x70;
+		sii_bar = pcidev_mapbar(dev, "SATA SiI registers",
+					PCI_BASE_ADDRESS_0, 0x70, SATASII_MEMMAP_SIZE);
 	} else {
-		addr = pcidev_readbar(dev, PCI_BASE_ADDRESS_5);
-		if (!addr)
-			return 1;
-		reg_offset = 0x50;
+		sii_bar = pcidev_mapbar(dev, "SATA SiI registers",
+					PCI_BASE_ADDRESS_5, 0x50, SATASII_MEMMAP_SIZE);
 	}
-
-	sii_bar = rphysmap("SATA SiI registers", addr, SATASII_MEMMAP_SIZE);
 	if (sii_bar == ERROR_PTR)
 		return 1;
-	sii_bar += reg_offset;
 
 	/* Check if ROM cycle are OK. */
 	if ((id != 0x0680) && (!(pci_mmio_readl(sii_bar) & (1 << 26))))




More information about the flashrom mailing list