xref: /linux/tools/testing/selftests/kvm/x86/xapic_tpr_test.c (revision 1fd1dc41724319406b0aff221a352a400b0ddfc5)
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <fcntl.h>
3 #include <stdatomic.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <sys/ioctl.h>
8 #include <unistd.h>
9 
10 #include "apic.h"
11 #include "kvm_util.h"
12 #include "processor.h"
13 #include "test_util.h"
14 
15 static bool is_x2apic;
16 
17 #define IRQ_VECTOR 0x20
18 
19 /* See also the comment at similar assertion in memslot_perf_test.c */
20 static_assert(ATOMIC_INT_LOCK_FREE == 2, "atomic int is not lockless");
21 
22 static atomic_uint tpr_guest_irq_sync_val;
23 
24 static void tpr_guest_irq_sync_flag_reset(void)
25 {
26 	atomic_store_explicit(&tpr_guest_irq_sync_val, 0,
27 			      memory_order_release);
28 }
29 
30 static unsigned int tpr_guest_irq_sync_val_get(void)
31 {
32 	return atomic_load_explicit(&tpr_guest_irq_sync_val,
33 				    memory_order_acquire);
34 }
35 
36 static void tpr_guest_irq_sync_val_inc(void)
37 {
38 	atomic_fetch_add_explicit(&tpr_guest_irq_sync_val, 1,
39 				  memory_order_acq_rel);
40 }
41 
42 static void tpr_guest_irq_handler_xapic(struct ex_regs *regs)
43 {
44 	tpr_guest_irq_sync_val_inc();
45 
46 	xapic_write_reg(APIC_EOI, 0);
47 }
48 
49 static void tpr_guest_irq_handler_x2apic(struct ex_regs *regs)
50 {
51 	tpr_guest_irq_sync_val_inc();
52 
53 	x2apic_write_reg(APIC_EOI, 0);
54 }
55 
56 static void tpr_guest_irq_queue(void)
57 {
58 	if (is_x2apic) {
59 		x2apic_write_reg(APIC_SELF_IPI, IRQ_VECTOR);
60 	} else {
61 		uint32_t icr, icr2;
62 
63 		icr = APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED |
64 			IRQ_VECTOR;
65 		icr2 = 0;
66 
67 		xapic_write_reg(APIC_ICR2, icr2);
68 		xapic_write_reg(APIC_ICR, icr);
69 	}
70 }
71 
72 static uint8_t tpr_guest_tpr_get(void)
73 {
74 	uint32_t taskpri;
75 
76 	if (is_x2apic)
77 		taskpri = x2apic_read_reg(APIC_TASKPRI);
78 	else
79 		taskpri = xapic_read_reg(APIC_TASKPRI);
80 
81 	return GET_APIC_PRI(taskpri);
82 }
83 
84 static uint8_t tpr_guest_ppr_get(void)
85 {
86 	uint32_t procpri;
87 
88 	if (is_x2apic)
89 		procpri = x2apic_read_reg(APIC_PROCPRI);
90 	else
91 		procpri = xapic_read_reg(APIC_PROCPRI);
92 
93 	return GET_APIC_PRI(procpri);
94 }
95 
96 static uint8_t tpr_guest_cr8_get(void)
97 {
98 	uint64_t cr8;
99 
100 	asm volatile ("mov %%cr8, %[cr8]\n\t" : [cr8] "=r"(cr8));
101 
102 	return cr8 & GENMASK(3, 0);
103 }
104 
105 static void tpr_guest_check_tpr_ppr_cr8_equal(void)
106 {
107 	uint8_t tpr;
108 
109 	tpr = tpr_guest_tpr_get();
110 
111 	GUEST_ASSERT_EQ(tpr_guest_ppr_get(), tpr);
112 	GUEST_ASSERT_EQ(tpr_guest_cr8_get(), tpr);
113 }
114 
115 static void tpr_guest_code(void)
116 {
117 	cli();
118 
119 	if (is_x2apic)
120 		x2apic_enable();
121 	else
122 		xapic_enable();
123 
124 	GUEST_ASSERT_EQ(tpr_guest_tpr_get(), 0);
125 	tpr_guest_check_tpr_ppr_cr8_equal();
126 
127 	tpr_guest_irq_queue();
128 
129 	/* TPR = 0 but IRQ masked by IF=0, should not fire */
130 	udelay(1000);
131 	GUEST_ASSERT_EQ(tpr_guest_irq_sync_val_get(), 0);
132 
133 	sti();
134 
135 	/* IF=1 now, IRQ should fire */
136 	while (tpr_guest_irq_sync_val_get() == 0)
137 		cpu_relax();
138 	GUEST_ASSERT_EQ(tpr_guest_irq_sync_val_get(), 1);
139 
140 	GUEST_SYNC(true);
141 	tpr_guest_check_tpr_ppr_cr8_equal();
142 
143 	tpr_guest_irq_queue();
144 
145 	/* IRQ masked by barely high enough TPR now, should not fire */
146 	udelay(1000);
147 	GUEST_ASSERT_EQ(tpr_guest_irq_sync_val_get(), 1);
148 
149 	GUEST_SYNC(false);
150 	tpr_guest_check_tpr_ppr_cr8_equal();
151 
152 	/* TPR barely low enough now to unmask IRQ, should fire */
153 	while (tpr_guest_irq_sync_val_get() == 1)
154 		cpu_relax();
155 	GUEST_ASSERT_EQ(tpr_guest_irq_sync_val_get(), 2);
156 
157 	GUEST_DONE();
158 }
159 
160 static uint8_t lapic_tpr_get(struct kvm_lapic_state *xapic)
161 {
162 	return GET_APIC_PRI(*((u32 *)&xapic->regs[APIC_TASKPRI]));
163 }
164 
165 static void lapic_tpr_set(struct kvm_lapic_state *xapic, uint8_t val)
166 {
167 	u32 *taskpri = (u32 *)&xapic->regs[APIC_TASKPRI];
168 
169 	*taskpri = SET_APIC_PRI(*taskpri, val);
170 }
171 
172 static uint8_t sregs_tpr(struct kvm_sregs *sregs)
173 {
174 	return sregs->cr8 & GENMASK(3, 0);
175 }
176 
177 static void test_tpr_check_tpr_zero(struct kvm_vcpu *vcpu)
178 {
179 	struct kvm_lapic_state xapic;
180 
181 	vcpu_ioctl(vcpu, KVM_GET_LAPIC, &xapic);
182 
183 	TEST_ASSERT_EQ(lapic_tpr_get(&xapic), 0);
184 }
185 
186 static void test_tpr_check_tpr_cr8_equal(struct kvm_vcpu *vcpu)
187 {
188 	struct kvm_sregs sregs;
189 	struct kvm_lapic_state xapic;
190 
191 	vcpu_sregs_get(vcpu, &sregs);
192 	vcpu_ioctl(vcpu, KVM_GET_LAPIC, &xapic);
193 
194 	TEST_ASSERT_EQ(sregs_tpr(&sregs), lapic_tpr_get(&xapic));
195 }
196 
197 static void test_tpr_set_tpr_for_irq(struct kvm_vcpu *vcpu, bool mask)
198 {
199 	struct kvm_lapic_state xapic;
200 	uint8_t tpr;
201 
202 	static_assert(IRQ_VECTOR >= 16, "invalid IRQ vector number");
203 	tpr = IRQ_VECTOR / 16;
204 	if (!mask)
205 		tpr--;
206 
207 	vcpu_ioctl(vcpu, KVM_GET_LAPIC, &xapic);
208 	lapic_tpr_set(&xapic, tpr);
209 	vcpu_ioctl(vcpu, KVM_SET_LAPIC, &xapic);
210 }
211 
212 static void test_tpr(bool __is_x2apic)
213 {
214 	struct kvm_vcpu *vcpu;
215 	struct kvm_vm *vm;
216 	bool done = false;
217 
218 	is_x2apic = __is_x2apic;
219 
220 	vm = vm_create_with_one_vcpu(&vcpu, tpr_guest_code);
221 	if (is_x2apic) {
222 		vm_install_exception_handler(vm, IRQ_VECTOR,
223 					     tpr_guest_irq_handler_x2apic);
224 	} else {
225 		vm_install_exception_handler(vm, IRQ_VECTOR,
226 					     tpr_guest_irq_handler_xapic);
227 		vcpu_clear_cpuid_feature(vcpu, X86_FEATURE_X2APIC);
228 		virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA);
229 	}
230 
231 	sync_global_to_guest(vcpu->vm, is_x2apic);
232 
233 	/* According to the SDM/APM the TPR value at reset is 0 */
234 	test_tpr_check_tpr_zero(vcpu);
235 	test_tpr_check_tpr_cr8_equal(vcpu);
236 
237 	tpr_guest_irq_sync_flag_reset();
238 	sync_global_to_guest(vcpu->vm, tpr_guest_irq_sync_val);
239 
240 	while (!done) {
241 		struct ucall uc;
242 
243 		alarm(2);
244 		vcpu_run(vcpu);
245 		alarm(0);
246 
247 		switch (get_ucall(vcpu, &uc)) {
248 		case UCALL_ABORT:
249 			REPORT_GUEST_ASSERT(uc);
250 			break;
251 		case UCALL_DONE:
252 			test_tpr_check_tpr_cr8_equal(vcpu);
253 			done = true;
254 			break;
255 		case UCALL_SYNC:
256 			test_tpr_check_tpr_cr8_equal(vcpu);
257 			test_tpr_set_tpr_for_irq(vcpu, uc.args[1]);
258 			break;
259 		default:
260 			TEST_FAIL("Unknown ucall result 0x%lx", uc.cmd);
261 			break;
262 		}
263 	}
264 	kvm_vm_free(vm);
265 }
266 
267 int main(int argc, char *argv[])
268 {
269 	/*
270 	 * Use separate VMs for the xAPIC and x2APIC tests so that x2APIC can
271 	 * be fully hidden from the guest.  KVM disallows changing CPUID after
272 	 * KVM_RUN and AVIC is disabled if _any_ vCPU is allowed to use x2APIC.
273 	 */
274 	test_tpr(false);
275 	test_tpr(true);
276 }
277