@@ -101,6 +101,7 @@ struct kvm_x86_cpu_feature {
#define X86_FEATURE_INVPCID KVM_X86_CPU_FEATURE(0x7, 0, EBX, 10)
#define X86_FEATURE_RTM KVM_X86_CPU_FEATURE(0x7, 0, EBX, 11)
#define X86_FEATURE_MPX KVM_X86_CPU_FEATURE(0x7, 0, EBX, 14)
+#define X86_FEATURE_RDSEED KVM_X86_CPU_FEATURE(0x7, 0, EBX, 18)
#define X86_FEATURE_SMAP KVM_X86_CPU_FEATURE(0x7, 0, EBX, 20)
#define X86_FEATURE_PCOMMIT KVM_X86_CPU_FEATURE(0x7, 0, EBX, 22)
#define X86_FEATURE_CLFLUSHOPT KVM_X86_CPU_FEATURE(0x7, 0, EBX, 23)
@@ -61,8 +61,8 @@
#define SECONDARY_EXEC_SHADOW_VMCS 0x00004000
#define SECONDARY_EXEC_RDSEED_EXITING 0x00010000
#define SECONDARY_EXEC_ENABLE_PML 0x00020000
-#define SECONDARY_EPT_VE 0x00040000
-#define SECONDARY_ENABLE_XSAV_RESTORE 0x00100000
+#define SECONDARY_EXEC_EPT_VE 0x00040000
+#define SECONDARY_EXEC_ENABLE_XSAVES 0x00100000
#define SECONDARY_EXEC_TSC_SCALING 0x02000000
#define PIN_BASED_EXT_INTR_MASK 0x00000001
@@ -12,6 +12,96 @@
#include "kvm_util.h"
#include "vmx.h"
+static void vmx_sec_exec_assert_allowed(struct kvm_vcpu *vcpu,
+ const char *name, uint64_t ctrl)
+{
+ TEST_ASSERT(vcpu_get_msr(vcpu, MSR_IA32_VMX_PROCBASED_CTLS2) & ctrl,
+ "Expected '%s' to be allowed in sec exec controls", name);
+}
+
+static void vmx_sec_exec_assert_denied(struct kvm_vcpu *vcpu,
+ const char *name, uint64_t ctrl)
+{
+ TEST_ASSERT(!(vcpu_get_msr(vcpu, MSR_IA32_VMX_PROCBASED_CTLS2) & ctrl),
+ "Expected '%s' to be denied in sec exec controls", name);
+}
+
+static void vmx_sec_exec_control_test(struct kvm_vcpu *vcpu,
+ const char *name,
+ struct kvm_x86_cpu_feature feature,
+ uint64_t ctrl, bool kvm_owned)
+{
+ /* Allowed-1 settings are in the upper 32 bits. */
+ ctrl <<= 32;
+
+ if (!this_cpu_has(feature))
+ return;
+
+ if (kvm_owned) {
+ vcpu_set_cpuid_feature(vcpu, feature);
+ vmx_sec_exec_assert_allowed(vcpu, name, ctrl);
+
+ vcpu_clear_cpuid_feature(vcpu, feature);
+ vmx_sec_exec_assert_denied(vcpu, name, ctrl);
+
+ /* Make sure KVM is actually toggling the bit. */
+ vcpu_set_cpuid_feature(vcpu, feature);
+ vmx_sec_exec_assert_allowed(vcpu, name, ctrl);
+ } else {
+ vcpu_set_msr(vcpu, MSR_IA32_VMX_PROCBASED_CTLS2,
+ vcpu_get_msr(vcpu, MSR_IA32_VMX_PROCBASED_CTLS2) | ctrl);
+ vmx_sec_exec_assert_allowed(vcpu, name, ctrl);
+
+ vcpu_set_cpuid_feature(vcpu, feature);
+ vmx_sec_exec_assert_allowed(vcpu, name, ctrl);
+
+ vcpu_clear_cpuid_feature(vcpu, feature);
+ vmx_sec_exec_assert_allowed(vcpu, name, ctrl);
+
+ vcpu_set_msr(vcpu, MSR_IA32_VMX_PROCBASED_CTLS2,
+ vcpu_get_msr(vcpu, MSR_IA32_VMX_PROCBASED_CTLS2) & ~ctrl);
+ vmx_sec_exec_assert_denied(vcpu, name, ctrl);
+
+ vcpu_set_cpuid_feature(vcpu, feature);
+ vmx_sec_exec_assert_denied(vcpu, name, ctrl);
+
+ vcpu_clear_cpuid_feature(vcpu, feature);
+ vmx_sec_exec_assert_denied(vcpu, name, ctrl);
+ }
+}
+
+#define vmx_sec_exec_feature_test(vcpu, name, kvm_owned) \
+ vmx_sec_exec_control_test(vcpu, #name, X86_FEATURE_##name, \
+ SECONDARY_EXEC_ENABLE_##name, kvm_owned)
+
+#define vmx_sec_exec_exiting_test(vcpu, name, kvm_owned) \
+ vmx_sec_exec_control_test(vcpu, #name, X86_FEATURE_##name, \
+ SECONDARY_EXEC_##name##_EXITING, kvm_owned)
+
+static void vmx_sec_exec_controls_test(struct kvm_vcpu *vcpu)
+{
+ int i;
+
+ if (this_cpu_has(X86_FEATURE_XSAVE))
+ vcpu_set_cpuid_feature(vcpu, X86_FEATURE_XSAVE);
+
+ if (this_cpu_has(X86_FEATURE_RDPID))
+ vcpu_clear_cpuid_feature(vcpu, X86_FEATURE_RDPID);
+
+ /*
+ * Verify that for features KVM has historically taken control of, KVM
+ * updates PROCBASED_CTLS2 during KVM_SET_CPUID if userspace has never
+ * set the MSR, but leaves it alone once userspace writes the MSR.
+ */
+ for (i = 0; i < 2; i++) {
+ vmx_sec_exec_feature_test(vcpu, XSAVES, !i);
+ vmx_sec_exec_feature_test(vcpu, RDTSCP, !i);
+ vmx_sec_exec_feature_test(vcpu, INVPCID, !i);
+ vmx_sec_exec_exiting_test(vcpu, RDRAND, !i);
+ vmx_sec_exec_exiting_test(vcpu, RDSEED, !i);
+ }
+}
+
static void vmx_fixed1_msr_test(struct kvm_vcpu *vcpu, uint32_t msr_index,
uint64_t mask)
{
@@ -78,6 +168,8 @@ int main(void)
/* No need to actually do KVM_RUN, thus no guest code. */
vm = vm_create_with_one_vcpu(&vcpu, NULL);
+ vmx_sec_exec_controls_test(vcpu);
+
vmx_save_restore_msrs_test(vcpu);
kvm_vm_free(vm);