From bbffe435248444065bd76141c41bbe65db950cc9 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 6 Jul 2012 12:08:18 -0600 Subject: PCI: leave MEM and IO decoding disabled during 64-bit BAR sizing, too After 253d2e5498, we disable MEM and IO decoding for most devices while we size 32-bit BARs. However, we restore the original COMMAND register before we size the upper 32 bits of 64-bit BARs, so we can still cause a conflict. This patch waits to restore the original COMMAND register until we're completely finished sizing the BAR. Reference: https://lkml.org/lkml/2007/8/25/154 Acked-by: Jacob Pan Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 658ac977cb5..66b3a6ffe5a 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -152,9 +152,6 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, pci_read_config_dword(dev, pos, &sz); pci_write_config_dword(dev, pos, l); - if (!dev->mmio_always_on) - pci_write_config_word(dev, PCI_COMMAND, orig_cmd); - /* * 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 @@ -239,6 +236,9 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, } out: + if (!dev->mmio_always_on) + pci_write_config_word(dev, PCI_COMMAND, orig_cmd); + return (res->flags & IORESOURCE_MEM_64) ? 1 : 0; fail: res->flags = 0; -- cgit v1.2.3 From 9aac537e0e33f4e4f28b8e7472c283fb6460c650 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 9 Jul 2012 19:49:37 -0600 Subject: 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 --- drivers/pci/setup-res.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) 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 @@ void pci_update_resource(struct pci_dev *dev, int resno) { struct pci_bus_region region; + bool disable; + u16 cmd; u32 new, check, mask; int reg; enum pci_bar_type type; @@ -67,6 +69,18 @@ void pci_update_resource(struct pci_dev *dev, int resno) new |= PCI_ROM_ADDRESS_ENABLE; } + /* + * We can't update a 64-bit BAR atomically, so when possible, + * disable decoding so that a half-updated BAR won't conflict + * with another device. + */ + disable = (res->flags & IORESOURCE_MEM_64) && !dev->mmio_always_on; + if (disable) { + pci_read_config_word(dev, PCI_COMMAND, &cmd); + pci_write_config_word(dev, PCI_COMMAND, + cmd & ~PCI_COMMAND_MEMORY); + } + pci_write_config_dword(dev, reg, new); pci_read_config_dword(dev, reg, &check); @@ -84,6 +98,10 @@ void pci_update_resource(struct pci_dev *dev, int resno) "(high %#08x != %#08x)\n", resno, new, check); } } + + if (disable) + pci_write_config_word(dev, PCI_COMMAND, cmd); + res->flags &= ~IORESOURCE_UNSET; dev_dbg(&dev->dev, "BAR %d: set to %pR (PCI address [%#llx-%#llx])\n", resno, res, (unsigned long long)region.start, -- cgit v1.2.3