Tests to verify that KVM performs the correct checks on Host/Guest state at VM-entry, as described in SDM 26.3.1.1 "Checks on Guest Control Registers, Debug Registers, and MSRs" and SDM 26.2.2 "Checks on Host Control Registers and MSRs". Test that KVM does the following: If the "load IA32_PERF_GLOBAL_CTRL" VM-entry control is 1, the reserved bits of the IA32_PERF_GLOBAL_CTRL MSR must be 0 in the GUEST_IA32_PERF_GLOBAL_CTRL VMCS field. Otherwise, the VM-entry should fail with an exit reason of "VM-entry failure due to invalid guest state" (33). On a successful VM-entry, the correct value should be observed when the nested VM performs an RDMSR on IA32_PERF_GLOBAL_CTRL. If the "load IA32_PERF_GLOBAL_CTRL" VM-exit control is 1, the reserved bits of the IA32_PERF_GLOBAL_CTRL MSR must be 0 in the HOST_IA32_PERF_GLOBAL_CTRL VMCS field. Otherwise, the VM-entry should fail with a VM-instruction error of "VM entry with invalid host-state field(s)" (8). On a successful VM-exit, the correct value should be observed when L1 performs an RDMSR on IA32_PERF_GLOBAL_CTRL. Suggested-by: Jim Mattson <jmattson@xxxxxxxxxx> Co-developed-by: Krish Sadhukhan <krish.sadhukhan@xxxxxxxxxx> Signed-off-by: Krish Sadhukhan <krish.sadhukhan@xxxxxxxxxx> Signed-off-by: Oliver Upton <oupton@xxxxxxxxxx> Reviewed-by: Krish Sadhukhan <krish.sadhukhan@xxxxxxxxxx> --- x86/vmx_tests.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/x86/vmx_tests.c b/x86/vmx_tests.c index 95c1c01d2966..0d5e463f9887 100644 --- a/x86/vmx_tests.c +++ b/x86/vmx_tests.c @@ -7032,6 +7032,164 @@ static void test_load_host_pat(void) test_pat(HOST_PAT, "HOST_PAT", EXI_CONTROLS, EXI_LOAD_PAT); } +union cpuidA_eax { + struct { + unsigned int version_id:8; + unsigned int num_counters_gp:8; + unsigned int bit_width:8; + unsigned int mask_length:8; + } split; + unsigned int full; +}; + +union cpuidA_edx { + struct { + unsigned int num_counters_fixed:5; + unsigned int bit_width_fixed:8; + unsigned int reserved:9; + } split; + unsigned int full; +}; + +static bool valid_pgc(u64 val) +{ + struct cpuid id; + union cpuidA_eax eax; + union cpuidA_edx edx; + u64 mask; + + id = cpuid(0xA); + eax.full = id.a; + edx.full = id.d; + mask = ~(((1ull << eax.split.num_counters_gp) - 1) | + (((1ull << edx.split.num_counters_fixed) - 1) << 32)); + + return !(val & mask); +} + +static void test_pgc_vmlaunch(u32 xerror, u32 xreason, bool xfail, bool host) +{ + u32 inst_err; + u64 obs; + bool success; + struct vmx_state_area_test_data *data = &vmx_state_area_test_data; + + if (host) { + success = vmlaunch_succeeds(); + obs = rdmsr(data->msr); + if (!success) { + inst_err = vmcs_read(VMX_INST_ERROR); + report("vmlaunch failed, VMX Inst Error is %d (expected %d)", + xerror == inst_err, inst_err, xerror); + } else { + report("Host state is 0x%lx (expected 0x%lx)", + !data->enabled || data->exp == obs, obs, data->exp); + report("vmlaunch succeeded", success != xfail); + } + } else { + if (xfail) { + enter_guest_with_invalid_guest_state(); + } else { + enter_guest(); + } + report_guest_state_test("load GUEST_PERF_GLOBAL_CTRL", + xreason, GUEST_PERF_GLOBAL_CTRL, + "GUEST_PERF_GLOBAL_CTRL"); + } +} + +/* + * test_load_perf_global_ctrl is a generic function for testing the + * "load IA32_PERF_GLOBAL_CTRL" VM-{Entry,Exit} controls. This test function + * tests the provided ctrl_val when disabled and enabled. + * + * @nr: VMCS field number corresponding to the host/guest state field + * @name: Name of the above VMCS field for printing in test report + * @ctrl_nr: VMCS field number corresponding to the VM-{Entry,Exit} control + * @ctrl_val: Bit to set on the ctrl_field + */ +static void test_perf_global_ctrl(u32 nr, const char *name, u32 ctrl_nr, + const char *ctrl_name, u64 ctrl_val) +{ + u64 ctrl_saved = vmcs_read(ctrl_nr); + u64 pgc_saved = vmcs_read(nr); + u64 i, val; + bool host = nr == HOST_PERF_GLOBAL_CTRL; + struct vmx_state_area_test_data *data = &vmx_state_area_test_data; + + data->msr = MSR_CORE_PERF_GLOBAL_CTRL; + msr_bmp_init(); + vmcs_write(ctrl_nr, ctrl_saved & ~ctrl_val); + data->enabled = false; + report_prefix_pushf("\"load IA32_PERF_GLOBAL_CTRL\"=0 on %s", + ctrl_name); + + for (i = 0; i < 64; i++) { + val = 1ull << i; + vmcs_write(nr, val); + report_prefix_pushf("%s = 0x%lx", name, val); + test_pgc_vmlaunch(0, VMX_VMCALL, false, host); + report_prefix_pop(); + } + report_prefix_pop(); + + vmcs_write(ctrl_nr, ctrl_saved | ctrl_val); + data->enabled = true; + report_prefix_pushf("\"load IA32_PERF_GLOBAL_CTRL\"=1 on %s", + ctrl_name); + for (i = 0; i < 64; i++) { + val = 1ull << i; + data->exp = val; + vmcs_write(nr, val); + report_prefix_pushf("%s = 0x%lx", name, val); + if (valid_pgc(val)) { + test_pgc_vmlaunch(0, VMX_VMCALL, false, host); + } else { + if (host) + test_pgc_vmlaunch( + VMXERR_ENTRY_INVALID_HOST_STATE_FIELD, + 0, + true, + host); + else + test_pgc_vmlaunch( + 0, + VMX_ENTRY_FAILURE | VMX_FAIL_STATE, + true, + host); + } + report_prefix_pop(); + } + + report_prefix_pop(); + vmcs_write(ctrl_nr, ctrl_saved); + vmcs_write(nr, pgc_saved); +} + +static void test_load_host_perf_global_ctrl(void) +{ + if (!(ctrl_exit_rev.clr & EXI_LOAD_PERF)) { + printf("\"load IA32_PERF_GLOBAL_CTRL\" exit control not supported\n"); + return; + } + + test_perf_global_ctrl(HOST_PERF_GLOBAL_CTRL, "HOST_PERF_GLOBAL_CTRL", + EXI_CONTROLS, "EXI_CONTROLS", EXI_LOAD_PERF); +} + + +static void test_load_guest_perf_global_ctrl(void) +{ + if (!(ctrl_enter_rev.clr & ENT_LOAD_PERF)) { + printf("\"load IA32_PERF_GLOBAL_CTRL\" entry control not supported\n"); + return; + } + + test_perf_global_ctrl(GUEST_PERF_GLOBAL_CTRL, "GUEST_PERF_GLOBAL_CTRL", + ENT_CONTROLS, "ENT_CONTROLS", ENT_LOAD_PERF); +} + + /* * test_vmcs_field - test a value for the given VMCS field * @field: VMCS field @@ -7261,6 +7419,7 @@ static void vmx_host_state_area_test(void) test_host_segment_regs(); test_host_desc_tables(); test_host_addr_size(); + test_load_host_perf_global_ctrl(); } /* @@ -7296,6 +7455,7 @@ static void vmx_guest_state_area_test(void) test_load_guest_pat(); test_guest_efer(); + test_load_guest_perf_global_ctrl(); /* * Let the guest finish execution -- 2.24.0.rc1.363.gb1bccd3e3d-goog