]> git.baikalelectronics.ru Git - kernel.git/commitdiff
PCI: vmd: Disable bridge window for domain reset
authorNirmal Patel <nirmal.patel@linux.intel.com>
Thu, 10 Aug 2023 21:50:29 +0000 (17:50 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 23 Sep 2023 09:11:05 +0000 (11:11 +0200)
[ Upstream commit f73eedc90bf73d48e8368e6b0b4ad76a7fffaef7 ]

During domain reset process vmd_domain_reset() clears PCI
configuration space of VMD root ports. But certain platform
has observed following errors and failed to boot.
  ...
  DMAR: VT-d detected Invalidation Queue Error: Reason f
  DMAR: VT-d detected Invalidation Time-out Error: SID ffff
  DMAR: VT-d detected Invalidation Completion Error: SID ffff
  DMAR: QI HEAD: UNKNOWN qw0 = 0x0, qw1 = 0x0
  DMAR: QI PRIOR: UNKNOWN qw0 = 0x0, qw1 = 0x0
  DMAR: Invalidation Time-out Error (ITE) cleared

The root cause is that memset_io() clears prefetchable memory base/limit
registers and prefetchable base/limit 32 bits registers sequentially.
This seems to be enabling prefetchable memory if the device disabled
prefetchable memory originally.

Here is an example (before memset_io()):

  PCI configuration space for 10000:00:00.0:
  86 80 30 20 06 00 10 00 04 00 04 06 00 00 01 00
  00 00 00 00 00 00 00 00 00 01 01 00 00 00 00 20
  00 00 00 00 01 00 01 00 ff ff ff ff 75 05 00 00
  ...

So, prefetchable memory is ffffffff00000000-575000fffff, which is
disabled. When memset_io() clears prefetchable base 32 bits register,
the prefetchable memory becomes 0000000000000000-575000fffff, which is
enabled and incorrect.

Here is the quote from section 7.5.1.3.9 of PCI Express Base 6.0 spec:

  The Prefetchable Memory Limit register must be programmed to a smaller
  value than the Prefetchable Memory Base register if there is no
  prefetchable memory on the secondary side of the bridge.

This is believed to be the reason for the failure and in addition the
sequence of operation in vmd_domain_reset() is not following the PCIe
specs.

Disable the bridge window by executing a sequence of operations
borrowed from pci_disable_bridge_window() and pci_setup_bridge_io(),
that comply with the PCI specifications.

Link: https://lore.kernel.org/r/20230810215029.1177379-1-nirmal.patel@linux.intel.com
Signed-off-by: Nirmal Patel <nirmal.patel@linux.intel.com>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/pci/controller/vmd.c

index d1eb17e3f147490979bba6608d89dad7b41c13c6..d4c9b888a79d7dd02e4105321c2f58049762061e 100644 (file)
@@ -526,8 +526,23 @@ static void vmd_domain_reset(struct vmd_dev *vmd)
                                     PCI_CLASS_BRIDGE_PCI))
                                        continue;
 
-                               memset_io(base + PCI_IO_BASE, 0,
-                                         PCI_ROM_ADDRESS1 - PCI_IO_BASE);
+                               /*
+                                * Temporarily disable the I/O range before updating
+                                * PCI_IO_BASE.
+                                */
+                               writel(0x0000ffff, base + PCI_IO_BASE_UPPER16);
+                               /* Update lower 16 bits of I/O base/limit */
+                               writew(0x00f0, base + PCI_IO_BASE);
+                               /* Update upper 16 bits of I/O base/limit */
+                               writel(0, base + PCI_IO_BASE_UPPER16);
+
+                               /* MMIO Base/Limit */
+                               writel(0x0000fff0, base + PCI_MEMORY_BASE);
+
+                               /* Prefetchable MMIO Base/Limit */
+                               writel(0, base + PCI_PREF_LIMIT_UPPER32);
+                               writel(0x0000fff0, base + PCI_PREF_MEMORY_BASE);
+                               writel(0xffffffff, base + PCI_PREF_BASE_UPPER32);
                        }
                }
        }