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