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 32 x2apic_write_reg(APIC_TASKPRI, 0xf0); 33 GUEST_ASSERT_EQ(x2apic_read_reg(APIC_TASKPRI), 0xf0); 34 35 asm volatile("cpuid" ::: "eax", "ebx", "ecx", "edx"); 36 } 37 38 static void l1_svm_code(struct svm_test_data *svm) 39 { 40 struct vmcb_control_area *ctrl = &svm->vmcb->control; 41 42 generic_svm_setup(svm, l2_guest_code); 43 ctrl->intercept |= BIT_ULL(INTERCEPT_CPUID) | BIT_ULL(INTERCEPT_MSR_PROT); 44 45 run_guest(svm->vmcb, svm->vmcb_gpa); 46 GUEST_ASSERT_EQ(ctrl->exit_code, SVM_EXIT_CPUID); 47 48 stgi(); 49 } 50 51 static void l1_vmx_code(struct vmx_pages *vmx) 52 { 53 u64 control; 54 55 GUEST_ASSERT_EQ(prepare_for_vmx_operation(vmx), true); 56 GUEST_ASSERT_EQ(load_vmcs(vmx), true); 57 58 prepare_vmcs(vmx, NULL); 59 GUEST_ASSERT_EQ(vmwrite(GUEST_RIP, (unsigned long)l2_guest_code), 0); 60 61 control = vmreadz(CPU_BASED_VM_EXEC_CONTROL); 62 control |= CPU_BASED_USE_MSR_BITMAPS; 63 GUEST_ASSERT_EQ(vmwrite(CPU_BASED_VM_EXEC_CONTROL, control), 0); 64 65 GUEST_ASSERT(!vmlaunch()); 66 GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_CPUID); 67 } 68 69 static void l1_guest_code(void *test_data) 70 { 71 x2apic_enable(); 72 73 if (this_cpu_has(X86_FEATURE_SVM)) 74 l1_svm_code(test_data); 75 else 76 l1_vmx_code(test_data); 77 78 sti_nop(); 79 80 GUEST_ASSERT_EQ(x2apic_read_reg(APIC_TASKPRI), 0xf0); 81 82 x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | POSTED_INTR_VECTOR); 83 GUEST_ASSERT_EQ(nr_irqs, 0); 84 85 x2apic_write_reg(APIC_TASKPRI, 0xff); 86 GUEST_ASSERT_EQ(x2apic_read_reg(APIC_TASKPRI), 0xff); 87 x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | POSTED_INTR_WAKEUP_VECTOR); 88 GUEST_ASSERT_EQ(nr_irqs, 0); 89 90 x2apic_write_reg(APIC_TASKPRI, 0); 91 GUEST_ASSERT_EQ(x2apic_read_reg(APIC_TASKPRI), 0); 92 93 x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | POSTED_INTR_NESTED_VECTOR); 94 GUEST_ASSERT_EQ(nr_irqs, 3); 95 96 GUEST_DONE(); 97 } 98 99 static void __test_x2apic_intercepts(void) 100 { 101 gva_t nested_test_data_gva; 102 struct kvm_vcpu *vcpu; 103 struct kvm_vm *vm; 104 struct ucall uc; 105 106 vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); 107 vm_install_exception_handler(vm, POSTED_INTR_VECTOR, guest_irq_handler); 108 vm_install_exception_handler(vm, POSTED_INTR_WAKEUP_VECTOR, guest_irq_handler); 109 vm_install_exception_handler(vm, POSTED_INTR_NESTED_VECTOR, guest_irq_handler); 110 111 sync_global_to_guest(vm, inhibit_apicv); 112 113 if (kvm_cpu_has(X86_FEATURE_SVM)) 114 vcpu_alloc_svm(vm, &nested_test_data_gva); 115 else 116 vcpu_alloc_vmx(vm, &nested_test_data_gva); 117 118 vcpu_args_set(vcpu, 1, nested_test_data_gva); 119 120 vcpu_run(vcpu); 121 122 TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); 123 124 switch (get_ucall(vcpu, &uc)) { 125 case UCALL_DONE: 126 break; 127 case UCALL_ABORT: 128 REPORT_GUEST_ASSERT(uc); 129 break; 130 default: 131 TEST_FAIL("Expected DONE, got unexpected ucall %lu", uc.cmd); 132 } 133 134 kvm_vm_free(vm); 135 } 136 137 #define test_x2apic_intercepts(inhibit_apic_setting) \ 138 do { \ 139 inhibit_apic_setting; \ 140 \ 141 __test_x2apic_intercepts(); \ 142 } while (0) 143 144 int main(int argc, char *argv[]) 145 { 146 TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM) || kvm_cpu_has(X86_FEATURE_VMX)); 147 148 test_x2apic_intercepts(inhibit_apicv = true); 149 test_x2apic_intercepts(inhibit_apicv = false); 150 } 151