Signed-off-by: Sheng Yang <sheng@xxxxxxxxxxxxxxx> Signed-off-by: Avi Kivity <avi@xxxxxxxxxx> --- v3: rearrange printf()s v2: accurate instruction boundary tests avoid playing with the stack; use exception tables instead kvm/test/config-x86-common.mak | 2 + kvm/test/config-x86_64.mak | 2 +- kvm/test/flat.lds | 7 ++- kvm/test/lib/x86/idt.h | 19 +++++ kvm/test/x86/idt.c | 150 ++++++++++++++++++++++++++++++++++++++++ kvm/test/x86/idt_test.c | 49 +++++++++++++ 6 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 kvm/test/lib/x86/idt.h create mode 100644 kvm/test/x86/idt.c create mode 100644 kvm/test/x86/idt_test.c diff --git a/kvm/test/config-x86-common.mak b/kvm/test/config-x86-common.mak index c97de52..800b635 100644 --- a/kvm/test/config-x86-common.mak +++ b/kvm/test/config-x86-common.mak @@ -59,6 +59,8 @@ $(TEST_DIR)/realmode.o: bits = 32 $(TEST_DIR)/msr.flat: $(cstart.o) $(TEST_DIR)/msr.o +$(TEST_DIR)/idt_test.flat: $(cstart.o) $(TEST_DIR)/idt.o $(TEST_DIR)/idt_test.o + arch_clean: $(RM) $(TEST_DIR)/*.o $(TEST_DIR)/*.flat \ $(TEST_DIR)/.*.d $(TEST_DIR)/lib/.*.d $(TEST_DIR)/lib/*.o diff --git a/kvm/test/config-x86_64.mak b/kvm/test/config-x86_64.mak index d8fd2b5..f9cd121 100644 --- a/kvm/test/config-x86_64.mak +++ b/kvm/test/config-x86_64.mak @@ -5,6 +5,6 @@ ldarch = elf64-x86-64 CFLAGS += -D__x86_64__ tests = $(TEST_DIR)/access.flat $(TEST_DIR)/apic.flat \ - $(TEST_DIR)/emulator.flat + $(TEST_DIR)/emulator.flat $(TEST_DIR)/idt_test.flat include config-x86-common.mak diff --git a/kvm/test/flat.lds b/kvm/test/flat.lds index 4120595..4888f3a 100644 --- a/kvm/test/flat.lds +++ b/kvm/test/flat.lds @@ -4,7 +4,12 @@ SECTIONS stext = .; .text : { *(.init) *(.text) *(.text.*) } . = ALIGN(4K); - .data : { *(.data) } + .data : { + *(.data) + exception_table_start = .; + *(.data.ex) + exception_table_end = .; + } . = ALIGN(16); .rodata : { *(.rodata) } . = ALIGN(16); diff --git a/kvm/test/lib/x86/idt.h b/kvm/test/lib/x86/idt.h new file mode 100644 index 0000000..6babcb4 --- /dev/null +++ b/kvm/test/lib/x86/idt.h @@ -0,0 +1,19 @@ +#ifndef __IDT_TEST__ +#define __IDT_TEST__ + +void setup_idt(void); + +#define ASM_TRY(catch) \ + "movl $0, %%gs:4 \n\t" \ + ".pushsection .data.ex \n\t" \ + ".quad 1111f, " catch "\n\t" \ + ".popsection \n\t" \ + "1111:" + +#define UD_VECTOR 6 +#define GP_VECTOR 13 + +unsigned exception_vector(void); +unsigned exception_error_code(void); + +#endif diff --git a/kvm/test/x86/idt.c b/kvm/test/x86/idt.c new file mode 100644 index 0000000..999b3f0 --- /dev/null +++ b/kvm/test/x86/idt.c @@ -0,0 +1,150 @@ +#include "idt.h" +#include "libcflat.h" + +typedef struct { + unsigned short offset0; + unsigned short selector; + unsigned short ist : 3; + unsigned short : 5; + unsigned short type : 4; + unsigned short : 1; + unsigned short dpl : 2; + unsigned short p : 1; + unsigned short offset1; + unsigned offset2; + unsigned reserved; +} idt_entry_t; + +static idt_entry_t idt[256]; + +typedef struct { + unsigned short limit; + unsigned long linear_addr; +} __attribute__((packed)) descriptor_table_t; + +void lidt(idt_entry_t *idt, int nentries) +{ + descriptor_table_t dt; + + dt.limit = nentries * sizeof(*idt) - 1; + dt.linear_addr = (unsigned long)idt; + asm volatile ("lidt %0" : : "m"(dt)); +} + +unsigned short read_cs() +{ + unsigned short r; + + asm volatile ("mov %%cs, %0" : "=r"(r)); + return r; +} + +void memset(void *a, unsigned char v, int n) +{ + unsigned char *x = a; + + while (n--) + *x++ = v; +} + +void set_idt_entry(idt_entry_t *e, void *addr, int dpl) +{ + memset(e, 0, sizeof *e); + e->offset0 = (unsigned long)addr; + e->selector = read_cs(); + e->ist = 0; + e->type = 14; + e->dpl = dpl; + e->p = 1; + e->offset1 = (unsigned long)addr >> 16; + e->offset2 = (unsigned long)addr >> 32; +} + +struct ex_regs { + unsigned long rax, rcx, rdx, rbx; + unsigned long dummy, rbp, rsi, rdi; + unsigned long r8, r9, r10, r11; + unsigned long r12, r13, r14, r15; + unsigned long vector; + unsigned long error_code; + unsigned long rip; + unsigned long cs; + unsigned long rflags; +}; + +struct ex_record { + unsigned long rip; + unsigned long handler; +}; + +extern struct ex_record exception_table_start, exception_table_end; + +void do_handle_exception(struct ex_regs *regs) +{ + struct ex_record *ex; + unsigned ex_val; + + ex_val = regs->vector | (regs->error_code << 16); + + asm("mov %0, %%gs:4" : : "r"(ex_val)); + + for (ex = &exception_table_start; ex != &exception_table_end; ++ex) { + if (ex->rip == regs->rip) { + regs->rip = ex->handler; + return; + } + } + printf("unhandled excecption\n"); + exit(7); +} + +asm (".pushsection .text \n\t" + "ud_fault: \n\t" + "pushq $0 \n\t" + "pushq $6 \n\t" + "jmp handle_exception \n\t" + + "gp_fault: \n\t" + "pushq $13 \n\t" + "jmp handle_exception \n\t" + + "handle_exception: \n\t" + "push %r15; push %r14; push %r13; push %r12 \n\t" + "push %r11; push %r10; push %r9; push %r8 \n\t" + "push %rdi; push %rsi; push %rbp; sub $8, %rsp \n\t" + "push %rbx; push %rdx; push %rcx; push %rax \n\t" + "mov %rsp, %rdi \n\t" + "call do_handle_exception \n\t" + "pop %rax; pop %rcx; pop %rdx; pop %rbx \n\t" + "add $8, %rsp; pop %rbp; pop %rsi; pop %rdi \n\t" + "pop %r8; pop %r9; pop %r10; pop %r11 \n\t" + "pop %r12; pop %r13; pop %r14; pop %r15 \n\t" + "add $16, %rsp \n\t" + "iretq \n\t" + ".popsection"); + + +void setup_idt(void) +{ + extern char ud_fault, gp_fault; + + lidt(idt, 256); + set_idt_entry(&idt[6], &ud_fault, 0); + set_idt_entry(&idt[13], &gp_fault, 0); +} + +unsigned exception_vector(void) +{ + unsigned short vector; + + asm("mov %%gs:4, %0" : "=rm"(vector)); + return vector; +} + +unsigned exception_error_code(void) +{ + unsigned short error_code; + + asm("mov %%gs:6, %0" : "=rm"(error_code)); + return error_code; +} diff --git a/kvm/test/x86/idt_test.c b/kvm/test/x86/idt_test.c new file mode 100644 index 0000000..59256d4 --- /dev/null +++ b/kvm/test/x86/idt_test.c @@ -0,0 +1,49 @@ +#include "libcflat.h" +#include "idt.h" + +int test_ud2(void) +{ + asm volatile(ASM_TRY("1f") + "ud2 \n\t" + "1:" :); + return exception_vector(); +} + +int test_gp(void) +{ + unsigned long tmp; + + asm volatile("mov $0xffffffff, %0 \n\t" + ASM_TRY("1f") + "mov %0, %%cr4\n\t" + "1:" + : "=a"(tmp)); + return exception_vector(); +} + +static int nr_fail, nr_test; + +static void report(int cond, const char *name) +{ + ++nr_test; + if (!cond) { + ++nr_fail; + printf("%s: FAIL\n", name); + } else { + printf("%s: PASS\n", name); + } +} + +int main(void) +{ + int r; + + printf("Starting IDT test\n"); + setup_idt(); + r = test_gp(); + report(r == GP_VECTOR, "Testing #GP"); + r = test_ud2(); + report(r == UD_VECTOR, "Testing #UD"); + printf("%d failures of %d tests\n", nr_fail, nr_test); + return !nr_fail ? 0 : 1; +} -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html