SPECS/linux/02-pci-probe.patch
e7547e1f
 From c6e73db5fafa151b81a969a74c3138488a4310fa Mon Sep 17 00:00:00 2001
becc5c79
 From: Alexey Makhalov <amakhalov@vmware.com>
 Date: Tue, 29 Sep 2015 15:55:49 -0700
 Subject: [PATCH] pci/probe.c: Hardcodded pci probe.
8e2e50b6
 
b3e5d5c4
 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.
 It returns: 0 if BAR not available for the given device
             1 if available and sz parameter will have BAR size
 
 guess_bar_count() returns maximum number of BARs for the given
 class to do not probe for all 6 bars
 
 has_rom() returns 0 if given class does not have any ROM BARs,
 which allows to skip ROM probing
 
 Added pci=scan_all cmdline parameter verifies hardcodded pci
 values at runtime.
becc5c79
 ---
e7547e1f
  drivers/pci/pci.c   |   2 +
  drivers/pci/pci.h   |   2 +
  drivers/pci/probe.c | 377 +++++++++++++++++++++++++++++++++++++++++++++++++++-
  3 files changed, 377 insertions(+), 4 deletions(-)
becc5c79
 
5d3b52dd
 
 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 @@
e7547e1f
  				pci_add_flags(PCI_SCAN_ALL_PCIE_DEVS);
5d3b52dd
  			} else if (!strncmp(str, "disable_acs_redir=", 18)) {
  				disable_acs_redir_param = str + 18;
e7547e1f
 +			} else if (!strncmp(str, "scan_all", 8)) {
 +				pci_scan_all();
  			} else {
  				printk(KERN_ERR "PCI: Unknown option `%s'\n",
  						str);
5d3b52dd
 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 @@
e7547e1f
  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;
5d3b52dd
 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 @@
8e2e50b6
  
  #define PCI_COMMAND_DECODE_ENABLE	(PCI_COMMAND_MEMORY | PCI_COMMAND_IO)
  
e7547e1f
 +static int pci_scan_and_verify = 0;
 +
 +void pci_scan_all(void)
 +{
 +	pci_scan_and_verify = 1;
 +}
8e2e50b6
 +
 +/* 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)
 +{
c8bf217f
 +	u32 l = 0, sz = 0;
8e2e50b6
 +	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 {
e7547e1f
 +		if (l & PCI_ROM_ADDRESS_ENABLE)
 +			res->flags |= IORESOURCE_ROM_ENABLE;
8e2e50b6
 +		l64 = l & PCI_ROM_ADDRESS_MASK;
 +		sz64 = sz & PCI_ROM_ADDRESS_MASK;
c8bf217f
 +		mask64 = PCI_ROM_ADDRESS_MASK;
8e2e50b6
 +	}
 +
 +	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) {
c8bf217f
 +		if ((sizeof(pci_bus_addr_t) < 8 || sizeof(resource_size_t) < 8) &&
8e2e50b6
 +		    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;
 +		}
 +
c8bf217f
 +		if ((sizeof(pci_bus_addr_t) < 8) && l) {
8e2e50b6
 +			/* 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;
 +}
 +
e7547e1f
 +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;
 +}
 +
8e2e50b6
 +static int is_known_device(struct pci_dev *dev, int pos, int *sz)
 +{
a3c18b12
 +	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;
becc5c79
 +				case 0x10:
a3c18b12
 +				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) {
becc5c79
 +				case 0x10:
a3c18b12
 +				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 */
becc5c79
 +			case 0x0405:
a3c18b12
 +			switch (pos) {
becc5c79
 +				case 0x10:
a3c18b12
 +					*sz = 0xfffffff1;
becc5c79
 +					return 1;
a3c18b12
 +				case 0x14:
e7547e1f
 +					/* variable mem size */
 +					*sz = 0xffffffff;
 +					return 0;
a3c18b12
 +				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 */
becc5c79
 +			case 0x0740:
a3c18b12
 +			switch (pos) {
becc5c79
 +				case 0x10:
a3c18b12
 +					*sz = 0xffffffc1;
 +					return 1;
 +				case 0x14:
e7547e1f
 +					*sz = 0xffffe004;
a3c18b12
 +					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) {
becc5c79
 +				case 0x10:
a3c18b12
 +				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 */
becc5c79
 +			case 0x07c0:
a3c18b12
 +			switch (pos) {
becc5c79
 +				case 0x10:
a3c18b12
 +					*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;
 +
e7547e1f
 +		/* 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;
a3c18b12
 +	}
e7547e1f
 +	*sz = 0xffffffff;
8e2e50b6
 +        return 0;
 +}
 +
  /**
67abad8d
   * pci_read_base - Read a PCI BAR
8e2e50b6
   * @dev: the PCI device
5d3b52dd
 @@ -180,13 +520,20 @@
e7547e1f
  int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
  		    struct resource *res, unsigned int pos)
  {
c8bf217f
 -	u32 l = 0, sz = 0, mask;
 +	u32 l = 0, sz = 0, known_sz, mask;
e7547e1f
  	u64 l64, sz64, mask64;
  	u16 orig_cmd;
  	struct pci_bus_region region, inverted_region;
8e2e50b6
  
  	mask = type ? PCI_ROM_ADDRESS_MASK : ~0;
  
e7547e1f
 +	if (is_known_device(dev, pos, &known_sz) && !pci_scan_and_verify)
 +		return __pci_read_base_shortcut(dev, type, res, pos, known_sz, 0);
8e2e50b6
 +
 +	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);
5d3b52dd
 @@ -196,7 +543,6 @@
8e2e50b6
  		}
  	}
  
 -	res->name = pci_name(dev);
  
  	pci_read_config_dword(dev, pos, &l);
  	pci_write_config_dword(dev, pos, l | mask);
5d3b52dd
 @@ -212,6 +558,11 @@
e7547e1f
  	if (sz == 0xffffffff)
  		sz = 0;
a3c18b12
  
e7547e1f
 +	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);
a3c18b12
 +
87fc5e5e
  	/*
e7547e1f
  	 * I don't know how l can have all bits set.  Copied from old code.
  	 * Maybe it fixes a bug on some ancient platform.
5d3b52dd
 @@ -316,8 +667,19 @@
e7547e1f
  fail:
  	res->flags = 0;
  out:
 -	if (res->flags)
 +	if (res->flags) {
67abad8d
  		pci_printk(KERN_DEBUG, dev, "reg 0x%x: %pR\n", pos, res);
e7547e1f
 +		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");
 +		}
 +	}
a3c18b12
  
e7547e1f
  	return (res->flags & IORESOURCE_MEM_64) ? 1 : 0;
  }
5d3b52dd
 @@ -1646,7 +2008,13 @@
a3c18b12
  		if (class == PCI_CLASS_BRIDGE_PCI)
  			goto bad;
  		pci_read_irq(dev);
 -		pci_read_bases(dev, 6, PCI_ROM_ADDRESS);
 +
e7547e1f
 +		if (pci_scan_and_verify)
 +			pci_read_bases(dev, 6, PCI_ROM_ADDRESS);
b3e5d5c4
 +		else
e7547e1f
 +			pci_read_bases(dev,
b3e5d5c4
 +				guess_bar_count(dev->class),
 +				has_rom(dev->class, PCI_ROM_ADDRESS));
a3c18b12
  
67abad8d
  		pci_subsystem_ids(dev, &dev->subsystem_vendor, &dev->subsystem_device);