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