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
guest_irq_handler(struct ex_regs * regs)21 static void guest_irq_handler(struct ex_regs *regs)
22 {
23 nr_irqs++;
24 x2apic_write_reg(APIC_EOI, 0);
25 }
26
l2_guest_code(void)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
l1_svm_code(struct svm_test_data * svm)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
l1_vmx_code(struct vmx_pages * vmx,struct hyperv_test_pages * hv_pages)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
l1_vmx_code_part2(void)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
l1_test_x2apic_intercepts(void)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
l1_guest_code(void * test_data,void * hv_pages)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
test_x2apic_intercepts(bool with_inhibit_apicv,bool use_evmcs)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
main(int argc,char * argv[])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