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.

Signed-off-by: Gleb Natapov <g...@redhat.com>
---
 config-x86-common.mak |    4 +-
 lib/x86/desc.c        |    7 +-
 lib/x86/desc.h        |    3 +
 lib/x86/vm.c          |    6 +
 lib/x86/vm.h          |    1 +
 x86/eventinj.c        |  372 +++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 391 insertions(+), 2 deletions(-)
 create mode 100644 x86/eventinj.c

diff --git a/config-x86-common.mak b/config-x86-common.mak
index 3a77a93..732c4b7 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_and_config = $(TEST_DIR)/*.flat $(TEST_DIR)/unittests.cfg
 
@@ -77,6 +77,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..bcdc481
--- /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;
+}
-- 
1.7.2.3

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to