xref: /linux/tools/testing/selftests/kvm/x86/nested_x2apic_test.c (revision 2a31b5378c6db9c4a8d399e5d10d32cd4b732bef)
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 	for (;;) {
33 		x2apic_write_reg(APIC_TASKPRI, 0xf0);
34 		GUEST_ASSERT_EQ(x2apic_read_reg(APIC_TASKPRI), 0xf0);
35 
36 		asm volatile("cpuid" ::: "eax", "ebx", "ecx", "edx");
37 	}
38 }
39 
40 static void l1_svm_code(struct svm_test_data *svm)
41 {
42 	struct vmcb_control_area *ctrl = &svm->vmcb->control;
43 
44 	generic_svm_setup(svm, l2_guest_code);
45 	ctrl->intercept |= BIT_ULL(INTERCEPT_CPUID) | BIT_ULL(INTERCEPT_MSR_PROT);
46 
47 	run_guest(svm->vmcb, svm->vmcb_gpa);
48 	GUEST_ASSERT_EQ(ctrl->exit_code, SVM_EXIT_CPUID);
49 
50 	stgi();
51 	x2apic_write_reg(APIC_TASKPRI, 0);
52 }
53 
54 static void l1_vmx_code(struct vmx_pages *vmx, struct hyperv_test_pages *hv_pages)
55 {
56 	u64 control;
57 
58 	if (hv_pages) {
59 		wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID);
60 		enable_vp_assist(hv_pages->vp_assist_gpa, hv_pages->vp_assist);
61 		evmcs_enable();
62 	}
63 
64 	GUEST_ASSERT_EQ(prepare_for_vmx_operation(vmx), true);
65 
66 	if (hv_pages) {
67 		GUEST_ASSERT(load_evmcs(hv_pages));
68 		current_evmcs->hv_enlightenments_control.msr_bitmap = 1;
69 	} else {
70 		GUEST_ASSERT(load_vmcs(vmx));
71 	}
72 
73 	prepare_vmcs(vmx, NULL);
74 	GUEST_ASSERT_EQ(vmwrite(GUEST_RIP, (unsigned long)l2_guest_code), 0);
75 
76 	control = vmreadz(PIN_BASED_VM_EXEC_CONTROL);
77 	control |= PIN_BASED_EXT_INTR_MASK;
78 	vmwrite(PIN_BASED_VM_EXEC_CONTROL, control);
79 
80 	control = vmreadz(CPU_BASED_VM_EXEC_CONTROL);
81 	control |= CPU_BASED_USE_MSR_BITMAPS | CPU_BASED_TPR_SHADOW;
82 	GUEST_ASSERT_EQ(vmwrite(CPU_BASED_VM_EXEC_CONTROL, control), 0);
83 
84 	control = vmreadz(SECONDARY_VM_EXEC_CONTROL);
85 	control |= SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE |
86 		   SECONDARY_EXEC_APIC_REGISTER_VIRT |
87 		   SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY;
88 	control &= (rdmsr(MSR_IA32_VMX_PROCBASED_CTLS2) >> 32);
89 	GUEST_ASSERT_EQ(vmwrite(SECONDARY_VM_EXEC_CONTROL, control), 0);
90 
91 	GUEST_ASSERT(!vmlaunch());
92 	GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_CPUID);
93 	GUEST_ASSERT_EQ(vmwrite(GUEST_RIP,
94 			vmreadz(GUEST_RIP) + vmreadz(VM_EXIT_INSTRUCTION_LEN)), 0);
95 }
96 
97 static void l1_vmx_code_part2(void)
98 {
99 	u64 control;
100 
101 	control = vmreadz(CPU_BASED_VM_EXEC_CONTROL);
102 	control &= ~CPU_BASED_TPR_SHADOW;
103 	GUEST_ASSERT_EQ(vmwrite(CPU_BASED_VM_EXEC_CONTROL, control), 0);
104 
105 	control = vmreadz(SECONDARY_VM_EXEC_CONTROL);
106 	control &= ~(SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE |
107 			SECONDARY_EXEC_APIC_REGISTER_VIRT |
108 			SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY);
109 	GUEST_ASSERT_EQ(vmwrite(SECONDARY_VM_EXEC_CONTROL, control), 0);
110 
111 	GUEST_ASSERT(!vmresume());
112 	GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_CPUID);
113 	GUEST_ASSERT_EQ(vmwrite(GUEST_RIP,
114 			vmreadz(GUEST_RIP) + vmreadz(VM_EXIT_INSTRUCTION_LEN)), 0);
115 }
116 
117 static void l1_test_x2apic_intercepts(void)
118 {
119 	GUEST_ASSERT_EQ(nr_irqs, 0);
120 
121 	sti_nop();
122 
123 	GUEST_ASSERT_EQ(x2apic_read_reg(APIC_TASKPRI), 0xf0);
124 
125 	x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | POSTED_INTR_VECTOR);
126 	GUEST_ASSERT_EQ(nr_irqs, 0);
127 
128 	x2apic_write_reg(APIC_TASKPRI, 0xff);
129 	GUEST_ASSERT_EQ(x2apic_read_reg(APIC_TASKPRI), 0xff);
130 	x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | POSTED_INTR_WAKEUP_VECTOR);
131 	GUEST_ASSERT_EQ(nr_irqs, 0);
132 
133 	x2apic_write_reg(APIC_TASKPRI, 0);
134 	GUEST_ASSERT_EQ(x2apic_read_reg(APIC_TASKPRI), 0);
135 
136 	x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | POSTED_INTR_NESTED_VECTOR);
137 	GUEST_ASSERT_EQ(nr_irqs, 3);
138 
139 	nr_irqs = 0;
140 }
141 
142 static void l1_guest_code(void *test_data, void *hv_pages)
143 {
144 	x2apic_enable();
145 
146 	if (this_cpu_has(X86_FEATURE_SVM))
147 		l1_svm_code(test_data);
148 	else
149 		l1_vmx_code(test_data, hv_pages);
150 
151 	GUEST_ASSERT_EQ(x2apic_read_reg(APIC_TASKPRI), 0);
152 	x2apic_write_reg(APIC_TASKPRI, 0xf0);
153 
154 	l1_test_x2apic_intercepts();
155 
156 	if (this_cpu_has(X86_FEATURE_VMX)) {
157 		l1_vmx_code_part2();
158 		l1_test_x2apic_intercepts();
159 	}
160 
161 	GUEST_DONE();
162 }
163 
164 static void test_x2apic_intercepts(bool with_inhibit_apicv, bool use_evmcs)
165 {
166 	gva_t nested_test_data_gva, hv_pages_gva = 0;
167 	struct kvm_vcpu *vcpu;
168 	struct kvm_vm *vm;
169 	struct ucall uc;
170 
171 	inhibit_apicv = with_inhibit_apicv;
172 
173 	vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
174 	vm_install_exception_handler(vm, POSTED_INTR_VECTOR, guest_irq_handler);
175 	vm_install_exception_handler(vm, POSTED_INTR_WAKEUP_VECTOR, guest_irq_handler);
176 	vm_install_exception_handler(vm, POSTED_INTR_NESTED_VECTOR, guest_irq_handler);
177 
178 	sync_global_to_guest(vm, inhibit_apicv);
179 
180 	if (kvm_cpu_has(X86_FEATURE_SVM))
181 		vcpu_alloc_svm(vm, &nested_test_data_gva);
182 	else
183 		vcpu_alloc_vmx(vm, &nested_test_data_gva);
184 
185 	if (use_evmcs) {
186 		vcpu_set_hv_cpuid(vcpu);
187 		vcpu_enable_evmcs(vcpu);
188 
189 		vcpu_alloc_hyperv_test_pages(vm, &hv_pages_gva);
190 	}
191 
192 	vcpu_args_set(vcpu, 2, nested_test_data_gva, hv_pages_gva);
193 
194 	vcpu_run(vcpu);
195 
196 	TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
197 
198 	switch (get_ucall(vcpu, &uc)) {
199 	case UCALL_DONE:
200 		break;
201 	case UCALL_ABORT:
202 		REPORT_GUEST_ASSERT(uc);
203 		break;
204 	default:
205 		TEST_FAIL("Expected DONE, got unexpected ucall %lu", uc.cmd);
206 	}
207 
208 	kvm_vm_free(vm);
209 }
210 
211 int main(int argc, char *argv[])
212 {
213 	TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM) || kvm_cpu_has(X86_FEATURE_VMX));
214 
215 	test_x2apic_intercepts(true, false);
216 	test_x2apic_intercepts(false, false);
217 
218 	if (kvm_has_cap(KVM_CAP_HYPERV_ENLIGHTENED_VMCS)) {
219 		test_x2apic_intercepts(true, true);
220 		test_x2apic_intercepts(false, true);
221 	}
222 }
223