0d8ce9
From a7e5f1d8f1f874434f8b3e6d6eac784d5e3e3971 Mon Sep 17 00:00:00 2001
0d8ce9
From: =?UTF-8?q?Volker=20R=C3=BCmelin?= <vr_qemu@t-online.de>
0d8ce9
Date: Sat, 2 Apr 2022 20:28:39 +0200
0d8ce9
Subject: [PATCH 3/3] reset: force standard PCI configuration access
0d8ce9
MIME-Version: 1.0
0d8ce9
Content-Type: text/plain; charset=UTF-8
0d8ce9
Content-Transfer-Encoding: 8bit
0d8ce9
0d8ce9
RH-Author: Gerd Hoffmann <kraxel@redhat.com>
0d8ce9
RH-MergeRequest: 7: pci: fix reset for q35 and tcg
0d8ce9
RH-Commit: [2/2] 693fbb9148c81f344dc9f64e7a36e51b42ee1a95
0d8ce9
RH-Bugzilla: 2083884
0d8ce9
RH-Acked-by: Pawel Polawski <ppolawsk@redhat.com>
0d8ce9
RH-Acked-by: Oliver Steffen <osteffen@redhat.com>
0d8ce9
RH-Acked-by: Igor Mammedov <imammedo@redhat.com>
0d8ce9
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
0d8ce9
0d8ce9
After a reset of a QEMU -machine q35 guest, the PCI Express
0d8ce9
Enhanced Configuration Mechanism is disabled and the variable
0d8ce9
mmconfig no longer matches the configuration register PCIEXBAR
0d8ce9
of the Q35 chipset. Until the variable mmconfig is reset to 0,
0d8ce9
all pci_config_*() functions no longer work.
0d8ce9
0d8ce9
The variable mmconfig is located in one of the read-only C-F
0d8ce9
segments. To reset it the pci_config_*() functions are needed,
0d8ce9
but they do not work.
0d8ce9
0d8ce9
Replace all pci_config_*() calls with Standard PCI Configuration
0d8ce9
Mechanism pci_ioconfig_*() calls until mmconfig is overwritten
0d8ce9
with 0 by a fresh copy of the BIOS.
0d8ce9
0d8ce9
This fixes
0d8ce9
0d8ce9
In resume (status=0)
0d8ce9
In 32bit resume
0d8ce9
Attempting a hard reboot
0d8ce9
Unable to unlock ram - bridge not found
0d8ce9
0d8ce9
and a reset loop with QEMU -accel tcg.
0d8ce9
0d8ce9
Signed-off-by: Volker RĂ¼melin <vr_qemu@t-online.de>
0d8ce9
(cherry picked from commit 01774004c7f7fdc9c1e8f1715f70d3b913f8d491)
0d8ce9
---
0d8ce9
 src/fw/shadow.c | 14 +++++++-------
0d8ce9
 src/hw/pci.c    | 27 +++++++++++++++++++++++++++
0d8ce9
 src/hw/pci.h    |  6 ++++++
0d8ce9
 3 files changed, 40 insertions(+), 7 deletions(-)
0d8ce9
0d8ce9
diff --git a/src/fw/shadow.c b/src/fw/shadow.c
0d8ce9
index 4c627a8f..8930616e 100644
0d8ce9
--- a/src/fw/shadow.c
0d8ce9
+++ b/src/fw/shadow.c
0d8ce9
@@ -32,8 +32,8 @@ __make_bios_writable_intel(u16 bdf, u32 pam0)
0d8ce9
 {
0d8ce9
     // Read in current PAM settings from pci config space
0d8ce9
     union pamdata_u pamdata;
0d8ce9
-    pamdata.data32[0] = pci_config_readl(bdf, ALIGN_DOWN(pam0, 4));
0d8ce9
-    pamdata.data32[1] = pci_config_readl(bdf, ALIGN_DOWN(pam0, 4) + 4);
0d8ce9
+    pamdata.data32[0] = pci_ioconfig_readl(bdf, ALIGN_DOWN(pam0, 4));
0d8ce9
+    pamdata.data32[1] = pci_ioconfig_readl(bdf, ALIGN_DOWN(pam0, 4) + 4);
0d8ce9
     u8 *pam = &pamdata.data8[pam0 & 0x03];
0d8ce9
 
0d8ce9
     // Make ram from 0xc0000-0xf0000 writable
0d8ce9
@@ -46,8 +46,8 @@ __make_bios_writable_intel(u16 bdf, u32 pam0)
0d8ce9
     pam[0] = 0x30;
0d8ce9
 
0d8ce9
     // Write PAM settings back to pci config space
0d8ce9
-    pci_config_writel(bdf, ALIGN_DOWN(pam0, 4), pamdata.data32[0]);
0d8ce9
-    pci_config_writel(bdf, ALIGN_DOWN(pam0, 4) + 4, pamdata.data32[1]);
0d8ce9
+    pci_ioconfig_writel(bdf, ALIGN_DOWN(pam0, 4), pamdata.data32[0]);
0d8ce9
+    pci_ioconfig_writel(bdf, ALIGN_DOWN(pam0, 4) + 4, pamdata.data32[1]);
0d8ce9
 
0d8ce9
     if (!ram_present)
0d8ce9
         // Copy bios.
0d8ce9
@@ -59,7 +59,7 @@ __make_bios_writable_intel(u16 bdf, u32 pam0)
0d8ce9
 static void
0d8ce9
 make_bios_writable_intel(u16 bdf, u32 pam0)
0d8ce9
 {
0d8ce9
-    int reg = pci_config_readb(bdf, pam0);
0d8ce9
+    int reg = pci_ioconfig_readb(bdf, pam0);
0d8ce9
     if (!(reg & 0x10)) {
0d8ce9
         // QEMU doesn't fully implement the piix shadow capabilities -
0d8ce9
         // if ram isn't backing the bios segment when shadowing is
0d8ce9
@@ -125,8 +125,8 @@ make_bios_writable(void)
0d8ce9
     // At this point, statically allocated variables can't be written,
0d8ce9
     // so do this search manually.
0d8ce9
     int bdf;
0d8ce9
-    foreachbdf(bdf, 0) {
0d8ce9
-        u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID);
0d8ce9
+    pci_ioconfig_foreachbdf(bdf, 0) {
0d8ce9
+        u32 vendev = pci_ioconfig_readl(bdf, PCI_VENDOR_ID);
0d8ce9
         u16 vendor = vendev & 0xffff, device = vendev >> 16;
0d8ce9
         if (vendor == PCI_VENDOR_ID_INTEL
0d8ce9
             && device == PCI_DEVICE_ID_INTEL_82441) {
0d8ce9
diff --git a/src/hw/pci.c b/src/hw/pci.c
0d8ce9
index f13cbdea..8eda84b2 100644
0d8ce9
--- a/src/hw/pci.c
0d8ce9
+++ b/src/hw/pci.c
0d8ce9
@@ -157,6 +157,33 @@ u8 pci_find_capability(u16 bdf, u8 cap_id, u8 cap)
0d8ce9
     return 0;
0d8ce9
 }
0d8ce9
 
0d8ce9
+// Helper function for pci_ioconfig_foreachbdf() macro - return next device
0d8ce9
+int pci_ioconfig_next(int bdf, int bus)
0d8ce9
+{
0d8ce9
+    if (pci_bdf_to_fn(bdf) == 0
0d8ce9
+        && (pci_ioconfig_readb(bdf, PCI_HEADER_TYPE) & 0x80) == 0)
0d8ce9
+        // Last found device wasn't a multi-function device - skip to
0d8ce9
+        // the next device.
0d8ce9
+        bdf += 8;
0d8ce9
+    else
0d8ce9
+        bdf += 1;
0d8ce9
+
0d8ce9
+    for (;;) {
0d8ce9
+        if (pci_bdf_to_bus(bdf) != bus)
0d8ce9
+            return -1;
0d8ce9
+
0d8ce9
+        u16 v = pci_ioconfig_readw(bdf, PCI_VENDOR_ID);
0d8ce9
+        if (v != 0x0000 && v != 0xffff)
0d8ce9
+            // Device is present.
0d8ce9
+            return bdf;
0d8ce9
+
0d8ce9
+        if (pci_bdf_to_fn(bdf) == 0)
0d8ce9
+            bdf += 8;
0d8ce9
+        else
0d8ce9
+            bdf += 1;
0d8ce9
+    }
0d8ce9
+}
0d8ce9
+
0d8ce9
 // Helper function for foreachbdf() macro - return next device
0d8ce9
 int
0d8ce9
 pci_next(int bdf, int bus)
0d8ce9
diff --git a/src/hw/pci.h b/src/hw/pci.h
0d8ce9
index ee6acafc..b2f5baf4 100644
0d8ce9
--- a/src/hw/pci.h
0d8ce9
+++ b/src/hw/pci.h
0d8ce9
@@ -27,6 +27,11 @@ static inline u16 pci_bus_devfn_to_bdf(int bus, u16 devfn) {
0d8ce9
     return (bus << 8) | devfn;
0d8ce9
 }
0d8ce9
 
0d8ce9
+#define pci_ioconfig_foreachbdf(BDF, BUS)                               \
0d8ce9
+    for (BDF=pci_ioconfig_next(pci_bus_devfn_to_bdf((BUS), 0)-1, (BUS)) \
0d8ce9
+         ; BDF >= 0                                                     \
0d8ce9
+         ; BDF=pci_ioconfig_next(BDF, (BUS)))
0d8ce9
+
0d8ce9
 #define foreachbdf(BDF, BUS)                                    \
0d8ce9
     for (BDF=pci_next(pci_bus_devfn_to_bdf((BUS), 0)-1, (BUS))  \
0d8ce9
          ; BDF >= 0                                             \
0d8ce9
@@ -39,6 +44,7 @@ void pci_ioconfig_writeb(u16 bdf, u32 addr, u8 val);
0d8ce9
 u32 pci_ioconfig_readl(u16 bdf, u32 addr);
0d8ce9
 u16 pci_ioconfig_readw(u16 bdf, u32 addr);
0d8ce9
 u8 pci_ioconfig_readb(u16 bdf, u32 addr);
0d8ce9
+int pci_ioconfig_next(int bdf, int bus);
0d8ce9
 
0d8ce9
 // PCI configuration access using either PCI CAM or PCIe ECAM
0d8ce9
 void pci_config_writel(u16 bdf, u32 addr, u32 val);
0d8ce9
-- 
0d8ce9
2.35.3
0d8ce9