From a2d9f1f747247f31f769930a8198b12a0667d21f Mon Sep 17 00:00:00 2001
From: Alexey Makhalov <amakhalov@vmware.com>
Date: Tue, 29 Sep 2015 15:55:49 -0700
Subject: [PATCH] pci/probe.c: Hardcodded pci probe.
---
drivers/pci/probe.c | 312 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 310 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index edb1984..522bdaa 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -162,6 +162,281 @@ static inline unsigned long decode_bar(struct pci_dev *dev, u32 bar)
#define PCI_COMMAND_DECODE_ENABLE (PCI_COMMAND_MEMORY | PCI_COMMAND_IO)
+
+/* shortcut version of __pci_read_base where we know the sizes already */
+int __pci_read_base_shortcut(struct pci_dev *dev, enum pci_bar_type type,
+ struct resource *res, unsigned int pos, u32 sz_in, u32 sz2_in)
+{
+ u32 l, sz;
+ u64 l64, sz64, mask64;
+ struct pci_bus_region region, inverted_region;
+
+ res->name = pci_name(dev);
+
+ pci_read_config_dword(dev, pos, &l);
+
+ sz = sz_in;
+
+ /*
+ * All bits set in sz means the device isn't working properly.
+ * If the BAR isn't implemented, all bits must be 0. If it's a
+ * memory BAR or a ROM, bit 0 must be clear; if it's an io BAR, bit
+ * 1 must be clear.
+ * Here we set the size and is not 0xffffffff
+ */
+
+ /*
+ * I don't know how l can have all bits set. Copied from old code.
+ * Maybe it fixes a bug on some ancient platform.
+ */
+ if (l == 0xffffffff)
+ l = 0;
+
+ if (type == pci_bar_unknown) {
+ res->flags = decode_bar(dev, l);
+ res->flags |= IORESOURCE_SIZEALIGN;
+ if (res->flags & IORESOURCE_IO) {
+ l64 = l & PCI_BASE_ADDRESS_IO_MASK;
+ sz64 = sz & PCI_BASE_ADDRESS_IO_MASK;
+ mask64 = PCI_BASE_ADDRESS_IO_MASK & (u32)IO_SPACE_LIMIT;
+ } else {
+ l64 = l & PCI_BASE_ADDRESS_MEM_MASK;
+ sz64 = sz & PCI_BASE_ADDRESS_MEM_MASK;
+ mask64 = (u32)PCI_BASE_ADDRESS_MEM_MASK;
+ }
+ } else {
+ res->flags |= (l & IORESOURCE_ROM_ENABLE);
+ l64 = l & PCI_ROM_ADDRESS_MASK;
+ sz64 = sz & PCI_ROM_ADDRESS_MASK;
+ mask64 = (u32)PCI_ROM_ADDRESS_MASK;
+ }
+
+ if (res->flags & IORESOURCE_MEM_64) {
+ pci_read_config_dword(dev, pos + 4, &l);
+ sz = sz2_in;
+
+ l64 |= ((u64)l << 32);
+ sz64 |= ((u64)sz << 32);
+ mask64 |= ((u64)~0 << 32);
+ }
+
+ if (!sz64)
+ goto fail;
+
+ sz64 = pci_size(l64, sz64, mask64);
+ if (!sz64) {
+ dev_info(&dev->dev, FW_BUG "reg 0x%x: invalid BAR (can't size)\n",
+ pos);
+ goto fail;
+ }
+
+ if (res->flags & IORESOURCE_MEM_64) {
+ if ((sizeof(dma_addr_t) < 8 || sizeof(resource_size_t) < 8) &&
+ sz64 > 0x100000000ULL) {
+ res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED;
+ res->start = 0;
+ res->end = 0;
+ dev_err(&dev->dev, "reg 0x%x: can't handle BAR larger than 4GB (size %#010llx)\n",
+ pos, (unsigned long long)sz64);
+ goto out;
+ }
+
+ if ((sizeof(dma_addr_t) < 8) && l) {
+ /* Above 32-bit boundary; try to reallocate */
+ res->flags |= IORESOURCE_UNSET;
+ res->start = 0;
+ res->end = sz64;
+ dev_info(&dev->dev, "reg 0x%x: can't handle BAR above 4GB (bus address %#010llx)\n",
+ pos, (unsigned long long)l64);
+ goto out;
+ }
+ }
+
+ region.start = l64;
+ region.end = l64 + sz64;
+
+ pcibios_bus_to_resource(dev->bus, res, ®ion);
+ pcibios_resource_to_bus(dev->bus, &inverted_region, res);
+
+ /*
+ * If "A" is a BAR value (a bus address), "bus_to_resource(A)" is
+ * the corresponding resource address (the physical address used by
+ * the CPU. Converting that resource address back to a bus address
+ * should yield the original BAR value:
+ *
+ * resource_to_bus(bus_to_resource(A)) == A
+ *
+ * If it doesn't, CPU accesses to "bus_to_resource(A)" will not
+ * be claimed by the device.
+ */
+ if (inverted_region.start != region.start) {
+ res->flags |= IORESOURCE_UNSET;
+ res->start = 0;
+ res->end = region.end - region.start;
+ dev_info(&dev->dev, "reg 0x%x: initial BAR value %#010llx invalid\n",
+ pos, (unsigned long long)region.start);
+ }
+
+ goto out;
+
+
+fail:
+ res->flags = 0;
+out:
+ if (res->flags)
+ dev_printk(KERN_DEBUG, &dev->dev, "reg 0x%x: %pR\n", pos, res);
+
+ return (res->flags & IORESOURCE_MEM_64) ? 1 : 0;
+}
+
+static int is_known_device(struct pci_dev *dev, int pos, int *sz)
+{
+ switch (dev->vendor) {
+ /* Intel Corporation */
+ case 0x8086:
+ switch (dev->device) {
+ /* IDE interface: 82371AB/EB/MB PIIX4 IDE */
+ case 0x7111:
+ switch (pos) {
+ case 0x20:
+ *sz = 0xfffffff1;
+ return 1;
+ case 0x10:
+ case 0x14:
+ case 0x18:
+ case 0x1c:
+ case 0x24:
+ case 0x30:
+ *sz = 0; /* Not implemented */
+ return 1;
+ }
+ break;
+
+ /* Bridge: 82371AB/EB/MB PIIX4 ACPI */
+ case 0x7113:
+ switch (pos) {
+ case 0x10:
+ case 0x14:
+ case 0x18:
+ case 0x1c:
+ case 0x20:
+ case 0x24:
+ case 0x30:
+ *sz = 0; /* Not implemented */
+ return 1;
+ }
+ break;
+ }
+ break;
+
+ /* VMware, Inc */
+ case 0x15ad:
+ switch (dev->device) {
+ /* VMware SVGA II Adapter */
+ case 0x0405:
+ switch (pos) {
+ case 0x10:
+ *sz = 0xfffffff1;
+ return 1;
+ case 0x14:
+ *sz = 0xfc000000;
+ return 1;
+ case 0x18:
+ *sz = 0xff800000;
+ return 1;
+ case 0x1c:
+ case 0x20:
+ case 0x24:
+ *sz = 0; /* Not implemented */
+ return 1;
+ case 0x30:
+ *sz = 0xffff8000;
+ return 1;
+ }
+ break;
+
+ /* VMware Virtual Machine Communication Interface */
+ case 0x0740:
+ switch (pos) {
+ case 0x10:
+ *sz = 0xffffffc1;
+ return 1;
+ case 0x14:
+ *sz = 0xffffe000;
+ return 1;
+ case 0x1c:
+ case 0x20:
+ case 0x24:
+ case 0x30:
+ *sz = 0; /* Not implemented */
+ return 1;
+ }
+ break;
+
+ /* VMware PCI bridge */
+ case 0x0790:
+ /* VMware PCI Express Root Port */
+ case 0x07a0:
+ switch (pos) {
+ case 0x10:
+ case 0x14:
+ case 0x38:
+ *sz = 0; /* Not implemented */
+ return 1;
+ }
+ break;
+
+ /* VMware VMXNET3 Ethernet Controller */
+ case 0x07b0:
+ switch (pos) {
+ case 0x10:
+ *sz = 0xfffff000;
+ return 1;
+ case 0x14:
+ *sz = 0xfffff000;
+ return 1;
+ case 0x18:
+ *sz = 0xffffe000;
+ return 1;
+ case 0x1c:
+ *sz = 0xfffffff1;
+ return 1;
+ case 0x20:
+ case 0x24:
+ *sz = 0; /* Not implemented */
+ return 1;
+ case 0x30:
+ *sz = 0xffff0000;
+ return 1;
+ }
+ break;
+
+ /* VMware PVSCSI SCSI Controller */
+ case 0x07c0:
+ switch (pos) {
+ case 0x10:
+ *sz = 0xfffffff9;
+ return 1;
+ case 0x14:
+ *sz = 0xffff8000;
+ return 1;
+ case 0x1c:
+ case 0x20:
+ case 0x24:
+ *sz = 0; /* Not implemented */
+ return 1;
+ case 0x30:
+ *sz = 0xffff0000;
+ return 1;
+ }
+ break;
+ }
+ break;
+
+ }
+ return 0;
+}
+
/**
* pci_read_base - read a PCI BAR
* @dev: the PCI device
@@ -181,6 +456,13 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
mask = type ? PCI_ROM_ADDRESS_MASK : ~0;
+ if (is_known_device(dev, pos, &sz))
+ return __pci_read_base_shortcut(dev, type, res, pos, sz, 0);
+
+ res->name = pci_name(dev);
+
+ printk("Starting probe for %s %x:%x:%x\n", res->name, dev->vendor, dev->device, pos);
+
/* No printks while decoding is disabled! */
if (!dev->mmio_always_on) {
pci_read_config_word(dev, PCI_COMMAND, &orig_cmd);
@@ -190,7 +472,6 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
}
}
- res->name = pci_name(dev);
pci_read_config_dword(dev, pos, &l);
pci_write_config_dword(dev, pos, l | mask);
@@ -1145,6 +1426,28 @@ int pci_cfg_space_size(struct pci_dev *dev)
#define LEGACY_IO_RESOURCE (IORESOURCE_IO | IORESOURCE_PCI_FIXED)
+static int guess_bar_count(int class)
+{
+ if (class == 0x068000)
+ return 0;
+ if (class == 0x020000)
+ return 3;
+ if (class == 0x010000)
+ return 2;
+ if (class == 0x00ff00)
+ return 1;
+ return 6;
+}
+
+static int has_rom(int class, int rom)
+{
+ if (class == 0x020000)
+ return 0;
+ if (class == 0x010000 || class == 0x00ff00)
+ return 0;
+ return rom;
+}
+
void pci_msi_setup_pci_dev(struct pci_dev *dev)
{
/*
@@ -1178,6 +1481,7 @@ int pci_setup_device(struct pci_dev *dev)
int pos = 0;
struct pci_bus_region region;
struct resource *res;
+ int maxbar;
if (pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type))
return -EIO;
@@ -1224,7 +1528,11 @@ int pci_setup_device(struct pci_dev *dev)
if (class == PCI_CLASS_BRIDGE_PCI)
goto bad;
pci_read_irq(dev);
- pci_read_bases(dev, 6, PCI_ROM_ADDRESS);
+
+ maxbar = guess_bar_count(dev->class);
+
+ if (class != PCI_CLASS_STORAGE_IDE)
+ pci_read_bases(dev, maxbar, has_rom(dev->class, PCI_ROM_ADDRESS));
pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device);
--
1.9.1