When a vCPU is running an L2 guest, "info registers" shows only
L2's current state and not the L1 state that will be restored on a
VM-exit. Likewise, while L1 is current, it does not show the L2
guest state configured in vmcs12.

The vmcs12 blob returned by KVM_GET_NESTED_STATE contains both the
guest-state area configured for L2 and the host-state area restored
for L1. Parse the relevant fields from the migration-stable vmcs12
layout and append the other level's state to the existing register
dump.

Use KVM_STATE_NESTED_GUEST_MODE to identify the current level, and
guard the parser with the VMCS12 revision. This is limited to VMX;
SVM nested state is left unchanged.

Signed-off-by: Zhengrong Li <[email protected]>
---

Changes in v2:
- Use KVM_STATE_NESTED_GUEST_MODE instead of HF_GUEST_MASK to select
  the current level.
- Label the existing register dump as current state and VMCS12 values
  as additional state for the other level.
- Test both L1-current and L2-current paths with a nested Windows guest.

v1: 
https://lore.kernel.org/qemu-devel/[email protected]/

 target/i386/cpu-dump.c | 255 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 255 insertions(+)

diff --git a/target/i386/cpu-dump.c b/target/i386/cpu-dump.c
index 9e290c0..178d1dc 100644
--- a/target/i386/cpu-dump.c
+++ b/target/i386/cpu-dump.c
@@ -23,6 +23,9 @@
 #ifndef CONFIG_USER_ONLY
 #include "hw/i386/apic_internal.h"
 #endif
+#ifdef CONFIG_KVM
+#include <linux/kvm.h>
+#endif
 
 /***********************************************************/
 /* x86 debug */
@@ -343,6 +346,253 @@ void x86_cpu_dump_local_apic_state(CPUState *cs, int 
flags)
 #define DUMP_CODE_BYTES_TOTAL    50
 #define DUMP_CODE_BYTES_BACKWARD 20
 
+#ifdef CONFIG_KVM
+/*
+ * Byte offsets into the vmcs12 blob returned by KVM_GET_NESTED_STATE.
+ * Taken from struct vmcs12 in arch/x86/kvm/vmx/vmcs12.h (kernel v6.16).
+ * The layout is locked by live-migration compatibility and compile-time
+ * CHECK_OFFSET assertions in the kernel, so existing offsets are stable.
+ * Only new fields may be appended at the end; existing fields cannot be
+ * moved, deleted, or have their type changed.
+ */
+#define VMCS12_REVISION                  0x11e57ed0
+#define VMCS12_GUEST_IA32_PAT            192
+#define VMCS12_GUEST_IA32_EFER           200
+#define VMCS12_HOST_IA32_PAT             256
+#define VMCS12_HOST_IA32_EFER            264
+#define VMCS12_GUEST_CR0                 424
+#define VMCS12_GUEST_CR3                 432
+#define VMCS12_GUEST_CR4                 440
+#define VMCS12_GUEST_ES_BASE             448
+#define VMCS12_GUEST_CS_BASE             456
+#define VMCS12_GUEST_SS_BASE             464
+#define VMCS12_GUEST_DS_BASE             472
+#define VMCS12_GUEST_FS_BASE             480
+#define VMCS12_GUEST_GS_BASE             488
+#define VMCS12_GUEST_LDTR_BASE           496
+#define VMCS12_GUEST_TR_BASE             504
+#define VMCS12_GUEST_GDTR_BASE           512
+#define VMCS12_GUEST_IDTR_BASE           520
+#define VMCS12_GUEST_DR7                 528
+#define VMCS12_GUEST_RSP                 536
+#define VMCS12_GUEST_RIP                 544
+#define VMCS12_GUEST_RFLAGS              552
+#define VMCS12_GUEST_SYSENTER_ESP        568
+#define VMCS12_GUEST_SYSENTER_EIP        576
+#define VMCS12_HOST_CR0                  584
+#define VMCS12_HOST_CR3                  592
+#define VMCS12_HOST_CR4                  600
+#define VMCS12_HOST_FS_BASE              608
+#define VMCS12_HOST_GS_BASE              616
+#define VMCS12_HOST_TR_BASE              624
+#define VMCS12_HOST_GDTR_BASE            632
+#define VMCS12_HOST_IDTR_BASE            640
+#define VMCS12_HOST_IA32_SYSENTER_ESP    648
+#define VMCS12_HOST_IA32_SYSENTER_EIP    656
+#define VMCS12_HOST_RSP                  664
+#define VMCS12_HOST_RIP                  672
+#define VMCS12_GUEST_ES_LIMIT            840
+#define VMCS12_GUEST_CS_LIMIT            844
+#define VMCS12_GUEST_SS_LIMIT            848
+#define VMCS12_GUEST_DS_LIMIT            852
+#define VMCS12_GUEST_FS_LIMIT            856
+#define VMCS12_GUEST_GS_LIMIT            860
+#define VMCS12_GUEST_LDTR_LIMIT          864
+#define VMCS12_GUEST_TR_LIMIT            868
+#define VMCS12_GUEST_GDTR_LIMIT          872
+#define VMCS12_GUEST_IDTR_LIMIT          876
+#define VMCS12_GUEST_ES_AR_BYTES         880
+#define VMCS12_GUEST_CS_AR_BYTES         884
+#define VMCS12_GUEST_SS_AR_BYTES         888
+#define VMCS12_GUEST_DS_AR_BYTES         892
+#define VMCS12_GUEST_FS_AR_BYTES         896
+#define VMCS12_GUEST_GS_AR_BYTES         900
+#define VMCS12_GUEST_LDTR_AR_BYTES       904
+#define VMCS12_GUEST_TR_AR_BYTES         908
+#define VMCS12_GUEST_SYSENTER_CS         920
+#define VMCS12_HOST_IA32_SYSENTER_CS     924
+#define VMCS12_GUEST_ES_SELECTOR         964
+#define VMCS12_GUEST_CS_SELECTOR         966
+#define VMCS12_GUEST_SS_SELECTOR         968
+#define VMCS12_GUEST_DS_SELECTOR         970
+#define VMCS12_GUEST_FS_SELECTOR         972
+#define VMCS12_GUEST_GS_SELECTOR         974
+#define VMCS12_GUEST_LDTR_SELECTOR       976
+#define VMCS12_GUEST_TR_SELECTOR         978
+#define VMCS12_HOST_ES_SELECTOR          982
+#define VMCS12_HOST_CS_SELECTOR          984
+#define VMCS12_HOST_SS_SELECTOR          986
+#define VMCS12_HOST_DS_SELECTOR          988
+#define VMCS12_HOST_FS_SELECTOR          990
+#define VMCS12_HOST_GS_SELECTOR          992
+#define VMCS12_HOST_TR_SELECTOR          994
+
+static inline uint64_t vmcs12_read64(const uint8_t *vmcs12, size_t off)
+{
+    uint64_t val;
+    memcpy(&val, vmcs12 + off, sizeof(val));
+    return val;
+}
+
+static inline uint32_t vmcs12_read32(const uint8_t *vmcs12, size_t off)
+{
+    uint32_t val;
+    memcpy(&val, vmcs12 + off, sizeof(val));
+    return val;
+}
+
+static inline uint16_t vmcs12_read16(const uint8_t *vmcs12, size_t off)
+{
+    uint16_t val;
+    memcpy(&val, vmcs12 + off, sizeof(val));
+    return val;
+}
+
+static void dump_vmcs12_nested_state(CPUX86State *env, FILE *f,
+                                     const char *seg_name[6])
+{
+    const uint8_t *vmcs12;
+    uint64_t base[8];
+    uint32_t limit[8], ar[8];
+    uint16_t sel[8];
+    int i;
+
+    if (!env->nested_state ||
+        env->nested_state->format != KVM_STATE_NESTED_FORMAT_VMX) {
+        return;
+    }
+
+    vmcs12 = env->nested_state->data.vmx[0].vmcs12;
+
+    /* Sanity check: vmcs12 revision must match expected layout */
+    if (vmcs12_read32(vmcs12, 0) != VMCS12_REVISION) {
+        return;
+    }
+
+    qemu_fprintf(f, "\nNested VMX context:\n");
+    if (env->nested_state->flags & KVM_STATE_NESTED_GUEST_MODE) {
+        /*
+         * The registers above are L2's current state. Show the
+         * additional L1 state that VM-exit will restore.
+         */
+        qemu_fprintf(f, "Current state: L2 (registers above)\n");
+        qemu_fprintf(f, "Additional L1 state (VMCS12 host-state area):\n");
+        qemu_fprintf(f, "RIP=%016" PRIx64 " RSP=%016" PRIx64 "\n",
+                     vmcs12_read64(vmcs12, VMCS12_HOST_RIP),
+                     vmcs12_read64(vmcs12, VMCS12_HOST_RSP));
+        qemu_fprintf(f, "CR0=%016" PRIx64 " CR3=%016" PRIx64
+                     " CR4=%016" PRIx64 "\n",
+                     vmcs12_read64(vmcs12, VMCS12_HOST_CR0),
+                     vmcs12_read64(vmcs12, VMCS12_HOST_CR3),
+                     vmcs12_read64(vmcs12, VMCS12_HOST_CR4));
+        qemu_fprintf(f, "EFER=%016" PRIx64 " PAT=%016" PRIx64 "\n",
+                     vmcs12_read64(vmcs12, VMCS12_HOST_IA32_EFER),
+                     vmcs12_read64(vmcs12, VMCS12_HOST_IA32_PAT));
+
+        sel[0] = vmcs12_read16(vmcs12, VMCS12_HOST_ES_SELECTOR);
+        sel[1] = vmcs12_read16(vmcs12, VMCS12_HOST_CS_SELECTOR);
+        sel[2] = vmcs12_read16(vmcs12, VMCS12_HOST_SS_SELECTOR);
+        sel[3] = vmcs12_read16(vmcs12, VMCS12_HOST_DS_SELECTOR);
+        sel[4] = vmcs12_read16(vmcs12, VMCS12_HOST_FS_SELECTOR);
+        sel[5] = vmcs12_read16(vmcs12, VMCS12_HOST_GS_SELECTOR);
+        for (i = 0; i < 6; i++) {
+            qemu_fprintf(f, "%-4s sel=%04x\n", seg_name[i], sel[i]);
+        }
+        qemu_fprintf(f, "TR   sel=%04x base=%016" PRIx64 "\n",
+                     vmcs12_read16(vmcs12, VMCS12_HOST_TR_SELECTOR),
+                     vmcs12_read64(vmcs12, VMCS12_HOST_TR_BASE));
+        qemu_fprintf(f, "FS base=%016" PRIx64 " GS base=%016" PRIx64 "\n",
+                     vmcs12_read64(vmcs12, VMCS12_HOST_FS_BASE),
+                     vmcs12_read64(vmcs12, VMCS12_HOST_GS_BASE));
+        qemu_fprintf(f, "GDT=     %016" PRIx64 "\n",
+                     vmcs12_read64(vmcs12, VMCS12_HOST_GDTR_BASE));
+        qemu_fprintf(f, "IDT=     %016" PRIx64 "\n",
+                     vmcs12_read64(vmcs12, VMCS12_HOST_IDTR_BASE));
+        qemu_fprintf(f, "SYSENTER cs=%08x esp=%016" PRIx64
+                     " eip=%016" PRIx64 "\n",
+                     vmcs12_read32(vmcs12, VMCS12_HOST_IA32_SYSENTER_CS),
+                     vmcs12_read64(vmcs12, VMCS12_HOST_IA32_SYSENTER_ESP),
+                     vmcs12_read64(vmcs12, VMCS12_HOST_IA32_SYSENTER_EIP));
+    } else {
+        /*
+         * The registers above are L1's current state. Show the
+         * additional L2 state configured in the guest-state area.
+         */
+        qemu_fprintf(f, "Current state: L1 (registers above)\n");
+        qemu_fprintf(f, "Additional L2 state (VMCS12 guest-state area):\n");
+        qemu_fprintf(f, "RIP=%016" PRIx64 " RSP=%016" PRIx64
+                     " RFL=%016" PRIx64 "\n",
+                     vmcs12_read64(vmcs12, VMCS12_GUEST_RIP),
+                     vmcs12_read64(vmcs12, VMCS12_GUEST_RSP),
+                     vmcs12_read64(vmcs12, VMCS12_GUEST_RFLAGS));
+        qemu_fprintf(f, "CR0=%016" PRIx64 " CR3=%016" PRIx64
+                     " CR4=%016" PRIx64 "\n",
+                     vmcs12_read64(vmcs12, VMCS12_GUEST_CR0),
+                     vmcs12_read64(vmcs12, VMCS12_GUEST_CR3),
+                     vmcs12_read64(vmcs12, VMCS12_GUEST_CR4));
+        qemu_fprintf(f, "DR7=%016" PRIx64 " EFER=%016" PRIx64
+                     " PAT=%016" PRIx64 "\n",
+                     vmcs12_read64(vmcs12, VMCS12_GUEST_DR7),
+                     vmcs12_read64(vmcs12, VMCS12_GUEST_IA32_EFER),
+                     vmcs12_read64(vmcs12, VMCS12_GUEST_IA32_PAT));
+
+        sel[0] = vmcs12_read16(vmcs12, VMCS12_GUEST_ES_SELECTOR);
+        sel[1] = vmcs12_read16(vmcs12, VMCS12_GUEST_CS_SELECTOR);
+        sel[2] = vmcs12_read16(vmcs12, VMCS12_GUEST_SS_SELECTOR);
+        sel[3] = vmcs12_read16(vmcs12, VMCS12_GUEST_DS_SELECTOR);
+        sel[4] = vmcs12_read16(vmcs12, VMCS12_GUEST_FS_SELECTOR);
+        sel[5] = vmcs12_read16(vmcs12, VMCS12_GUEST_GS_SELECTOR);
+        base[0] = vmcs12_read64(vmcs12, VMCS12_GUEST_ES_BASE);
+        base[1] = vmcs12_read64(vmcs12, VMCS12_GUEST_CS_BASE);
+        base[2] = vmcs12_read64(vmcs12, VMCS12_GUEST_SS_BASE);
+        base[3] = vmcs12_read64(vmcs12, VMCS12_GUEST_DS_BASE);
+        base[4] = vmcs12_read64(vmcs12, VMCS12_GUEST_FS_BASE);
+        base[5] = vmcs12_read64(vmcs12, VMCS12_GUEST_GS_BASE);
+        limit[0] = vmcs12_read32(vmcs12, VMCS12_GUEST_ES_LIMIT);
+        limit[1] = vmcs12_read32(vmcs12, VMCS12_GUEST_CS_LIMIT);
+        limit[2] = vmcs12_read32(vmcs12, VMCS12_GUEST_SS_LIMIT);
+        limit[3] = vmcs12_read32(vmcs12, VMCS12_GUEST_DS_LIMIT);
+        limit[4] = vmcs12_read32(vmcs12, VMCS12_GUEST_FS_LIMIT);
+        limit[5] = vmcs12_read32(vmcs12, VMCS12_GUEST_GS_LIMIT);
+        ar[0] = vmcs12_read32(vmcs12, VMCS12_GUEST_ES_AR_BYTES);
+        ar[1] = vmcs12_read32(vmcs12, VMCS12_GUEST_CS_AR_BYTES);
+        ar[2] = vmcs12_read32(vmcs12, VMCS12_GUEST_SS_AR_BYTES);
+        ar[3] = vmcs12_read32(vmcs12, VMCS12_GUEST_DS_AR_BYTES);
+        ar[4] = vmcs12_read32(vmcs12, VMCS12_GUEST_FS_AR_BYTES);
+        ar[5] = vmcs12_read32(vmcs12, VMCS12_GUEST_GS_AR_BYTES);
+        for (i = 0; i < 6; i++) {
+            qemu_fprintf(f, "%-4s sel=%04x base=%016" PRIx64
+                         " limit=%08x ar=%08x\n",
+                         seg_name[i], sel[i], base[i], limit[i], ar[i]);
+        }
+        qemu_fprintf(f, "LDT  sel=%04x base=%016" PRIx64
+                     " limit=%08x ar=%08x\n",
+                     vmcs12_read16(vmcs12, VMCS12_GUEST_LDTR_SELECTOR),
+                     vmcs12_read64(vmcs12, VMCS12_GUEST_LDTR_BASE),
+                     vmcs12_read32(vmcs12, VMCS12_GUEST_LDTR_LIMIT),
+                     vmcs12_read32(vmcs12, VMCS12_GUEST_LDTR_AR_BYTES));
+        qemu_fprintf(f, "TR   sel=%04x base=%016" PRIx64
+                     " limit=%08x ar=%08x\n",
+                     vmcs12_read16(vmcs12, VMCS12_GUEST_TR_SELECTOR),
+                     vmcs12_read64(vmcs12, VMCS12_GUEST_TR_BASE),
+                     vmcs12_read32(vmcs12, VMCS12_GUEST_TR_LIMIT),
+                     vmcs12_read32(vmcs12, VMCS12_GUEST_TR_AR_BYTES));
+
+        qemu_fprintf(f, "GDT=     %016" PRIx64 " %08x\n",
+                     vmcs12_read64(vmcs12, VMCS12_GUEST_GDTR_BASE),
+                     vmcs12_read32(vmcs12, VMCS12_GUEST_GDTR_LIMIT));
+        qemu_fprintf(f, "IDT=     %016" PRIx64 " %08x\n",
+                     vmcs12_read64(vmcs12, VMCS12_GUEST_IDTR_BASE),
+                     vmcs12_read32(vmcs12, VMCS12_GUEST_IDTR_LIMIT));
+        qemu_fprintf(f, "SYSENTER cs=%08x esp=%016" PRIx64
+                     " eip=%016" PRIx64 "\n",
+                     vmcs12_read32(vmcs12, VMCS12_GUEST_SYSENTER_CS),
+                     vmcs12_read64(vmcs12, VMCS12_GUEST_SYSENTER_ESP),
+                     vmcs12_read64(vmcs12, VMCS12_GUEST_SYSENTER_EIP));
+    }
+}
+#endif /* CONFIG_KVM */
+
 void x86_cpu_dump_state(CPUState *cs, FILE *f, int flags)
 {
     X86CPU *cpu = X86_CPU(cs);
@@ -595,4 +845,9 @@ void x86_cpu_dump_state(CPUState *cs, FILE *f, int flags)
         }
         qemu_fprintf(f, "\n");
     }
+
+#ifdef CONFIG_KVM
+    /* Show nested peer state from vmcs12 when applicable */
+    dump_vmcs12_nested_state(env, f, seg_name);
+#endif
 }
-- 
2.43.7


Reply via email to