From c6e73db5fafa151b81a969a74c3138488a4310fa 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.

Added pci=scan_all cmdline parameter to verify hardcodded pci values
---
 drivers/pci/pci.c   |   2 +
 drivers/pci/pci.h   |   2 +
 drivers/pci/probe.c | 377 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 377 insertions(+), 4 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 6922964e3dff..e4db16e7c1d2 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -5303,6 +5303,8 @@ static int __init pci_setup(char *str)
 				pcie_bus_config = PCIE_BUS_PEER2PEER;
 			} else if (!strncmp(str, "pcie_scan_all", 13)) {
 				pci_add_flags(PCI_SCAN_ALL_PCIE_DEVS);
+			} else if (!strncmp(str, "scan_all", 8)) {
+				pci_scan_all();
 			} else {
 				printk(KERN_ERR "PCI: Unknown option `%s'\n",
 						str);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index a5d37f6a9fb5..aaa456c9bf95 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -163,6 +163,8 @@ void pci_no_msi(void);
 static inline void pci_no_msi(void) { }
 #endif
 
+void pci_scan_all(void);
+
 static inline void pci_msi_set_enable(struct pci_dev *dev, int enable)
 {
 	u16 control;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index d266d800f246..11544ddd6e5c 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -163,6 +163,346 @@ static inline unsigned long decode_bar(struct pci_dev *dev, u32 bar)
 
 #define PCI_COMMAND_DECODE_ENABLE	(PCI_COMMAND_MEMORY | PCI_COMMAND_IO)
 
+static int pci_scan_and_verify = 0;
+
+void pci_scan_all(void)
+{
+	pci_scan_and_verify = 1;
+}
+
+/* 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 = 0, sz = 0;
+	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 {
+		if (l & PCI_ROM_ADDRESS_ENABLE)
+			res->flags |= IORESOURCE_ROM_ENABLE;
+		l64 = l & PCI_ROM_ADDRESS_MASK;
+		sz64 = sz & PCI_ROM_ADDRESS_MASK;
+		mask64 = 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(pci_bus_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(pci_bus_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, &region);
+	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 guess_bar_count(int class)
+{
+        if (class == 0x088000)
+            return 2;
+        if (class == 0x068000)
+            return 0;
+        if (class == 0x060100)
+            return 0;
+        if (class == 0x060000)
+            return 0;
+        if (class == 0x030000)
+            return 3;
+        if (class == 0x020000 || class == 0x010000)
+            return 4;
+        if (class == 0x00ff00)
+            return 1;
+        return 6;
+}
+
+static int has_rom(int class, int rom)
+{
+        if (class == 0x088000 ||
+	    class == 0x060100 ||
+	    class == 0x060000 ||
+	    class == 0x00ff00)
+          return 0;
+        return rom;
+}
+
+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:
+					/* variable mem size */
+					*sz = 0xffffffff;
+					return 0;
+				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 = 0xffffe004;
+					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;
+
+		/* SCSI storage controller */
+		case 0x1000:
+		switch (dev->device) {
+			/* LSI Logic */
+			case 0x0030:
+			switch (pos) {
+				case 0x10:
+					*sz = 0xff01;
+					return 1;
+				case 0x14:
+					*sz = 0xfffe0004;
+					return 1;
+				case 0x1c:
+					*sz = 0xfffe0004;
+					return 1;
+				case 0x18:
+				case 0x20:
+				case 0x24:
+					*sz = 0; /* Not implemented */
+					return 1;
+				case 0x30:
+					*sz = 0xffffc000;
+					return 1;
+			}
+			break;
+		}
+		break;
+	}
+	*sz = 0xffffffff;
+        return 0;
+}
+
 /**
  * pci_read_base - read a PCI BAR
  * @dev: the PCI device
@@ -175,13 +515,20 @@ static inline unsigned long decode_bar(struct pci_dev *dev, u32 bar)
 int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
 		    struct resource *res, unsigned int pos)
 {
-	u32 l = 0, sz = 0, mask;
+	u32 l = 0, sz = 0, known_sz, mask;
 	u64 l64, sz64, mask64;
 	u16 orig_cmd;
 	struct pci_bus_region region, inverted_region;
 
 	mask = type ? PCI_ROM_ADDRESS_MASK : ~0;
 
+	if (is_known_device(dev, pos, &known_sz) && !pci_scan_and_verify)
+		return __pci_read_base_shortcut(dev, type, res, pos, known_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);
@@ -191,7 +538,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);
@@ -207,6 +553,11 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
 	if (sz == 0xffffffff)
 		sz = 0;
 
+	if (pci_scan_and_verify && known_sz != 0xffffffff && known_sz != sz)
+		dev_err(&dev->dev,
+			"wrong known bar size: %x != %x\n",
+			known_sz, sz);
+
 	/*
 	 * I don't know how l can have all bits set.  Copied from old code.
 	 * Maybe it fixes a bug on some ancient platform.
@@ -311,8 +662,19 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
 fail:
 	res->flags = 0;
 out:
-	if (res->flags)
+	if (res->flags) {
 		dev_printk(KERN_DEBUG, &dev->dev, "reg 0x%x: %pR\n", pos, res);
+		if (pci_scan_and_verify) {
+			int bar = (pos - PCI_BASE_ADDRESS_0) >> 2;
+			int maxbar = guess_bar_count(dev->class);
+			if (bar < 6 && bar >= maxbar)
+				dev_err(&dev->dev,
+					"wrong guess_bar_count value %d\n",
+					maxbar);
+			if (pos == PCI_ROM_ADDRESS && !has_rom(dev->class, 1))
+				dev_err(&dev->dev, "wrong has_rom value\n");
+		}
+	}
 
 	return (res->flags & IORESOURCE_MEM_64) ? 1 : 0;
 }
@@ -1265,7 +1627,14 @@ 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);
+
+		if (pci_scan_and_verify)
+			pci_read_bases(dev, 6, PCI_ROM_ADDRESS);
+		else if (class != PCI_CLASS_STORAGE_IDE)
+			pci_read_bases(dev,
+			guess_bar_count(dev->class),
+			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);
 
-- 
2.11.0