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 
11 #define CPUID_MWAIT (1u << 3)
12 
13 enum monitor_mwait_testcases {
14 	MWAIT_QUIRK_DISABLED = BIT(0),
15 	MISC_ENABLES_QUIRK_DISABLED = BIT(1),
16 	MWAIT_DISABLED = BIT(2),
17 };
18 
19 /*
20  * If both MWAIT and its quirk are disabled, MONITOR/MWAIT should #UD, in all
21  * other scenarios KVM should emulate them as nops.
22  */
23 #define GUEST_ASSERT_MONITOR_MWAIT(insn, testcase, vector)		\
24 do {									\
25 	bool fault_wanted = ((testcase) & MWAIT_QUIRK_DISABLED) &&	\
26 			    ((testcase) & MWAIT_DISABLED);		\
27 									\
28 	if (fault_wanted)						\
29 		__GUEST_ASSERT((vector) == UD_VECTOR,			\
30 			       "Expected #UD on " insn " for testcase '0x%x', got '0x%x'", \
31 			       testcase, vector);			\
32 	else								\
33 		__GUEST_ASSERT(!(vector),				\
34 			       "Expected success on " insn " for testcase '0x%x', got '0x%x'", \
35 			       testcase, vector);			\
36 } while (0)
37 
guest_monitor_wait(int testcase)38 static void guest_monitor_wait(int testcase)
39 {
40 	u8 vector;
41 
42 	GUEST_SYNC(testcase);
43 
44 	/*
45 	 * Arbitrarily MONITOR this function, SVM performs fault checks before
46 	 * intercept checks, so the inputs for MONITOR and MWAIT must be valid.
47 	 */
48 	vector = kvm_asm_safe("monitor", "a"(guest_monitor_wait), "c"(0), "d"(0));
49 	GUEST_ASSERT_MONITOR_MWAIT("MONITOR", testcase, vector);
50 
51 	vector = kvm_asm_safe("mwait", "a"(guest_monitor_wait), "c"(0), "d"(0));
52 	GUEST_ASSERT_MONITOR_MWAIT("MWAIT", testcase, vector);
53 }
54 
guest_code(void)55 static void guest_code(void)
56 {
57 	guest_monitor_wait(MWAIT_DISABLED);
58 
59 	guest_monitor_wait(MWAIT_QUIRK_DISABLED | MWAIT_DISABLED);
60 
61 	guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED | MWAIT_DISABLED);
62 	guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED);
63 
64 	guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED | MWAIT_QUIRK_DISABLED | MWAIT_DISABLED);
65 	guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED | MWAIT_QUIRK_DISABLED);
66 
67 	GUEST_DONE();
68 }
69 
main(int argc,char * argv[])70 int main(int argc, char *argv[])
71 {
72 	uint64_t disabled_quirks;
73 	struct kvm_vcpu *vcpu;
74 	struct kvm_vm *vm;
75 	struct ucall uc;
76 	int testcase;
77 
78 	TEST_REQUIRE(this_cpu_has(X86_FEATURE_MWAIT));
79 	TEST_REQUIRE(kvm_has_cap(KVM_CAP_DISABLE_QUIRKS2));
80 
81 	vm = vm_create_with_one_vcpu(&vcpu, guest_code);
82 	vcpu_clear_cpuid_feature(vcpu, X86_FEATURE_MWAIT);
83 
84 	while (1) {
85 		vcpu_run(vcpu);
86 		TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
87 
88 		switch (get_ucall(vcpu, &uc)) {
89 		case UCALL_SYNC:
90 			testcase = uc.args[1];
91 			break;
92 		case UCALL_ABORT:
93 			REPORT_GUEST_ASSERT(uc);
94 			goto done;
95 		case UCALL_DONE:
96 			goto done;
97 		default:
98 			TEST_FAIL("Unknown ucall %lu", uc.cmd);
99 			goto done;
100 		}
101 
102 		disabled_quirks = 0;
103 		if (testcase & MWAIT_QUIRK_DISABLED)
104 			disabled_quirks |= KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS;
105 		if (testcase & MISC_ENABLES_QUIRK_DISABLED)
106 			disabled_quirks |= KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT;
107 		vm_enable_cap(vm, KVM_CAP_DISABLE_QUIRKS2, disabled_quirks);
108 
109 		/*
110 		 * If the MISC_ENABLES quirk (KVM neglects to update CPUID to
111 		 * enable/disable MWAIT) is disabled, toggle the ENABLE_MWAIT
112 		 * bit in MISC_ENABLES accordingly.  If the quirk is enabled,
113 		 * the only valid configuration is MWAIT disabled, as CPUID
114 		 * can't be manually changed after running the vCPU.
115 		 */
116 		if (!(testcase & MISC_ENABLES_QUIRK_DISABLED)) {
117 			TEST_ASSERT(testcase & MWAIT_DISABLED,
118 				    "Can't toggle CPUID features after running vCPU");
119 			continue;
120 		}
121 
122 		vcpu_set_msr(vcpu, MSR_IA32_MISC_ENABLE,
123 			     (testcase & MWAIT_DISABLED) ? 0 : MSR_IA32_MISC_ENABLE_MWAIT);
124 	}
125 
126 done:
127 	kvm_vm_free(vm);
128 	return 0;
129 }
130