QMP query-dump-guest-memory-capability reports win-dmp as available for
any x86 VM, and dump-guest-memory accepts the win-dmp format
unconditionally. Both are wrong: win-dmp only works when the guest has
published a Windows dump header through vmcoreinfo.

The guest registers that note with the vmcoreinfo device (its physical
address and size), so win_dump_available() can read it back directly and
validate the note size and the Windows dump header signature. This needs
no other guest state, so it does not stop the vCPUs.

The capability query reads the note on the main thread with the BQL held
and has no migration guard of its own, so it is skipped while a migration
destination is still receiving guest RAM: there the read would deadlock
against the postcopy load (which needs the BQL) or, in precopy, see
incomplete pages.

Based on the original work of Nikolai Barybin.

Signed-off-by: Denis V. Lunev <[email protected]>
---
 dump/win_dump-x86.c | 49 ++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 44 insertions(+), 5 deletions(-)

diff --git a/dump/win_dump-x86.c b/dump/win_dump-x86.c
index d893dea7f1..6a8b2b71a7 100644
--- a/dump/win_dump-x86.c
+++ b/dump/win_dump-x86.c
@@ -17,11 +17,10 @@
 #include "qemu/win_dump_defs.h"
 #include "win_dump.h"
 #include "cpu.h"
-
-bool win_dump_available(Error **errp)
-{
-    return true;
-}
+#include "qemu/bswap.h"
+#include "hw/misc/vmcoreinfo.h"
+#include "migration/misc.h"
+#include "standard-headers/linux/qemu_fw_cfg.h"
 
 static size_t win_dump_ptr_size(bool x64)
 {
@@ -403,6 +402,46 @@ static void restore_context(WinDumpHeader *h, bool x64,
     }
 }
 
+bool win_dump_available(Error **errp)
+{
+    VMCoreInfoState *vmci = vmcoreinfo_find();
+    g_autofree uint8_t *note = NULL;
+    Error *local_err = NULL;
+    WinDumpHeader *h;
+    uint32_t size;
+    bool x64 = true;
+
+    if (migration_guest_ram_loading()) {
+        error_setg(errp, "win-dump: not available during migration");
+        return false;
+    }
+
+    if (!vmci || !vmci->has_vmcoreinfo ||
+        le16_to_cpu(vmci->vmcoreinfo.guest_format) !=
+            FW_CFG_VMCOREINFO_FORMAT_ELF) {
+        error_setg(errp, "win-dump: no vmcoreinfo note from the guest");
+        return false;
+    }
+
+    size = le32_to_cpu(vmci->vmcoreinfo.size);
+    if (size != VMCOREINFO_WIN_DUMP_NOTE_SIZE32 &&
+        size != VMCOREINFO_WIN_DUMP_NOTE_SIZE64) {
+        error_setg(errp, "win-dump: invalid vmcoreinfo note size");
+        return false;
+    }
+
+    note = g_malloc(size);
+    cpu_physical_memory_read(le64_to_cpu(vmci->vmcoreinfo.paddr), note, size);
+
+    h = (void *)(note + VMCOREINFO_ELF_NOTE_HDR_SIZE);
+    if (!check_header(h, &x64, &local_err)) {
+        error_propagate(errp, local_err);
+        return false;
+    }
+
+    return true;
+}
+
 void create_win_dump(DumpState *s, Error **errp)
 {
     WinDumpHeader *h = (void *)(s->guest_note + VMCOREINFO_ELF_NOTE_HDR_SIZE);
-- 
2.53.0


Reply via email to