aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2012-07-09 21:49:37 -0400
committerBjorn Helgaas <bhelgaas@google.com>2012-07-09 21:50:53 -0400
commit9aac537e0e33f4e4f28b8e7472c283fb6460c650 (patch)
tree0e27b819f093360a0e028b0af20ee8ae6ad949f3 /drivers/pci
parentbbffe435248444065bd76141c41bbe65db950cc9 (diff)
PCI: disable MEM decoding while updating 64-bit MEM BARs
When we update 64-bit BARs, we have to perform two config writes. Between the writes, the half-written BAR value could match a MEM access intended for another device. This could result in corruption of this device (for writes) or an unexpected response machine check (for reads). To prevent this, disable MEM decoding while updating such BARs. This uses the same safety test as 253d2e5498, which disables both MEM and IO while sizing BARs, namely, we don't disable decoding for host bridge devices. Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/setup-res.c18
1 files changed, 18 insertions, 0 deletions
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index eea85dafc76..1a0e60e265e 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -30,6 +30,8 @@
30void pci_update_resource(struct pci_dev *dev, int resno) 30void pci_update_resource(struct pci_dev *dev, int resno)
31{ 31{
32 struct pci_bus_region region; 32 struct pci_bus_region region;
33 bool disable;
34 u16 cmd;
33 u32 new, check, mask; 35 u32 new, check, mask;
34 int reg; 36 int reg;
35 enum pci_bar_type type; 37 enum pci_bar_type type;
@@ -67,6 +69,18 @@ void pci_update_resource(struct pci_dev *dev, int resno)
67 new |= PCI_ROM_ADDRESS_ENABLE; 69 new |= PCI_ROM_ADDRESS_ENABLE;
68 } 70 }
69 71
72 /*
73 * We can't update a 64-bit BAR atomically, so when possible,
74 * disable decoding so that a half-updated BAR won't conflict
75 * with another device.
76 */
77 disable = (res->flags & IORESOURCE_MEM_64) && !dev->mmio_always_on;
78 if (disable) {
79 pci_read_config_word(dev, PCI_COMMAND, &cmd);
80 pci_write_config_word(dev, PCI_COMMAND,
81 cmd & ~PCI_COMMAND_MEMORY);
82 }
83
70 pci_write_config_dword(dev, reg, new); 84 pci_write_config_dword(dev, reg, new);
71 pci_read_config_dword(dev, reg, &check); 85 pci_read_config_dword(dev, reg, &check);
72 86
@@ -84,6 +98,10 @@ void pci_update_resource(struct pci_dev *dev, int resno)
84 "(high %#08x != %#08x)\n", resno, new, check); 98 "(high %#08x != %#08x)\n", resno, new, check);
85 } 99 }
86 } 100 }
101
102 if (disable)
103 pci_write_config_word(dev, PCI_COMMAND, cmd);
104
87 res->flags &= ~IORESOURCE_UNSET; 105 res->flags &= ~IORESOURCE_UNSET;
88 dev_dbg(&dev->dev, "BAR %d: set to %pR (PCI address [%#llx-%#llx])\n", 106 dev_dbg(&dev->dev, "BAR %d: set to %pR (PCI address [%#llx-%#llx])\n",
89 resno, res, (unsigned long long)region.start, 107 resno, res, (unsigned long long)region.start,