From c6e73db5fafa151b81a969a74c3138488a4310fa Mon Sep 17 00:00:00 2001 From: Alexey Makhalov 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, ®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 @@ -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