1 // SPDX-License-Identifier: GPL-2.0-only 2 #include "test_util.h" 3 #include "kvm_util.h" 4 #include "processor.h" 5 #include "vmx.h" 6 #include "svm_util.h" 7 8 /* 9 * Use the kernel's posted interrupt vectors to minimize the risk of crashing 10 * the host if KVM is buggy. Note, the vectors aren't set in stone, ideally 11 * these will be kept up-to-date if the kernel vectors change, but it's "fine" 12 * if they are stale. 13 */ 14 #define POSTED_INTR_VECTOR 0xf2 15 #define POSTED_INTR_WAKEUP_VECTOR 0xf1 16 #define POSTED_INTR_NESTED_VECTOR 0xf0 17 18 static bool inhibit_apicv; 19 static volatile unsigned int nr_irqs; 20 21 static void guest_irq_handler(struct ex_regs *regs) 22 { 23 nr_irqs++; 24 x2apic_write_reg(APIC_EOI, 0); 25 } 26 27 static void l2_guest_code(void) 28 { 29 if (inhibit_apicv) 30 wrmsr(MSR_IA32_APICBASE, rdmsr(MSR_IA32_APICBASE) & GENMASK_ULL(11, 0)); 31 asm volatile("cpuid" ::: "eax", "ebx", "ecx", "edx"); 32 } 33 34 static void l1_svm_code(struct svm_test_data *svm) 35 { 36 struct vmcb_control_area *ctrl = &svm->vmcb->control; 37 38 generic_svm_setup(svm, l2_guest_code); 39 ctrl->intercept |= BIT_ULL(INTERCEPT_CPUID) | BIT_ULL(INTERCEPT_MSR_PROT); 40 41 run_guest(svm->vmcb, svm->vmcb_gpa); 42 GUEST_ASSERT_EQ(ctrl->exit_code, SVM_EXIT_CPUID); 43 44 stgi(); 45 } 46 47 static void l1_vmx_code(struct vmx_pages *vmx) 48 { 49 u64 control; 50 51 GUEST_ASSERT_EQ(prepare_for_vmx_operation(vmx), true); 52 GUEST_ASSERT_EQ(load_vmcs(vmx), true); 53 54 prepare_vmcs(vmx, NULL); 55 GUEST_ASSERT_EQ(vmwrite(GUEST_RIP, (unsigned long)l2_guest_code), 0); 56 57 control = vmreadz(CPU_BASED_VM_EXEC_CONTROL); 58 control |= CPU_BASED_USE_MSR_BITMAPS; 59 GUEST_ASSERT_EQ(vmwrite(CPU_BASED_VM_EXEC_CONTROL, control), 0); 60 61 GUEST_ASSERT(!vmlaunch()); 62 GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_CPUID); 63 } 64 65 static void l1_guest_code(void *test_data) 66 { 67 x2apic_enable(); 68 69 if (this_cpu_has(X86_FEATURE_SVM)) 70 l1_svm_code(test_data); 71 else 72 l1_vmx_code(test_data); 73 74 sti_nop(); 75 76 x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | POSTED_INTR_VECTOR); 77 x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | POSTED_INTR_WAKEUP_VECTOR); 78 x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | POSTED_INTR_NESTED_VECTOR); 79 GUEST_ASSERT_EQ(nr_irqs, 3); 80 GUEST_DONE(); 81 } 82 83 static void __test_x2apic_intercepts(void) 84 { 85 gva_t nested_test_data_gva; 86 struct kvm_vcpu *vcpu; 87 struct kvm_vm *vm; 88 struct ucall uc; 89 90 vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); 91 vm_install_exception_handler(vm, POSTED_INTR_VECTOR, guest_irq_handler); 92 vm_install_exception_handler(vm, POSTED_INTR_WAKEUP_VECTOR, guest_irq_handler); 93 vm_install_exception_handler(vm, POSTED_INTR_NESTED_VECTOR, guest_irq_handler); 94 95 sync_global_to_guest(vm, inhibit_apicv); 96 97 if (kvm_cpu_has(X86_FEATURE_SVM)) 98 vcpu_alloc_svm(vm, &nested_test_data_gva); 99 else 100 vcpu_alloc_vmx(vm, &nested_test_data_gva); 101 102 vcpu_args_set(vcpu, 1, nested_test_data_gva); 103 104 vcpu_run(vcpu); 105 106 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); 107 108 switch (get_ucall(vcpu, &uc)) { 109 case UCALL_DONE: 110 break; 111 case UCALL_ABORT: 112 REPORT_GUEST_ASSERT(uc); 113 break; 114 default: 115 TEST_FAIL("Expected DONE, got unexpected ucall %lu", uc.cmd); 116 } 117 118 kvm_vm_free(vm); 119 } 120 121 #define test_x2apic_intercepts(inhibit_apic_setting) \ 122 do { \ 123 inhibit_apic_setting; \ 124 \ 125 __test_x2apic_intercepts(); \ 126 } while (0) 127 128 int main(int argc, char *argv[]) 129 { 130 TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM) || kvm_cpu_has(X86_FEATURE_VMX)); 131 132 test_x2apic_intercepts(inhibit_apicv = true); 133 test_x2apic_intercepts(inhibit_apicv = false); 134 } 135