Besides the existing DR7 test on debug controls, introduce a similar separate test for IA32_DEBUGCTLMSR. Previously, the IA32_DEBUGCTLMSR was combined with the DR7 test. However, it attempted to access the LBR and BTF bits in the MSR which can be invalid. Although KVM will exempt these two bits from validity check, they will be cleared and resulted in the unexpected MSR value. In this new test, access a valid bit (DEBUGCTLMSR_BUS_LOCK_DETECT, bit 2) based on the enumration of Bus Lock Detect. Signed-off-by: Chenyi Qiang --- x86/vmx_tests.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c index 1832bda3..9a2e598f 100644 --- a/x86/vmx_tests.c +++ b/x86/vmx_tests.c @@ -1944,6 +1944,92 @@ static int dbgctls_dr7_exit_handler(union exit_reason exit_reason) return VMX_TEST_VMEXIT; } +static int dbgctls_msr_init(struct vmcs *vmcs) +{ + /* Check for DEBUGCTLMSR_BUS_LOCK_DETECT(bit 2) in IA32_DEBUGCTLMSR */ + if (!(cpuid(7).c & (1 << 24))) { + report_skip("%s : \"Bus Lock Detect\" not supported", __func__); + return VMX_TEST_VMSKIP; + } + + msr_bmp_init(); + wrmsr(MSR_IA32_DEBUGCTLMSR, 0x0); + vmcs_write(GUEST_DEBUGCTL, 0x4); + + vmcs_write(ENT_CONTROLS, vmcs_read(ENT_CONTROLS) | ENT_LOAD_DBGCTLS); + vmcs_write(EXI_CONTROLS, vmcs_read(EXI_CONTROLS) | EXI_SAVE_DBGCTLS); + + return VMX_TEST_START; +} + +static void dbgctls_msr_main(void) +{ + u64 debugctl; + + debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR); + report(debugctl == 0x4, "DEBUGCTLMSR: Load debug controls"); + + vmx_set_test_stage(0); + vmcall(); + report(vmx_get_test_stage() == 1, "DEBUGCTLMSR: Save debug controls"); + + if (ctrl_enter_rev.set & ENT_LOAD_DBGCTLS || + ctrl_exit_rev.set & EXI_SAVE_DBGCTLS) { + printf("\tDebug controls are always loaded/saved\n"); + return; + } + vmx_set_test_stage(2); + vmcall(); + + debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR); + report(debugctl == 0x0, + "DEBUGCTLMSR: Guest=host debug controls"); + + vmx_set_test_stage(3); + vmcall(); + report(vmx_get_test_stage() == 4, "DEBUGCTLMSR: Don't save debug controls"); +} + +static int dbgctls_msr_exit_handler(union exit_reason exit_reason) +{ + u32 insn_len = vmcs_read(EXI_INST_LEN); + u64 guest_rip = vmcs_read(GUEST_RIP); + u64 debugctl; + + debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR); + + switch (exit_reason.basic) { + case VMX_VMCALL: + switch (vmx_get_test_stage()) { + case 0: + if (debugctl == 0 && + vmcs_read(GUEST_DEBUGCTL) == 0x4) + vmx_inc_test_stage(); + break; + case 2: + wrmsr(MSR_IA32_DEBUGCTLMSR, 0x0); + vmcs_write(GUEST_DEBUGCTL, 0x4); + + vmcs_write(ENT_CONTROLS, + vmcs_read(ENT_CONTROLS) & ~ENT_LOAD_DBGCTLS); + vmcs_write(EXI_CONTROLS, + vmcs_read(EXI_CONTROLS) & ~EXI_SAVE_DBGCTLS); + break; + case 3: + if (debugctl == 0 && + vmcs_read(GUEST_DEBUGCTL) == 0x4) + vmx_inc_test_stage(); + break; + } + vmcs_write(GUEST_RIP, guest_rip + insn_len); + return VMX_TEST_RESUME; + default: + report_fail("Unknown exit reason, %d", exit_reason.full); + print_vmexit_info(exit_reason); + } + return VMX_TEST_VMEXIT; +} + struct vmx_msr_entry { u32 index; u32 reserved; @@ -11386,6 +11472,8 @@ struct vmx_test vmx_tests[] = { nmi_hlt_exit_handler, NULL, {0} }, { "debug controls dr7", dbgctls_dr7_init, dbgctls_dr7_main, dbgctls_dr7_exit_handler, NULL, {0} }, + { "debug controls msr", dbgctls_msr_init, dbgctls_msr_main, dbgctls_msr_exit_handler, + NULL, {0} }, { "MSR switch", msr_switch_init, msr_switch_main, msr_switch_exit_handler, NULL, {0}, msr_switch_entry_failure }, { "vmmcall", vmmcall_init, vmmcall_main, vmmcall_exit_handler, NULL, {0} }, -- 2.43.5