xref: /kvm-unit-tests/x86/eventinj.c (revision 03b1e4570f9678c59a1fdcd0428d21681541602e)
1 #include "libcflat.h"
2 #include "vm.h"
3 #include "processor.h"
4 #include "desc.h"
5 #include "isr.h"
6 #include "apic.h"
7 #include "apic-defs.h"
8 #include "vmalloc.h"
9 #include "alloc_page.h"
10 #include "delay.h"
11 #include "fwcfg.h"
12 
13 #ifdef __x86_64__
14 #  define R "r"
15 #else
16 #  define R "e"
17 #endif
18 
19 void do_pf_tss(void);
20 
21 static void apic_self_ipi(u8 v)
22 {
23 	apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED |
24 		       APIC_INT_ASSERT | v, 0);
25 }
26 
27 static void apic_self_nmi(void)
28 {
29 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
30 }
31 
32 #define flush_phys_addr(__s) do {					\
33 		if (test_device_enabled())				\
34 			outl(__s, 0xe4);				\
35 	} while (0)
36 
37 #define flush_stack() do {						\
38 		int __l;						\
39 		flush_phys_addr(virt_to_phys(&__l));			\
40 	} while (0)
41 
42 extern char isr_iret_ip[];
43 
44 static void flush_idt_page(void)
45 {
46 	struct descriptor_table_ptr ptr;
47 	sidt(&ptr);
48 	flush_phys_addr(virt_to_phys((void*)ptr.base));
49 }
50 
51 static volatile unsigned int test_divider;
52 static volatile int test_count;
53 
54 ulong stack_phys;
55 void *stack_va;
56 
57 void do_pf_tss(void)
58 {
59 	printf("PF running\n");
60 	install_pte(phys_to_virt(read_cr3()), 1, stack_va,
61 		    stack_phys | PT_PRESENT_MASK | PT_WRITABLE_MASK, 0);
62 	invlpg(stack_va);
63 }
64 
65 extern void pf_tss(void);
66 
67 asm ("pf_tss: \n\t"
68 #ifdef __x86_64__
69         // no task on x86_64, save/restore caller-save regs
70         "push %rax; push %rcx; push %rdx; push %rsi; push %rdi\n"
71         "push %r8; push %r9; push %r10; push %r11\n"
72 #endif
73         "call do_pf_tss \n\t"
74 #ifdef __x86_64__
75         "pop %r11; pop %r10; pop %r9; pop %r8\n"
76         "pop %rdi; pop %rsi; pop %rdx; pop %rcx; pop %rax\n"
77 #endif
78         "add $"S", %"R "sp\n\t"	// discard error code
79         "iret"W" \n\t"
80         "jmp pf_tss\n\t"
81     );
82 
83 
84 #ifndef __x86_64__
85 static void of_isr(struct ex_regs *r)
86 {
87 	printf("OF isr running\n");
88 	test_count++;
89 }
90 #endif
91 
92 static void np_isr(struct ex_regs *r)
93 {
94 	printf("NP isr running %lx err=%lx\n", r->rip, r->error_code);
95 	set_idt_sel(33, read_cs());
96 	test_count++;
97 }
98 
99 static void de_isr(struct ex_regs *r)
100 {
101 	printf("DE isr running divider is %d\n", test_divider);
102 	test_divider = 10;
103 }
104 
105 static void bp_isr(struct ex_regs *r)
106 {
107 	printf("BP isr running\n");
108 	test_count++;
109 }
110 
111 static void nested_nmi_isr(struct ex_regs *r)
112 {
113 	printf("Nested NMI isr running rip=%lx\n", r->rip);
114 
115 	if (r->rip != (ulong)&isr_iret_ip)
116 		test_count++;
117 }
118 static void nmi_isr(struct ex_regs *r)
119 {
120 	printf("NMI isr running %p\n", &isr_iret_ip);
121 	test_count++;
122 	handle_exception(2, nested_nmi_isr);
123 	printf("Sending nested NMI to self\n");
124 	apic_self_nmi();
125 	io_delay();
126 	printf("After nested NMI to self\n");
127 }
128 
129 unsigned long *iret_stack;
130 
131 static void nested_nmi_iret_isr(struct ex_regs *r)
132 {
133 	printf("Nested NMI isr running rip=%lx\n", r->rip);
134 
135 	if (r->rip == iret_stack[-3])
136 		test_count++;
137 }
138 
139 extern void do_iret(ulong phys_stack, void *virt_stack);
140 
141 // Return to same privilege level won't pop SS or SP, so
142 // save it in RDX while we run on the nested stack
143 
144 extern bool no_test_device;
145 
146 asm("do_iret:"
147 #ifdef __x86_64__
148 	"mov %rdi, %rax \n\t"		// phys_stack
149 	"mov %rsi, %rdx \n\t"		// virt_stack
150 #else
151 	"mov 4(%esp), %eax \n\t"	// phys_stack
152 	"mov 8(%esp), %edx \n\t"	// virt_stack
153 #endif
154 	"xchg %"R "dx, %"R "sp \n\t"	// point to new stack
155 	"pushf"W" \n\t"
156 	"mov %cs, %ecx \n\t"
157 	"push"W" %"R "cx \n\t"
158 	"push"W" $2f \n\t"
159 
160 	"cmpb $0, no_test_device\n\t"	// see if need to flush
161 	"jnz 1f\n\t"
162 	"outl %eax, $0xe4 \n\t"		// flush page
163 	"1: \n\t"
164 	"iret"W" \n\t"
165 	"2: xchg %"R "dx, %"R "sp \n\t"	// point to old stack
166 	"ret\n\t"
167    );
168 
169 static void nmi_iret_isr(struct ex_regs *r)
170 {
171 	unsigned long *s = alloc_page();
172 	test_count++;
173 	printf("NMI isr running stack %p\n", s);
174 	handle_exception(2, nested_nmi_iret_isr);
175 	printf("Sending nested NMI to self\n");
176 	apic_self_nmi();
177 	printf("After nested NMI to self\n");
178 	iret_stack = &s[128];
179 	do_iret(virt_to_phys(s), iret_stack);
180 	printf("After iret\n");
181 }
182 
183 static void tirq0(isr_regs_t *r)
184 {
185 	printf("irq0 running\n");
186 	if (test_count == 1)
187 		test_count++;
188 	eoi();
189 }
190 
191 static void tirq1(isr_regs_t *r)
192 {
193 	printf("irq1 running\n");
194 	test_count++;
195 	eoi();
196 }
197 
198 ulong saved_stack;
199 
200 #define switch_stack(S) do {						\
201 		asm volatile ("mov %%" R "sp, %0":"=r"(saved_stack));	\
202 		asm volatile ("mov %0, %%" R "sp"::"r"(S));		\
203 	} while(0)
204 
205 #define restore_stack() do {						\
206 		asm volatile ("mov %0, %%" R "sp"::"r"(saved_stack));	\
207 	} while(0)
208 
209 int main(void)
210 {
211 	unsigned int res;
212 	ulong *pt, *cr3, i;
213 
214 	setup_vm();
215 	setup_idt();
216 	setup_alt_stack();
217 
218 	handle_irq(32, tirq0);
219 	handle_irq(33, tirq1);
220 
221 	/* generate HW exception that will fault on IDT and stack */
222 	handle_exception(0, de_isr);
223 	printf("Try to divide by 0\n");
224 	flush_idt_page();
225 	flush_stack();
226 	asm volatile ("divl %3": "=a"(res)
227 		      : "d"(0), "a"(1500), "m"(test_divider));
228 	printf("Result is %d\n", res);
229 	report("DE exception", res == 150);
230 
231 	/* generate soft exception (BP) that will fault on IDT and stack */
232 	test_count = 0;
233 	handle_exception(3, bp_isr);
234 	printf("Try int 3\n");
235 	flush_idt_page();
236 	flush_stack();
237 	asm volatile ("int $3");
238 	printf("After int 3\n");
239 	report("BP exception", test_count == 1);
240 
241 #ifndef __x86_64__
242 	/* generate soft exception (OF) that will fault on IDT */
243 	test_count = 0;
244 	handle_exception(4, of_isr);
245 	flush_idt_page();
246 	printf("Try into\n");
247 	asm volatile ("addb $127, %b0\ninto"::"a"(127));
248 	printf("After into\n");
249 	report("OF exception", test_count == 1);
250 
251 	/* generate soft exception (OF) using two bit instruction that will
252 	   fault on IDT */
253 	test_count = 0;
254 	handle_exception(4, of_isr);
255 	flush_idt_page();
256 	printf("Try into\n");
257 	asm volatile ("addb $127, %b0\naddr16 into"::"a"(127));
258 	printf("After into\n");
259 	report("2 byte OF exception", test_count == 1);
260 #endif
261 
262 	/* generate HW interrupt that will fault on IDT */
263 	test_count = 0;
264 	flush_idt_page();
265 	printf("Sending vec 33 to self\n");
266 	irq_enable();
267 	apic_self_ipi(33);
268 	io_delay();
269 	irq_disable();
270 	printf("After vec 33 to self\n");
271 	report("vec 33", test_count == 1);
272 
273 	/* generate soft interrupt that will fault on IDT and stack */
274 	test_count = 0;
275 	flush_idt_page();
276 	printf("Try int $33\n");
277 	flush_stack();
278 	asm volatile ("int $33");
279 	printf("After int $33\n");
280 	report("int $33", test_count == 1);
281 
282 	/* Inject two HW interrupt than open iterrupt windows. Both interrupt
283 	   will fault on IDT access */
284 	test_count = 0;
285 	flush_idt_page();
286 	printf("Sending vec 32 and 33 to self\n");
287 	apic_self_ipi(32);
288 	apic_self_ipi(33);
289 	io_delay();
290 	irq_enable();
291 	asm volatile("nop");
292 	irq_disable();
293 	printf("After vec 32 and 33 to self\n");
294 	report("vec 32/33", test_count == 2);
295 
296 
297 	/* Inject HW interrupt, do sti and than (while in irq shadow) inject
298 	   soft interrupt. Fault during soft interrupt. Soft interrup shoud be
299 	   handled before HW interrupt */
300 	test_count = 0;
301 	flush_idt_page();
302 	printf("Sending vec 32 and int $33\n");
303 	apic_self_ipi(32);
304 	flush_stack();
305 	io_delay();
306 	asm volatile ("sti; int $33");
307 	irq_disable();
308 	printf("After vec 32 and int $33\n");
309 	report("vec 32/int $33", test_count == 2);
310 
311 	/* test that TPR is honored */
312 	test_count = 0;
313 	handle_irq(62, tirq1);
314 	flush_idt_page();
315 	printf("Sending vec 33 and 62 and mask one with TPR\n");
316 	apic_write(APIC_TASKPRI, 0xf << 4);
317 	irq_enable();
318 	apic_self_ipi(32);
319 	apic_self_ipi(62);
320 	io_delay();
321 	apic_write(APIC_TASKPRI, 0x2 << 4);
322 	printf("After 33/62 TPR test\n");
323 	report("TPR", test_count == 1);
324 	apic_write(APIC_TASKPRI, 0x0);
325 	while(test_count != 2); /* wait for second irq */
326 	irq_disable();
327 
328 	/* test fault durint NP delivery */
329 	printf("Before NP test\n");
330 	test_count = 0;
331 	handle_exception(11, np_isr);
332 	set_idt_sel(33, NP_SEL);
333 	flush_idt_page();
334 	flush_stack();
335 	asm volatile ("int $33");
336 	printf("After int33\n");
337 	report("NP exception", test_count == 2);
338 
339 	/* generate NMI that will fault on IDT */
340 	test_count = 0;
341 	handle_exception(2, nmi_isr);
342 	flush_idt_page();
343 	printf("Sending NMI to self\n");
344 	apic_self_nmi();
345 	printf("After NMI to self\n");
346 	/* this is needed on VMX without NMI window notification.
347 	   Interrupt windows is used instead, so let pending NMI
348 	   to be injected */
349 	irq_enable();
350 	asm volatile ("nop");
351 	irq_disable();
352 	report("NMI", test_count == 2);
353 
354 	/* generate NMI that will fault on IRET */
355 	printf("Before NMI IRET test\n");
356 	test_count = 0;
357 	handle_exception(2, nmi_iret_isr);
358 	printf("Sending NMI to self\n");
359 	apic_self_nmi();
360 	/* this is needed on VMX without NMI window notification.
361 	   Interrupt windows is used instead, so let pending NMI
362 	   to be injected */
363 	irq_enable();
364 	asm volatile ("nop");
365 	irq_disable();
366 	printf("After NMI to self\n");
367 	report("NMI", test_count == 2);
368 	stack_phys = (ulong)virt_to_phys(alloc_page());
369 	stack_va = alloc_vpage();
370 
371 	/* Generate DE and PF exceptions serially */
372 	test_divider = 0;
373 	set_intr_alt_stack(14, pf_tss);
374 	handle_exception(0, de_isr);
375 	printf("Try to divide by 0\n");
376 	/* install read only pte */
377 	install_pte(phys_to_virt(read_cr3()), 1, stack_va,
378 		    stack_phys | PT_PRESENT_MASK, 0);
379 	invlpg(stack_va);
380 	flush_phys_addr(stack_phys);
381 	switch_stack(stack_va + 4095);
382 	flush_idt_page();
383 	asm volatile ("divl %3": "=a"(res)
384 		      : "d"(0), "a"(1500), "m"(test_divider));
385 	restore_stack();
386 	printf("Result is %d\n", res);
387 	report("DE PF exceptions", res == 150);
388 
389 	/* Generate NP and PF exceptions serially */
390 	printf("Before NP test\n");
391 	test_count = 0;
392 	set_intr_alt_stack(14, pf_tss);
393 	handle_exception(11, np_isr);
394 	set_idt_sel(33, NP_SEL);
395 	/* install read only pte */
396 	install_pte(phys_to_virt(read_cr3()), 1, stack_va,
397 		    stack_phys | PT_PRESENT_MASK, 0);
398 	invlpg(stack_va);
399 	flush_idt_page();
400 	flush_phys_addr(stack_phys);
401 	switch_stack(stack_va + 4095);
402 	asm volatile ("int $33");
403 	restore_stack();
404 	printf("After int33\n");
405 	report("NP PF exceptions", test_count == 2);
406 
407 	pt = alloc_page();
408 	cr3 = (void*)read_cr3();
409 	/* use shadowed stack during interrupt delivery */
410 	for (i = 0; i < 4096/sizeof(ulong); i++) {
411 		if (!cr3[i]) {
412 			cr3[i] = virt_to_phys(pt) | PT_PRESENT_MASK | PT_WRITABLE_MASK;
413 			pt[0] = virt_to_phys(pt) | PT_PRESENT_MASK | PT_WRITABLE_MASK;
414 #ifndef __x86_64__
415 			((ulong*)(i<<22))[1] = 0;
416 #else
417 			((ulong*)(i<<39))[1] = 0;
418 #endif
419 			write_cr3(virt_to_phys(cr3));
420 			break;
421 		}
422 	}
423 	test_count = 0;
424 	printf("Try int 33 with shadowed stack\n");
425 	switch_stack(((char*)pt) + 4095);
426 	asm volatile("int $33");
427 	restore_stack();
428 	printf("After int 33 with shadowed stack\n");
429 	report("int 33 with shadowed stack", test_count == 1);
430 
431 	return report_summary();
432 }
433