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