From patchwork Wed Dec 22 15:06:29 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gleb Natapov X-Patchwork-Id: 427881 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id oBMF6ibR013811 for ; Wed, 22 Dec 2010 15:06:44 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753096Ab0LVPGh (ORCPT ); Wed, 22 Dec 2010 10:06:37 -0500 Received: from mx1.redhat.com ([209.132.183.28]:58429 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753501Ab0LVPGe (ORCPT ); Wed, 22 Dec 2010 10:06:34 -0500 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id oBMF6Xuh026628 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Wed, 22 Dec 2010 10:06:33 -0500 Received: from dhcp-1-237.tlv.redhat.com (dhcp-1-237.tlv.redhat.com [10.35.1.237]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id oBMF6WiL021239; Wed, 22 Dec 2010 10:06:33 -0500 Received: by dhcp-1-237.tlv.redhat.com (Postfix, from userid 13519) id DB15818D3FD; Wed, 22 Dec 2010 17:06:29 +0200 (IST) From: Gleb Natapov To: avi@redhat.com, mtosatti@redhat.com Cc: kvm@vger.kernel.org Subject: [PATCH unit-tests 16/16] Add even injection test. Date: Wed, 22 Dec 2010 17:06:29 +0200 Message-Id: <1293030389-1143-17-git-send-email-gleb@redhat.com> In-Reply-To: <1293030389-1143-1-git-send-email-gleb@redhat.com> References: <1293030389-1143-1-git-send-email-gleb@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.22 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Wed, 22 Dec 2010 15:06:45 +0000 (UTC) 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; +}