Commit a4b87a16 authored by Gleb Natapov's avatar Gleb Natapov Committed by Avi Kivity
Browse files

Add event injection test



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: default avatarGleb Natapov <gleb@redhat.com>
Signed-off-by: default avatarAvi Kivity <avi@redhat.com>
parent 4029c34b
......@@ -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
......
......@@ -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;
......
......@@ -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);
......
......@@ -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;
}
......@@ -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,
......
#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;
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment