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..0ca94d3
--- /dev/null
+++ b/kvm/test/x86/idt.c
@@ -0,0 +1,154 @@
+#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;
+
+    printf("exception: vector %ld error %lx rip %lx\n", regs->vector,
+           regs->error_code, regs->rip);
+
+    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) {
+        printf("ex_table: %lx %lx\n", ex->rip, ex->handler);
+        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..ce4fd64
--- /dev/null
+++ b/kvm/test/x86/idt_test.c
@@ -0,0 +1,53 @@
+#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);
+    }
+}
+
+int test(void)
+{
+    int r;
+
+    setup_idt();
+    printf("GP testing: ");
+    r = test_gp();
+    report(r == GP_VECTOR, "#GP");
+    r = test_ud2();
+    report(r == UD_VECTOR, "#UD");
+    return !nr_fail ? 0 : 1;
+}
+
+int main(void)
+{
+    printf("start testing\n");
+    test();
+    return 0;
+}
