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
13enum 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)		\
24do {									\
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
38static 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
55static 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
70int 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(kvm_has_cap(KVM_CAP_DISABLE_QUIRKS2));
79
80	vm = vm_create_with_one_vcpu(&vcpu, guest_code);
81	vcpu_clear_cpuid_feature(vcpu, X86_FEATURE_MWAIT);
82
83	vm_init_descriptor_tables(vm);
84	vcpu_init_descriptor_tables(vcpu);
85
86	while (1) {
87		vcpu_run(vcpu);
88		TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
89
90		switch (get_ucall(vcpu, &uc)) {
91		case UCALL_SYNC:
92			testcase = uc.args[1];
93			break;
94		case UCALL_ABORT:
95			REPORT_GUEST_ASSERT(uc);
96			goto done;
97		case UCALL_DONE:
98			goto done;
99		default:
100			TEST_FAIL("Unknown ucall %lu", uc.cmd);
101			goto done;
102		}
103
104		disabled_quirks = 0;
105		if (testcase & MWAIT_QUIRK_DISABLED)
106			disabled_quirks |= KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS;
107		if (testcase & MISC_ENABLES_QUIRK_DISABLED)
108			disabled_quirks |= KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT;
109		vm_enable_cap(vm, KVM_CAP_DISABLE_QUIRKS2, disabled_quirks);
110
111		/*
112		 * If the MISC_ENABLES quirk (KVM neglects to update CPUID to
113		 * enable/disable MWAIT) is disabled, toggle the ENABLE_MWAIT
114		 * bit in MISC_ENABLES accordingly.  If the quirk is enabled,
115		 * the only valid configuration is MWAIT disabled, as CPUID
116		 * can't be manually changed after running the vCPU.
117		 */
118		if (!(testcase & MISC_ENABLES_QUIRK_DISABLED)) {
119			TEST_ASSERT(testcase & MWAIT_DISABLED,
120				    "Can't toggle CPUID features after running vCPU");
121			continue;
122		}
123
124		vcpu_set_msr(vcpu, MSR_IA32_MISC_ENABLE,
125			     (testcase & MWAIT_DISABLED) ? 0 : MSR_IA32_MISC_ENABLE_MWAIT);
126	}
127
128done:
129	kvm_vm_free(vm);
130	return 0;
131}
132