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 -ur linux-4.19.1/drivers/pci/pci.c linux-4.19.1_new/drivers/pci/pci.c
--- linux-4.19.1/drivers/pci/pci.c 2018-11-04 13:50:54.000000000 +0000
+++ linux-4.19.1_new/drivers/pci/pci.c 2018-11-08 01:59:21.813106907 +0000
@@ -6116,6 +6116,8 @@
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 {
printk(KERN_ERR "PCI: Unknown option `%s'\n",
str);
diff -ur linux-4.19.1/drivers/pci/pci.h linux-4.19.1_new/drivers/pci/pci.h
--- linux-4.19.1/drivers/pci/pci.h 2018-11-04 13:50:54.000000000 +0000
+++ linux-4.19.1_new/drivers/pci/pci.h 2018-11-08 01:57:51.917110565 +0000
@@ -147,6 +147,8 @@
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 -ur linux-4.19.1/drivers/pci/probe.c linux-4.19.1_new/drivers/pci/probe.c
--- linux-4.19.1/drivers/pci/probe.c 2018-11-04 13:50:54.000000000 +0000
+++ linux-4.19.1_new/drivers/pci/probe.c 2018-11-08 01:57:51.917110565 +0000
@@ -168,6 +168,346 @@
#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, ®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 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
@@ -180,13 +520,20 @@
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);
@@ -196,7 +543,6 @@
}
}
- res->name = pci_name(dev);
pci_read_config_dword(dev, pos, &l);
pci_write_config_dword(dev, pos, l | mask);
@@ -212,6 +558,11 @@
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.
@@ -316,8 +667,19 @@
fail:
res->flags = 0;
out:
- if (res->flags)
+ if (res->flags) {
pci_printk(KERN_DEBUG, 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;
}
@@ -1646,7 +2008,13 @@
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_subsystem_ids(dev, &dev->subsystem_vendor, &dev->subsystem_device);