[COMMIT master] Add event injection test

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Gleb Natapov <gleb@xxxxxxxxxx>

Add various event injection test. Those tests use testdev to unmap
pages from shadow pages/ept tables which make it possible to test
rare scenarios.

[avi: my compiler didn't like "foo"R"bar", added spaces]

Signed-off-by: Gleb Natapov <gleb@xxxxxxxxxx>
Signed-off-by: Avi Kivity <avi@xxxxxxxxxx>

diff --git a/config-x86-common.mak b/config-x86-common.mak
index 17769fa..cca8f87 100644
--- a/config-x86-common.mak
+++ b/config-x86-common.mak
@@ -33,7 +33,7 @@ tests-common = $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \
                $(TEST_DIR)/smptest.flat  $(TEST_DIR)/port80.flat \
                $(TEST_DIR)/realmode.flat $(TEST_DIR)/msr.flat \
                $(TEST_DIR)/hypercall.flat $(TEST_DIR)/sieve.flat \
-               $(TEST_DIR)/kvmclock_test.flat
+               $(TEST_DIR)/kvmclock_test.flat  $(TEST_DIR)/eventinj.flat
 
 tests-common += api/api-sample
 tests-common += api/dirty-log
@@ -80,6 +80,8 @@ $(TEST_DIR)/svm.elf: $(cstart.o)
 $(TEST_DIR)/kvmclock_test.elf: $(cstart.o) $(TEST_DIR)/kvmclock.o \
                                 $(TEST_DIR)/kvmclock_test.o
 
+$(TEST_DIR)/eventinj.elf: $(cstart.o) $(TEST_DIR)/eventinj.o
+
 arch_clean:
 	$(RM) $(TEST_DIR)/*.o $(TEST_DIR)/*.flat $(TEST_DIR)/*.elf \
 	$(TEST_DIR)/.*.d $(TEST_DIR)/lib/.*.d $(TEST_DIR)/lib/*.o
diff --git a/lib/x86/desc.c b/lib/x86/desc.c
index 0da8989..1bd4421 100644
--- a/lib/x86/desc.c
+++ b/lib/x86/desc.c
@@ -62,7 +62,7 @@ typedef struct {
 	u16 iomap_base;
 } tss32_t;
 
-static idt_entry_t idt[256];
+static idt_entry_t idt[256] __attribute__((aligned(4096)));
 
 void load_lidt(idt_entry_t *idt, int nentries)
 {
@@ -90,6 +90,11 @@ void set_idt_entry(int vec, void *addr, int dpl)
 #endif
 }
 
+void set_idt_sel(int vec, u16 sel)
+{
+    idt_entry_t *e = &idt[vec];
+    e->selector = sel;
+}
 
 struct ex_record {
     unsigned long rip;
diff --git a/lib/x86/desc.h b/lib/x86/desc.h
index 073878d..0b4897c 100644
--- a/lib/x86/desc.h
+++ b/lib/x86/desc.h
@@ -37,9 +37,12 @@ struct ex_regs {
 #define TSS_MAIN 0x20
 #define TSS_INTR 0x28
 
+#define NP_SEL 0x18
+
 unsigned exception_vector(void);
 unsigned exception_error_code(void);
 void set_idt_entry(int vec, void *addr, int dpl);
+void set_idt_sel(int vec, u16 sel);
 void set_gdt_entry(int num, u32 base,  u32 limit, u8 access, u8 gran);
 void set_intr_task_gate(int e, void *fn);
 void print_current_tss_info(void);
diff --git a/lib/x86/vm.c b/lib/x86/vm.c
index 1ca8a05..abbb0c9 100644
--- a/lib/x86/vm.c
+++ b/lib/x86/vm.c
@@ -248,3 +248,9 @@ void *vmap(unsigned long long phys, unsigned long size)
     }
     return mem;
 }
+
+void *alloc_vpage(void)
+{
+	vfree_top -= PAGE_SIZE;
+	return vfree_top;
+}
diff --git a/lib/x86/vm.h b/lib/x86/vm.h
index a3d2676..bf8fd52 100644
--- a/lib/x86/vm.h
+++ b/lib/x86/vm.h
@@ -20,6 +20,7 @@ void setup_vm();
 void *vmalloc(unsigned long size);
 void vfree(void *mem);
 void *vmap(unsigned long long phys, unsigned long size);
+void *alloc_vpage(void);
 
 void install_pte(unsigned long *cr3,
                         int pte_level,
diff --git a/x86/eventinj.c b/x86/eventinj.c
new file mode 100644
index 0000000..7bed7c5
--- /dev/null
+++ b/x86/eventinj.c
@@ -0,0 +1,372 @@
+#include "libcflat.h"
+#include "processor.h"
+#include "vm.h"
+#include "desc.h"
+#include "isr.h"
+#include "apic.h"
+#include "apic-defs.h"
+
+#ifdef __x86_64__
+#  define R "r"
+#else
+#  define R "e"
+#endif
+
+static int g_fail;
+static int g_tests;
+
+static inline void io_delay(void)
+{
+}
+
+static inline void outl(int addr, int val)
+{
+        asm volatile ("outl %1, %w0" : : "d" (addr), "a" (val));
+}
+
+static void report(const char *msg, int pass)
+{
+    ++g_tests;
+    printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL"));
+    if (!pass)
+        ++g_fail;
+}
+
+void apic_self_ipi(u8 v)
+{
+	apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED |
+		       APIC_INT_ASSERT | v, 0);
+}
+
+void apic_self_nmi(void)
+{
+	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
+}
+
+static void eoi(void)
+{
+    apic_write(APIC_EOI, 0);
+}
+
+#define flush_phys_addr(__s) outl(0xe4, __s)
+#define flush_stack() do {						\
+		int __l;						\
+		flush_phys_addr(virt_to_phys(&__l));			\
+	} while (0)
+
+extern char isr_iret_ip[];
+
+static void flush_idt_page()
+{
+	struct descriptor_table_ptr ptr;
+	sidt(&ptr);
+	flush_phys_addr(virt_to_phys((void*)ptr.base));
+}
+
+static volatile unsigned int test_divider;
+static volatile int test_count;
+
+#ifndef __x86_64__
+ulong stack_phys;
+void *stack_va;
+
+static void pf_tss(void)
+{
+start:
+	printf("PF running\n");
+	install_pte(phys_to_virt(read_cr3()), 1, stack_va,
+		    stack_phys | PTE_PRESENT | PTE_WRITE, 0);
+	invlpg(stack_va);
+	asm volatile ("iret");
+	goto start;
+}
+
+static void of_isr(struct ex_regs *r)
+{
+	printf("OF isr running\n");
+	test_count++;
+}
+
+static void np_isr(struct ex_regs *r)
+{
+	printf("NP isr running %x err=%x\n", r->rip, r->error_code);
+	set_idt_sel(33, read_cs());
+	test_count++;
+}
+#endif
+
+static void de_isr(struct ex_regs *r)
+{
+	printf("DE isr running divider is %d\n", test_divider);
+	test_divider = 10;
+}
+
+static void bp_isr(struct ex_regs *r)
+{
+	printf("BP isr running\n");
+	test_count++;
+}
+
+static void nested_nmi_isr(struct ex_regs *r)
+{
+	printf("Nested NMI isr running rip=%x\n", r->rip);
+
+	if (r->rip != (ulong)&isr_iret_ip)
+		test_count++;
+}
+static void nmi_isr(struct ex_regs *r)
+{
+	printf("NMI isr running %x\n", &isr_iret_ip);
+	test_count++;
+	handle_exception(2, nested_nmi_isr);
+	printf("Try send nested NMI to itself\n");
+	apic_self_nmi();
+	io_delay();
+	printf("After nested NMI to itself\n");
+}
+
+static void tirq0(isr_regs_t *r)
+{
+	printf("irq0 running\n");
+	if (test_count != 0)
+		test_count++;
+	eoi();
+}
+
+static void tirq1(isr_regs_t *r)
+{
+	printf("irq1 running\n");
+	test_count++;
+	eoi();
+}
+
+ulong saved_stack;
+
+#define switch_stack(S) do {						\
+		asm volatile ("mov %%" R "sp, %0":"=r"(saved_stack));	\
+		asm volatile ("mov %0, %%" R "sp"::"r"(S));		\
+	} while(0)
+
+#define restore_stack() do {						\
+		asm volatile ("mov %0, %%" R "sp"::"r"(saved_stack));	\
+	} while(0)
+
+int main()
+{
+	unsigned int res;
+	ulong *pt, *cr3, i;
+
+	setup_vm();
+	setup_idt();
+	setup_gdt();
+	setup_tss32();
+
+	handle_irq(32, tirq0);
+	handle_irq(33, tirq1);
+
+	/* generate HW exception that will fault on IDT and stack */
+	handle_exception(0, de_isr);
+	printf("Try to divide by 0\n");
+	flush_idt_page();
+	flush_stack();
+	asm volatile ("divl %3": "=a"(res)
+		      : "d"(0), "a"(1500), "m"(test_divider));
+	printf("Result is %d\n", res);
+	report("DE exception", res == 150);
+
+	/* generate soft exception (BP) that will fault on IDT and stack */
+	test_count = 0;
+	handle_exception(3, bp_isr);
+	printf("Try int 3\n");
+	flush_idt_page();
+	flush_stack();
+	asm volatile ("int $3");
+	printf("After int 3\n");
+	report("BP exception", test_count == 1);
+
+#ifndef __x86_64__
+	/* generate soft exception (OF) that will fault on IDT */
+	test_count = 0;
+	handle_exception(4, of_isr);
+	flush_idt_page();
+	printf("Try into\n");
+	asm volatile ("addb $127, %b0\ninto"::"a"(127));
+	printf("After into\n");
+	report("OF exception", test_count == 1);
+
+	/* generate soft exception (OF) using two bit instruction that will
+	   fault on IDT */
+	test_count = 0;
+	handle_exception(4, of_isr);
+	flush_idt_page();
+	printf("Try into\n");
+	asm volatile ("addb $127, %b0\naddr16 into"::"a"(127));
+	printf("After into\n");
+	report("2 byte OF exception", test_count == 1);
+#endif
+
+	/* generate HW interrupt that will fault on IDT */
+	test_count = 0;
+	flush_idt_page();
+	printf("Try send vec 33 to itself\n");
+	irq_enable();
+	apic_self_ipi(33);
+	io_delay();
+	irq_disable();
+	printf("After vec 33 to itself\n");
+	report("vec 33", test_count == 1);
+
+	/* generate soft interrupt that will fault on IDT and stack */
+	test_count = 0;
+	flush_idt_page();
+	printf("Try int $33\n");
+	flush_stack();
+	asm volatile ("int $33");
+	printf("After int $33\n");
+	report("int $33", test_count == 1);
+
+	/* Inject two HW interrupt than open iterrupt windows. Both interrupt
+	   will fault on IDT access */
+	test_count = 0;
+	flush_idt_page();
+	printf("Try send vec 32 and 33 to itself\n");
+	apic_self_ipi(32);
+	apic_self_ipi(33);
+	io_delay();
+	irq_enable();
+	asm volatile("nop");
+	irq_disable();
+	printf("After vec 32 and 33 to itself\n");
+	report("vec 32/33", test_count == 2);
+
+
+	/* Inject HW interrupt, do sti and than (while in irq shadow) inject
+	   soft interrupt. Fault during soft interrupt. Soft interrup shoud be
+	   handled before HW interrupt */
+	test_count = 0;
+	flush_idt_page();
+	printf("Try send vec 32 and int $33\n");
+	apic_self_ipi(32);
+	flush_stack();
+	io_delay();
+	irq_enable();
+	asm volatile ("int $33");
+	irq_disable();
+	printf("After vec 32 and int $33\n");
+	report("vec 32/int $33", test_count == 2);
+
+	/* test that TPR is honored */
+	test_count = 0;
+	handle_irq(62, tirq1);
+	flush_idt_page();
+	printf("Try send vec 33 and 62 and mask one with TPR\n");
+	apic_write(APIC_TASKPRI, 0xf << 4);
+	irq_enable();
+	apic_self_ipi(32);
+	apic_self_ipi(62);
+	io_delay();
+	apic_write(APIC_TASKPRI, 0x2 << 4);
+	printf("After 33/62 TPR test\n");
+	report("TPR", test_count == 1);
+	apic_write(APIC_TASKPRI, 0x0);
+	while(test_count != 2); /* wait for second irq */
+	irq_disable();
+
+#ifndef __x86_64__
+	/* test fault durint NP delivery */
+	printf("Before NP test\n");
+	test_count = 0;
+	handle_exception(11, np_isr);
+	set_idt_sel(33, NP_SEL);
+	flush_idt_page();
+	flush_stack();
+	asm volatile ("int $33");
+	printf("After int33\n");
+	report("NP exception", test_count == 2);
+#endif
+
+	/* generate NMI that will fault on IDT */
+	test_count = 0;
+	handle_exception(2, nmi_isr);
+	flush_idt_page();
+	printf("Try send NMI to itself\n");
+	apic_self_nmi();
+	printf("After NMI to itself\n");
+	/* this is needed on VMX without NMI window notificatoin.
+	   Interrupt windows is used instead, so let pending NMI
+	   to be injected */
+	irq_enable();
+	asm volatile ("nop");
+	irq_disable();
+	report("NMI", test_count == 2);
+
+#ifndef __x86_64__
+	stack_phys = (ulong)virt_to_phys(alloc_page());
+	stack_va = alloc_vpage();
+
+	/* Generate DE and PF exceptions serially */
+	test_divider = 0;
+	set_intr_task_gate(14, pf_tss);
+	handle_exception(0, de_isr);
+	printf("Try to divide by 0\n");
+	/* install read only pte */
+	install_pte(phys_to_virt(read_cr3()), 1, stack_va,
+		    stack_phys | PTE_PRESENT, 0);
+	invlpg(stack_va);
+	flush_phys_addr(stack_phys);
+	switch_stack(stack_va + 4095);
+	flush_idt_page();
+	asm volatile ("divl %3": "=a"(res)
+		      : "d"(0), "a"(1500), "m"(test_divider));
+	restore_stack();
+	printf("Result is %d\n", res);
+	report("DE PF exceptions", res == 150);
+
+	/* Generate NP and PF exceptions serially */
+	printf("Before NP test\n");
+	test_count = 0;
+	set_intr_task_gate(14, pf_tss);
+	handle_exception(11, np_isr);
+	set_idt_sel(33, NP_SEL);
+	/* install read only pte */
+	install_pte(phys_to_virt(read_cr3()), 1, stack_va,
+		    stack_phys | PTE_PRESENT, 0);
+	invlpg(stack_va);
+	flush_idt_page();
+	flush_phys_addr(stack_phys);
+	switch_stack(stack_va + 4095);
+	asm volatile ("int $33");
+	restore_stack();
+	printf("After int33\n");
+	report("NP PF exceptions", test_count == 2);
+#endif
+
+	pt = alloc_page();
+	cr3 = (void*)read_cr3();
+	memset(pt, 0, 4096);
+	/* use shadowed stack during interrupt delivery */
+	for (i = 0; i < 4096/sizeof(ulong); i++) {
+		if (!cr3[i]) {
+			cr3[i] = virt_to_phys(pt) | PTE_PRESENT | PTE_WRITE;
+			pt[0] = virt_to_phys(pt) | PTE_PRESENT | PTE_WRITE;
+#ifndef __x86_64__
+			((ulong*)(i<<22))[1] = 0;
+#else
+			((ulong*)(i<<39))[1] = 0;
+#endif
+			write_cr3(virt_to_phys(cr3));
+			break;
+		}
+	}
+	test_count = 0;
+	printf("Try int 33 with shadowed stack\n");
+	switch_stack(((char*)pt) + 4095);
+	asm volatile("int $33");
+	restore_stack();
+	printf("After int 33 with shadowed stack\n");
+	report("int 33 with shadowed stack", test_count == 1);
+
+	printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail);
+
+	return g_fail != 0;
+}
--
To unsubscribe from this list: send the line "unsubscribe kvm-commits" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [KVM Development]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Walks]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux