xref: /linux/tools/testing/selftests/kvm/x86/nested_x2apic_test.c (revision 0c19bc0ef11d8ea7f929a3ec3ce549cfe0781859)
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