From 1441e406fabe396aab8355cef50a9c834ecb7688 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.

PCI probing takes a long time to scan resources for all devices
on PCI bus. Idea of this patch is to hardcode known resources
for known devices. In VMware hypervisor we do not have much
virtual PCI devices.

is_known_device() has list of known devices and available
resources (BARs) for them.

Added pci=scan_all cmdline parameter verifies hardcodded pci
values at runtime.

Signed-off-by: Alexey Makhalov <amakhalov@vmware.com>
Signed-off-by: Vikash Bansal <bvikas@vmware.com>
Signed-off-by: Bo Gan <ganb@vmware.com>
---
 drivers/pci/iov.c   |   3 +-
 drivers/pci/pci.c   |   2 +
 drivers/pci/pci.h   |   4 +-
 drivers/pci/probe.c | 269 +++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 270 insertions(+), 8 deletions(-)

diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 952217572113..19dbb064e05a 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -794,7 +794,8 @@ static int sriov_init(struct pci_dev *dev, int pos)
 			bar64 = (res->flags & IORESOURCE_MEM_64) ? 1 : 0;
 		else
 			bar64 = __pci_read_base(dev, pci_bar_unknown, res,
-						pos + PCI_SRIOV_BAR + i * 4);
+						pos + PCI_SRIOV_BAR + i * 4,
+						PCI_ERROR_RESPONSE, 0);
 		if (!res->flags)
 			continue;
 		if (resource_size(res) & (PAGE_SIZE - 1)) {
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 95bc329e74c0..7132e08d28a2 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -6896,6 +6896,8 @@ static int __init pci_setup(char *str)
 				pci_add_flags(PCI_SCAN_ALL_PCIE_DEVS);
 			} else if (!strncmp(str, "disable_acs_redir=", 18)) {
 				disable_acs_redir_param = str + 18;
+			} else if (!strncmp(str, "scan_all", 8)) {
+				pci_scan_all();
 			} else {
 				pr_err("PCI: Unknown option `%s'\n", str);
 			}
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 785f31086313..95f154404c0f 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -162,6 +162,7 @@ void pci_no_msi(void);
 static inline void pci_no_msi(void) { }
 #endif
 
+void pci_scan_all(void);
 void pci_realloc_get_opt(char *);
 
 static inline int pci_no_d1d2(struct pci_dev *dev)
@@ -234,7 +235,8 @@ int pci_idt_bus_quirk(struct pci_bus *bus, int devfn, u32 *pl, int crs_timeout);
 
 int pci_setup_device(struct pci_dev *dev);
 int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
-		    struct resource *res, unsigned int reg);
+		    struct resource *res, unsigned int reg,
+		    u32 sz_known, u32 sz2_known);
 void pci_configure_ari(struct pci_dev *dev);
 void __pci_bus_size_bridges(struct pci_bus *bus,
 			struct list_head *realloc_head);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index bdcad5e0f057..9360bf51a914 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -32,6 +32,159 @@ static struct resource busn_resource = {
 	.flags	= IORESOURCE_BUS,
 };
 
+/**
+ *pci_dev_bar_info : Representation of BARs for PCI device
+ *@vid: Vendor ID of PCI device
+ *@did: Device ID of PCI device
+ *@nbar: Numbers of bars to be read for PCI device
+ *@rom[2]: Known Values of ROM bars (rom1 for type1 (bridge) device)
+ *@bar[6]: Known Values of Bars 0-5
+ *
+ * bar[i]/rom = 0: no BAR at this position.
+ * bar[i]/rom = 0xffffffff: BAR has to be scanned,
+ *              applicable for variable size BAR (such as video memory).
+ * any other value: size in PCI BAR format.
+ */
+
+struct pci_dev_bar_info {
+	u16 did;
+	u8  nbar;
+	u32 rom[2];
+	u32 bar[6];
+};
+
+struct pci_vendor_bar_info {
+	u16 vid;
+	u32 ndev;
+	const struct pci_dev_bar_info *dev;
+};
+
+/* List of LSI Logic devices */
+static const struct pci_dev_bar_info lsi_devs[]  = {
+	{ 0x0030,	/* 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI */
+	4,		/* Number of bars */
+	{ 0xffffc000 },	/* ROM */
+			/* Bars */
+	{ 0xff01, 0xfffe0004, 0, 0xfffe0004 } },
+
+	{ 0x0054,	/* LSI Logic : SAS1068 PCI-X Fusion-MPT SAS */
+	4,		/* Number of bars */
+	{ 0xffffc000 },	/* ROM */
+			/* Bars */
+	{ 0xff01, 0xffffc004, 0, 0xffff0004 } },
+};
+
+
+/* List of Intel Devices */
+static const struct pci_dev_bar_info intel_devs[]  = {
+	{ 0x100f,	/* Eth controller[e1000]: Intel 82545EM Gigabit NIC */
+	5,		/* Number of bars */
+	{ 0xffff0000 },	/* ROM */
+			/* Bars */
+	{ 0xfffe0004, 0, 0xffff0004, 0, 0xffffffc1 } },
+
+	{ 0x10d3,	/* Eth controller[e1000e]: Intel 82574L Gigabit NIC */
+	4,		/* Number of bars */
+	{ 0xffff0000 },	/* ROM */
+			/* Bars */
+	{ 0xfffe0000, 0xfffe0000, 0xffffffe1, 0xffffc000 } },
+
+	{ 0x7110,	/* ISA bridge: 82371AB/EB/MB PIIX4 ISA */
+	0,		/* Number of bars */
+	{ 0 },		/* ROM */
+	{ 0 } },	/* Bars */
+
+	{ 0x7111,	/* IDE interface: 82371AB/EB/MB PIIX4 IDE */
+	5,		/* Number of bars */
+	{ 0 },		/* ROM */
+			/* Bars */
+	{ 0, 0, 0, 0, 0xfffffff1 } },
+
+	{ 0x7113,	/* Bridge: 82371AB/EB/MB PIIX4 ACPI */
+	0,		/* Number of bars */
+	{ 0 },		/* ROM */
+	{ 0 } },	/* Bars */
+
+	{ 0x7190,	/* Bridge: 82443BX/ZX/DX Host bridge */
+	0,		/* Number of bars */
+	{ 0 },		/* ROM */
+	{ 0 } },	/* Bars */
+
+	{ 0x7191,	/* PCI Bridge: Intel 440BX/ZX/DX - 82443BX/ZX/DX AGP */
+	0,		/* Number of bars */
+	{ 0 },		/* ROM */
+	{ 0 } },	/* Bars */
+};
+
+/* List of VMware Devices */
+static const struct pci_dev_bar_info vmw_devs[] = {
+	{ 0x0405,	/* VMware SVGA II Adapter */
+	3,		/* Number of bars */
+	{ 0xffff8000 },	/* ROM */
+			/* Bars */
+	{0xfffffff1, 0xffffffff, 0xff800000 } },
+
+	{ 0x0740,	/* VMware Virtual Machine Communication Interface */
+	2,		/* Number of bars */
+	{ 0 },		/* ROM */
+			/* Bars */
+	{0xffffffc1, 0xffffffff } },
+
+	{ 0x0790,	/* VMware PCI bridge */
+	0,		/* Number of bars */
+	{ 0 },		/* ROM */
+	{ 0 } },	/* Bars */
+
+	{ 0x07a0,	/* VMware PCI Express Root Port */
+	0,		/* Number of bars */
+	{ 0 },		/* ROM */
+	{ 0 } },	/* Bars */
+
+	{ 0x07b0,	/* VMware VMXNET3 Ethernet Controller */
+	4,		/* Number of bars */
+	{ 0xffff0000 },	/* ROM */
+			/* Bars */
+	{ 0xffffffff, 0xfffff000, 0xffffe000, 0xfffffff1 } },
+
+	{ 0x07c0,	/* VMware PVSCSI SCSI Controller */
+	2,		/* Number of bars */
+	{ 0xffff0000 },	/* ROM */
+			/* Bars (64-bit addressing for BAR 1) */
+	{ 0xfffffff9, 0xffff8004, 0xffffffff } },
+
+	{ 0x07e0,	/* VMware SATA AHCI controller */
+	6,		/* Number of bars */
+	{ 0xffff0000 },	/* ROM */
+			/* Bars */
+	{ 0, 0, 0, 0, 0, 0xfffff000 } },
+
+	{ 0x0770,	/* VMware USB2 EHCI Controller */
+	1,		/* Number of bars */
+	{ 0 },		/* ROM */
+			/* Bars */
+	{ 0xfffff000 } },
+
+	{ 0x0774,	/* VMware USB1.1 UHCI Controller */
+	5,		/* Number of bars */
+	{ 0 },		/* ROM */
+			/* Bars */
+	{ 0, 0, 0, 0, 0xffffffe1 } },
+
+	{ 0x0779,	/* VMware USB3 xHCI 1.0 Controller */
+	1,		/* Number of bars */
+	{ 0 },		/* ROM */
+			/* Bars */
+	{ 0xfffe0004 } },
+};
+
+static const struct pci_vendor_bar_info vendors_bar_table[] = {
+	{ 0x1000 /* LSI Logic */, ARRAY_SIZE(lsi_devs), lsi_devs },
+	{ 0x15ad /* VMware */,    ARRAY_SIZE(vmw_devs), vmw_devs },
+	{ 0x8086 /* Intel */,     ARRAY_SIZE(intel_devs), intel_devs },
+};
+
+static int pci_scan_and_verify;
+
 /* Ugh.  Need to stop exporting this to modules. */
 LIST_HEAD(pci_root_buses);
 EXPORT_SYMBOL(pci_root_buses);
@@ -163,6 +316,79 @@ static inline unsigned long decode_bar(struct pci_dev *dev, u32 bar)
 	return flags;
 }
 
+void pci_scan_all(void)
+{
+	pci_scan_and_verify = 1;
+}
+
+/**
+ * __check_known_bars_sz - Provide data for known PCI device BAR sizes
+ * @dev: the PCI device
+ * @reg: PCI register in the config space
+ * @sz_out: (out)
+ *     For known BARs, The data read from the BAR register after
+ *     a write of all 1's to it.
+ *     For non-existent Bars, 0
+ *     For BARs that do need probing, 0xffffffff (PCI_ERROR_RESPONSE)
+ * @sz2_out: (out)
+ *     Only when sz_out is sane (not 0 or PCI_ERROR_RESPONSE), it returns
+ *     the BAR+1 response for 64-bit memory probing
+ */
+static void __check_known_bars(struct pci_dev *dev, unsigned int reg,
+			       u32 *sz_out, u32 *sz2_out)
+{
+	u16 vid = dev->vendor;
+	u16 did = dev->device;
+	const struct pci_dev_bar_info *bar_info = NULL;
+	int i, j;
+
+	BUG_ON(
+		reg != PCI_BASE_ADDRESS_0 &&
+		reg != PCI_BASE_ADDRESS_1 &&
+		reg != PCI_BASE_ADDRESS_2 &&
+		reg != PCI_BASE_ADDRESS_3 &&
+		reg != PCI_BASE_ADDRESS_4 &&
+		reg != PCI_BASE_ADDRESS_5 &&
+		reg != PCI_ROM_ADDRESS &&
+		reg != PCI_ROM_ADDRESS1);
+
+	for (i = 0; i < ARRAY_SIZE(vendors_bar_table); i++) {
+		if (vendors_bar_table[i].vid != vid)
+			continue;
+
+		for (j = 0; j < vendors_bar_table[i].ndev; j++) {
+			if (vendors_bar_table[i].dev[j].did == did) {
+				bar_info = &vendors_bar_table[i].dev[j];
+				goto loop_out;
+			}
+		}
+		break; /* Matched table for vendor not having dev, so break */
+	}
+loop_out:
+	if (!bar_info) {
+		PCI_SET_ERROR_RESPONSE(sz_out);
+		return;
+	}
+	*sz2_out = 0;
+	switch (reg) {
+	case PCI_ROM_ADDRESS:
+		*sz_out = bar_info->rom[(reg - PCI_ROM_ADDRESS) >> 2];
+		break;
+	case PCI_ROM_ADDRESS1:
+		*sz_out = bar_info->rom[(reg - PCI_ROM_ADDRESS1) >> 2];
+		break;
+	default:
+		*sz_out = bar_info->bar[(reg - PCI_BASE_ADDRESS_0) >> 2];
+		break;
+	}
+	if ((*sz_out & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
+			PCI_BASE_ADDRESS_MEM_TYPE_64) {
+		/* Check 64-bit sizing, if so, respond with bar+1 */
+		BUG_ON(reg > PCI_BASE_ADDRESS_4);
+		*sz2_out = bar_info->bar[((reg - PCI_BASE_ADDRESS_0) >> 2) + 1];
+	}
+}
+
 #define PCI_COMMAND_DECODE_ENABLE	(PCI_COMMAND_MEMORY | PCI_COMMAND_IO)
 
 /**
@@ -175,17 +396,26 @@ static inline unsigned long decode_bar(struct pci_dev *dev, u32 bar)
  * Returns 1 if the BAR is 64-bit, or 0 if 32-bit.
  */
 int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
-		    struct resource *res, unsigned int pos)
+		    struct resource *res, unsigned int pos,
+		    const u32 sz_known, const u32 sz2_known)
 {
 	u32 l = 0, sz = 0, mask;
 	u64 l64, sz64, mask64;
 	u16 orig_cmd;
 	struct pci_bus_region region, inverted_region;
+	const bool known = !PCI_POSSIBLE_ERROR(sz_known);
+	const bool noprobe = known && !pci_scan_and_verify;
+	const bool validate = known && pci_scan_and_verify;
 
 	mask = type ? PCI_ROM_ADDRESS_MASK : ~0;
 
+	if (noprobe && !sz_known) {
+		/* BAR doesn't exist */
+		goto fail;
+	}
+
 	/* No printks while decoding is disabled! */
-	if (!dev->mmio_always_on) {
+	if (!noprobe && !dev->mmio_always_on) {
 		pci_read_config_word(dev, PCI_COMMAND, &orig_cmd);
 		if (orig_cmd & PCI_COMMAND_DECODE_ENABLE) {
 			pci_write_config_word(dev, PCI_COMMAND,
@@ -196,9 +426,17 @@ 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);
+	if (noprobe) {
+		sz = sz_known;
+		goto skip_size_probe0;
+	}
 	pci_write_config_dword(dev, pos, l | mask);
 	pci_read_config_dword(dev, pos, &sz);
 	pci_write_config_dword(dev, pos, l);
+skip_size_probe0:
+	if (validate && sz != sz_known)
+		dev_err(&dev->dev, "reg 0x%x: wrong known bar size: %x vs %x\n",
+			pos, sz_known, sz);
 
 	/*
 	 * All bits set in sz means the device isn't working properly.
@@ -238,16 +476,24 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
 
 	if (res->flags & IORESOURCE_MEM_64) {
 		pci_read_config_dword(dev, pos + 4, &l);
+		if (noprobe) {
+			sz = sz2_known;
+			goto skip_size_probe1;
+		}
 		pci_write_config_dword(dev, pos + 4, ~0);
 		pci_read_config_dword(dev, pos + 4, &sz);
 		pci_write_config_dword(dev, pos + 4, l);
+skip_size_probe1:
+		if (validate && sz != sz2_known)
+			dev_err(&dev->dev, "reg 0x%x: wrong known bar+1 size: %x vs %x\n",
+				pos, sz2_known, sz);
 
 		l64 |= ((u64)l << 32);
 		sz64 |= ((u64)sz << 32);
 		mask64 |= ((u64)~0 << 32);
 	}
 
-	if (!dev->mmio_always_on && (orig_cmd & PCI_COMMAND_DECODE_ENABLE))
+	if (!noprobe && !dev->mmio_always_on && (orig_cmd & PCI_COMMAND_DECODE_ENABLE))
 		pci_write_config_word(dev, PCI_COMMAND, orig_cmd);
 
 	if (!sz64)
@@ -313,8 +559,11 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
 fail:
 	res->flags = 0;
 out:
-	if (res->flags)
+	if (res->flags) {
 		pci_info(dev, "reg 0x%x: %pR\n", pos, res);
+		if (validate && !sz_known)
+			dev_err(&dev->dev, "reg 0x%x: Not in known BAR list\n", pos);
+	}
 
 	return (res->flags & IORESOURCE_MEM_64) ? 1 : 0;
 }
@@ -332,16 +581,24 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
 
 	for (pos = 0; pos < howmany; pos++) {
 		struct resource *res = &dev->resource[pos];
+		u32 sz_known = 0, sz2_known = 0;
+
 		reg = PCI_BASE_ADDRESS_0 + (pos << 2);
-		pos += __pci_read_base(dev, pci_bar_unknown, res, reg);
+		__check_known_bars(dev, reg, &sz_known, &sz2_known);
+		pos += __pci_read_base(dev, pci_bar_unknown, res, reg,
+					sz_known, sz2_known);
 	}
 
 	if (rom) {
 		struct resource *res = &dev->resource[PCI_ROM_RESOURCE];
+		u32 sz_known = 0, sz2_known = 0;
+
 		dev->rom_base_reg = rom;
 		res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH |
 				IORESOURCE_READONLY | IORESOURCE_SIZEALIGN;
-		__pci_read_base(dev, pci_bar_mem32, res, rom);
+		__check_known_bars(dev, rom, &sz_known, &sz2_known);
+		__pci_read_base(dev, pci_bar_mem32, res, rom,
+					sz_known, sz2_known);
 	}
 }
 
-- 
2.25.1