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 X86_FEATURE_MWAIT (1u << 3) 12 13 enum monitor_mwait_testcases { 14 MWAIT_QUIRK_DISABLED = BIT(0), 15 MISC_ENABLES_QUIRK_DISABLED = BIT(1), 16 MWAIT_DISABLED = BIT(2), 17 }; 18 19 static void guest_monitor_wait(int testcase) 20 { 21 /* 22 * If both MWAIT and its quirk are disabled, MONITOR/MWAIT should #UD, 23 * in all other scenarios KVM should emulate them as nops. 24 */ 25 bool fault_wanted = (testcase & MWAIT_QUIRK_DISABLED) && 26 (testcase & MWAIT_DISABLED); 27 u8 vector; 28 29 GUEST_SYNC(testcase); 30 31 vector = kvm_asm_safe("monitor"); 32 if (fault_wanted) 33 GUEST_ASSERT_2(vector == UD_VECTOR, testcase, vector); 34 else 35 GUEST_ASSERT_2(!vector, testcase, vector); 36 37 vector = kvm_asm_safe("monitor"); 38 if (fault_wanted) 39 GUEST_ASSERT_2(vector == UD_VECTOR, testcase, vector); 40 else 41 GUEST_ASSERT_2(!vector, testcase, vector); 42 } 43 44 static void guest_code(void) 45 { 46 guest_monitor_wait(MWAIT_DISABLED); 47 48 guest_monitor_wait(MWAIT_QUIRK_DISABLED | MWAIT_DISABLED); 49 50 guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED | MWAIT_DISABLED); 51 guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED); 52 53 guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED | MWAIT_QUIRK_DISABLED | MWAIT_DISABLED); 54 guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED | MWAIT_QUIRK_DISABLED); 55 56 GUEST_DONE(); 57 } 58 59 int main(int argc, char *argv[]) 60 { 61 uint64_t disabled_quirks; 62 struct kvm_cpuid2 *cpuid; 63 struct kvm_cpuid_entry2 *entry; 64 struct kvm_vcpu *vcpu; 65 struct kvm_run *run; 66 struct kvm_vm *vm; 67 struct ucall uc; 68 int testcase; 69 70 TEST_REQUIRE(kvm_has_cap(KVM_CAP_DISABLE_QUIRKS2)); 71 72 cpuid = kvm_get_supported_cpuid(); 73 74 entry = kvm_get_supported_cpuid_index(1, 0); 75 entry->ecx &= ~X86_FEATURE_MWAIT; 76 set_cpuid(cpuid, entry); 77 78 vm = vm_create_with_one_vcpu(&vcpu, guest_code); 79 vcpu_set_cpuid(vcpu, cpuid); 80 81 run = vcpu->run; 82 83 vm_init_descriptor_tables(vm); 84 vcpu_init_descriptor_tables(vcpu); 85 86 while (1) { 87 vcpu_run(vcpu); 88 89 TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, 90 "Unexpected exit reason: %u (%s),\n", 91 run->exit_reason, 92 exit_reason_str(run->exit_reason)); 93 94 switch (get_ucall(vcpu, &uc)) { 95 case UCALL_SYNC: 96 testcase = uc.args[1]; 97 break; 98 case UCALL_ABORT: 99 TEST_FAIL("%s at %s:%ld, testcase = %lx, vector = %ld", 100 (const char *)uc.args[0], __FILE__, 101 uc.args[1], uc.args[2], uc.args[3]); 102 goto done; 103 case UCALL_DONE: 104 goto done; 105 default: 106 TEST_FAIL("Unknown ucall %lu", uc.cmd); 107 goto done; 108 } 109 110 disabled_quirks = 0; 111 if (testcase & MWAIT_QUIRK_DISABLED) 112 disabled_quirks |= KVM_X86_QUIRK_MWAIT_NEVER_FAULTS; 113 if (testcase & MISC_ENABLES_QUIRK_DISABLED) 114 disabled_quirks |= KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT; 115 vm_enable_cap(vm, KVM_CAP_DISABLE_QUIRKS2, disabled_quirks); 116 117 /* 118 * If the MISC_ENABLES quirk (KVM neglects to update CPUID to 119 * enable/disable MWAIT) is disabled, toggle the ENABLE_MWAIT 120 * bit in MISC_ENABLES accordingly. If the quirk is enabled, 121 * the only valid configuration is MWAIT disabled, as CPUID 122 * can't be manually changed after running the vCPU. 123 */ 124 if (!(testcase & MISC_ENABLES_QUIRK_DISABLED)) { 125 TEST_ASSERT(testcase & MWAIT_DISABLED, 126 "Can't toggle CPUID features after running vCPU"); 127 continue; 128 } 129 130 vcpu_set_msr(vcpu, MSR_IA32_MISC_ENABLE, 131 (testcase & MWAIT_DISABLED) ? 0 : MSR_IA32_MISC_ENABLE_MWAIT); 132 } 133 134 done: 135 kvm_vm_free(vm); 136 return 0; 137 } 138