xref: /linux/tools/testing/selftests/kvm/x86/monitor_mwait_test.c (revision 0e8863244ef5b7d4391816062fcc07ff49aa7dcf)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <fcntl.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/ioctl.h>
7 
8 #include "kvm_util.h"
9 #include "processor.h"
10 #include "kselftest.h"
11 
12 #define CPUID_MWAIT (1u << 3)
13 
14 enum monitor_mwait_testcases {
15 	MWAIT_QUIRK_DISABLED = BIT(0),
16 	MISC_ENABLES_QUIRK_DISABLED = BIT(1),
17 	MWAIT_DISABLED = BIT(2),
18 	CPUID_DISABLED = BIT(3),
19 	TEST_MAX = CPUID_DISABLED * 2 - 1,
20 };
21 
22 /*
23  * If both MWAIT and its quirk are disabled, MONITOR/MWAIT should #UD, in all
24  * other scenarios KVM should emulate them as nops.
25  */
26 #define GUEST_ASSERT_MONITOR_MWAIT(insn, testcase, vector)		\
27 do {									\
28 	bool fault_wanted = ((testcase) & MWAIT_QUIRK_DISABLED) &&	\
29 			    ((testcase) & MWAIT_DISABLED);		\
30 									\
31 	if (fault_wanted)						\
32 		__GUEST_ASSERT((vector) == UD_VECTOR,			\
33 			       "Expected #UD on " insn " for testcase '0x%x', got '0x%x'", \
34 			       testcase, vector);			\
35 	else								\
36 		__GUEST_ASSERT(!(vector),				\
37 			       "Expected success on " insn " for testcase '0x%x', got '0x%x'", \
38 			       testcase, vector);			\
39 } while (0)
40 
guest_monitor_wait(void * arg)41 static void guest_monitor_wait(void *arg)
42 {
43 	int testcase = (int) (long) arg;
44 	u8 vector;
45 
46 	u64 val = rdmsr(MSR_IA32_MISC_ENABLE) & ~MSR_IA32_MISC_ENABLE_MWAIT;
47 	if (!(testcase & MWAIT_DISABLED))
48 		val |= MSR_IA32_MISC_ENABLE_MWAIT;
49 	wrmsr(MSR_IA32_MISC_ENABLE, val);
50 
51 	__GUEST_ASSERT(this_cpu_has(X86_FEATURE_MWAIT) == !(testcase & MWAIT_DISABLED),
52 		       "Expected CPUID.MWAIT %s\n",
53 		       (testcase & MWAIT_DISABLED) ? "cleared" : "set");
54 
55 	/*
56 	 * Arbitrarily MONITOR this function, SVM performs fault checks before
57 	 * intercept checks, so the inputs for MONITOR and MWAIT must be valid.
58 	 */
59 	vector = kvm_asm_safe("monitor", "a"(guest_monitor_wait), "c"(0), "d"(0));
60 	GUEST_ASSERT_MONITOR_MWAIT("MONITOR", testcase, vector);
61 
62 	vector = kvm_asm_safe("mwait", "a"(guest_monitor_wait), "c"(0), "d"(0));
63 	GUEST_ASSERT_MONITOR_MWAIT("MWAIT", testcase, vector);
64 
65 	GUEST_DONE();
66 }
67 
main(int argc,char * argv[])68 int main(int argc, char *argv[])
69 {
70 	uint64_t disabled_quirks;
71 	struct kvm_vcpu *vcpu;
72 	struct kvm_vm *vm;
73 	struct ucall uc;
74 	int testcase;
75 	char test[80];
76 
77 	TEST_REQUIRE(kvm_has_cap(KVM_CAP_DISABLE_QUIRKS2));
78 
79 	ksft_print_header();
80 	ksft_set_plan(12);
81 	for (testcase = 0; testcase <= TEST_MAX; testcase++) {
82 		vm = vm_create_with_one_vcpu(&vcpu, guest_monitor_wait);
83 		vcpu_args_set(vcpu, 1, (void *)(long)testcase);
84 
85 		disabled_quirks = 0;
86 		if (testcase & MWAIT_QUIRK_DISABLED) {
87 			disabled_quirks |= KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS;
88 			strcpy(test, "MWAIT can fault");
89 		} else {
90 			strcpy(test, "MWAIT never faults");
91 		}
92 		if (testcase & MISC_ENABLES_QUIRK_DISABLED) {
93 			disabled_quirks |= KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT;
94 			strcat(test, ", MISC_ENABLE updates CPUID");
95 		} else {
96 			strcat(test, ", no CPUID updates");
97 		}
98 
99 		vm_enable_cap(vm, KVM_CAP_DISABLE_QUIRKS2, disabled_quirks);
100 
101 		if (!(testcase & MISC_ENABLES_QUIRK_DISABLED) &&
102 		    (!!(testcase & CPUID_DISABLED) ^ !!(testcase & MWAIT_DISABLED)))
103 			continue;
104 
105 		if (testcase & CPUID_DISABLED) {
106 			strcat(test, ", CPUID clear");
107 			vcpu_clear_cpuid_feature(vcpu, X86_FEATURE_MWAIT);
108 		} else {
109 			strcat(test, ", CPUID set");
110 			vcpu_set_cpuid_feature(vcpu, X86_FEATURE_MWAIT);
111 		}
112 
113 		if (testcase & MWAIT_DISABLED)
114 			strcat(test, ", MWAIT disabled");
115 
116 		vcpu_run(vcpu);
117 		TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
118 
119 		switch (get_ucall(vcpu, &uc)) {
120 		case UCALL_ABORT:
121 			/* Detected in vcpu_run */
122 			break;
123 		case UCALL_DONE:
124 			ksft_test_result_pass("%s\n", test);
125 			break;
126 		default:
127 			TEST_FAIL("Unknown ucall %lu", uc.cmd);
128 			break;
129 		}
130 		kvm_vm_free(vm);
131 	}
132 	ksft_finished();
133 
134 	return 0;
135 }
136