summaryrefslogtreecommitdiff
path: root/drivers/pci
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2016-02-25 14:35:57 -0600
committerSasha Levin <sasha.levin@oracle.com>2016-04-18 08:50:40 -0400
commitaa57ba13f44426a076ac567e965654453d4be1f1 (patch)
tree4e7c08f27c0369f03071480738d706b7d73b2e74 /drivers/pci
parent517a021fdba44206722c85bd9267dabd67475fa6 (diff)
PCI: Disable IO/MEM decoding for devices with non-compliant BARs
[ Upstream commit b84106b4e2290c081cdab521fa832596cdfea246 ] The PCI config header (first 64 bytes of each device's config space) is defined by the PCI spec so generic software can identify the device and manage its usage of I/O, memory, and IRQ resources. Some non-spec-compliant devices put registers other than BARs where the BARs should be. When the PCI core sizes these "BARs", the reads and writes it does may have unwanted side effects, and the "BAR" may appear to describe non-sensical address space. Add a flag bit to mark non-compliant devices so we don't touch their BARs. Turn off IO/MEM decoding to prevent the devices from consuming address space, since we can't read the BARs to find out what that address space would be. Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Tested-by: Andi Kleen <ak@linux.intel.com> CC: stable@vger.kernel.org Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/probe.c14
1 files changed, 14 insertions, 0 deletions
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index bbfd01ca996a..25ad1b27ffae 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -176,6 +176,9 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
u16 orig_cmd;
struct pci_bus_region region, inverted_region;
+ if (dev->non_compliant_bars)
+ return 0;
+
mask = type ? PCI_ROM_ADDRESS_MASK : ~0;
/* No printks while decoding is disabled! */
@@ -1116,6 +1119,7 @@ int pci_cfg_space_size(struct pci_dev *dev)
int pci_setup_device(struct pci_dev *dev)
{
u32 class;
+ u16 cmd;
u8 hdr_type;
struct pci_slot *slot;
int pos = 0;
@@ -1163,6 +1167,16 @@ int pci_setup_device(struct pci_dev *dev)
/* device class may be changed after fixup */
class = dev->class >> 8;
+ if (dev->non_compliant_bars) {
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ if (cmd & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) {
+ dev_info(&dev->dev, "device has non-compliant BARs; disabling IO/MEM decoding\n");
+ cmd &= ~PCI_COMMAND_IO;
+ cmd &= ~PCI_COMMAND_MEMORY;
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
+ }
+
switch (dev->hdr_type) { /* header type */
case PCI_HEADER_TYPE_NORMAL: /* standard header */
if (class == PCI_CLASS_BRIDGE_PCI)