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
