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