Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <fcntl.h>
0003 #include <stdio.h>
0004 #include <stdlib.h>
0005 #include <string.h>
0006 #include <sys/ioctl.h>
0007 
0008 #include "kvm_util.h"
0009 #include "processor.h"
0010 
0011 #define CPUID_MWAIT (1u << 3)
0012 
0013 enum monitor_mwait_testcases {
0014     MWAIT_QUIRK_DISABLED = BIT(0),
0015     MISC_ENABLES_QUIRK_DISABLED = BIT(1),
0016     MWAIT_DISABLED = BIT(2),
0017 };
0018 
0019 static void guest_monitor_wait(int testcase)
0020 {
0021     /*
0022      * If both MWAIT and its quirk are disabled, MONITOR/MWAIT should #UD,
0023      * in all other scenarios KVM should emulate them as nops.
0024      */
0025     bool fault_wanted = (testcase & MWAIT_QUIRK_DISABLED) &&
0026                 (testcase & MWAIT_DISABLED);
0027     u8 vector;
0028 
0029     GUEST_SYNC(testcase);
0030 
0031     /*
0032      * Arbitrarily MONITOR this function, SVM performs fault checks before
0033      * intercept checks, so the inputs for MONITOR and MWAIT must be valid.
0034      */
0035     vector = kvm_asm_safe("monitor", "a"(guest_monitor_wait), "c"(0), "d"(0));
0036     if (fault_wanted)
0037         GUEST_ASSERT_2(vector == UD_VECTOR, testcase, vector);
0038     else
0039         GUEST_ASSERT_2(!vector, testcase, vector);
0040 
0041     vector = kvm_asm_safe("mwait", "a"(guest_monitor_wait), "c"(0), "d"(0));
0042     if (fault_wanted)
0043         GUEST_ASSERT_2(vector == UD_VECTOR, testcase, vector);
0044     else
0045         GUEST_ASSERT_2(!vector, testcase, vector);
0046 }
0047 
0048 static void guest_code(void)
0049 {
0050     guest_monitor_wait(MWAIT_DISABLED);
0051 
0052     guest_monitor_wait(MWAIT_QUIRK_DISABLED | MWAIT_DISABLED);
0053 
0054     guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED | MWAIT_DISABLED);
0055     guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED);
0056 
0057     guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED | MWAIT_QUIRK_DISABLED | MWAIT_DISABLED);
0058     guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED | MWAIT_QUIRK_DISABLED);
0059 
0060     GUEST_DONE();
0061 }
0062 
0063 int main(int argc, char *argv[])
0064 {
0065     uint64_t disabled_quirks;
0066     struct kvm_vcpu *vcpu;
0067     struct kvm_run *run;
0068     struct kvm_vm *vm;
0069     struct ucall uc;
0070     int testcase;
0071 
0072     TEST_REQUIRE(kvm_has_cap(KVM_CAP_DISABLE_QUIRKS2));
0073 
0074     vm = vm_create_with_one_vcpu(&vcpu, guest_code);
0075     vcpu_clear_cpuid_feature(vcpu, X86_FEATURE_MWAIT);
0076 
0077     run = vcpu->run;
0078 
0079     vm_init_descriptor_tables(vm);
0080     vcpu_init_descriptor_tables(vcpu);
0081 
0082     while (1) {
0083         vcpu_run(vcpu);
0084 
0085         TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
0086                 "Unexpected exit reason: %u (%s),\n",
0087                 run->exit_reason,
0088                 exit_reason_str(run->exit_reason));
0089 
0090         switch (get_ucall(vcpu, &uc)) {
0091         case UCALL_SYNC:
0092             testcase = uc.args[1];
0093             break;
0094         case UCALL_ABORT:
0095             REPORT_GUEST_ASSERT_2(uc, "testcase = %lx, vector = %ld");
0096             goto done;
0097         case UCALL_DONE:
0098             goto done;
0099         default:
0100             TEST_FAIL("Unknown ucall %lu", uc.cmd);
0101             goto done;
0102         }
0103 
0104         disabled_quirks = 0;
0105         if (testcase & MWAIT_QUIRK_DISABLED)
0106             disabled_quirks |= KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS;
0107         if (testcase & MISC_ENABLES_QUIRK_DISABLED)
0108             disabled_quirks |= KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT;
0109         vm_enable_cap(vm, KVM_CAP_DISABLE_QUIRKS2, disabled_quirks);
0110 
0111         /*
0112          * If the MISC_ENABLES quirk (KVM neglects to update CPUID to
0113          * enable/disable MWAIT) is disabled, toggle the ENABLE_MWAIT
0114          * bit in MISC_ENABLES accordingly.  If the quirk is enabled,
0115          * the only valid configuration is MWAIT disabled, as CPUID
0116          * can't be manually changed after running the vCPU.
0117          */
0118         if (!(testcase & MISC_ENABLES_QUIRK_DISABLED)) {
0119             TEST_ASSERT(testcase & MWAIT_DISABLED,
0120                     "Can't toggle CPUID features after running vCPU");
0121             continue;
0122         }
0123 
0124         vcpu_set_msr(vcpu, MSR_IA32_MISC_ENABLE,
0125                  (testcase & MWAIT_DISABLED) ? 0 : MSR_IA32_MISC_ENABLE_MWAIT);
0126     }
0127 
0128 done:
0129     kvm_vm_free(vm);
0130     return 0;
0131 }