Test that the default behavior of KVM is to ignore userspace MSR writes and conditionally expose the "load IA32_PERF_GLOBAL_CTRL" bits in the VMX control MSRs if the guest CPUID exposes a supporting vPMU. Additionally, test that when the corresponding quirk is disabled, userspace can still clear these bits regardless of what is exposed in CPUID. Signed-off-by: Oliver Upton <oupton@xxxxxxxxxx> --- tools/testing/selftests/kvm/.gitignore | 1 + tools/testing/selftests/kvm/Makefile | 1 + .../kvm/x86_64/vmx_control_msrs_test.c | 160 ++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 tools/testing/selftests/kvm/x86_64/vmx_control_msrs_test.c diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore index 052ddfe4b23a..38edeace1432 100644 --- a/tools/testing/selftests/kvm/.gitignore +++ b/tools/testing/selftests/kvm/.gitignore @@ -38,6 +38,7 @@ /x86_64/userspace_msr_exit_test /x86_64/vmx_apic_access_test /x86_64/vmx_close_while_nested_test +/x86_64/vmx_control_msrs_test /x86_64/vmx_dirty_log_test /x86_64/vmx_exception_with_invalid_guest_state /x86_64/vmx_invalid_nested_guest_state diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index f7fa5655e535..a1f0c5885b6d 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -70,6 +70,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/userspace_io_test TEST_GEN_PROGS_x86_64 += x86_64/userspace_msr_exit_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_apic_access_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test +TEST_GEN_PROGS_x86_64 += x86_64/vmx_control_msrs_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_dirty_log_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_exception_with_invalid_guest_state TEST_GEN_PROGS_x86_64 += x86_64/vmx_invalid_nested_guest_state diff --git a/tools/testing/selftests/kvm/x86_64/vmx_control_msrs_test.c b/tools/testing/selftests/kvm/x86_64/vmx_control_msrs_test.c new file mode 100644 index 000000000000..4ab780483e15 --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/vmx_control_msrs_test.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * VMX control MSR test + * + * Copyright (C) 2022 Google LLC. + * + * Tests for KVM ownership of bits in the VMX entry/exit control MSRs. Checks + * that KVM will set owned bits where appropriate, and will not if + * KVM_X86_QUIRK_TWEAK_VMX_CTRL_MSRS is disabled. + */ + +#include "kvm_util.h" +#include "vmx.h" + +#define VCPU_ID 0 + +static void get_vmx_control_msr(struct kvm_vm *vm, uint32_t msr_index, + uint32_t *low, uint32_t *high) +{ + uint64_t val; + + val = vcpu_get_msr(vm, VCPU_ID, msr_index); + *low = val; + *high = val >> 32; +} + +static void set_vmx_control_msr(struct kvm_vm *vm, uint32_t msr_index, + uint32_t low, uint32_t high) +{ + uint64_t val = (((uint64_t) high) << 32) | low; + + vcpu_set_msr(vm, VCPU_ID, msr_index, val); +} + +static void test_vmx_control_msr(struct kvm_vm *vm, uint32_t msr_index, uint32_t set, + uint32_t clear, uint32_t exp_set, uint32_t exp_clear) +{ + uint32_t low, high; + + get_vmx_control_msr(vm, msr_index, &low, &high); + + high &= ~clear; + high |= set; + + set_vmx_control_msr(vm, msr_index, low, high); + + get_vmx_control_msr(vm, msr_index, &low, &high); + ASSERT_EQ(high & exp_set, exp_set); + ASSERT_EQ(~high & exp_clear, exp_clear); +} + +static void clear_performance_monitoring_leaf(struct kvm_cpuid2 *cpuid) +{ + struct kvm_cpuid_entry2 ent = {0}; + + ent.function = 0xa; + TEST_ASSERT(set_cpuid(cpuid, &ent), + "failed to clear Architectual Performance Monitoring leaf (0xA)"); +} + +static void load_perf_global_ctrl_test(struct kvm_vm *vm) +{ + uint32_t entry_low, entry_high, exit_low, exit_high; + struct kvm_enable_cap cap = {0}; + struct kvm_cpuid2 *cpuid; + + get_vmx_control_msr(vm, MSR_IA32_VMX_TRUE_ENTRY_CTLS, &entry_low, &entry_high); + get_vmx_control_msr(vm, MSR_IA32_VMX_TRUE_EXIT_CTLS, &exit_low, &exit_high); + + if (!(entry_high & VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL) || + !(exit_high & VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL)) { + print_skip("\"load IA32_PERF_GLOBAL_CTRL\" VM-{Entry,Exit} controls not supported"); + return; + } + + /* + * Test that KVM will set these bits regardless of userspace if the + * guest CPUID exposes a supporting vPMU. + */ + test_vmx_control_msr(vm, MSR_IA32_VMX_TRUE_ENTRY_CTLS, + 0, /* set */ + VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL, /* clear */ + VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL, /* exp_set */ + 0); /* exp_clear */ + test_vmx_control_msr(vm, MSR_IA32_VMX_TRUE_EXIT_CTLS, + 0, /* set */ + VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL, /* clear */ + VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL, /* exp_set */ + 0); /* exp_clear */ + + /* + * Hide vPMU in CPUID + */ + cpuid = _kvm_get_supported_cpuid(); + clear_performance_monitoring_leaf(cpuid); + vcpu_set_cpuid(vm, VCPU_ID, cpuid); + free(cpuid); + + /* + * Test that KVM will clear these bits if guest CPUID does not expose a + * supporting vPMU. + */ + test_vmx_control_msr(vm, MSR_IA32_VMX_TRUE_ENTRY_CTLS, + 0, /* set */ + 0, /* clear */ + 0, /* exp_set */ + VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL); /* exp_clear */ + test_vmx_control_msr(vm, MSR_IA32_VMX_TRUE_EXIT_CTLS, + 0, /* set */ + 0, /* clear */ + 0, /* exp_set */ + VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL); /* exp_clear */ + + /* + * Re-enable vPMU in CPUID + */ + vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); + + /* + * Disable the quirk, giving userspace control of the VMX capability + * MSRs. + */ + cap.cap = KVM_CAP_DISABLE_QUIRKS2; + cap.args[0] = KVM_X86_QUIRK_TWEAK_VMX_CTRL_MSRS; + vm_enable_cap(vm, &cap); + + /* + * Test that userspace can clear these bits, even if it exposes a vPMU + * that supports IA32_PERF_GLOBAL_CTRL. + */ + test_vmx_control_msr(vm, MSR_IA32_VMX_TRUE_ENTRY_CTLS, + 0, /* set */ + VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL, /* clear */ + 0, /* exp_set */ + VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL); /* exp_clear */ + test_vmx_control_msr(vm, MSR_IA32_VMX_TRUE_EXIT_CTLS, + 0, /* set */ + VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL, /* clear */ + 0, /* exp_set */ + VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL); /* exp_clear */ +} + +int main(void) +{ + struct kvm_vm *vm; + + if (!kvm_check_cap(KVM_CAP_DISABLE_QUIRKS2)) { + print_skip("KVM_CAP_DISABLE_QUIRKS2 not supported"); + exit(KSFT_SKIP); + } + + nested_vmx_check_supported(); + + /* No need to run a guest for these tests */ + vm = vm_create_default(VCPU_ID, 0, NULL); + + load_perf_global_ctrl_test(vm); + + kvm_vm_free(vm); +} -- 2.35.1.574.g5d30c73bfb-goog