12325d4ddSSean Christopherson // SPDX-License-Identifier: GPL-2.0
22325d4ddSSean Christopherson #include <fcntl.h>
32325d4ddSSean Christopherson #include <stdio.h>
42325d4ddSSean Christopherson #include <stdlib.h>
52325d4ddSSean Christopherson #include <string.h>
62325d4ddSSean Christopherson #include <sys/ioctl.h>
72325d4ddSSean Christopherson 
82325d4ddSSean Christopherson #include "kvm_util.h"
92325d4ddSSean Christopherson #include "processor.h"
102325d4ddSSean Christopherson 
11683edfd4SSean Christopherson #define CPUID_MWAIT (1u << 3)
122325d4ddSSean Christopherson 
132325d4ddSSean Christopherson enum monitor_mwait_testcases {
142325d4ddSSean Christopherson 	MWAIT_QUIRK_DISABLED = BIT(0),
152325d4ddSSean Christopherson 	MISC_ENABLES_QUIRK_DISABLED = BIT(1),
162325d4ddSSean Christopherson 	MWAIT_DISABLED = BIT(2),
172325d4ddSSean Christopherson };
182325d4ddSSean Christopherson 
19*0f52e4aaSSean Christopherson /*
20*0f52e4aaSSean Christopherson  * If both MWAIT and its quirk are disabled, MONITOR/MWAIT should #UD, in all
21*0f52e4aaSSean Christopherson  * other scenarios KVM should emulate them as nops.
22*0f52e4aaSSean Christopherson  */
23*0f52e4aaSSean Christopherson #define GUEST_ASSERT_MONITOR_MWAIT(insn, testcase, vector)		\
24*0f52e4aaSSean Christopherson do {									\
25*0f52e4aaSSean Christopherson 	bool fault_wanted = ((testcase) & MWAIT_QUIRK_DISABLED) &&	\
26*0f52e4aaSSean Christopherson 			    ((testcase) & MWAIT_DISABLED);		\
27*0f52e4aaSSean Christopherson 									\
28*0f52e4aaSSean Christopherson 	if (fault_wanted)						\
29*0f52e4aaSSean Christopherson 		__GUEST_ASSERT((vector) == UD_VECTOR,			\
30*0f52e4aaSSean Christopherson 			       "Expected #UD on " insn " for testcase '0x%x', got '0x%x'", vector); \
31*0f52e4aaSSean Christopherson 	else								\
32*0f52e4aaSSean Christopherson 		__GUEST_ASSERT(!(vector),				\
33*0f52e4aaSSean Christopherson 			       "Expected success on " insn " for testcase '0x%x', got '0x%x'", vector); \
34*0f52e4aaSSean Christopherson } while (0)
35*0f52e4aaSSean Christopherson 
guest_monitor_wait(int testcase)362325d4ddSSean Christopherson static void guest_monitor_wait(int testcase)
372325d4ddSSean Christopherson {
382325d4ddSSean Christopherson 	u8 vector;
392325d4ddSSean Christopherson 
402325d4ddSSean Christopherson 	GUEST_SYNC(testcase);
412325d4ddSSean Christopherson 
42b624ae35SSean Christopherson 	/*
43b624ae35SSean Christopherson 	 * Arbitrarily MONITOR this function, SVM performs fault checks before
44b624ae35SSean Christopherson 	 * intercept checks, so the inputs for MONITOR and MWAIT must be valid.
45b624ae35SSean Christopherson 	 */
46b624ae35SSean Christopherson 	vector = kvm_asm_safe("monitor", "a"(guest_monitor_wait), "c"(0), "d"(0));
47*0f52e4aaSSean Christopherson 	GUEST_ASSERT_MONITOR_MWAIT("MONITOR", testcase, vector);
482325d4ddSSean Christopherson 
49b624ae35SSean Christopherson 	vector = kvm_asm_safe("mwait", "a"(guest_monitor_wait), "c"(0), "d"(0));
50*0f52e4aaSSean Christopherson 	GUEST_ASSERT_MONITOR_MWAIT("MWAIT", testcase, vector);
512325d4ddSSean Christopherson }
522325d4ddSSean Christopherson 
guest_code(void)532325d4ddSSean Christopherson static void guest_code(void)
542325d4ddSSean Christopherson {
552325d4ddSSean Christopherson 	guest_monitor_wait(MWAIT_DISABLED);
562325d4ddSSean Christopherson 
572325d4ddSSean Christopherson 	guest_monitor_wait(MWAIT_QUIRK_DISABLED | MWAIT_DISABLED);
582325d4ddSSean Christopherson 
592325d4ddSSean Christopherson 	guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED | MWAIT_DISABLED);
602325d4ddSSean Christopherson 	guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED);
612325d4ddSSean Christopherson 
622325d4ddSSean Christopherson 	guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED | MWAIT_QUIRK_DISABLED | MWAIT_DISABLED);
632325d4ddSSean Christopherson 	guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED | MWAIT_QUIRK_DISABLED);
642325d4ddSSean Christopherson 
652325d4ddSSean Christopherson 	GUEST_DONE();
662325d4ddSSean Christopherson }
672325d4ddSSean Christopherson 
main(int argc,char * argv[])682325d4ddSSean Christopherson int main(int argc, char *argv[])
692325d4ddSSean Christopherson {
702325d4ddSSean Christopherson 	uint64_t disabled_quirks;
712325d4ddSSean Christopherson 	struct kvm_vcpu *vcpu;
722325d4ddSSean Christopherson 	struct kvm_vm *vm;
732325d4ddSSean Christopherson 	struct ucall uc;
742325d4ddSSean Christopherson 	int testcase;
752325d4ddSSean Christopherson 
762325d4ddSSean Christopherson 	TEST_REQUIRE(kvm_has_cap(KVM_CAP_DISABLE_QUIRKS2));
772325d4ddSSean Christopherson 
782325d4ddSSean Christopherson 	vm = vm_create_with_one_vcpu(&vcpu, guest_code);
79b78843beSSean Christopherson 	vcpu_clear_cpuid_feature(vcpu, X86_FEATURE_MWAIT);
802325d4ddSSean Christopherson 
812325d4ddSSean Christopherson 	vm_init_descriptor_tables(vm);
822325d4ddSSean Christopherson 	vcpu_init_descriptor_tables(vcpu);
832325d4ddSSean Christopherson 
842325d4ddSSean Christopherson 	while (1) {
852325d4ddSSean Christopherson 		vcpu_run(vcpu);
86c96f57b0SVipin Sharma 		TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
872325d4ddSSean Christopherson 
882325d4ddSSean Christopherson 		switch (get_ucall(vcpu, &uc)) {
892325d4ddSSean Christopherson 		case UCALL_SYNC:
902325d4ddSSean Christopherson 			testcase = uc.args[1];
912325d4ddSSean Christopherson 			break;
922325d4ddSSean Christopherson 		case UCALL_ABORT:
93*0f52e4aaSSean Christopherson 			REPORT_GUEST_ASSERT(uc);
942325d4ddSSean Christopherson 			goto done;
952325d4ddSSean Christopherson 		case UCALL_DONE:
962325d4ddSSean Christopherson 			goto done;
972325d4ddSSean Christopherson 		default:
982325d4ddSSean Christopherson 			TEST_FAIL("Unknown ucall %lu", uc.cmd);
992325d4ddSSean Christopherson 			goto done;
1002325d4ddSSean Christopherson 		}
1012325d4ddSSean Christopherson 
1022325d4ddSSean Christopherson 		disabled_quirks = 0;
1032325d4ddSSean Christopherson 		if (testcase & MWAIT_QUIRK_DISABLED)
10443bb9e00SSean Christopherson 			disabled_quirks |= KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS;
1052325d4ddSSean Christopherson 		if (testcase & MISC_ENABLES_QUIRK_DISABLED)
1062325d4ddSSean Christopherson 			disabled_quirks |= KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT;
1072325d4ddSSean Christopherson 		vm_enable_cap(vm, KVM_CAP_DISABLE_QUIRKS2, disabled_quirks);
1082325d4ddSSean Christopherson 
1092325d4ddSSean Christopherson 		/*
1102325d4ddSSean Christopherson 		 * If the MISC_ENABLES quirk (KVM neglects to update CPUID to
1112325d4ddSSean Christopherson 		 * enable/disable MWAIT) is disabled, toggle the ENABLE_MWAIT
1122325d4ddSSean Christopherson 		 * bit in MISC_ENABLES accordingly.  If the quirk is enabled,
1132325d4ddSSean Christopherson 		 * the only valid configuration is MWAIT disabled, as CPUID
1142325d4ddSSean Christopherson 		 * can't be manually changed after running the vCPU.
1152325d4ddSSean Christopherson 		 */
1162325d4ddSSean Christopherson 		if (!(testcase & MISC_ENABLES_QUIRK_DISABLED)) {
1172325d4ddSSean Christopherson 			TEST_ASSERT(testcase & MWAIT_DISABLED,
1182325d4ddSSean Christopherson 				    "Can't toggle CPUID features after running vCPU");
1192325d4ddSSean Christopherson 			continue;
1202325d4ddSSean Christopherson 		}
1212325d4ddSSean Christopherson 
1222325d4ddSSean Christopherson 		vcpu_set_msr(vcpu, MSR_IA32_MISC_ENABLE,
1232325d4ddSSean Christopherson 			     (testcase & MWAIT_DISABLED) ? 0 : MSR_IA32_MISC_ENABLE_MWAIT);
1242325d4ddSSean Christopherson 	}
1252325d4ddSSean Christopherson 
1262325d4ddSSean Christopherson done:
1272325d4ddSSean Christopherson 	kvm_vm_free(vm);
1282325d4ddSSean Christopherson 	return 0;
1292325d4ddSSean Christopherson }
130