xref: /linux/tools/testing/selftests/kvm/x86/monitor_mwait_test.c (revision 0e8863244ef5b7d4391816062fcc07ff49aa7dcf)
167730e6cSSean Christopherson // SPDX-License-Identifier: GPL-2.0
267730e6cSSean Christopherson #include <fcntl.h>
367730e6cSSean Christopherson #include <stdio.h>
467730e6cSSean Christopherson #include <stdlib.h>
567730e6cSSean Christopherson #include <string.h>
667730e6cSSean Christopherson #include <sys/ioctl.h>
767730e6cSSean Christopherson 
867730e6cSSean Christopherson #include "kvm_util.h"
967730e6cSSean Christopherson #include "processor.h"
10*80fd6635SPaolo Bonzini #include "kselftest.h"
1167730e6cSSean Christopherson 
1267730e6cSSean Christopherson #define CPUID_MWAIT (1u << 3)
1367730e6cSSean Christopherson 
1467730e6cSSean Christopherson enum monitor_mwait_testcases {
1567730e6cSSean Christopherson 	MWAIT_QUIRK_DISABLED = BIT(0),
1667730e6cSSean Christopherson 	MISC_ENABLES_QUIRK_DISABLED = BIT(1),
1767730e6cSSean Christopherson 	MWAIT_DISABLED = BIT(2),
18*80fd6635SPaolo Bonzini 	CPUID_DISABLED = BIT(3),
19*80fd6635SPaolo Bonzini 	TEST_MAX = CPUID_DISABLED * 2 - 1,
2067730e6cSSean Christopherson };
2167730e6cSSean Christopherson 
2267730e6cSSean Christopherson /*
2367730e6cSSean Christopherson  * If both MWAIT and its quirk are disabled, MONITOR/MWAIT should #UD, in all
2467730e6cSSean Christopherson  * other scenarios KVM should emulate them as nops.
2567730e6cSSean Christopherson  */
2667730e6cSSean Christopherson #define GUEST_ASSERT_MONITOR_MWAIT(insn, testcase, vector)		\
2767730e6cSSean Christopherson do {									\
2867730e6cSSean Christopherson 	bool fault_wanted = ((testcase) & MWAIT_QUIRK_DISABLED) &&	\
2967730e6cSSean Christopherson 			    ((testcase) & MWAIT_DISABLED);		\
3067730e6cSSean Christopherson 									\
3167730e6cSSean Christopherson 	if (fault_wanted)						\
3267730e6cSSean Christopherson 		__GUEST_ASSERT((vector) == UD_VECTOR,			\
3367730e6cSSean Christopherson 			       "Expected #UD on " insn " for testcase '0x%x', got '0x%x'", \
3467730e6cSSean Christopherson 			       testcase, vector);			\
3567730e6cSSean Christopherson 	else								\
3667730e6cSSean Christopherson 		__GUEST_ASSERT(!(vector),				\
3767730e6cSSean Christopherson 			       "Expected success on " insn " for testcase '0x%x', got '0x%x'", \
3867730e6cSSean Christopherson 			       testcase, vector);			\
3967730e6cSSean Christopherson } while (0)
4067730e6cSSean Christopherson 
guest_monitor_wait(void * arg)41*80fd6635SPaolo Bonzini static void guest_monitor_wait(void *arg)
4267730e6cSSean Christopherson {
43*80fd6635SPaolo Bonzini 	int testcase = (int) (long) arg;
4467730e6cSSean Christopherson 	u8 vector;
4567730e6cSSean Christopherson 
46*80fd6635SPaolo Bonzini 	u64 val = rdmsr(MSR_IA32_MISC_ENABLE) & ~MSR_IA32_MISC_ENABLE_MWAIT;
47*80fd6635SPaolo Bonzini 	if (!(testcase & MWAIT_DISABLED))
48*80fd6635SPaolo Bonzini 		val |= MSR_IA32_MISC_ENABLE_MWAIT;
49*80fd6635SPaolo Bonzini 	wrmsr(MSR_IA32_MISC_ENABLE, val);
50*80fd6635SPaolo Bonzini 
51*80fd6635SPaolo Bonzini 	__GUEST_ASSERT(this_cpu_has(X86_FEATURE_MWAIT) == !(testcase & MWAIT_DISABLED),
52*80fd6635SPaolo Bonzini 		       "Expected CPUID.MWAIT %s\n",
53*80fd6635SPaolo Bonzini 		       (testcase & MWAIT_DISABLED) ? "cleared" : "set");
5467730e6cSSean Christopherson 
5567730e6cSSean Christopherson 	/*
5667730e6cSSean Christopherson 	 * Arbitrarily MONITOR this function, SVM performs fault checks before
5767730e6cSSean Christopherson 	 * intercept checks, so the inputs for MONITOR and MWAIT must be valid.
5867730e6cSSean Christopherson 	 */
5967730e6cSSean Christopherson 	vector = kvm_asm_safe("monitor", "a"(guest_monitor_wait), "c"(0), "d"(0));
6067730e6cSSean Christopherson 	GUEST_ASSERT_MONITOR_MWAIT("MONITOR", testcase, vector);
6167730e6cSSean Christopherson 
6267730e6cSSean Christopherson 	vector = kvm_asm_safe("mwait", "a"(guest_monitor_wait), "c"(0), "d"(0));
6367730e6cSSean Christopherson 	GUEST_ASSERT_MONITOR_MWAIT("MWAIT", testcase, vector);
6467730e6cSSean Christopherson 
6567730e6cSSean Christopherson 	GUEST_DONE();
6667730e6cSSean Christopherson }
6767730e6cSSean Christopherson 
main(int argc,char * argv[])6867730e6cSSean Christopherson int main(int argc, char *argv[])
6967730e6cSSean Christopherson {
7067730e6cSSean Christopherson 	uint64_t disabled_quirks;
7167730e6cSSean Christopherson 	struct kvm_vcpu *vcpu;
7267730e6cSSean Christopherson 	struct kvm_vm *vm;
7367730e6cSSean Christopherson 	struct ucall uc;
7467730e6cSSean Christopherson 	int testcase;
75*80fd6635SPaolo Bonzini 	char test[80];
7667730e6cSSean Christopherson 
7767730e6cSSean Christopherson 	TEST_REQUIRE(kvm_has_cap(KVM_CAP_DISABLE_QUIRKS2));
7867730e6cSSean Christopherson 
79*80fd6635SPaolo Bonzini 	ksft_print_header();
80*80fd6635SPaolo Bonzini 	ksft_set_plan(12);
81*80fd6635SPaolo Bonzini 	for (testcase = 0; testcase <= TEST_MAX; testcase++) {
82*80fd6635SPaolo Bonzini 		vm = vm_create_with_one_vcpu(&vcpu, guest_monitor_wait);
83*80fd6635SPaolo Bonzini 		vcpu_args_set(vcpu, 1, (void *)(long)testcase);
8467730e6cSSean Christopherson 
85*80fd6635SPaolo Bonzini 		disabled_quirks = 0;
86*80fd6635SPaolo Bonzini 		if (testcase & MWAIT_QUIRK_DISABLED) {
87*80fd6635SPaolo Bonzini 			disabled_quirks |= KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS;
88*80fd6635SPaolo Bonzini 			strcpy(test, "MWAIT can fault");
89*80fd6635SPaolo Bonzini 		} else {
90*80fd6635SPaolo Bonzini 			strcpy(test, "MWAIT never faults");
91*80fd6635SPaolo Bonzini 		}
92*80fd6635SPaolo Bonzini 		if (testcase & MISC_ENABLES_QUIRK_DISABLED) {
93*80fd6635SPaolo Bonzini 			disabled_quirks |= KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT;
94*80fd6635SPaolo Bonzini 			strcat(test, ", MISC_ENABLE updates CPUID");
95*80fd6635SPaolo Bonzini 		} else {
96*80fd6635SPaolo Bonzini 			strcat(test, ", no CPUID updates");
97*80fd6635SPaolo Bonzini 		}
98*80fd6635SPaolo Bonzini 
99*80fd6635SPaolo Bonzini 		vm_enable_cap(vm, KVM_CAP_DISABLE_QUIRKS2, disabled_quirks);
100*80fd6635SPaolo Bonzini 
101*80fd6635SPaolo Bonzini 		if (!(testcase & MISC_ENABLES_QUIRK_DISABLED) &&
102*80fd6635SPaolo Bonzini 		    (!!(testcase & CPUID_DISABLED) ^ !!(testcase & MWAIT_DISABLED)))
103*80fd6635SPaolo Bonzini 			continue;
104*80fd6635SPaolo Bonzini 
105*80fd6635SPaolo Bonzini 		if (testcase & CPUID_DISABLED) {
106*80fd6635SPaolo Bonzini 			strcat(test, ", CPUID clear");
107*80fd6635SPaolo Bonzini 			vcpu_clear_cpuid_feature(vcpu, X86_FEATURE_MWAIT);
108*80fd6635SPaolo Bonzini 		} else {
109*80fd6635SPaolo Bonzini 			strcat(test, ", CPUID set");
110*80fd6635SPaolo Bonzini 			vcpu_set_cpuid_feature(vcpu, X86_FEATURE_MWAIT);
111*80fd6635SPaolo Bonzini 		}
112*80fd6635SPaolo Bonzini 
113*80fd6635SPaolo Bonzini 		if (testcase & MWAIT_DISABLED)
114*80fd6635SPaolo Bonzini 			strcat(test, ", MWAIT disabled");
115*80fd6635SPaolo Bonzini 
11667730e6cSSean Christopherson 		vcpu_run(vcpu);
11767730e6cSSean Christopherson 		TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
11867730e6cSSean Christopherson 
11967730e6cSSean Christopherson 		switch (get_ucall(vcpu, &uc)) {
12067730e6cSSean Christopherson 		case UCALL_ABORT:
121*80fd6635SPaolo Bonzini 			/* Detected in vcpu_run */
122*80fd6635SPaolo Bonzini 			break;
12367730e6cSSean Christopherson 		case UCALL_DONE:
124*80fd6635SPaolo Bonzini 			ksft_test_result_pass("%s\n", test);
125*80fd6635SPaolo Bonzini 			break;
12667730e6cSSean Christopherson 		default:
12767730e6cSSean Christopherson 			TEST_FAIL("Unknown ucall %lu", uc.cmd);
128*80fd6635SPaolo Bonzini 			break;
12967730e6cSSean Christopherson 		}
13067730e6cSSean Christopherson 		kvm_vm_free(vm);
131*80fd6635SPaolo Bonzini 	}
132*80fd6635SPaolo Bonzini 	ksft_finished();
133*80fd6635SPaolo Bonzini 
13467730e6cSSean Christopherson 	return 0;
13567730e6cSSean Christopherson }
136