mch_update_smbase_smram() essentially uses wmask[MCH_HOST_BRIDGE_F_SMBASE]
to track SMBASE area state. Since 'wmask' state is not migrated is not
migrated, the destination QEMU always sees
 wmask[MCH_HOST_BRIDGE_F_SMBASE] == 0xff

As a result, when mch_update() calls mch_update_smbase_smram() on the
destination, it resets ->config[MCH_HOST_BRIDGE_F_SMBASE] and disables
the smbase-window memory region—even if it was enabled on the source.

'info mtree' on source:
  address-space: KVM-SMRAM
    0000000000000000-ffffffffffffffff (prio 0, i/o): mem-container-smram
      0000000000000000-00000000ffffffff (prio 10, i/o): smram
        0000000000030000-000000000004ffff (prio 0, i/o): alias smbase-window 
@pc.ram
        00000000000a0000-00000000000bffff (prio 0, i/o): alias smram-low @pc.ram
        000000007f000000-000000007fffffff (prio 0, i/o): alias tseg-window 
@pc.ram
      0000000000000000-ffffffffffffffff (prio 0, i/o): alias mem-smram @system

'info mtree' on dest after migration:
  address-space: KVM-SMRAM
    0000000000000000-ffffffffffffffff (prio 0, i/o): mem-container-smram
      0000000000000000-00000000ffffffff (prio 10, i/o): smram
        00000000000a0000-00000000000bffff (prio 0, i/o): alias smram-low @pc.ram
        000000007f000000-000000007fffffff (prio 0, i/o): alias tseg-window 
@pc.ram
      0000000000000000-ffffffffffffffff (prio 0, i/o): alias mem-smram @system

This mismatch in memory regions breaks CPR-transfer migration with VFIO:
the DMA mappings sent via VFIO_IOMMU_MAP_DMA on the destination must
match the source. Otherwise, the destination QEMU aborts:

    qemu: vfio_container_dma_map(0x..., 0x0, 0xa0000, 0x....) = -22 (Invalid 
argument)
    qemu: hardware error: vfio: DMA mapping failed, unable to continue

According to mch_update_smbase_smram() the valid combinations of
->config and ->wmask for MCH_HOST_BRIDGE_F_SMBASE are:

  1) ->config[] == 0x0 && ->wmask[] == 0xff
  2) ->config[] == MCH_HOST_BRIDGE_F_SMBASE_IN_RAM &&
          ->wmask[] == MCH_HOST_BRIDGE_F_SMBASE_LCK
  3) ->config[] == MCH_HOST_BRIDGE_F_SMBASE_LCK &&
          ->wmask[] == 0

Add mch_smbase_smram_post_load() to restore ->wmask of
MCH_HOST_BRIDGE_F_SMBASE based on ->config[] value, ensuring that the
follow-up call to mch_update_smbase_smram() correctly reinstates the
smbase-window region if it was enabled on the source.

Cc: [email protected]
Fixes: f404220e279c ("q35: implement 128K SMRAM at default SMBASE address")
Signed-off-by: Andrey Ryabinin <[email protected]>
---
 hw/pci-host/q35.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c
index a708758d36..2466a1aded 100644
--- a/hw/pci-host/q35.c
+++ b/hw/pci-host/q35.c
@@ -517,9 +517,27 @@ static void mch_update(MCHPCIState *mch)
                      IO_APIC_DEFAULT_ADDRESS - 1);
 }
 
+static void mch_smbase_smram_post_load(MCHPCIState *mch)
+{
+    PCIDevice *pd = PCI_DEVICE(mch);
+    uint8_t *reg = pd->config + MCH_HOST_BRIDGE_F_SMBASE;
+
+    if (!mch->has_smram_at_smbase) {
+        return;
+    }
+
+    if (*reg == MCH_HOST_BRIDGE_F_SMBASE_IN_RAM) {
+        pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] =
+            MCH_HOST_BRIDGE_F_SMBASE_LCK;
+    } else if (*reg == MCH_HOST_BRIDGE_F_SMBASE_LCK) {
+        pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] = 0;
+    }
+}
 static int mch_post_load(void *opaque, int version_id)
 {
     MCHPCIState *mch = opaque;
+
+    mch_smbase_smram_post_load(mch);
     mch_update(mch);
     return 0;
 }
-- 
2.51.2


Reply via email to