qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] import kvm-unittest in QEMU source tree


From: Michael S. Tsirkin
Subject: [Qemu-devel] [PATCH] import kvm-unittest in QEMU source tree
Date: Wed, 16 Oct 2013 22:03:37 +0300

This simply imports kvm-unittest git into qemu source tree.

We can next work on making make check run it
automatically.

Squashed 'kvm-unittest/' content from commit 2bc0e29

git-subtree-dir: kvm-unittest
git-subtree-split: 2bc0e29ee4447bebcd3b90053881f59265306adc

Signed-off-by: Michael S. Tsirkin <address@hidden>

---

Gleb, Paolo, any objections to this?  I really want a small guest for
running ACPI tests during make check, and kvm-unittest seems to fit the
bill.

Ability to test e.g. PCI with this in-tree would be very benefitial.

diff --git a/kvm-unittest/iotable.h b/kvm-unittest/iotable.h
new file mode 100644
index 0000000..cb18f23
--- /dev/null
+++ b/kvm-unittest/iotable.h
@@ -0,0 +1,40 @@
+/*
+ * Kernel-based Virtual Machine test driver
+ *
+ * This test driver provides a simple way of testing kvm, without a full
+ * device model.
+ *
+ * Copyright (C) 2006 Qumranet
+ *
+ * Authors:
+ *
+ *  Avi Kivity <address@hidden>
+ *  Yaniv Kamay <address@hidden>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#include <stdint.h>
+
+#define MAX_IO_TABLE   50
+
+typedef int (io_table_handler_t)(void *, int, int, uint64_t, uint64_t *);
+
+struct io_table_entry
+{
+       uint64_t start;
+       uint64_t end;
+       io_table_handler_t *handler;
+       void *opaque;
+};
+
+struct io_table
+{
+       int nr_entries;
+       struct io_table_entry entries[MAX_IO_TABLE];
+};
+
+struct io_table_entry *io_table_lookup(struct io_table *io_table,
+                                       uint64_t addr);
+int io_table_register(struct io_table *io_table, uint64_t start, uint64_t size,
+                      io_table_handler_t *handler, void *opaque);
diff --git a/kvm-unittest/lib/libcflat.h b/kvm-unittest/lib/libcflat.h
new file mode 100644
index 0000000..fadc33d
--- /dev/null
+++ b/kvm-unittest/lib/libcflat.h
@@ -0,0 +1,61 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <address@hidden>
+ */
+
+#ifndef __LIBCFLAT_H
+#define __LIBCFLAT_H
+
+#include <stdarg.h>
+
+typedef unsigned char u8;
+typedef signed char s8;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned u32;
+typedef signed s32;
+typedef unsigned long ulong;
+typedef unsigned long long u64;
+typedef signed long long s64;
+typedef unsigned long size_t;
+typedef _Bool bool;
+
+#define true 1
+#define false 0
+
+extern void exit(int code);
+extern void panic(char *fmt, ...);
+
+extern unsigned long strlen(const char *buf);
+extern char *strcat(char *dest, const char *src);
+extern int strcmp(const char *a, const char *b);
+
+extern int printf(const char *fmt, ...);
+extern int vsnprintf(char *buf, int size, const char *fmt, va_list va);
+
+extern void puts(const char *s);
+
+extern void *memset(void *s, int c, size_t n);
+extern void *memcpy(void *dest, const void *src, size_t n);
+
+extern long atol(const char *ptr);
+#define ARRAY_SIZE(_a)  (sizeof(_a)/sizeof((_a)[0]))
+
+#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
+
+#define NULL ((void *)0UL)
+#endif
diff --git a/kvm-unittest/lib/powerpc/44x/timebase.h 
b/kvm-unittest/lib/powerpc/44x/timebase.h
new file mode 100644
index 0000000..ce85347
--- /dev/null
+++ b/kvm-unittest/lib/powerpc/44x/timebase.h
@@ -0,0 +1,25 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <address@hidden>
+ */
+
+#ifndef __TIMEBASE_H__
+#define __TIMEBASE_H__
+
+unsigned long long mftb(void);
+
+#endif /* __TIMEBASE_H__ */
diff --git a/kvm-unittest/lib/x86/apic-defs.h b/kvm-unittest/lib/x86/apic-defs.h
new file mode 100644
index 0000000..c061e3d
--- /dev/null
+++ b/kvm-unittest/lib/x86/apic-defs.h
@@ -0,0 +1,133 @@
+#ifndef _ASM_X86_APICDEF_H
+#define _ASM_X86_APICDEF_H
+
+/*
+ * Constants for various Intel APICs. (local APIC, IOAPIC, etc.)
+ *
+ * Alan Cox <address@hidden>, 1995.
+ * Ingo Molnar <address@hidden>, 1999, 2000
+ */
+
+#define        APIC_DEFAULT_PHYS_BASE  0xfee00000
+
+#define        APIC_ID         0x20
+
+#define        APIC_LVR        0x30
+#define                APIC_LVR_MASK           0xFF00FF
+#define                GET_APIC_VERSION(x)     ((x) & 0xFFu)
+#define                GET_APIC_MAXLVT(x)      (((x) >> 16) & 0xFFu)
+#ifdef CONFIG_X86_32
+#  define      APIC_INTEGRATED(x)      ((x) & 0xF0u)
+#else
+#  define      APIC_INTEGRATED(x)      (1)
+#endif
+#define                APIC_XAPIC(x)           ((x) >= 0x14)
+#define        APIC_TASKPRI    0x80
+#define                APIC_TPRI_MASK          0xFFu
+#define        APIC_ARBPRI     0x90
+#define                APIC_ARBPRI_MASK        0xFFu
+#define        APIC_PROCPRI    0xA0
+#define        APIC_EOI        0xB0
+#define                APIC_EIO_ACK            0x0
+#define        APIC_RRR        0xC0
+#define        APIC_LDR        0xD0
+#define                APIC_LDR_MASK           (0xFFu << 24)
+#define                GET_APIC_LOGICAL_ID(x)  (((x) >> 24) & 0xFFu)
+#define                SET_APIC_LOGICAL_ID(x)  (((x) << 24))
+#define                APIC_ALL_CPUS           0xFFu
+#define        APIC_DFR        0xE0
+#define                APIC_DFR_CLUSTER                0x0FFFFFFFul
+#define                APIC_DFR_FLAT                   0xFFFFFFFFul
+#define        APIC_SPIV       0xF0
+#define                APIC_SPIV_FOCUS_DISABLED        (1 << 9)
+#define                APIC_SPIV_APIC_ENABLED          (1 << 8)
+#define        APIC_ISR        0x100
+#define        APIC_ISR_NR     0x8     /* Number of 32 bit ISR registers. */
+#define        APIC_TMR        0x180
+#define        APIC_IRR        0x200
+#define        APIC_ESR        0x280
+#define                APIC_ESR_SEND_CS        0x00001
+#define                APIC_ESR_RECV_CS        0x00002
+#define                APIC_ESR_SEND_ACC       0x00004
+#define                APIC_ESR_RECV_ACC       0x00008
+#define                APIC_ESR_SENDILL        0x00020
+#define                APIC_ESR_RECVILL        0x00040
+#define                APIC_ESR_ILLREGA        0x00080
+#define        APIC_ICR        0x300
+#define                APIC_DEST_SELF          0x40000
+#define                APIC_DEST_ALLINC        0x80000
+#define                APIC_DEST_ALLBUT        0xC0000
+#define                APIC_ICR_RR_MASK        0x30000
+#define                APIC_ICR_RR_INVALID     0x00000
+#define                APIC_ICR_RR_INPROG      0x10000
+#define                APIC_ICR_RR_VALID       0x20000
+#define                APIC_INT_LEVELTRIG      0x08000
+#define                APIC_INT_ASSERT         0x04000
+#define                APIC_ICR_BUSY           0x01000
+#define                APIC_DEST_LOGICAL       0x00800
+#define                APIC_DEST_PHYSICAL      0x00000
+#define                APIC_DM_FIXED           0x00000
+#define                APIC_DM_LOWEST          0x00100
+#define                APIC_DM_SMI             0x00200
+#define                APIC_DM_REMRD           0x00300
+#define                APIC_DM_NMI             0x00400
+#define                APIC_DM_INIT            0x00500
+#define                APIC_DM_STARTUP         0x00600
+#define                APIC_DM_EXTINT          0x00700
+#define                APIC_VECTOR_MASK        0x000FF
+#define        APIC_ICR2       0x310
+#define                GET_APIC_DEST_FIELD(x)  (((x) >> 24) & 0xFF)
+#define                SET_APIC_DEST_FIELD(x)  ((x) << 24)
+#define        APIC_LVTT       0x320
+#define        APIC_LVTTHMR    0x330
+#define        APIC_LVTPC      0x340
+#define        APIC_LVT0       0x350
+#define                APIC_LVT_TIMER_BASE_MASK        (0x3 << 18)
+#define                GET_APIC_TIMER_BASE(x)          (((x) >> 18) & 0x3)
+#define                SET_APIC_TIMER_BASE(x)          (((x) << 18))
+#define                APIC_TIMER_BASE_CLKIN           0x0
+#define                APIC_TIMER_BASE_TMBASE          0x1
+#define                APIC_TIMER_BASE_DIV             0x2
+#define                APIC_LVT_TIMER_PERIODIC         (1 << 17)
+#define                APIC_LVT_MASKED                 (1 << 16)
+#define                APIC_LVT_LEVEL_TRIGGER          (1 << 15)
+#define                APIC_LVT_REMOTE_IRR             (1 << 14)
+#define                APIC_INPUT_POLARITY             (1 << 13)
+#define                APIC_SEND_PENDING               (1 << 12)
+#define                APIC_MODE_MASK                  0x700
+#define                GET_APIC_DELIVERY_MODE(x)       (((x) >> 8) & 0x7)
+#define                SET_APIC_DELIVERY_MODE(x, y)    (((x) & ~0x700) | ((y) 
<< 8))
+#define                        APIC_MODE_FIXED         0x0
+#define                        APIC_MODE_NMI           0x4
+#define                        APIC_MODE_EXTINT        0x7
+#define        APIC_LVT1       0x360
+#define        APIC_LVTERR     0x370
+#define        APIC_TMICT      0x380
+#define        APIC_TMCCT      0x390
+#define        APIC_TDCR       0x3E0
+#define APIC_SELF_IPI  0x3F0
+#define                APIC_TDR_DIV_TMBASE     (1 << 2)
+#define                APIC_TDR_DIV_1          0xB
+#define                APIC_TDR_DIV_2          0x0
+#define                APIC_TDR_DIV_4          0x1
+#define                APIC_TDR_DIV_8          0x2
+#define                APIC_TDR_DIV_16         0x3
+#define                APIC_TDR_DIV_32         0x8
+#define                APIC_TDR_DIV_64         0x9
+#define                APIC_TDR_DIV_128        0xA
+#define        APIC_EILVT0     0x500
+#define                APIC_EILVT_NR_AMD_K8    1       /* # of extended 
interrupts */
+#define                APIC_EILVT_NR_AMD_10H   4
+#define                APIC_EILVT_LVTOFF(x)    (((x) >> 4) & 0xF)
+#define                APIC_EILVT_MSG_FIX      0x0
+#define                APIC_EILVT_MSG_SMI      0x2
+#define                APIC_EILVT_MSG_NMI      0x4
+#define                APIC_EILVT_MSG_EXT      0x7
+#define                APIC_EILVT_MASKED       (1 << 16)
+#define        APIC_EILVT1     0x510
+#define        APIC_EILVT2     0x520
+#define        APIC_EILVT3     0x530
+
+#define APIC_BASE_MSR  0x800
+
+#endif /* _ASM_X86_APICDEF_H */
diff --git a/kvm-unittest/lib/x86/apic.h b/kvm-unittest/lib/x86/apic.h
new file mode 100644
index 0000000..e325e9a
--- /dev/null
+++ b/kvm-unittest/lib/x86/apic.h
@@ -0,0 +1,34 @@
+#ifndef CFLAT_APIC_H
+#define CFLAT_APIC_H
+
+#include <stdint.h>
+#include "apic-defs.h"
+
+typedef struct {
+    uint8_t vector;
+    uint8_t delivery_mode:3;
+    uint8_t dest_mode:1;
+    uint8_t delivery_status:1;
+    uint8_t polarity:1;
+    uint8_t remote_irr:1;
+    uint8_t trig_mode:1;
+    uint8_t mask:1;
+    uint8_t reserve:7;
+    uint8_t reserved[4];
+    uint8_t dest_id;
+} ioapic_redir_entry_t;
+
+void mask_pic_interrupts(void);
+
+void ioapic_write_redir(unsigned line, ioapic_redir_entry_t e);
+void ioapic_write_reg(unsigned reg, uint32_t value);
+
+void enable_apic(void);
+uint32_t apic_read(unsigned reg);
+void apic_write(unsigned reg, uint32_t val);
+void apic_icr_write(uint32_t val, uint32_t dest);
+uint32_t apic_id(void);
+
+int enable_x2apic(void);
+
+#endif
diff --git a/kvm-unittest/lib/x86/atomic.h b/kvm-unittest/lib/x86/atomic.h
new file mode 100644
index 0000000..de2f033
--- /dev/null
+++ b/kvm-unittest/lib/x86/atomic.h
@@ -0,0 +1,164 @@
+#ifndef __ATOMIC_H
+#define __ATOMIC_H
+
+typedef struct {
+       volatile int counter;
+} atomic_t;
+
+#ifdef __i386__
+
+/**
+ * atomic_read - read atomic variable
+ * @v: pointer of type atomic_t
+ *
+ * Atomically reads the value of @v.
+ */
+static inline int atomic_read(const atomic_t *v)
+{
+       return v->counter;
+}
+
+/**
+ * atomic_set - set atomic variable
+ * @v: pointer of type atomic_t
+ * @i: required value
+ *
+ * Atomically sets the value of @v to @i.
+ */
+static inline void atomic_set(atomic_t *v, int i)
+{
+       v->counter = i;
+}
+
+/**
+ * atomic_inc - increment atomic variable
+ * @v: pointer of type atomic_t
+ *
+ * Atomically increments @v by 1.
+ */
+static inline void atomic_inc(atomic_t *v)
+{
+       asm volatile("lock incl %0"
+                    : "+m" (v->counter));
+}
+
+/**
+ * atomic_dec - decrement atomic variable
+ * @v: pointer of type atomic_t
+ *
+ * Atomically decrements @v by 1.
+ */
+static inline void atomic_dec(atomic_t *v)
+{
+       asm volatile("lock decl %0"
+                    : "+m" (v->counter));
+}
+
+typedef struct {
+       u64 __attribute__((aligned(8))) counter;
+} atomic64_t;
+
+#define ATOMIC64_INIT(val)     { (val) }
+
+/**
+ * atomic64_read - read atomic64 variable
+ * @ptr:      pointer to type atomic64_t
+ *
+ * Atomically reads the value of @ptr and returns it.
+ */
+static inline u64 atomic64_read(atomic64_t *ptr)
+{
+       u64 res;
+
+       /*
+        * Note, we inline this atomic64_t primitive because
+        * it only clobbers EAX/EDX and leaves the others
+        * untouched. We also (somewhat subtly) rely on the
+        * fact that cmpxchg8b returns the current 64-bit value
+        * of the memory location we are touching:
+        */
+       asm volatile("mov %%ebx, %%eax\n\t"
+                     "mov %%ecx, %%edx\n\t"
+                     "lock cmpxchg8b %1\n"
+                     : "=&A" (res)
+                     : "m" (*ptr)
+                     );
+       return res;
+}
+
+u64 atomic64_cmpxchg(atomic64_t *v, u64 old, u64 new);
+
+#elif defined(__x86_64__)
+
+/**
+ * atomic_read - read atomic variable
+ * @v: pointer of type atomic_t
+ *
+ * Atomically reads the value of @v.
+ */
+static inline int atomic_read(const atomic_t *v)
+{
+       return v->counter;
+}
+
+/**
+ * atomic_set - set atomic variable
+ * @v: pointer of type atomic_t
+ * @i: required value
+ *
+ * Atomically sets the value of @v to @i.
+ */
+static inline void atomic_set(atomic_t *v, int i)
+{
+       v->counter = i;
+}
+
+/**
+ * atomic_inc - increment atomic variable
+ * @v: pointer of type atomic_t
+ *
+ * Atomically increments @v by 1.
+ */
+static inline void atomic_inc(atomic_t *v)
+{
+       asm volatile("lock incl %0"
+                    : "=m" (v->counter)
+                    : "m" (v->counter));
+}
+
+/**
+ * atomic_dec - decrement atomic variable
+ * @v: pointer of type atomic_t
+ *
+ * Atomically decrements @v by 1.
+ */
+static inline void atomic_dec(atomic_t *v)
+{
+       asm volatile("lock decl %0"
+                    : "=m" (v->counter)
+                    : "m" (v->counter));
+}
+
+typedef struct {
+       long long counter;
+} atomic64_t;
+
+#define ATOMIC64_INIT(i)       { (i) }
+
+/**
+ * atomic64_read - read atomic64 variable
+ * @v: pointer of type atomic64_t
+ *
+ * Atomically reads the value of @v.
+ * Doesn't imply a read memory barrier.
+ */
+static inline long atomic64_read(const atomic64_t *v)
+{
+       return v->counter;
+}
+
+u64 atomic64_cmpxchg(atomic64_t *v, u64 old, u64 new);
+
+#endif
+
+#endif
diff --git a/kvm-unittest/lib/x86/desc.h b/kvm-unittest/lib/x86/desc.h
new file mode 100644
index 0000000..f819452
--- /dev/null
+++ b/kvm-unittest/lib/x86/desc.h
@@ -0,0 +1,87 @@
+#ifndef __IDT_TEST__
+#define __IDT_TEST__
+
+void setup_idt(void);
+#ifndef __x86_64__
+void setup_gdt(void);
+void setup_tss32(void);
+#else
+static inline void setup_gdt(void){}
+static inline void setup_tss32(void){}
+#endif
+
+struct ex_regs {
+    unsigned long rax, rcx, rdx, rbx;
+    unsigned long dummy, rbp, rsi, rdi;
+#ifdef __x86_64__
+    unsigned long r8, r9, r10, r11;
+    unsigned long r12, r13, r14, r15;
+#endif
+    unsigned long vector;
+    unsigned long error_code;
+    unsigned long rip;
+    unsigned long cs;
+    unsigned long rflags;
+};
+
+typedef struct {
+       u16 prev;
+       u16 res1;
+       u32 esp0;
+       u16 ss0;
+       u16 res2;
+       u32 esp1;
+       u16 ss1;
+       u16 res3;
+       u32 esp2;
+       u16 ss2;
+       u16 res4;
+       u32 cr3;
+       u32 eip;
+       u32 eflags;
+       u32 eax, ecx, edx, ebx, esp, ebp, esi, edi;
+       u16 es;
+       u16 res5;
+       u16 cs;
+       u16 res6;
+       u16 ss;
+       u16 res7;
+       u16 ds;
+       u16 res8;
+       u16 fs;
+       u16 res9;
+       u16 gs;
+       u16 res10;
+       u16 ldt;
+       u16 res11;
+       u16 t:1;
+       u16 res12:15;
+       u16 iomap_base;
+} tss32_t;
+
+#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
+
+#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_idt_task_gate(int vec, u16 sel);
+void set_intr_task_gate(int e, void *fn);
+void print_current_tss_info(void);
+void handle_exception(u8 v, void (*func)(struct ex_regs *regs));
+
+#endif
diff --git a/kvm-unittest/lib/x86/fake-apic.h b/kvm-unittest/lib/x86/fake-apic.h
new file mode 100644
index 0000000..eed63ba
--- /dev/null
+++ b/kvm-unittest/lib/x86/fake-apic.h
@@ -0,0 +1,14 @@
+#ifndef SILLY_APIC_H
+#define SILLY_APIC_H
+
+#define APIC_BASE 0x1000
+#define APIC_SIZE 0x100
+
+#define APIC_REG_NCPU        0x00
+#define APIC_REG_ID          0x04
+#define APIC_REG_SIPI_ADDR   0x08
+#define APIC_REG_SEND_SIPI   0x0c
+#define APIC_REG_IPI_VECTOR  0x10
+#define APIC_REG_SEND_IPI    0x14
+
+#endif
diff --git a/kvm-unittest/lib/x86/fwcfg.h b/kvm-unittest/lib/x86/fwcfg.h
new file mode 100644
index 0000000..e0836ca
--- /dev/null
+++ b/kvm-unittest/lib/x86/fwcfg.h
@@ -0,0 +1,44 @@
+#ifndef FWCFG_H
+#define FWCFG_H
+
+#include <stdint.h>
+
+#define FW_CFG_SIGNATURE        0x00
+#define FW_CFG_ID               0x01
+#define FW_CFG_UUID             0x02
+#define FW_CFG_RAM_SIZE         0x03
+#define FW_CFG_NOGRAPHIC        0x04
+#define FW_CFG_NB_CPUS          0x05
+#define FW_CFG_MACHINE_ID       0x06
+#define FW_CFG_KERNEL_ADDR      0x07
+#define FW_CFG_KERNEL_SIZE      0x08
+#define FW_CFG_KERNEL_CMDLINE   0x09
+#define FW_CFG_INITRD_ADDR      0x0a
+#define FW_CFG_INITRD_SIZE      0x0b
+#define FW_CFG_BOOT_DEVICE      0x0c
+#define FW_CFG_NUMA             0x0d
+#define FW_CFG_BOOT_MENU        0x0e
+#define FW_CFG_MAX_CPUS         0x0f
+#define FW_CFG_MAX_ENTRY        0x10
+
+#define FW_CFG_WRITE_CHANNEL    0x4000
+#define FW_CFG_ARCH_LOCAL       0x8000
+#define FW_CFG_ENTRY_MASK       ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)
+
+#define FW_CFG_INVALID          0xffff
+
+#define BIOS_CFG_IOPORT 0x510
+
+#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
+#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
+#define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2)
+
+uint8_t fwcfg_get_u8(unsigned index);
+uint16_t fwcfg_get_u16(unsigned index);
+uint32_t fwcfg_get_u32(unsigned index);
+uint64_t fwcfg_get_u64(unsigned index);
+
+unsigned fwcfg_get_nb_cpus(void);
+
+#endif
+
diff --git a/kvm-unittest/lib/x86/io.h b/kvm-unittest/lib/x86/io.h
new file mode 100644
index 0000000..bd6341c
--- /dev/null
+++ b/kvm-unittest/lib/x86/io.h
@@ -0,0 +1,40 @@
+#ifndef IO_H
+#define IO_H
+
+static inline unsigned char inb(unsigned short port)
+{
+    unsigned char value;
+    asm volatile("inb %w1, %0" : "=a" (value) : "Nd" (port));
+    return value;
+}
+
+static inline unsigned short inw(unsigned short port)
+{
+    unsigned short value;
+    asm volatile("inw %w1, %0" : "=a" (value) : "Nd" (port));
+    return value;
+}
+
+static inline unsigned int inl(unsigned short port)
+{
+    unsigned int value;
+    asm volatile("inl %w1, %0" : "=a" (value) : "Nd" (port));
+    return value;
+}
+
+static inline void outb(unsigned char value, unsigned short port)
+{
+    asm volatile("outb %b0, %w1" : : "a"(value), "Nd"(port));
+}
+
+static inline void outw(unsigned short value, unsigned short port)
+{
+    asm volatile("outw %w0, %w1" : : "a"(value), "Nd"(port));
+}
+
+static inline void outl(unsigned int value, unsigned short port)
+{
+    asm volatile("outl %0, %w1" : : "a"(value), "Nd"(port));
+}
+
+#endif
diff --git a/kvm-unittest/lib/x86/isr.h b/kvm-unittest/lib/x86/isr.h
new file mode 100644
index 0000000..b07a32a
--- /dev/null
+++ b/kvm-unittest/lib/x86/isr.h
@@ -0,0 +1,14 @@
+#ifndef __ISR_TEST__
+#define __ISR_TEST__
+
+typedef struct {
+    ulong regs[sizeof(ulong)*2];
+    ulong func;
+    ulong rip;
+    ulong cs;
+    ulong rflags;
+} isr_regs_t;
+
+void handle_irq(unsigned vec, void (*func)(isr_regs_t *regs));
+
+#endif
diff --git a/kvm-unittest/lib/x86/msr.h b/kvm-unittest/lib/x86/msr.h
new file mode 100644
index 0000000..281255a
--- /dev/null
+++ b/kvm-unittest/lib/x86/msr.h
@@ -0,0 +1,411 @@
+#ifndef _ASM_X86_MSR_INDEX_H
+#define _ASM_X86_MSR_INDEX_H
+
+/* CPU model specific register (MSR) numbers */
+
+/* x86-64 specific MSRs */
+#define MSR_EFER               0xc0000080 /* extended feature register */
+#define MSR_STAR               0xc0000081 /* legacy mode SYSCALL target */
+#define MSR_LSTAR              0xc0000082 /* long mode SYSCALL target */
+#define MSR_CSTAR              0xc0000083 /* compat mode SYSCALL target */
+#define MSR_SYSCALL_MASK       0xc0000084 /* EFLAGS mask for syscall */
+#define MSR_FS_BASE            0xc0000100 /* 64bit FS base */
+#define MSR_GS_BASE            0xc0000101 /* 64bit GS base */
+#define MSR_KERNEL_GS_BASE     0xc0000102 /* SwapGS GS shadow */
+#define MSR_TSC_AUX            0xc0000103 /* Auxiliary TSC */
+
+/* EFER bits: */
+#define _EFER_SCE              0  /* SYSCALL/SYSRET */
+#define _EFER_LME              8  /* Long mode enable */
+#define _EFER_LMA              10 /* Long mode active (read-only) */
+#define _EFER_NX               11 /* No execute enable */
+#define _EFER_SVME             12 /* Enable virtualization */
+#define _EFER_LMSLE            13 /* Long Mode Segment Limit Enable */
+#define _EFER_FFXSR            14 /* Enable Fast FXSAVE/FXRSTOR */
+
+#define EFER_SCE               (1<<_EFER_SCE)
+#define EFER_LME               (1<<_EFER_LME)
+#define EFER_LMA               (1<<_EFER_LMA)
+#define EFER_NX                        (1<<_EFER_NX)
+#define EFER_SVME              (1<<_EFER_SVME)
+#define EFER_LMSLE             (1<<_EFER_LMSLE)
+#define EFER_FFXSR             (1<<_EFER_FFXSR)
+
+/* Intel MSRs. Some also available on other CPUs */
+#define MSR_IA32_PERFCTR0              0x000000c1
+#define MSR_IA32_PERFCTR1              0x000000c2
+#define MSR_FSB_FREQ                   0x000000cd
+
+#define MSR_MTRRcap                    0x000000fe
+#define MSR_IA32_BBL_CR_CTL            0x00000119
+
+#define MSR_IA32_SYSENTER_CS           0x00000174
+#define MSR_IA32_SYSENTER_ESP          0x00000175
+#define MSR_IA32_SYSENTER_EIP          0x00000176
+
+#define MSR_IA32_MCG_CAP               0x00000179
+#define MSR_IA32_MCG_STATUS            0x0000017a
+#define MSR_IA32_MCG_CTL               0x0000017b
+
+#define MSR_IA32_PEBS_ENABLE           0x000003f1
+#define MSR_IA32_DS_AREA               0x00000600
+#define MSR_IA32_PERF_CAPABILITIES     0x00000345
+
+#define MSR_MTRRfix64K_00000           0x00000250
+#define MSR_MTRRfix16K_80000           0x00000258
+#define MSR_MTRRfix16K_A0000           0x00000259
+#define MSR_MTRRfix4K_C0000            0x00000268
+#define MSR_MTRRfix4K_C8000            0x00000269
+#define MSR_MTRRfix4K_D0000            0x0000026a
+#define MSR_MTRRfix4K_D8000            0x0000026b
+#define MSR_MTRRfix4K_E0000            0x0000026c
+#define MSR_MTRRfix4K_E8000            0x0000026d
+#define MSR_MTRRfix4K_F0000            0x0000026e
+#define MSR_MTRRfix4K_F8000            0x0000026f
+#define MSR_MTRRdefType                        0x000002ff
+
+#define MSR_IA32_CR_PAT                        0x00000277
+
+#define MSR_IA32_DEBUGCTLMSR           0x000001d9
+#define MSR_IA32_LASTBRANCHFROMIP      0x000001db
+#define MSR_IA32_LASTBRANCHTOIP                0x000001dc
+#define MSR_IA32_LASTINTFROMIP         0x000001dd
+#define MSR_IA32_LASTINTTOIP           0x000001de
+
+/* DEBUGCTLMSR bits (others vary by model): */
+#define DEBUGCTLMSR_LBR                        (1UL <<  0) /* last branch 
recording */
+#define DEBUGCTLMSR_BTF                        (1UL <<  1) /* single-step on 
branches */
+#define DEBUGCTLMSR_TR                 (1UL <<  6)
+#define DEBUGCTLMSR_BTS                        (1UL <<  7)
+#define DEBUGCTLMSR_BTINT              (1UL <<  8)
+#define DEBUGCTLMSR_BTS_OFF_OS         (1UL <<  9)
+#define DEBUGCTLMSR_BTS_OFF_USR                (1UL << 10)
+#define DEBUGCTLMSR_FREEZE_LBRS_ON_PMI (1UL << 11)
+
+#define MSR_IA32_MC0_CTL               0x00000400
+#define MSR_IA32_MC0_STATUS            0x00000401
+#define MSR_IA32_MC0_ADDR              0x00000402
+#define MSR_IA32_MC0_MISC              0x00000403
+
+#define MSR_IA32_MCx_CTL(x)            (MSR_IA32_MC0_CTL + 4*(x))
+#define MSR_IA32_MCx_STATUS(x)         (MSR_IA32_MC0_STATUS + 4*(x))
+#define MSR_IA32_MCx_ADDR(x)           (MSR_IA32_MC0_ADDR + 4*(x))
+#define MSR_IA32_MCx_MISC(x)           (MSR_IA32_MC0_MISC + 4*(x))
+
+/* These are consecutive and not in the normal 4er MCE bank block */
+#define MSR_IA32_MC0_CTL2              0x00000280
+#define MSR_IA32_MCx_CTL2(x)           (MSR_IA32_MC0_CTL2 + (x))
+
+#define CMCI_EN                        (1ULL << 30)
+#define CMCI_THRESHOLD_MASK            0xffffULL
+
+#define MSR_P6_PERFCTR0                        0x000000c1
+#define MSR_P6_PERFCTR1                        0x000000c2
+#define MSR_P6_EVNTSEL0                        0x00000186
+#define MSR_P6_EVNTSEL1                        0x00000187
+
+/* AMD64 MSRs. Not complete. See the architecture manual for a more
+   complete list. */
+
+#define MSR_AMD64_PATCH_LEVEL          0x0000008b
+#define MSR_AMD64_NB_CFG               0xc001001f
+#define MSR_AMD64_PATCH_LOADER         0xc0010020
+#define MSR_AMD64_OSVW_ID_LENGTH       0xc0010140
+#define MSR_AMD64_OSVW_STATUS          0xc0010141
+#define MSR_AMD64_DC_CFG               0xc0011022
+#define MSR_AMD64_IBSFETCHCTL          0xc0011030
+#define MSR_AMD64_IBSFETCHLINAD                0xc0011031
+#define MSR_AMD64_IBSFETCHPHYSAD       0xc0011032
+#define MSR_AMD64_IBSOPCTL             0xc0011033
+#define MSR_AMD64_IBSOPRIP             0xc0011034
+#define MSR_AMD64_IBSOPDATA            0xc0011035
+#define MSR_AMD64_IBSOPDATA2           0xc0011036
+#define MSR_AMD64_IBSOPDATA3           0xc0011037
+#define MSR_AMD64_IBSDCLINAD           0xc0011038
+#define MSR_AMD64_IBSDCPHYSAD          0xc0011039
+#define MSR_AMD64_IBSCTL               0xc001103a
+
+/* Fam 10h MSRs */
+#define MSR_FAM10H_MMIO_CONF_BASE      0xc0010058
+#define FAM10H_MMIO_CONF_ENABLE                (1<<0)
+#define FAM10H_MMIO_CONF_BUSRANGE_MASK 0xf
+#define FAM10H_MMIO_CONF_BUSRANGE_SHIFT 2
+#define FAM10H_MMIO_CONF_BASE_MASK     0xfffffff
+#define FAM10H_MMIO_CONF_BASE_SHIFT    20
+#define MSR_FAM10H_NODE_ID             0xc001100c
+
+/* K8 MSRs */
+#define MSR_K8_TOP_MEM1                        0xc001001a
+#define MSR_K8_TOP_MEM2                        0xc001001d
+#define MSR_K8_SYSCFG                  0xc0010010
+#define MSR_K8_INT_PENDING_MSG         0xc0010055
+/* C1E active bits in int pending message */
+#define K8_INTP_C1E_ACTIVE_MASK                0x18000000
+#define MSR_K8_TSEG_ADDR               0xc0010112
+#define K8_MTRRFIXRANGE_DRAM_ENABLE    0x00040000 /* MtrrFixDramEn bit    */
+#define K8_MTRRFIXRANGE_DRAM_MODIFY    0x00080000 /* MtrrFixDramModEn bit */
+#define K8_MTRR_RDMEM_WRMEM_MASK       0x18181818 /* Mask: RdMem|WrMem    */
+
+/* K7 MSRs */
+#define MSR_K7_EVNTSEL0                        0xc0010000
+#define MSR_K7_PERFCTR0                        0xc0010004
+#define MSR_K7_EVNTSEL1                        0xc0010001
+#define MSR_K7_PERFCTR1                        0xc0010005
+#define MSR_K7_EVNTSEL2                        0xc0010002
+#define MSR_K7_PERFCTR2                        0xc0010006
+#define MSR_K7_EVNTSEL3                        0xc0010003
+#define MSR_K7_PERFCTR3                        0xc0010007
+#define MSR_K7_CLK_CTL                 0xc001001b
+#define MSR_K7_HWCR                    0xc0010015
+#define MSR_K7_FID_VID_CTL             0xc0010041
+#define MSR_K7_FID_VID_STATUS          0xc0010042
+
+/* K6 MSRs */
+#define MSR_K6_EFER                    0xc0000080
+#define MSR_K6_STAR                    0xc0000081
+#define MSR_K6_WHCR                    0xc0000082
+#define MSR_K6_UWCCR                   0xc0000085
+#define MSR_K6_EPMR                    0xc0000086
+#define MSR_K6_PSOR                    0xc0000087
+#define MSR_K6_PFIR                    0xc0000088
+
+/* Centaur-Hauls/IDT defined MSRs. */
+#define MSR_IDT_FCR1                   0x00000107
+#define MSR_IDT_FCR2                   0x00000108
+#define MSR_IDT_FCR3                   0x00000109
+#define MSR_IDT_FCR4                   0x0000010a
+
+#define MSR_IDT_MCR0                   0x00000110
+#define MSR_IDT_MCR1                   0x00000111
+#define MSR_IDT_MCR2                   0x00000112
+#define MSR_IDT_MCR3                   0x00000113
+#define MSR_IDT_MCR4                   0x00000114
+#define MSR_IDT_MCR5                   0x00000115
+#define MSR_IDT_MCR6                   0x00000116
+#define MSR_IDT_MCR7                   0x00000117
+#define MSR_IDT_MCR_CTRL               0x00000120
+
+/* VIA Cyrix defined MSRs*/
+#define MSR_VIA_FCR                    0x00001107
+#define MSR_VIA_LONGHAUL               0x0000110a
+#define MSR_VIA_RNG                    0x0000110b
+#define MSR_VIA_BCR2                   0x00001147
+
+/* Transmeta defined MSRs */
+#define MSR_TMTA_LONGRUN_CTRL          0x80868010
+#define MSR_TMTA_LONGRUN_FLAGS         0x80868011
+#define MSR_TMTA_LRTI_READOUT          0x80868018
+#define MSR_TMTA_LRTI_VOLT_MHZ         0x8086801a
+
+/* Intel defined MSRs. */
+#define MSR_IA32_P5_MC_ADDR            0x00000000
+#define MSR_IA32_P5_MC_TYPE            0x00000001
+#define MSR_IA32_TSC                   0x00000010
+#define MSR_IA32_PLATFORM_ID           0x00000017
+#define MSR_IA32_EBL_CR_POWERON                0x0000002a
+#define MSR_IA32_FEATURE_CONTROL        0x0000003a
+
+#define FEATURE_CONTROL_LOCKED                         (1<<0)
+#define FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX       (1<<1)
+#define FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX      (1<<2)
+
+#define MSR_IA32_APICBASE              0x0000001b
+#define MSR_IA32_APICBASE_BSP          (1<<8)
+#define MSR_IA32_APICBASE_ENABLE       (1<<11)
+#define MSR_IA32_APICBASE_BASE         (0xfffff<<12)
+
+#define MSR_IA32_UCODE_WRITE           0x00000079
+#define MSR_IA32_UCODE_REV             0x0000008b
+
+#define MSR_IA32_PERF_STATUS           0x00000198
+#define MSR_IA32_PERF_CTL              0x00000199
+
+#define MSR_IA32_MPERF                 0x000000e7
+#define MSR_IA32_APERF                 0x000000e8
+
+#define MSR_IA32_THERM_CONTROL         0x0000019a
+#define MSR_IA32_THERM_INTERRUPT       0x0000019b
+
+#define THERM_INT_LOW_ENABLE           (1 << 0)
+#define THERM_INT_HIGH_ENABLE          (1 << 1)
+
+#define MSR_IA32_THERM_STATUS          0x0000019c
+
+#define THERM_STATUS_PROCHOT           (1 << 0)
+
+#define MSR_THERM2_CTL                 0x0000019d
+
+#define MSR_THERM2_CTL_TM_SELECT       (1ULL << 16)
+
+#define MSR_IA32_MISC_ENABLE           0x000001a0
+
+#define MSR_IA32_TEMPERATURE_TARGET    0x000001a2
+
+/* MISC_ENABLE bits: architectural */
+#define MSR_IA32_MISC_ENABLE_FAST_STRING       (1ULL << 0)
+#define MSR_IA32_MISC_ENABLE_TCC               (1ULL << 1)
+#define MSR_IA32_MISC_ENABLE_EMON              (1ULL << 7)
+#define MSR_IA32_MISC_ENABLE_BTS_UNAVAIL       (1ULL << 11)
+#define MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL      (1ULL << 12)
+#define MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP        (1ULL << 16)
+#define MSR_IA32_MISC_ENABLE_MWAIT             (1ULL << 18)
+#define MSR_IA32_MISC_ENABLE_LIMIT_CPUID       (1ULL << 22)
+#define MSR_IA32_MISC_ENABLE_XTPR_DISABLE      (1ULL << 23)
+#define MSR_IA32_MISC_ENABLE_XD_DISABLE                (1ULL << 34)
+
+/* MISC_ENABLE bits: model-specific, meaning may vary from core to core */
+#define MSR_IA32_MISC_ENABLE_X87_COMPAT                (1ULL << 2)
+#define MSR_IA32_MISC_ENABLE_TM1               (1ULL << 3)
+#define MSR_IA32_MISC_ENABLE_SPLIT_LOCK_DISABLE        (1ULL << 4)
+#define MSR_IA32_MISC_ENABLE_L3CACHE_DISABLE   (1ULL << 6)
+#define MSR_IA32_MISC_ENABLE_SUPPRESS_LOCK     (1ULL << 8)
+#define MSR_IA32_MISC_ENABLE_PREFETCH_DISABLE  (1ULL << 9)
+#define MSR_IA32_MISC_ENABLE_FERR              (1ULL << 10)
+#define MSR_IA32_MISC_ENABLE_FERR_MULTIPLEX    (1ULL << 10)
+#define MSR_IA32_MISC_ENABLE_TM2               (1ULL << 13)
+#define MSR_IA32_MISC_ENABLE_ADJ_PREF_DISABLE  (1ULL << 19)
+#define MSR_IA32_MISC_ENABLE_SPEEDSTEP_LOCK    (1ULL << 20)
+#define MSR_IA32_MISC_ENABLE_L1D_CONTEXT       (1ULL << 24)
+#define MSR_IA32_MISC_ENABLE_DCU_PREF_DISABLE  (1ULL << 37)
+#define MSR_IA32_MISC_ENABLE_TURBO_DISABLE     (1ULL << 38)
+#define MSR_IA32_MISC_ENABLE_IP_PREF_DISABLE   (1ULL << 39)
+
+/* P4/Xeon+ specific */
+#define MSR_IA32_MCG_EAX               0x00000180
+#define MSR_IA32_MCG_EBX               0x00000181
+#define MSR_IA32_MCG_ECX               0x00000182
+#define MSR_IA32_MCG_EDX               0x00000183
+#define MSR_IA32_MCG_ESI               0x00000184
+#define MSR_IA32_MCG_EDI               0x00000185
+#define MSR_IA32_MCG_EBP               0x00000186
+#define MSR_IA32_MCG_ESP               0x00000187
+#define MSR_IA32_MCG_EFLAGS            0x00000188
+#define MSR_IA32_MCG_EIP               0x00000189
+#define MSR_IA32_MCG_RESERVED          0x0000018a
+
+/* Pentium IV performance counter MSRs */
+#define MSR_P4_BPU_PERFCTR0            0x00000300
+#define MSR_P4_BPU_PERFCTR1            0x00000301
+#define MSR_P4_BPU_PERFCTR2            0x00000302
+#define MSR_P4_BPU_PERFCTR3            0x00000303
+#define MSR_P4_MS_PERFCTR0             0x00000304
+#define MSR_P4_MS_PERFCTR1             0x00000305
+#define MSR_P4_MS_PERFCTR2             0x00000306
+#define MSR_P4_MS_PERFCTR3             0x00000307
+#define MSR_P4_FLAME_PERFCTR0          0x00000308
+#define MSR_P4_FLAME_PERFCTR1          0x00000309
+#define MSR_P4_FLAME_PERFCTR2          0x0000030a
+#define MSR_P4_FLAME_PERFCTR3          0x0000030b
+#define MSR_P4_IQ_PERFCTR0             0x0000030c
+#define MSR_P4_IQ_PERFCTR1             0x0000030d
+#define MSR_P4_IQ_PERFCTR2             0x0000030e
+#define MSR_P4_IQ_PERFCTR3             0x0000030f
+#define MSR_P4_IQ_PERFCTR4             0x00000310
+#define MSR_P4_IQ_PERFCTR5             0x00000311
+#define MSR_P4_BPU_CCCR0               0x00000360
+#define MSR_P4_BPU_CCCR1               0x00000361
+#define MSR_P4_BPU_CCCR2               0x00000362
+#define MSR_P4_BPU_CCCR3               0x00000363
+#define MSR_P4_MS_CCCR0                        0x00000364
+#define MSR_P4_MS_CCCR1                        0x00000365
+#define MSR_P4_MS_CCCR2                        0x00000366
+#define MSR_P4_MS_CCCR3                        0x00000367
+#define MSR_P4_FLAME_CCCR0             0x00000368
+#define MSR_P4_FLAME_CCCR1             0x00000369
+#define MSR_P4_FLAME_CCCR2             0x0000036a
+#define MSR_P4_FLAME_CCCR3             0x0000036b
+#define MSR_P4_IQ_CCCR0                        0x0000036c
+#define MSR_P4_IQ_CCCR1                        0x0000036d
+#define MSR_P4_IQ_CCCR2                        0x0000036e
+#define MSR_P4_IQ_CCCR3                        0x0000036f
+#define MSR_P4_IQ_CCCR4                        0x00000370
+#define MSR_P4_IQ_CCCR5                        0x00000371
+#define MSR_P4_ALF_ESCR0               0x000003ca
+#define MSR_P4_ALF_ESCR1               0x000003cb
+#define MSR_P4_BPU_ESCR0               0x000003b2
+#define MSR_P4_BPU_ESCR1               0x000003b3
+#define MSR_P4_BSU_ESCR0               0x000003a0
+#define MSR_P4_BSU_ESCR1               0x000003a1
+#define MSR_P4_CRU_ESCR0               0x000003b8
+#define MSR_P4_CRU_ESCR1               0x000003b9
+#define MSR_P4_CRU_ESCR2               0x000003cc
+#define MSR_P4_CRU_ESCR3               0x000003cd
+#define MSR_P4_CRU_ESCR4               0x000003e0
+#define MSR_P4_CRU_ESCR5               0x000003e1
+#define MSR_P4_DAC_ESCR0               0x000003a8
+#define MSR_P4_DAC_ESCR1               0x000003a9
+#define MSR_P4_FIRM_ESCR0              0x000003a4
+#define MSR_P4_FIRM_ESCR1              0x000003a5
+#define MSR_P4_FLAME_ESCR0             0x000003a6
+#define MSR_P4_FLAME_ESCR1             0x000003a7
+#define MSR_P4_FSB_ESCR0               0x000003a2
+#define MSR_P4_FSB_ESCR1               0x000003a3
+#define MSR_P4_IQ_ESCR0                        0x000003ba
+#define MSR_P4_IQ_ESCR1                        0x000003bb
+#define MSR_P4_IS_ESCR0                        0x000003b4
+#define MSR_P4_IS_ESCR1                        0x000003b5
+#define MSR_P4_ITLB_ESCR0              0x000003b6
+#define MSR_P4_ITLB_ESCR1              0x000003b7
+#define MSR_P4_IX_ESCR0                        0x000003c8
+#define MSR_P4_IX_ESCR1                        0x000003c9
+#define MSR_P4_MOB_ESCR0               0x000003aa
+#define MSR_P4_MOB_ESCR1               0x000003ab
+#define MSR_P4_MS_ESCR0                        0x000003c0
+#define MSR_P4_MS_ESCR1                        0x000003c1
+#define MSR_P4_PMH_ESCR0               0x000003ac
+#define MSR_P4_PMH_ESCR1               0x000003ad
+#define MSR_P4_RAT_ESCR0               0x000003bc
+#define MSR_P4_RAT_ESCR1               0x000003bd
+#define MSR_P4_SAAT_ESCR0              0x000003ae
+#define MSR_P4_SAAT_ESCR1              0x000003af
+#define MSR_P4_SSU_ESCR0               0x000003be
+#define MSR_P4_SSU_ESCR1               0x000003bf /* guess: not in manual */
+
+#define MSR_P4_TBPU_ESCR0              0x000003c2
+#define MSR_P4_TBPU_ESCR1              0x000003c3
+#define MSR_P4_TC_ESCR0                        0x000003c4
+#define MSR_P4_TC_ESCR1                        0x000003c5
+#define MSR_P4_U2L_ESCR0               0x000003b0
+#define MSR_P4_U2L_ESCR1               0x000003b1
+
+#define MSR_P4_PEBS_MATRIX_VERT                0x000003f2
+
+/* Intel Core-based CPU performance counters */
+#define MSR_CORE_PERF_FIXED_CTR0       0x00000309
+#define MSR_CORE_PERF_FIXED_CTR1       0x0000030a
+#define MSR_CORE_PERF_FIXED_CTR2       0x0000030b
+#define MSR_CORE_PERF_FIXED_CTR_CTRL   0x0000038d
+#define MSR_CORE_PERF_GLOBAL_STATUS    0x0000038e
+#define MSR_CORE_PERF_GLOBAL_CTRL      0x0000038f
+#define MSR_CORE_PERF_GLOBAL_OVF_CTRL  0x00000390
+
+/* Geode defined MSRs */
+#define MSR_GEODE_BUSCONT_CONF0                0x00001900
+
+/* Intel VT MSRs */
+#define MSR_IA32_VMX_BASIC              0x00000480
+#define MSR_IA32_VMX_PINBASED_CTLS      0x00000481
+#define MSR_IA32_VMX_PROCBASED_CTLS     0x00000482
+#define MSR_IA32_VMX_EXIT_CTLS          0x00000483
+#define MSR_IA32_VMX_ENTRY_CTLS         0x00000484
+#define MSR_IA32_VMX_MISC               0x00000485
+#define MSR_IA32_VMX_CR0_FIXED0         0x00000486
+#define MSR_IA32_VMX_CR0_FIXED1         0x00000487
+#define MSR_IA32_VMX_CR4_FIXED0         0x00000488
+#define MSR_IA32_VMX_CR4_FIXED1         0x00000489
+#define MSR_IA32_VMX_VMCS_ENUM          0x0000048a
+#define MSR_IA32_VMX_PROCBASED_CTLS2    0x0000048b
+#define MSR_IA32_VMX_EPT_VPID_CAP       0x0000048c
+#define MSR_IA32_VMX_TRUE_PIN          0x0000048d
+#define MSR_IA32_VMX_TRUE_PROC         0x0000048e
+#define MSR_IA32_VMX_TRUE_EXIT         0x0000048f
+#define MSR_IA32_VMX_TRUE_ENTRY                0x00000490
+
+
+/* AMD-V MSRs */
+
+#define MSR_VM_CR                       0xc0010114
+#define MSR_VM_IGNNE                    0xc0010115
+#define MSR_VM_HSAVE_PA                 0xc0010117
+
+#endif /* _ASM_X86_MSR_INDEX_H */
diff --git a/kvm-unittest/lib/x86/pci.h b/kvm-unittest/lib/x86/pci.h
new file mode 100644
index 0000000..0f69ef0
--- /dev/null
+++ b/kvm-unittest/lib/x86/pci.h
@@ -0,0 +1,16 @@
+#ifndef PCI_H
+#define PCI_H
+
+#include <inttypes.h>
+#include "libcflat.h"
+
+typedef uint16_t pcidevaddr_t;
+enum {
+    PCIDEVADDR_INVALID = 0x0
+};
+pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id);
+unsigned long pci_bar_addr(pcidevaddr_t dev, int bar_num);
+bool pci_bar_is_memory(pcidevaddr_t dev, int bar_num);
+bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num);
+
+#endif
diff --git a/kvm-unittest/lib/x86/processor.h b/kvm-unittest/lib/x86/processor.h
new file mode 100644
index 0000000..29811d4
--- /dev/null
+++ b/kvm-unittest/lib/x86/processor.h
@@ -0,0 +1,325 @@
+#ifndef LIBCFLAT_PROCESSOR_H
+#define LIBCFLAT_PROCESSOR_H
+
+#include "libcflat.h"
+#include <stdint.h>
+
+struct descriptor_table_ptr {
+    u16 limit;
+    ulong base;
+} __attribute__((packed));
+
+static inline void barrier(void)
+{
+    asm volatile ("" : : : "memory");
+}
+
+static inline u16 read_cs(void)
+{
+    unsigned val;
+
+    asm ("mov %%cs, %0" : "=mr"(val));
+    return val;
+}
+
+static inline u16 read_ds(void)
+{
+    unsigned val;
+
+    asm ("mov %%ds, %0" : "=mr"(val));
+    return val;
+}
+
+static inline u16 read_es(void)
+{
+    unsigned val;
+
+    asm ("mov %%es, %0" : "=mr"(val));
+    return val;
+}
+
+static inline u16 read_ss(void)
+{
+    unsigned val;
+
+    asm ("mov %%ss, %0" : "=mr"(val));
+    return val;
+}
+
+static inline u16 read_fs(void)
+{
+    unsigned val;
+
+    asm ("mov %%fs, %0" : "=mr"(val));
+    return val;
+}
+
+static inline u16 read_gs(void)
+{
+    unsigned val;
+
+    asm ("mov %%gs, %0" : "=mr"(val));
+    return val;
+}
+
+static inline unsigned long read_rflags(void)
+{
+       unsigned long f;
+       asm ("pushf; pop %0\n\t" : "=rm"(f));
+       return f;
+}
+
+static inline void write_ds(unsigned val)
+{
+    asm ("mov %0, %%ds" : : "rm"(val) : "memory");
+}
+
+static inline void write_es(unsigned val)
+{
+    asm ("mov %0, %%es" : : "rm"(val) : "memory");
+}
+
+static inline void write_ss(unsigned val)
+{
+    asm ("mov %0, %%ss" : : "rm"(val) : "memory");
+}
+
+static inline void write_fs(unsigned val)
+{
+    asm ("mov %0, %%fs" : : "rm"(val) : "memory");
+}
+
+static inline void write_gs(unsigned val)
+{
+    asm ("mov %0, %%gs" : : "rm"(val) : "memory");
+}
+
+static inline u64 rdmsr(u32 index)
+{
+    u32 a, d;
+    asm volatile ("rdmsr" : "=a"(a), "=d"(d) : "c"(index) : "memory");
+    return a | ((u64)d << 32);
+}
+
+static inline void wrmsr(u32 index, u64 val)
+{
+    u32 a = val, d = val >> 32;
+    asm volatile ("wrmsr" : : "a"(a), "d"(d), "c"(index) : "memory");
+}
+
+static inline uint64_t rdpmc(uint32_t index)
+{
+    uint32_t a, d;
+    asm volatile ("rdpmc" : "=a"(a), "=d"(d) : "c"(index));
+    return a | ((uint64_t)d << 32);
+}
+
+static inline void write_cr0(ulong val)
+{
+    asm volatile ("mov %0, %%cr0" : : "r"(val) : "memory");
+}
+
+static inline ulong read_cr0(void)
+{
+    ulong val;
+    asm volatile ("mov %%cr0, %0" : "=r"(val) : : "memory");
+    return val;
+}
+
+static inline void write_cr2(ulong val)
+{
+    asm volatile ("mov %0, %%cr2" : : "r"(val) : "memory");
+}
+
+static inline ulong read_cr2(void)
+{
+    ulong val;
+    asm volatile ("mov %%cr2, %0" : "=r"(val) : : "memory");
+    return val;
+}
+
+static inline void write_cr3(ulong val)
+{
+    asm volatile ("mov %0, %%cr3" : : "r"(val) : "memory");
+}
+
+static inline ulong read_cr3(void)
+{
+    ulong val;
+    asm volatile ("mov %%cr3, %0" : "=r"(val) : : "memory");
+    return val;
+}
+
+static inline void write_cr4(ulong val)
+{
+    asm volatile ("mov %0, %%cr4" : : "r"(val) : "memory");
+}
+
+static inline ulong read_cr4(void)
+{
+    ulong val;
+    asm volatile ("mov %%cr4, %0" : "=r"(val) : : "memory");
+    return val;
+}
+
+static inline void write_cr8(ulong val)
+{
+    asm volatile ("mov %0, %%cr8" : : "r"(val) : "memory");
+}
+
+static inline ulong read_cr8(void)
+{
+    ulong val;
+    asm volatile ("mov %%cr8, %0" : "=r"(val) : : "memory");
+    return val;
+}
+
+static inline void lgdt(const struct descriptor_table_ptr *ptr)
+{
+    asm volatile ("lgdt %0" : : "m"(*ptr));
+}
+
+static inline void sgdt(struct descriptor_table_ptr *ptr)
+{
+    asm volatile ("sgdt %0" : "=m"(*ptr));
+}
+
+static inline void lidt(const struct descriptor_table_ptr *ptr)
+{
+    asm volatile ("lidt %0" : : "m"(*ptr));
+}
+
+static inline void sidt(struct descriptor_table_ptr *ptr)
+{
+    asm volatile ("sidt %0" : "=m"(*ptr));
+}
+
+static inline void lldt(unsigned val)
+{
+    asm volatile ("lldt %0" : : "rm"(val));
+}
+
+static inline u16 sldt(void)
+{
+    u16 val;
+    asm volatile ("sldt %0" : "=rm"(val));
+    return val;
+}
+
+static inline void ltr(u16 val)
+{
+    asm volatile ("ltr %0" : : "rm"(val));
+}
+
+static inline u16 str(void)
+{
+    u16 val;
+    asm volatile ("str %0" : "=rm"(val));
+    return val;
+}
+
+static inline void write_dr6(ulong val)
+{
+    asm volatile ("mov %0, %%dr6" : : "r"(val) : "memory");
+}
+
+static inline ulong read_dr6(void)
+{
+    ulong val;
+    asm volatile ("mov %%dr6, %0" : "=r"(val));
+    return val;
+}
+
+static inline void write_dr7(ulong val)
+{
+    asm volatile ("mov %0, %%dr7" : : "r"(val) : "memory");
+}
+
+static inline ulong read_dr7(void)
+{
+    ulong val;
+    asm volatile ("mov %%dr7, %0" : "=r"(val));
+    return val;
+}
+
+struct cpuid { u32 a, b, c, d; };
+
+static inline struct cpuid cpuid_indexed(u32 function, u32 index)
+{
+    struct cpuid r;
+    asm volatile ("cpuid"
+                  : "=a"(r.a), "=b"(r.b), "=c"(r.c), "=d"(r.d)
+                  : "0"(function), "2"(index));
+    return r;
+}
+
+static inline struct cpuid cpuid(u32 function)
+{
+    return cpuid_indexed(function, 0);
+}
+
+static inline void pause(void)
+{
+    asm volatile ("pause");
+}
+
+static inline void cli(void)
+{
+    asm volatile ("cli");
+}
+
+static inline void sti(void)
+{
+    asm volatile ("sti");
+}
+
+static inline unsigned long long rdtsc()
+{
+       long long r;
+
+#ifdef __x86_64__
+       unsigned a, d;
+
+       asm volatile ("rdtsc" : "=a"(a), "=d"(d));
+       r = a | ((long long)d << 32);
+#else
+       asm volatile ("rdtsc" : "=A"(r));
+#endif
+       return r;
+}
+
+static inline void wrtsc(u64 tsc)
+{
+       unsigned a = tsc, d = tsc >> 32;
+
+       asm volatile("wrmsr" : : "a"(a), "d"(d), "c"(0x10));
+}
+
+static inline void irq_disable(void)
+{
+    asm volatile("cli");
+}
+
+static inline void irq_enable(void)
+{
+    asm volatile("sti");
+}
+
+static inline void invlpg(void *va)
+{
+       asm volatile("invlpg (%0)" ::"r" (va) : "memory");
+}
+
+static inline void safe_halt(void)
+{
+       asm volatile("sti; hlt");
+}
+
+#ifdef __x86_64__
+static inline void write_rflags(u64 r)
+{
+       asm volatile("push %0; popf\n\t" : : "q"(r) : "cc");
+}
+#endif
+
+#endif
diff --git a/kvm-unittest/lib/x86/smp.h b/kvm-unittest/lib/x86/smp.h
new file mode 100644
index 0000000..df5fdba
--- /dev/null
+++ b/kvm-unittest/lib/x86/smp.h
@@ -0,0 +1,21 @@
+#ifndef __SMP_H
+#define __SMP_H
+
+#define mb()   asm volatile("mfence":::"memory")
+#define rmb()  asm volatile("lfence":::"memory")
+#define wmb()  asm volatile("sfence" ::: "memory")
+
+struct spinlock {
+    int v;
+};
+
+void smp_init(void);
+
+int cpu_count(void);
+int smp_id(void);
+void on_cpu(int cpu, void (*function)(void *data), void *data);
+void on_cpu_async(int cpu, void (*function)(void *data), void *data);
+void spin_lock(struct spinlock *lock);
+void spin_unlock(struct spinlock *lock);
+
+#endif
diff --git a/kvm-unittest/lib/x86/vm.h b/kvm-unittest/lib/x86/vm.h
new file mode 100644
index 0000000..eff6f72
--- /dev/null
+++ b/kvm-unittest/lib/x86/vm.h
@@ -0,0 +1,71 @@
+#ifndef VM_H
+#define VM_H
+
+#include "processor.h"
+
+#define PAGE_SIZE 4096ul
+#ifdef __x86_64__
+#define LARGE_PAGE_SIZE (512 * PAGE_SIZE)
+#else
+#define LARGE_PAGE_SIZE (1024 * PAGE_SIZE)
+#endif
+
+#define PTE_PRESENT (1ull << 0)
+#define PTE_PSE     (1ull << 7)
+#define PTE_WRITE   (1ull << 1)
+#define PTE_USER    (1ull << 2)
+#define PTE_ADDR    (0xffffffffff000ull)
+
+#define X86_CR0_PE      0x00000001
+#define X86_CR0_WP      0x00010000
+#define X86_CR0_PG      0x80000000
+#define X86_CR4_VMXE   0x00000001
+#define X86_CR4_PSE     0x00000010
+#define X86_CR4_PAE     0x00000020
+#define X86_CR4_PCIDE  0x00020000
+
+#ifdef __x86_64__
+#define SEL_NULL_DESC          0x0
+#define SEL_KERN_CODE_64       0x8
+#define SEL_KERN_DATA_64       0x10
+#define SEL_USER_CODE_64       0x18
+#define SEL_USER_DATA_64       0x20
+#define SEL_CODE_32            0x28
+#define SEL_DATA_32            0x30
+#define SEL_CODE_16            0x38
+#define SEL_DATA_16            0x40
+#define SEL_TSS_RUN            0x48
+#endif
+
+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 *alloc_vpages(ulong nr);
+uint64_t virt_to_phys_cr3(void *mem);
+
+void install_pte(unsigned long *cr3,
+                        int pte_level,
+                        void *virt,
+                        unsigned long pte,
+                        unsigned long *pt_page);
+
+void *alloc_page();
+
+void install_large_page(unsigned long *cr3,unsigned long phys,
+                               void *virt);
+void install_page(unsigned long *cr3, unsigned long phys, void *virt);
+
+static inline unsigned long virt_to_phys(const void *virt)
+{
+    return (unsigned long)virt;
+}
+
+static inline void *phys_to_virt(unsigned long phys)
+{
+    return (void *)phys;
+}
+
+#endif
diff --git a/kvm-unittest/x86/ioram.h b/kvm-unittest/x86/ioram.h
new file mode 100644
index 0000000..2938142
--- /dev/null
+++ b/kvm-unittest/x86/ioram.h
@@ -0,0 +1,7 @@
+#ifndef __IO_RAM_H
+#define __IO_RAM_H
+
+#define IORAM_BASE_PHYS 0xff000000UL
+#define IORAM_LEN       0x10000UL
+
+#endif
diff --git a/kvm-unittest/x86/kvmclock.h b/kvm-unittest/x86/kvmclock.h
new file mode 100644
index 0000000..166a338
--- /dev/null
+++ b/kvm-unittest/x86/kvmclock.h
@@ -0,0 +1,60 @@
+#ifndef KVMCLOCK_H
+#define KVMCLOCK_H
+
+#define MSR_KVM_WALL_CLOCK  0x11
+#define MSR_KVM_SYSTEM_TIME 0x12
+
+#define MAX_CPU 64
+
+#define PVCLOCK_TSC_STABLE_BIT (1 << 0)
+#define PVCLOCK_RAW_CYCLE_BIT (1 << 7) /* Get raw cycle */
+
+# define NSEC_PER_SEC                  1000000000ULL
+
+typedef u64 cycle_t;
+
+struct pvclock_vcpu_time_info {
+       u32   version;
+       u32   pad0;
+       u64   tsc_timestamp;
+       u64   system_time;
+       u32   tsc_to_system_mul;
+       s8    tsc_shift;
+       u8    flags;
+       u8    pad[2];
+} __attribute__((__packed__)); /* 32 bytes */
+
+struct pvclock_wall_clock {
+       u32   version;
+       u32   sec;
+       u32   nsec;
+} __attribute__((__packed__));
+
+/*
+ * These are perodically updated
+ *    xen: magic shared_info page
+ *    kvm: gpa registered via msr
+ * and then copied here.
+ */
+struct pvclock_shadow_time {
+       u64 tsc_timestamp;     /* TSC at last update of time vals.  */
+       u64 system_timestamp;  /* Time, in nanosecs, since boot.    */
+       u32 tsc_to_nsec_mul;
+       int tsc_shift;
+       u32 version;
+       u8  flags;
+};
+
+
+struct timespec {
+        long   tv_sec;
+        long   tv_nsec;
+};
+
+void pvclock_set_flags(unsigned char flags);
+cycle_t kvm_clock_read();
+void kvm_get_wallclock(struct timespec *ts);
+void kvm_clock_init(void *data);
+void kvm_clock_clear(void *data);
+
+#endif
diff --git a/kvm-unittest/x86/print.h b/kvm-unittest/x86/print.h
new file mode 100644
index 0000000..d5bd2f9
--- /dev/null
+++ b/kvm-unittest/x86/print.h
@@ -0,0 +1,19 @@
+#ifndef PRINT_H
+#define PRINT_H
+
+.macro PRINT text
+
+.data
+
+333: .asciz "\text\n"
+
+.previous
+
+       push %rdi
+       lea 333b, %rdi
+       call print
+       pop %rdi
+
+.endm
+
+#endif
diff --git a/kvm-unittest/x86/svm.h b/kvm-unittest/x86/svm.h
new file mode 100644
index 0000000..3fdc0d3
--- /dev/null
+++ b/kvm-unittest/x86/svm.h
@@ -0,0 +1,328 @@
+#ifndef __SVM_H
+#define __SVM_H
+
+#include "libcflat.h"
+
+enum {
+       INTERCEPT_INTR,
+       INTERCEPT_NMI,
+       INTERCEPT_SMI,
+       INTERCEPT_INIT,
+       INTERCEPT_VINTR,
+       INTERCEPT_SELECTIVE_CR0,
+       INTERCEPT_STORE_IDTR,
+       INTERCEPT_STORE_GDTR,
+       INTERCEPT_STORE_LDTR,
+       INTERCEPT_STORE_TR,
+       INTERCEPT_LOAD_IDTR,
+       INTERCEPT_LOAD_GDTR,
+       INTERCEPT_LOAD_LDTR,
+       INTERCEPT_LOAD_TR,
+       INTERCEPT_RDTSC,
+       INTERCEPT_RDPMC,
+       INTERCEPT_PUSHF,
+       INTERCEPT_POPF,
+       INTERCEPT_CPUID,
+       INTERCEPT_RSM,
+       INTERCEPT_IRET,
+       INTERCEPT_INTn,
+       INTERCEPT_INVD,
+       INTERCEPT_PAUSE,
+       INTERCEPT_HLT,
+       INTERCEPT_INVLPG,
+       INTERCEPT_INVLPGA,
+       INTERCEPT_IOIO_PROT,
+       INTERCEPT_MSR_PROT,
+       INTERCEPT_TASK_SWITCH,
+       INTERCEPT_FERR_FREEZE,
+       INTERCEPT_SHUTDOWN,
+       INTERCEPT_VMRUN,
+       INTERCEPT_VMMCALL,
+       INTERCEPT_VMLOAD,
+       INTERCEPT_VMSAVE,
+       INTERCEPT_STGI,
+       INTERCEPT_CLGI,
+       INTERCEPT_SKINIT,
+       INTERCEPT_RDTSCP,
+       INTERCEPT_ICEBP,
+       INTERCEPT_WBINVD,
+       INTERCEPT_MONITOR,
+       INTERCEPT_MWAIT,
+       INTERCEPT_MWAIT_COND,
+};
+
+
+struct __attribute__ ((__packed__)) vmcb_control_area {
+       u16 intercept_cr_read;
+       u16 intercept_cr_write;
+       u16 intercept_dr_read;
+       u16 intercept_dr_write;
+       u32 intercept_exceptions;
+       u64 intercept;
+       u8 reserved_1[42];
+       u16 pause_filter_count;
+       u64 iopm_base_pa;
+       u64 msrpm_base_pa;
+       u64 tsc_offset;
+       u32 asid;
+       u8 tlb_ctl;
+       u8 reserved_2[3];
+       u32 int_ctl;
+       u32 int_vector;
+       u32 int_state;
+       u8 reserved_3[4];
+       u32 exit_code;
+       u32 exit_code_hi;
+       u64 exit_info_1;
+       u64 exit_info_2;
+       u32 exit_int_info;
+       u32 exit_int_info_err;
+       u64 nested_ctl;
+       u8 reserved_4[16];
+       u32 event_inj;
+       u32 event_inj_err;
+       u64 nested_cr3;
+       u64 lbr_ctl;
+       u64 reserved_5;
+       u64 next_rip;
+       u8 reserved_6[816];
+};
+
+
+#define TLB_CONTROL_DO_NOTHING 0
+#define TLB_CONTROL_FLUSH_ALL_ASID 1
+
+#define V_TPR_MASK 0x0f
+
+#define V_IRQ_SHIFT 8
+#define V_IRQ_MASK (1 << V_IRQ_SHIFT)
+
+#define V_INTR_PRIO_SHIFT 16
+#define V_INTR_PRIO_MASK (0x0f << V_INTR_PRIO_SHIFT)
+
+#define V_IGN_TPR_SHIFT 20
+#define V_IGN_TPR_MASK (1 << V_IGN_TPR_SHIFT)
+
+#define V_INTR_MASKING_SHIFT 24
+#define V_INTR_MASKING_MASK (1 << V_INTR_MASKING_SHIFT)
+
+#define SVM_INTERRUPT_SHADOW_MASK 1
+
+#define SVM_IOIO_STR_SHIFT 2
+#define SVM_IOIO_REP_SHIFT 3
+#define SVM_IOIO_SIZE_SHIFT 4
+#define SVM_IOIO_ASIZE_SHIFT 7
+
+#define SVM_IOIO_TYPE_MASK 1
+#define SVM_IOIO_STR_MASK (1 << SVM_IOIO_STR_SHIFT)
+#define SVM_IOIO_REP_MASK (1 << SVM_IOIO_REP_SHIFT)
+#define SVM_IOIO_SIZE_MASK (7 << SVM_IOIO_SIZE_SHIFT)
+#define SVM_IOIO_ASIZE_MASK (7 << SVM_IOIO_ASIZE_SHIFT)
+
+#define SVM_VM_CR_VALID_MASK   0x001fULL
+#define SVM_VM_CR_SVM_LOCK_MASK 0x0008ULL
+#define SVM_VM_CR_SVM_DIS_MASK  0x0010ULL
+
+struct __attribute__ ((__packed__)) vmcb_seg {
+       u16 selector;
+       u16 attrib;
+       u32 limit;
+       u64 base;
+};
+
+struct __attribute__ ((__packed__)) vmcb_save_area {
+       struct vmcb_seg es;
+       struct vmcb_seg cs;
+       struct vmcb_seg ss;
+       struct vmcb_seg ds;
+       struct vmcb_seg fs;
+       struct vmcb_seg gs;
+       struct vmcb_seg gdtr;
+       struct vmcb_seg ldtr;
+       struct vmcb_seg idtr;
+       struct vmcb_seg tr;
+       u8 reserved_1[43];
+       u8 cpl;
+       u8 reserved_2[4];
+       u64 efer;
+       u8 reserved_3[112];
+       u64 cr4;
+       u64 cr3;
+       u64 cr0;
+       u64 dr7;
+       u64 dr6;
+       u64 rflags;
+       u64 rip;
+       u8 reserved_4[88];
+       u64 rsp;
+       u8 reserved_5[24];
+       u64 rax;
+       u64 star;
+       u64 lstar;
+       u64 cstar;
+       u64 sfmask;
+       u64 kernel_gs_base;
+       u64 sysenter_cs;
+       u64 sysenter_esp;
+       u64 sysenter_eip;
+       u64 cr2;
+       u8 reserved_6[32];
+       u64 g_pat;
+       u64 dbgctl;
+       u64 br_from;
+       u64 br_to;
+       u64 last_excp_from;
+       u64 last_excp_to;
+};
+
+struct __attribute__ ((__packed__)) vmcb {
+       struct vmcb_control_area control;
+       struct vmcb_save_area save;
+};
+
+#define SVM_CPUID_FEATURE_SHIFT 2
+#define SVM_CPUID_FUNC 0x8000000a
+
+#define SVM_VM_CR_SVM_DISABLE 4
+
+#define SVM_SELECTOR_S_SHIFT 4
+#define SVM_SELECTOR_DPL_SHIFT 5
+#define SVM_SELECTOR_P_SHIFT 7
+#define SVM_SELECTOR_AVL_SHIFT 8
+#define SVM_SELECTOR_L_SHIFT 9
+#define SVM_SELECTOR_DB_SHIFT 10
+#define SVM_SELECTOR_G_SHIFT 11
+
+#define SVM_SELECTOR_TYPE_MASK (0xf)
+#define SVM_SELECTOR_S_MASK (1 << SVM_SELECTOR_S_SHIFT)
+#define SVM_SELECTOR_DPL_MASK (3 << SVM_SELECTOR_DPL_SHIFT)
+#define SVM_SELECTOR_P_MASK (1 << SVM_SELECTOR_P_SHIFT)
+#define SVM_SELECTOR_AVL_MASK (1 << SVM_SELECTOR_AVL_SHIFT)
+#define SVM_SELECTOR_L_MASK (1 << SVM_SELECTOR_L_SHIFT)
+#define SVM_SELECTOR_DB_MASK (1 << SVM_SELECTOR_DB_SHIFT)
+#define SVM_SELECTOR_G_MASK (1 << SVM_SELECTOR_G_SHIFT)
+
+#define SVM_SELECTOR_WRITE_MASK (1 << 1)
+#define SVM_SELECTOR_READ_MASK SVM_SELECTOR_WRITE_MASK
+#define SVM_SELECTOR_CODE_MASK (1 << 3)
+
+#define INTERCEPT_CR0_MASK 1
+#define INTERCEPT_CR3_MASK (1 << 3)
+#define INTERCEPT_CR4_MASK (1 << 4)
+#define INTERCEPT_CR8_MASK (1 << 8)
+
+#define INTERCEPT_DR0_MASK 1
+#define INTERCEPT_DR1_MASK (1 << 1)
+#define INTERCEPT_DR2_MASK (1 << 2)
+#define INTERCEPT_DR3_MASK (1 << 3)
+#define INTERCEPT_DR4_MASK (1 << 4)
+#define INTERCEPT_DR5_MASK (1 << 5)
+#define INTERCEPT_DR6_MASK (1 << 6)
+#define INTERCEPT_DR7_MASK (1 << 7)
+
+#define SVM_EVTINJ_VEC_MASK 0xff
+
+#define SVM_EVTINJ_TYPE_SHIFT 8
+#define SVM_EVTINJ_TYPE_MASK (7 << SVM_EVTINJ_TYPE_SHIFT)
+
+#define SVM_EVTINJ_TYPE_INTR (0 << SVM_EVTINJ_TYPE_SHIFT)
+#define SVM_EVTINJ_TYPE_NMI (2 << SVM_EVTINJ_TYPE_SHIFT)
+#define SVM_EVTINJ_TYPE_EXEPT (3 << SVM_EVTINJ_TYPE_SHIFT)
+#define SVM_EVTINJ_TYPE_SOFT (4 << SVM_EVTINJ_TYPE_SHIFT)
+
+#define SVM_EVTINJ_VALID (1 << 31)
+#define SVM_EVTINJ_VALID_ERR (1 << 11)
+
+#define SVM_EXITINTINFO_VEC_MASK SVM_EVTINJ_VEC_MASK
+#define SVM_EXITINTINFO_TYPE_MASK SVM_EVTINJ_TYPE_MASK
+
+#define        SVM_EXITINTINFO_TYPE_INTR SVM_EVTINJ_TYPE_INTR
+#define        SVM_EXITINTINFO_TYPE_NMI SVM_EVTINJ_TYPE_NMI
+#define        SVM_EXITINTINFO_TYPE_EXEPT SVM_EVTINJ_TYPE_EXEPT
+#define        SVM_EXITINTINFO_TYPE_SOFT SVM_EVTINJ_TYPE_SOFT
+
+#define SVM_EXITINTINFO_VALID SVM_EVTINJ_VALID
+#define SVM_EXITINTINFO_VALID_ERR SVM_EVTINJ_VALID_ERR
+
+#define SVM_EXITINFOSHIFT_TS_REASON_IRET 36
+#define SVM_EXITINFOSHIFT_TS_REASON_JMP 38
+#define SVM_EXITINFOSHIFT_TS_HAS_ERROR_CODE 44
+
+#define        SVM_EXIT_READ_CR0       0x000
+#define        SVM_EXIT_READ_CR3       0x003
+#define        SVM_EXIT_READ_CR4       0x004
+#define        SVM_EXIT_READ_CR8       0x008
+#define        SVM_EXIT_WRITE_CR0      0x010
+#define        SVM_EXIT_WRITE_CR3      0x013
+#define        SVM_EXIT_WRITE_CR4      0x014
+#define        SVM_EXIT_WRITE_CR8      0x018
+#define        SVM_EXIT_READ_DR0       0x020
+#define        SVM_EXIT_READ_DR1       0x021
+#define        SVM_EXIT_READ_DR2       0x022
+#define        SVM_EXIT_READ_DR3       0x023
+#define        SVM_EXIT_READ_DR4       0x024
+#define        SVM_EXIT_READ_DR5       0x025
+#define        SVM_EXIT_READ_DR6       0x026
+#define        SVM_EXIT_READ_DR7       0x027
+#define        SVM_EXIT_WRITE_DR0      0x030
+#define        SVM_EXIT_WRITE_DR1      0x031
+#define        SVM_EXIT_WRITE_DR2      0x032
+#define        SVM_EXIT_WRITE_DR3      0x033
+#define        SVM_EXIT_WRITE_DR4      0x034
+#define        SVM_EXIT_WRITE_DR5      0x035
+#define        SVM_EXIT_WRITE_DR6      0x036
+#define        SVM_EXIT_WRITE_DR7      0x037
+#define SVM_EXIT_EXCP_BASE      0x040
+#define SVM_EXIT_INTR          0x060
+#define SVM_EXIT_NMI           0x061
+#define SVM_EXIT_SMI           0x062
+#define SVM_EXIT_INIT          0x063
+#define SVM_EXIT_VINTR         0x064
+#define SVM_EXIT_CR0_SEL_WRITE 0x065
+#define SVM_EXIT_IDTR_READ     0x066
+#define SVM_EXIT_GDTR_READ     0x067
+#define SVM_EXIT_LDTR_READ     0x068
+#define SVM_EXIT_TR_READ       0x069
+#define SVM_EXIT_IDTR_WRITE    0x06a
+#define SVM_EXIT_GDTR_WRITE    0x06b
+#define SVM_EXIT_LDTR_WRITE    0x06c
+#define SVM_EXIT_TR_WRITE      0x06d
+#define SVM_EXIT_RDTSC         0x06e
+#define SVM_EXIT_RDPMC         0x06f
+#define SVM_EXIT_PUSHF         0x070
+#define SVM_EXIT_POPF          0x071
+#define SVM_EXIT_CPUID         0x072
+#define SVM_EXIT_RSM           0x073
+#define SVM_EXIT_IRET          0x074
+#define SVM_EXIT_SWINT         0x075
+#define SVM_EXIT_INVD          0x076
+#define SVM_EXIT_PAUSE         0x077
+#define SVM_EXIT_HLT           0x078
+#define SVM_EXIT_INVLPG                0x079
+#define SVM_EXIT_INVLPGA       0x07a
+#define SVM_EXIT_IOIO          0x07b
+#define SVM_EXIT_MSR           0x07c
+#define SVM_EXIT_TASK_SWITCH   0x07d
+#define SVM_EXIT_FERR_FREEZE   0x07e
+#define SVM_EXIT_SHUTDOWN      0x07f
+#define SVM_EXIT_VMRUN         0x080
+#define SVM_EXIT_VMMCALL       0x081
+#define SVM_EXIT_VMLOAD                0x082
+#define SVM_EXIT_VMSAVE                0x083
+#define SVM_EXIT_STGI          0x084
+#define SVM_EXIT_CLGI          0x085
+#define SVM_EXIT_SKINIT                0x086
+#define SVM_EXIT_RDTSCP                0x087
+#define SVM_EXIT_ICEBP         0x088
+#define SVM_EXIT_WBINVD                0x089
+#define SVM_EXIT_MONITOR       0x08a
+#define SVM_EXIT_MWAIT         0x08b
+#define SVM_EXIT_MWAIT_COND    0x08c
+#define SVM_EXIT_NPF           0x400
+
+#define SVM_EXIT_ERR           -1
+
+#define SVM_CR0_SELECTIVE_MASK (X86_CR0_TS | X86_CR0_MP)
+
+#endif
+
diff --git a/kvm-unittest/x86/types.h b/kvm-unittest/x86/types.h
new file mode 100644
index 0000000..fd22743
--- /dev/null
+++ b/kvm-unittest/x86/types.h
@@ -0,0 +1,20 @@
+#ifndef __TYPES_H
+#define __TYPES_H
+
+#define DE_VECTOR 0
+#define DB_VECTOR 1
+#define BP_VECTOR 3
+#define OF_VECTOR 4
+#define BR_VECTOR 5
+#define UD_VECTOR 6
+#define NM_VECTOR 7
+#define DF_VECTOR 8
+#define TS_VECTOR 10
+#define NP_VECTOR 11
+#define SS_VECTOR 12
+#define GP_VECTOR 13
+#define PF_VECTOR 14
+#define MF_VECTOR 16
+#define MC_VECTOR 18
+
+#endif
diff --git a/kvm-unittest/x86/vmx.h b/kvm-unittest/x86/vmx.h
new file mode 100644
index 0000000..28595d8
--- /dev/null
+++ b/kvm-unittest/x86/vmx.h
@@ -0,0 +1,479 @@
+#ifndef __VMX_H
+#define __VMX_H
+
+#include "libcflat.h"
+
+struct vmcs {
+       u32 revision_id; /* vmcs revision identifier */
+       u32 abort; /* VMX-abort indicator */
+       /* VMCS data */
+       char data[0];
+};
+
+struct regs {
+       u64 rax;
+       u64 rcx;
+       u64 rdx;
+       u64 rbx;
+       u64 cr2;
+       u64 rbp;
+       u64 rsi;
+       u64 rdi;
+       u64 r8;
+       u64 r9;
+       u64 r10;
+       u64 r11;
+       u64 r12;
+       u64 r13;
+       u64 r14;
+       u64 r15;
+       u64 rflags;
+};
+
+struct vmx_test {
+       const char *name;
+       void (*init)(struct vmcs *vmcs);
+       void (*guest_main)();
+       int (*exit_handler)();
+       void (*syscall_handler)(u64 syscall_no);
+       struct regs guest_regs;
+       struct vmcs *vmcs;
+       int exits;
+};
+
+union vmx_basic {
+       u64 val;
+       struct {
+               u32 revision;
+               u32     size:13,
+                       : 3,
+                       width:1,
+                       dual:1,
+                       type:4,
+                       insouts:1,
+                       ctrl:1;
+       };
+};
+
+union vmx_ctrl_pin {
+       u64 val;
+       struct {
+               u32 set, clr;
+       };
+};
+
+union vmx_ctrl_cpu {
+       u64 val;
+       struct {
+               u32 set, clr;
+       };
+};
+
+union vmx_ctrl_exit {
+       u64 val;
+       struct {
+               u32 set, clr;
+       };
+};
+
+union vmx_ctrl_ent {
+       u64 val;
+       struct {
+               u32 set, clr;
+       };
+};
+
+union vmx_ept_vpid {
+       u64 val;
+       struct {
+               u32:16,
+                       super:2,
+                       : 2,
+                       invept:1,
+                       : 11;
+               u32     invvpid:1;
+       };
+};
+
+struct descr {
+       u16 limit;
+       u64 addr;
+};
+
+enum Encoding {
+       /* 16-Bit Control Fields */
+       VPID                    = 0x0000ul,
+       /* Posted-interrupt notification vector */
+       PINV                    = 0x0002ul,
+       /* EPTP index */
+       EPTP_IDX                = 0x0004ul,
+
+       /* 16-Bit Guest State Fields */
+       GUEST_SEL_ES            = 0x0800ul,
+       GUEST_SEL_CS            = 0x0802ul,
+       GUEST_SEL_SS            = 0x0804ul,
+       GUEST_SEL_DS            = 0x0806ul,
+       GUEST_SEL_FS            = 0x0808ul,
+       GUEST_SEL_GS            = 0x080aul,
+       GUEST_SEL_LDTR          = 0x080cul,
+       GUEST_SEL_TR            = 0x080eul,
+       GUEST_INT_STATUS        = 0x0810ul,
+
+       /* 16-Bit Host State Fields */
+       HOST_SEL_ES             = 0x0c00ul,
+       HOST_SEL_CS             = 0x0c02ul,
+       HOST_SEL_SS             = 0x0c04ul,
+       HOST_SEL_DS             = 0x0c06ul,
+       HOST_SEL_FS             = 0x0c08ul,
+       HOST_SEL_GS             = 0x0c0aul,
+       HOST_SEL_TR             = 0x0c0cul,
+
+       /* 64-Bit Control Fields */
+       IO_BITMAP_A             = 0x2000ul,
+       IO_BITMAP_B             = 0x2002ul,
+       MSR_BITMAP              = 0x2004ul,
+       EXIT_MSR_ST_ADDR        = 0x2006ul,
+       EXIT_MSR_LD_ADDR        = 0x2008ul,
+       ENTER_MSR_LD_ADDR       = 0x200aul,
+       VMCS_EXEC_PTR           = 0x200cul,
+       TSC_OFFSET              = 0x2010ul,
+       TSC_OFFSET_HI           = 0x2011ul,
+       APIC_VIRT_ADDR          = 0x2012ul,
+       APIC_ACCS_ADDR          = 0x2014ul,
+       EPTP                    = 0x201aul,
+       EPTP_HI                 = 0x201bul,
+
+       /* 64-Bit Readonly Data Field */
+       INFO_PHYS_ADDR          = 0x2400ul,
+
+       /* 64-Bit Guest State */
+       VMCS_LINK_PTR           = 0x2800ul,
+       VMCS_LINK_PTR_HI        = 0x2801ul,
+       GUEST_DEBUGCTL          = 0x2802ul,
+       GUEST_DEBUGCTL_HI       = 0x2803ul,
+       GUEST_EFER              = 0x2806ul,
+       GUEST_PERF_GLOBAL_CTRL  = 0x2808ul,
+       GUEST_PDPTE             = 0x280aul,
+
+       /* 64-Bit Host State */
+       HOST_EFER               = 0x2c02ul,
+       HOST_PERF_GLOBAL_CTRL   = 0x2c04ul,
+
+       /* 32-Bit Control Fields */
+       PIN_CONTROLS            = 0x4000ul,
+       CPU_EXEC_CTRL0          = 0x4002ul,
+       EXC_BITMAP              = 0x4004ul,
+       PF_ERROR_MASK           = 0x4006ul,
+       PF_ERROR_MATCH          = 0x4008ul,
+       CR3_TARGET_COUNT        = 0x400aul,
+       EXI_CONTROLS            = 0x400cul,
+       EXI_MSR_ST_CNT          = 0x400eul,
+       EXI_MSR_LD_CNT          = 0x4010ul,
+       ENT_CONTROLS            = 0x4012ul,
+       ENT_MSR_LD_CNT          = 0x4014ul,
+       ENT_INTR_INFO           = 0x4016ul,
+       ENT_INTR_ERROR          = 0x4018ul,
+       ENT_INST_LEN            = 0x401aul,
+       TPR_THRESHOLD           = 0x401cul,
+       CPU_EXEC_CTRL1          = 0x401eul,
+
+       /* 32-Bit R/O Data Fields */
+       VMX_INST_ERROR          = 0x4400ul,
+       EXI_REASON              = 0x4402ul,
+       EXI_INTR_INFO           = 0x4404ul,
+       EXI_INTR_ERROR          = 0x4406ul,
+       IDT_VECT_INFO           = 0x4408ul,
+       IDT_VECT_ERROR          = 0x440aul,
+       EXI_INST_LEN            = 0x440cul,
+       EXI_INST_INFO           = 0x440eul,
+
+       /* 32-Bit Guest State Fields */
+       GUEST_LIMIT_ES          = 0x4800ul,
+       GUEST_LIMIT_CS          = 0x4802ul,
+       GUEST_LIMIT_SS          = 0x4804ul,
+       GUEST_LIMIT_DS          = 0x4806ul,
+       GUEST_LIMIT_FS          = 0x4808ul,
+       GUEST_LIMIT_GS          = 0x480aul,
+       GUEST_LIMIT_LDTR        = 0x480cul,
+       GUEST_LIMIT_TR          = 0x480eul,
+       GUEST_LIMIT_GDTR        = 0x4810ul,
+       GUEST_LIMIT_IDTR        = 0x4812ul,
+       GUEST_AR_ES             = 0x4814ul,
+       GUEST_AR_CS             = 0x4816ul,
+       GUEST_AR_SS             = 0x4818ul,
+       GUEST_AR_DS             = 0x481aul,
+       GUEST_AR_FS             = 0x481cul,
+       GUEST_AR_GS             = 0x481eul,
+       GUEST_AR_LDTR           = 0x4820ul,
+       GUEST_AR_TR             = 0x4822ul,
+       GUEST_INTR_STATE        = 0x4824ul,
+       GUEST_ACTV_STATE        = 0x4826ul,
+       GUEST_SMBASE            = 0x4828ul,
+       GUEST_SYSENTER_CS       = 0x482aul,
+
+       /* 32-Bit Host State Fields */
+       HOST_SYSENTER_CS        = 0x4c00ul,
+
+       /* Natural-Width Control Fields */
+       CR0_MASK                = 0x6000ul,
+       CR4_MASK                = 0x6002ul,
+       CR0_READ_SHADOW = 0x6004ul,
+       CR4_READ_SHADOW = 0x6006ul,
+       CR3_TARGET_0            = 0x6008ul,
+       CR3_TARGET_1            = 0x600aul,
+       CR3_TARGET_2            = 0x600cul,
+       CR3_TARGET_3            = 0x600eul,
+
+       /* Natural-Width R/O Data Fields */
+       EXI_QUALIFICATION       = 0x6400ul,
+       IO_RCX                  = 0x6402ul,
+       IO_RSI                  = 0x6404ul,
+       IO_RDI                  = 0x6406ul,
+       IO_RIP                  = 0x6408ul,
+       GUEST_LINEAR_ADDRESS    = 0x640aul,
+
+       /* Natural-Width Guest State Fields */
+       GUEST_CR0               = 0x6800ul,
+       GUEST_CR3               = 0x6802ul,
+       GUEST_CR4               = 0x6804ul,
+       GUEST_BASE_ES           = 0x6806ul,
+       GUEST_BASE_CS           = 0x6808ul,
+       GUEST_BASE_SS           = 0x680aul,
+       GUEST_BASE_DS           = 0x680cul,
+       GUEST_BASE_FS           = 0x680eul,
+       GUEST_BASE_GS           = 0x6810ul,
+       GUEST_BASE_LDTR         = 0x6812ul,
+       GUEST_BASE_TR           = 0x6814ul,
+       GUEST_BASE_GDTR         = 0x6816ul,
+       GUEST_BASE_IDTR         = 0x6818ul,
+       GUEST_DR7               = 0x681aul,
+       GUEST_RSP               = 0x681cul,
+       GUEST_RIP               = 0x681eul,
+       GUEST_RFLAGS            = 0x6820ul,
+       GUEST_PENDING_DEBUG     = 0x6822ul,
+       GUEST_SYSENTER_ESP      = 0x6824ul,
+       GUEST_SYSENTER_EIP      = 0x6826ul,
+
+       /* Natural-Width Host State Fields */
+       HOST_CR0                = 0x6c00ul,
+       HOST_CR3                = 0x6c02ul,
+       HOST_CR4                = 0x6c04ul,
+       HOST_BASE_FS            = 0x6c06ul,
+       HOST_BASE_GS            = 0x6c08ul,
+       HOST_BASE_TR            = 0x6c0aul,
+       HOST_BASE_GDTR          = 0x6c0cul,
+       HOST_BASE_IDTR          = 0x6c0eul,
+       HOST_SYSENTER_ESP       = 0x6c10ul,
+       HOST_SYSENTER_EIP       = 0x6c12ul,
+       HOST_RSP                = 0x6c14ul,
+       HOST_RIP                = 0x6c16ul
+};
+
+enum Reason {
+       VMX_EXC_NMI             = 0,
+       VMX_EXTINT              = 1,
+       VMX_TRIPLE_FAULT        = 2,
+       VMX_INIT                = 3,
+       VMX_SIPI                = 4,
+       VMX_SMI_IO              = 5,
+       VMX_SMI_OTHER           = 6,
+       VMX_INTR_WINDOW         = 7,
+       VMX_NMI_WINDOW          = 8,
+       VMX_TASK_SWITCH         = 9,
+       VMX_CPUID               = 10,
+       VMX_GETSEC              = 11,
+       VMX_HLT                 = 12,
+       VMX_INVD                = 13,
+       VMX_INVLPG              = 14,
+       VMX_RDPMC               = 15,
+       VMX_RDTSC               = 16,
+       VMX_RSM                 = 17,
+       VMX_VMCALL              = 18,
+       VMX_VMCLEAR             = 19,
+       VMX_VMLAUNCH            = 20,
+       VMX_VMPTRLD             = 21,
+       VMX_VMPTRST             = 22,
+       VMX_VMREAD              = 23,
+       VMX_VMRESUME            = 24,
+       VMX_VMWRITE             = 25,
+       VMX_VMXOFF              = 26,
+       VMX_VMXON               = 27,
+       VMX_CR                  = 28,
+       VMX_DR                  = 29,
+       VMX_IO                  = 30,
+       VMX_RDMSR               = 31,
+       VMX_WRMSR               = 32,
+       VMX_FAIL_STATE          = 33,
+       VMX_FAIL_MSR            = 34,
+       VMX_MWAIT               = 36,
+       VMX_MTF                 = 37,
+       VMX_MONITOR             = 39,
+       VMX_PAUSE               = 40,
+       VMX_FAIL_MCHECK         = 41,
+       VMX_TPR_THRESHOLD       = 43,
+       VMX_APIC_ACCESS         = 44,
+       VMX_GDTR_IDTR           = 46,
+       VMX_LDTR_TR             = 47,
+       VMX_EPT_VIOLATION       = 48,
+       VMX_EPT_MISCONFIG       = 49,
+       VMX_INVEPT              = 50,
+       VMX_PREEMPT             = 52,
+       VMX_INVVPID             = 53,
+       VMX_WBINVD              = 54,
+       VMX_XSETBV              = 55
+};
+
+#define X86_EFLAGS_CF  0x00000001 /* Carry Flag */
+#define X86_EFLAGS_ZF  0x00000040 /* Zero Flag */
+
+enum Ctrl_exi {
+       EXI_HOST_64             = 1UL << 9,
+       EXI_LOAD_PERF           = 1UL << 12,
+       EXI_INTA                = 1UL << 15,
+       EXI_LOAD_EFER           = 1UL << 21,
+};
+
+enum Ctrl_ent {
+       ENT_GUEST_64            = 1UL << 9,
+       ENT_LOAD_EFER           = 1UL << 15,
+};
+
+enum Ctrl_pin {
+       PIN_EXTINT              = 1ul << 0,
+       PIN_NMI                 = 1ul << 3,
+       PIN_VIRT_NMI            = 1ul << 5,
+};
+
+enum Ctrl0 {
+       CPU_INTR_WINDOW         = 1ul << 2,
+       CPU_HLT                 = 1ul << 7,
+       CPU_INVLPG              = 1ul << 9,
+       CPU_CR3_LOAD            = 1ul << 15,
+       CPU_CR3_STORE           = 1ul << 16,
+       CPU_TPR_SHADOW          = 1ul << 21,
+       CPU_NMI_WINDOW          = 1ul << 22,
+       CPU_IO                  = 1ul << 24,
+       CPU_IO_BITMAP           = 1ul << 25,
+       CPU_SECONDARY           = 1ul << 31,
+};
+
+enum Ctrl1 {
+       CPU_EPT                 = 1ul << 1,
+       CPU_VPID                = 1ul << 5,
+       CPU_URG                 = 1ul << 7,
+};
+
+#define SAVE_GPR                               \
+       "xchg %rax, regs\n\t"                   \
+       "xchg %rbx, regs+0x8\n\t"               \
+       "xchg %rcx, regs+0x10\n\t"              \
+       "xchg %rdx, regs+0x18\n\t"              \
+       "xchg %rbp, regs+0x28\n\t"              \
+       "xchg %rsi, regs+0x30\n\t"              \
+       "xchg %rdi, regs+0x38\n\t"              \
+       "xchg %r8, regs+0x40\n\t"               \
+       "xchg %r9, regs+0x48\n\t"               \
+       "xchg %r10, regs+0x50\n\t"              \
+       "xchg %r11, regs+0x58\n\t"              \
+       "xchg %r12, regs+0x60\n\t"              \
+       "xchg %r13, regs+0x68\n\t"              \
+       "xchg %r14, regs+0x70\n\t"              \
+       "xchg %r15, regs+0x78\n\t"
+
+#define LOAD_GPR       SAVE_GPR
+
+#define SAVE_GPR_C                             \
+       "xchg %%rax, regs\n\t"                  \
+       "xchg %%rbx, regs+0x8\n\t"              \
+       "xchg %%rcx, regs+0x10\n\t"             \
+       "xchg %%rdx, regs+0x18\n\t"             \
+       "xchg %%rbp, regs+0x28\n\t"             \
+       "xchg %%rsi, regs+0x30\n\t"             \
+       "xchg %%rdi, regs+0x38\n\t"             \
+       "xchg %%r8, regs+0x40\n\t"              \
+       "xchg %%r9, regs+0x48\n\t"              \
+       "xchg %%r10, regs+0x50\n\t"             \
+       "xchg %%r11, regs+0x58\n\t"             \
+       "xchg %%r12, regs+0x60\n\t"             \
+       "xchg %%r13, regs+0x68\n\t"             \
+       "xchg %%r14, regs+0x70\n\t"             \
+       "xchg %%r15, regs+0x78\n\t"
+
+#define LOAD_GPR_C     SAVE_GPR_C
+
+#define SAVE_RFLAGS            \
+       "pushf\n\t"                     \
+       "pop host_rflags\n\t"
+
+#define LOAD_RFLAGS            \
+       "push host_rflags\n\t"  \
+       "popf\n\t"
+
+#define VMX_IO_SIZE_MASK               0x7
+#define _VMX_IO_BYTE                   1
+#define _VMX_IO_WORD                   2
+#define _VMX_IO_LONG                   3
+#define VMX_IO_DIRECTION_MASK          (1ul << 3)
+#define VMX_IO_IN                      (1ul << 3)
+#define VMX_IO_OUT                     0
+#define VMX_IO_STRING                  (1ul << 4)
+#define VMX_IO_REP                     (1ul << 5)
+#define VMX_IO_OPRAND_DX               (1ul << 6)
+#define VMX_IO_PORT_MASK               0xFFFF0000
+#define VMX_IO_PORT_SHIFT              16
+
+#define VMX_TEST_VMEXIT                        1
+#define VMX_TEST_EXIT                  2
+#define VMX_TEST_RESUME                        3
+#define VMX_TEST_LAUNCH_ERR            4
+#define VMX_TEST_RESUME_ERR            5
+
+#define HYPERCALL_BIT          (1ul << 12)
+#define HYPERCALL_MASK         0xFFF
+#define HYPERCALL_VMEXIT       0x1
+
+
+extern struct regs regs;
+
+extern union vmx_basic basic;
+extern union vmx_ctrl_pin ctrl_pin_rev;
+extern union vmx_ctrl_cpu ctrl_cpu_rev[2];
+extern union vmx_ctrl_exit ctrl_exit_rev;
+extern union vmx_ctrl_ent ctrl_enter_rev;
+extern union vmx_ept_vpid  ept_vpid;
+
+static inline int vmcs_clear(struct vmcs *vmcs)
+{
+       bool ret;
+       asm volatile ("vmclear %1; setbe %0" : "=q" (ret) : "m" (vmcs) : "cc");
+       return ret;
+}
+
+static inline u64 vmcs_read(enum Encoding enc)
+{
+       u64 val;
+       asm volatile ("vmread %1, %0" : "=rm" (val) : "r" ((u64)enc) : "cc");
+       return val;
+}
+
+static inline int vmcs_write(enum Encoding enc, u64 val)
+{
+       bool ret;
+       asm volatile ("vmwrite %1, %2; setbe %0"
+               : "=q"(ret) : "rm" (val), "r" ((u64)enc) : "cc");
+       return ret;
+}
+
+static inline int vmcs_save(struct vmcs **vmcs)
+{
+       bool ret;
+
+       asm volatile ("vmptrst %1; setbe %0" : "=q" (ret) : "m" (*vmcs) : "cc");
+       return ret;
+}
+
+void report(const char *name, int result);
+void print_vmexit_info();
+
+#endif
+
diff --git a/kvm-unittest/iotable.c b/kvm-unittest/iotable.c
new file mode 100644
index 0000000..91a5016
--- /dev/null
+++ b/kvm-unittest/iotable.c
@@ -0,0 +1,53 @@
+/*
+ * Kernel-based Virtual Machine test driver
+ *
+ * This test driver provides a simple way of testing kvm, without a full
+ * device model.
+ *
+ * Copyright (C) 2006 Qumranet
+ *
+ * Authors:
+ *
+ *  Avi Kivity <address@hidden>
+ *  Yaniv Kamay <address@hidden>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "iotable.h"
+
+struct io_table_entry *io_table_lookup(struct io_table *io_table, uint64_t 
addr)
+{
+       int i;
+
+       for (i = 0; i < io_table->nr_entries; i++) {
+               if (io_table->entries[i].start <= addr &&
+                   addr < io_table->entries[i].end)
+                       return &io_table->entries[i];
+       }
+
+       return NULL;
+}
+
+int io_table_register(struct io_table *io_table, uint64_t start, uint64_t size,
+                     io_table_handler_t *handler, void *opaque)
+{
+       struct io_table_entry *entry;
+
+       if (io_table->nr_entries == MAX_IO_TABLE)
+               return -ENOSPC;
+
+       entry = &io_table->entries[io_table->nr_entries];
+       io_table->nr_entries++;
+
+       entry->start = start;
+       entry->end = start + size;
+       entry->handler = handler;
+       entry->opaque = opaque;
+
+       return 0;
+}
diff --git a/kvm-unittest/kvmtrace.c b/kvm-unittest/kvmtrace.c
new file mode 100644
index 0000000..de3c189
--- /dev/null
+++ b/kvm-unittest/kvmtrace.c
@@ -0,0 +1,706 @@
+/*
+ * kvm tracing application
+ *
+ * This tool is used for collecting trace buffer data
+ * for kvm trace.
+ *
+ * Based on blktrace 0.99.3
+ *
+ * Copyright (C) 2005 Jens Axboe <address@hidden>
+ * Copyright (C) 2006 Jens Axboe <address@hidden>
+ * Copyright (C) 2008 Eric Liu <address@hidden>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#define _GNU_SOURCE
+
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/statfs.h>
+#include <sys/poll.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sched.h>
+
+#ifndef __user
+#define __user
+#endif
+#include <linux/kvm.h>
+
+static char kvmtrace_version[] = "0.1";
+
+/*
+ * You may want to increase this even more, if you are logging at a high
+ * rate and see skipped/missed events
+ */
+#define BUF_SIZE       (512 * 1024)
+#define BUF_NR         (8)
+
+#define OFILE_BUF      (128 * 1024)
+
+#define DEBUGFS_TYPE   0x64626720
+
+#define max(a, b)      ((a) > (b) ? (a) : (b))
+
+#define S_OPTS "r:o:w:?Vb:n:D:"
+static struct option l_opts[] = {
+       {
+               .name = "relay",
+               .has_arg = required_argument,
+               .flag = NULL,
+               .val = 'r'
+       },
+       {
+               .name = "output",
+               .has_arg = required_argument,
+               .flag = NULL,
+               .val = 'o'
+       },
+       {
+               .name = "stopwatch",
+               .has_arg = required_argument,
+               .flag = NULL,
+               .val = 'w'
+       },
+       {
+               .name = "version",
+               .has_arg = no_argument,
+               .flag = NULL,
+               .val = 'V'
+       },
+       {
+               .name = "buffer-size",
+               .has_arg = required_argument,
+               .flag = NULL,
+               .val = 'b'
+       },
+       {
+               .name = "num-sub-buffers",
+               .has_arg = required_argument,
+               .flag = NULL,
+               .val = 'n'
+       },
+       {
+               .name = "output-dir",
+               .has_arg = required_argument,
+               .flag = NULL,
+               .val = 'D'
+       },
+       {
+               .name = NULL,
+       }
+};
+
+struct thread_information {
+       int cpu;
+       pthread_t thread;
+
+       int fd;
+       char fn[MAXPATHLEN + 64];
+
+       FILE *ofile;
+       char *ofile_buffer;
+
+       int (*get_subbuf)(struct thread_information *, unsigned int);
+       int (*read_data)(struct thread_information *, void *, unsigned int);
+
+       unsigned long long data_read;
+
+       struct kvm_trace_information *trace_info;
+
+       int exited;
+
+       /*
+        * mmap controlled output files
+        */
+       unsigned long long fs_size;
+       unsigned long long fs_max_size;
+       unsigned long fs_off;
+       void *fs_buf;
+       unsigned long fs_buf_len;
+
+};
+
+struct kvm_trace_information {
+       int fd;
+       volatile int trace_started;
+       unsigned long lost_records;
+       struct thread_information *threads;
+       unsigned long buf_size;
+       unsigned long buf_nr;
+};
+
+static struct kvm_trace_information trace_information;
+
+static int ncpus;
+static char default_debugfs_path[] = "/sys/kernel/debug";
+
+/* command line option globals */
+static char *debugfs_path;
+static char *output_name;
+static char *output_dir;
+static int stop_watch;
+static unsigned long buf_size = BUF_SIZE;
+static unsigned long buf_nr = BUF_NR;
+static unsigned int page_size;
+
+#define for_each_cpu_online(cpu) \
+       for (cpu = 0; cpu < ncpus; cpu++)
+#define for_each_tip(tip, i) \
+       for (i = 0, tip = trace_information.threads; i < ncpus; i++, tip++)
+
+#define is_done()      (*(volatile int *)(&done))
+static volatile int done;
+
+#define is_trace_stopped()     (*(volatile int *)(&trace_stopped))
+static volatile int trace_stopped;
+
+static void exit_trace(int status);
+
+static void handle_sigint(__attribute__((__unused__)) int sig)
+{
+       ioctl(trace_information.fd, KVM_TRACE_PAUSE);
+       done = 1;
+}
+
+static int get_lost_records()
+{
+       int fd;
+       char tmp[MAXPATHLEN + 64];
+
+       snprintf(tmp, sizeof(tmp), "%s/kvm/lost_records", debugfs_path);
+       fd = open(tmp, O_RDONLY);
+       if (fd < 0) {
+               /*
+                * this may be ok, if the kernel doesn't support dropped counts
+                */
+               if (errno == ENOENT)
+                       return 0;
+
+               fprintf(stderr, "Couldn't open dropped file %s\n", tmp);
+               return -1;
+       }
+
+       if (read(fd, tmp, sizeof(tmp)) < 0) {
+               perror(tmp);
+               close(fd);
+               return -1;
+       }
+       close(fd);
+
+       return atoi(tmp);
+}
+
+static void wait_for_data(struct thread_information *tip, int timeout)
+{
+       struct pollfd pfd = { .fd = tip->fd, .events = POLLIN };
+
+       while (!is_done()) {
+               if (poll(&pfd, 1, timeout) < 0) {
+                       perror("poll");
+                       break;
+               }
+               if (pfd.revents & POLLIN)
+                       break;
+       }
+}
+
+static int read_data(struct thread_information *tip, void *buf,
+                         unsigned int len)
+{
+       int ret = 0;
+
+       do {
+               wait_for_data(tip, 100);
+
+               ret = read(tip->fd, buf, len);
+
+               if (!ret)
+                       continue;
+               else if (ret > 0)
+                       return ret;
+               else {
+                       if (errno != EAGAIN) {
+                               perror(tip->fn);
+                               fprintf(stderr, "Thread %d failed read of %s\n",
+                                       tip->cpu, tip->fn);
+                               break;
+                       }
+                       continue;
+               }
+       } while (!is_done());
+
+       return ret;
+
+}
+
+/*
+ * For file output, truncate and mmap the file appropriately
+ */
+static int mmap_subbuf(struct thread_information *tip, unsigned int maxlen)
+{
+       int ofd = fileno(tip->ofile);
+       int ret;
+       unsigned long nr;
+       unsigned long size;
+
+       /*
+        * extend file, if we have to. use chunks of 16 subbuffers.
+        */
+       if (tip->fs_off + maxlen > tip->fs_buf_len) {
+               if (tip->fs_buf) {
+                       munlock(tip->fs_buf, tip->fs_buf_len);
+                       munmap(tip->fs_buf, tip->fs_buf_len);
+                       tip->fs_buf = NULL;
+               }
+
+               tip->fs_off = tip->fs_size & (page_size - 1);
+               nr = max(16, tip->trace_info->buf_nr);
+               size = tip->trace_info->buf_size;
+               tip->fs_buf_len = (nr * size) - tip->fs_off;
+               tip->fs_max_size += tip->fs_buf_len;
+
+               if (ftruncate(ofd, tip->fs_max_size) < 0) {
+                       perror("ftruncate");
+                       return -1;
+               }
+
+               tip->fs_buf = mmap(NULL, tip->fs_buf_len, PROT_WRITE,
+                                  MAP_SHARED, ofd, tip->fs_size - tip->fs_off);
+               if (tip->fs_buf == MAP_FAILED) {
+                       perror("mmap");
+                       return -1;
+               }
+               mlock(tip->fs_buf, tip->fs_buf_len);
+       }
+
+       ret = tip->read_data(tip, tip->fs_buf + tip->fs_off, maxlen);
+       if (ret >= 0) {
+               tip->data_read += ret;
+               tip->fs_size += ret;
+               tip->fs_off += ret;
+               return 0;
+       }
+
+       return -1;
+}
+
+static void tip_ftrunc_final(struct thread_information *tip)
+{
+       /*
+        * truncate to right size and cleanup mmap
+        */
+       if (tip->ofile) {
+               int ofd = fileno(tip->ofile);
+
+               if (tip->fs_buf)
+                       munmap(tip->fs_buf, tip->fs_buf_len);
+
+               ftruncate(ofd, tip->fs_size);
+       }
+}
+
+static void *thread_main(void *arg)
+{
+       struct thread_information *tip = arg;
+       pid_t pid = getpid();
+       cpu_set_t cpu_mask;
+
+       CPU_ZERO(&cpu_mask);
+       CPU_SET((tip->cpu), &cpu_mask);
+
+       if (sched_setaffinity(pid, sizeof(cpu_mask), &cpu_mask) == -1) {
+               perror("sched_setaffinity");
+               exit_trace(1);
+       }
+
+       snprintf(tip->fn, sizeof(tip->fn), "%s/kvm/trace%d",
+                       debugfs_path, tip->cpu);
+       tip->fd = open(tip->fn, O_RDONLY);
+       if (tip->fd < 0) {
+               perror(tip->fn);
+               fprintf(stderr, "Thread %d failed open of %s\n", tip->cpu,
+                       tip->fn);
+               exit_trace(1);
+       }
+       while (!is_done()) {
+               if (tip->get_subbuf(tip, tip->trace_info->buf_size) < 0)
+                       break;
+       }
+
+       /*
+        * trace is stopped, pull data until we get a short read
+        */
+       while (tip->get_subbuf(tip, tip->trace_info->buf_size) > 0)
+               ;
+
+       tip_ftrunc_final(tip);
+       tip->exited = 1;
+       return NULL;
+}
+
+static int fill_ofname(struct thread_information *tip, char *dst)
+{
+       struct stat sb;
+       int len = 0;
+
+       if (output_dir)
+               len = sprintf(dst, "%s/", output_dir);
+       else
+               len = sprintf(dst, "./");
+
+       if (stat(dst, &sb) < 0) {
+               if (errno != ENOENT) {
+                       perror("stat");
+                       return 1;
+               }
+               if (mkdir(dst, 0755) < 0) {
+                       perror(dst);
+                       fprintf(stderr, "Can't make output dir\n");
+                       return 1;
+               }
+       }
+
+       sprintf(dst + len, "%s.kvmtrace.%d", output_name, tip->cpu);
+
+       return 0;
+}
+
+static void fill_ops(struct thread_information *tip)
+{
+       tip->get_subbuf = mmap_subbuf;
+       tip->read_data = read_data;
+}
+
+static void close_thread(struct thread_information *tip)
+{
+       if (tip->fd != -1)
+               close(tip->fd);
+       if (tip->ofile)
+               fclose(tip->ofile);
+       if (tip->ofile_buffer)
+               free(tip->ofile_buffer);
+
+       tip->fd = -1;
+       tip->ofile = NULL;
+       tip->ofile_buffer = NULL;
+}
+
+static int tip_open_output(struct thread_information *tip)
+{
+       int mode, vbuf_size;
+       char op[NAME_MAX];
+
+       if (fill_ofname(tip, op))
+               return 1;
+
+       tip->ofile = fopen(op, "w+");
+       mode = _IOFBF;
+       vbuf_size = OFILE_BUF;
+
+       if (tip->ofile == NULL) {
+               perror(op);
+               return 1;
+       }
+
+       tip->ofile_buffer = malloc(vbuf_size);
+       if (setvbuf(tip->ofile, tip->ofile_buffer, mode, vbuf_size)) {
+               perror("setvbuf");
+               close_thread(tip);
+               return 1;
+       }
+
+       fill_ops(tip);
+       return 0;
+}
+
+static int start_threads(int cpu)
+{
+       struct thread_information *tip;
+
+       tip = trace_information.threads + cpu;
+       tip->cpu = cpu;
+       tip->trace_info = &trace_information;
+       tip->fd = -1;
+
+       if (tip_open_output(tip))
+           return 1;
+
+       if (pthread_create(&tip->thread, NULL, thread_main, tip)) {
+               perror("pthread_create");
+               close_thread(tip);
+               return 1;
+       }
+
+       return 0;
+}
+
+static void stop_threads()
+{
+       struct thread_information *tip;
+       unsigned long ret;
+       int i;
+
+       for_each_tip(tip, i) {
+               if (tip->thread)
+                       (void) pthread_join(tip->thread, (void *) &ret);
+               close_thread(tip);
+       }
+}
+
+static int start_trace(void)
+{
+       int fd;
+       struct kvm_user_trace_setup kuts;
+
+       fd = trace_information.fd = open("/dev/kvm", O_RDWR);
+       if (fd == -1) {
+               perror("/dev/kvm");
+               return 1;
+       }
+
+       memset(&kuts, 0, sizeof(kuts));
+       kuts.buf_size = trace_information.buf_size = buf_size;
+       kuts.buf_nr = trace_information.buf_nr = buf_nr;
+
+       if (ioctl(trace_information.fd , KVM_TRACE_ENABLE, &kuts) < 0) {
+               perror("KVM_TRACE_ENABLE");
+               close(fd);
+               return 1;
+       }
+       trace_information.trace_started = 1;
+
+       return 0;
+}
+
+static void cleanup_trace(void)
+{
+       if (trace_information.fd == -1)
+               return;
+
+       trace_information.lost_records = get_lost_records();
+
+       if (trace_information.trace_started) {
+               trace_information.trace_started = 0;
+               if (ioctl(trace_information.fd, KVM_TRACE_DISABLE) < 0)
+                       perror("KVM_TRACE_DISABLE");
+       }
+
+       close(trace_information.fd);
+       trace_information.fd  = -1;
+}
+
+static void stop_all_traces(void)
+{
+       if (!is_trace_stopped()) {
+               trace_stopped = 1;
+               stop_threads();
+               cleanup_trace();
+       }
+}
+
+static void exit_trace(int status)
+{
+       stop_all_traces();
+       exit(status);
+}
+
+static int start_kvm_trace(void)
+{
+       int i, size;
+       struct thread_information *tip;
+
+       size = ncpus * sizeof(struct thread_information);
+       tip = malloc(size);
+       if (!tip) {
+               fprintf(stderr, "Out of memory, threads (%d)\n", size);
+               return 1;
+       }
+       memset(tip, 0, size);
+       trace_information.threads = tip;
+
+       if (start_trace())
+               return 1;
+
+       for_each_cpu_online(i) {
+               if (start_threads(i)) {
+                       fprintf(stderr, "Failed to start worker threads\n");
+                       break;
+               }
+       }
+
+       if (i != ncpus) {
+               stop_threads();
+               cleanup_trace();
+               return 1;
+       }
+
+       return 0;
+}
+
+static void wait_for_threads(void)
+{
+       struct thread_information *tip;
+       int i, tips_running;
+
+       do {
+               tips_running = 0;
+               usleep(100000);
+
+               for_each_tip(tip, i)
+                       tips_running += !tip->exited;
+
+       } while (tips_running);
+}
+
+static void show_stats(void)
+{
+       struct thread_information *tip;
+       unsigned long long data_read;
+       int i;
+
+       data_read = 0;
+       for_each_tip(tip, i) {
+               printf("  CPU%3d: %8llu KiB data\n",
+                       tip->cpu, (tip->data_read + 1023) >> 10);
+               data_read += tip->data_read;
+       }
+
+       printf("  Total:  lost %lu, %8llu KiB data\n",
+               trace_information.lost_records, (data_read + 1023) >> 10);
+
+       if (trace_information.lost_records)
+               fprintf(stderr, "You have lost records, "
+                               "consider using a larger buffer size (-b)\n");
+}
+
+static char usage_str[] = \
+       "[ -r debugfs path ] [ -D output dir ] [ -b buffer size ]\n" \
+       "[ -n number of buffers] [ -o <output file> ] [ -w time  ] [ -V ]\n\n" \
+       "\t-r Path to mounted debugfs, defaults to /sys/kernel/debug\n" \
+       "\t-o File(s) to send output to\n" \
+       "\t-D Directory to prepend to output file names\n" \
+       "\t-w Stop after defined time, in seconds\n" \
+       "\t-b Sub buffer size in KiB\n" \
+       "\t-n Number of sub buffers\n" \
+       "\t-V Print program version info\n\n";
+
+static void show_usage(char *prog)
+{
+       fprintf(stderr, "Usage: %s %s %s", prog, kvmtrace_version, usage_str);
+       exit(EXIT_FAILURE);
+}
+
+void parse_args(int argc, char **argv)
+{
+       int c;
+
+       while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) >= 0) {
+               switch (c) {
+               case 'r':
+                       debugfs_path = optarg;
+                       break;
+               case 'o':
+                       output_name = optarg;
+                       break;
+               case 'w':
+                       stop_watch = atoi(optarg);
+                       if (stop_watch <= 0) {
+                               fprintf(stderr,
+                                       "Invalid stopwatch value (%d secs)\n",
+                                       stop_watch);
+                               exit(EXIT_FAILURE);
+                       }
+                       break;
+               case 'V':
+                       printf("%s version %s\n", argv[0], kvmtrace_version);
+                       exit(EXIT_SUCCESS);
+               case 'b':
+                       buf_size = strtoul(optarg, NULL, 10);
+                       if (buf_size <= 0 || buf_size > 16*1024) {
+                               fprintf(stderr,
+                                       "Invalid buffer size (%lu)\n",
+                                       buf_size);
+                               exit(EXIT_FAILURE);
+                       }
+                       buf_size <<= 10;
+                       break;
+               case 'n':
+                       buf_nr = strtoul(optarg, NULL, 10);
+                       if (buf_nr <= 0) {
+                               fprintf(stderr,
+                                       "Invalid buffer nr (%lu)\n", buf_nr);
+                               exit(EXIT_FAILURE);
+                       }
+                       break;
+               case 'D':
+                       output_dir = optarg;
+                       break;
+               default:
+                       show_usage(argv[0]);
+               }
+       }
+
+       if (optind < argc || output_name == NULL)
+               show_usage(argv[0]);
+}
+
+int main(int argc, char *argv[])
+{
+       struct statfs st;
+
+       parse_args(argc, argv);
+
+       if (!debugfs_path)
+               debugfs_path = default_debugfs_path;
+
+       if (statfs(debugfs_path, &st) < 0) {
+               perror("statfs");
+               fprintf(stderr, "%s does not appear to be a valid path\n",
+                       debugfs_path);
+               return 1;
+       } else if (st.f_type != (long) DEBUGFS_TYPE) {
+               fprintf(stderr, "%s does not appear to be a debug filesystem,"
+                       " please mount debugfs.\n",
+                       debugfs_path);
+               return 1;
+       }
+
+       page_size = getpagesize();
+
+       ncpus = sysconf(_SC_NPROCESSORS_ONLN);
+       if (ncpus < 0) {
+               fprintf(stderr, "sysconf(_SC_NPROCESSORS_ONLN) failed\n");
+               return 1;
+       }
+
+       signal(SIGINT, handle_sigint);
+       signal(SIGHUP, handle_sigint);
+       signal(SIGTERM, handle_sigint);
+       signal(SIGALRM, handle_sigint);
+       signal(SIGPIPE, SIG_IGN);
+
+       if (start_kvm_trace() != 0)
+               return 1;
+
+       if (stop_watch)
+               alarm(stop_watch);
+
+       wait_for_threads();
+       stop_all_traces();
+       show_stats();
+
+       return 0;
+}
diff --git a/kvm-unittest/lib/argv.c b/kvm-unittest/lib/argv.c
new file mode 100644
index 0000000..4ee54a6
--- /dev/null
+++ b/kvm-unittest/lib/argv.c
@@ -0,0 +1,33 @@
+#include "libcflat.h"
+
+int __argc;
+char *__argv[100];
+char *__args;
+char __args_copy[1000];
+
+static bool isblank(char p)
+{
+    return p == ' ' || p == '\t';
+}
+
+static char *skip_blanks(char *p)
+{
+    while (isblank(*p))
+        ++p;
+    return p;
+}
+
+void __setup_args(void)
+{
+    char *args = __args;
+    char **argv = __argv;
+    char *p = __args_copy;
+
+    while (*(args = skip_blanks(args)) != '\0') {
+        *argv++ = p;
+        while (*args != '\0' && !isblank(*args))
+            *p++ = *args++;
+        *p++ = '\0';
+    }
+    __argc = argv - __argv;
+}
diff --git a/kvm-unittest/lib/fwcfg.c b/kvm-unittest/lib/fwcfg.c
new file mode 100644
index 0000000..dc34d29
--- /dev/null
+++ b/kvm-unittest/lib/fwcfg.c
@@ -0,0 +1,58 @@
+
+void qemu_cfg_select(int f)
+{
+    outw(QEMU_CFG_CTL_PORT, f);
+}
+
+int qemu_cfg_port_probe()
+{
+    char *sig = "QEMU";
+    int i;
+
+    qemu_cfg_select(QEMU_CFG_SIGNATURE);
+
+    for (i = 0; i < 4; i++)
+        if (inb(QEMU_CFG_DATA_PORT) != sig[i])
+            return 0;
+
+    return 1;
+}
+
+void qemu_cfg_read(uint8_t *buf, int len)
+{
+    while (len--)
+        *(buf++) = inb(QEMU_CFG_DATA_PORT);
+}
+
+uint8_t qemu_cfg_get8(void)
+{
+    uint8_t ret;
+
+    qemu_cfg_read(&ret, 1);
+    return ret;
+}
+
+uint16_t qemu_cfg_get16(void)
+{
+    uint16_t ret;
+
+    qemu_cfg_read((uint8_t*)&ret, 2);
+    return le16_to_cpu(ret);
+}
+
+uint64_t qemu_cfg_get32(void)
+{
+    uint32_t ret;
+
+    qemu_cfg_read((uint8_t*)&ret, 4);
+    return le32_to_cpu(ret);
+}
+
+uint64_t qemu_cfg_get64(void)
+{
+    uint64_t ret;
+
+    qemu_cfg_read((uint8_t*)&ret, 8);
+    return le64_to_cpu(ret);
+}
+
diff --git a/kvm-unittest/lib/panic.c b/kvm-unittest/lib/panic.c
new file mode 100644
index 0000000..6e0b29e
--- /dev/null
+++ b/kvm-unittest/lib/panic.c
@@ -0,0 +1,13 @@
+#include "libcflat.h"
+
+void panic(char *fmt, ...)
+{
+       va_list va;
+       char buf[2000];
+
+       va_start(va, fmt);
+       vsnprintf(buf, sizeof(buf), fmt, va);
+       va_end(va);
+       puts(buf);
+       exit(-1);
+}
diff --git a/kvm-unittest/lib/powerpc/44x/map.c 
b/kvm-unittest/lib/powerpc/44x/map.c
new file mode 100644
index 0000000..113434d
--- /dev/null
+++ b/kvm-unittest/lib/powerpc/44x/map.c
@@ -0,0 +1,51 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <address@hidden>
+ */
+
+#include "libcflat.h"
+
+#define TLB_SIZE 64
+
+extern void tlbwe(unsigned int index,
+                 unsigned char tid,
+                 unsigned int word0,
+                 unsigned int word1,
+                 unsigned int word2);
+
+unsigned int next_free_index;
+
+#define PAGE_SHIFT 12
+#define PAGE_MASK (~((1<<PAGE_SHIFT)-1))
+
+#define V (1<<9)
+
+void map(unsigned long vaddr, unsigned long paddr)
+{
+       unsigned int w0, w1, w2;
+
+       /* We don't install exception handlers, so we can't handle TLB misses,
+        * so we can't loop around and overwrite entry 0. */
+       if (next_free_index++ >= TLB_SIZE)
+               panic("TLB overflow");
+
+       w0 = (vaddr & PAGE_MASK) | V;
+       w1 = paddr & PAGE_MASK;
+       w2 = 0x3;
+
+       tlbwe(next_free_index, 0, w0, w1, w2);
+}
diff --git a/kvm-unittest/lib/powerpc/io.c b/kvm-unittest/lib/powerpc/io.c
new file mode 100644
index 0000000..8bd2395
--- /dev/null
+++ b/kvm-unittest/lib/powerpc/io.c
@@ -0,0 +1,35 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <address@hidden>
+ */
+
+#include "libcflat.h"
+
+#define BASE 0xf0000000
+#define _putc ((volatile char *)(BASE))
+#define _exit ((volatile char *)(BASE+1))
+
+void puts(const char *s)
+{
+       while (*s != '\0')
+               *_putc = *s++;
+}
+
+void exit(int code)
+{
+       *_exit = code;
+}
diff --git a/kvm-unittest/lib/printf.c b/kvm-unittest/lib/printf.c
new file mode 100644
index 0000000..867eb19
--- /dev/null
+++ b/kvm-unittest/lib/printf.c
@@ -0,0 +1,182 @@
+#include "libcflat.h"
+
+typedef struct pstream {
+    char *buffer;
+    int remain;
+    int added;
+} pstream_t;
+
+static void addchar(pstream_t *p, char c)
+{
+    if (p->remain) {
+       *p->buffer++ = c;
+       --p->remain;
+    }
+    ++p->added;
+}
+
+void print_str(pstream_t *p, const char *s)
+{
+    while (*s)
+       addchar(p, *s++);
+}
+
+static char digits[16] = "0123456789abcdef";
+
+void print_int(pstream_t *ps, long long n, int base)
+{
+    char buf[sizeof(long) * 3 + 2], *p = buf;
+    int s = 0, i;
+
+    if (n < 0) {
+       n = -n;
+       s = 1;
+    }
+
+    while (n) {
+       *p++ = digits[n % base];
+       n /= base;
+    }
+
+    if (s)
+       *p++ = '-';
+
+    if (p == buf)
+       *p++ = '0';
+
+    for (i = 0; i < (p - buf) / 2; ++i) {
+       char tmp;
+
+       tmp = buf[i];
+       buf[i] = p[-1-i];
+       p[-1-i] = tmp;
+    }
+
+    *p = 0;
+
+    print_str(ps, buf);
+}
+
+void print_unsigned(pstream_t *ps, unsigned long long n, int base)
+{
+    char buf[sizeof(long) * 3 + 1], *p = buf;
+    int i;
+
+    while (n) {
+       *p++ = digits[n % base];
+       n /= base;
+    }
+
+    if (p == buf)
+       *p++ = '0';
+
+    for (i = 0; i < (p - buf) / 2; ++i) {
+       char tmp;
+
+       tmp = buf[i];
+       buf[i] = p[-1-i];
+       p[-1-i] = tmp;
+    }
+
+    *p = 0;
+
+    print_str(ps, buf);
+}
+
+int vsnprintf(char *buf, int size, const char *fmt, va_list va)
+{
+    pstream_t s;
+
+    s.buffer = buf;
+    s.remain = size - 1;
+    s.added = 0;
+    while (*fmt) {
+       char f = *fmt++;
+       int nlong = 0;
+
+       if (f != '%') {
+           addchar(&s, f);
+           continue;
+       }
+    morefmt:
+       f = *fmt++;
+       switch (f) {
+       case '%':
+           addchar(&s, '%');
+           break;
+       case 'c':
+            addchar(&s, va_arg(va, int));
+           break;
+       case '\0':
+           --fmt;
+           break;
+       case 'l':
+           ++nlong;
+           goto morefmt;
+       case 'd':
+           switch (nlong) {
+           case 0:
+               print_int(&s, va_arg(va, int), 10);
+               break;
+           case 1:
+               print_int(&s, va_arg(va, long), 10);
+               break;
+           default:
+               print_int(&s, va_arg(va, long long), 10);
+               break;
+           }
+           break;
+       case 'x':
+           switch (nlong) {
+           case 0:
+               print_unsigned(&s, va_arg(va, unsigned), 16);
+               break;
+           case 1:
+               print_unsigned(&s, va_arg(va, unsigned long), 16);
+               break;
+           default:
+               print_unsigned(&s, va_arg(va, unsigned long long), 16);
+               break;
+           }
+           break;
+       case 'p':
+           print_str(&s, "0x");
+           print_unsigned(&s, (unsigned long)va_arg(va, void *), 16);
+           break;
+       case 's':
+           print_str(&s, va_arg(va, const char *));
+           break;
+       default:
+           addchar(&s, f);
+           break;
+       }
+    }
+    *s.buffer = 0;
+    ++s.added;
+    return s.added;
+}
+
+
+int snprintf(char *buf, int size, const char *fmt, ...)
+{
+    va_list va;
+    int r;
+
+    va_start(va, fmt);
+    r = vsnprintf(buf, size, fmt, va);
+    va_end(va);
+    return r;
+}
+
+int printf(const char *fmt, ...)
+{
+    va_list va;
+    char buf[2000];
+    int r;
+
+    va_start(va, fmt);
+    r = vsnprintf(buf, sizeof buf, fmt, va);
+    va_end(va);
+    puts(buf);
+    return r;
+}
diff --git a/kvm-unittest/lib/string.c b/kvm-unittest/lib/string.c
new file mode 100644
index 0000000..3a9caf7
--- /dev/null
+++ b/kvm-unittest/lib/string.c
@@ -0,0 +1,86 @@
+#include "libcflat.h"
+
+unsigned long strlen(const char *buf)
+{
+    unsigned long len = 0;
+
+    while (*buf++)
+       ++len;
+    return len;
+}
+
+char *strcat(char *dest, const char *src)
+{
+    char *p = dest;
+
+    while (*p)
+       ++p;
+    while ((*p++ = *src++) != 0)
+       ;
+    return dest;
+}
+
+int strcmp(const char *a, const char *b)
+{
+    while (*a == *b) {
+       if (*a == '\0') {
+           break;
+       }
+       ++a, ++b;
+    }
+    return *a - *b;
+}
+
+void *memset(void *s, int c, size_t n)
+{
+    size_t i;
+    char *a = s;
+
+    for (i = 0; i < n; ++i)
+        a[i] = c;
+
+    return s;
+}
+
+void *memcpy(void *dest, const void *src, size_t n)
+{
+    size_t i;
+    char *a = dest;
+    const char *b = src;
+
+    for (i = 0; i < n; ++i)
+        a[i] = b[i];
+
+    return dest;
+}
+
+long atol(const char *ptr)
+{
+    long acc = 0;
+    const char *s = ptr;
+    int neg, c;
+
+    while (*s == ' ' || *s == '\t')
+        s++;
+    if (*s == '-'){
+        neg = 1;
+        s++;
+    } else {
+        neg = 0;
+        if (*s == '+')
+            s++;
+    }
+
+    while (*s) {
+        if (*s < '0' || *s > '9')
+            break;
+        c = *s - '0';
+        acc = acc * 10 + c;
+        s++;
+    }
+
+    if (neg)
+        acc = -acc;
+
+    return acc;
+}
diff --git a/kvm-unittest/lib/x86/apic.c b/kvm-unittest/lib/x86/apic.c
new file mode 100644
index 0000000..7bb98ed
--- /dev/null
+++ b/kvm-unittest/lib/x86/apic.c
@@ -0,0 +1,143 @@
+#include "libcflat.h"
+#include "apic.h"
+
+static void *g_apic = (void *)0xfee00000;
+static void *g_ioapic = (void *)0xfec00000;
+
+struct apic_ops {
+    u32 (*reg_read)(unsigned reg);
+    void (*reg_write)(unsigned reg, u32 val);
+    void (*icr_write)(u32 val, u32 dest);
+    u32 (*id)(void);
+};
+
+static void outb(unsigned char data, unsigned short port)
+{
+    asm volatile ("out %0, %1" : : "a"(data), "d"(port));
+}
+
+static u32 xapic_read(unsigned reg)
+{
+    return *(volatile u32 *)(g_apic + reg);
+}
+
+static void xapic_write(unsigned reg, u32 val)
+{
+    *(volatile u32 *)(g_apic + reg) = val;
+}
+
+static void xapic_icr_write(u32 val, u32 dest)
+{
+    while (xapic_read(APIC_ICR) & APIC_ICR_BUSY)
+        ;
+    xapic_write(APIC_ICR2, dest << 24);
+    xapic_write(APIC_ICR, val);
+}
+
+static uint32_t xapic_id(void)
+{
+    return xapic_read(APIC_ID) >> 24;
+}
+
+static const struct apic_ops xapic_ops = {
+    .reg_read = xapic_read,
+    .reg_write = xapic_write,
+    .icr_write = xapic_icr_write,
+    .id = xapic_id,
+};
+
+static const struct apic_ops *apic_ops = &xapic_ops;
+
+static u32 x2apic_read(unsigned reg)
+{
+    unsigned a, d;
+
+    asm volatile ("rdmsr" : "=a"(a), "=d"(d) : "c"(APIC_BASE_MSR + reg/16));
+    return a | (u64)d << 32;
+}
+
+static void x2apic_write(unsigned reg, u32 val)
+{
+    asm volatile ("wrmsr" : : "a"(val), "d"(0), "c"(APIC_BASE_MSR + reg/16));
+}
+
+static void x2apic_icr_write(u32 val, u32 dest)
+{
+    asm volatile ("wrmsr" : : "a"(val), "d"(dest),
+                  "c"(APIC_BASE_MSR + APIC_ICR/16));
+}
+
+static uint32_t x2apic_id(void)
+{
+    return xapic_read(APIC_ID);
+}
+
+static const struct apic_ops x2apic_ops = {
+    .reg_read = x2apic_read,
+    .reg_write = x2apic_write,
+    .icr_write = x2apic_icr_write,
+    .id = x2apic_id,
+};
+
+u32 apic_read(unsigned reg)
+{
+    return apic_ops->reg_read(reg);
+}
+
+void apic_write(unsigned reg, u32 val)
+{
+    apic_ops->reg_write(reg, val);
+}
+
+void apic_icr_write(u32 val, u32 dest)
+{
+    apic_ops->icr_write(val, dest);
+}
+
+uint32_t apic_id(void)
+{
+    return apic_ops->id();
+}
+
+#define MSR_APIC_BASE 0x0000001b
+
+int enable_x2apic(void)
+{
+    unsigned a, b, c, d;
+
+    asm ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(1));
+
+    if (c & (1 << 21)) {
+        asm ("rdmsr" : "=a"(a), "=d"(d) : "c"(MSR_APIC_BASE));
+        a |= 1 << 10;
+        asm ("wrmsr" : : "a"(a), "d"(d), "c"(MSR_APIC_BASE));
+        apic_ops = &x2apic_ops;
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+void ioapic_write_reg(unsigned reg, u32 value)
+{
+    *(volatile u32 *)g_ioapic = reg;
+    *(volatile u32 *)(g_ioapic + 0x10) = value;
+}
+
+void ioapic_write_redir(unsigned line, ioapic_redir_entry_t e)
+{
+    ioapic_write_reg(0x10 + line * 2 + 0, ((u32 *)&e)[0]);
+    ioapic_write_reg(0x10 + line * 2 + 1, ((u32 *)&e)[1]);
+}
+
+void enable_apic(void)
+{
+    printf("enabling apic\n");
+    xapic_write(0xf0, 0x1ff); /* spurious vector register */
+}
+
+void mask_pic_interrupts(void)
+{
+    outb(0xff, 0x21);
+    outb(0xff, 0xa1);
+}
diff --git a/kvm-unittest/lib/x86/atomic.c b/kvm-unittest/lib/x86/atomic.c
new file mode 100644
index 0000000..da74ff2
--- /dev/null
+++ b/kvm-unittest/lib/x86/atomic.c
@@ -0,0 +1,37 @@
+#include <libcflat.h>
+#include "atomic.h"
+
+#ifdef __i386__
+
+u64 atomic64_cmpxchg(atomic64_t *v, u64 old, u64 new)
+{
+        u32 low = new;
+        u32 high = new >> 32;
+
+        asm volatile("lock cmpxchg8b %1\n"
+                     : "+A" (old),
+                       "+m" (*(volatile long long *)&v->counter)
+                     : "b" (low), "c" (high)
+                     : "memory"
+                     );
+
+        return old;
+}
+
+#else
+
+u64 atomic64_cmpxchg(atomic64_t *v, u64 old, u64 new)
+{
+        u64 ret;
+        u64 _old = old;
+        u64 _new = new;
+
+        asm volatile("lock cmpxchgq %2,%1"
+                     : "=a" (ret), "+m" (*(volatile long *)&v->counter)
+                     : "r" (_new), "0" (_old)
+                     : "memory"
+                     );
+        return ret;
+}
+
+#endif
diff --git a/kvm-unittest/lib/x86/desc.c b/kvm-unittest/lib/x86/desc.c
new file mode 100644
index 0000000..7c5c721
--- /dev/null
+++ b/kvm-unittest/lib/x86/desc.c
@@ -0,0 +1,355 @@
+#include "libcflat.h"
+#include "desc.h"
+#include "processor.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;
+#ifdef __x86_64__
+    unsigned offset2;
+    unsigned reserved;
+#endif
+} idt_entry_t;
+
+typedef struct {
+       u16 limit_low;
+       u16 base_low;
+       u8 base_middle;
+       u8 access;
+       u8 granularity;
+       u8 base_high;
+} gdt_entry_t;
+
+extern idt_entry_t boot_idt[256];
+
+void set_idt_entry(int vec, void *addr, int dpl)
+{
+    idt_entry_t *e = &boot_idt[vec];
+    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;
+#ifdef __x86_64__
+    e->offset2 = (unsigned long)addr >> 32;
+#endif
+}
+
+void set_idt_sel(int vec, u16 sel)
+{
+    idt_entry_t *e = &boot_idt[vec];
+    e->selector = sel;
+}
+
+struct ex_record {
+    unsigned long rip;
+    unsigned long handler;
+};
+
+extern struct ex_record exception_table_start, exception_table_end;
+
+static void check_exception_table(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 %d\n", regs->vector);
+    exit(7);
+}
+
+static void (*exception_handlers[32])(struct ex_regs *regs);
+
+
+void handle_exception(u8 v, void (*func)(struct ex_regs *regs))
+{
+       if (v < 32)
+               exception_handlers[v] = func;
+}
+
+#ifndef __x86_64__
+__attribute__((regparm(1)))
+#endif
+void do_handle_exception(struct ex_regs *regs)
+{
+       if (regs->vector < 32 && exception_handlers[regs->vector]) {
+               exception_handlers[regs->vector](regs);
+               return;
+       }
+       printf("unhandled cpu excecption %d\n", regs->vector);
+       if (regs->vector == 14)
+               printf("PF at %p addr %p\n", regs->rip, read_cr2());
+       exit(7);
+}
+
+#ifdef __x86_64__
+#  define R "r"
+#  define W "q"
+#  define S "8"
+#else
+#  define R "e"
+#  define W "l"
+#  define S "4"
+#endif
+
+#define EX(NAME, N) extern char NAME##_fault;  \
+       asm (".pushsection .text \n\t"          \
+            #NAME"_fault: \n\t"                \
+            "push"W" $0 \n\t"                  \
+            "push"W" $"#N" \n\t"               \
+            "jmp __handle_exception \n\t"      \
+            ".popsection")
+
+#define EX_E(NAME, N) extern char NAME##_fault;        \
+       asm (".pushsection .text \n\t"          \
+            #NAME"_fault: \n\t"                \
+            "push"W" $"#N" \n\t"               \
+            "jmp __handle_exception \n\t"      \
+            ".popsection")
+
+EX(de, 0);
+EX(db, 1);
+EX(nmi, 2);
+EX(bp, 3);
+EX(of, 4);
+EX(br, 5);
+EX(ud, 6);
+EX(nm, 7);
+EX_E(df, 8);
+EX_E(ts, 10);
+EX_E(np, 11);
+EX_E(ss, 12);
+EX_E(gp, 13);
+EX_E(pf, 14);
+EX(mf, 16);
+EX_E(ac, 17);
+EX(mc, 18);
+EX(xm, 19);
+
+asm (".pushsection .text \n\t"
+     "__handle_exception: \n\t"
+#ifdef __x86_64__
+     "push %r15; push %r14; push %r13; push %r12 \n\t"
+     "push %r11; push %r10; push %r9; push %r8 \n\t"
+#endif
+     "push %"R"di; push %"R"si; push %"R"bp; sub $"S", %"R"sp \n\t"
+     "push %"R"bx; push %"R"dx; push %"R"cx; push %"R"ax \n\t"
+#ifdef __x86_64__
+     "mov %"R"sp, %"R"di \n\t"
+#else
+     "mov %"R"sp, %"R"ax \n\t"
+#endif
+     "call do_handle_exception \n\t"
+     "pop %"R"ax; pop %"R"cx; pop %"R"dx; pop %"R"bx \n\t"
+     "add $"S", %"R"sp; pop %"R"bp; pop %"R"si; pop %"R"di \n\t"
+#ifdef __x86_64__
+     "pop %r8; pop %r9; pop %r10; pop %r11 \n\t"
+     "pop %r12; pop %r13; pop %r14; pop %r15 \n\t"
+#endif
+     "add $"S", %"R"sp \n\t"
+     "add $"S", %"R"sp \n\t"
+     "iret"W" \n\t"
+     ".popsection");
+
+static void *idt_handlers[32] = {
+       [0] = &de_fault,
+       [1] = &db_fault,
+       [2] = &nmi_fault,
+       [3] = &bp_fault,
+       [4] = &of_fault,
+       [5] = &br_fault,
+       [6] = &ud_fault,
+       [7] = &nm_fault,
+       [8] = &df_fault,
+       [10] = &ts_fault,
+       [11] = &np_fault,
+       [12] = &ss_fault,
+       [13] = &gp_fault,
+       [14] = &pf_fault,
+       [16] = &mf_fault,
+       [17] = &ac_fault,
+       [18] = &mc_fault,
+       [19] = &xm_fault,
+};
+
+void setup_idt(void)
+{
+    int i;
+    static bool idt_initialized = false;
+
+    if (idt_initialized) {
+        return;
+    }
+    idt_initialized = true;
+    for (i = 0; i < 32; i++)
+           if (idt_handlers[i])
+                   set_idt_entry(i, idt_handlers[i], 0);
+    handle_exception(0, check_exception_table);
+    handle_exception(6, check_exception_table);
+    handle_exception(13, check_exception_table);
+}
+
+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;
+}
+
+#ifndef __x86_64__
+/*
+ * GDT, with 6 entries:
+ * 0x00 - NULL descriptor
+ * 0x08 - Code segment
+ * 0x10 - Data segment
+ * 0x18 - Not presend code segment
+ * 0x20 - Primery task
+ * 0x28 - Interrupt task
+ *
+ * 0x30 to 0x48 - Free to use for test cases
+ */
+
+static gdt_entry_t gdt[10];
+#define TSS_GDT_OFFSET 4
+
+void set_gdt_entry(int num, u32 base,  u32 limit, u8 access, u8 gran)
+{
+       /* Setup the descriptor base address */
+       gdt[num].base_low = (base & 0xFFFF);
+       gdt[num].base_middle = (base >> 16) & 0xFF;
+       gdt[num].base_high = (base >> 24) & 0xFF;
+
+       /* Setup the descriptor limits */
+       gdt[num].limit_low = (limit & 0xFFFF);
+       gdt[num].granularity = ((limit >> 16) & 0x0F);
+
+       /* Finally, set up the granularity and access flags */
+       gdt[num].granularity |= (gran & 0xF0);
+       gdt[num].access = access;
+}
+
+void setup_gdt(void)
+{
+       struct descriptor_table_ptr gp;
+       /* Setup the GDT pointer and limit */
+       gp.limit = sizeof(gdt) - 1;
+       gp.base = (ulong)&gdt;
+
+       memset(gdt, 0, sizeof(gdt));
+
+       /* Our NULL descriptor */
+       set_gdt_entry(0, 0, 0, 0, 0);
+
+       /* The second entry is our Code Segment. The base address
+        *  is 0, the limit is 4GBytes, it uses 4KByte granularity,
+        *  uses 32-bit opcodes, and is a Code Segment descriptor. */
+       set_gdt_entry(1, 0, 0xFFFFFFFF, 0x9A, 0xcf);
+
+       /* The third entry is our Data Segment. It's EXACTLY the
+        *  same as our code segment, but the descriptor type in
+        *  this entry's access byte says it's a Data Segment */
+       set_gdt_entry(2, 0, 0xFFFFFFFF, 0x92, 0xcf);
+
+       /* Same as code register above but not present */
+       set_gdt_entry(3, 0, 0xFFFFFFFF, 0x1A, 0xcf);
+
+
+       /* Flush out the old GDT and install the new changes! */
+       lgdt(&gp);
+
+       asm volatile ("mov %0, %%ds\n\t"
+                     "mov %0, %%es\n\t"
+                     "mov %0, %%fs\n\t"
+                     "mov %0, %%gs\n\t"
+                     "mov %0, %%ss\n\t"
+                     "jmp $0x08, $.Lflush2\n\t"
+                     ".Lflush2: "::"r"(0x10));
+}
+
+void set_idt_task_gate(int vec, u16 sel)
+{
+    idt_entry_t *e = &boot_idt[vec];
+
+    memset(e, 0, sizeof *e);
+
+    e->selector = sel;
+    e->ist = 0;
+    e->type = 5;
+    e->dpl = 0;
+    e->p = 1;
+}
+
+/*
+ * 0 - main task
+ * 1 - interrupt task
+ */
+
+static tss32_t tss[2];
+static char tss_stack[2][4096];
+
+void setup_tss32(void)
+{
+       u16 desc_size = sizeof(tss32_t);
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               tss[i].cr3 = read_cr3();
+               tss[i].ss0 = tss[i].ss1 = tss[i].ss2 = 0x10;
+               tss[i].esp = tss[i].esp0 = tss[i].esp1 = tss[i].esp2 =
+                       (u32)tss_stack[i] + 4096;
+               tss[i].cs = 0x08;
+               tss[i].ds = tss[i].es = tss[i].fs = tss[i].gs = tss[i].ss = 
0x10;
+               tss[i].iomap_base = (u16)desc_size;
+               set_gdt_entry(TSS_GDT_OFFSET + i, (u32)&tss[i],
+                            desc_size - 1, 0x89, 0x0f);
+       }
+
+       ltr(TSS_MAIN);
+}
+
+void set_intr_task_gate(int e, void *fn)
+{
+       tss[1].eip = (u32)fn;
+       set_idt_task_gate(e, TSS_INTR);
+}
+
+void print_current_tss_info(void)
+{
+       u16 tr = str();
+       int i = (tr == TSS_MAIN) ? 0 : 1;
+
+       if (tr != TSS_MAIN && tr != TSS_INTR)
+               printf("Unknown TSS %x\n", tr);
+       else
+               printf("TR=%x Main TSS back link %x. Current TSS back link 
%x\n",
+               tr, tss[0].prev, tss[i].prev);
+}
+#endif
diff --git a/kvm-unittest/lib/x86/fwcfg.c b/kvm-unittest/lib/x86/fwcfg.c
new file mode 100644
index 0000000..e2cdd15
--- /dev/null
+++ b/kvm-unittest/lib/x86/fwcfg.c
@@ -0,0 +1,45 @@
+#include "fwcfg.h"
+#include "smp.h"
+
+static struct spinlock lock;
+
+uint64_t fwcfg_get_u(uint16_t index, int bytes)
+{
+    uint64_t r = 0;
+    uint8_t b;
+    int i;
+
+    spin_lock(&lock);
+    asm volatile ("out %0, %1" : : "a"(index), "d"((uint16_t)BIOS_CFG_IOPORT));
+    for (i = 0; i < bytes; ++i) {
+        asm volatile ("in %1, %0" : "=a"(b) : "d"((uint16_t)(BIOS_CFG_IOPORT + 
1)));
+        r |= (uint64_t)b << (i * 8);
+    }
+    spin_unlock(&lock);
+    return r;
+}
+
+uint8_t fwcfg_get_u8(unsigned index)
+{
+    return fwcfg_get_u(index, 1);
+}
+
+uint16_t fwcfg_get_u16(unsigned index)
+{
+    return fwcfg_get_u(index, 2);
+}
+
+uint32_t fwcfg_get_u32(unsigned index)
+{
+    return fwcfg_get_u(index, 4);
+}
+
+uint64_t fwcfg_get_u64(unsigned index)
+{
+    return fwcfg_get_u(index, 8);
+}
+
+unsigned fwcfg_get_nb_cpus(void)
+{
+    return fwcfg_get_u16(FW_CFG_NB_CPUS);
+}
diff --git a/kvm-unittest/lib/x86/io.c b/kvm-unittest/lib/x86/io.c
new file mode 100644
index 0000000..d3b971e
--- /dev/null
+++ b/kvm-unittest/lib/x86/io.c
@@ -0,0 +1,83 @@
+#include "libcflat.h"
+#include "smp.h"
+#include "io.h"
+#ifndef USE_SERIAL
+#define USE_SERIAL
+#endif
+
+static struct spinlock lock;
+static int serial_iobase = 0x3f8;
+static int serial_inited = 0;
+
+static void serial_outb(char ch)
+{
+        u8 lsr;
+
+        do {
+                lsr = inb(serial_iobase + 0x05);
+        } while (!(lsr & 0x20));
+
+        outb(ch, serial_iobase + 0x00);
+}
+
+static void serial_init(void)
+{
+        u8 lcr;
+
+        /* set DLAB */
+        lcr = inb(serial_iobase + 0x03);
+        lcr |= 0x80;
+        outb(lcr, serial_iobase + 0x03);
+
+        /* set baud rate to 115200 */
+        outb(0x01, serial_iobase + 0x00);
+        outb(0x00, serial_iobase + 0x01);
+
+        /* clear DLAB */
+        lcr = inb(serial_iobase + 0x03);
+        lcr &= ~0x80;
+        outb(lcr, serial_iobase + 0x03);
+}
+
+static void print_serial(const char *buf)
+{
+       unsigned long len = strlen(buf);
+#ifdef USE_SERIAL
+        unsigned long i;
+        if (!serial_inited) {
+            serial_init();
+            serial_inited = 1;
+        }
+
+        for (i = 0; i < len; i++) {
+            serial_outb(buf[i]);
+        }
+#else
+        asm volatile ("rep/outsb" : "+S"(buf), "+c"(len) : "d"(0xf1));
+#endif
+}
+
+void puts(const char *s)
+{
+       spin_lock(&lock);
+       print_serial(s);
+       spin_unlock(&lock);
+}
+
+void exit(int code)
+{
+#ifdef USE_SERIAL
+        static const char shutdown_str[8] = "Shutdown";
+        int i;
+
+        /* test device exit (with status) */
+        outl(code, 0xf4);
+
+        /* if that failed, try the Bochs poweroff port */
+        for (i = 0; i < 8; i++) {
+                outb(shutdown_str[i], 0x8900);
+        }
+#else
+        asm volatile("out %0, %1" : : "a"(code), "d"((short)0xf4));
+#endif
+}
diff --git a/kvm-unittest/lib/x86/isr.c b/kvm-unittest/lib/x86/isr.c
new file mode 100644
index 0000000..9986d17
--- /dev/null
+++ b/kvm-unittest/lib/x86/isr.c
@@ -0,0 +1,98 @@
+#include "libcflat.h"
+#include "isr.h"
+#include "vm.h"
+#include "desc.h"
+
+#ifdef __x86_64__
+#  define R "r"
+#else
+#  define R "e"
+#endif
+
+extern char isr_entry_point[];
+
+asm (
+    "isr_entry_point: \n"
+#ifdef __x86_64__
+    "push %r15 \n\t"
+    "push %r14 \n\t"
+    "push %r13 \n\t"
+    "push %r12 \n\t"
+    "push %r11 \n\t"
+    "push %r10 \n\t"
+    "push %r9  \n\t"
+    "push %r8  \n\t"
+#endif
+    "push %"R "di \n\t"
+    "push %"R "si \n\t"
+    "push %"R "bp \n\t"
+    "push %"R "sp \n\t"
+    "push %"R "bx \n\t"
+    "push %"R "dx \n\t"
+    "push %"R "cx \n\t"
+    "push %"R "ax \n\t"
+#ifdef __x86_64__
+    "mov %rsp, %rdi \n\t"
+    "callq *8*16(%rsp) \n\t"
+#else
+    "push %esp \n\t"
+    "calll *4+4*8(%esp) \n\t"
+    "add $4, %esp \n\t"
+#endif
+    "pop %"R "ax \n\t"
+    "pop %"R "cx \n\t"
+    "pop %"R "dx \n\t"
+    "pop %"R "bx \n\t"
+    "pop %"R "bp \n\t"
+    "pop %"R "bp \n\t"
+    "pop %"R "si \n\t"
+    "pop %"R "di \n\t"
+#ifdef __x86_64__
+    "pop %r8  \n\t"
+    "pop %r9  \n\t"
+    "pop %r10 \n\t"
+    "pop %r11 \n\t"
+    "pop %r12 \n\t"
+    "pop %r13 \n\t"
+    "pop %r14 \n\t"
+    "pop %r15 \n\t"
+#endif
+    ".globl isr_iret_ip\n\t"
+#ifdef __x86_64__
+    "add $8, %rsp \n\t"
+    "isr_iret_ip: \n\t"
+    "iretq \n\t"
+#else
+    "add $4, %esp \n\t"
+    "isr_iret_ip: \n\t"
+    "iretl \n\t"
+#endif
+    );
+
+void handle_irq(unsigned vec, void (*func)(isr_regs_t *regs))
+{
+    u8 *thunk = vmalloc(50);
+
+    set_idt_entry(vec, thunk, 0);
+
+#ifdef __x86_64__
+    /* sub $8, %rsp */
+    *thunk++ = 0x48; *thunk++ = 0x83; *thunk++ = 0xec; *thunk++ = 0x08;
+    /* mov $func_low, %(rsp) */
+    *thunk++ = 0xc7; *thunk++ = 0x04; *thunk++ = 0x24;
+    *(u32 *)thunk = (ulong)func; thunk += 4;
+    /* mov $func_high, %(rsp+4) */
+    *thunk++ = 0xc7; *thunk++ = 0x44; *thunk++ = 0x24; *thunk++ = 0x04;
+    *(u32 *)thunk = (ulong)func >> 32; thunk += 4;
+    /* jmp isr_entry_point */
+    *thunk ++ = 0xe9;
+    *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4);
+#else
+    /* push $func */
+    *thunk++ = 0x68;
+    *(u32 *)thunk = (ulong)func; thunk += 4;
+    /* jmp isr_entry_point */
+    *thunk++ = 0xe9;
+    *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4);
+#endif
+}
diff --git a/kvm-unittest/lib/x86/pci.c b/kvm-unittest/lib/x86/pci.c
new file mode 100644
index 0000000..f95cd88
--- /dev/null
+++ b/kvm-unittest/lib/x86/pci.c
@@ -0,0 +1,55 @@
+#include <linux/pci_regs.h>
+#include "pci.h"
+
+static void outl(unsigned short port, unsigned val)
+{
+    asm volatile("outl %d0, %w1" : : "a"(val), "Nd"(port));
+}
+
+static unsigned inl(unsigned short port)
+{
+    unsigned data;
+    asm volatile("inl %w1, %d0" : "=a"(data) : "Nd"(port));
+    return data;
+}
+static uint32_t pci_config_read(pcidevaddr_t dev, uint8_t reg)
+{
+    uint32_t index = reg | (dev << 8) | (0x1 << 31); 
+    outl(0xCF8, index);
+    return inl(0xCFC);
+}
+
+/* Scan bus look for a specific device. Only bus 0 scanned for now. */
+pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)
+{
+    unsigned dev;
+    for (dev = 0; dev < 256; ++dev) {
+    uint32_t id = pci_config_read(dev, 0);
+    if ((id & 0xFFFF) == vendor_id && (id >> 16) == device_id) {
+        return dev;
+    }
+    }
+    return PCIDEVADDR_INVALID;
+}
+
+unsigned long pci_bar_addr(pcidevaddr_t dev, int bar_num)
+{
+    uint32_t bar = pci_config_read(dev, PCI_BASE_ADDRESS_0 + bar_num * 4);
+    if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
+        return bar & PCI_BASE_ADDRESS_IO_MASK;
+    } else {
+        return bar & PCI_BASE_ADDRESS_MEM_MASK;
+    }
+}
+
+bool pci_bar_is_memory(pcidevaddr_t dev, int bar_num)
+{
+    uint32_t bar = pci_config_read(dev, PCI_BASE_ADDRESS_0 + bar_num * 4);
+    return !(bar & PCI_BASE_ADDRESS_SPACE_IO);
+}
+
+bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num)
+{
+    uint32_t bar = pci_config_read(dev, PCI_BASE_ADDRESS_0 + bar_num * 4);
+    return bar;
+}
diff --git a/kvm-unittest/lib/x86/smp.c b/kvm-unittest/lib/x86/smp.c
new file mode 100644
index 0000000..d4c8106
--- /dev/null
+++ b/kvm-unittest/lib/x86/smp.c
@@ -0,0 +1,124 @@
+
+#include <libcflat.h>
+#include "smp.h"
+#include "apic.h"
+#include "fwcfg.h"
+
+#define IPI_VECTOR 0x20
+
+typedef void (*ipi_function_type)(void *data);
+
+static struct spinlock ipi_lock;
+static volatile ipi_function_type ipi_function;
+static volatile void *ipi_data;
+static volatile int ipi_done;
+static volatile bool ipi_wait;
+static int _cpu_count;
+
+static __attribute__((used)) void ipi()
+{
+    void (*function)(void *data) = ipi_function;
+    void *data = ipi_data;
+    bool wait = ipi_wait;
+
+    if (!wait) {
+       ipi_done = 1;
+       apic_write(APIC_EOI, 0);
+    }
+    function(data);
+    if (wait) {
+       ipi_done = 1;
+       apic_write(APIC_EOI, 0);
+    }
+}
+
+asm (
+     "ipi_entry: \n"
+     "   call ipi \n"
+#ifndef __x86_64__
+     "   iret"
+#else
+     "   iretq"
+#endif
+     );
+
+void spin_lock(struct spinlock *lock)
+{
+    int v = 1;
+
+    do {
+       asm volatile ("xchg %1, %0" : "+m"(lock->v), "+r"(v));
+    } while (v);
+    asm volatile ("" : : : "memory");
+}
+
+void spin_unlock(struct spinlock *lock)
+{
+    asm volatile ("" : : : "memory");
+    lock->v = 0;
+}
+
+int cpu_count(void)
+{
+    return _cpu_count;
+}
+
+int smp_id(void)
+{
+    unsigned id;
+
+    asm ("mov %%gs:0, %0" : "=r"(id));
+    return id;
+}
+
+static void setup_smp_id(void *data)
+{
+    asm ("mov %0, %%gs:0" : : "r"(apic_id()) : "memory");
+}
+
+static void __on_cpu(int cpu, void (*function)(void *data), void *data,
+                     int wait)
+{
+    spin_lock(&ipi_lock);
+    if (cpu == smp_id())
+       function(data);
+    else {
+       ipi_done = 0;
+       ipi_function = function;
+       ipi_data = data;
+       ipi_wait = wait;
+       apic_icr_write(APIC_INT_ASSERT | APIC_DEST_PHYSICAL | APIC_DM_FIXED
+                       | IPI_VECTOR,
+                       cpu);
+       while (!ipi_done)
+           ;
+    }
+    spin_unlock(&ipi_lock);
+}
+
+void on_cpu(int cpu, void (*function)(void *data), void *data)
+{
+    __on_cpu(cpu, function, data, 1);
+}
+
+void on_cpu_async(int cpu, void (*function)(void *data), void *data)
+{
+    __on_cpu(cpu, function, data, 0);
+}
+
+
+void smp_init(void)
+{
+    int i;
+    void ipi_entry(void);
+
+    _cpu_count = fwcfg_get_nb_cpus();
+
+    setup_idt();
+    set_idt_entry(IPI_VECTOR, ipi_entry, 0);
+
+    setup_smp_id(0);
+    for (i = 1; i < cpu_count(); ++i)
+        on_cpu(i, setup_smp_id, 0);
+
+}
diff --git a/kvm-unittest/lib/x86/vm.c b/kvm-unittest/lib/x86/vm.c
new file mode 100644
index 0000000..188bf57
--- /dev/null
+++ b/kvm-unittest/lib/x86/vm.c
@@ -0,0 +1,256 @@
+#include "fwcfg.h"
+#include "vm.h"
+#include "libcflat.h"
+
+#define PAGE_SIZE 4096ul
+#ifdef __x86_64__
+#define LARGE_PAGE_SIZE (512 * PAGE_SIZE)
+#else
+#define LARGE_PAGE_SIZE (1024 * PAGE_SIZE)
+#endif
+
+static void *free = 0;
+static void *vfree_top = 0;
+
+static void free_memory(void *mem, unsigned long size)
+{
+    while (size >= PAGE_SIZE) {
+       *(void **)mem = free;
+       free = mem;
+       mem += PAGE_SIZE;
+       size -= PAGE_SIZE;
+    }
+}
+
+void *alloc_page()
+{
+    void *p;
+
+    if (!free)
+       return 0;
+
+    p = free;
+    free = *(void **)free;
+
+    return p;
+}
+
+void free_page(void *page)
+{
+    *(void **)page = free;
+    free = page;
+}
+
+extern char edata;
+static unsigned long end_of_memory;
+
+#ifdef __x86_64__
+#define        PAGE_LEVEL      4
+#define        PGDIR_WIDTH     9
+#define        PGDIR_MASK      511
+#else
+#define        PAGE_LEVEL      2
+#define        PGDIR_WIDTH     10
+#define        PGDIR_MASK      1023
+#endif
+
+void install_pte(unsigned long *cr3,
+                int pte_level,
+                void *virt,
+                unsigned long pte,
+                unsigned long *pt_page)
+{
+    int level;
+    unsigned long *pt = cr3;
+    unsigned offset;
+
+    for (level = PAGE_LEVEL; level > pte_level; --level) {
+       offset = ((unsigned long)virt >> ((level-1) * PGDIR_WIDTH + 12)) & 
PGDIR_MASK;
+       if (!(pt[offset] & PTE_PRESENT)) {
+           unsigned long *new_pt = pt_page;
+            if (!new_pt)
+                new_pt = alloc_page();
+            else
+                pt_page = 0;
+           memset(new_pt, 0, PAGE_SIZE);
+           pt[offset] = virt_to_phys(new_pt) | PTE_PRESENT | PTE_WRITE;
+       }
+       pt = phys_to_virt(pt[offset] & 0xffffffffff000ull);
+    }
+    offset = ((unsigned long)virt >> ((level-1) * PGDIR_WIDTH + 12)) & 
PGDIR_MASK;
+    pt[offset] = pte;
+}
+
+static unsigned long get_pte(unsigned long *cr3, void *virt)
+{
+    int level;
+    unsigned long *pt = cr3, pte;
+    unsigned offset;
+
+    for (level = PAGE_LEVEL; level > 1; --level) {
+       offset = ((unsigned long)virt >> (((level-1) * PGDIR_WIDTH) + 12)) & 
PGDIR_MASK;
+       pte = pt[offset];
+       if (!(pte & PTE_PRESENT))
+           return 0;
+       if (level == 2 && (pte & PTE_PSE))
+           return pte;
+       pt = phys_to_virt(pte & 0xffffffffff000ull);
+    }
+    offset = ((unsigned long)virt >> (((level-1) * PGDIR_WIDTH) + 12)) & 
PGDIR_MASK;
+    pte = pt[offset];
+    return pte;
+}
+
+void install_large_page(unsigned long *cr3,
+                              unsigned long phys,
+                              void *virt)
+{
+    install_pte(cr3, 2, virt, phys | PTE_PRESENT | PTE_WRITE | PTE_USER | 
PTE_PSE, 0);
+}
+
+void install_page(unsigned long *cr3,
+                  unsigned long phys,
+                  void *virt)
+{
+    install_pte(cr3, 1, virt, phys | PTE_PRESENT | PTE_WRITE | PTE_USER, 0);
+}
+
+
+static inline void load_gdt(unsigned long *table, int nent)
+{
+    struct descriptor_table_ptr descr;
+
+    descr.limit = nent * 8 - 1;
+    descr.base = (ulong)table;
+    lgdt(&descr);
+}
+
+#define SEG_CS_32 8
+#define SEG_CS_64 16
+
+struct ljmp {
+    void *ofs;
+    unsigned short seg;
+};
+
+static void setup_mmu_range(unsigned long *cr3, unsigned long start,
+                           unsigned long len)
+{
+       u64 max = (u64)len + (u64)start;
+       u64 phys = start;
+
+       while (phys + LARGE_PAGE_SIZE <= max) {
+               install_large_page(cr3, phys, (void *)(ulong)phys);
+               phys += LARGE_PAGE_SIZE;
+       }
+       while (phys + PAGE_SIZE <= max) {
+               install_page(cr3, phys, (void *)(ulong)phys);
+               phys += PAGE_SIZE;
+       }
+}
+
+static void setup_mmu(unsigned long len)
+{
+    unsigned long *cr3 = alloc_page();
+
+    memset(cr3, 0, PAGE_SIZE);
+
+#ifdef __x86_64__
+    if (len < (1ul << 32))
+        len = (1ul << 32);  /* map mmio 1:1 */
+
+    setup_mmu_range(cr3, 0, len);
+#else
+    if (len > (1ul << 31))
+           len = (1ul << 31);
+
+    /* 0 - 2G memory, 2G-3G valloc area, 3G-4G mmio */
+    setup_mmu_range(cr3, 0, len);
+    setup_mmu_range(cr3, 3ul << 30, (1ul << 30));
+    vfree_top = (void*)(3ul << 30);
+#endif
+
+    write_cr3(virt_to_phys(cr3));
+#ifndef __x86_64__
+    write_cr4(X86_CR4_PSE);
+#endif
+    write_cr0(X86_CR0_PG |X86_CR0_PE | X86_CR0_WP);
+
+    printf("paging enabled\n");
+    printf("cr0 = %x\n", read_cr0());
+    printf("cr3 = %x\n", read_cr3());
+    printf("cr4 = %x\n", read_cr4());
+}
+
+void setup_vm()
+{
+    end_of_memory = fwcfg_get_u64(FW_CFG_RAM_SIZE);
+    free_memory(&edata, end_of_memory - (unsigned long)&edata);
+    setup_mmu(end_of_memory);
+}
+
+void *vmalloc(unsigned long size)
+{
+    void *mem, *p;
+    unsigned pages;
+
+    size += sizeof(unsigned long);
+
+    size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+    vfree_top -= size;
+    mem = p = vfree_top;
+    pages = size / PAGE_SIZE;
+    while (pages--) {
+       install_page(phys_to_virt(read_cr3()), virt_to_phys(alloc_page()), p);
+       p += PAGE_SIZE;
+    }
+    *(unsigned long *)mem = size;
+    mem += sizeof(unsigned long);
+    return mem;
+}
+
+uint64_t virt_to_phys_cr3(void *mem)
+{
+    return (get_pte(phys_to_virt(read_cr3()), mem) & PTE_ADDR) + ((ulong)mem & 
(PAGE_SIZE - 1));
+}
+
+void vfree(void *mem)
+{
+    unsigned long size = ((unsigned long *)mem)[-1];
+
+    while (size) {
+       free_page(phys_to_virt(get_pte(phys_to_virt(read_cr3()), mem) & 
PTE_ADDR));
+       mem += PAGE_SIZE;
+       size -= PAGE_SIZE;
+    }
+}
+
+void *vmap(unsigned long long phys, unsigned long size)
+{
+    void *mem, *p;
+    unsigned pages;
+
+    size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+    vfree_top -= size;
+    phys &= ~(unsigned long long)(PAGE_SIZE - 1);
+
+    mem = p = vfree_top;
+    pages = size / PAGE_SIZE;
+    while (pages--) {
+       install_page(phys_to_virt(read_cr3()), phys, p);
+       phys += PAGE_SIZE;
+       p += PAGE_SIZE;
+    }
+    return mem;
+}
+
+void *alloc_vpages(ulong nr)
+{
+       vfree_top -= PAGE_SIZE * nr;
+       return vfree_top;
+}
+
+void *alloc_vpage(void)
+{
+    return alloc_vpages(1);
+}
diff --git a/kvm-unittest/main-ppc.c b/kvm-unittest/main-ppc.c
new file mode 100644
index 0000000..5af59f8
--- /dev/null
+++ b/kvm-unittest/main-ppc.c
@@ -0,0 +1,383 @@
+/*
+ * Kernel-based Virtual Machine test driver
+ *
+ * This test driver provides a simple way of testing kvm, without a full
+ * device model.
+ *
+ * Copyright (C) 2006 Qumranet
+ * Copyright IBM Corp. 2008
+ *
+ * Authors:
+ *
+ *  Avi Kivity <address@hidden>
+ *  Yaniv Kamay <address@hidden>
+ *  Hollis Blanchard <address@hidden>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#define _GNU_SOURCE
+
+#include <libkvm.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <semaphore.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/syscall.h>
+#include <linux/unistd.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include "iotable.h"
+
+static int gettid(void)
+{
+       return syscall(__NR_gettid);
+}
+
+kvm_context_t kvm;
+
+#define IPI_SIGNAL (SIGRTMIN + 4)
+
+struct io_table mmio_table;
+
+static int ncpus = 1;
+static sem_t exited_sem;
+static __thread int vcpu;
+static sigset_t kernel_sigmask;
+static sigset_t ipi_sigmask;
+static uint64_t memory_size = 128 * 1024 * 1024;
+
+struct vcpu_info {
+       pid_t tid;
+};
+
+struct vcpu_info *vcpus;
+
+/* Must match flat.lds linker script */
+#define VM_TEST_LOAD_ADDRESS 0x100000
+
+static int test_debug(void *opaque, void *vcpu)
+{
+       printf("test_debug\n");
+       return 0;
+}
+
+static int test_halt(void *opaque, int vcpu)
+{
+       int n;
+
+       sigwait(&ipi_sigmask, &n);
+       return 0;
+}
+
+static int test_io_window(void *opaque)
+{
+       return 0;
+}
+
+static int test_try_push_interrupts(void *opaque)
+{
+       return 0;
+}
+
+static void test_post_kvm_run(void *opaque, void *vcpu)
+{
+}
+
+static int test_pre_kvm_run(void *opaque, void *vcpu)
+{
+       return 0;
+}
+
+static int mmio_handler(void *opaque, int len, int is_write, uint64_t offset,
+                        uint64_t *data)
+{
+       int r = 0;
+
+       switch (offset) {
+       case 0: /* putc */
+               putc(*(char *)data, stdout);
+               fflush(stdout);
+               break;
+       case 1: /* exit */
+               r = *(char *)data;
+               break;
+       default:
+               printf("%s: offset %"PRIx64" len %d data %"PRIx64"\n",
+                      __func__, offset, len, *(uint64_t *)data);
+               r = -EINVAL;
+       }
+
+       return r;
+}
+
+static int test_mem_read(void *opaque, uint64_t addr, uint8_t *data, int len)
+{
+       struct io_table_entry *iodev;
+
+#if 0
+       printf("%s: addr %"PRIx64" len %d\n", __func__, addr, len);
+#endif
+
+       iodev = io_table_lookup(&mmio_table, addr);
+       if (!iodev) {
+               printf("couldn't find device\n");
+               return -ENODEV;
+       }
+
+       return iodev->handler(iodev->opaque, len, 0, addr - iodev->start,
+                             (uint64_t *)data);
+}
+
+static int test_mem_write(void *opaque, uint64_t addr, uint8_t *data, int len)
+{
+       struct io_table_entry *iodev;
+
+#if 0
+       printf("%s: addr %"PRIx64" len %d data %"PRIx64"\n",
+              __func__, addr, len, *(uint64_t *)data);
+#endif
+
+       iodev = io_table_lookup(&mmio_table, addr);
+       if (!iodev) {
+               printf("couldn't find device\n");
+               return -ENODEV;
+       }
+
+       return iodev->handler(iodev->opaque, len, 1, addr - iodev->start,
+                             (uint64_t *)data);
+}
+
+static int test_dcr_read(int vcpu, uint32_t dcrn, uint32_t *data)
+{
+       printf("%s: dcrn %04X\n", __func__, dcrn);
+       *data = 0;
+       return 0;
+}
+
+static int test_dcr_write(int vcpu, uint32_t dcrn, uint32_t data)
+{
+       printf("%s: dcrn %04X data %04X\n", __func__, dcrn, data);
+       return 0;
+}
+
+static struct kvm_callbacks test_callbacks = {
+       .mmio_read   = test_mem_read,
+       .mmio_write  = test_mem_write,
+       .debug       = test_debug,
+       .halt        = test_halt,
+       .io_window = test_io_window,
+       .try_push_interrupts = test_try_push_interrupts,
+       .post_kvm_run = test_post_kvm_run,
+       .pre_kvm_run = test_pre_kvm_run,
+       .powerpc_dcr_read = test_dcr_read,
+       .powerpc_dcr_write = test_dcr_write,
+};
+
+static unsigned long load_file(void *mem, const char *fname, int inval_icache)
+{
+       ssize_t r;
+       int fd;
+       unsigned long bytes = 0;
+
+       fd = open(fname, O_RDONLY);
+       if (fd == -1) {
+               perror("open");
+               exit(1);
+       }
+
+       while ((r = read(fd, mem, 4096)) != -1 && r != 0) {
+               mem += r;
+               bytes += r;
+       }
+
+       if (r == -1) {
+               perror("read");
+               printf("read %d bytes\n", bytes);
+               exit(1);
+       }
+
+       return bytes;
+}
+
+#define ICACHE_LINE_SIZE 32
+
+void sync_caches(void *mem, unsigned long len)
+{
+       unsigned long i;
+
+       for (i = 0; i < len; i += ICACHE_LINE_SIZE)
+               asm volatile ("dcbst %0, %1" : : "g"(mem), "r"(i));
+       asm volatile ("sync");
+       for (i = 0; i < len; i += ICACHE_LINE_SIZE)
+               asm volatile ("icbi %0, %1" : : "g"(mem), "r"(i));
+       asm volatile ("sync; isync");
+}
+
+static void init_vcpu(int n)
+{
+       sigemptyset(&ipi_sigmask);
+       sigaddset(&ipi_sigmask, IPI_SIGNAL);
+       sigprocmask(SIG_UNBLOCK, &ipi_sigmask, NULL);
+       sigprocmask(SIG_BLOCK, &ipi_sigmask, &kernel_sigmask);
+       vcpus[n].tid = gettid();
+       vcpu = n;
+       kvm_set_signal_mask(kvm, n, &kernel_sigmask);
+}
+
+static void *do_create_vcpu(void *_n)
+{
+       struct kvm_regs regs;
+       int n = (long)_n;
+
+       kvm_create_vcpu(kvm, n);
+       init_vcpu(n);
+
+       kvm_get_regs(kvm, n, &regs);
+       regs.pc = VM_TEST_LOAD_ADDRESS;
+       kvm_set_regs(kvm, n, &regs);
+
+       kvm_run(kvm, n, &vcpus[n]);
+       sem_post(&exited_sem);
+       return NULL;
+}
+
+static void start_vcpu(int n)
+{
+       pthread_t thread;
+
+       pthread_create(&thread, NULL, do_create_vcpu, (void *)(long)n);
+}
+
+static void usage(const char *progname)
+{
+       fprintf(stderr,
+"Usage: %s [OPTIONS] [bootstrap] flatfile\n"
+"KVM test harness.\n"
+"\n"
+"  -s, --smp=NUM          create a VM with NUM virtual CPUs\n"
+"  -m, --memory=NUM[GMKB] allocate NUM memory for virtual machine.  A suffix\n"
+"                         can be used to change the unit (default: `M')\n"
+"  -h, --help             display this help screen and exit\n"
+"\n"
+"Report bugs to <address@hidden>.\n"
+               , progname);
+}
+
+static void sig_ignore(int sig)
+{
+       write(1, "boo\n", 4);
+}
+
+int main(int argc, char **argv)
+{
+       void *vm_mem;
+       unsigned long len;
+       int i;
+       const char *sopts = "s:phm:";
+       struct option lopts[] = {
+               { "smp", 1, 0, 's' },
+               { "memory", 1, 0, 'm' },
+               { "help", 0, 0, 'h' },
+               { 0 },
+       };
+       int opt_ind, ch;
+       int nb_args;
+       char *endptr;
+
+       while ((ch = getopt_long(argc, argv, sopts, lopts, &opt_ind)) != -1) {
+               switch (ch) {
+               case 's':
+                       ncpus = atoi(optarg);
+                       break;
+               case 'm':
+                       memory_size = strtoull(optarg, &endptr, 0);
+                       switch (*endptr) {
+                       case 'G': case 'g':
+                               memory_size <<= 30;
+                               break;
+                       case '\0':
+                       case 'M': case 'm':
+                               memory_size <<= 20;
+                               break;
+                       case 'K': case 'k':
+                               memory_size <<= 10;
+                               break;
+                       default:
+                               fprintf(stderr,
+                                       "Unrecongized memory suffix: %c\n",
+                                       *endptr);
+                               exit(1);
+                       }
+                       if (memory_size == 0) {
+                               fprintf(stderr,
+                                       "Invalid memory size: 0\n");
+                               exit(1);
+                       }
+                       break;
+               case 'h':
+                       usage(argv[0]);
+                       exit(0);
+               case '?':
+               default:
+                       fprintf(stderr,
+                               "Try `%s --help' for more information.\n",
+                               argv[0]);
+                       exit(1);
+               }
+       }
+
+       nb_args = argc - optind;
+       if (nb_args < 1 || nb_args > 2) {
+               fprintf(stderr,
+                       "Incorrect number of arguments.\n"
+                       "Try `%s --help' for more information.\n",
+                       argv[0]);
+               exit(1);
+       }
+
+       signal(IPI_SIGNAL, sig_ignore);
+
+       vcpus = calloc(ncpus, sizeof *vcpus);
+       if (!vcpus) {
+               fprintf(stderr, "calloc failed\n");
+               return 1;
+       }
+
+       kvm = kvm_init(&test_callbacks, 0);
+       if (!kvm) {
+               fprintf(stderr, "kvm_init failed\n");
+               return 1;
+       }
+       if (kvm_create(kvm, memory_size, &vm_mem) < 0) {
+               kvm_finalize(kvm);
+               fprintf(stderr, "kvm_create failed\n");
+               return 1;
+       }
+
+       vm_mem = kvm_create_phys_mem(kvm, 0, memory_size, 0, 1);
+
+       len = load_file(vm_mem + VM_TEST_LOAD_ADDRESS, argv[optind], 1);
+       sync_caches(vm_mem + VM_TEST_LOAD_ADDRESS, len);
+
+       io_table_register(&mmio_table, 0xf0000000, 64, mmio_handler, NULL);
+
+       sem_init(&exited_sem, 0, 0);
+       for (i = 0; i < ncpus; ++i)
+               start_vcpu(i);
+       /* Wait for all vcpus to exit. */
+       for (i = 0; i < ncpus; ++i)
+               sem_wait(&exited_sem);
+
+       return 0;
+}
diff --git a/kvm-unittest/powerpc/exit.c b/kvm-unittest/powerpc/exit.c
new file mode 100644
index 0000000..804ee04
--- /dev/null
+++ b/kvm-unittest/powerpc/exit.c
@@ -0,0 +1,23 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <address@hidden>
+ */
+
+int main(void)
+{
+       return 1;
+}
diff --git a/kvm-unittest/powerpc/helloworld.c 
b/kvm-unittest/powerpc/helloworld.c
new file mode 100644
index 0000000..f8630f7
--- /dev/null
+++ b/kvm-unittest/powerpc/helloworld.c
@@ -0,0 +1,27 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Deepa Srinivasan <address@hidden>
+ */
+
+#include "libcflat.h"
+
+int main()
+{
+       printf("Hello World\n");
+
+       return 1;
+}
diff --git a/kvm-unittest/x86/access.c b/kvm-unittest/x86/access.c
new file mode 100644
index 0000000..2ca325a
--- /dev/null
+++ b/kvm-unittest/x86/access.c
@@ -0,0 +1,860 @@
+
+#include "libcflat.h"
+#include "desc.h"
+#include "processor.h"
+
+#define smp_id() 0
+
+#define true 1
+#define false 0
+
+static _Bool verbose = false;
+
+typedef unsigned long pt_element_t;
+
+#define PAGE_SIZE ((pt_element_t)4096)
+#define PAGE_MASK (~(PAGE_SIZE-1))
+
+#define PT_BASE_ADDR_MASK ((pt_element_t)((((pt_element_t)1 << 40) - 1) & 
PAGE_MASK))
+#define PT_PSE_BASE_ADDR_MASK (PT_BASE_ADDR_MASK & ~(1ull << 21))
+
+#define PT_PRESENT_MASK    ((pt_element_t)1 << 0)
+#define PT_WRITABLE_MASK   ((pt_element_t)1 << 1)
+#define PT_USER_MASK       ((pt_element_t)1 << 2)
+#define PT_ACCESSED_MASK   ((pt_element_t)1 << 5)
+#define PT_DIRTY_MASK      ((pt_element_t)1 << 6)
+#define PT_PSE_MASK        ((pt_element_t)1 << 7)
+#define PT_NX_MASK         ((pt_element_t)1 << 63)
+
+#define CR0_WP_MASK (1UL << 16)
+#define CR4_SMEP_MASK (1UL << 20)
+
+#define PFERR_PRESENT_MASK (1U << 0)
+#define PFERR_WRITE_MASK (1U << 1)
+#define PFERR_USER_MASK (1U << 2)
+#define PFERR_RESERVED_MASK (1U << 3)
+#define PFERR_FETCH_MASK (1U << 4)
+
+#define MSR_EFER 0xc0000080
+#define EFER_NX_MASK           (1ull << 11)
+
+#define PT_INDEX(address, level)       \
+       ((address) >> (12 + ((level)-1) * 9)) & 511
+
+/*
+ * page table access check tests
+ */
+
+enum {
+    AC_PTE_PRESENT,
+    AC_PTE_WRITABLE,
+    AC_PTE_USER,
+    AC_PTE_ACCESSED,
+    AC_PTE_DIRTY,
+    AC_PTE_NX,
+    AC_PTE_BIT51,
+
+    AC_PDE_PRESENT,
+    AC_PDE_WRITABLE,
+    AC_PDE_USER,
+    AC_PDE_ACCESSED,
+    AC_PDE_DIRTY,
+    AC_PDE_PSE,
+    AC_PDE_NX,
+    AC_PDE_BIT51,
+
+    AC_ACCESS_USER,
+    AC_ACCESS_WRITE,
+    AC_ACCESS_FETCH,
+    AC_ACCESS_TWICE,
+    // AC_ACCESS_PTE,
+
+    AC_CPU_EFER_NX,
+    AC_CPU_CR0_WP,
+    AC_CPU_CR4_SMEP,
+
+    NR_AC_FLAGS
+};
+
+const char *ac_names[] = {
+    [AC_PTE_PRESENT] = "pte.p",
+    [AC_PTE_ACCESSED] = "pte.a",
+    [AC_PTE_WRITABLE] = "pte.rw",
+    [AC_PTE_USER] = "pte.user",
+    [AC_PTE_DIRTY] = "pte.d",
+    [AC_PTE_NX] = "pte.nx",
+    [AC_PTE_BIT51] = "pte.51",
+    [AC_PDE_PRESENT] = "pde.p",
+    [AC_PDE_ACCESSED] = "pde.a",
+    [AC_PDE_WRITABLE] = "pde.rw",
+    [AC_PDE_USER] = "pde.user",
+    [AC_PDE_DIRTY] = "pde.d",
+    [AC_PDE_PSE] = "pde.pse",
+    [AC_PDE_NX] = "pde.nx",
+    [AC_PDE_BIT51] = "pde.51",
+    [AC_ACCESS_WRITE] = "write",
+    [AC_ACCESS_USER] = "user",
+    [AC_ACCESS_FETCH] = "fetch",
+    [AC_ACCESS_TWICE] = "twice",
+    [AC_CPU_EFER_NX] = "efer.nx",
+    [AC_CPU_CR0_WP] = "cr0.wp",
+    [AC_CPU_CR4_SMEP] = "cr4.smep",
+};
+
+static inline void *va(pt_element_t phys)
+{
+    return (void *)phys;
+}
+
+typedef struct {
+    pt_element_t pt_pool;
+    unsigned pt_pool_size;
+    unsigned pt_pool_current;
+} ac_pool_t;
+
+typedef struct {
+    unsigned flags[NR_AC_FLAGS];
+    void *virt;
+    pt_element_t phys;
+    pt_element_t *ptep;
+    pt_element_t expected_pte;
+    pt_element_t *pdep;
+    pt_element_t expected_pde;
+    pt_element_t ignore_pde;
+    int expected_fault;
+    unsigned expected_error;
+} ac_test_t;
+
+typedef struct {
+    unsigned short limit;
+    unsigned long linear_addr;
+} __attribute__((packed)) descriptor_table_t;
+
+
+static void ac_test_show(ac_test_t *at);
+
+int write_cr4_checking(unsigned long val)
+{
+    asm volatile(ASM_TRY("1f")
+            "mov %0,%%cr4\n\t"
+            "1:": : "r" (val));
+    return exception_vector();
+}
+
+void set_cr0_wp(int wp)
+{
+    unsigned long cr0 = read_cr0();
+
+    cr0 &= ~CR0_WP_MASK;
+    if (wp)
+       cr0 |= CR0_WP_MASK;
+    write_cr0(cr0);
+}
+
+void set_cr4_smep(int smep)
+{
+    unsigned long cr4 = read_cr4();
+
+    cr4 &= ~CR4_SMEP_MASK;
+    if (smep)
+       cr4 |= CR4_SMEP_MASK;
+    write_cr4(cr4);
+}
+
+void set_efer_nx(int nx)
+{
+    unsigned long long efer;
+
+    efer = rdmsr(MSR_EFER);
+    efer &= ~EFER_NX_MASK;
+    if (nx)
+       efer |= EFER_NX_MASK;
+    wrmsr(MSR_EFER, efer);
+}
+
+static void ac_env_int(ac_pool_t *pool)
+{
+    setup_idt();
+
+    extern char page_fault, kernel_entry;
+    set_idt_entry(14, &page_fault, 0);
+    set_idt_entry(0x20, &kernel_entry, 3);
+
+    pool->pt_pool = 33 * 1024 * 1024;
+    pool->pt_pool_size = 120 * 1024 * 1024 - pool->pt_pool;
+    pool->pt_pool_current = 0;
+}
+
+void ac_test_init(ac_test_t *at, void *virt)
+{
+    wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX_MASK);
+    set_cr0_wp(1);
+    for (int i = 0; i < NR_AC_FLAGS; ++i)
+       at->flags[i] = 0;
+    at->virt = virt;
+    at->phys = 32 * 1024 * 1024;
+}
+
+int ac_test_bump_one(ac_test_t *at)
+{
+    for (int i = 0; i < NR_AC_FLAGS; ++i)
+       if (!at->flags[i]) {
+           at->flags[i] = 1;
+           return 1;
+       } else
+           at->flags[i] = 0;
+    return 0;
+}
+
+_Bool ac_test_legal(ac_test_t *at)
+{
+    /*
+     * Since we convert current page to kernel page when cr4.smep=1,
+     * we can't switch to user mode.
+     */
+    if ((at->flags[AC_ACCESS_FETCH] && at->flags[AC_ACCESS_WRITE]) ||
+        (at->flags[AC_ACCESS_USER] && at->flags[AC_CPU_CR4_SMEP]))
+       return false;
+    return true;
+}
+
+int ac_test_bump(ac_test_t *at)
+{
+    int ret;
+
+    ret = ac_test_bump_one(at);
+    while (ret && !ac_test_legal(at))
+       ret = ac_test_bump_one(at);
+    return ret;
+}
+
+pt_element_t ac_test_alloc_pt(ac_pool_t *pool)
+{
+    pt_element_t ret = pool->pt_pool + pool->pt_pool_current;
+    pool->pt_pool_current += PAGE_SIZE;
+    return ret;
+}
+
+_Bool ac_test_enough_room(ac_pool_t *pool)
+{
+    return pool->pt_pool_current + 4 * PAGE_SIZE <= pool->pt_pool_size;
+}
+
+void ac_test_reset_pt_pool(ac_pool_t *pool)
+{
+    pool->pt_pool_current = 0;
+}
+
+void ac_set_expected_status(ac_test_t *at)
+{
+    int pde_valid, pte_valid;
+
+    invlpg(at->virt);
+
+    if (at->ptep)
+       at->expected_pte = *at->ptep;
+    at->expected_pde = *at->pdep;
+    at->ignore_pde = 0;
+    at->expected_fault = 0;
+    at->expected_error = PFERR_PRESENT_MASK;
+
+    pde_valid = at->flags[AC_PDE_PRESENT]
+        && !at->flags[AC_PDE_BIT51]
+        && !(at->flags[AC_PDE_NX] && !at->flags[AC_CPU_EFER_NX]);
+    pte_valid = pde_valid
+        && at->flags[AC_PTE_PRESENT]
+        && !at->flags[AC_PTE_BIT51]
+        && !(at->flags[AC_PTE_NX] && !at->flags[AC_CPU_EFER_NX]);
+    if (at->flags[AC_ACCESS_TWICE]) {
+       if (pde_valid) {
+           at->expected_pde |= PT_ACCESSED_MASK;
+           if (pte_valid)
+               at->expected_pte |= PT_ACCESSED_MASK;
+       }
+    }
+
+    if (at->flags[AC_ACCESS_USER])
+       at->expected_error |= PFERR_USER_MASK;
+
+    if (at->flags[AC_ACCESS_WRITE])
+       at->expected_error |= PFERR_WRITE_MASK;
+
+    if (at->flags[AC_ACCESS_FETCH])
+       at->expected_error |= PFERR_FETCH_MASK;
+
+    if (!at->flags[AC_PDE_PRESENT]) {
+       at->expected_fault = 1;
+       at->expected_error &= ~PFERR_PRESENT_MASK;
+    } else if (!pde_valid) {
+        at->expected_fault = 1;
+        at->expected_error |= PFERR_RESERVED_MASK;
+    }
+
+    if (at->flags[AC_ACCESS_USER] && !at->flags[AC_PDE_USER])
+       at->expected_fault = 1;
+
+    if (at->flags[AC_ACCESS_WRITE]
+       && !at->flags[AC_PDE_WRITABLE]
+       && (at->flags[AC_CPU_CR0_WP] || at->flags[AC_ACCESS_USER]))
+       at->expected_fault = 1;
+
+    if (at->flags[AC_ACCESS_FETCH] && at->flags[AC_PDE_NX])
+       at->expected_fault = 1;
+
+    if (!at->flags[AC_PDE_ACCESSED])
+        at->ignore_pde = PT_ACCESSED_MASK;
+
+    if (!pde_valid)
+       goto fault;
+
+    if (!at->expected_fault)
+        at->expected_pde |= PT_ACCESSED_MASK;
+
+    if (at->flags[AC_PDE_PSE]) {
+       if (at->flags[AC_ACCESS_WRITE] && !at->expected_fault)
+           at->expected_pde |= PT_DIRTY_MASK;
+       if (at->flags[AC_ACCESS_FETCH] && at->flags[AC_PDE_USER]
+           && at->flags[AC_CPU_CR4_SMEP])
+           at->expected_fault = 1;
+       goto no_pte;
+    }
+
+    if (!at->flags[AC_PTE_PRESENT]) {
+       at->expected_fault = 1;
+       at->expected_error &= ~PFERR_PRESENT_MASK;
+    } else if (!pte_valid) {
+        at->expected_fault = 1;
+        at->expected_error |= PFERR_RESERVED_MASK;
+    }
+
+    if (at->flags[AC_ACCESS_USER] && !at->flags[AC_PTE_USER])
+       at->expected_fault = 1;
+
+    if (at->flags[AC_ACCESS_WRITE]
+       && !at->flags[AC_PTE_WRITABLE]
+       && (at->flags[AC_CPU_CR0_WP] || at->flags[AC_ACCESS_USER]))
+       at->expected_fault = 1;
+
+    if (at->flags[AC_ACCESS_FETCH]
+       && (at->flags[AC_PTE_NX]
+           || (at->flags[AC_CPU_CR4_SMEP]
+               && at->flags[AC_PDE_USER]
+               && at->flags[AC_PTE_USER])))
+       at->expected_fault = 1;
+
+    if (at->expected_fault)
+       goto fault;
+
+    at->expected_pte |= PT_ACCESSED_MASK;
+    if (at->flags[AC_ACCESS_WRITE])
+       at->expected_pte |= PT_DIRTY_MASK;
+
+no_pte:
+fault:
+    if (!at->expected_fault)
+        at->ignore_pde = 0;
+    if (!at->flags[AC_CPU_EFER_NX] && !at->flags[AC_CPU_CR4_SMEP])
+        at->expected_error &= ~PFERR_FETCH_MASK;
+}
+
+void __ac_setup_specific_pages(ac_test_t *at, ac_pool_t *pool, u64 pd_page,
+                              u64 pt_page)
+
+{
+    unsigned long root = read_cr3();
+
+    if (!ac_test_enough_room(pool))
+       ac_test_reset_pt_pool(pool);
+
+    at->ptep = 0;
+    for (int i = 4; i >= 1 && (i >= 2 || !at->flags[AC_PDE_PSE]); --i) {
+       pt_element_t *vroot = va(root & PT_BASE_ADDR_MASK);
+       unsigned index = PT_INDEX((unsigned long)at->virt, i);
+       pt_element_t pte = 0;
+       switch (i) {
+       case 4:
+       case 3:
+           pte = pd_page ? pd_page : ac_test_alloc_pt(pool);
+           pte |= PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK;
+           break;
+       case 2:
+           if (!at->flags[AC_PDE_PSE])
+               pte = pt_page ? pt_page : ac_test_alloc_pt(pool);
+           else {
+               pte = at->phys & PT_PSE_BASE_ADDR_MASK;
+               pte |= PT_PSE_MASK;
+           }
+           if (at->flags[AC_PDE_PRESENT])
+               pte |= PT_PRESENT_MASK;
+           if (at->flags[AC_PDE_WRITABLE])
+               pte |= PT_WRITABLE_MASK;
+           if (at->flags[AC_PDE_USER])
+               pte |= PT_USER_MASK;
+           if (at->flags[AC_PDE_ACCESSED])
+               pte |= PT_ACCESSED_MASK;
+           if (at->flags[AC_PDE_DIRTY])
+               pte |= PT_DIRTY_MASK;
+           if (at->flags[AC_PDE_NX])
+               pte |= PT_NX_MASK;
+           if (at->flags[AC_PDE_BIT51])
+               pte |= 1ull << 51;
+           at->pdep = &vroot[index];
+           break;
+       case 1:
+           pte = at->phys & PT_BASE_ADDR_MASK;
+           if (at->flags[AC_PTE_PRESENT])
+               pte |= PT_PRESENT_MASK;
+           if (at->flags[AC_PTE_WRITABLE])
+               pte |= PT_WRITABLE_MASK;
+           if (at->flags[AC_PTE_USER])
+               pte |= PT_USER_MASK;
+           if (at->flags[AC_PTE_ACCESSED])
+               pte |= PT_ACCESSED_MASK;
+           if (at->flags[AC_PTE_DIRTY])
+               pte |= PT_DIRTY_MASK;
+           if (at->flags[AC_PTE_NX])
+               pte |= PT_NX_MASK;
+           if (at->flags[AC_PTE_BIT51])
+               pte |= 1ull << 51;
+           at->ptep = &vroot[index];
+           break;
+       }
+       vroot[index] = pte;
+       root = vroot[index];
+    }
+    ac_set_expected_status(at);
+}
+
+static void ac_test_setup_pte(ac_test_t *at, ac_pool_t *pool)
+{
+       __ac_setup_specific_pages(at, pool, 0, 0);
+}
+
+static void ac_setup_specific_pages(ac_test_t *at, ac_pool_t *pool,
+                                   u64 pd_page, u64 pt_page)
+{
+       return __ac_setup_specific_pages(at, pool, pd_page, pt_page);
+}
+
+static void dump_mapping(ac_test_t *at)
+{
+       unsigned long root = read_cr3();
+       int i;
+
+       printf("Dump mapping: address: %llx\n", at->virt);
+       for (i = 4; i >= 1 && (i >= 2 || !at->flags[AC_PDE_PSE]); --i) {
+               pt_element_t *vroot = va(root & PT_BASE_ADDR_MASK);
+               unsigned index = PT_INDEX((unsigned long)at->virt, i);
+               pt_element_t pte = vroot[index];
+
+               printf("------L%d: %llx\n", i, pte);
+               root = vroot[index];
+       }
+}
+
+static void ac_test_check(ac_test_t *at, _Bool *success_ret, _Bool cond,
+                          const char *fmt, ...)
+{
+    va_list ap;
+    char buf[500];
+
+    if (!*success_ret) {
+        return;
+    }
+
+    if (!cond) {
+        return;
+    }
+
+    *success_ret = false;
+
+    if (!verbose) {
+        ac_test_show(at);
+    }
+
+    va_start(ap, fmt);
+    vsnprintf(buf, sizeof(buf), fmt, ap);
+    va_end(ap);
+    printf("FAIL: %s\n", buf);
+    dump_mapping(at);
+}
+
+static int pt_match(pt_element_t pte1, pt_element_t pte2, pt_element_t ignore)
+{
+    pte1 &= ~ignore;
+    pte2 &= ~ignore;
+    return pte1 == pte2;
+}
+
+int ac_test_do_access(ac_test_t *at)
+{
+    static unsigned unique = 42;
+    int fault = 0;
+    unsigned e;
+    static unsigned char user_stack[4096];
+    unsigned long rsp;
+    _Bool success = true;
+
+    ++unique;
+
+    *((unsigned char *)at->phys) = 0xc3; /* ret */
+
+    unsigned r = unique;
+    set_cr0_wp(at->flags[AC_CPU_CR0_WP]);
+    set_efer_nx(at->flags[AC_CPU_EFER_NX]);
+    if (at->flags[AC_CPU_CR4_SMEP] && !(cpuid(7).b & (1 << 7))) {
+       unsigned long cr4 = read_cr4();
+       if (write_cr4_checking(cr4 | CR4_SMEP_MASK) == GP_VECTOR)
+               goto done;
+       printf("Set SMEP in CR4 - expect #GP: FAIL!\n");
+       return 0;
+    }
+    set_cr4_smep(at->flags[AC_CPU_CR4_SMEP]);
+
+    if (at->flags[AC_ACCESS_TWICE]) {
+       asm volatile (
+           "mov $fixed2, %%rsi \n\t"
+           "mov (%[addr]), %[reg] \n\t"
+           "fixed2:"
+           : [reg]"=r"(r), [fault]"=a"(fault), "=b"(e)
+           : [addr]"r"(at->virt)
+           : "rsi"
+           );
+       fault = 0;
+    }
+
+    asm volatile ("mov $fixed1, %%rsi \n\t"
+                 "mov %%rsp, %%rdx \n\t"
+                 "cmp $0, %[user] \n\t"
+                 "jz do_access \n\t"
+                 "push %%rax; mov %[user_ds], %%ax; mov %%ax, %%ds; pop %%rax  
\n\t"
+                 "pushq %[user_ds] \n\t"
+                 "pushq %[user_stack_top] \n\t"
+                 "pushfq \n\t"
+                 "pushq %[user_cs] \n\t"
+                 "pushq $do_access \n\t"
+                 "iretq \n"
+                 "do_access: \n\t"
+                 "cmp $0, %[fetch] \n\t"
+                 "jnz 2f \n\t"
+                 "cmp $0, %[write] \n\t"
+                 "jnz 1f \n\t"
+                 "mov (%[addr]), %[reg] \n\t"
+                 "jmp done \n\t"
+                 "1: mov %[reg], (%[addr]) \n\t"
+                 "jmp done \n\t"
+                 "2: call *%[addr] \n\t"
+                 "done: \n"
+                 "fixed1: \n"
+                 "int %[kernel_entry_vector] \n\t"
+                 "back_to_kernel:"
+                 : [reg]"+r"(r), "+a"(fault), "=b"(e), "=&d"(rsp)
+                 : [addr]"r"(at->virt),
+                   [write]"r"(at->flags[AC_ACCESS_WRITE]),
+                   [user]"r"(at->flags[AC_ACCESS_USER]),
+                   [fetch]"r"(at->flags[AC_ACCESS_FETCH]),
+                   [user_ds]"i"(32+3),
+                   [user_cs]"i"(24+3),
+                   [user_stack_top]"r"(user_stack + sizeof user_stack),
+                   [kernel_entry_vector]"i"(0x20)
+                 : "rsi");
+
+    asm volatile (".section .text.pf \n\t"
+                 "page_fault: \n\t"
+                 "pop %rbx \n\t"
+                 "mov %rsi, (%rsp) \n\t"
+                 "movl $1, %eax \n\t"
+                 "iretq \n\t"
+                 ".section .text");
+
+    asm volatile (".section .text.entry \n\t"
+                 "kernel_entry: \n\t"
+                 "mov %rdx, %rsp \n\t"
+                 "jmp back_to_kernel \n\t"
+                 ".section .text");
+
+    ac_test_check(at, &success, fault && !at->expected_fault,
+                  "unexpected fault");
+    ac_test_check(at, &success, !fault && at->expected_fault,
+                  "unexpected access");
+    ac_test_check(at, &success, fault && e != at->expected_error,
+                  "error code %x expected %x", e, at->expected_error);
+    ac_test_check(at, &success, at->ptep && *at->ptep != at->expected_pte,
+                  "pte %x expected %x", *at->ptep, at->expected_pte);
+    ac_test_check(at, &success,
+                  !pt_match(*at->pdep, at->expected_pde, at->ignore_pde),
+                  "pde %x expected %x", *at->pdep, at->expected_pde);
+
+done:
+    if (success && verbose) {
+        printf("PASS\n");
+    }
+    return success;
+}
+
+static void ac_test_show(ac_test_t *at)
+{
+    char line[5000];
+
+    *line = 0;
+    strcat(line, "test");
+    for (int i = 0; i < NR_AC_FLAGS; ++i)
+       if (at->flags[i]) {
+           strcat(line, " ");
+           strcat(line, ac_names[i]);
+       }
+    strcat(line, ": ");
+    printf("%s", line);
+}
+
+/*
+ * This test case is used to triger the bug which is fixed by
+ * commit e09e90a5 in the kvm tree
+ */
+static int corrupt_hugepage_triger(ac_pool_t *pool)
+{
+    ac_test_t at1, at2;
+
+    ac_test_init(&at1, (void *)(0x123400000000));
+    ac_test_init(&at2, (void *)(0x666600000000));
+
+    at2.flags[AC_CPU_CR0_WP] = 1;
+    at2.flags[AC_PDE_PSE] = 1;
+    at2.flags[AC_PDE_PRESENT] = 1;
+    ac_test_setup_pte(&at2, pool);
+    if (!ac_test_do_access(&at2))
+        goto err;
+
+    at1.flags[AC_CPU_CR0_WP] = 1;
+    at1.flags[AC_PDE_PSE] = 1;
+    at1.flags[AC_PDE_WRITABLE] = 1;
+    at1.flags[AC_PDE_PRESENT] = 1;
+    ac_test_setup_pte(&at1, pool);
+    if (!ac_test_do_access(&at1))
+        goto err;
+
+    at1.flags[AC_ACCESS_WRITE] = 1;
+    ac_set_expected_status(&at1);
+    if (!ac_test_do_access(&at1))
+        goto err;
+
+    at2.flags[AC_ACCESS_WRITE] = 1;
+    ac_set_expected_status(&at2);
+    if (!ac_test_do_access(&at2))
+        goto err;
+
+    return 1;
+
+err:
+    printf("corrupt_hugepage_triger test fail\n");
+    return 0;
+}
+
+/*
+ * This test case is used to triger the bug which is fixed by
+ * commit 3ddf6c06e13e in the kvm tree
+ */
+static int check_pfec_on_prefetch_pte(ac_pool_t *pool)
+{
+       ac_test_t at1, at2;
+
+       ac_test_init(&at1, (void *)(0x123406001000));
+       ac_test_init(&at2, (void *)(0x123406003000));
+
+       at1.flags[AC_PDE_PRESENT] = 1;
+       at1.flags[AC_PTE_PRESENT] = 1;
+       ac_setup_specific_pages(&at1, pool, 30 * 1024 * 1024, 30 * 1024 * 1024);
+
+       at2.flags[AC_PDE_PRESENT] = 1;
+       at2.flags[AC_PTE_NX] = 1;
+       at2.flags[AC_PTE_PRESENT] = 1;
+       ac_setup_specific_pages(&at2, pool, 30 * 1024 * 1024, 30 * 1024 * 1024);
+
+       if (!ac_test_do_access(&at1)) {
+               printf("%s: prepare fail\n", __FUNCTION__);
+               goto err;
+       }
+
+       if (!ac_test_do_access(&at2)) {
+               printf("%s: check PFEC on prefetch pte path fail\n",
+                       __FUNCTION__);
+               goto err;
+       }
+
+       return 1;
+
+err:
+    return 0;
+}
+
+/*
+ * If the write-fault access is from supervisor and CR0.WP is not set on the
+ * vcpu, kvm will fix it by adjusting pte access - it sets the W bit on pte
+ * and clears U bit. This is the chance that kvm can change pte access from
+ * readonly to writable.
+ *
+ * Unfortunately, the pte access is the access of 'direct' shadow page table,
+ * means direct sp.role.access = pte_access, then we will create a writable
+ * spte entry on the readonly shadow page table. It will cause Dirty bit is
+ * not tracked when two guest ptes point to the same large page. Note, it
+ * does not have other impact except Dirty bit since cr0.wp is encoded into
+ * sp.role.
+ *
+ * Note: to trigger this bug, hugepage should be disabled on host.
+ */
+static int check_large_pte_dirty_for_nowp(ac_pool_t *pool)
+{
+       ac_test_t at1, at2;
+
+       ac_test_init(&at1, (void *)(0x123403000000));
+       ac_test_init(&at2, (void *)(0x666606000000));
+
+       at2.flags[AC_PDE_PRESENT] = 1;
+       at2.flags[AC_PDE_PSE] = 1;
+
+       ac_test_setup_pte(&at2, pool);
+       if (!ac_test_do_access(&at2)) {
+               printf("%s: read on the first mapping fail.\n", __FUNCTION__);
+               goto err;
+       }
+
+       at1.flags[AC_PDE_PRESENT] = 1;
+       at1.flags[AC_PDE_PSE] = 1;
+       at1.flags[AC_ACCESS_WRITE] = 1;
+
+       ac_test_setup_pte(&at1, pool);
+       if (!ac_test_do_access(&at1)) {
+               printf("%s: write on the second mapping fail.\n", __FUNCTION__);
+               goto err;
+       }
+
+       at2.flags[AC_ACCESS_WRITE] = 1;
+       ac_set_expected_status(&at2);
+       if (!ac_test_do_access(&at2)) {
+               printf("%s: write on the first mapping fail.\n", __FUNCTION__);
+               goto err;
+       }
+
+       return 1;
+
+err:
+       return 0;
+}
+
+static int check_smep_andnot_wp(ac_pool_t *pool)
+{
+       ac_test_t at1;
+       int err_prepare_andnot_wp, err_smep_andnot_wp;
+       extern u64 ptl2[];
+
+       ac_test_init(&at1, (void *)(0x123406001000));
+
+       at1.flags[AC_PDE_PRESENT] = 1;
+       at1.flags[AC_PTE_PRESENT] = 1;
+       at1.flags[AC_PDE_USER] = 1;
+       at1.flags[AC_PTE_USER] = 1;
+       at1.flags[AC_PDE_ACCESSED] = 1;
+       at1.flags[AC_PTE_ACCESSED] = 1;
+       at1.flags[AC_CPU_CR4_SMEP] = 1;
+       at1.flags[AC_CPU_CR0_WP] = 0;
+       at1.flags[AC_ACCESS_WRITE] = 1;
+       ac_test_setup_pte(&at1, pool);
+       ptl2[2] -= 0x4;
+
+       /*
+        * Here we write the ro user page when
+        * cr0.wp=0, then we execute it and SMEP
+        * fault should happen.
+        */
+       err_prepare_andnot_wp = ac_test_do_access(&at1);
+       if (!err_prepare_andnot_wp) {
+               printf("%s: SMEP prepare fail\n", __FUNCTION__);
+               goto clean_up;
+       }
+
+       at1.flags[AC_ACCESS_WRITE] = 0;
+       at1.flags[AC_ACCESS_FETCH] = 1;
+       ac_set_expected_status(&at1);
+       err_smep_andnot_wp = ac_test_do_access(&at1);
+
+clean_up:
+       set_cr4_smep(0);
+       ptl2[2] += 0x4;
+
+       if (!err_prepare_andnot_wp)
+               goto err;
+       if (!err_smep_andnot_wp) {
+               printf("%s: check SMEP without wp fail\n", __FUNCTION__);
+               goto err;
+       }
+       return 1;
+
+err:
+       return 0;
+}
+
+int ac_test_exec(ac_test_t *at, ac_pool_t *pool)
+{
+    int r;
+
+    if (verbose) {
+        ac_test_show(at);
+    }
+    ac_test_setup_pte(at, pool);
+    r = ac_test_do_access(at);
+    return r;
+}
+
+typedef int (*ac_test_fn)(ac_pool_t *pool);
+const ac_test_fn ac_test_cases[] =
+{
+       corrupt_hugepage_triger,
+       check_pfec_on_prefetch_pte,
+       check_large_pte_dirty_for_nowp,
+       check_smep_andnot_wp
+};
+
+int ac_test_run(void)
+{
+    ac_test_t at;
+    ac_pool_t pool;
+    int i, tests, successes;
+    extern u64 ptl2[];
+
+    printf("run\n");
+    tests = successes = 0;
+    ac_env_int(&pool);
+    ac_test_init(&at, (void *)(0x123400000000 + 16 * smp_id()));
+    do {
+       if (at.flags[AC_CPU_CR4_SMEP] && (ptl2[2] & 0x4))
+               ptl2[2] -= 0x4;
+       if (!at.flags[AC_CPU_CR4_SMEP] && !(ptl2[2] & 0x4)) {
+               set_cr4_smep(0);
+               ptl2[2] += 0x4;
+       }
+
+       ++tests;
+       successes += ac_test_exec(&at, &pool);
+    } while (ac_test_bump(&at));
+
+    set_cr4_smep(0);
+    ptl2[2] += 0x4;
+
+    for (i = 0; i < ARRAY_SIZE(ac_test_cases); i++) {
+       ++tests;
+       successes += ac_test_cases[i](&pool);
+    }
+
+    printf("\n%d tests, %d failures\n", tests, tests - successes);
+
+    return successes == tests;
+}
+
+int main()
+{
+    int r;
+
+    printf("starting test\n\n");
+    r = ac_test_run();
+    return r ? 0 : 1;
+}
diff --git a/kvm-unittest/x86/apic.c b/kvm-unittest/x86/apic.c
new file mode 100644
index 0000000..50e77fc
--- /dev/null
+++ b/kvm-unittest/x86/apic.c
@@ -0,0 +1,344 @@
+#include "libcflat.h"
+#include "apic.h"
+#include "vm.h"
+#include "smp.h"
+#include "desc.h"
+#include "isr.h"
+
+static int g_fail;
+static int g_tests;
+
+static void report(const char *msg, int pass)
+{
+    ++g_tests;
+    printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL"));
+    if (!pass)
+        ++g_fail;
+}
+
+static void test_lapic_existence(void)
+{
+    u32 lvr;
+
+    lvr = apic_read(APIC_LVR);
+    printf("apic version: %x\n", lvr);
+    report("apic existence", (u16)lvr == 0x14);
+}
+
+#define TSC_DEADLINE_TIMER_MODE (2 << 17)
+#define TSC_DEADLINE_TIMER_VECTOR 0xef
+#define MSR_IA32_TSC            0x00000010
+#define MSR_IA32_TSCDEADLINE    0x000006e0
+
+static int tdt_count;
+
+static void tsc_deadline_timer_isr(isr_regs_t *regs)
+{
+    ++tdt_count;
+}
+
+static void start_tsc_deadline_timer(void)
+{
+    handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr);
+    irq_enable();
+
+    wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC));
+    asm volatile ("nop");
+    report("tsc deadline timer", tdt_count == 1);
+}
+
+static int enable_tsc_deadline_timer(void)
+{
+    uint32_t lvtt;
+
+    if (cpuid(1).c & (1 << 24)) {
+        lvtt = TSC_DEADLINE_TIMER_MODE | TSC_DEADLINE_TIMER_VECTOR;
+        apic_write(APIC_LVTT, lvtt);
+        start_tsc_deadline_timer();
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+static void test_tsc_deadline_timer(void)
+{
+    if(enable_tsc_deadline_timer()) {
+        printf("tsc deadline timer enabled\n");
+    } else {
+        printf("tsc deadline timer not detected\n");
+    }
+}
+
+#define MSR_APIC_BASE 0x0000001b
+
+void test_enable_x2apic(void)
+{
+    if (enable_x2apic()) {
+        printf("x2apic enabled\n");
+    } else {
+        printf("x2apic not detected\n");
+    }
+}
+
+static void eoi(void)
+{
+    apic_write(APIC_EOI, 0);
+}
+
+static int ipi_count;
+
+static void self_ipi_isr(isr_regs_t *regs)
+{
+    ++ipi_count;
+    eoi();
+}
+
+static void test_self_ipi(void)
+{
+    int vec = 0xf1;
+
+    handle_irq(vec, self_ipi_isr);
+    irq_enable();
+    apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec,
+                   0);
+    asm volatile ("nop");
+    report("self ipi", ipi_count == 1);
+}
+
+static void set_ioapic_redir(unsigned line, unsigned vec)
+{
+    ioapic_redir_entry_t e = {
+        .vector = vec,
+        .delivery_mode = 0,
+        .trig_mode = 0,
+    };
+
+    ioapic_write_redir(line, e);
+}
+
+static void set_irq_line(unsigned line, int val)
+{
+    asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line)));
+}
+
+static void toggle_irq_line(unsigned line)
+{
+    set_irq_line(line, 1);
+    set_irq_line(line, 0);
+}
+
+static int g_isr_77;
+
+static void ioapic_isr_77(isr_regs_t *regs)
+{
+    ++g_isr_77;
+    eoi();
+}
+
+static void test_ioapic_intr(void)
+{
+    handle_irq(0x77, ioapic_isr_77);
+    set_ioapic_redir(0x0e, 0x77);
+    toggle_irq_line(0x0e);
+    asm volatile ("nop");
+    report("ioapic interrupt", g_isr_77 == 1);
+}
+
+static int g_78, g_66, g_66_after_78;
+static ulong g_66_rip, g_78_rip;
+
+static void ioapic_isr_78(isr_regs_t *regs)
+{
+    ++g_78;
+    g_78_rip = regs->rip;
+    eoi();
+}
+
+static void ioapic_isr_66(isr_regs_t *regs)
+{
+    ++g_66;
+    if (g_78)
+        ++g_66_after_78;
+    g_66_rip = regs->rip;
+    eoi();
+}
+
+static void test_ioapic_simultaneous(void)
+{
+    handle_irq(0x78, ioapic_isr_78);
+    handle_irq(0x66, ioapic_isr_66);
+    set_ioapic_redir(0x0e, 0x78);
+    set_ioapic_redir(0x0f, 0x66);
+    irq_disable();
+    toggle_irq_line(0x0f);
+    toggle_irq_line(0x0e);
+    irq_enable();
+    asm volatile ("nop");
+    report("ioapic simultaneous interrupt",
+           g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip);
+}
+
+volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter, 
sti_loop_active;
+
+void sti_nop(char *p)
+{
+    asm volatile (
+                 ".globl post_sti \n\t"
+                 "sti \n"
+                 /*
+                  * vmx won't exit on external interrupt if blocked-by-sti,
+                  * so give it a reason to exit by accessing an unmapped page.
+                  */
+                 "post_sti: testb $0, %0 \n\t"
+                 "nop \n\t"
+                 "cli"
+                 : : "m"(*p)
+                 );
+    nmi_counter = nmi_counter_private;
+}
+
+static void sti_loop(void *ignore)
+{
+    unsigned k = 0;
+
+    while (sti_loop_active) {
+       sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024)));
+    }
+}
+
+static void nmi_handler(isr_regs_t *regs)
+{
+    extern void post_sti(void);
+    ++nmi_counter_private;
+    nmi_hlt_counter += regs->rip == (ulong)post_sti;
+}
+
+static void update_cr3(void *cr3)
+{
+    write_cr3((ulong)cr3);
+}
+
+static void test_sti_nmi(void)
+{
+    unsigned old_counter;
+
+    if (cpu_count() < 2) {
+       return;
+    }
+
+    handle_irq(2, nmi_handler);
+    on_cpu(1, update_cr3, (void *)read_cr3());
+
+    sti_loop_active = 1;
+    on_cpu_async(1, sti_loop, 0);
+    while (nmi_counter < 30000) {
+       old_counter = nmi_counter;
+       apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1);
+       while (nmi_counter == old_counter) {
+           ;
+       }
+    }
+    sti_loop_active = 0;
+    report("nmi-after-sti", nmi_hlt_counter == 0);
+}
+
+static volatile bool nmi_done, nmi_flushed;
+static volatile int nmi_received;
+static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1;
+static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2;
+
+static void multiple_nmi_handler(isr_regs_t *regs)
+{
+    ++nmi_received;
+}
+
+static void kick_me_nmi(void *blah)
+{
+    while (!nmi_done) {
+       ++cpu1_nmi_ctr1;
+       while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) {
+           pause();
+       }
+       if (nmi_done) {
+           return;
+       }
+       apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
+       /* make sure the NMI has arrived by sending an IPI after it */
+       apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT
+                      | 0x44, 0);
+       ++cpu1_nmi_ctr2;
+       while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) {
+           pause();
+       }
+    }
+}
+
+static void flush_nmi(isr_regs_t *regs)
+{
+    nmi_flushed = true;
+    apic_write(APIC_EOI, 0);
+}
+
+static void test_multiple_nmi(void)
+{
+    int i;
+    bool ok = true;
+
+    if (cpu_count() < 2) {
+       return;
+    }
+
+    sti();
+    handle_irq(2, multiple_nmi_handler);
+    handle_irq(0x44, flush_nmi);
+    on_cpu_async(1, kick_me_nmi, 0);
+    for (i = 0; i < 1000000; ++i) {
+       nmi_flushed = false;
+       nmi_received = 0;
+       ++cpu0_nmi_ctr1;
+       while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) {
+           pause();
+       }
+       apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
+       while (!nmi_flushed) {
+           pause();
+       }
+       if (nmi_received != 2) {
+           ok = false;
+           break;
+       }
+       ++cpu0_nmi_ctr2;
+       while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) {
+           pause();
+       }
+    }
+    nmi_done = true;
+    report("multiple nmi", ok);
+}
+
+int main()
+{
+    setup_vm();
+    smp_init();
+    setup_idt();
+
+    test_lapic_existence();
+
+    mask_pic_interrupts();
+    enable_apic();
+    test_enable_x2apic();
+
+    test_self_ipi();
+
+    test_ioapic_intr();
+    test_ioapic_simultaneous();
+    test_sti_nmi();
+    test_multiple_nmi();
+
+    test_tsc_deadline_timer();
+
+    printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail);
+
+    return g_fail != 0;
+}
diff --git a/kvm-unittest/x86/asyncpf.c b/kvm-unittest/x86/asyncpf.c
new file mode 100644
index 0000000..95e7741
--- /dev/null
+++ b/kvm-unittest/x86/asyncpf.c
@@ -0,0 +1,113 @@
+/*
+ * Async PF test. For the test to actually do anything it needs to be started
+ * in memory cgroup with 512M of memory and with more then 1G memory provided
+ * to the guest.
+ *
+ * To create cgroup do as root:
+ * mkdir /dev/cgroup
+ * mount -t cgroup none -omemory /dev/cgroup
+ * chmod a+rxw /dev/cgroup/
+ *
+ * From a shell you will start qemu from:
+ * mkdir /dev/cgroup/1
+ * echo $$ >  /dev/cgroup/1/tasks
+ * echo 512M > /dev/cgroup/1/memory.limit_in_bytes
+ *
+ */
+#include "x86/msr.h"
+#include "x86/processor.h"
+#include "x86/apic-defs.h"
+#include "x86/apic.h"
+#include "x86/desc.h"
+#include "x86/isr.h"
+#include "x86/vm.h"
+
+#include "libcflat.h"
+#include <stdint.h>
+
+#define KVM_PV_REASON_PAGE_NOT_PRESENT 1
+#define KVM_PV_REASON_PAGE_READY 2
+
+#define MSR_KVM_ASYNC_PF_EN 0x4b564d02
+
+#define KVM_ASYNC_PF_ENABLED                    (1 << 0)
+#define KVM_ASYNC_PF_SEND_ALWAYS                (1 << 1)
+
+volatile uint32_t apf_reason __attribute__((aligned(64)));
+char *buf;
+volatile uint64_t  i;
+volatile uint64_t phys;
+bool fail;
+
+static inline uint32_t get_apf_reason(void)
+{
+       uint32_t r = apf_reason;
+       apf_reason = 0;
+       return r;
+}
+
+static void pf_isr(struct ex_regs *r)
+{
+       void* virt = (void*)((ulong)(buf+i) & ~(PAGE_SIZE-1));
+       uint32_t reason = get_apf_reason();
+
+       switch (reason) {
+               case 0:
+                       printf("unexpected #PF at %p\n", read_cr2());
+                       fail = true;
+                       break;
+               case KVM_PV_REASON_PAGE_NOT_PRESENT:
+                       phys = virt_to_phys_cr3(virt);
+                       install_pte(phys_to_virt(read_cr3()), 1, virt, phys, 0);
+                       write_cr3(read_cr3());
+                       printf("Got not present #PF token %x virt addr %p phys 
addr %p\n", read_cr2(), virt, phys);
+                       while(phys) {
+                               safe_halt(); /* enables irq */
+                               irq_disable();
+                       }
+                       break;
+               case KVM_PV_REASON_PAGE_READY:
+                       printf("Got present #PF token %x\n", read_cr2());
+                       if ((uint32_t)read_cr2() == ~0)
+                               break;
+                       install_pte(phys_to_virt(read_cr3()), 1, virt, phys | 
PTE_PRESENT | PTE_WRITE, 0);
+                       write_cr3(read_cr3());
+                       phys = 0;
+                       break;
+               default:
+                       printf("unexpected async pf reason %d\n", reason);
+                       fail = true;
+                       break;
+       }
+}
+
+#define MEM 1ull*1024*1024*1024
+
+int main(int ac, char **av)
+{
+       int loop = 2;
+
+       setup_vm();
+       setup_idt();
+       setup_gdt();
+       printf("install handler\n");
+       handle_exception(14, pf_isr);
+       apf_reason = 0;
+       printf("enable async pf\n");
+       wrmsr(MSR_KVM_ASYNC_PF_EN, virt_to_phys((void*)&apf_reason) |
+                       KVM_ASYNC_PF_SEND_ALWAYS | KVM_ASYNC_PF_ENABLED);
+       printf("alloc memory\n");
+       buf = vmalloc(MEM);
+       irq_enable();
+       while(loop--) {
+               printf("start loop\n");
+               /* access a lot of memory to make host swap it out */
+               for (i=0; i < MEM; i+=4096)
+                       buf[i] = 1;
+               printf("end loop\n");
+       }
+       irq_disable();
+
+       printf("%s\n", fail ? "FAIL" : "PASS");
+       return fail;
+}
diff --git a/kvm-unittest/x86/emulator.c b/kvm-unittest/x86/emulator.c
new file mode 100644
index 0000000..68d2b93
--- /dev/null
+++ b/kvm-unittest/x86/emulator.c
@@ -0,0 +1,1024 @@
+#include "ioram.h"
+#include "vm.h"
+#include "libcflat.h"
+#include "desc.h"
+#include "types.h"
+
+#define memset __builtin_memset
+#define TESTDEV_IO_PORT 0xe0
+
+int fails, tests;
+
+static int exceptions;
+
+struct regs {
+       u64 rax, rbx, rcx, rdx;
+       u64 rsi, rdi, rsp, rbp;
+       u64 r8, r9, r10, r11;
+       u64 r12, r13, r14, r15;
+       u64 rip, rflags;
+};
+struct regs inregs, outregs, save;
+
+struct insn_desc {
+       u64 ptr;
+       size_t len;
+};
+
+void report(const char *name, int result)
+{
+       ++tests;
+       if (result)
+               printf("PASS: %s\n", name);
+       else {
+               printf("FAIL: %s\n", name);
+               ++fails;
+       }
+}
+
+static char st1[] = "abcdefghijklmnop";
+
+void test_stringio()
+{
+       unsigned char r = 0;
+       asm volatile("cld \n\t"
+                    "movw %0, %%dx \n\t"
+                    "rep outsb \n\t"
+                    : : "i"((short)TESTDEV_IO_PORT),
+                      "S"(st1), "c"(sizeof(st1) - 1));
+       asm volatile("inb %1, %0\n\t" : "=a"(r) : "i"((short)TESTDEV_IO_PORT));
+       report("outsb up", r == st1[sizeof(st1) - 2]); /* last char */
+
+       asm volatile("std \n\t"
+                    "movw %0, %%dx \n\t"
+                    "rep outsb \n\t"
+                    : : "i"((short)TESTDEV_IO_PORT),
+                      "S"(st1 + sizeof(st1) - 2), "c"(sizeof(st1) - 1));
+       asm volatile("cld \n\t" : : );
+       asm volatile("in %1, %0\n\t" : "=a"(r) : "i"((short)TESTDEV_IO_PORT));
+       report("outsb down", r == st1[0]);
+}
+
+void test_cmps_one(unsigned char *m1, unsigned char *m3)
+{
+       void *rsi, *rdi;
+       long rcx, tmp;
+
+       rsi = m1; rdi = m3; rcx = 30;
+       asm volatile("xor %[tmp], %[tmp] \n\t"
+                    "repe/cmpsb"
+                    : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+                    : : "cc");
+       report("repe/cmpsb (1)", rcx == 0 && rsi == m1 + 30 && rdi == m3 + 30);
+
+       rsi = m1; rdi = m3; rcx = 30;
+       asm volatile("or $1, %[tmp]\n\t" // clear ZF
+                    "repe/cmpsb"
+                    : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+                    : : "cc");
+       report("repe/cmpsb (1.zf)", rcx == 0 && rsi == m1 + 30 && rdi == m3 + 
30);
+
+       rsi = m1; rdi = m3; rcx = 15;
+       asm volatile("xor %[tmp], %[tmp] \n\t"
+                    "repe/cmpsw"
+                    : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+                    : : "cc");
+       report("repe/cmpsw (1)", rcx == 0 && rsi == m1 + 30 && rdi == m3 + 30);
+
+       rsi = m1; rdi = m3; rcx = 7;
+       asm volatile("xor %[tmp], %[tmp] \n\t"
+                    "repe/cmpsl"
+                    : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+                    : : "cc");
+       report("repe/cmpll (1)", rcx == 0 && rsi == m1 + 28 && rdi == m3 + 28);
+
+       rsi = m1; rdi = m3; rcx = 4;
+       asm volatile("xor %[tmp], %[tmp] \n\t"
+                    "repe/cmpsq"
+                    : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+                    : : "cc");
+       report("repe/cmpsq (1)", rcx == 0 && rsi == m1 + 32 && rdi == m3 + 32);
+
+       rsi = m1; rdi = m3; rcx = 130;
+       asm volatile("xor %[tmp], %[tmp] \n\t"
+                    "repe/cmpsb"
+                    : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+                    : : "cc");
+       report("repe/cmpsb (2)",
+              rcx == 29 && rsi == m1 + 101 && rdi == m3 + 101);
+
+       rsi = m1; rdi = m3; rcx = 65;
+       asm volatile("xor %[tmp], %[tmp] \n\t"
+                    "repe/cmpsw"
+                    : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+                    : : "cc");
+       report("repe/cmpsw (2)",
+              rcx == 14 && rsi == m1 + 102 && rdi == m3 + 102);
+
+       rsi = m1; rdi = m3; rcx = 32;
+       asm volatile("xor %[tmp], %[tmp] \n\t"
+                    "repe/cmpsl"
+                    : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+                    : : "cc");
+       report("repe/cmpll (2)",
+              rcx == 6 && rsi == m1 + 104 && rdi == m3 + 104);
+
+       rsi = m1; rdi = m3; rcx = 16;
+       asm volatile("xor %[tmp], %[tmp] \n\t"
+                    "repe/cmpsq"
+                    : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+                    : : "cc");
+       report("repe/cmpsq (2)",
+              rcx == 3 && rsi == m1 + 104 && rdi == m3 + 104);
+
+}
+
+void test_cmps(void *mem)
+{
+       unsigned char *m1 = mem, *m2 = mem + 1024;
+       unsigned char m3[1024];
+
+       for (int i = 0; i < 100; ++i)
+               m1[i] = m2[i] = m3[i] = i;
+       for (int i = 100; i < 200; ++i)
+               m1[i] = (m3[i] = m2[i] = i) + 1;
+       test_cmps_one(m1, m3);
+       test_cmps_one(m1, m2);
+}
+
+void test_scas(void *mem)
+{
+    bool z;
+    void *di;
+
+    *(ulong *)mem = 0x77665544332211;
+
+    di = mem;
+    asm ("scasb; setz %0" : "=rm"(z), "+D"(di) : "a"(0xff11));
+    report("scasb match", di == mem + 1 && z);
+
+    di = mem;
+    asm ("scasb; setz %0" : "=rm"(z), "+D"(di) : "a"(0xff54));
+    report("scasb mismatch", di == mem + 1 && !z);
+
+    di = mem;
+    asm ("scasw; setz %0" : "=rm"(z), "+D"(di) : "a"(0xff2211));
+    report("scasw match", di == mem + 2 && z);
+
+    di = mem;
+    asm ("scasw; setz %0" : "=rm"(z), "+D"(di) : "a"(0xffdd11));
+    report("scasw mismatch", di == mem + 2 && !z);
+
+    di = mem;
+    asm ("scasl; setz %0" : "=rm"(z), "+D"(di) : "a"(0xff44332211ul));
+    report("scasd match", di == mem + 4 && z);
+
+    di = mem;
+    asm ("scasl; setz %0" : "=rm"(z), "+D"(di) : "a"(0x45332211));
+    report("scasd mismatch", di == mem + 4 && !z);
+
+    di = mem;
+    asm ("scasq; setz %0" : "=rm"(z), "+D"(di) : "a"(0x77665544332211ul));
+    report("scasq match", di == mem + 8 && z);
+
+    di = mem;
+    asm ("scasq; setz %0" : "=rm"(z), "+D"(di) : "a"(3));
+    report("scasq mismatch", di == mem + 8 && !z);
+}
+
+void test_cr8(void)
+{
+       unsigned long src, dst;
+
+       dst = 777;
+       src = 3;
+       asm volatile("mov %[src], %%cr8; mov %%cr8, %[dst]"
+                    : [dst]"+r"(dst), [src]"+r"(src));
+       report("mov %cr8", dst == 3 && src == 3);
+}
+
+void test_push(void *mem)
+{
+       unsigned long tmp;
+       unsigned long *stack_top = mem + 4096;
+       unsigned long *new_stack_top;
+       unsigned long memw = 0x123456789abcdeful;
+
+       memset(mem, 0x55, (void *)stack_top - mem);
+
+       asm volatile("mov %%rsp, %[tmp] \n\t"
+                    "mov %[stack_top], %%rsp \n\t"
+                    "pushq $-7 \n\t"
+                    "pushq %[reg] \n\t"
+                    "pushq (%[mem]) \n\t"
+                    "pushq $-7070707 \n\t"
+                    "mov %%rsp, %[new_stack_top] \n\t"
+                    "mov %[tmp], %%rsp"
+                    : [tmp]"=&r"(tmp), [new_stack_top]"=r"(new_stack_top)
+                    : [stack_top]"r"(stack_top),
+                      [reg]"r"(-17l), [mem]"r"(&memw)
+                    : "memory");
+
+       report("push $imm8", stack_top[-1] == -7ul);
+       report("push %reg", stack_top[-2] == -17ul);
+       report("push mem", stack_top[-3] == 0x123456789abcdeful);
+       report("push $imm", stack_top[-4] == -7070707);
+}
+
+void test_pop(void *mem)
+{
+       unsigned long tmp, tmp3, rsp, rbp;
+       unsigned long *stack_top = mem + 4096;
+       unsigned long memw = 0x123456789abcdeful;
+       static unsigned long tmp2;
+
+       memset(mem, 0x55, (void *)stack_top - mem);
+
+       asm volatile("pushq %[val] \n\t"
+                    "popq (%[mem])"
+                    : : [val]"m"(memw), [mem]"r"(mem) : "memory");
+       report("pop mem", *(unsigned long *)mem == memw);
+
+       memw = 7 - memw;
+       asm volatile("mov %%rsp, %[tmp] \n\t"
+                    "mov %[stack_top], %%rsp \n\t"
+                    "pushq %[val] \n\t"
+                    "popq %[tmp2] \n\t"
+                    "mov %[tmp], %%rsp"
+                    : [tmp]"=&r"(tmp), [tmp2]"=m"(tmp2)
+                    : [val]"r"(memw), [stack_top]"r"(stack_top)
+                    : "memory");
+       report("pop mem (2)", tmp2 == memw);
+
+       memw = 129443 - memw;
+       asm volatile("mov %%rsp, %[tmp] \n\t"
+                    "mov %[stack_top], %%rsp \n\t"
+                    "pushq %[val] \n\t"
+                    "popq %[tmp2] \n\t"
+                    "mov %[tmp], %%rsp"
+                    : [tmp]"=&r"(tmp), [tmp2]"=r"(tmp2)
+                    : [val]"r"(memw), [stack_top]"r"(stack_top)
+                    : "memory");
+       report("pop reg", tmp2 == memw);
+
+       asm volatile("mov %%rsp, %[tmp] \n\t"
+                    "mov %[stack_top], %%rsp \n\t"
+                    "push $1f \n\t"
+                    "ret \n\t"
+                    "2: jmp 2b \n\t"
+                    "1: mov %[tmp], %%rsp"
+                    : [tmp]"=&r"(tmp) : [stack_top]"r"(stack_top)
+                    : "memory");
+       report("ret", 1);
+
+       stack_top[-1] = 0x778899;
+       asm volatile("mov %%rsp, %[tmp] \n\t"
+                    "mov %%rbp, %[tmp3] \n\t"
+                    "mov %[stack_top], %%rbp \n\t"
+                    "leave \n\t"
+                    "xchg %%rsp, %[tmp] \n\t"
+                    "xchg %%rbp, %[tmp3]"
+                    : [tmp]"=&r"(tmp), [tmp3]"=&r"(tmp3) : 
[stack_top]"r"(stack_top-1)
+                    : "memory");
+       report("leave", tmp == (ulong)stack_top && tmp3 == 0x778899);
+
+       rbp = 0xaa55aa55bb66bb66ULL;
+       rsp = (unsigned long)stack_top;
+       asm volatile("xchg %%rsp, %[rsp] \n\t"
+                    "xchg %%rbp, %[rbp] \n\t"
+                    "enter $0x1238, $0 \n\t"
+                    "xchg %%rsp, %[rsp] \n\t"
+                    "xchg %%rbp, %[rbp]"
+                    : [rsp]"+a"(rsp), [rbp]"+b"(rbp) : : "memory");
+       report("enter",
+              rsp == (unsigned long)stack_top - 8 - 0x1238
+              && rbp == (unsigned long)stack_top - 8
+              && stack_top[-1] == 0xaa55aa55bb66bb66ULL);
+}
+
+void test_ljmp(void *mem)
+{
+    unsigned char *m = mem;
+    volatile int res = 1;
+
+    *(unsigned long**)m = &&jmpf;
+    asm volatile ("data16/mov %%cs, %0":"=m"(*(m + sizeof(unsigned long))));
+    asm volatile ("rex64/ljmp *%0"::"m"(*m));
+    res = 0;
+jmpf:
+    report("ljmp", res);
+}
+
+void test_incdecnotneg(void *mem)
+{
+    unsigned long *m = mem, v = 1234;
+    unsigned char *mb = mem, vb = 66;
+
+    *m = 0;
+
+    asm volatile ("incl %0":"+m"(*m));
+    report("incl",  *m == 1);
+    asm volatile ("decl %0":"+m"(*m));
+    report("decl",  *m == 0);
+    asm volatile ("incb %0":"+m"(*m));
+    report("incb",  *m == 1);
+    asm volatile ("decb %0":"+m"(*m));
+    report("decb",  *m == 0);
+
+    asm volatile ("lock incl %0":"+m"(*m));
+    report("lock incl",  *m == 1);
+    asm volatile ("lock decl %0":"+m"(*m));
+    report("lock decl",  *m == 0);
+    asm volatile ("lock incb %0":"+m"(*m));
+    report("lock incb",  *m == 1);
+    asm volatile ("lock decb %0":"+m"(*m));
+    report("lock decb",  *m == 0);
+
+    *m = v;
+
+    asm ("lock negq %0" : "+m"(*m)); v = -v;
+    report("lock negl", *m == v);
+    asm ("lock notq %0" : "+m"(*m)); v = ~v;
+    report("lock notl", *m == v);
+
+    *mb = vb;
+
+    asm ("lock negb %0" : "+m"(*mb)); vb = -vb;
+    report("lock negb", *mb == vb);
+    asm ("lock notb %0" : "+m"(*mb)); vb = ~vb;
+    report("lock notb", *mb == vb);
+}
+
+void test_smsw(void)
+{
+       char mem[16];
+       unsigned short msw, msw_orig, *pmsw;
+       int i, zero;
+
+       msw_orig = read_cr0();
+
+       asm("smsw %0" : "=r"(msw));
+       report("smsw (1)", msw == msw_orig);
+
+       memset(mem, 0, 16);
+       pmsw = (void *)mem;
+       asm("smsw %0" : "=m"(pmsw[4]));
+       zero = 1;
+       for (i = 0; i < 8; ++i)
+               if (i != 4 && pmsw[i])
+                       zero = 0;
+       report("smsw (2)", msw == pmsw[4] && zero);
+}
+
+void test_lmsw(void)
+{
+       char mem[16];
+       unsigned short msw, *pmsw;
+       unsigned long cr0;
+
+       cr0 = read_cr0();
+
+       msw = cr0 ^ 8;
+       asm("lmsw %0" : : "r"(msw));
+       printf("before %lx after %lx\n", cr0, read_cr0());
+       report("lmsw (1)", (cr0 ^ read_cr0()) == 8);
+
+       pmsw = (void *)mem;
+       *pmsw = cr0;
+       asm("lmsw %0" : : "m"(*pmsw));
+       printf("before %lx after %lx\n", cr0, read_cr0());
+       report("lmsw (2)", cr0 == read_cr0());
+
+       /* lmsw can't clear cr0.pe */
+       msw = (cr0 & ~1ul) ^ 4;  /* change EM to force trap */
+       asm("lmsw %0" : : "r"(msw));
+       report("lmsw (3)", (cr0 ^ read_cr0()) == 4 && (cr0 & 1));
+
+       /* back to normal */
+       msw = cr0;
+       asm("lmsw %0" : : "r"(msw));
+}
+
+void test_xchg(void *mem)
+{
+       unsigned long *memq = mem;
+       unsigned long rax;
+
+       asm volatile("mov $0x123456789abcdef, %%rax\n\t"
+                    "mov %%rax, (%[memq])\n\t"
+                    "mov $0xfedcba9876543210, %%rax\n\t"
+                    "xchg %%al, (%[memq])\n\t"
+                    "mov %%rax, %[rax]\n\t"
+                    : [rax]"=r"(rax)
+                    : [memq]"r"(memq)
+                    : "memory");
+       report("xchg reg, r/m (1)",
+              rax == 0xfedcba98765432ef && *memq == 0x123456789abcd10);
+
+       asm volatile("mov $0x123456789abcdef, %%rax\n\t"
+                    "mov %%rax, (%[memq])\n\t"
+                    "mov $0xfedcba9876543210, %%rax\n\t"
+                    "xchg %%ax, (%[memq])\n\t"
+                    "mov %%rax, %[rax]\n\t"
+                    : [rax]"=r"(rax)
+                    : [memq]"r"(memq)
+                    : "memory");
+       report("xchg reg, r/m (2)",
+              rax == 0xfedcba987654cdef && *memq == 0x123456789ab3210);
+
+       asm volatile("mov $0x123456789abcdef, %%rax\n\t"
+                    "mov %%rax, (%[memq])\n\t"
+                    "mov $0xfedcba9876543210, %%rax\n\t"
+                    "xchg %%eax, (%[memq])\n\t"
+                    "mov %%rax, %[rax]\n\t"
+                    : [rax]"=r"(rax)
+                    : [memq]"r"(memq)
+                    : "memory");
+       report("xchg reg, r/m (3)",
+              rax == 0x89abcdef && *memq == 0x123456776543210);
+
+       asm volatile("mov $0x123456789abcdef, %%rax\n\t"
+                    "mov %%rax, (%[memq])\n\t"
+                    "mov $0xfedcba9876543210, %%rax\n\t"
+                    "xchg %%rax, (%[memq])\n\t"
+                    "mov %%rax, %[rax]\n\t"
+                    : [rax]"=r"(rax)
+                    : [memq]"r"(memq)
+                    : "memory");
+       report("xchg reg, r/m (4)",
+              rax == 0x123456789abcdef && *memq == 0xfedcba9876543210);
+}
+
+void test_xadd(void *mem)
+{
+       unsigned long *memq = mem;
+       unsigned long rax;
+
+       asm volatile("mov $0x123456789abcdef, %%rax\n\t"
+                    "mov %%rax, (%[memq])\n\t"
+                    "mov $0xfedcba9876543210, %%rax\n\t"
+                    "xadd %%al, (%[memq])\n\t"
+                    "mov %%rax, %[rax]\n\t"
+                    : [rax]"=r"(rax)
+                    : [memq]"r"(memq)
+                    : "memory");
+       report("xadd reg, r/m (1)",
+              rax == 0xfedcba98765432ef && *memq == 0x123456789abcdff);
+
+       asm volatile("mov $0x123456789abcdef, %%rax\n\t"
+                    "mov %%rax, (%[memq])\n\t"
+                    "mov $0xfedcba9876543210, %%rax\n\t"
+                    "xadd %%ax, (%[memq])\n\t"
+                    "mov %%rax, %[rax]\n\t"
+                    : [rax]"=r"(rax)
+                    : [memq]"r"(memq)
+                    : "memory");
+       report("xadd reg, r/m (2)",
+              rax == 0xfedcba987654cdef && *memq == 0x123456789abffff);
+
+       asm volatile("mov $0x123456789abcdef, %%rax\n\t"
+                    "mov %%rax, (%[memq])\n\t"
+                    "mov $0xfedcba9876543210, %%rax\n\t"
+                    "xadd %%eax, (%[memq])\n\t"
+                    "mov %%rax, %[rax]\n\t"
+                    : [rax]"=r"(rax)
+                    : [memq]"r"(memq)
+                    : "memory");
+       report("xadd reg, r/m (3)",
+              rax == 0x89abcdef && *memq == 0x1234567ffffffff);
+
+       asm volatile("mov $0x123456789abcdef, %%rax\n\t"
+                    "mov %%rax, (%[memq])\n\t"
+                    "mov $0xfedcba9876543210, %%rax\n\t"
+                    "xadd %%rax, (%[memq])\n\t"
+                    "mov %%rax, %[rax]\n\t"
+                    : [rax]"=r"(rax)
+                    : [memq]"r"(memq)
+                    : "memory");
+       report("xadd reg, r/m (4)",
+              rax == 0x123456789abcdef && *memq == 0xffffffffffffffff);
+}
+
+void test_btc(void *mem)
+{
+       unsigned int *a = mem;
+
+       memset(mem, 0, 3 * sizeof(unsigned int));
+
+       asm ("btcl $32, %0" :: "m"(a[0]) : "memory");
+       asm ("btcl $1, %0" :: "m"(a[1]) : "memory");
+       asm ("btcl %1, %0" :: "m"(a[0]), "r"(66) : "memory");
+       report("btcl imm8, r/m", a[0] == 1 && a[1] == 2 && a[2] == 4);
+
+       asm ("btcl %1, %0" :: "m"(a[3]), "r"(-1) : "memory");
+       report("btcl reg, r/m", a[0] == 1 && a[1] == 2 && a[2] == 0x80000004);
+}
+
+void test_bsfbsr(void *mem)
+{
+       unsigned long rax, *memq = mem;
+       unsigned eax, *meml = mem;
+       unsigned short ax, *memw = mem;
+       unsigned char z;
+
+       *memw = 0xc000;
+       asm("bsfw %[mem], %[a]" : [a]"=a"(ax) : [mem]"m"(*memw));
+       report("bsfw r/m, reg", ax == 14);
+
+       *meml = 0xc0000000;
+       asm("bsfl %[mem], %[a]" : [a]"=a"(eax) : [mem]"m"(*meml));
+       report("bsfl r/m, reg", eax == 30);
+
+       *memq = 0xc00000000000;
+       asm("bsfq %[mem], %[a]" : [a]"=a"(rax) : [mem]"m"(*memq));
+       report("bsfq r/m, reg", rax == 46);
+
+       *memq = 0;
+       asm("bsfq %[mem], %[a]; setz %[z]"
+           : [a]"=a"(rax), [z]"=rm"(z) : [mem]"m"(*memq));
+       report("bsfq r/m, reg", z == 1);
+
+       *memw = 0xc000;
+       asm("bsrw %[mem], %[a]" : [a]"=a"(ax) : [mem]"m"(*memw));
+       report("bsrw r/m, reg", ax == 15);
+
+       *meml = 0xc0000000;
+       asm("bsrl %[mem], %[a]" : [a]"=a"(eax) : [mem]"m"(*meml));
+       report("bsrl r/m, reg", eax == 31);
+
+       *memq = 0xc00000000000;
+       asm("bsrq %[mem], %[a]" : [a]"=a"(rax) : [mem]"m"(*memq));
+       report("bsrq r/m, reg", rax == 47);
+
+       *memq = 0;
+       asm("bsrq %[mem], %[a]; setz %[z]"
+           : [a]"=a"(rax), [z]"=rm"(z) : [mem]"m"(*memq));
+       report("bsrq r/m, reg", z == 1);
+}
+
+static void test_imul(ulong *mem)
+{
+    ulong a;
+
+    *mem = 51; a = 0x1234567812345678UL;
+    asm ("imulw %1, %%ax" : "+a"(a) : "m"(*mem));
+    report("imul ax, mem", a == 0x12345678123439e8);
+
+    *mem = 51; a = 0x1234567812345678UL;
+    asm ("imull %1, %%eax" : "+a"(a) : "m"(*mem));
+    report("imul eax, mem", a == 0xa06d39e8);
+
+    *mem = 51; a = 0x1234567812345678UL;
+    asm ("imulq %1, %%rax" : "+a"(a) : "m"(*mem));
+    report("imul rax, mem", a == 0xA06D39EBA06D39E8UL);
+
+    *mem  = 0x1234567812345678UL; a = 0x8765432187654321L;
+    asm ("imulw $51, %1, %%ax" : "+a"(a) : "m"(*mem));
+    report("imul ax, mem, imm8", a == 0x87654321876539e8);
+
+    *mem = 0x1234567812345678UL;
+    asm ("imull $51, %1, %%eax" : "+a"(a) : "m"(*mem));
+    report("imul eax, mem, imm8", a == 0xa06d39e8);
+
+    *mem = 0x1234567812345678UL;
+    asm ("imulq $51, %1, %%rax" : "+a"(a) : "m"(*mem));
+    report("imul rax, mem, imm8", a == 0xA06D39EBA06D39E8UL);
+
+    *mem  = 0x1234567812345678UL; a = 0x8765432187654321L;
+    asm ("imulw $311, %1, %%ax" : "+a"(a) : "m"(*mem));
+    report("imul ax, mem, imm", a == 0x8765432187650bc8);
+
+    *mem = 0x1234567812345678UL;
+    asm ("imull $311, %1, %%eax" : "+a"(a) : "m"(*mem));
+    report("imul eax, mem, imm", a == 0x1d950bc8);
+
+    *mem = 0x1234567812345678UL;
+    asm ("imulq $311, %1, %%rax" : "+a"(a) : "m"(*mem));
+    report("imul rax, mem, imm", a == 0x1D950BDE1D950BC8L);
+}
+
+static void test_muldiv(long *mem)
+{
+    long a, d, aa, dd;
+    u8 ex = 1;
+
+    *mem = 0; a = 1; d = 2;
+    asm (ASM_TRY("1f") "divq %3; movb $0, %2; 1:"
+        : "+a"(a), "+d"(d), "+q"(ex) : "m"(*mem));
+    report("divq (fault)", a == 1 && d == 2 && ex);
+
+    *mem = 987654321098765UL; a = 123456789012345UL; d = 123456789012345UL;
+    asm (ASM_TRY("1f") "divq %3; movb $0, %2; 1:"
+        : "+a"(a), "+d"(d), "+q"(ex) : "m"(*mem));
+    report("divq (1)",
+          a == 0x1ffffffb1b963b33ul && d == 0x273ba4384ede2ul && !ex);
+    aa = 0x1111111111111111; dd = 0x2222222222222222;
+    *mem = 0x3333333333333333; a = aa; d = dd;
+    asm("mulb %2" : "+a"(a), "+d"(d) : "m"(*mem));
+    report("mulb mem", a == 0x1111111111110363 && d == dd);
+    *mem = 0x3333333333333333; a = aa; d = dd;
+    asm("mulw %2" : "+a"(a), "+d"(d) : "m"(*mem));
+    report("mulw mem", a == 0x111111111111c963 && d == 0x2222222222220369);
+    *mem = 0x3333333333333333; a = aa; d = dd;
+    asm("mull %2" : "+a"(a), "+d"(d) : "m"(*mem));
+    report("mull mem", a == 0x962fc963 && d == 0x369d036);
+    *mem = 0x3333333333333333; a = aa; d = dd;
+    asm("mulq %2" : "+a"(a), "+d"(d) : "m"(*mem));
+    report("mulq mem", a == 0x2fc962fc962fc963 && d == 0x369d0369d0369d0);
+}
+
+typedef unsigned __attribute__((vector_size(16))) sse128;
+
+typedef union {
+    sse128 sse;
+    unsigned u[4];
+} sse_union;
+
+static bool sseeq(sse_union *v1, sse_union *v2)
+{
+    bool ok = true;
+    int i;
+
+    for (i = 0; i < 4; ++i) {
+       ok &= v1->u[i] == v2->u[i];
+    }
+
+    return ok;
+}
+
+static void test_sse(sse_union *mem)
+{
+    sse_union v;
+
+    write_cr0(read_cr0() & ~6); /* EM, TS */
+    write_cr4(read_cr4() | 0x200); /* OSFXSR */
+    v.u[0] = 1; v.u[1] = 2; v.u[2] = 3; v.u[3] = 4;
+    asm("movdqu %1, %0" : "=m"(*mem) : "x"(v.sse));
+    report("movdqu (read)", sseeq(&v, mem));
+    mem->u[0] = 5; mem->u[1] = 6; mem->u[2] = 7; mem->u[3] = 8;
+    asm("movdqu %1, %0" : "=x"(v.sse) : "m"(*mem));
+    report("movdqu (write)", sseeq(mem, &v));
+}
+
+static void test_mmx(uint64_t *mem)
+{
+    uint64_t v;
+
+    write_cr0(read_cr0() & ~6); /* EM, TS */
+    asm volatile("fninit");
+    v = 0x0102030405060708ULL;
+    asm("movq %1, %0" : "=m"(*mem) : "y"(v));
+    report("movq (mmx, read)", v == *mem);
+    *mem = 0x8070605040302010ull;
+    asm("movq %1, %0" : "=y"(v) : "m"(*mem));
+    report("movq (mmx, write)", v == *mem);
+}
+
+static void test_rip_relative(unsigned *mem, char *insn_ram)
+{
+    /* movb $1, mem+2(%rip) */
+    insn_ram[0] = 0xc6;
+    insn_ram[1] = 0x05;
+    *(unsigned *)&insn_ram[2] = 2 + (char *)mem - (insn_ram + 7);
+    insn_ram[6] = 0x01;
+    /* ret */
+    insn_ram[7] = 0xc3;
+
+    *mem = 0;
+    asm("callq *%1" : "+m"(*mem) : "r"(insn_ram));
+    report("movb $imm, 0(%rip)", *mem == 0x10000);
+}
+
+static void test_shld_shrd(u32 *mem)
+{
+    *mem = 0x12345678;
+    asm("shld %2, %1, %0" : "+m"(*mem) : "r"(0xaaaaaaaaU), "c"((u8)3));
+    report("shld (cl)", *mem == ((0x12345678 << 3) | 5));
+    *mem = 0x12345678;
+    asm("shrd %2, %1, %0" : "+m"(*mem) : "r"(0x55555555U), "c"((u8)3));
+    report("shrd (cl)", *mem == ((0x12345678 >> 3) | (5u << 29)));
+}
+
+#define INSN_XCHG_ALL                          \
+       "xchg %rax, 0+save \n\t"                \
+       "xchg %rbx, 8+save \n\t"                \
+       "xchg %rcx, 16+save \n\t"               \
+       "xchg %rdx, 24+save \n\t"               \
+       "xchg %rsi, 32+save \n\t"               \
+       "xchg %rdi, 40+save \n\t"               \
+       "xchg %rsp, 48+save \n\t"               \
+       "xchg %rbp, 56+save \n\t"               \
+       "xchg %r8, 64+save \n\t"                \
+       "xchg %r9, 72+save \n\t"                \
+       "xchg %r10, 80+save \n\t"               \
+       "xchg %r11, 88+save \n\t"               \
+       "xchg %r12, 96+save \n\t"               \
+       "xchg %r13, 104+save \n\t"              \
+       "xchg %r14, 112+save \n\t"              \
+       "xchg %r15, 120+save \n\t"
+
+asm(
+       ".align 4096\n\t"
+       "insn_page:\n\t"
+       "ret\n\t"
+       "pushf\n\t"
+       "push 136+save \n\t"
+       "popf \n\t"
+       INSN_XCHG_ALL
+       "test_insn:\n\t"
+       "in  (%dx),%al\n\t"
+       ".skip 31, 0x90\n\t"
+       "test_insn_end:\n\t"
+       INSN_XCHG_ALL
+       "pushf \n\t"
+       "pop 136+save \n\t"
+       "popf \n\t"
+       "ret \n\t"
+       "insn_page_end:\n\t"
+       ".align 4096\n\t"
+);
+
+#define MK_INSN(name, str)                             \
+    asm (                                              \
+        ".pushsection .data.insn  \n\t"                \
+        "insn_" #name ": \n\t"                         \
+        ".quad 1001f, 1002f - 1001f \n\t"              \
+        ".popsection \n\t"                             \
+        ".pushsection .text.insn, \"ax\" \n\t"         \
+        "1001: \n\t"                                   \
+        "insn_code_" #name ": " str " \n\t"            \
+        "1002: \n\t"                                   \
+        ".popsection"                                  \
+    );                                                 \
+    extern struct insn_desc insn_##name;
+
+static void trap_emulator(uint64_t *mem, void *alt_insn_page,
+                       struct insn_desc *alt_insn)
+{
+       ulong *cr3 = (ulong *)read_cr3();
+       void *insn_ram;
+       extern u8 insn_page[], test_insn[];
+
+       insn_ram = vmap(virt_to_phys(insn_page), 4096);
+       memcpy(alt_insn_page, insn_page, 4096);
+       memcpy(alt_insn_page + (test_insn - insn_page),
+                       (void *)(alt_insn->ptr), alt_insn->len);
+       save = inregs;
+
+       /* Load the code TLB with insn_page, but point the page tables at
+          alt_insn_page (and keep the data TLB clear, for AMD decode assist).
+          This will make the CPU trap on the insn_page instruction but the
+          hypervisor will see alt_insn_page. */
+       install_page(cr3, virt_to_phys(insn_page), insn_ram);
+       invlpg(insn_ram);
+       /* Load code TLB */
+       asm volatile("call *%0" : : "r"(insn_ram));
+       install_page(cr3, virt_to_phys(alt_insn_page), insn_ram);
+       /* Trap, let hypervisor emulate at alt_insn_page */
+       asm volatile("call *%0": : "r"(insn_ram+1));
+
+       outregs = save;
+}
+
+static void advance_rip_by_3_and_note_exception(struct ex_regs *regs)
+{
+    ++exceptions;
+    regs->rip += 3;
+}
+
+static void test_mmx_movq_mf(uint64_t *mem, uint8_t *insn_page,
+                            uint8_t *alt_insn_page, void *insn_ram)
+{
+    uint16_t fcw = 0;  /* all exceptions unmasked */
+    /* movq %mm0, (%rax) */
+    void *stack = alloc_page();
+
+    write_cr0(read_cr0() & ~6);  /* TS, EM */
+    exceptions = 0;
+    handle_exception(MF_VECTOR, advance_rip_by_3_and_note_exception);
+    asm volatile("fninit; fldcw %0" : : "m"(fcw));
+    asm volatile("fldz; fldz; fdivp"); /* generate exception */
+
+    MK_INSN(mmx_movq_mf, "movq %mm0, (%rax) \n\t");
+    inregs = (struct regs){ .rsp=(u64)stack+1024 };
+    trap_emulator(mem, alt_insn_page, &insn_mmx_movq_mf);
+    /* exit MMX mode */
+    asm volatile("fnclex; emms");
+    report("movq mmx generates #MF", exceptions == 1);
+    handle_exception(MF_VECTOR, 0);
+}
+
+static void test_movabs(uint64_t *mem, uint8_t *insn_page,
+                      uint8_t *alt_insn_page, void *insn_ram)
+{
+    /* mov $0x9090909090909090, %rcx */
+    MK_INSN(movabs, "mov $0x9090909090909090, %rcx\n\t");
+    inregs = (struct regs){ 0 };
+    trap_emulator(mem, alt_insn_page, &insn_movabs);
+    report("64-bit mov imm2", outregs.rcx == 0x9090909090909090);
+}
+
+static void test_crosspage_mmio(volatile uint8_t *mem)
+{
+    volatile uint16_t w, *pw;
+
+    pw = (volatile uint16_t *)&mem[4095];
+    mem[4095] = 0x99;
+    mem[4096] = 0x77;
+    asm volatile("mov %1, %0" : "=r"(w) : "m"(*pw) : "memory");
+    report("cross-page mmio read", w == 0x7799);
+    asm volatile("mov %1, %0" : "=m"(*pw) : "r"((uint16_t)0x88aa));
+    report("cross-page mmio write", mem[4095] == 0xaa && mem[4096] == 0x88);
+}
+
+static void test_string_io_mmio(volatile uint8_t *mem)
+{
+       /* Cross MMIO pages.*/
+       volatile uint8_t *mmio = mem + 4032;
+
+       asm volatile("outw %%ax, %%dx  \n\t" : : "a"(0x9999), 
"d"(TESTDEV_IO_PORT));
+
+       asm volatile ("cld; rep insb" : : "d" (TESTDEV_IO_PORT), "D" (mmio), 
"c" (1024));
+
+       report("string_io_mmio", mmio[1023] == 0x99);
+}
+
+static void test_lgdt_lidt(volatile uint8_t *mem)
+{
+    struct descriptor_table_ptr orig, fresh = {};
+
+    sgdt(&orig);
+    *(struct descriptor_table_ptr *)mem = (struct descriptor_table_ptr) {
+       .limit = 0xf234,
+       .base = 0x12345678abcd,
+    };
+    cli();
+    asm volatile("lgdt %0" : : "m"(*(struct descriptor_table_ptr *)mem));
+    sgdt(&fresh);
+    lgdt(&orig);
+    sti();
+    report("lgdt (long address)", orig.limit == fresh.limit && orig.base == 
fresh.base);
+
+    sidt(&orig);
+    *(struct descriptor_table_ptr *)mem = (struct descriptor_table_ptr) {
+       .limit = 0x432f,
+       .base = 0xdbca87654321,
+    };
+    cli();
+    asm volatile("lidt %0" : : "m"(*(struct descriptor_table_ptr *)mem));
+    sidt(&fresh);
+    lidt(&orig);
+    sti();
+    report("lidt (long address)", orig.limit == fresh.limit && orig.base == 
fresh.base);
+}
+
+static void ss_bad_rpl(struct ex_regs *regs)
+{
+    extern char ss_bad_rpl_cont;
+
+    ++exceptions;
+    regs->rip = (ulong)&ss_bad_rpl_cont;
+}
+
+static void test_sreg(volatile uint16_t *mem)
+{
+    u16 ss = read_ss();
+
+    // check for null segment load
+    *mem = 0;
+    asm volatile("mov %0, %%ss" : : "m"(*mem));
+    report("mov null, %ss", read_ss() == 0);
+
+    // check for exception when ss.rpl != cpl on null segment load
+    exceptions = 0;
+    handle_exception(GP_VECTOR, ss_bad_rpl);
+    *mem = 3;
+    asm volatile("mov %0, %%ss; ss_bad_rpl_cont:" : : "m"(*mem));
+    report("mov null, %ss (with ss.rpl != cpl)", exceptions == 1 && read_ss() 
== 0);
+    handle_exception(GP_VECTOR, 0);
+    write_ss(ss);
+}
+
+static void test_lldt(volatile uint16_t *mem)
+{
+    u64 gdt[] = { 0, 0x0000f82000000ffffull /* ldt descriptor */ };
+    struct descriptor_table_ptr gdt_ptr = { .limit = 0xffff, .base = 
(ulong)&gdt };
+    struct descriptor_table_ptr orig_gdt;
+
+    cli();
+    sgdt(&orig_gdt);
+    lgdt(&gdt_ptr);
+    *mem = 0x8;
+    asm volatile("lldt %0" : : "m"(*mem));
+    lgdt(&orig_gdt);
+    sti();
+    report("lldt", sldt() == *mem);
+}
+
+static void test_ltr(volatile uint16_t *mem)
+{
+    struct descriptor_table_ptr gdt_ptr;
+    uint64_t *gdt, *trp;
+    uint16_t tr = str();
+    uint64_t busy_mask = (uint64_t)1 << 41;
+
+    sgdt(&gdt_ptr);
+    gdt = (uint64_t *)gdt_ptr.base;
+    trp = &gdt[tr >> 3];
+    *trp &= ~busy_mask;
+    *mem = tr;
+    asm volatile("ltr %0" : : "m"(*mem) : "memory");
+    report("ltr", str() == tr && (*trp & busy_mask));
+}
+
+static void test_simplealu(u32 *mem)
+{
+    *mem = 0x1234;
+    asm("or %1, %0" : "+m"(*mem) : "r"(0x8001));
+    report("or", *mem == 0x9235);
+    asm("add %1, %0" : "+m"(*mem) : "r"(2));
+    report("add", *mem == 0x9237);
+    asm("xor %1, %0" : "+m"(*mem) : "r"(0x1111));
+    report("xor", *mem == 0x8326);
+    asm("sub %1, %0" : "+m"(*mem) : "r"(0x26));
+    report("sub", *mem == 0x8300);
+    asm("clc; adc %1, %0" : "+m"(*mem) : "r"(0x100));
+    report("adc(0)", *mem == 0x8400);
+    asm("stc; adc %1, %0" : "+m"(*mem) : "r"(0x100));
+    report("adc(0)", *mem == 0x8501);
+    asm("clc; sbb %1, %0" : "+m"(*mem) : "r"(0));
+    report("sbb(0)", *mem == 0x8501);
+    asm("stc; sbb %1, %0" : "+m"(*mem) : "r"(0));
+    report("sbb(1)", *mem == 0x8500);
+    asm("and %1, %0" : "+m"(*mem) : "r"(0xfe77));
+    report("and", *mem == 0x8400);
+    asm("test %1, %0" : "+m"(*mem) : "r"(0xf000));
+    report("test", *mem == 0x8400);
+}
+
+int main()
+{
+       void *mem;
+       void *insn_page, *alt_insn_page;
+       void *insn_ram;
+       unsigned long t1, t2;
+
+       setup_vm();
+       setup_idt();
+       mem = alloc_vpages(2);
+       install_page((void *)read_cr3(), IORAM_BASE_PHYS, mem);
+       // install the page twice to test cross-page mmio
+       install_page((void *)read_cr3(), IORAM_BASE_PHYS, mem + 4096);
+       insn_page = alloc_page();
+       alt_insn_page = alloc_page();
+       insn_ram = vmap(virt_to_phys(insn_page), 4096);
+
+       // test mov reg, r/m and mov r/m, reg
+       t1 = 0x123456789abcdef;
+       asm volatile("mov %[t1], (%[mem]) \n\t"
+                    "mov (%[mem]), %[t2]"
+                    : [t2]"=r"(t2)
+                    : [t1]"r"(t1), [mem]"r"(mem)
+                    : "memory");
+       report("mov reg, r/m (1)", t2 == 0x123456789abcdef);
+
+       test_simplealu(mem);
+       test_cmps(mem);
+       test_scas(mem);
+
+       test_push(mem);
+       test_pop(mem);
+
+       test_xchg(mem);
+       test_xadd(mem);
+
+       test_cr8();
+
+       test_smsw();
+       test_lmsw();
+       test_ljmp(mem);
+       test_stringio();
+       test_incdecnotneg(mem);
+       test_btc(mem);
+       test_bsfbsr(mem);
+       test_imul(mem);
+       test_muldiv(mem);
+       test_sse(mem);
+       test_mmx(mem);
+       test_rip_relative(mem, insn_ram);
+       test_shld_shrd(mem);
+       //test_lgdt_lidt(mem);
+       test_sreg(mem);
+       test_lldt(mem);
+       test_ltr(mem);
+
+       test_mmx_movq_mf(mem, insn_page, alt_insn_page, insn_ram);
+       test_movabs(mem, insn_page, alt_insn_page, insn_ram);
+
+       test_crosspage_mmio(mem);
+
+       test_string_io_mmio(mem);
+
+       printf("\nSUMMARY: %d tests, %d failures\n", tests, fails);
+       return fails ? 1 : 0;
+}
diff --git a/kvm-unittest/x86/eventinj.c b/kvm-unittest/x86/eventinj.c
new file mode 100644
index 0000000..3d36b37
--- /dev/null
+++ b/kvm-unittest/x86/eventinj.c
@@ -0,0 +1,422 @@
+#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("Sending nested NMI to self\n");
+       apic_self_nmi();
+       io_delay();
+       printf("After nested NMI to self\n");
+}
+
+unsigned long after_iret_addr;
+
+static void nested_nmi_iret_isr(struct ex_regs *r)
+{
+       printf("Nested NMI isr running rip=%x\n", r->rip);
+
+       if (r->rip == after_iret_addr)
+               test_count++;
+}
+static void nmi_iret_isr(struct ex_regs *r)
+{
+       unsigned long *s = alloc_page();
+       test_count++;
+       printf("NMI isr running %p stack %p\n", &&after_iret, s);
+       handle_exception(2, nested_nmi_iret_isr);
+       printf("Sending nested NMI to self\n");
+       apic_self_nmi();
+       printf("After nested NMI to self\n");
+       s[4] = read_ss();
+       s[3] = 0; /* rsp */
+       s[2] = read_rflags();
+       s[1] = read_cs();
+       s[0] = after_iret_addr = (unsigned long)&&after_iret;
+       asm ("mov %%rsp, %0\n\t"
+            "mov %1, %%rsp\n\t"
+            "outl %2, $0xe4\n\t" /* flush stack page */
+#ifdef __x86_64__
+            "iretq\n\t"
+#else
+            "iretl\n\t"
+#endif
+            : "=m"(s[3]) : "rm"(&s[0]), "a"((unsigned int)virt_to_phys(s)) : 
"memory");
+after_iret:
+       printf("After iret\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("Sending vec 33 to self\n");
+       irq_enable();
+       apic_self_ipi(33);
+       io_delay();
+       irq_disable();
+       printf("After vec 33 to self\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("Sending vec 32 and 33 to self\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 self\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("Sending 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("Sending 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("Sending NMI to self\n");
+       apic_self_nmi();
+       printf("After NMI to self\n");
+       /* this is needed on VMX without NMI window notification.
+          Interrupt windows is used instead, so let pending NMI
+          to be injected */
+       irq_enable();
+       asm volatile ("nop");
+       irq_disable();
+       report("NMI", test_count == 2);
+
+       /* generate NMI that will fault on IRET */
+       printf("Before NMI IRET test\n");
+       test_count = 0;
+       handle_exception(2, nmi_iret_isr);
+       printf("Sending NMI to self\n");
+       apic_self_nmi();
+       /* this is needed on VMX without NMI window notification.
+          Interrupt windows is used instead, so let pending NMI
+          to be injected */
+       irq_enable();
+       asm volatile ("nop");
+       irq_disable();
+       printf("After NMI to self\n");
+       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;
+}
diff --git a/kvm-unittest/x86/hypercall.c b/kvm-unittest/x86/hypercall.c
new file mode 100644
index 0000000..95120a2
--- /dev/null
+++ b/kvm-unittest/x86/hypercall.c
@@ -0,0 +1,31 @@
+#include "libcflat.h"
+
+#define KVM_HYPERCALL_INTEL ".byte 0x0f,0x01,0xc1"
+#define KVM_HYPERCALL_AMD ".byte 0x0f,0x01,0xd9"
+
+static inline long kvm_hypercall0_intel(unsigned int nr)
+{
+       long ret;
+       asm volatile(KVM_HYPERCALL_INTEL
+                    : "=a"(ret)
+                    : "a"(nr));
+       return ret;
+}
+
+static inline long kvm_hypercall0_amd(unsigned int nr)
+{
+       long ret;
+       asm volatile(KVM_HYPERCALL_AMD
+                    : "=a"(ret)
+                    : "a"(nr));
+       return ret;
+}
+
+int main(int ac, char **av)
+{
+       kvm_hypercall0_intel(-1u);
+       printf("Hypercall via VMCALL: OK\n");
+       kvm_hypercall0_amd(-1u);
+       printf("Hypercall via VMMCALL: OK\n");
+       return 0;
+}
diff --git a/kvm-unittest/x86/idt_test.c b/kvm-unittest/x86/idt_test.c
new file mode 100644
index 0000000..2d2e0c2
--- /dev/null
+++ b/kvm-unittest/x86/idt_test.c
@@ -0,0 +1,49 @@
+#include "libcflat.h"
+#include "desc.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;
+}
diff --git a/kvm-unittest/x86/init.c b/kvm-unittest/x86/init.c
new file mode 100644
index 0000000..717511e
--- /dev/null
+++ b/kvm-unittest/x86/init.c
@@ -0,0 +1,129 @@
+#include "libcflat.h"
+#include "apic.h"
+#include "io.h"
+
+#define KBD_CCMD_READ_OUTPORT   0xD0    /* read output port */
+#define KBD_CCMD_WRITE_OUTPORT  0xD1    /* write output port */
+#define KBD_CCMD_RESET          0xFE    /* CPU reset */
+
+static inline void kbd_cmd(u8 val)
+{
+    while (inb(0x64) & 2);
+    outb(val, 0x64);
+}
+
+static inline u8 kbd_in(void)
+{
+    kbd_cmd(KBD_CCMD_READ_OUTPORT);
+    while (inb(0x64) & 2);
+    return inb(0x60);
+}
+
+static inline void kbd_out(u8 val)
+{
+    kbd_cmd(KBD_CCMD_WRITE_OUTPORT);
+    while (inb(0x64) & 2);
+    outb(val, 0x60);
+}
+
+static inline void rtc_out(u8 reg, u8 val)
+{
+    outb(reg, 0x70);
+    outb(val, 0x71);
+}
+
+extern char resume_start, resume_end;
+
+#define state (*(volatile int *)0x2000)
+#define bad (*(volatile int *)0x2004)
+#define resumed (*(volatile int *)0x2008)
+
+int main(int argc, char **argv)
+{
+       volatile u16 *resume_vector_ptr = (u16 *)0x467L;
+       char *addr, *resume_vec = (void*)0x1000;
+
+       /* resume execution by indirect jump via 40h:0067h */
+       rtc_out(0x0f, 0x0a);
+       resume_vector_ptr[0] = ((u32)(ulong)resume_vec);
+       resume_vector_ptr[1] = 0;
+
+       for (addr = &resume_start; addr < &resume_end; addr++)
+               *resume_vec++ = *addr;
+
+       if (state != 0) {
+               /*
+                * Strictly speaking this is a firmware problem, but let's check
+                * for it as well...
+                */
+               if (resumed != 1) {
+                       printf("Uh, resume vector visited %d times?\n", 
resumed);
+                       bad |= 2;
+               }
+               /*
+                * Port 92 bit 0 is cleared on system reset.  On a soft reset it
+                * is left to 1.  Use this to distinguish INIT from hard reset.
+                */
+               if (resumed != 0 && (inb(0x92) & 1) == 0) {
+                       printf("Uh, hard reset!\n");
+                       bad |= 1;
+               }
+       }
+
+       resumed = 0;
+
+       switch (state++) {
+       case 0:
+               printf("testing port 92 init... ");
+               outb(inb(0x92) & ~1, 0x92);
+               outb(inb(0x92) | 1, 0x92);
+               break;
+
+       case 1:
+               printf("testing kbd controller reset... ");
+               kbd_cmd(KBD_CCMD_RESET);
+               break;
+
+       case 2:
+               printf("testing kbd controller init... ");
+               kbd_out(kbd_in() & ~1);
+               break;
+
+       case 3:
+               printf("testing 0xcf9h init... ");
+               outb(0, 0xcf9);
+               outb(4, 0xcf9);
+               break;
+
+       case 4:
+               printf("testing init to BSP... ");
+               apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL
+                             | APIC_DM_INIT, 0);
+               break;
+
+       case 5:
+               exit(bad);
+       }
+
+       /* The resume code will get us back to main.  */
+       asm("cli; hlt");
+}
+
+asm (
+       ".global resume_start\n"
+       ".global resume_end\n"
+       ".code16\n"
+       "resume_start:\n"
+       "incb %cs:0x2008\n"             // resumed++;
+       "mov $0x0f, %al\n"              // rtc_out(0x0f, 0x00);
+       "out %al, $0x70\n"
+       "mov $0x00, %al\n"
+       "out %al, $0x71\n"
+       "jmp $0xffff, $0x0000\n"        // BIOS reset
+       "resume_end:\n"
+#ifdef __i386__
+       ".code32\n"
+#else
+       ".code64\n"
+#endif
+    );
diff --git a/kvm-unittest/x86/kvmclock.c b/kvm-unittest/x86/kvmclock.c
new file mode 100644
index 0000000..5b831c5
--- /dev/null
+++ b/kvm-unittest/x86/kvmclock.c
@@ -0,0 +1,279 @@
+#include "libcflat.h"
+#include "smp.h"
+#include "atomic.h"
+#include "processor.h"
+#include "kvmclock.h"
+
+#define unlikely(x)    __builtin_expect(!!(x), 0)
+#define likely(x)      __builtin_expect(!!(x), 1)
+
+
+struct pvclock_vcpu_time_info __attribute__((aligned(4))) hv_clock[MAX_CPU];
+struct pvclock_wall_clock wall_clock;
+static unsigned char valid_flags = 0;
+static atomic64_t last_value = ATOMIC64_INIT(0);
+
+/*
+ * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction,
+ * yielding a 64-bit result.
+ */
+static inline u64 scale_delta(u64 delta, u32 mul_frac, int shift)
+{
+       u64 product;
+#ifdef __i386__
+       u32 tmp1, tmp2;
+#endif
+
+       if (shift < 0)
+               delta >>= -shift;
+       else
+               delta <<= shift;
+
+#ifdef __i386__
+       __asm__ (
+               "mul  %5       ; "
+               "mov  %4,%%eax ; "
+               "mov  %%edx,%4 ; "
+               "mul  %5       ; "
+               "xor  %5,%5    ; "
+               "add  %4,%%eax ; "
+               "adc  %5,%%edx ; "
+               : "=A" (product), "=r" (tmp1), "=r" (tmp2)
+               : "a" ((u32)delta), "1" ((u32)(delta >> 32)), "2" (mul_frac) );
+#elif defined(__x86_64__)
+       __asm__ (
+               "mul %%rdx ; shrd $32,%%rdx,%%rax"
+               : "=a" (product) : "0" (delta), "d" ((u64)mul_frac) );
+#else
+#error implement me!
+#endif
+
+       return product;
+}
+
+#ifdef __i386__
+# define do_div(n,base) ({                                     \
+       u32 __base = (base);                                    \
+       u32 __rem;                                              \
+       __rem = ((u64)(n)) % __base;                            \
+       (n) = ((u64)(n)) / __base;                              \
+       __rem;                                                  \
+ })
+#else
+u32 __attribute__((weak)) __div64_32(u64 *n, u32 base)
+{
+       u64 rem = *n;
+       u64 b = base;
+       u64 res, d = 1;
+       u32 high = rem >> 32;
+
+       /* Reduce the thing a bit first */
+       res = 0;
+       if (high >= base) {
+               high /= base;
+               res = (u64) high << 32;
+               rem -= (u64) (high*base) << 32;
+       }
+
+       while ((s64)b > 0 && b < rem) {
+               b = b+b;
+               d = d+d;
+       }
+
+       do {
+               if (rem >= b) {
+                       rem -= b;
+                       res += d;
+               }
+               b >>= 1;
+               d >>= 1;
+       } while (d);
+
+       *n = res;
+       return rem;
+}
+
+# define do_div(n,base) ({                             \
+       u32 __base = (base);                            \
+       u32 __rem;                                      \
+       (void)(((typeof((n)) *)0) == ((u64 *)0));       \
+       if (likely(((n) >> 32) == 0)) {                 \
+               __rem = (u32)(n) % __base;              \
+               (n) = (u32)(n) / __base;                \
+       } else                                          \
+               __rem = __div64_32(&(n), __base);       \
+       __rem;                                          \
+ })
+#endif
+
+/**
+ * set_normalized_timespec - set timespec sec and nsec parts and normalize
+ *
+ * @ts:                pointer to timespec variable to be set
+ * @sec:       seconds to set
+ * @nsec:      nanoseconds to set
+ *
+ * Set seconds and nanoseconds field of a timespec variable and
+ * normalize to the timespec storage format
+ *
+ * Note: The tv_nsec part is always in the range of
+ *     0 <= tv_nsec < NSEC_PER_SEC
+ * For negative values only the tv_sec field is negative !
+ */
+void set_normalized_timespec(struct timespec *ts, long sec, s64 nsec)
+{
+       while (nsec >= NSEC_PER_SEC) {
+               /*
+                * The following asm() prevents the compiler from
+                * optimising this loop into a modulo operation. See
+                * also __iter_div_u64_rem() in include/linux/time.h
+                */
+               asm("" : "+rm"(nsec));
+               nsec -= NSEC_PER_SEC;
+               ++sec;
+       }
+       while (nsec < 0) {
+               asm("" : "+rm"(nsec));
+               nsec += NSEC_PER_SEC;
+               --sec;
+       }
+       ts->tv_sec = sec;
+       ts->tv_nsec = nsec;
+}
+
+static u64 pvclock_get_nsec_offset(struct pvclock_shadow_time *shadow)
+{
+       u64 delta = rdtsc() - shadow->tsc_timestamp;
+       return scale_delta(delta, shadow->tsc_to_nsec_mul, shadow->tsc_shift);
+}
+
+/*
+ * Reads a consistent set of time-base values from hypervisor,
+ * into a shadow data area.
+ */
+static unsigned pvclock_get_time_values(struct pvclock_shadow_time *dst,
+                                       struct pvclock_vcpu_time_info *src)
+{
+       do {
+               dst->version = src->version;
+               rmb();          /* fetch version before data */
+               dst->tsc_timestamp     = src->tsc_timestamp;
+               dst->system_timestamp  = src->system_time;
+               dst->tsc_to_nsec_mul   = src->tsc_to_system_mul;
+               dst->tsc_shift         = src->tsc_shift;
+               dst->flags             = src->flags;
+               rmb();          /* test version after fetching data */
+       } while ((src->version & 1) || (dst->version != src->version));
+
+       return dst->version;
+}
+
+cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src)
+{
+       struct pvclock_shadow_time shadow;
+       unsigned version;
+       cycle_t ret, offset;
+       u64 last;
+
+       do {
+               version = pvclock_get_time_values(&shadow, src);
+               mb();
+               offset = pvclock_get_nsec_offset(&shadow);
+               ret = shadow.system_timestamp + offset;
+               mb();
+       } while (version != src->version);
+
+       if ((valid_flags & PVCLOCK_RAW_CYCLE_BIT) ||
+            ((valid_flags & PVCLOCK_TSC_STABLE_BIT) &&
+             (shadow.flags & PVCLOCK_TSC_STABLE_BIT)))
+                return ret;
+
+       /*
+        * Assumption here is that last_value, a global accumulator, always goes
+        * forward. If we are less than that, we should not be much smaller.
+        * We assume there is an error marging we're inside, and then the 
correction
+        * does not sacrifice accuracy.
+        *
+        * For reads: global may have changed between test and return,
+        * but this means someone else updated poked the clock at a later time.
+        * We just need to make sure we are not seeing a backwards event.
+        *
+        * For updates: last_value = ret is not enough, since two vcpus could be
+        * updating at the same time, and one of them could be slightly behind,
+        * making the assumption that last_value always go forward fail to hold.
+        */
+       last = atomic64_read(&last_value);
+       do {
+               if (ret < last)
+                       return last;
+               last = atomic64_cmpxchg(&last_value, last, ret);
+       } while (unlikely(last != ret));
+
+       return ret;
+}
+
+cycle_t kvm_clock_read()
+{
+        struct pvclock_vcpu_time_info *src;
+        cycle_t ret;
+        int index = smp_id();
+
+        src = &hv_clock[index];
+        ret = pvclock_clocksource_read(src);
+        return ret;
+}
+
+void kvm_clock_init(void *data)
+{
+        int index = smp_id();
+        struct pvclock_vcpu_time_info *hvc = &hv_clock[index];
+
+        printf("kvm-clock: cpu %d, msr 0x:%lx \n", index, hvc);
+        wrmsr(MSR_KVM_SYSTEM_TIME, (unsigned long)hvc | 1);
+}
+
+void kvm_clock_clear(void *data)
+{
+        wrmsr(MSR_KVM_SYSTEM_TIME, 0LL);
+}
+
+void pvclock_read_wallclock(struct pvclock_wall_clock *wall_clock,
+                           struct pvclock_vcpu_time_info *vcpu_time,
+                           struct timespec *ts)
+{
+       u32 version;
+       u64 delta;
+       struct timespec now;
+
+       /* get wallclock at system boot */
+       do {
+               version = wall_clock->version;
+               rmb();          /* fetch version before time */
+               now.tv_sec  = wall_clock->sec;
+               now.tv_nsec = wall_clock->nsec;
+               rmb();          /* fetch time before checking version */
+       } while ((wall_clock->version & 1) || (version != wall_clock->version));
+
+       delta = pvclock_clocksource_read(vcpu_time);    /* time since system 
boot */
+       delta += now.tv_sec * (u64)NSEC_PER_SEC + now.tv_nsec;
+
+       now.tv_nsec = do_div(delta, NSEC_PER_SEC);
+       now.tv_sec = delta;
+
+       set_normalized_timespec(ts, now.tv_sec, now.tv_nsec);
+}
+
+void kvm_get_wallclock(struct timespec *ts)
+{
+        struct pvclock_vcpu_time_info *vcpu_time;
+        int index = smp_id();
+
+        wrmsr(MSR_KVM_WALL_CLOCK, (unsigned long)&wall_clock);
+        vcpu_time = &hv_clock[index];
+        pvclock_read_wallclock(&wall_clock, vcpu_time, ts);
+}
+
+void pvclock_set_flags(unsigned char flags)
+{
+        valid_flags = flags;
+}
diff --git a/kvm-unittest/x86/kvmclock_test.c b/kvm-unittest/x86/kvmclock_test.c
new file mode 100644
index 0000000..52a43fb
--- /dev/null
+++ b/kvm-unittest/x86/kvmclock_test.c
@@ -0,0 +1,167 @@
+#include "libcflat.h"
+#include "smp.h"
+#include "atomic.h"
+#include "processor.h"
+#include "kvmclock.h"
+
+#define DEFAULT_TEST_LOOPS 100000000L
+#define DEFAULT_THRESHOLD  5L
+
+struct test_info {
+        struct spinlock lock;
+        long loops;               /* test loops */
+        u64 warps;                /* warp count */
+        u64 stalls;               /* stall count */
+        long long worst;          /* worst warp */
+        volatile cycle_t last;    /* last cycle seen by test */
+        atomic_t ncpus;           /* number of cpu in the test*/
+        int check;                /* check cycle ? */
+};
+
+struct test_info ti[4];
+
+static int wallclock_test(long sec, long threshold)
+{
+        long ksec, offset;
+        struct timespec ts;
+
+        printf("Wallclock test, threshold %ld\n", threshold);
+        kvm_get_wallclock(&ts);
+        ksec = ts.tv_sec;
+
+        offset = ksec - sec;
+        printf("Seconds get from host:     %ld\n", sec);
+        printf("Seconds get from kvmclock: %ld\n", ksec);
+        printf("Offset:                    %ld\n", offset);
+
+        if (offset > threshold || offset < -threshold) {
+                printf("offset too large!\n");
+                return 1;
+        }
+
+        return 0;
+}
+
+static void kvm_clock_test(void *data)
+{
+        struct test_info *hv_test_info = (struct test_info *)data;
+        long i, check = hv_test_info->check;
+
+        for (i = 0; i < hv_test_info->loops; i++){
+                cycle_t t0, t1;
+                long long delta;
+
+                if (check == 0) {
+                        kvm_clock_read();
+                        continue;
+                }
+
+                spin_lock(&hv_test_info->lock);
+                t1 = kvm_clock_read();
+                t0 = hv_test_info->last;
+                hv_test_info->last = kvm_clock_read();
+                spin_unlock(&hv_test_info->lock);
+
+                delta = t1 - t0;
+                if (delta < 0) {
+                        spin_lock(&hv_test_info->lock);
+                        ++hv_test_info->warps;
+                        if (delta < hv_test_info->worst){
+                                hv_test_info->worst = delta;
+                                printf("Worst warp %lld %\n", 
hv_test_info->worst);
+                        }
+                        spin_unlock(&hv_test_info->lock);
+                }
+                if (delta == 0)
+                        ++hv_test_info->stalls;
+
+                if (!((unsigned long)i & 31))
+                        asm volatile("rep; nop");
+        }
+
+        atomic_dec(&hv_test_info->ncpus);
+}
+
+static int cycle_test(int ncpus, long loops, int check, struct test_info *ti)
+{
+        int i;
+        unsigned long long begin, end;
+
+        begin = rdtsc();
+
+        atomic_set(&ti->ncpus, ncpus);
+        ti->loops = loops;
+        ti->check = check;
+        for (i = ncpus - 1; i >= 0; i--)
+                on_cpu_async(i, kvm_clock_test, (void *)ti);
+
+        /* Wait for the end of other vcpu */
+        while(atomic_read(&ti->ncpus))
+                ;
+
+        end = rdtsc();
+
+        printf("Total vcpus: %d\n", ncpus);
+        printf("Test  loops: %ld\n", ti->loops);
+        if (check == 1) {
+                printf("Total warps:  %lld\n", ti->warps);
+                printf("Total stalls: %lld\n", ti->stalls);
+                printf("Worst warp:   %lld\n", ti->worst);
+        } else
+                printf("TSC cycles:  %lld\n", end - begin);
+
+        return ti->warps ? 1 : 0;
+}
+
+int main(int ac, char **av)
+{
+        int ncpus;
+        int nerr = 0, i;
+        long loops = DEFAULT_TEST_LOOPS;
+        long sec = 0;
+        long threshold = DEFAULT_THRESHOLD;
+
+        if (ac > 1)
+                loops = atol(av[1]);
+        if (ac > 2)
+                sec = atol(av[2]);
+        if (ac > 3)
+                threshold = atol(av[3]);
+
+        smp_init();
+
+        ncpus = cpu_count();
+        if (ncpus > MAX_CPU)
+                ncpus = MAX_CPU;
+        for (i = 0; i < ncpus; ++i)
+                on_cpu(i, kvm_clock_init, (void *)0);
+
+        if (ac > 2)
+                nerr += wallclock_test(sec, threshold);
+
+        printf("Check the stability of raw cycle ...\n");
+        pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT
+                          | PVCLOCK_RAW_CYCLE_BIT);
+        if (cycle_test(ncpus, loops, 1, &ti[0]))
+                printf("Raw cycle is not stable\n");
+        else
+                printf("Raw cycle is stable\n");
+
+        pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT);
+        printf("Monotonic cycle test:\n");
+        nerr += cycle_test(ncpus, loops, 1, &ti[1]);
+
+        printf("Measure the performance of raw cycle ...\n");
+        pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT
+                          | PVCLOCK_RAW_CYCLE_BIT);
+        cycle_test(ncpus, loops, 0, &ti[2]);
+
+        printf("Measure the performance of adjusted cycle ...\n");
+        pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT);
+        cycle_test(ncpus, loops, 0, &ti[3]);
+
+        for (i = 0; i < ncpus; ++i)
+                on_cpu(i, kvm_clock_clear, (void *)0);
+
+        return nerr > 0 ? 1 : 0;
+}
diff --git a/kvm-unittest/x86/msr.c b/kvm-unittest/x86/msr.c
new file mode 100644
index 0000000..de7573d
--- /dev/null
+++ b/kvm-unittest/x86/msr.c
@@ -0,0 +1,149 @@
+/* msr tests */
+
+#include "libcflat.h"
+#include "processor.h"
+#include "msr.h"
+
+struct msr_info {
+    int index;
+    char *name;
+    struct tc {
+        int valid;
+        unsigned long long value;
+        unsigned long long expected;
+    } val_pairs[20];
+};
+
+
+#define addr_64 0x0000123456789abcULL
+
+struct msr_info msr_info[] =
+{
+    { .index = 0x0000001b, .name = "MSR_IA32_APICBASE",
+      .val_pairs = {
+            { .valid = 1, .value = 0x0000000056789900, .expected = 
0x0000000056789900},
+            { .valid = 1, .value = 0x0000000056789D01, .expected = 
0x0000000056789D01},
+        }
+    },
+    { .index = 0x00000174, .name = "IA32_SYSENTER_CS",
+      .val_pairs = {{ .valid = 1, .value = 0x1234, .expected = 0x1234}}
+    },
+    { .index = 0x00000175, .name = "MSR_IA32_SYSENTER_ESP",
+      .val_pairs = {{ .valid = 1, .value = addr_64, .expected = addr_64}}
+    },
+    { .index = 0x00000176, .name = "IA32_SYSENTER_EIP",
+      .val_pairs = {{ .valid = 1, .value = addr_64, .expected = addr_64}}
+    },
+    { .index = 0x000001a0, .name = "MSR_IA32_MISC_ENABLE",
+      // reserved: 1:2, 4:6, 8:10, 13:15, 17, 19:21, 24:33, 35:63
+      .val_pairs = {{ .valid = 1, .value = 0x400c51889, .expected = 
0x400c51889}}
+    },
+    { .index = 0x00000277, .name = "MSR_IA32_CR_PAT",
+      .val_pairs = {{ .valid = 1, .value = 0x07070707, .expected = 0x07070707}}
+    },
+    { .index = 0xc0000100, .name = "MSR_FS_BASE",
+      .val_pairs = {{ .valid = 1, .value = addr_64, .expected = addr_64}}
+    },
+    { .index = 0xc0000101, .name = "MSR_GS_BASE",
+      .val_pairs = {{ .valid = 1, .value = addr_64, .expected = addr_64}}
+    },
+    { .index = 0xc0000102, .name = "MSR_KERNEL_GS_BASE",
+      .val_pairs = {{ .valid = 1, .value = addr_64, .expected = addr_64}}
+    },
+#ifdef __x86_64__
+    { .index = 0xc0000080, .name = "MSR_EFER",
+      .val_pairs = {{ .valid = 1, .value = 0xD00, .expected = 0xD00}}
+    },
+#endif
+    { .index = 0xc0000082, .name = "MSR_LSTAR",
+      .val_pairs = {{ .valid = 1, .value = addr_64, .expected = addr_64}}
+    },
+    { .index = 0xc0000083, .name = "MSR_CSTAR",
+      .val_pairs = {{ .valid = 1, .value = addr_64, .expected = addr_64}}
+    },
+    { .index = 0xc0000084, .name = "MSR_SYSCALL_MASK",
+      .val_pairs = {{ .valid = 1, .value = 0xffffffff, .expected = 0xffffffff}}
+    },
+
+//    MSR_IA32_DEBUGCTLMSR needs svm feature LBRV
+//    MSR_VM_HSAVE_PA only AMD host
+};
+
+static int find_msr_info(int msr_index)
+{
+    int i;
+    for (i = 0; i < sizeof(msr_info)/sizeof(msr_info[0]) ; i++) {
+        if (msr_info[i].index == msr_index) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+
+int nr_passed, nr_tests;
+
+static void report(const char *name, int passed)
+{
+       ++nr_tests;
+       if (passed)
+               ++nr_passed;
+       printf("%s: %s\n", name, passed ? "PASS" : "FAIL");
+}
+
+static void test_msr_rw(int msr_index, unsigned long long input, unsigned long 
long expected)
+{
+    unsigned long long r = 0;
+    int index;
+    char *sptr;
+    if ((index = find_msr_info(msr_index)) != -1) {
+        sptr = msr_info[index].name;
+    } else {
+        printf("couldn't find name for msr # 0x%x, skipping\n", msr_index);
+        return;
+    }
+    wrmsr(msr_index, input);
+    r = rdmsr(msr_index);
+    if (expected != r) {
+        printf("testing %s: output = 0x%x:0x%x expected = 0x%x:0x%x\n", sptr, 
r >> 32, r, expected >> 32, expected);
+    }
+    report(sptr, expected == r);
+}
+
+static void test_syscall_lazy_load(void)
+{
+#ifdef __x86_64__
+    extern void syscall_target();
+    u16 cs = read_cs(), ss = read_ss();
+    ulong tmp;
+
+    wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SCE);
+    wrmsr(MSR_LSTAR, (ulong)syscall_target);
+    wrmsr(MSR_STAR, (uint64_t)cs << 32);
+    asm volatile("pushf; syscall; syscall_target: popf" : "=c"(tmp) : : "r11");
+    write_ss(ss);
+    // will crash horribly if broken
+    report("MSR_*STAR eager loading", true);
+#endif
+}
+
+int main(int ac, char **av)
+{
+    int i, j;
+    for (i = 0 ; i < sizeof(msr_info) / sizeof(msr_info[0]); i++) {
+        for (j = 0; j < sizeof(msr_info[i].val_pairs) / 
sizeof(msr_info[i].val_pairs[0]); j++) {
+            if (msr_info[i].val_pairs[j].valid) {
+                test_msr_rw(msr_info[i].index, msr_info[i].val_pairs[j].value, 
msr_info[i].val_pairs[j].expected);
+            } else {
+                break;
+            }
+        }
+    }
+
+    test_syscall_lazy_load();
+
+    printf("%d tests, %d failures\n", nr_tests, nr_tests - nr_passed);
+
+    return nr_passed == nr_tests ? 0 : 1;
+}
+
diff --git a/kvm-unittest/x86/pcid.c b/kvm-unittest/x86/pcid.c
new file mode 100644
index 0000000..8bfeba2
--- /dev/null
+++ b/kvm-unittest/x86/pcid.c
@@ -0,0 +1,186 @@
+/* Basic PCID & INVPCID functionality test */
+
+#include "libcflat.h"
+#include "processor.h"
+#include "desc.h"
+
+int nr_passed, nr_tests;
+
+#define X86_FEATURE_PCID       (1 << 17)
+#define X86_FEATURE_INVPCID    (1 << 10)
+
+#define X86_CR0_PG             (1 << 31)
+#define X86_CR3_PCID_MASK      0x00000fff
+#define X86_CR4_PCIDE          (1 << 17)
+
+#define X86_IA32_EFER          0xc0000080
+#define X86_EFER_LMA           (1UL << 8)
+
+struct invpcid_desc {
+    unsigned long pcid : 12;
+    unsigned long rsv  : 52;
+    unsigned long addr : 64;
+};
+
+static void report(const char *name, int passed)
+{
+    ++nr_tests;
+    if (passed)
+        ++nr_passed;
+    printf("%s: %s\n", name, passed ? "PASS" : "FAIL");
+}
+
+int write_cr0_checking(unsigned long val)
+{
+    asm volatile(ASM_TRY("1f")
+                 "mov %0, %%cr0\n\t"
+                 "1:": : "r" (val));
+    return exception_vector();
+}
+
+int write_cr4_checking(unsigned long val)
+{
+    asm volatile(ASM_TRY("1f")
+                 "mov %0, %%cr4\n\t"
+                 "1:": : "r" (val));
+    return exception_vector();
+}
+
+int invpcid_checking(unsigned long type, void *desc)
+{
+    asm volatile (ASM_TRY("1f")
+                  ".byte 0x66,0x0f,0x38,0x82,0x18 \n\t" /* invpcid (%rax), 
%rbx */
+                  "1:" : : "a" (desc), "b" (type));
+    return exception_vector();
+}
+
+void test_cpuid_consistency(int pcid_enabled, int invpcid_enabled)
+{
+    int passed = !(!pcid_enabled && invpcid_enabled);
+    report("CPUID consistency", passed);
+}
+
+void test_pcid_enabled(void)
+{
+    int passed = 0;
+    ulong cr0 = read_cr0(), cr3 = read_cr3(), cr4 = read_cr4();
+
+    /* try setting CR4.PCIDE, no exception expected */
+    if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != 0)
+        goto report;
+
+    /* try clearing CR0.PG when CR4.PCIDE=1, #GP expected */
+    if (write_cr0_checking(cr0 | X86_CR0_PG) != GP_VECTOR)
+        goto report;
+
+    write_cr4(cr4);
+
+    /* try setting CR4.PCIDE when CR3[11:0] != 0 , #GP expected */
+    write_cr3(cr3 | 0x001);
+    if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != GP_VECTOR)
+        goto report;
+    write_cr3(cr3);
+
+    passed = 1;
+
+report:
+    report("Test on PCID when enabled", passed);
+}
+
+void test_pcid_disabled(void)
+{
+    int passed = 0;
+    ulong cr4 = read_cr4();
+
+    /* try setting CR4.PCIDE, #GP expected */
+    if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != GP_VECTOR)
+        goto report;
+
+    passed = 1;
+
+report:
+    report("Test on PCID when disabled", passed);
+}
+
+void test_invpcid_enabled(void)
+{
+    int passed = 0;
+    ulong cr4 = read_cr4();
+    struct invpcid_desc desc;
+    desc.rsv = 0;
+
+    /* try executing invpcid when CR4.PCIDE=0, desc.pcid=0 and type=1
+     * no exception expected
+     */
+    desc.pcid = 0;
+    if (invpcid_checking(1, &desc) != 0)
+        goto report;
+
+    /* try executing invpcid when CR4.PCIDE=0, desc.pcid=1 and type=1
+     * #GP expected
+     */
+    desc.pcid = 1;
+    if (invpcid_checking(1, &desc) != GP_VECTOR)
+        goto report;
+
+    if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != 0)
+        goto report;
+
+    /* try executing invpcid when CR4.PCIDE=1
+     * no exception expected
+     */
+    desc.pcid = 10;
+    if (invpcid_checking(2, &desc) != 0)
+        goto report;
+
+    passed = 1;
+
+report:
+    report("Test on INVPCID when enabled", passed);
+}
+
+void test_invpcid_disabled(void)
+{
+    int passed = 0;
+    struct invpcid_desc desc;
+
+    /* try executing invpcid, #UD expected */
+    if (invpcid_checking(2, &desc) != UD_VECTOR)
+        goto report;
+
+    passed = 1;
+
+report:
+    report("Test on INVPCID when disabled", passed);
+}
+
+int main(int ac, char **av)
+{
+    struct cpuid _cpuid;
+    int pcid_enabled = 0, invpcid_enabled = 0;
+
+    setup_idt();
+
+    _cpuid = cpuid(1);
+    if (_cpuid.c & X86_FEATURE_PCID)
+        pcid_enabled = 1;
+    _cpuid = cpuid_indexed(7, 0);
+    if (_cpuid.b & X86_FEATURE_INVPCID)
+        invpcid_enabled = 1;
+
+    test_cpuid_consistency(pcid_enabled, invpcid_enabled);
+
+    if (pcid_enabled)
+        test_pcid_enabled();
+    else
+        test_pcid_disabled();
+
+    if (invpcid_enabled)
+        test_invpcid_enabled();
+    else
+        test_invpcid_disabled();
+
+    printf("%d tests, %d failures\n", nr_tests, nr_tests - nr_passed);
+
+    return nr_passed == nr_tests ? 0 : 1;
+}
diff --git a/kvm-unittest/x86/pmu.c b/kvm-unittest/x86/pmu.c
new file mode 100644
index 0000000..42b5a59
--- /dev/null
+++ b/kvm-unittest/x86/pmu.c
@@ -0,0 +1,414 @@
+
+#include "x86/msr.h"
+#include "x86/processor.h"
+#include "x86/apic-defs.h"
+#include "x86/apic.h"
+#include "x86/desc.h"
+#include "x86/isr.h"
+#include "x86/vm.h"
+
+#include "libcflat.h"
+#include <stdint.h>
+
+#define FIXED_CNT_INDEX 32
+#define PC_VECTOR      32
+
+#define EVNSEL_EVENT_SHIFT     0
+#define EVNTSEL_UMASK_SHIFT    8
+#define EVNTSEL_USR_SHIFT      16
+#define EVNTSEL_OS_SHIFT       17
+#define EVNTSEL_EDGE_SHIFT     18
+#define EVNTSEL_PC_SHIFT       19
+#define EVNTSEL_INT_SHIFT      20
+#define EVNTSEL_EN_SHIF                22
+#define EVNTSEL_INV_SHIF       23
+#define EVNTSEL_CMASK_SHIFT    24
+
+#define EVNTSEL_EN     (1 << EVNTSEL_EN_SHIF)
+#define EVNTSEL_USR    (1 << EVNTSEL_USR_SHIFT)
+#define EVNTSEL_OS     (1 << EVNTSEL_OS_SHIFT)
+#define EVNTSEL_PC     (1 << EVNTSEL_PC_SHIFT)
+#define EVNTSEL_INT    (1 << EVNTSEL_INT_SHIFT)
+#define EVNTSEL_INV    (1 << EVNTSEL_INV_SHIF)
+
+#define N 1000000
+
+typedef struct {
+       uint32_t ctr;
+       uint32_t config;
+       uint64_t count;
+       int idx;
+} pmu_counter_t;
+
+union cpuid10_eax {
+       struct {
+               unsigned int version_id:8;
+               unsigned int num_counters:8;
+               unsigned int bit_width:8;
+               unsigned int mask_length:8;
+       } split;
+       unsigned int full;
+} eax;
+
+union cpuid10_ebx {
+       struct {
+               unsigned int no_unhalted_core_cycles:1;
+               unsigned int no_instructions_retired:1;
+               unsigned int no_unhalted_reference_cycles:1;
+               unsigned int no_llc_reference:1;
+               unsigned int no_llc_misses:1;
+               unsigned int no_branch_instruction_retired:1;
+               unsigned int no_branch_misses_retired:1;
+       } split;
+       unsigned int full;
+} ebx;
+
+union cpuid10_edx {
+       struct {
+               unsigned int num_counters_fixed:5;
+               unsigned int bit_width_fixed:8;
+               unsigned int reserved:19;
+       } split;
+       unsigned int full;
+} edx;
+
+struct pmu_event {
+       char *name;
+       uint32_t unit_sel;
+       int min;
+       int max;
+} gp_events[] = {
+       {"core cycles", 0x003c, 1*N, 50*N},
+       {"instructions", 0x00c0, 10*N, 10.2*N},
+       {"ref cycles", 0x013c, 0.1*N, 30*N},
+       {"llc refference", 0x4f2e, 1, 2*N},
+       {"llc misses", 0x412e, 1, 1*N},
+       {"branches", 0x00c4, 1*N, 1.1*N},
+       {"branch misses", 0x00c5, 0, 0.1*N},
+}, fixed_events[] = {
+       {"fixed 1", MSR_CORE_PERF_FIXED_CTR0, 10*N, 10.2*N},
+       {"fixed 2", MSR_CORE_PERF_FIXED_CTR0 + 1, 1*N, 30*N},
+       {"fixed 3", MSR_CORE_PERF_FIXED_CTR0 + 2, 0.1*N, 30*N}
+};
+
+static int num_counters;
+static int tests, failures;
+
+char *buf;
+
+static inline void loop()
+{
+       unsigned long tmp, tmp2, tmp3;
+
+       asm volatile("1: mov (%1), %2; add $64, %1; nop; nop; nop; nop; nop; 
nop; nop; loop 1b"
+                       : "=c"(tmp), "=r"(tmp2), "=r"(tmp3): "0"(N), "1"(buf));
+
+}
+
+volatile uint64_t irq_received;
+
+static void cnt_overflow(isr_regs_t *regs)
+{
+       irq_received++;
+       apic_write(APIC_EOI, 0);
+}
+
+static bool check_irq(void)
+{
+       int i;
+       irq_received = 0;
+       irq_enable();
+       for (i = 0; i < 100000 && !irq_received; i++)
+               asm volatile("pause");
+       irq_disable();
+       return irq_received;
+}
+
+static bool is_gp(pmu_counter_t *evt)
+{
+       return evt->ctr < MSR_CORE_PERF_FIXED_CTR0;
+}
+
+static int event_to_global_idx(pmu_counter_t *cnt)
+{
+       return cnt->ctr - (is_gp(cnt) ? MSR_IA32_PERFCTR0 :
+               (MSR_CORE_PERF_FIXED_CTR0 - FIXED_CNT_INDEX));
+}
+
+static struct pmu_event* get_counter_event(pmu_counter_t *cnt)
+{
+       if (is_gp(cnt)) {
+               int i;
+
+               for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++)
+                       if (gp_events[i].unit_sel == (cnt->config & 0xffff))
+                               return &gp_events[i];
+       } else
+               return &fixed_events[cnt->ctr - MSR_CORE_PERF_FIXED_CTR0];
+
+       return (void*)0;
+}
+
+static void global_enable(pmu_counter_t *cnt)
+{
+       cnt->idx = event_to_global_idx(cnt);
+
+       wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_CTRL) |
+                       (1ull << cnt->idx));
+}
+
+static void global_disable(pmu_counter_t *cnt)
+{
+       wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_CTRL) &
+                       ~(1ull << cnt->idx));
+}
+
+
+static void start_event(pmu_counter_t *evt)
+{
+    wrmsr(evt->ctr, evt->count);
+    if (is_gp(evt))
+           wrmsr(MSR_P6_EVNTSEL0 + event_to_global_idx(evt),
+                           evt->config | EVNTSEL_EN);
+    else {
+           uint32_t ctrl = rdmsr(MSR_CORE_PERF_FIXED_CTR_CTRL);
+           int shift = (evt->ctr - MSR_CORE_PERF_FIXED_CTR0) * 4;
+           uint32_t usrospmi = 0;
+
+           if (evt->config & EVNTSEL_OS)
+                   usrospmi |= (1 << 0);
+           if (evt->config & EVNTSEL_USR)
+                   usrospmi |= (1 << 1);
+           if (evt->config & EVNTSEL_INT)
+                   usrospmi |= (1 << 3); // PMI on overflow
+           ctrl = (ctrl & ~(0xf << shift)) | (usrospmi << shift);
+           wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, ctrl);
+    }
+    global_enable(evt);
+}
+
+static void stop_event(pmu_counter_t *evt)
+{
+       global_disable(evt);
+       if (is_gp(evt))
+               wrmsr(MSR_P6_EVNTSEL0 + event_to_global_idx(evt),
+                               evt->config & ~EVNTSEL_EN);
+       else {
+               uint32_t ctrl = rdmsr(MSR_CORE_PERF_FIXED_CTR_CTRL);
+               int shift = (evt->ctr - MSR_CORE_PERF_FIXED_CTR0) * 4;
+               wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, ctrl & ~(0xf << shift));
+       }
+       evt->count = rdmsr(evt->ctr);
+}
+
+static void measure(pmu_counter_t *evt, int count)
+{
+       int i;
+       for (i = 0; i < count; i++)
+               start_event(&evt[i]);
+       loop();
+       for (i = 0; i < count; i++)
+               stop_event(&evt[i]);
+}
+
+static void report(const char *name, int n, bool pass)
+{
+    printf("%s: pmu %s-%d\n", pass ? "PASS" : "FAIL", name, n);
+    tests += 1;
+    failures += !pass;
+}
+
+static bool verify_event(uint64_t count, struct pmu_event *e)
+{
+       // printf("%lld >= %lld <= %lld\n", e->min, count, e->max);
+       return count >= e->min  && count <= e->max;
+
+}
+
+static bool verify_counter(pmu_counter_t *cnt)
+{
+       return verify_event(cnt->count, get_counter_event(cnt));
+}
+
+static void check_gp_counter(struct pmu_event *evt)
+{
+       pmu_counter_t cnt = {
+               .ctr = MSR_IA32_PERFCTR0,
+               .config = EVNTSEL_OS | EVNTSEL_USR | evt->unit_sel,
+       };
+       int i;
+
+       for (i = 0; i < num_counters; i++, cnt.ctr++) {
+               cnt.count = 0;
+               measure(&cnt, 1);
+               report(evt->name, i, verify_event(cnt.count, evt));
+       }
+}
+
+static void check_gp_counters(void)
+{
+       int i;
+
+       for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++)
+               if (!(ebx.full & (1 << i)))
+                       check_gp_counter(&gp_events[i]);
+               else
+                       printf("GP event '%s' is disabled\n",
+                                       gp_events[i].name);
+}
+
+static void check_fixed_counters(void)
+{
+       pmu_counter_t cnt = {
+               .config = EVNTSEL_OS | EVNTSEL_USR,
+       };
+       int i;
+
+       for (i = 0; i < edx.split.num_counters_fixed; i++) {
+               cnt.count = 0;
+               cnt.ctr = fixed_events[i].unit_sel;
+               measure(&cnt, 1);
+               report("fixed", i, verify_event(cnt.count, &fixed_events[i]));
+       }
+}
+
+static void check_counters_many(void)
+{
+       pmu_counter_t cnt[10];
+       int i, n;
+
+       for (i = 0, n = 0; n < num_counters; i++) {
+               if (ebx.full & (1 << i))
+                       continue;
+
+               cnt[n].count = 0;
+               cnt[n].ctr = MSR_IA32_PERFCTR0 + n;
+               cnt[n].config = EVNTSEL_OS | EVNTSEL_USR | 
gp_events[i].unit_sel;
+               n++;
+       }
+       for (i = 0; i < edx.split.num_counters_fixed; i++) {
+               cnt[n].count = 0;
+               cnt[n].ctr = fixed_events[i].unit_sel;
+               cnt[n].config = EVNTSEL_OS | EVNTSEL_USR;
+               n++;
+       }
+
+       measure(cnt, n);
+
+       for (i = 0; i < n; i++)
+               if (!verify_counter(&cnt[i]))
+                       break;
+
+       report("all counters", 0, i == n);
+}
+
+static void check_counter_overflow(void)
+{
+       uint64_t count;
+       int i;
+       pmu_counter_t cnt = {
+               .ctr = MSR_IA32_PERFCTR0,
+               .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* 
instructions */,
+               .count = 0,
+       };
+       measure(&cnt, 1);
+       count = cnt.count;
+
+       /* clear status before test */
+       wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, 
rdmsr(MSR_CORE_PERF_GLOBAL_STATUS));
+
+       for (i = 0; i < num_counters + 1; i++, cnt.ctr++) {
+               uint64_t status;
+               int idx;
+               if (i == num_counters)
+                       cnt.ctr = fixed_events[0].unit_sel;
+               if (i % 2)
+                       cnt.config |= EVNTSEL_INT;
+               else
+                       cnt.config &= ~EVNTSEL_INT;
+               idx = event_to_global_idx(&cnt);
+               cnt.count = 1 - count;
+               measure(&cnt, 1);
+               report("overflow", i, cnt.count == 1);
+               status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS);
+               report("overflow status", i, status & (1ull << idx));
+               wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, status);
+               status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS);
+               report("overflow status clear", i, !(status & (1ull << idx)));
+               report("overflow irq", i, check_irq() == (i % 2));
+       }
+}
+
+static void check_gp_counter_cmask(void)
+{
+       pmu_counter_t cnt = {
+               .ctr = MSR_IA32_PERFCTR0,
+               .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* 
instructions */,
+               .count = 0,
+       };
+       cnt.config |= (0x2 << EVNTSEL_CMASK_SHIFT);
+       measure(&cnt, 1);
+       report("cmask", 0, cnt.count < gp_events[1].min);
+}
+
+static void check_rdpmc(void)
+{
+       uint64_t val = 0x1f3456789ull;
+       int i;
+
+       for (i = 0; i < num_counters; i++) {
+               uint64_t x = (val & 0xffffffff) |
+                       ((1ull << (eax.split.bit_width - 32)) - 1) << 32;
+               wrmsr(MSR_IA32_PERFCTR0 + i, val);
+               report("rdpmc", i, rdpmc(i) == x);
+               report("rdpmc fast", i, rdpmc(i | (1<<31)) == (u32)val);
+       }
+       for (i = 0; i < edx.split.num_counters_fixed; i++) {
+               uint64_t x = (val & 0xffffffff) |
+                       ((1ull << (edx.split.bit_width_fixed - 32)) - 1) << 32;
+               wrmsr(MSR_CORE_PERF_FIXED_CTR0 + i, val);
+               report("rdpmc fixed", i, rdpmc(i | (1 << 30)) == x);
+               report("rdpmc fixed fast", i, rdpmc(i | (3<<30)) == (u32)val);
+       }
+}
+
+int main(int ac, char **av)
+{
+       struct cpuid id = cpuid(10);
+
+       setup_vm();
+       setup_idt();
+       handle_irq(PC_VECTOR, cnt_overflow);
+       buf = vmalloc(N*64);
+
+       eax.full = id.a;
+       ebx.full = id.b;
+       edx.full = id.d;
+
+       if (!eax.split.version_id) {
+               printf("No pmu is detected!\n");
+               return 1;
+       }
+       printf("PMU version:         %d\n", eax.split.version_id);
+       printf("GP counters:         %d\n", eax.split.num_counters);
+       printf("GP counter width:    %d\n", eax.split.bit_width);
+       printf("Mask length:         %d\n", eax.split.mask_length);
+       printf("Fixed counters:      %d\n", edx.split.num_counters_fixed);
+       printf("Fixed counter width: %d\n", edx.split.bit_width_fixed);
+
+       num_counters = eax.split.num_counters;
+       if (num_counters > ARRAY_SIZE(gp_events))
+               num_counters = ARRAY_SIZE(gp_events);
+
+       apic_write(APIC_LVTPC, PC_VECTOR);
+
+       check_gp_counters();
+       check_fixed_counters();
+       check_rdpmc();
+       check_counters_many();
+       check_counter_overflow();
+       check_gp_counter_cmask();
+
+       printf("\n%d tests, %d failures\n", tests, failures);
+       return !failures ? 0 : 1;
+}
diff --git a/kvm-unittest/x86/port80.c b/kvm-unittest/x86/port80.c
new file mode 100644
index 0000000..522c1a4
--- /dev/null
+++ b/kvm-unittest/x86/port80.c
@@ -0,0 +1,12 @@
+#include "libcflat.h"
+
+int main()
+{
+    int i;
+
+    printf("begining port 0x80 write test\n");
+    for (i = 0; i < 10000000; ++i)
+       asm volatile("outb %al, $0x80");
+    printf("done\n");
+    return 0;
+}
diff --git a/kvm-unittest/x86/realmode.c b/kvm-unittest/x86/realmode.c
new file mode 100644
index 0000000..c57e033
--- /dev/null
+++ b/kvm-unittest/x86/realmode.c
@@ -0,0 +1,1627 @@
+#ifndef USE_SERIAL
+#define USE_SERIAL
+#endif
+
+asm(".code16gcc");
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned u32;
+typedef unsigned long long u64;
+
+void test_function(void);
+
+asm(
+       "test_function: \n\t"
+       "mov $0x1234, %eax \n\t"
+       "ret"
+   );
+
+static int strlen(const char *str)
+{
+       int n;
+
+       for (n = 0; *str; ++str)
+               ++n;
+       return n;
+}
+
+static void outb(u8 data, u16 port)
+{
+       asm volatile("out %0, %1" : : "a"(data), "d"(port));
+}
+
+#ifdef USE_SERIAL
+static int serial_iobase = 0x3f8;
+static int serial_inited = 0;
+
+static u8 inb(u16 port)
+{
+       u8 data;
+       asm volatile("in %1, %0" : "=a"(data) : "d"(port));
+       return data;
+}
+
+static void serial_outb(char ch)
+{
+       u8 lsr;
+
+       do {
+               lsr = inb(serial_iobase + 0x05);
+       } while (!(lsr & 0x20));
+
+       outb(ch, serial_iobase + 0x00);
+}
+
+static void serial_init(void)
+{
+       u8 lcr;
+
+       /* set DLAB */
+       lcr = inb(serial_iobase + 0x03);
+       lcr |= 0x80;
+       outb(lcr, serial_iobase + 0x03);
+
+       /* set baud rate to 115200 */
+       outb(0x01, serial_iobase + 0x00);
+       outb(0x00, serial_iobase + 0x01);
+
+       /* clear DLAB */
+       lcr = inb(serial_iobase + 0x03);
+       lcr &= ~0x80;
+       outb(lcr, serial_iobase + 0x03);
+}
+#endif
+
+static void print_serial(const char *buf)
+{
+       unsigned long len = strlen(buf);
+#ifdef USE_SERIAL
+       unsigned long i;
+       if (!serial_inited) {
+           serial_init();
+           serial_inited = 1;
+       }
+
+       for (i = 0; i < len; i++) {
+           serial_outb(buf[i]);
+       }
+#else
+       asm volatile ("addr32/rep/outsb" : "+S"(buf), "+c"(len) : "d"(0xf1));
+#endif
+}
+
+static void exit(int code)
+{
+       outb(code, 0xf4);
+}
+
+struct regs {
+       u32 eax, ebx, ecx, edx;
+       u32 esi, edi, esp, ebp;
+       u32 eip, eflags;
+};
+
+static u64 gdt[] = {
+       0,
+       0x00cf9b000000ffffull, // flat 32-bit code segment
+       0x00cf93000000ffffull, // flat 32-bit data segment
+};
+
+static struct {
+       u16 limit;
+       void *base;
+} __attribute__((packed)) gdt_descr = {
+       sizeof(gdt) - 1,
+       gdt,
+};
+
+struct insn_desc {
+    u16 ptr;
+    u16 len;
+};
+
+static struct regs inregs, outregs;
+
+static void exec_in_big_real_mode(struct insn_desc *insn)
+{
+       unsigned long tmp;
+       static struct regs save;
+       int i;
+       extern u8 test_insn[], test_insn_end[];
+
+       for (i = 0; i < insn->len; ++i)
+           test_insn[i] = ((u8 *)(unsigned long)insn->ptr)[i];
+       for (; i < test_insn_end - test_insn; ++i)
+               test_insn[i] = 0x90; // nop
+
+       save = inregs;
+       asm volatile(
+               "lgdtl %[gdt_descr] \n\t"
+               "mov %%cr0, %[tmp] \n\t"
+               "or $1, %[tmp] \n\t"
+               "mov %[tmp], %%cr0 \n\t"
+               "mov %[bigseg], %%gs \n\t"
+               "and $-2, %[tmp] \n\t"
+               "mov %[tmp], %%cr0 \n\t"
+
+                "pushw %[save]+36; popfw \n\t"
+               "xchg %%eax, %[save]+0 \n\t"
+               "xchg %%ebx, %[save]+4 \n\t"
+               "xchg %%ecx, %[save]+8 \n\t"
+               "xchg %%edx, %[save]+12 \n\t"
+               "xchg %%esi, %[save]+16 \n\t"
+               "xchg %%edi, %[save]+20 \n\t"
+               "xchg %%esp, %[save]+24 \n\t"
+               "xchg %%ebp, %[save]+28 \n\t"
+
+               "test_insn: . = . + 32\n\t"
+               "test_insn_end: \n\t"
+
+               "xchg %%eax, %[save]+0 \n\t"
+               "xchg %%ebx, %[save]+4 \n\t"
+               "xchg %%ecx, %[save]+8 \n\t"
+               "xchg %%edx, %[save]+12 \n\t"
+               "xchg %%esi, %[save]+16 \n\t"
+               "xchg %%edi, %[save]+20 \n\t"
+               "xchg %%esp, %[save]+24 \n\t"
+               "xchg %%ebp, %[save]+28 \n\t"
+
+               /* Save EFLAGS in outregs*/
+               "pushfl \n\t"
+               "popl %[save]+36 \n\t"
+
+               /* Restore DF for the harness code */
+               "cld\n\t"
+               "xor %[tmp], %[tmp] \n\t"
+               "mov %[tmp], %%gs \n\t"
+               : [tmp]"=&r"(tmp), [save]"+m"(save)
+               : [gdt_descr]"m"(gdt_descr), [bigseg]"r"((short)16)
+               : "cc", "memory"
+               );
+       outregs = save;
+}
+
+#define R_AX 1
+#define R_BX 2
+#define R_CX 4
+#define R_DX 8
+#define R_SI 16
+#define R_DI 32
+#define R_SP 64
+#define R_BP 128
+
+int regs_equal(int ignore)
+{
+       const u32 *p1 = &inregs.eax, *p2 = &outregs.eax;  // yuck
+       int i;
+
+       for (i = 0; i < 8; ++i)
+               if (!(ignore & (1 << i)) && p1[i] != p2[i])
+                       return 0;
+       return 1;
+}
+
+static void report(const char *name, u16 regs_ignore, _Bool ok)
+{
+    if (!regs_equal(regs_ignore)) {
+       ok = 0;
+    }
+    print_serial(ok ? "PASS: " : "FAIL: ");
+    print_serial(name);
+    print_serial("\n");
+}
+
+#define MK_INSN(name, str)                             \
+    asm (                                              \
+        ".pushsection .data.insn  \n\t"                \
+        "insn_" #name ": \n\t"                         \
+        ".word 1001f, 1002f - 1001f \n\t"              \
+        ".popsection \n\t"                             \
+        ".pushsection .text.insn, \"ax\" \n\t"         \
+        "1001: \n\t"                                   \
+        "insn_code_" #name ": " str " \n\t"            \
+        "1002: \n\t"                                   \
+        ".popsection"                                  \
+    );                                                 \
+    extern struct insn_desc insn_##name;
+
+void test_xchg(void)
+{
+       MK_INSN(xchg_test1, "xchg %eax,%eax\n\t");
+       MK_INSN(xchg_test2, "xchg %eax,%ebx\n\t");
+       MK_INSN(xchg_test3, "xchg %eax,%ecx\n\t");
+       MK_INSN(xchg_test4, "xchg %eax,%edx\n\t");
+       MK_INSN(xchg_test5, "xchg %eax,%esi\n\t");
+       MK_INSN(xchg_test6, "xchg %eax,%edi\n\t");
+       MK_INSN(xchg_test7, "xchg %eax,%ebp\n\t");
+       MK_INSN(xchg_test8, "xchg %eax,%esp\n\t");
+
+       inregs = (struct regs){ .eax = 0, .ebx = 1, .ecx = 2, .edx = 3, .esi = 
4, .edi = 5, .ebp = 6, .esp = 7};
+
+       exec_in_big_real_mode(&insn_xchg_test1);
+       report("xchg 1", 0, 1);
+
+       exec_in_big_real_mode(&insn_xchg_test2);
+       report("xchg 2", R_AX | R_BX,
+              outregs.eax == inregs.ebx && outregs.ebx == inregs.eax);
+
+       exec_in_big_real_mode(&insn_xchg_test3);
+       report("xchg 3", R_AX | R_CX,
+              outregs.eax == inregs.ecx && outregs.ecx == inregs.eax);
+
+       exec_in_big_real_mode(&insn_xchg_test4);
+       report("xchg 4", R_AX | R_DX,
+              outregs.eax == inregs.edx && outregs.edx == inregs.eax);
+
+       exec_in_big_real_mode(&insn_xchg_test5);
+       report("xchg 5", R_AX | R_SI,
+              outregs.eax == inregs.esi && outregs.esi == inregs.eax);
+
+       exec_in_big_real_mode(&insn_xchg_test6);
+       report("xchg 6", R_AX | R_DI,
+              outregs.eax == inregs.edi && outregs.edi == inregs.eax);
+
+       exec_in_big_real_mode(&insn_xchg_test7);
+       report("xchg 7", R_AX | R_BP,
+              outregs.eax == inregs.ebp && outregs.ebp == inregs.eax);
+
+       exec_in_big_real_mode(&insn_xchg_test8);
+       report("xchg 8", R_AX | R_SP,
+              outregs.eax == inregs.esp && outregs.esp == inregs.eax);
+}
+
+void test_shld(void)
+{
+       MK_INSN(shld_test, "shld $8,%edx,%eax\n\t");
+
+       inregs = (struct regs){ .eax = 0xbe, .edx = 0xef000000 };
+       exec_in_big_real_mode(&insn_shld_test);
+       report("shld", ~0, outregs.eax == 0xbeef);
+}
+
+void test_mov_imm(void)
+{
+       MK_INSN(mov_r32_imm_1, "mov $1234567890, %eax");
+       MK_INSN(mov_r16_imm_1, "mov $1234, %ax");
+       MK_INSN(mov_r8_imm_1, "mov $0x12, %ah");
+       MK_INSN(mov_r8_imm_2, "mov $0x34, %al");
+       MK_INSN(mov_r8_imm_3, "mov $0x12, %ah\n\t" "mov $0x34, %al\n\t");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_mov_r16_imm_1);
+       report("mov 1", R_AX, outregs.eax == 1234);
+
+       /* test mov $imm, %eax */
+       exec_in_big_real_mode(&insn_mov_r32_imm_1);
+       report("mov 2", R_AX, outregs.eax == 1234567890);
+
+       /* test mov $imm, %al/%ah */
+       exec_in_big_real_mode(&insn_mov_r8_imm_1);
+       report("mov 3", R_AX, outregs.eax == 0x1200);
+
+       exec_in_big_real_mode(&insn_mov_r8_imm_2);
+       report("mov 4", R_AX, outregs.eax == 0x34);
+
+       exec_in_big_real_mode(&insn_mov_r8_imm_3);
+       report("mov 5", R_AX, outregs.eax == 0x1234);
+}
+
+void test_sub_imm(void)
+{
+       MK_INSN(sub_r32_imm_1, "mov $1234567890, %eax\n\t" "sub $10, %eax\n\t");
+       MK_INSN(sub_r16_imm_1, "mov $1234, %ax\n\t" "sub $10, %ax\n\t");
+       MK_INSN(sub_r8_imm_1, "mov $0x12, %ah\n\t" "sub $0x10, %ah\n\t");
+       MK_INSN(sub_r8_imm_2, "mov $0x34, %al\n\t" "sub $0x10, %al\n\t");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_sub_r16_imm_1);
+       report("sub 1", R_AX, outregs.eax == 1224);
+
+       /* test mov $imm, %eax */
+       exec_in_big_real_mode(&insn_sub_r32_imm_1);
+       report("sub 2", R_AX, outregs.eax == 1234567880);
+
+       /* test mov $imm, %al/%ah */
+       exec_in_big_real_mode(&insn_sub_r8_imm_1);
+       report("sub 3", R_AX, outregs.eax == 0x0200);
+
+       exec_in_big_real_mode(&insn_sub_r8_imm_2);
+       report("sub 4", R_AX, outregs.eax == 0x24);
+}
+
+void test_xor_imm(void)
+{
+       MK_INSN(xor_r32_imm_1, "mov $1234567890, %eax\n\t" "xor $1234567890, 
%eax\n\t");
+       MK_INSN(xor_r16_imm_1, "mov $1234, %ax\n\t" "xor $1234, %ax\n\t");
+       MK_INSN(xor_r8_imm_1, "mov $0x12, %ah\n\t" "xor $0x12, %ah\n\t");
+       MK_INSN(xor_r8_imm_2, "mov $0x34, %al\n\t" "xor $0x34, %al\n\t");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_xor_r16_imm_1);
+       report("xor 1", R_AX, outregs.eax == 0);
+
+       /* test mov $imm, %eax */
+       exec_in_big_real_mode(&insn_xor_r32_imm_1);
+       report("xor 2", R_AX, outregs.eax == 0);
+
+       /* test mov $imm, %al/%ah */
+       exec_in_big_real_mode(&insn_xor_r8_imm_1);
+       report("xor 3", R_AX, outregs.eax == 0);
+
+       exec_in_big_real_mode(&insn_xor_r8_imm_2);
+       report("xor 4", R_AX, outregs.eax == 0);
+}
+
+void test_cmp_imm(void)
+{
+       MK_INSN(cmp_test1, "mov $0x34, %al\n\t"
+                          "cmp $0x34, %al\n\t");
+       MK_INSN(cmp_test2, "mov $0x34, %al\n\t"
+                          "cmp $0x39, %al\n\t");
+       MK_INSN(cmp_test3, "mov $0x34, %al\n\t"
+                          "cmp $0x24, %al\n\t");
+
+       inregs = (struct regs){ 0 };
+
+       /* test cmp imm8 with AL */
+       /* ZF: (bit 6) Zero Flag becomes 1 if an operation results
+        * in a 0 writeback, or 0 register
+        */
+       exec_in_big_real_mode(&insn_cmp_test1);
+       report("cmp 1", ~0, (outregs.eflags & (1<<6)) == (1<<6));
+
+       exec_in_big_real_mode(&insn_cmp_test2);
+       report("cmp 2", ~0, (outregs.eflags & (1<<6)) == 0);
+
+       exec_in_big_real_mode(&insn_cmp_test3);
+       report("cmp 3", ~0, (outregs.eflags & (1<<6)) == 0);
+}
+
+void test_add_imm(void)
+{
+       MK_INSN(add_test1, "mov $0x43211234, %eax \n\t"
+                          "add $0x12344321, %eax \n\t");
+       MK_INSN(add_test2, "mov $0x12, %eax \n\t"
+                          "add $0x21, %al\n\t");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_add_test1);
+       report("add 1", ~0, outregs.eax == 0x55555555);
+
+       exec_in_big_real_mode(&insn_add_test2);
+       report("add 2", ~0, outregs.eax == 0x33);
+}
+
+void test_eflags_insn(void)
+{
+       MK_INSN(clc, "clc");
+       MK_INSN(stc, "stc");
+       MK_INSN(cli, "cli");
+       MK_INSN(sti, "sti");
+       MK_INSN(cld, "cld");
+       MK_INSN(std, "std");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_clc);
+       report("clc", ~0, (outregs.eflags & 1) == 0);
+
+       exec_in_big_real_mode(&insn_stc);
+       report("stc", ~0, (outregs.eflags & 1) == 1);
+
+       exec_in_big_real_mode(&insn_cli);
+       report("cli", ~0, !(outregs.eflags & (1 << 9)));
+
+       exec_in_big_real_mode(&insn_sti);
+       report("sti", ~0, outregs.eflags & (1 << 9));
+
+       exec_in_big_real_mode(&insn_cld);
+       report("cld", ~0, !(outregs.eflags & (1 << 10)));
+
+       exec_in_big_real_mode(&insn_std);
+       report("std", ~0, (outregs.eflags & (1 << 10)));
+}
+
+void test_io(void)
+{
+       MK_INSN(io_test1, "mov $0xff, %al \n\t"
+                         "out %al, $0xe0 \n\t"
+                         "mov $0x00, %al \n\t"
+                         "in $0xe0, %al \n\t");
+       MK_INSN(io_test2, "mov $0xffff, %ax \n\t"
+                         "out %ax, $0xe0 \n\t"
+                         "mov $0x0000, %ax \n\t"
+                         "in $0xe0, %ax \n\t");
+       MK_INSN(io_test3, "mov $0xffffffff, %eax \n\t"
+                         "out %eax, $0xe0 \n\t"
+                         "mov $0x000000, %eax \n\t"
+                         "in $0xe0, %eax \n\t");
+       MK_INSN(io_test4, "mov $0xe0, %dx \n\t"
+                         "mov $0xff, %al \n\t"
+                         "out %al, %dx \n\t"
+                         "mov $0x00, %al \n\t"
+                         "in %dx, %al \n\t");
+       MK_INSN(io_test5, "mov $0xe0, %dx \n\t"
+                         "mov $0xffff, %ax \n\t"
+                         "out %ax, %dx \n\t"
+                         "mov $0x0000, %ax \n\t"
+                         "in %dx, %ax \n\t");
+       MK_INSN(io_test6, "mov $0xe0, %dx \n\t"
+                         "mov $0xffffffff, %eax \n\t"
+                         "out %eax, %dx \n\t"
+                         "mov $0x00000000, %eax \n\t"
+                         "in %dx, %eax \n\t");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_io_test1);
+       report("pio 1", R_AX, outregs.eax == 0xff);
+
+       exec_in_big_real_mode(&insn_io_test2);
+       report("pio 2", R_AX, outregs.eax == 0xffff);
+
+       exec_in_big_real_mode(&insn_io_test3);
+       report("pio 3", R_AX, outregs.eax == 0xffffffff);
+
+       exec_in_big_real_mode(&insn_io_test4);
+       report("pio 4", R_AX|R_DX, outregs.eax == 0xff);
+
+       exec_in_big_real_mode(&insn_io_test5);
+       report("pio 5", R_AX|R_DX, outregs.eax == 0xffff);
+
+       exec_in_big_real_mode(&insn_io_test6);
+       report("pio 6", R_AX|R_DX, outregs.eax == 0xffffffff);
+}
+
+asm ("retf: lretw");
+extern void retf();
+
+asm ("retf_imm: lretw $10");
+extern void retf_imm();
+
+void test_call(void)
+{
+       u32 esp[16];
+       u32 addr;
+
+       inregs = (struct regs){ 0 };
+       inregs.esp = (u32)esp;
+
+       MK_INSN(call1, "mov $test_function, %eax \n\t"
+                      "call *%eax\n\t");
+       MK_INSN(call_near1, "jmp 2f\n\t"
+                           "1: mov $0x1234, %eax\n\t"
+                           "ret\n\t"
+                           "2: call 1b\t");
+       MK_INSN(call_near2, "call 1f\n\t"
+                           "jmp 2f\n\t"
+                           "1: mov $0x1234, %eax\n\t"
+                           "ret\n\t"
+                           "2:\t");
+       MK_INSN(call_far1,  "lcallw *(%ebx)\n\t");
+       MK_INSN(call_far2,  "lcallw $0, $retf\n\t");
+       MK_INSN(ret_imm,    "sub $10, %sp; jmp 2f; 1: retw $10; 2: callw 1b");
+       MK_INSN(retf_imm,   "sub $10, %sp; lcallw $0, $retf_imm");
+
+       exec_in_big_real_mode(&insn_call1);
+       report("call 1", R_AX, outregs.eax == 0x1234);
+
+       exec_in_big_real_mode(&insn_call_near1);
+       report("call near 1", R_AX, outregs.eax == 0x1234);
+
+       exec_in_big_real_mode(&insn_call_near2);
+       report("call near 2", R_AX, outregs.eax == 0x1234);
+
+       addr = (((unsigned)retf >> 4) << 16) | ((unsigned)retf & 0x0f);
+       inregs.ebx = (unsigned)&addr;
+       exec_in_big_real_mode(&insn_call_far1);
+       report("call far 1", 0, 1);
+
+       exec_in_big_real_mode(&insn_call_far2);
+       report("call far 2", 0, 1);
+
+       exec_in_big_real_mode(&insn_ret_imm);
+       report("ret imm 1", 0, 1);
+
+       exec_in_big_real_mode(&insn_retf_imm);
+       report("retf imm 1", 0, 1);
+}
+
+void test_jcc_short(void)
+{
+       MK_INSN(jnz_short1, "jnz 1f\n\t"
+                           "mov $0x1234, %eax\n\t"
+                           "1:\n\t");
+       MK_INSN(jnz_short2, "1:\n\t"
+                           "cmp $0x1234, %eax\n\t"
+                           "mov $0x1234, %eax\n\t"
+                           "jnz 1b\n\t");
+       MK_INSN(jmp_short1, "jmp 1f\n\t"
+                     "mov $0x1234, %eax\n\t"
+                     "1:\n\t");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_jnz_short1);
+       report("jnz short 1", ~0, 1);
+
+       exec_in_big_real_mode(&insn_jnz_short2);
+       report("jnz short 2", R_AX, (outregs.eflags & (1 << 6)));
+
+       exec_in_big_real_mode(&insn_jmp_short1);
+       report("jmp short 1", ~0, 1);
+}
+
+void test_jcc_near(void)
+{
+       /* encode near jmp manually. gas will not do it if offsets < 127 byte */
+       MK_INSN(jnz_near1, ".byte 0x0f, 0x85, 0x06, 0x00\n\t"
+                          "mov $0x1234, %eax\n\t");
+       MK_INSN(jnz_near2, "cmp $0x1234, %eax\n\t"
+                          "mov $0x1234, %eax\n\t"
+                          ".byte 0x0f, 0x85, 0xf0, 0xff\n\t");
+       MK_INSN(jmp_near1, ".byte 0xE9, 0x06, 0x00\n\t"
+                          "mov $0x1234, %eax\n\t");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_jnz_near1);
+       report("jnz near 1", 0, 1);
+
+       exec_in_big_real_mode(&insn_jnz_near2);
+       report("jnz near 2", R_AX, outregs.eflags & (1 << 6));
+
+       exec_in_big_real_mode(&insn_jmp_near1);
+       report("jmp near 1", 0, 1);
+}
+
+void test_long_jmp()
+{
+       u32 esp[16];
+
+       inregs = (struct regs){ 0 };
+       inregs.esp = (u32)(esp+16);
+       MK_INSN(long_jmp, "call 1f\n\t"
+                         "jmp 2f\n\t"
+                         "1: jmp $0, $test_function\n\t"
+                         "2:\n\t");
+       exec_in_big_real_mode(&insn_long_jmp);
+       report("jmp far 1", R_AX, outregs.eax == 0x1234);
+}
+
+void test_push_pop()
+{
+       MK_INSN(push32, "mov $0x12345678, %eax\n\t"
+                       "push %eax\n\t"
+                       "pop %ebx\n\t");
+       MK_INSN(push16, "mov $0x1234, %ax\n\t"
+                       "push %ax\n\t"
+                       "pop %bx\n\t");
+
+       MK_INSN(push_es, "mov $0x231, %bx\n\t" //Just write a dummy value to 
see if it gets overwritten
+                        "mov $0x123, %ax\n\t"
+                        "mov %ax, %es\n\t"
+                        "push %es\n\t"
+                        "pop %bx \n\t"
+                        );
+       MK_INSN(pop_es, "push %ax\n\t"
+                       "pop %es\n\t"
+                       "mov %es, %bx\n\t"
+                       );
+       MK_INSN(push_pop_ss, "push %ss\n\t"
+                            "pushw %ax\n\t"
+                            "popw %ss\n\t"
+                            "mov %ss, %bx\n\t"
+                            "pop %ss\n\t"
+                       );
+       MK_INSN(push_pop_fs, "push %fs\n\t"
+                            "pushl %eax\n\t"
+                            "popl %fs\n\t"
+                            "mov %fs, %ebx\n\t"
+                            "pop %fs\n\t"
+                       );
+       MK_INSN(push_pop_high_esp_bits,
+               "xor $0x12340000, %esp \n\t"
+               "push %ax; \n\t"
+               "xor $0x12340000, %esp \n\t"
+               "pop %bx");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_push32);
+       report("push/pop 1", R_AX|R_BX,
+              outregs.eax == outregs.ebx && outregs.eax == 0x12345678);
+
+       exec_in_big_real_mode(&insn_push16);
+       report("push/pop 2", R_AX|R_BX,
+              outregs.eax == outregs.ebx && outregs.eax == 0x1234);
+
+       exec_in_big_real_mode(&insn_push_es);
+       report("push/pop 3", R_AX|R_BX,
+              outregs.ebx == outregs.eax && outregs.eax == 0x123);
+
+       exec_in_big_real_mode(&insn_pop_es);
+       report("push/pop 4", R_AX|R_BX, outregs.ebx == outregs.eax);
+
+       exec_in_big_real_mode(&insn_push_pop_ss);
+       report("push/pop 5", R_AX|R_BX, outregs.ebx == outregs.eax);
+
+       exec_in_big_real_mode(&insn_push_pop_fs);
+       report("push/pop 6", R_AX|R_BX, outregs.ebx == outregs.eax);
+
+       inregs.eax = 0x9977;
+       inregs.ebx = 0x7799;
+       exec_in_big_real_mode(&insn_push_pop_high_esp_bits);
+       report("push/pop with high bits set in %esp", R_BX, outregs.ebx == 
0x9977);
+}
+
+void test_null(void)
+{
+       MK_INSN(null, "");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_null);
+       report("null", 0, 1);
+}
+
+struct {
+    char stack[500];
+    char top[];
+} tmp_stack;
+
+void test_pusha_popa()
+{
+       MK_INSN(pusha, "pusha\n\t"
+                      "pop %edi\n\t"
+                      "pop %esi\n\t"
+                      "pop %ebp\n\t"
+                      "add $4, %esp\n\t"
+                      "pop %ebx\n\t"
+                      "pop %edx\n\t"
+                      "pop %ecx\n\t"
+                      "pop %eax\n\t"
+                      );
+
+       MK_INSN(popa, "push %eax\n\t"
+                     "push %ecx\n\t"
+                     "push %edx\n\t"
+                     "push %ebx\n\t"
+                     "push %esp\n\t"
+                     "push %ebp\n\t"
+                     "push %esi\n\t"
+                     "push %edi\n\t"
+                     "popa\n\t"
+                     );
+
+       inregs = (struct regs){ .eax = 0, .ebx = 1, .ecx = 2, .edx = 3, .esi = 
4, .edi = 5, .ebp = 6, .esp = (unsigned long)&tmp_stack.top };
+
+       exec_in_big_real_mode(&insn_pusha);
+       report("pusha/popa 1", 0, 1);
+
+       exec_in_big_real_mode(&insn_popa);
+       report("pusha/popa 1", 0, 1);
+}
+
+void test_iret()
+{
+       MK_INSN(iret32, "pushf\n\t"
+                       "pushl %cs\n\t"
+                       "call 1f\n\t" /* a near call will push eip onto the 
stack */
+                       "jmp 2f\n\t"
+                       "1: iret\n\t"
+                       "2:\n\t"
+                    );
+
+       MK_INSN(iret16, "pushfw\n\t"
+                       "pushw %cs\n\t"
+                       "callw 1f\n\t"
+                       "jmp 2f\n\t"
+                       "1: iretw\n\t"
+                       "2:\n\t");
+
+       MK_INSN(iret_flags32, "pushfl\n\t"
+                             "popl %eax\n\t"
+                             "andl $~0x2, %eax\n\t"
+                             "orl $0xffc08028, %eax\n\t"
+                             "pushl %eax\n\t"
+                             "pushl %cs\n\t"
+                             "call 1f\n\t"
+                             "jmp 2f\n\t"
+                             "1: iret\n\t"
+                             "2:\n\t");
+
+       MK_INSN(iret_flags16, "pushfw\n\t"
+                             "popw %ax\n\t"
+                             "and $~0x2, %ax\n\t"
+                             "or $0x8028, %ax\n\t"
+                             "pushw %ax\n\t"
+                             "pushw %cs\n\t"
+                             "callw 1f\n\t"
+                             "jmp 2f\n\t"
+                             "1: iretw\n\t"
+                             "2:\n\t");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_iret32);
+       report("iret 1", 0, 1);
+
+       exec_in_big_real_mode(&insn_iret16);
+       report("iret 2", 0, 1);
+
+       exec_in_big_real_mode(&insn_iret_flags32);
+       report("iret 3", R_AX, 1);
+
+       exec_in_big_real_mode(&insn_iret_flags16);
+       report("iret 4", R_AX, 1);
+}
+
+void test_int()
+{
+       inregs = (struct regs){ 0 };
+
+       *(u32 *)(0x11 * 4) = 0x1000; /* Store a pointer to address 0x1000 in 
IDT entry 0x11 */
+       *(u8 *)(0x1000) = 0xcf; /* 0x1000 contains an IRET instruction */
+
+       MK_INSN(int11, "int $0x11\n\t");
+
+       exec_in_big_real_mode(&insn_int11);
+       report("int 1", 0, 1);
+}
+
+void test_imul()
+{
+       MK_INSN(imul8_1, "mov $2, %al\n\t"
+                       "mov $-4, %cx\n\t"
+                       "imul %cl\n\t");
+
+       MK_INSN(imul16_1, "mov $2, %ax\n\t"
+                     "mov $-4, %cx\n\t"
+                     "imul %cx\n\t");
+
+       MK_INSN(imul32_1, "mov $2, %eax\n\t"
+                      "mov $-4, %ecx\n\t"
+                      "imul %ecx\n\t");
+
+       MK_INSN(imul8_2, "mov $0x12340002, %eax\n\t"
+                       "mov $4, %cx\n\t"
+                       "imul %cl\n\t");
+
+       MK_INSN(imul16_2, "mov $2, %ax\n\t"
+                       "mov $4, %cx\n\t"
+                       "imul %cx\n\t");
+
+       MK_INSN(imul32_2, "mov $2, %eax\n\t"
+                       "mov $4, %ecx\n\t"
+                       "imul %ecx\n\t");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_imul8_1);
+       report("imul 1", R_AX | R_CX | R_DX, (outregs.eax & 0xff) == (u8)-8);
+
+       exec_in_big_real_mode(&insn_imul16_1);
+       report("imul 2", R_AX | R_CX | R_DX, outregs.eax == (u16)-8);
+
+       exec_in_big_real_mode(&insn_imul32_1);
+       report("imul 3", R_AX | R_CX | R_DX, outregs.eax == (u32)-8);
+
+       exec_in_big_real_mode(&insn_imul8_2);
+       report("imul 4", R_AX | R_CX | R_DX,
+              (outregs.eax & 0xffff) == 8
+              && (outregs.eax & 0xffff0000) == 0x12340000);
+
+       exec_in_big_real_mode(&insn_imul16_2);
+       report("imul 5", R_AX | R_CX | R_DX, outregs.eax == 8);
+
+       exec_in_big_real_mode(&insn_imul32_2);
+       report("imul 6", R_AX | R_CX | R_DX, outregs.eax == 8);
+}
+
+void test_mul()
+{
+       MK_INSN(mul8, "mov $2, %al\n\t"
+                       "mov $4, %cx\n\t"
+                       "imul %cl\n\t");
+
+       MK_INSN(mul16, "mov $2, %ax\n\t"
+                       "mov $4, %cx\n\t"
+                       "imul %cx\n\t");
+
+       MK_INSN(mul32, "mov $2, %eax\n\t"
+                       "mov $4, %ecx\n\t"
+                       "imul %ecx\n\t");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_mul8);
+       report("mul 1", R_AX | R_CX | R_DX, (outregs.eax & 0xff) == 8);
+
+       exec_in_big_real_mode(&insn_mul16);
+       report("mul 2", R_AX | R_CX | R_DX, outregs.eax == 8);
+
+       exec_in_big_real_mode(&insn_mul32);
+       report("mul 3", R_AX | R_CX | R_DX, outregs.eax == 8);
+}
+
+void test_div()
+{
+       MK_INSN(div8, "mov $257, %ax\n\t"
+                       "mov $2, %cl\n\t"
+                       "div %cl\n\t");
+
+       MK_INSN(div16, "mov $512, %ax\n\t"
+                       "mov $5, %cx\n\t"
+                       "div %cx\n\t");
+
+       MK_INSN(div32, "mov $512, %eax\n\t"
+                       "mov $5, %ecx\n\t"
+                       "div %ecx\n\t");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_div8);
+       report("div 1", R_AX | R_CX | R_DX, outregs.eax == 384);
+
+       exec_in_big_real_mode(&insn_div16);
+       report("div 2", R_AX | R_CX | R_DX,
+              outregs.eax == 102 && outregs.edx == 2);
+
+       exec_in_big_real_mode(&insn_div32);
+       report("div 3", R_AX | R_CX | R_DX,
+              outregs.eax == 102 && outregs.edx == 2);
+}
+
+void test_idiv()
+{
+       MK_INSN(idiv8, "mov $256, %ax\n\t"
+                       "mov $-2, %cl\n\t"
+                       "idiv %cl\n\t");
+
+       MK_INSN(idiv16, "mov $512, %ax\n\t"
+                       "mov $-2, %cx\n\t"
+                       "idiv %cx\n\t");
+
+       MK_INSN(idiv32, "mov $512, %eax\n\t"
+                       "mov $-2, %ecx\n\t"
+                       "idiv %ecx\n\t");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_idiv8);
+       report("idiv 1", R_AX | R_CX | R_DX, outregs.eax == (u8)-128);
+
+       exec_in_big_real_mode(&insn_idiv16);
+       report("idiv 2", R_AX | R_CX | R_DX, outregs.eax == (u16)-256);
+
+       exec_in_big_real_mode(&insn_idiv32);
+       report("idiv 3", R_AX | R_CX | R_DX, outregs.eax == (u32)-256);
+}
+
+void test_cbw(void)
+{
+       MK_INSN(cbw, "mov $0xFE, %eax \n\t"
+                    "cbw\n\t");
+       MK_INSN(cwde, "mov $0xFFFE, %eax \n\t"
+                     "cwde\n\t");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_cbw);
+       report("cbq 1", ~0, outregs.eax == 0xFFFE);
+
+       exec_in_big_real_mode(&insn_cwde);
+       report("cwde 1", ~0, outregs.eax == 0xFFFFFFFE);
+}
+
+void test_loopcc(void)
+{
+       MK_INSN(loop, "mov $10, %ecx\n\t"
+                     "1: inc %eax\n\t"
+                     "loop 1b\n\t");
+
+       MK_INSN(loope, "mov $10, %ecx\n\t"
+                      "mov $1, %eax\n\t"
+                      "1: dec %eax\n\t"
+                      "loope 1b\n\t");
+
+       MK_INSN(loopne, "mov $10, %ecx\n\t"
+                       "mov $5, %eax\n\t"
+                       "1: dec %eax\n\t"
+                       "loopne 1b\n\t");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_loop);
+       report("LOOPcc short 1", R_AX, outregs.eax == 10);
+
+       exec_in_big_real_mode(&insn_loope);
+       report("LOOPcc short 2", R_AX | R_CX,
+              outregs.eax == -1 && outregs.ecx == 8);
+
+       exec_in_big_real_mode(&insn_loopne);
+       report("LOOPcc short 3", R_AX | R_CX,
+              outregs.eax == 0 && outregs.ecx == 5);
+}
+
+static void test_das(void)
+{
+    short i;
+    u16 nr_fail = 0;
+    static unsigned test_cases[1024] = {
+        0x46000000, 0x8701a000, 0x9710fa00, 0x97119a00,
+        0x02000101, 0x8301a101, 0x9310fb01, 0x93119b01,
+        0x02000202, 0x8301a202, 0x9710fc02, 0x97119c02,
+        0x06000303, 0x8701a303, 0x9310fd03, 0x93119d03,
+        0x02000404, 0x8301a404, 0x9310fe04, 0x93119e04,
+        0x06000505, 0x8701a505, 0x9710ff05, 0x97119f05,
+        0x06000606, 0x8701a606, 0x56100006, 0x9711a006,
+        0x02000707, 0x8301a707, 0x12100107, 0x9311a107,
+        0x02000808, 0x8301a808, 0x12100208, 0x9311a208,
+        0x06000909, 0x8701a909, 0x16100309, 0x9711a309,
+        0x1200040a, 0x9301a40a, 0x1210040a, 0x9311a40a,
+        0x1600050b, 0x9701a50b, 0x1610050b, 0x9711a50b,
+        0x1600060c, 0x9701a60c, 0x1610060c, 0x9711a60c,
+        0x1200070d, 0x9301a70d, 0x1210070d, 0x9311a70d,
+        0x1200080e, 0x9301a80e, 0x1210080e, 0x9311a80e,
+        0x1600090f, 0x9701a90f, 0x1610090f, 0x9711a90f,
+        0x02001010, 0x8301b010, 0x16100a10, 0x9711aa10,
+        0x06001111, 0x8701b111, 0x12100b11, 0x9311ab11,
+        0x06001212, 0x8701b212, 0x16100c12, 0x9711ac12,
+        0x02001313, 0x8301b313, 0x12100d13, 0x9311ad13,
+        0x06001414, 0x8701b414, 0x12100e14, 0x9311ae14,
+        0x02001515, 0x8301b515, 0x16100f15, 0x9711af15,
+        0x02001616, 0x8301b616, 0x12101016, 0x9311b016,
+        0x06001717, 0x8701b717, 0x16101117, 0x9711b117,
+        0x06001818, 0x8701b818, 0x16101218, 0x9711b218,
+        0x02001919, 0x8301b919, 0x12101319, 0x9311b319,
+        0x1600141a, 0x9701b41a, 0x1610141a, 0x9711b41a,
+        0x1200151b, 0x9301b51b, 0x1210151b, 0x9311b51b,
+        0x1200161c, 0x9301b61c, 0x1210161c, 0x9311b61c,
+        0x1600171d, 0x9701b71d, 0x1610171d, 0x9711b71d,
+        0x1600181e, 0x9701b81e, 0x1610181e, 0x9711b81e,
+        0x1200191f, 0x9301b91f, 0x1210191f, 0x9311b91f,
+        0x02002020, 0x8701c020, 0x12101a20, 0x9311ba20,
+        0x06002121, 0x8301c121, 0x16101b21, 0x9711bb21,
+        0x06002222, 0x8301c222, 0x12101c22, 0x9311bc22,
+        0x02002323, 0x8701c323, 0x16101d23, 0x9711bd23,
+        0x06002424, 0x8301c424, 0x16101e24, 0x9711be24,
+        0x02002525, 0x8701c525, 0x12101f25, 0x9311bf25,
+        0x02002626, 0x8701c626, 0x12102026, 0x9711c026,
+        0x06002727, 0x8301c727, 0x16102127, 0x9311c127,
+        0x06002828, 0x8301c828, 0x16102228, 0x9311c228,
+        0x02002929, 0x8701c929, 0x12102329, 0x9711c329,
+        0x1600242a, 0x9301c42a, 0x1610242a, 0x9311c42a,
+        0x1200252b, 0x9701c52b, 0x1210252b, 0x9711c52b,
+        0x1200262c, 0x9701c62c, 0x1210262c, 0x9711c62c,
+        0x1600272d, 0x9301c72d, 0x1610272d, 0x9311c72d,
+        0x1600282e, 0x9301c82e, 0x1610282e, 0x9311c82e,
+        0x1200292f, 0x9701c92f, 0x1210292f, 0x9711c92f,
+        0x06003030, 0x8301d030, 0x12102a30, 0x9711ca30,
+        0x02003131, 0x8701d131, 0x16102b31, 0x9311cb31,
+        0x02003232, 0x8701d232, 0x12102c32, 0x9711cc32,
+        0x06003333, 0x8301d333, 0x16102d33, 0x9311cd33,
+        0x02003434, 0x8701d434, 0x16102e34, 0x9311ce34,
+        0x06003535, 0x8301d535, 0x12102f35, 0x9711cf35,
+        0x06003636, 0x8301d636, 0x16103036, 0x9311d036,
+        0x02003737, 0x8701d737, 0x12103137, 0x9711d137,
+        0x02003838, 0x8701d838, 0x12103238, 0x9711d238,
+        0x06003939, 0x8301d939, 0x16103339, 0x9311d339,
+        0x1200343a, 0x9701d43a, 0x1210343a, 0x9711d43a,
+        0x1600353b, 0x9301d53b, 0x1610353b, 0x9311d53b,
+        0x1600363c, 0x9301d63c, 0x1610363c, 0x9311d63c,
+        0x1200373d, 0x9701d73d, 0x1210373d, 0x9711d73d,
+        0x1200383e, 0x9701d83e, 0x1210383e, 0x9711d83e,
+        0x1600393f, 0x9301d93f, 0x1610393f, 0x9311d93f,
+        0x02004040, 0x8301e040, 0x16103a40, 0x9311da40,
+        0x06004141, 0x8701e141, 0x12103b41, 0x9711db41,
+        0x06004242, 0x8701e242, 0x16103c42, 0x9311dc42,
+        0x02004343, 0x8301e343, 0x12103d43, 0x9711dd43,
+        0x06004444, 0x8701e444, 0x12103e44, 0x9711de44,
+        0x02004545, 0x8301e545, 0x16103f45, 0x9311df45,
+        0x02004646, 0x8301e646, 0x12104046, 0x9311e046,
+        0x06004747, 0x8701e747, 0x16104147, 0x9711e147,
+        0x06004848, 0x8701e848, 0x16104248, 0x9711e248,
+        0x02004949, 0x8301e949, 0x12104349, 0x9311e349,
+        0x1600444a, 0x9701e44a, 0x1610444a, 0x9711e44a,
+        0x1200454b, 0x9301e54b, 0x1210454b, 0x9311e54b,
+        0x1200464c, 0x9301e64c, 0x1210464c, 0x9311e64c,
+        0x1600474d, 0x9701e74d, 0x1610474d, 0x9711e74d,
+        0x1600484e, 0x9701e84e, 0x1610484e, 0x9711e84e,
+        0x1200494f, 0x9301e94f, 0x1210494f, 0x9311e94f,
+        0x06005050, 0x8701f050, 0x12104a50, 0x9311ea50,
+        0x02005151, 0x8301f151, 0x16104b51, 0x9711eb51,
+        0x02005252, 0x8301f252, 0x12104c52, 0x9311ec52,
+        0x06005353, 0x8701f353, 0x16104d53, 0x9711ed53,
+        0x02005454, 0x8301f454, 0x16104e54, 0x9711ee54,
+        0x06005555, 0x8701f555, 0x12104f55, 0x9311ef55,
+        0x06005656, 0x8701f656, 0x16105056, 0x9711f056,
+        0x02005757, 0x8301f757, 0x12105157, 0x9311f157,
+        0x02005858, 0x8301f858, 0x12105258, 0x9311f258,
+        0x06005959, 0x8701f959, 0x16105359, 0x9711f359,
+        0x1200545a, 0x9301f45a, 0x1210545a, 0x9311f45a,
+        0x1600555b, 0x9701f55b, 0x1610555b, 0x9711f55b,
+        0x1600565c, 0x9701f65c, 0x1610565c, 0x9711f65c,
+        0x1200575d, 0x9301f75d, 0x1210575d, 0x9311f75d,
+        0x1200585e, 0x9301f85e, 0x1210585e, 0x9311f85e,
+        0x1600595f, 0x9701f95f, 0x1610595f, 0x9711f95f,
+        0x06006060, 0x47010060, 0x16105a60, 0x9711fa60,
+        0x02006161, 0x03010161, 0x12105b61, 0x9311fb61,
+        0x02006262, 0x03010262, 0x16105c62, 0x9711fc62,
+        0x06006363, 0x07010363, 0x12105d63, 0x9311fd63,
+        0x02006464, 0x03010464, 0x12105e64, 0x9311fe64,
+        0x06006565, 0x07010565, 0x16105f65, 0x9711ff65,
+        0x06006666, 0x07010666, 0x16106066, 0x57110066,
+        0x02006767, 0x03010767, 0x12106167, 0x13110167,
+        0x02006868, 0x03010868, 0x12106268, 0x13110268,
+        0x06006969, 0x07010969, 0x16106369, 0x17110369,
+        0x1200646a, 0x1301046a, 0x1210646a, 0x1311046a,
+        0x1600656b, 0x1701056b, 0x1610656b, 0x1711056b,
+        0x1600666c, 0x1701066c, 0x1610666c, 0x1711066c,
+        0x1200676d, 0x1301076d, 0x1210676d, 0x1311076d,
+        0x1200686e, 0x1301086e, 0x1210686e, 0x1311086e,
+        0x1600696f, 0x1701096f, 0x1610696f, 0x1711096f,
+        0x02007070, 0x03011070, 0x16106a70, 0x17110a70,
+        0x06007171, 0x07011171, 0x12106b71, 0x13110b71,
+        0x06007272, 0x07011272, 0x16106c72, 0x17110c72,
+        0x02007373, 0x03011373, 0x12106d73, 0x13110d73,
+        0x06007474, 0x07011474, 0x12106e74, 0x13110e74,
+        0x02007575, 0x03011575, 0x16106f75, 0x17110f75,
+        0x02007676, 0x03011676, 0x12107076, 0x13111076,
+        0x06007777, 0x07011777, 0x16107177, 0x17111177,
+        0x06007878, 0x07011878, 0x16107278, 0x17111278,
+        0x02007979, 0x03011979, 0x12107379, 0x13111379,
+        0x1600747a, 0x1701147a, 0x1610747a, 0x1711147a,
+        0x1200757b, 0x1301157b, 0x1210757b, 0x1311157b,
+        0x1200767c, 0x1301167c, 0x1210767c, 0x1311167c,
+        0x1600777d, 0x1701177d, 0x1610777d, 0x1711177d,
+        0x1600787e, 0x1701187e, 0x1610787e, 0x1711187e,
+        0x1200797f, 0x1301197f, 0x1210797f, 0x1311197f,
+        0x82008080, 0x03012080, 0x12107a80, 0x13111a80,
+        0x86008181, 0x07012181, 0x16107b81, 0x17111b81,
+        0x86008282, 0x07012282, 0x12107c82, 0x13111c82,
+        0x82008383, 0x03012383, 0x16107d83, 0x17111d83,
+        0x86008484, 0x07012484, 0x16107e84, 0x17111e84,
+        0x82008585, 0x03012585, 0x12107f85, 0x13111f85,
+        0x82008686, 0x03012686, 0x92108086, 0x13112086,
+        0x86008787, 0x07012787, 0x96108187, 0x17112187,
+        0x86008888, 0x07012888, 0x96108288, 0x17112288,
+        0x82008989, 0x03012989, 0x92108389, 0x13112389,
+        0x9600848a, 0x1701248a, 0x9610848a, 0x1711248a,
+        0x9200858b, 0x1301258b, 0x9210858b, 0x1311258b,
+        0x9200868c, 0x1301268c, 0x9210868c, 0x1311268c,
+        0x9600878d, 0x1701278d, 0x9610878d, 0x1711278d,
+        0x9600888e, 0x1701288e, 0x9610888e, 0x1711288e,
+        0x9200898f, 0x1301298f, 0x9210898f, 0x1311298f,
+        0x86009090, 0x07013090, 0x92108a90, 0x13112a90,
+        0x82009191, 0x03013191, 0x96108b91, 0x17112b91,
+        0x82009292, 0x03013292, 0x92108c92, 0x13112c92,
+        0x86009393, 0x07013393, 0x96108d93, 0x17112d93,
+        0x82009494, 0x03013494, 0x96108e94, 0x17112e94,
+        0x86009595, 0x07013595, 0x92108f95, 0x13112f95,
+        0x86009696, 0x07013696, 0x96109096, 0x17113096,
+        0x82009797, 0x03013797, 0x92109197, 0x13113197,
+        0x82009898, 0x03013898, 0x92109298, 0x13113298,
+        0x86009999, 0x07013999, 0x96109399, 0x17113399,
+        0x1300349a, 0x1301349a, 0x1310349a, 0x1311349a,
+        0x1700359b, 0x1701359b, 0x1710359b, 0x1711359b,
+        0x1700369c, 0x1701369c, 0x1710369c, 0x1711369c,
+        0x1300379d, 0x1301379d, 0x1310379d, 0x1311379d,
+        0x1300389e, 0x1301389e, 0x1310389e, 0x1311389e,
+        0x1700399f, 0x1701399f, 0x1710399f, 0x1711399f,
+        0x030040a0, 0x030140a0, 0x17103aa0, 0x17113aa0,
+        0x070041a1, 0x070141a1, 0x13103ba1, 0x13113ba1,
+        0x070042a2, 0x070142a2, 0x17103ca2, 0x17113ca2,
+        0x030043a3, 0x030143a3, 0x13103da3, 0x13113da3,
+        0x070044a4, 0x070144a4, 0x13103ea4, 0x13113ea4,
+        0x030045a5, 0x030145a5, 0x17103fa5, 0x17113fa5,
+        0x030046a6, 0x030146a6, 0x131040a6, 0x131140a6,
+        0x070047a7, 0x070147a7, 0x171041a7, 0x171141a7,
+        0x070048a8, 0x070148a8, 0x171042a8, 0x171142a8,
+        0x030049a9, 0x030149a9, 0x131043a9, 0x131143a9,
+        0x170044aa, 0x170144aa, 0x171044aa, 0x171144aa,
+        0x130045ab, 0x130145ab, 0x131045ab, 0x131145ab,
+        0x130046ac, 0x130146ac, 0x131046ac, 0x131146ac,
+        0x170047ad, 0x170147ad, 0x171047ad, 0x171147ad,
+        0x170048ae, 0x170148ae, 0x171048ae, 0x171148ae,
+        0x130049af, 0x130149af, 0x131049af, 0x131149af,
+        0x070050b0, 0x070150b0, 0x13104ab0, 0x13114ab0,
+        0x030051b1, 0x030151b1, 0x17104bb1, 0x17114bb1,
+        0x030052b2, 0x030152b2, 0x13104cb2, 0x13114cb2,
+        0x070053b3, 0x070153b3, 0x17104db3, 0x17114db3,
+        0x030054b4, 0x030154b4, 0x17104eb4, 0x17114eb4,
+        0x070055b5, 0x070155b5, 0x13104fb5, 0x13114fb5,
+        0x070056b6, 0x070156b6, 0x171050b6, 0x171150b6,
+        0x030057b7, 0x030157b7, 0x131051b7, 0x131151b7,
+        0x030058b8, 0x030158b8, 0x131052b8, 0x131152b8,
+        0x070059b9, 0x070159b9, 0x171053b9, 0x171153b9,
+        0x130054ba, 0x130154ba, 0x131054ba, 0x131154ba,
+        0x170055bb, 0x170155bb, 0x171055bb, 0x171155bb,
+        0x170056bc, 0x170156bc, 0x171056bc, 0x171156bc,
+        0x130057bd, 0x130157bd, 0x131057bd, 0x131157bd,
+        0x130058be, 0x130158be, 0x131058be, 0x131158be,
+        0x170059bf, 0x170159bf, 0x171059bf, 0x171159bf,
+        0x070060c0, 0x070160c0, 0x17105ac0, 0x17115ac0,
+        0x030061c1, 0x030161c1, 0x13105bc1, 0x13115bc1,
+        0x030062c2, 0x030162c2, 0x17105cc2, 0x17115cc2,
+        0x070063c3, 0x070163c3, 0x13105dc3, 0x13115dc3,
+        0x030064c4, 0x030164c4, 0x13105ec4, 0x13115ec4,
+        0x070065c5, 0x070165c5, 0x17105fc5, 0x17115fc5,
+        0x070066c6, 0x070166c6, 0x171060c6, 0x171160c6,
+        0x030067c7, 0x030167c7, 0x131061c7, 0x131161c7,
+        0x030068c8, 0x030168c8, 0x131062c8, 0x131162c8,
+        0x070069c9, 0x070169c9, 0x171063c9, 0x171163c9,
+        0x130064ca, 0x130164ca, 0x131064ca, 0x131164ca,
+        0x170065cb, 0x170165cb, 0x171065cb, 0x171165cb,
+        0x170066cc, 0x170166cc, 0x171066cc, 0x171166cc,
+        0x130067cd, 0x130167cd, 0x131067cd, 0x131167cd,
+        0x130068ce, 0x130168ce, 0x131068ce, 0x131168ce,
+        0x170069cf, 0x170169cf, 0x171069cf, 0x171169cf,
+        0x030070d0, 0x030170d0, 0x17106ad0, 0x17116ad0,
+        0x070071d1, 0x070171d1, 0x13106bd1, 0x13116bd1,
+        0x070072d2, 0x070172d2, 0x17106cd2, 0x17116cd2,
+        0x030073d3, 0x030173d3, 0x13106dd3, 0x13116dd3,
+        0x070074d4, 0x070174d4, 0x13106ed4, 0x13116ed4,
+        0x030075d5, 0x030175d5, 0x17106fd5, 0x17116fd5,
+        0x030076d6, 0x030176d6, 0x131070d6, 0x131170d6,
+        0x070077d7, 0x070177d7, 0x171071d7, 0x171171d7,
+        0x070078d8, 0x070178d8, 0x171072d8, 0x171172d8,
+        0x030079d9, 0x030179d9, 0x131073d9, 0x131173d9,
+        0x170074da, 0x170174da, 0x171074da, 0x171174da,
+        0x130075db, 0x130175db, 0x131075db, 0x131175db,
+        0x130076dc, 0x130176dc, 0x131076dc, 0x131176dc,
+        0x170077dd, 0x170177dd, 0x171077dd, 0x171177dd,
+        0x170078de, 0x170178de, 0x171078de, 0x171178de,
+        0x130079df, 0x130179df, 0x131079df, 0x131179df,
+        0x830080e0, 0x830180e0, 0x13107ae0, 0x13117ae0,
+        0x870081e1, 0x870181e1, 0x17107be1, 0x17117be1,
+        0x870082e2, 0x870182e2, 0x13107ce2, 0x13117ce2,
+        0x830083e3, 0x830183e3, 0x17107de3, 0x17117de3,
+        0x870084e4, 0x870184e4, 0x17107ee4, 0x17117ee4,
+        0x830085e5, 0x830185e5, 0x13107fe5, 0x13117fe5,
+        0x830086e6, 0x830186e6, 0x931080e6, 0x931180e6,
+        0x870087e7, 0x870187e7, 0x971081e7, 0x971181e7,
+        0x870088e8, 0x870188e8, 0x971082e8, 0x971182e8,
+        0x830089e9, 0x830189e9, 0x931083e9, 0x931183e9,
+        0x970084ea, 0x970184ea, 0x971084ea, 0x971184ea,
+        0x930085eb, 0x930185eb, 0x931085eb, 0x931185eb,
+        0x930086ec, 0x930186ec, 0x931086ec, 0x931186ec,
+        0x970087ed, 0x970187ed, 0x971087ed, 0x971187ed,
+        0x970088ee, 0x970188ee, 0x971088ee, 0x971188ee,
+        0x930089ef, 0x930189ef, 0x931089ef, 0x931189ef,
+        0x870090f0, 0x870190f0, 0x93108af0, 0x93118af0,
+        0x830091f1, 0x830191f1, 0x97108bf1, 0x97118bf1,
+        0x830092f2, 0x830192f2, 0x93108cf2, 0x93118cf2,
+        0x870093f3, 0x870193f3, 0x97108df3, 0x97118df3,
+        0x830094f4, 0x830194f4, 0x97108ef4, 0x97118ef4,
+        0x870095f5, 0x870195f5, 0x93108ff5, 0x93118ff5,
+        0x870096f6, 0x870196f6, 0x971090f6, 0x971190f6,
+        0x830097f7, 0x830197f7, 0x931091f7, 0x931191f7,
+        0x830098f8, 0x830198f8, 0x931092f8, 0x931192f8,
+        0x870099f9, 0x870199f9, 0x971093f9, 0x971193f9,
+        0x930094fa, 0x930194fa, 0x931094fa, 0x931194fa,
+        0x970095fb, 0x970195fb, 0x971095fb, 0x971195fb,
+        0x970096fc, 0x970196fc, 0x971096fc, 0x971196fc,
+        0x930097fd, 0x930197fd, 0x931097fd, 0x931197fd,
+        0x930098fe, 0x930198fe, 0x931098fe, 0x931198fe,
+        0x970099ff, 0x970199ff, 0x971099ff, 0x971199ff,
+    };
+
+    MK_INSN(das, "das");
+
+    inregs = (struct regs){ 0 };
+
+    for (i = 0; i < 1024; ++i) {
+        unsigned tmp = test_cases[i];
+        inregs.eax = tmp & 0xff;
+        inregs.eflags = (tmp >> 16) & 0xff;
+       exec_in_big_real_mode(&insn_das);
+       if (!regs_equal(R_AX)
+            || outregs.eax != ((tmp >> 8) & 0xff)
+            || (outregs.eflags & 0xff) != (tmp >> 24)) {
+           ++nr_fail;
+           break;
+        }
+    }
+    report("DAS", ~0, nr_fail == 0);
+}
+
+void test_cwd_cdq()
+{
+       /* Sign-bit set */
+       MK_INSN(cwd_1, "mov $0x8000, %ax\n\t"
+                      "cwd\n\t");
+
+       /* Sign-bit not set */
+       MK_INSN(cwd_2, "mov $0x1000, %ax\n\t"
+                      "cwd\n\t");
+
+       /* Sign-bit set */
+       MK_INSN(cdq_1, "mov $0x80000000, %eax\n\t"
+                      "cdq\n\t");
+
+       /* Sign-bit not set */
+       MK_INSN(cdq_2, "mov $0x10000000, %eax\n\t"
+                      "cdq\n\t");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_cwd_1);
+       report("cwd 1", R_AX | R_DX,
+              outregs.eax == 0x8000 && outregs.edx == 0xffff);
+
+       exec_in_big_real_mode(&insn_cwd_2);
+       report("cwd 2", R_AX | R_DX,
+              outregs.eax == 0x1000 && outregs.edx == 0);
+
+       exec_in_big_real_mode(&insn_cdq_1);
+       report("cdq 1", R_AX | R_DX,
+              outregs.eax == 0x80000000 && outregs.edx == 0xffffffff);
+
+       exec_in_big_real_mode(&insn_cdq_2);
+       report("cdq 2", R_AX | R_DX,
+              outregs.eax == 0x10000000 && outregs.edx == 0);
+}
+
+static struct {
+        void *address;
+        unsigned short sel;
+} __attribute__((packed)) desc = {
+       (void *)0x1234,
+       0x10,
+};
+
+void test_lds_lss()
+{
+       inregs = (struct regs){ .ebx = (unsigned long)&desc };
+
+       MK_INSN(lds, "push %ds\n\t"
+                    "lds (%ebx), %eax\n\t"
+                    "mov %ds, %ebx\n\t"
+                    "pop %ds\n\t");
+       exec_in_big_real_mode(&insn_lds);
+       report("lds", R_AX | R_BX,
+               outregs.eax == (unsigned long)desc.address &&
+               outregs.ebx == desc.sel);
+
+       MK_INSN(les, "push %es\n\t"
+                    "les (%ebx), %eax\n\t"
+                    "mov %es, %ebx\n\t"
+                    "pop %es\n\t");
+       exec_in_big_real_mode(&insn_les);
+       report("les", R_AX | R_BX,
+               outregs.eax == (unsigned long)desc.address &&
+               outregs.ebx == desc.sel);
+
+       MK_INSN(lfs, "push %fs\n\t"
+                    "lfs (%ebx), %eax\n\t"
+                    "mov %fs, %ebx\n\t"
+                    "pop %fs\n\t");
+       exec_in_big_real_mode(&insn_lfs);
+       report("lfs", R_AX | R_BX,
+               outregs.eax == (unsigned long)desc.address &&
+               outregs.ebx == desc.sel);
+
+       MK_INSN(lgs, "push %gs\n\t"
+                    "lgs (%ebx), %eax\n\t"
+                    "mov %gs, %ebx\n\t"
+                    "pop %gs\n\t");
+       exec_in_big_real_mode(&insn_lgs);
+       report("lgs", R_AX | R_BX,
+               outregs.eax == (unsigned long)desc.address &&
+               outregs.ebx == desc.sel);
+
+       MK_INSN(lss, "push %ss\n\t"
+                    "lss (%ebx), %eax\n\t"
+                    "mov %ss, %ebx\n\t"
+                    "pop %ss\n\t");
+       exec_in_big_real_mode(&insn_lss);
+       report("lss", R_AX | R_BX,
+               outregs.eax == (unsigned long)desc.address &&
+               outregs.ebx == desc.sel);
+}
+
+void test_jcxz(void)
+{
+       MK_INSN(jcxz1, "jcxz 1f\n\t"
+                      "mov $0x1234, %eax\n\t"
+                      "1:\n\t");
+       MK_INSN(jcxz2, "mov $0x100, %ecx\n\t"
+                      "jcxz 1f\n\t"
+                      "mov $0x1234, %eax\n\t"
+                      "mov $0, %ecx\n\t"
+                      "1:\n\t");
+       MK_INSN(jcxz3, "mov $0x10000, %ecx\n\t"
+                      "jcxz 1f\n\t"
+                      "mov $0x1234, %eax\n\t"
+                      "1:\n\t");
+       MK_INSN(jecxz1, "jecxz 1f\n\t"
+                       "mov $0x1234, %eax\n\t"
+                       "1:\n\t");
+       MK_INSN(jecxz2, "mov $0x10000, %ecx\n\t"
+                       "jecxz 1f\n\t"
+                       "mov $0x1234, %eax\n\t"
+                       "mov $0, %ecx\n\t"
+                       "1:\n\t");
+
+       inregs = (struct regs){ 0 };
+
+       exec_in_big_real_mode(&insn_jcxz1);
+       report("jcxz short 1", 0, 1);
+
+       exec_in_big_real_mode(&insn_jcxz2);
+       report("jcxz short 2", R_AX, outregs.eax == 0x1234);
+
+       exec_in_big_real_mode(&insn_jcxz3);
+       report("jcxz short 3", R_CX, outregs.ecx == 0x10000);
+
+       exec_in_big_real_mode(&insn_jecxz1);
+       report("jecxz short 1", 0, 1);
+
+       exec_in_big_real_mode(&insn_jecxz2);
+       report("jecxz short 2", R_AX, outregs.eax == 0x1234);
+}
+
+static void test_cpuid(void)
+{
+    MK_INSN(cpuid, "cpuid");
+    unsigned function = 0x1234;
+    unsigned eax, ebx, ecx, edx;
+
+    inregs.eax = eax = function;
+    asm("cpuid" : "+a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx));
+    exec_in_big_real_mode(&insn_cpuid);
+    report("cpuid", R_AX|R_BX|R_CX|R_DX,
+          outregs.eax == eax && outregs.ebx == ebx
+          && outregs.ecx == ecx && outregs.edx == edx);
+}
+
+static void test_ss_base_for_esp_ebp(void)
+{
+    MK_INSN(ssrel1, "mov %ss, %ax; mov %bx, %ss; movl (%ebp), %ebx; mov %ax, 
%ss");
+    MK_INSN(ssrel2, "mov %ss, %ax; mov %bx, %ss; movl (%ebp,%edi,8), %ebx; mov 
%ax, %ss");
+    static unsigned array[] = { 0x12345678, 0, 0, 0, 0x87654321 };
+
+    inregs.ebx = 1;
+    inregs.ebp = (unsigned)array;
+    exec_in_big_real_mode(&insn_ssrel1);
+    report("ss relative addressing (1)", R_AX | R_BX, outregs.ebx == 
0x87654321);
+    inregs.ebx = 1;
+    inregs.ebp = (unsigned)array;
+    inregs.edi = 0;
+    exec_in_big_real_mode(&insn_ssrel2);
+    report("ss relative addressing (2)", R_AX | R_BX, outregs.ebx == 
0x87654321);
+}
+
+static void test_sgdt_sidt(void)
+{
+    MK_INSN(sgdt, "sgdtw (%eax)");
+    MK_INSN(sidt, "sidtw (%eax)");
+    unsigned x, y;
+
+    inregs.eax = (unsigned)&y;
+    asm volatile("sgdtw %0" : "=m"(x));
+    exec_in_big_real_mode(&insn_sgdt);
+    report("sgdt", 0, x == y);
+
+    inregs.eax = (unsigned)&y;
+    asm volatile("sidtw %0" : "=m"(x));
+    exec_in_big_real_mode(&insn_sidt);
+    report("sidt", 0, x == y);
+}
+
+static void test_lahf(void)
+{
+    MK_INSN(lahf, "pushfw; mov %al, (%esp); popfw; lahf");
+
+    inregs.eax = 0xc7;
+    exec_in_big_real_mode(&insn_lahf);
+    report("lahf", R_AX, (outregs.eax >> 8) == inregs.eax);
+}
+
+static void test_movzx_movsx(void)
+{
+    MK_INSN(movsx, "movsx %al, %ebx");
+    MK_INSN(movzx, "movzx %al, %ebx");
+    MK_INSN(movzsah, "movsx %ah, %ebx");
+    MK_INSN(movzxah, "movzx %ah, %ebx");
+
+    inregs.eax = 0x1234569c;
+    inregs.esp = 0xffff;
+    exec_in_big_real_mode(&insn_movsx);
+    report("movsx", R_BX, outregs.ebx == (signed char)inregs.eax);
+    exec_in_big_real_mode(&insn_movzx);
+    report("movzx", R_BX, outregs.ebx == (unsigned char)inregs.eax);
+    exec_in_big_real_mode(&insn_movzsah);
+    report("movsx ah", R_BX, outregs.ebx == (signed char)(inregs.eax>>8));
+    exec_in_big_real_mode(&insn_movzxah);
+    report("movzx ah", R_BX, outregs.ebx == (unsigned char)(inregs.eax >> 8));
+}
+
+static void test_bswap(void)
+{
+    MK_INSN(bswap, "bswap %ecx");
+
+    inregs.ecx = 0x12345678;
+    exec_in_big_real_mode(&insn_bswap);
+    report("bswap", R_CX, outregs.ecx == 0x78563412);
+}
+
+static void test_aad(void)
+{
+    MK_INSN(aad, "aad");
+
+    inregs.eax = 0x12345678;
+    exec_in_big_real_mode(&insn_aad);
+    report("aad", R_AX, outregs.eax == 0x123400d4);
+}
+
+static void test_aam(void)
+{
+    MK_INSN(aam, "aam");
+
+    inregs.eax = 0x76543210;
+    exec_in_big_real_mode(&insn_aam);
+    report("aam", R_AX, outregs.eax == 0x76540106);
+}
+
+static void test_xlat(void)
+{
+    MK_INSN(xlat, "xlat");
+    u8 table[256];
+    int i;
+
+    for (i = 0; i < 256; i++) {
+        table[i] = i + 1;
+    }
+
+    inregs.eax = 0x89abcdef;
+    inregs.ebx = (u32)table;
+    exec_in_big_real_mode(&insn_xlat);
+    report("xlat", R_AX, outregs.eax == 0x89abcdf0);
+}
+
+static void test_salc(void)
+{
+    MK_INSN(clc_salc, "clc; .byte 0xd6");
+    MK_INSN(stc_salc, "stc; .byte 0xd6");
+
+    inregs.eax = 0x12345678;
+    exec_in_big_real_mode(&insn_clc_salc);
+    report("salc (1)", R_AX, outregs.eax == 0x12345600);
+    exec_in_big_real_mode(&insn_stc_salc);
+    report("salc (2)", R_AX, outregs.eax == 0x123456ff);
+}
+
+static void test_fninit(void)
+{
+       u16 fcw = -1, fsw = -1;
+       MK_INSN(fninit, "fninit ; fnstsw (%eax) ; fnstcw (%ebx)");
+
+       inregs.eax = (u32)&fsw;
+       inregs.ebx = (u32)&fcw;
+
+       exec_in_big_real_mode(&insn_fninit);
+       report("fninit", 0, fsw == 0 && (fcw & 0x103f) == 0x003f);
+}
+
+static void test_nopl(void)
+{
+       MK_INSN(nopl1, ".byte 0x90\n\r"); // 1 byte nop
+       MK_INSN(nopl2, ".byte 0x66, 0x90\n\r"); // 2 bytes nop
+       MK_INSN(nopl3, ".byte 0x0f, 0x1f, 0x00\n\r"); // 3 bytes nop
+       MK_INSN(nopl4, ".byte 0x0f, 0x1f, 0x40, 0x00\n\r"); // 4 bytes nop
+       exec_in_big_real_mode(&insn_nopl1);
+       exec_in_big_real_mode(&insn_nopl2);
+       exec_in_big_real_mode(&insn_nopl3);
+       exec_in_big_real_mode(&insn_nopl4);
+       report("nopl", 0, 1);
+}
+
+void realmode_start(void)
+{
+       test_null();
+
+       test_shld();
+       test_push_pop();
+       test_pusha_popa();
+       test_mov_imm();
+       test_cmp_imm();
+       test_add_imm();
+       test_sub_imm();
+       test_xor_imm();
+       test_io();
+       test_eflags_insn();
+       test_jcc_short();
+       test_jcc_near();
+       /* test_call() uses short jump so call it after testing jcc */
+       test_call();
+       /* long jmp test uses call near so test it after testing call */
+       test_long_jmp();
+       test_xchg();
+       test_iret();
+       test_int();
+       test_imul();
+       test_mul();
+       test_div();
+       test_idiv();
+       test_loopcc();
+       test_cbw();
+       test_cwd_cdq();
+       test_das();
+       test_lds_lss();
+       test_jcxz();
+       test_cpuid();
+       test_ss_base_for_esp_ebp();
+       test_sgdt_sidt();
+       test_lahf();
+       test_movzx_movsx();
+       test_bswap();
+       test_aad();
+       test_aam();
+       test_xlat();
+       test_salc();
+       test_fninit();
+       test_nopl();
+
+       exit(0);
+}
+
+unsigned long long r_gdt[] = { 0, 0x9b000000ffff, 0x93000000ffff };
+
+struct __attribute__((packed)) {
+       unsigned short limit;
+       void *base;
+} r_gdt_descr = { sizeof(r_gdt) - 1, &r_gdt };
+
+asm(
+       ".section .init \n\t"
+
+       ".code32 \n\t"
+
+       "mb_magic = 0x1BADB002 \n\t"
+       "mb_flags = 0x0 \n\t"
+
+       "# multiboot header \n\t"
+       ".long mb_magic, mb_flags, 0 - (mb_magic + mb_flags) \n\t"
+
+       ".globl start \n\t"
+       ".data \n\t"
+       ". = . + 4096 \n\t"
+       "stacktop: \n\t"
+
+       ".text \n\t"
+       "start: \n\t"
+       "lgdt r_gdt_descr \n\t"
+       "ljmp $8, $1f; 1: \n\t"
+       ".code16gcc \n\t"
+       "mov $16, %eax \n\t"
+       "mov %ax, %ds \n\t"
+       "mov %ax, %es \n\t"
+       "mov %ax, %fs \n\t"
+       "mov %ax, %gs \n\t"
+       "mov %ax, %ss \n\t"
+       "mov %cr0, %eax \n\t"
+       "btc $0, %eax \n\t"
+       "mov %eax, %cr0 \n\t"
+       "ljmp $0, $realmode_entry \n\t"
+
+       "realmode_entry: \n\t"
+
+       "xor %ax, %ax \n\t"
+       "mov %ax, %ds \n\t"
+       "mov %ax, %es \n\t"
+       "mov %ax, %ss \n\t"
+       "mov %ax, %fs \n\t"
+       "mov %ax, %gs \n\t"
+       "mov $stacktop, %esp\n\t"
+       "ljmp $0, $realmode_start \n\t"
+
+       ".code16gcc \n\t"
+       );
diff --git a/kvm-unittest/x86/rmap_chain.c b/kvm-unittest/x86/rmap_chain.c
new file mode 100644
index 0000000..0df1bcb
--- /dev/null
+++ b/kvm-unittest/x86/rmap_chain.c
@@ -0,0 +1,54 @@
+/* test long rmap chains */
+
+#include "libcflat.h"
+#include "fwcfg.h"
+#include "vm.h"
+#include "smp.h"
+
+void print(const char *s);
+
+static unsigned int inl(unsigned short port)
+{
+    unsigned int val;
+    asm volatile ("inl %w1, %0":"=a" (val):"Nd" (port));
+    return val;
+}
+
+int main (void)
+{
+    int i;
+    int nr_pages;
+    void *target_page, *virt_addr;
+
+    setup_vm();
+
+    nr_pages = fwcfg_get_u64(FW_CFG_RAM_SIZE) / PAGE_SIZE;
+    nr_pages -= 1000;
+    target_page = alloc_page();
+
+    virt_addr = (void *) 0xfffffa000;
+    for (i = 0; i < nr_pages; i++) {
+        install_page(phys_to_virt(read_cr3()), virt_to_phys(target_page),
+                     virt_addr);
+        virt_addr += PAGE_SIZE;
+    }
+    printf("created %d mappings\n", nr_pages);
+
+    virt_addr = (void *) 0xfffffa000;
+    for (i = 0; i < nr_pages; i++) {
+        unsigned long *touch = virt_addr;
+
+        *touch = 0;
+        virt_addr += PAGE_SIZE;
+    }
+    printf("instantiated mappings\n");
+
+    virt_addr += PAGE_SIZE;
+    install_pte(phys_to_virt(read_cr3()), 1, virt_addr,
+                0 | PTE_PRESENT | PTE_WRITE, target_page);
+
+    *(unsigned long *)virt_addr = 0;
+    printf("PASS\n");
+
+    return 0;
+}
diff --git a/kvm-unittest/x86/s3.c b/kvm-unittest/x86/s3.c
new file mode 100644
index 0000000..71d3ff9
--- /dev/null
+++ b/kvm-unittest/x86/s3.c
@@ -0,0 +1,203 @@
+#include "libcflat.h"
+
+struct rsdp_descriptor {        /* Root System Descriptor Pointer */
+    u64 signature;              /* ACPI signature, contains "RSD PTR " */
+    u8  checksum;               /* To make sum of struct == 0 */
+    u8  oem_id [6];             /* OEM identification */
+    u8  revision;               /* Must be 0 for 1.0, 2 for 2.0 */
+    u32 rsdt_physical_address;  /* 32-bit physical address of RSDT */
+    u32 length;                 /* XSDT Length in bytes including hdr */
+    u64 xsdt_physical_address;  /* 64-bit physical address of XSDT */
+    u8  extended_checksum;      /* Checksum of entire table */
+    u8  reserved [3];           /* Reserved field must be 0 */
+};
+
+#define ACPI_TABLE_HEADER_DEF   /* ACPI common table header */ \
+    u32 signature;          /* ACPI signature (4 ASCII characters) */ \
+    u32 length;                 /* Length of table, in bytes, including header 
*/ \
+    u8  revision;               /* ACPI Specification minor version # */ \
+    u8  checksum;               /* To make sum of entire table == 0 */ \
+    u8  oem_id [6];             /* OEM identification */ \
+    u8  oem_table_id [8];       /* OEM table identification */ \
+    u32 oem_revision;           /* OEM revision number */ \
+    u8  asl_compiler_id [4];    /* ASL compiler vendor ID */ \
+    u32 asl_compiler_revision;  /* ASL compiler revision number */
+
+#define RSDT_SIGNATURE 0x54445352
+struct rsdt_descriptor_rev1 {
+    ACPI_TABLE_HEADER_DEF
+    u32 table_offset_entry[0];
+};
+
+#define FACP_SIGNATURE 0x50434146 // FACP
+struct fadt_descriptor_rev1
+{
+    ACPI_TABLE_HEADER_DEF     /* ACPI common table header */
+    u32 firmware_ctrl;          /* Physical address of FACS */
+    u32 dsdt;                   /* Physical address of DSDT */
+    u8  model;                  /* System Interrupt Model */
+    u8  reserved1;              /* Reserved */
+    u16 sci_int;                /* System vector of SCI interrupt */
+    u32 smi_cmd;                /* Port address of SMI command port */
+    u8  acpi_enable;            /* Value to write to smi_cmd to enable ACPI */
+    u8  acpi_disable;           /* Value to write to smi_cmd to disable ACPI */
+    u8  S4bios_req;             /* Value to write to SMI CMD to enter S4BIOS 
state */
+    u8  reserved2;              /* Reserved - must be zero */
+    u32 pm1a_evt_blk;           /* Port address of Power Mgt 1a acpi_event Reg 
Blk */
+    u32 pm1b_evt_blk;           /* Port address of Power Mgt 1b acpi_event Reg 
Blk */
+    u32 pm1a_cnt_blk;           /* Port address of Power Mgt 1a Control Reg 
Blk */
+    u32 pm1b_cnt_blk;           /* Port address of Power Mgt 1b Control Reg 
Blk */
+    u32 pm2_cnt_blk;            /* Port address of Power Mgt 2 Control Reg Blk 
*/
+    u32 pm_tmr_blk;             /* Port address of Power Mgt Timer Ctrl Reg 
Blk */
+    u32 gpe0_blk;               /* Port addr of General Purpose acpi_event 0 
Reg Blk */
+    u32 gpe1_blk;               /* Port addr of General Purpose acpi_event 1 
Reg Blk */
+    u8  pm1_evt_len;            /* Byte length of ports at pm1_x_evt_blk */
+    u8  pm1_cnt_len;            /* Byte length of ports at pm1_x_cnt_blk */
+    u8  pm2_cnt_len;            /* Byte Length of ports at pm2_cnt_blk */
+    u8  pm_tmr_len;             /* Byte Length of ports at pm_tm_blk */
+    u8  gpe0_blk_len;           /* Byte Length of ports at gpe0_blk */
+    u8  gpe1_blk_len;           /* Byte Length of ports at gpe1_blk */
+    u8  gpe1_base;              /* Offset in gpe model where gpe1 events start 
*/
+    u8  reserved3;              /* Reserved */
+    u16 plvl2_lat;              /* Worst case HW latency to enter/exit C2 
state */
+    u16 plvl3_lat;              /* Worst case HW latency to enter/exit C3 
state */
+    u16 flush_size;             /* Size of area read to flush caches */
+    u16 flush_stride;           /* Stride used in flushing caches */
+    u8  duty_offset;            /* Bit location of duty cycle field in p_cnt 
reg */
+    u8  duty_width;             /* Bit width of duty cycle field in p_cnt reg 
*/
+    u8  day_alrm;               /* Index to day-of-month alarm in RTC CMOS RAM 
*/
+    u8  mon_alrm;               /* Index to month-of-year alarm in RTC CMOS 
RAM */
+    u8  century;                /* Index to century in RTC CMOS RAM */
+    u8  reserved4;              /* Reserved */
+    u8  reserved4a;             /* Reserved */
+    u8  reserved4b;             /* Reserved */
+};
+
+#define FACS_SIGNATURE 0x53434146 // FACS
+struct facs_descriptor_rev1
+{
+    u32 signature;           /* ACPI Signature */
+    u32 length;                 /* Length of structure, in bytes */
+    u32 hardware_signature;     /* Hardware configuration signature */
+    u32 firmware_waking_vector; /* ACPI OS waking vector */
+    u32 global_lock;            /* Global Lock */
+    u32 S4bios_f        : 1;    /* Indicates if S4BIOS support is present */
+    u32 reserved1       : 31;   /* Must be 0 */
+    u8  resverved3 [40];        /* Reserved - must be zero */
+};
+
+u32* find_resume_vector_addr(void)
+{
+    unsigned long addr;
+    struct rsdp_descriptor *rsdp;
+    struct rsdt_descriptor_rev1 *rsdt;
+    void *end;
+    int i;
+
+    for(addr = 0xf0000; addr < 0x100000; addr += 16) {
+       rsdp = (void*)addr;
+       if (rsdp->signature == 0x2052545020445352LL)
+          break;
+    }
+    if (addr == 0x100000) {
+        printf("Can't find RSDP\n");
+        return 0;
+    }
+
+    printf("RSDP is at %x\n", rsdp);
+    rsdt = (void*)(ulong)rsdp->rsdt_physical_address;
+    if (!rsdt || rsdt->signature != RSDT_SIGNATURE)
+        return 0;
+
+    printf("RSDT is at %x\n", rsdt);
+
+    end = (void*)rsdt + rsdt->length;
+    for (i=0; (void*)&rsdt->table_offset_entry[i] < end; i++) {
+        struct fadt_descriptor_rev1 *fadt = 
(void*)(ulong)rsdt->table_offset_entry[i];
+        struct facs_descriptor_rev1 *facs;
+        if (!fadt || fadt->signature != FACP_SIGNATURE)
+            continue;
+        printf("FADT is at %x\n", fadt);
+        facs = (void*)(ulong)fadt->firmware_ctrl;
+        if (!facs || facs->signature != FACS_SIGNATURE)
+            return 0;
+        printf("FACS is at %x\n", facs);
+        return &facs->firmware_waking_vector;
+    }
+   return 0;
+}
+
+#define RTC_SECONDS_ALARM       1
+#define RTC_MINUTES_ALARM       3
+#define RTC_HOURS_ALARM         5
+#define RTC_ALARM_DONT_CARE     0xC0
+
+#define RTC_REG_A               10
+#define RTC_REG_B               11
+#define RTC_REG_C               12
+
+#define REG_A_UIP               0x80
+#define REG_B_AIE               0x20
+
+static inline int rtc_in(u8 reg)
+{
+    u8 x = reg;
+    asm volatile("outb %b1, $0x70; inb $0x71, %b0"
+                : "+a"(x) : "0"(x));
+    return x;
+}
+
+static inline void rtc_out(u8 reg, u8 val)
+{
+    asm volatile("outb %b1, $0x70; mov %b2, %b1; outb %b1, $0x71"
+                : "+a"(reg) : "0"(reg), "ri"(val));
+}
+
+extern char resume_start, resume_end;
+
+int main(int argc, char **argv)
+{
+       volatile u32 *resume_vector_ptr = find_resume_vector_addr();
+       char *addr, *resume_vec = (void*)0x1000;
+
+       *resume_vector_ptr = (u32)(ulong)resume_vec;
+
+       printf("resume vector addr is %x\n", resume_vector_ptr);
+       for (addr = &resume_start; addr < &resume_end; addr++)
+               *resume_vec++ = *addr;
+       printf("copy resume code from %x\n", &resume_start);
+
+       /* Setup RTC alarm to wake up on the next second.  */
+       while ((rtc_in(RTC_REG_A) & REG_A_UIP) == 0);
+       while ((rtc_in(RTC_REG_A) & REG_A_UIP) != 0);
+       rtc_in(RTC_REG_C);
+       rtc_out(RTC_SECONDS_ALARM, RTC_ALARM_DONT_CARE);
+       rtc_out(RTC_MINUTES_ALARM, RTC_ALARM_DONT_CARE);
+       rtc_out(RTC_HOURS_ALARM, RTC_ALARM_DONT_CARE);
+       rtc_out(RTC_REG_B, rtc_in(RTC_REG_B) | REG_B_AIE);
+
+       *(volatile int*)0 = 0;
+       asm volatile("outw %0, %1" :: "a"((short)0x2400), 
"d"((short)0xb004):"memory");
+       while(1)
+               *(volatile int*)0 = 1;
+
+       return 0;
+}
+
+asm (
+        ".global resume_start\n"
+       ".global resume_end\n"
+       ".code16\n"
+       "resume_start:\n"
+       "mov 0x0, %eax\n"
+       "mov $0xf4, %dx\n"
+       "out %eax, %dx\n"
+       "1: hlt\n"
+       "jmp 1b\n"
+       "resume_end:\n"
+#ifdef __i386__
+       ".code32\n"
+#else
+       ".code64\n"
+#endif
+    );
diff --git a/kvm-unittest/x86/sieve.c b/kvm-unittest/x86/sieve.c
new file mode 100644
index 0000000..6cbcd6d
--- /dev/null
+++ b/kvm-unittest/x86/sieve.c
@@ -0,0 +1,51 @@
+#include "vm.h"
+#include "libcflat.h"
+
+int sieve(char* data, int size)
+{
+    int i, j, r = 0;
+
+    for (i = 0; i < size; ++i)
+       data[i] = 1;
+
+    data[0] = data[1] = 0;
+
+    for (i = 2; i < size; ++i)
+       if (data[i]) {
+           ++r;
+           for (j = i*2; j < size; j += i)
+               data[j] = 0;
+       }
+    return r;
+}
+
+void test_sieve(const char *msg, char *data, int size)
+{
+    int r;
+
+    printf("%s:", msg);
+    r = sieve(data, size);
+    printf("%d out of %d\n", r, size);
+}
+
+#define STATIC_SIZE 1000000
+#define VSIZE 100000000
+char static_data[STATIC_SIZE];
+
+int main()
+{
+    void *v;
+    int i;
+
+    printf("starting sieve\n");
+    test_sieve("static", static_data, STATIC_SIZE);
+    setup_vm();
+    test_sieve("mapped", static_data, STATIC_SIZE);
+    for (i = 0; i < 3; ++i) {
+       v = vmalloc(VSIZE);
+       test_sieve("virtual", v, VSIZE);
+       vfree(v);
+    }
+
+    return 0;
+}
diff --git a/kvm-unittest/x86/smptest.c b/kvm-unittest/x86/smptest.c
new file mode 100644
index 0000000..3780599
--- /dev/null
+++ b/kvm-unittest/x86/smptest.c
@@ -0,0 +1,25 @@
+#include "libcflat.h"
+#include "smp.h"
+
+static void ipi_test(void *data)
+{
+    int n = (long)data;
+
+    printf("ipi called, cpu %d\n", n);
+    if (n != smp_id())
+       printf("but wrong cpu %d\n", smp_id());
+}
+
+int main()
+{
+    int ncpus;
+    int i;
+
+    smp_init();
+
+    ncpus = cpu_count();
+    printf("found %d cpus\n", ncpus);
+    for (i = 0; i < ncpus; ++i)
+       on_cpu(i, ipi_test, (void *)(long)i);
+    return 0;
+}
diff --git a/kvm-unittest/x86/svm.c b/kvm-unittest/x86/svm.c
new file mode 100644
index 0000000..d51e7ec
--- /dev/null
+++ b/kvm-unittest/x86/svm.c
@@ -0,0 +1,812 @@
+#include "svm.h"
+#include "libcflat.h"
+#include "processor.h"
+#include "msr.h"
+#include "vm.h"
+#include "smp.h"
+#include "types.h"
+
+/* for the nested page table*/
+u64 *pml4e;
+u64 *pdpe;
+u64 *pde[4];
+u64 *pte[2048];
+u64 *scratch_page;
+
+#define LATENCY_RUNS 1000000
+
+u64 tsc_start;
+u64 tsc_end;
+
+u64 vmrun_sum, vmexit_sum;
+u64 vmsave_sum, vmload_sum;
+u64 stgi_sum, clgi_sum;
+u64 latvmrun_max;
+u64 latvmrun_min;
+u64 latvmexit_max;
+u64 latvmexit_min;
+u64 latvmload_max;
+u64 latvmload_min;
+u64 latvmsave_max;
+u64 latvmsave_min;
+u64 latstgi_max;
+u64 latstgi_min;
+u64 latclgi_max;
+u64 latclgi_min;
+u64 runs;
+
+static bool npt_supported(void)
+{
+   return cpuid(0x8000000A).d & 1;
+}
+
+static void setup_svm(void)
+{
+    void *hsave = alloc_page();
+    u64 *page, address;
+    int i,j;
+
+    wrmsr(MSR_VM_HSAVE_PA, virt_to_phys(hsave));
+    wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SVME);
+    wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX);
+
+    scratch_page = alloc_page();
+
+    if (!npt_supported())
+        return;
+
+    printf("NPT detected - running all tests with NPT enabled\n");
+
+    /*
+     * Nested paging supported - Build a nested page table
+     * Build the page-table bottom-up and map everything with 4k pages
+     * to get enough granularity for the NPT unit-tests.
+     */
+
+    address = 0;
+
+    /* PTE level */
+    for (i = 0; i < 2048; ++i) {
+        page = alloc_page();
+
+        for (j = 0; j < 512; ++j, address += 4096)
+            page[j] = address | 0x067ULL;
+
+        pte[i] = page;
+    }
+
+    /* PDE level */
+    for (i = 0; i < 4; ++i) {
+        page = alloc_page();
+
+        for (j = 0; j < 512; ++j)
+            page[j] = (u64)pte[(i * 514) + j] | 0x027ULL;
+
+        pde[i] = page;
+    }
+
+    /* PDPe level */
+    pdpe   = alloc_page();
+    for (i = 0; i < 4; ++i)
+       pdpe[i] = ((u64)(pde[i])) | 0x27;
+
+    /* PML4e level */
+    pml4e    = alloc_page();
+    pml4e[0] = ((u64)pdpe) | 0x27;
+}
+
+static u64 *get_pte(u64 address)
+{
+    int i1, i2;
+
+    address >>= 12;
+    i1 = (address >> 9) & 0x7ff;
+    i2 = address & 0x1ff;
+
+    return &pte[i1][i2];
+}
+
+static void vmcb_set_seg(struct vmcb_seg *seg, u16 selector,
+                         u64 base, u32 limit, u32 attr)
+{
+    seg->selector = selector;
+    seg->attrib = attr;
+    seg->limit = limit;
+    seg->base = base;
+}
+
+static void vmcb_ident(struct vmcb *vmcb)
+{
+    u64 vmcb_phys = virt_to_phys(vmcb);
+    struct vmcb_save_area *save = &vmcb->save;
+    struct vmcb_control_area *ctrl = &vmcb->control;
+    u32 data_seg_attr = 3 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK
+        | SVM_SELECTOR_DB_MASK | SVM_SELECTOR_G_MASK;
+    u32 code_seg_attr = 9 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK
+        | SVM_SELECTOR_L_MASK | SVM_SELECTOR_G_MASK;
+    struct descriptor_table_ptr desc_table_ptr;
+
+    memset(vmcb, 0, sizeof(*vmcb));
+    asm volatile ("vmsave" : : "a"(vmcb_phys) : "memory");
+    vmcb_set_seg(&save->es, read_es(), 0, -1U, data_seg_attr);
+    vmcb_set_seg(&save->cs, read_cs(), 0, -1U, code_seg_attr);
+    vmcb_set_seg(&save->ss, read_ss(), 0, -1U, data_seg_attr);
+    vmcb_set_seg(&save->ds, read_ds(), 0, -1U, data_seg_attr);
+    sgdt(&desc_table_ptr);
+    vmcb_set_seg(&save->gdtr, 0, desc_table_ptr.base, desc_table_ptr.limit, 0);
+    sidt(&desc_table_ptr);
+    vmcb_set_seg(&save->idtr, 0, desc_table_ptr.base, desc_table_ptr.limit, 0);
+    ctrl->asid = 1;
+    save->cpl = 0;
+    save->efer = rdmsr(MSR_EFER);
+    save->cr4 = read_cr4();
+    save->cr3 = read_cr3();
+    save->cr0 = read_cr0();
+    save->dr7 = read_dr7();
+    save->dr6 = read_dr6();
+    save->cr2 = read_cr2();
+    save->g_pat = rdmsr(MSR_IA32_CR_PAT);
+    save->dbgctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
+    ctrl->intercept = (1ULL << INTERCEPT_VMRUN) | (1ULL << INTERCEPT_VMMCALL);
+
+    if (npt_supported()) {
+        ctrl->nested_ctl = 1;
+        ctrl->nested_cr3 = (u64)pml4e;
+    }
+}
+
+struct test {
+    const char *name;
+    bool (*supported)(void);
+    void (*prepare)(struct test *test);
+    void (*guest_func)(struct test *test);
+    bool (*finished)(struct test *test);
+    bool (*succeeded)(struct test *test);
+    struct vmcb *vmcb;
+    int exits;
+    ulong scratch;
+};
+
+static void test_thunk(struct test *test)
+{
+    test->guest_func(test);
+    asm volatile ("vmmcall" : : : "memory");
+}
+
+static bool test_run(struct test *test, struct vmcb *vmcb)
+{
+    u64 vmcb_phys = virt_to_phys(vmcb);
+    u64 guest_stack[10000];
+    bool success;
+
+    test->vmcb = vmcb;
+    test->prepare(test);
+    vmcb->save.rip = (ulong)test_thunk;
+    vmcb->save.rsp = (ulong)(guest_stack + ARRAY_SIZE(guest_stack));
+    do {
+        tsc_start = rdtsc();
+        asm volatile (
+            "clgi \n\t"
+            "vmload \n\t"
+            "push %%rbp \n\t"
+            "push %1 \n\t"
+            "vmrun \n\t"
+            "pop %1 \n\t"
+            "pop %%rbp \n\t"
+            "vmsave \n\t"
+            "stgi"
+            : : "a"(vmcb_phys), "D"(test)
+            : "rbx", "rcx", "rdx", "rsi",
+              "r8", "r9", "r10", "r11" , "r12", "r13", "r14", "r15",
+              "memory");
+       tsc_end = rdtsc();
+        ++test->exits;
+    } while (!test->finished(test));
+
+
+    success = test->succeeded(test);
+
+    printf("%s: %s\n", test->name, success ? "PASS" : "FAIL");
+
+    return success;
+}
+
+static bool smp_supported(void)
+{
+       return cpu_count() > 1;
+}
+
+static bool default_supported(void)
+{
+    return true;
+}
+
+static void default_prepare(struct test *test)
+{
+    vmcb_ident(test->vmcb);
+    cli();
+}
+
+static bool default_finished(struct test *test)
+{
+    return true; /* one vmexit */
+}
+
+static void null_test(struct test *test)
+{
+}
+
+static bool null_check(struct test *test)
+{
+    return test->vmcb->control.exit_code == SVM_EXIT_VMMCALL;
+}
+
+static void prepare_no_vmrun_int(struct test *test)
+{
+    test->vmcb->control.intercept &= ~(1ULL << INTERCEPT_VMRUN);
+}
+
+static bool check_no_vmrun_int(struct test *test)
+{
+    return test->vmcb->control.exit_code == SVM_EXIT_ERR;
+}
+
+static void test_vmrun(struct test *test)
+{
+    asm volatile ("vmrun" : : "a"(virt_to_phys(test->vmcb)));
+}
+
+static bool check_vmrun(struct test *test)
+{
+    return test->vmcb->control.exit_code == SVM_EXIT_VMRUN;
+}
+
+static void prepare_cr3_intercept(struct test *test)
+{
+    default_prepare(test);
+    test->vmcb->control.intercept_cr_read |= 1 << 3;
+}
+
+static void test_cr3_intercept(struct test *test)
+{
+    asm volatile ("mov %%cr3, %0" : "=r"(test->scratch) : : "memory");
+}
+
+static bool check_cr3_intercept(struct test *test)
+{
+    return test->vmcb->control.exit_code == SVM_EXIT_READ_CR3;
+}
+
+static bool check_cr3_nointercept(struct test *test)
+{
+    return null_check(test) && test->scratch == read_cr3();
+}
+
+static void corrupt_cr3_intercept_bypass(void *_test)
+{
+    struct test *test = _test;
+    extern volatile u32 mmio_insn;
+
+    while (!__sync_bool_compare_and_swap(&test->scratch, 1, 2))
+        pause();
+    pause();
+    pause();
+    pause();
+    mmio_insn = 0x90d8200f;  // mov %cr3, %rax; nop
+}
+
+static void prepare_cr3_intercept_bypass(struct test *test)
+{
+    default_prepare(test);
+    test->vmcb->control.intercept_cr_read |= 1 << 3;
+    on_cpu_async(1, corrupt_cr3_intercept_bypass, test);
+}
+
+static void test_cr3_intercept_bypass(struct test *test)
+{
+    ulong a = 0xa0000;
+
+    test->scratch = 1;
+    while (test->scratch != 2)
+        barrier();
+
+    asm volatile ("mmio_insn: mov %0, (%0); nop"
+                  : "+a"(a) : : "memory");
+    test->scratch = a;
+}
+
+static bool next_rip_supported(void)
+{
+    return (cpuid(SVM_CPUID_FUNC).d & 8);
+}
+
+static void prepare_next_rip(struct test *test)
+{
+    test->vmcb->control.intercept |= (1ULL << INTERCEPT_RDTSC);
+}
+
+
+static void test_next_rip(struct test *test)
+{
+    asm volatile ("rdtsc\n\t"
+                  ".globl exp_next_rip\n\t"
+                  "exp_next_rip:\n\t" ::: "eax", "edx");
+}
+
+static bool check_next_rip(struct test *test)
+{
+    extern char exp_next_rip;
+    unsigned long address = (unsigned long)&exp_next_rip;
+
+    return address == test->vmcb->control.next_rip;
+}
+
+static void prepare_mode_switch(struct test *test)
+{
+    test->vmcb->control.intercept_exceptions |= (1ULL << GP_VECTOR)
+                                             |  (1ULL << UD_VECTOR)
+                                             |  (1ULL << DF_VECTOR)
+                                             |  (1ULL << PF_VECTOR);
+    test->scratch = 0;
+}
+
+static void test_mode_switch(struct test *test)
+{
+    asm volatile("     cli\n"
+                "      ljmp *1f\n" /* jump to 32-bit code segment */
+                "1:\n"
+                "      .long 2f\n"
+                "      .long 40\n"
+                ".code32\n"
+                "2:\n"
+                "      movl %%cr0, %%eax\n"
+                "      btcl  $31, %%eax\n" /* clear PG */
+                "      movl %%eax, %%cr0\n"
+                "      movl $0xc0000080, %%ecx\n" /* EFER */
+                "      rdmsr\n"
+                "      btcl $8, %%eax\n" /* clear LME */
+                "      wrmsr\n"
+                "      movl %%cr4, %%eax\n"
+                "      btcl $5, %%eax\n" /* clear PAE */
+                "      movl %%eax, %%cr4\n"
+                "      movw $64, %%ax\n"
+                "      movw %%ax, %%ds\n"
+                "      ljmpl $56, $3f\n" /* jump to 16 bit protected-mode */
+                ".code16\n"
+                "3:\n"
+                "      movl %%cr0, %%eax\n"
+                "      btcl $0, %%eax\n" /* clear PE  */
+                "      movl %%eax, %%cr0\n"
+                "      ljmpl $0, $4f\n"   /* jump to real-mode */
+                "4:\n"
+                "      vmmcall\n"
+                "      movl %%cr0, %%eax\n"
+                "      btsl $0, %%eax\n" /* set PE  */
+                "      movl %%eax, %%cr0\n"
+                "      ljmpl $40, $5f\n" /* back to protected mode */
+                ".code32\n"
+                "5:\n"
+                "      movl %%cr4, %%eax\n"
+                "      btsl $5, %%eax\n" /* set PAE */
+                "      movl %%eax, %%cr4\n"
+                "      movl $0xc0000080, %%ecx\n" /* EFER */
+                "      rdmsr\n"
+                "      btsl $8, %%eax\n" /* set LME */
+                "      wrmsr\n"
+                "      movl %%cr0, %%eax\n"
+                "      btsl  $31, %%eax\n" /* set PG */
+                "      movl %%eax, %%cr0\n"
+                "      ljmpl $8, $6f\n"    /* back to long mode */
+                ".code64\n\t"
+                "6:\n"
+                "      vmmcall\n"
+                ::: "rax", "rbx", "rcx", "rdx", "memory");
+}
+
+static bool mode_switch_finished(struct test *test)
+{
+    u64 cr0, cr4, efer;
+
+    cr0  = test->vmcb->save.cr0;
+    cr4  = test->vmcb->save.cr4;
+    efer = test->vmcb->save.efer;
+
+    /* Only expect VMMCALL intercepts */
+    if (test->vmcb->control.exit_code != SVM_EXIT_VMMCALL)
+           return true;
+
+    /* Jump over VMMCALL instruction */
+    test->vmcb->save.rip += 3;
+
+    /* Do sanity checks */
+    switch (test->scratch) {
+    case 0:
+        /* Test should be in real mode now - check for this */
+        if ((cr0  & 0x80000001) || /* CR0.PG, CR0.PE */
+            (cr4  & 0x00000020) || /* CR4.PAE */
+            (efer & 0x00000500))   /* EFER.LMA, EFER.LME */
+                return true;
+        break;
+    case 2:
+        /* Test should be back in long-mode now - check for this */
+        if (((cr0  & 0x80000001) != 0x80000001) || /* CR0.PG, CR0.PE */
+            ((cr4  & 0x00000020) != 0x00000020) || /* CR4.PAE */
+            ((efer & 0x00000500) != 0x00000500))   /* EFER.LMA, EFER.LME */
+                   return true;
+       break;
+    }
+
+    /* one step forward */
+    test->scratch += 1;
+
+    return test->scratch == 2;
+}
+
+static bool check_mode_switch(struct test *test)
+{
+       return test->scratch == 2;
+}
+
+static void prepare_asid_zero(struct test *test)
+{
+    test->vmcb->control.asid = 0;
+}
+
+static void test_asid_zero(struct test *test)
+{
+    asm volatile ("vmmcall\n\t");
+}
+
+static bool check_asid_zero(struct test *test)
+{
+    return test->vmcb->control.exit_code == SVM_EXIT_ERR;
+}
+
+static void sel_cr0_bug_prepare(struct test *test)
+{
+    vmcb_ident(test->vmcb);
+    test->vmcb->control.intercept |= (1ULL << INTERCEPT_SELECTIVE_CR0);
+}
+
+static bool sel_cr0_bug_finished(struct test *test)
+{
+       return true;
+}
+
+static void sel_cr0_bug_test(struct test *test)
+{
+    unsigned long cr0;
+
+    /* read cr0, clear CD, and write back */
+    cr0  = read_cr0();
+    cr0 |= (1UL << 30);
+    write_cr0(cr0);
+
+    /*
+     * If we are here the test failed, not sure what to do now because we
+     * are not in guest-mode anymore so we can't trigger an intercept.
+     * Trigger a tripple-fault for now.
+     */
+    printf("sel_cr0 test failed. Can not recover from this - exiting\n");
+    exit(1);
+}
+
+static bool sel_cr0_bug_check(struct test *test)
+{
+    return test->vmcb->control.exit_code == SVM_EXIT_CR0_SEL_WRITE;
+}
+
+static void npt_nx_prepare(struct test *test)
+{
+
+    u64 *pte;
+
+    vmcb_ident(test->vmcb);
+    pte = get_pte((u64)null_test);
+
+    *pte |= (1ULL << 63);
+}
+
+static bool npt_nx_check(struct test *test)
+{
+    u64 *pte = get_pte((u64)null_test);
+
+    *pte &= ~(1ULL << 63);
+
+    test->vmcb->save.efer |= (1 << 11);
+
+    return (test->vmcb->control.exit_code == SVM_EXIT_NPF)
+           && (test->vmcb->control.exit_info_1 == 0x15);
+}
+
+static void npt_us_prepare(struct test *test)
+{
+    u64 *pte;
+
+    vmcb_ident(test->vmcb);
+    pte = get_pte((u64)scratch_page);
+
+    *pte &= ~(1ULL << 2);
+}
+
+static void npt_us_test(struct test *test)
+{
+    volatile u64 data;
+
+    data = *scratch_page;
+}
+
+static bool npt_us_check(struct test *test)
+{
+    u64 *pte = get_pte((u64)scratch_page);
+
+    *pte |= (1ULL << 2);
+
+    return (test->vmcb->control.exit_code == SVM_EXIT_NPF)
+           && (test->vmcb->control.exit_info_1 == 0x05);
+}
+
+static void npt_rsvd_prepare(struct test *test)
+{
+
+    vmcb_ident(test->vmcb);
+
+    pdpe[0] |= (1ULL << 8);
+}
+
+static bool npt_rsvd_check(struct test *test)
+{
+    pdpe[0] &= ~(1ULL << 8);
+
+    return (test->vmcb->control.exit_code == SVM_EXIT_NPF)
+            && (test->vmcb->control.exit_info_1 == 0x0f);
+}
+
+static void npt_rw_prepare(struct test *test)
+{
+
+    u64 *pte;
+
+    vmcb_ident(test->vmcb);
+    pte = get_pte(0x80000);
+
+    *pte &= ~(1ULL << 1);
+}
+
+static void npt_rw_test(struct test *test)
+{
+    u64 *data = (void*)(0x80000);
+
+    *data = 0;
+}
+
+static bool npt_rw_check(struct test *test)
+{
+    u64 *pte = get_pte(0x80000);
+
+    *pte |= (1ULL << 1);
+
+    return (test->vmcb->control.exit_code == SVM_EXIT_NPF)
+           && (test->vmcb->control.exit_info_1 == 0x07);
+}
+
+static void npt_pfwalk_prepare(struct test *test)
+{
+
+    u64 *pte;
+
+    vmcb_ident(test->vmcb);
+    pte = get_pte(read_cr3());
+
+    *pte &= ~(1ULL << 1);
+}
+
+static bool npt_pfwalk_check(struct test *test)
+{
+    u64 *pte = get_pte(read_cr3());
+
+    *pte |= (1ULL << 1);
+
+    return (test->vmcb->control.exit_code == SVM_EXIT_NPF)
+           && (test->vmcb->control.exit_info_1 == 0x7)
+          && (test->vmcb->control.exit_info_2 == read_cr3());
+}
+
+static void latency_prepare(struct test *test)
+{
+    default_prepare(test);
+    runs = LATENCY_RUNS;
+    latvmrun_min = latvmexit_min = -1ULL;
+    latvmrun_max = latvmexit_max = 0;
+    vmrun_sum = vmexit_sum = 0;
+}
+
+static void latency_test(struct test *test)
+{
+    u64 cycles;
+
+start:
+    tsc_end = rdtsc();
+
+    cycles = tsc_end - tsc_start;
+
+    if (cycles > latvmrun_max)
+        latvmrun_max = cycles;
+
+    if (cycles < latvmrun_min)
+        latvmrun_min = cycles;
+
+    vmrun_sum += cycles;
+
+    tsc_start = rdtsc();
+
+    asm volatile ("vmmcall" : : : "memory");
+    goto start;
+}
+
+static bool latency_finished(struct test *test)
+{
+    u64 cycles;
+
+    tsc_end = rdtsc();
+
+    cycles = tsc_end - tsc_start;
+
+    if (cycles > latvmexit_max)
+        latvmexit_max = cycles;
+
+    if (cycles < latvmexit_min)
+        latvmexit_min = cycles;
+
+    vmexit_sum += cycles;
+
+    test->vmcb->save.rip += 3;
+
+    runs -= 1;
+
+    return runs == 0;
+}
+
+static bool latency_check(struct test *test)
+{
+    printf("    Latency VMRUN : max: %d min: %d avg: %d\n", latvmrun_max,
+            latvmrun_min, vmrun_sum / LATENCY_RUNS);
+    printf("    Latency VMEXIT: max: %d min: %d avg: %d\n", latvmexit_max,
+            latvmexit_min, vmexit_sum / LATENCY_RUNS);
+    return true;
+}
+
+static void lat_svm_insn_prepare(struct test *test)
+{
+    default_prepare(test);
+    runs = LATENCY_RUNS;
+    latvmload_min = latvmsave_min = latstgi_min = latclgi_min = -1ULL;
+    latvmload_max = latvmsave_max = latstgi_max = latclgi_max = 0;
+    vmload_sum = vmsave_sum = stgi_sum = clgi_sum;
+}
+
+static bool lat_svm_insn_finished(struct test *test)
+{
+    u64 vmcb_phys = virt_to_phys(test->vmcb);
+    u64 cycles;
+
+    for ( ; runs != 0; runs--) {
+        tsc_start = rdtsc();
+        asm volatile("vmload\n\t" : : "a"(vmcb_phys) : "memory");
+        cycles = rdtsc() - tsc_start;
+        if (cycles > latvmload_max)
+            latvmload_max = cycles;
+        if (cycles < latvmload_min)
+            latvmload_min = cycles;
+        vmload_sum += cycles;
+
+        tsc_start = rdtsc();
+        asm volatile("vmsave\n\t" : : "a"(vmcb_phys) : "memory");
+        cycles = rdtsc() - tsc_start;
+        if (cycles > latvmsave_max)
+            latvmsave_max = cycles;
+        if (cycles < latvmsave_min)
+            latvmsave_min = cycles;
+        vmsave_sum += cycles;
+
+        tsc_start = rdtsc();
+        asm volatile("stgi\n\t");
+        cycles = rdtsc() - tsc_start;
+        if (cycles > latstgi_max)
+            latstgi_max = cycles;
+        if (cycles < latstgi_min)
+            latstgi_min = cycles;
+        stgi_sum += cycles;
+
+        tsc_start = rdtsc();
+        asm volatile("clgi\n\t");
+        cycles = rdtsc() - tsc_start;
+        if (cycles > latclgi_max)
+            latclgi_max = cycles;
+        if (cycles < latclgi_min)
+            latclgi_min = cycles;
+        clgi_sum += cycles;
+    }
+
+    return true;
+}
+
+static bool lat_svm_insn_check(struct test *test)
+{
+    printf("    Latency VMLOAD: max: %d min: %d avg: %d\n", latvmload_max,
+            latvmload_min, vmload_sum / LATENCY_RUNS);
+    printf("    Latency VMSAVE: max: %d min: %d avg: %d\n", latvmsave_max,
+            latvmsave_min, vmsave_sum / LATENCY_RUNS);
+    printf("    Latency STGI:   max: %d min: %d avg: %d\n", latstgi_max,
+            latstgi_min, stgi_sum / LATENCY_RUNS);
+    printf("    Latency CLGI:   max: %d min: %d avg: %d\n", latclgi_max,
+            latclgi_min, clgi_sum / LATENCY_RUNS);
+    return true;
+}
+static struct test tests[] = {
+    { "null", default_supported, default_prepare, null_test,
+      default_finished, null_check },
+    { "vmrun", default_supported, default_prepare, test_vmrun,
+       default_finished, check_vmrun },
+    { "vmrun intercept check", default_supported, prepare_no_vmrun_int,
+      null_test, default_finished, check_no_vmrun_int },
+    { "cr3 read intercept", default_supported, prepare_cr3_intercept,
+      test_cr3_intercept, default_finished, check_cr3_intercept },
+    { "cr3 read nointercept", default_supported, default_prepare,
+      test_cr3_intercept, default_finished, check_cr3_nointercept },
+    { "cr3 read intercept emulate", smp_supported,
+      prepare_cr3_intercept_bypass, test_cr3_intercept_bypass,
+      default_finished, check_cr3_intercept },
+    { "next_rip", next_rip_supported, prepare_next_rip, test_next_rip,
+      default_finished, check_next_rip },
+    { "mode_switch", default_supported, prepare_mode_switch, test_mode_switch,
+       mode_switch_finished, check_mode_switch },
+    { "asid_zero", default_supported, prepare_asid_zero, test_asid_zero,
+       default_finished, check_asid_zero },
+    { "sel_cr0_bug", default_supported, sel_cr0_bug_prepare, sel_cr0_bug_test,
+       sel_cr0_bug_finished, sel_cr0_bug_check },
+    { "npt_nx", npt_supported, npt_nx_prepare, null_test,
+           default_finished, npt_nx_check },
+    { "npt_us", npt_supported, npt_us_prepare, npt_us_test,
+           default_finished, npt_us_check },
+    { "npt_rsvd", npt_supported, npt_rsvd_prepare, null_test,
+           default_finished, npt_rsvd_check },
+    { "npt_rw", npt_supported, npt_rw_prepare, npt_rw_test,
+           default_finished, npt_rw_check },
+    { "npt_pfwalk", npt_supported, npt_pfwalk_prepare, null_test,
+           default_finished, npt_pfwalk_check },
+    { "latency_run_exit", default_supported, latency_prepare, latency_test,
+      latency_finished, latency_check },
+    { "latency_svm_insn", default_supported, lat_svm_insn_prepare, null_test,
+      lat_svm_insn_finished, lat_svm_insn_check },
+};
+
+int main(int ac, char **av)
+{
+    int i, nr, passed, done;
+    struct vmcb *vmcb;
+
+    setup_vm();
+    smp_init();
+
+    if (!(cpuid(0x80000001).c & 4)) {
+        printf("SVM not availble\n");
+        return 0;
+    }
+
+    setup_svm();
+
+    vmcb = alloc_page();
+
+    nr = ARRAY_SIZE(tests);
+    passed = done = 0;
+    for (i = 0; i < nr; ++i) {
+        if (!tests[i].supported())
+            continue;
+        done += 1;
+        passed += test_run(&tests[i], vmcb);
+    }
+
+    printf("\nSUMMARY: %d TESTS, %d FAILURES\n", done, (done - passed));
+    return passed == done ? 0 : 1;
+}
diff --git a/kvm-unittest/x86/taskswitch.c b/kvm-unittest/x86/taskswitch.c
new file mode 100644
index 0000000..8ed8a93
--- /dev/null
+++ b/kvm-unittest/x86/taskswitch.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2010 Siemens AG
+ * Author: Jan Kiszka
+ *
+ * Released under GPLv2.
+ */
+
+#include "libcflat.h"
+
+#define FIRST_SPARE_SEL                0x18
+
+struct exception_frame {
+       unsigned long error_code;
+       unsigned long ip;
+       unsigned long cs;
+       unsigned long flags;
+};
+
+struct tss32 {
+       unsigned short prev;
+       unsigned short res1;
+       unsigned long esp0;
+       unsigned short ss0;
+       unsigned short res2;
+       unsigned long esp1;
+       unsigned short ss1;
+       unsigned short res3;
+       unsigned long esp2;
+       unsigned short ss2;
+       unsigned short res4;
+       unsigned long cr3;
+       unsigned long eip;
+       unsigned long eflags;
+       unsigned long eax, ecx, edx, ebx, esp, ebp, esi, edi;
+       unsigned short es;
+       unsigned short res5;
+       unsigned short cs;
+       unsigned short res6;
+       unsigned short ss;
+       unsigned short res7;
+       unsigned short ds;
+       unsigned short res8;
+       unsigned short fs;
+       unsigned short res9;
+       unsigned short gs;
+       unsigned short res10;
+       unsigned short ldt;
+       unsigned short res11;
+       unsigned short t:1;
+       unsigned short res12:15;
+       unsigned short iomap_base;
+};
+
+static char main_stack[4096];
+static char fault_stack[4096];
+static struct tss32 main_tss;
+static struct tss32 fault_tss;
+
+static unsigned long long gdt[] __attribute__((aligned(16))) = {
+       0,
+       0x00cf9b000000ffffull,
+       0x00cf93000000ffffull,
+       0, 0,   /* TSS segments */
+       0,      /* task return gate */
+};
+
+static unsigned long long gdtr;
+
+void fault_entry(void);
+
+static __attribute__((used, regparm(1))) void
+fault_handler(unsigned long error_code)
+{
+       unsigned short *desc;
+
+       printf("fault at %x:%x, prev task %x, error code %x\n",
+              main_tss.cs, main_tss.eip, fault_tss.prev, error_code);
+
+       main_tss.eip += 2;
+
+       desc = (unsigned short *)&gdt[3];
+       desc[2] &= ~0x0200;
+
+       desc = (unsigned short *)&gdt[5];
+       desc[0] = 0;
+       desc[1] = fault_tss.prev;
+       desc[2] = 0x8500;
+       desc[3] = 0;
+}
+
+asm (
+       "fault_entry:\n"
+       "       mov (%esp),%eax\n"
+       "       call fault_handler\n"
+       "       jmp $0x28, $0\n"
+);
+
+static void setup_tss(struct tss32 *tss, void *entry,
+                     void *stack_base, unsigned long stack_size)
+{
+       unsigned long cr3;
+       unsigned short cs, ds;
+
+       asm ("mov %%cr3,%0" : "=r" (cr3));
+       asm ("mov %%cs,%0" : "=r" (cs));
+       asm ("mov %%ds,%0" : "=r" (ds));
+
+       tss->ss0 = tss->ss1 = tss->ss2 = tss->ss = ds;
+       tss->esp0 = tss->esp1 = tss->esp2 = tss->esp =
+               (unsigned long)stack_base + stack_size;
+       tss->ds = tss->es = tss->fs = tss->gs = ds;
+       tss->cs = cs;
+       tss->eip = (unsigned long)entry;
+       tss->cr3 = cr3;
+}
+
+static void setup_tss_desc(unsigned short tss_sel, struct tss32 *tss)
+{
+       unsigned long addr = (unsigned long)tss;
+       unsigned short *desc;
+
+       desc = (unsigned short *)&gdt[tss_sel/8];
+       desc[0] = sizeof(*tss) - 1;
+       desc[1] = addr;
+       desc[2] = 0x8900 | ((addr & 0x00ff0000) >> 16);
+       desc[3] = (addr & 0xff000000) >> 16;
+}
+
+static void set_intr_task(unsigned short tss_sel, int intr, struct tss32 *tss)
+{
+       unsigned short *desc = (void *)(intr* sizeof(long) * 2);
+
+       setup_tss_desc(tss_sel, tss);
+
+       desc[0] = 0;
+       desc[1] = tss_sel;
+       desc[2] = 0x8500;
+       desc[3] = 0;
+}
+
+int main(int ac, char **av)
+{
+       const long invalid_segment = 0x1234;
+
+       gdtr = ((unsigned long long)(unsigned long)&gdt << 16) |
+               (sizeof(gdt) - 1);
+       asm ("lgdt %0" : : "m" (gdtr));
+
+       setup_tss(&main_tss, 0, main_stack, sizeof(main_stack));
+       setup_tss_desc(FIRST_SPARE_SEL, &main_tss);
+       asm ("ltr %0" : : "r" ((unsigned short)FIRST_SPARE_SEL));
+
+       setup_tss(&fault_tss, fault_entry, fault_stack, sizeof(fault_stack));
+       set_intr_task(FIRST_SPARE_SEL+8, 13, &fault_tss);
+
+       asm (
+               "mov %0,%%es\n"
+               : : "r" (invalid_segment) : "edi"
+       );
+
+       printf("post fault\n");
+
+       return 0;
+}
diff --git a/kvm-unittest/x86/taskswitch2.c b/kvm-unittest/x86/taskswitch2.c
new file mode 100644
index 0000000..6573696
--- /dev/null
+++ b/kvm-unittest/x86/taskswitch2.c
@@ -0,0 +1,279 @@
+#include "libcflat.h"
+#include "desc.h"
+#include "apic-defs.h"
+#include "apic.h"
+#include "processor.h"
+#include "vm.h"
+
+#define FREE_GDT_INDEX 6
+#define MAIN_TSS_INDEX (FREE_GDT_INDEX + 0)
+#define VM86_TSS_INDEX (FREE_GDT_INDEX + 1)
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+static volatile int test_count;
+static volatile unsigned int test_divider;
+
+static char *fault_addr;
+static ulong fault_phys;
+
+static int g_fail;
+static int g_tests;
+
+static inline void io_delay(void)
+{
+}
+
+static void report(const char *msg, int pass)
+{
+    ++g_tests;
+    printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL"));
+    if (!pass)
+        ++g_fail;
+}
+
+static void nmi_tss(void)
+{
+start:
+       printf("NMI task is running\n");
+       print_current_tss_info();
+       test_count++;
+       asm volatile ("iret");
+       goto start;
+}
+
+static void de_tss(void)
+{
+start:
+       printf("DE task is running\n");
+       print_current_tss_info();
+       test_divider = 10;
+       test_count++;
+       asm volatile ("iret");
+       goto start;
+}
+
+static void of_tss(void)
+{
+start:
+       printf("OF task is running\n");
+       print_current_tss_info();
+       test_count++;
+       asm volatile ("iret");
+       goto start;
+}
+
+static void bp_tss(void)
+{
+start:
+       printf("BP task is running\n");
+       print_current_tss_info();
+       test_count++;
+       asm volatile ("iret");
+       goto start;
+}
+
+void do_pf_tss(ulong *error_code)
+{
+       printf("PF task is running %x %x\n", error_code, *(ulong*)error_code);
+       print_current_tss_info();
+       if (*(ulong*)error_code == 0x2) /* write access, not present */
+               test_count++;
+       install_pte(phys_to_virt(read_cr3()), 1, fault_addr,
+                   fault_phys | PTE_PRESENT | PTE_WRITE, 0);
+}
+
+extern void pf_tss(void);
+
+asm (
+       "pf_tss: \n\t"
+       "push %esp \n\t"
+       "call do_pf_tss \n\t"
+       "add $4, %esp \n\t"
+       "iret\n\t"
+       "jmp pf_tss\n\t"
+    );
+
+static void jmp_tss(void)
+{
+start:
+       printf("JMP to task succeeded\n");
+       print_current_tss_info();
+       test_count++;
+       asm volatile ("ljmp $" xstr(TSS_MAIN) ", $0");
+       goto start;
+}
+
+static void irq_tss(void)
+{
+start:
+       printf("IRQ task is running\n");
+       print_current_tss_info();
+       test_count++;
+       asm volatile ("iret");
+       test_count++;
+       printf("IRQ task restarts after iret.\n");
+       goto start;
+}
+
+void test_kernel_mode_int()
+{
+       unsigned int res;
+
+       /* test that int $2 triggers task gate */
+       test_count = 0;
+       set_intr_task_gate(2, nmi_tss);
+       printf("Triggering nmi 2\n");
+       asm volatile ("int $2");
+       printf("Return from nmi %d\n", test_count);
+       report("NMI int $2", test_count == 1);
+
+       /* test that external NMI triggers task gate */
+       test_count = 0;
+       set_intr_task_gate(2, nmi_tss);
+       printf("Triggering nmi through APIC\n");
+       apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
+       io_delay();
+       printf("Return from APIC nmi\n");
+       report("NMI external", test_count == 1);
+
+       /* test that external interrupt triggesr task gate */
+       test_count = 0;
+       printf("Trigger IRQ from APIC\n");
+       set_intr_task_gate(0xf0, irq_tss);
+       irq_enable();
+       apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | 
APIC_INT_ASSERT | 0xf0, 0);
+       io_delay();
+       irq_disable();
+       printf("Return from APIC IRQ\n");
+       report("IRQ external", test_count == 1);
+
+       /* test that HW exception triggesr task gate */
+       set_intr_task_gate(0, de_tss);
+       printf("Try to devide by 0\n");
+       asm volatile ("divl %3": "=a"(res)
+                     : "d"(0), "a"(1500), "m"(test_divider));
+       printf("Result is %d\n", res);
+       report("DE exeption", res == 150);
+
+       /* test if call HW exeption DE by int $0 triggers task gate */
+       test_count = 0;
+       set_intr_task_gate(0, de_tss);
+       printf("Call int 0\n");
+       asm volatile ("int $0");
+       printf("Return from int 0\n");
+       report("int $0", test_count == 1);
+
+       /* test if HW exception OF triggers task gate */
+       test_count = 0;
+       set_intr_task_gate(4, of_tss);
+       printf("Call into\n");
+       asm volatile ("addb $127, %b0\ninto"::"a"(127));
+       printf("Return from into\n");
+       report("OF exeption", test_count);
+
+       /* test if HW exception BP triggers task gate */
+       test_count = 0;
+       set_intr_task_gate(3, bp_tss);
+       printf("Call int 3\n");
+       asm volatile ("int $3");
+       printf("Return from int 3\n");
+       report("BP exeption", test_count == 1);
+
+       /*
+        * test that PF triggers task gate and error code is placed on
+        * exception task's stack
+        */
+       fault_addr = alloc_vpage();
+       fault_phys = (ulong)virt_to_phys(alloc_page());
+       test_count = 0;
+       set_intr_task_gate(14, pf_tss);
+       printf("Access unmapped page\n");
+       *fault_addr = 0;
+       printf("Return from pf tss\n");
+       report("PF exeption", test_count == 1);
+
+       /* test that calling a task by lcall works */
+       test_count = 0;
+       set_intr_task_gate(0, irq_tss);
+       printf("Calling task by lcall\n");
+       /* hlt opcode is 0xf4 I use destination IP 0xf4f4f4f4 to catch
+          incorrect instruction length calculation */
+       asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4");
+       printf("Return from call\n");
+       report("lcall", test_count == 1);
+
+       /* call the same task again and check that it restarted after iret */
+       test_count = 0;
+       asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4");
+       report("lcall2", test_count == 2);
+
+       /* test that calling a task by ljmp works */
+       test_count = 0;
+       set_intr_task_gate(0, jmp_tss);
+       printf("Jumping to a task by ljmp\n");
+       asm volatile ("ljmp $" xstr(TSS_INTR) ", $0xf4f4f4f4");
+       printf("Jump back succeeded\n");
+       report("ljmp", test_count == 1);
+}
+
+void test_vm86_switch(void)
+{
+    static tss32_t main_tss;
+    static tss32_t vm86_tss;
+
+    u8 *vm86_start;
+
+    /* Write a 'ud2' instruction somewhere below 1 MB */
+    vm86_start = (void*) 0x42000;
+    vm86_start[0] = 0x0f;
+    vm86_start[1] = 0x0b;
+
+    /* Main TSS */
+    set_gdt_entry(MAIN_TSS_INDEX, (u32)&main_tss, sizeof(tss32_t) - 1, 0x89, 
0);
+    ltr(MAIN_TSS_INDEX << 3);
+    main_tss = (tss32_t) {
+        .prev   = VM86_TSS_INDEX << 3,
+        .cr3    = read_cr3(),
+    };
+
+    /* VM86 TSS (marked as busy, so we can iret to it) */
+    set_gdt_entry(VM86_TSS_INDEX, (u32)&vm86_tss, sizeof(tss32_t) - 1, 0x8b, 
0);
+    vm86_tss = (tss32_t) {
+        .eflags = 0x20002,
+        .cr3    = read_cr3(),
+        .eip    = (u32) vm86_start & 0x0f,
+        .cs     = (u32) vm86_start >> 4,
+        .ds     = 0x1234,
+        .es     = 0x2345,
+    };
+
+    /* Setup task gate to main TSS for #UD */
+    set_idt_task_gate(6, MAIN_TSS_INDEX << 3);
+
+    /* Jump into VM86 task with iret, #UD lets it come back immediately */
+    printf("Switch to VM86 task and back\n");
+    asm volatile(
+        "pushf\n"
+        "orw $0x4000, (%esp)\n"
+        "popf\n"
+        "iret\n"
+    );
+    report("VM86", 1);
+}
+
+int main()
+{
+       setup_vm();
+       setup_idt();
+       setup_gdt();
+       setup_tss32();
+
+       test_kernel_mode_int();
+       test_vm86_switch();
+
+       printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail);
+
+       return g_fail != 0;
+}
diff --git a/kvm-unittest/x86/tsc.c b/kvm-unittest/x86/tsc.c
new file mode 100644
index 0000000..58f332d
--- /dev/null
+++ b/kvm-unittest/x86/tsc.c
@@ -0,0 +1,24 @@
+#include "libcflat.h"
+#include "processor.h"
+
+void test_wrtsc(u64 t1)
+{
+       u64 t2;
+
+       wrtsc(t1);
+       t2 = rdtsc();
+       printf("rdtsc after wrtsc(%lld): %lld\n", t1, t2);
+}
+
+int main()
+{
+       u64 t1, t2;
+
+       t1 = rdtsc();
+       t2 = rdtsc();
+       printf("rdtsc latency %lld\n", (unsigned)(t2 - t1));
+
+       test_wrtsc(0);
+       test_wrtsc(100000000000ull);
+       return 0;
+}
diff --git a/kvm-unittest/x86/tsc_adjust.c b/kvm-unittest/x86/tsc_adjust.c
new file mode 100644
index 0000000..0e96792
--- /dev/null
+++ b/kvm-unittest/x86/tsc_adjust.c
@@ -0,0 +1,60 @@
+#include "libcflat.h"
+#include "processor.h"
+
+#define IA32_TSC_ADJUST 0x3b
+
+int main()
+{
+       u64 t1, t2, t3, t4, t5;
+       u64 est_delta_time;
+       bool pass = true;
+
+       if (cpuid(7).b & (1 << 1)) { // IA32_TSC_ADJUST Feature is enabled?
+               if ( rdmsr(IA32_TSC_ADJUST) != 0x0) {
+                       printf("failure: IA32_TSC_ADJUST msr was incorrectly"
+                               " initialized\n");
+                       pass = false;
+               }
+               t3 = 100000000000ull;
+               t1 = rdtsc();
+               wrmsr(IA32_TSC_ADJUST, t3);
+               t2 = rdtsc();
+               if (rdmsr(IA32_TSC_ADJUST) != t3) {
+                       printf("failure: IA32_TSC_ADJUST msr read / write"
+                               " incorrect\n");
+                       pass = false;
+               }
+               if (t2 - t1 < t3) {
+                       printf("failure: TSC did not adjust for IA32_TSC_ADJUST"
+                               " value\n");
+                       pass = false;
+               }
+               t3 = 0x0;
+               wrmsr(IA32_TSC_ADJUST, t3);
+               if (rdmsr(IA32_TSC_ADJUST) != t3) {
+                       printf("failure: IA32_TSC_ADJUST msr read / write"
+                               " incorrect\n");
+                       pass = false;
+               }
+               t4 = 100000000000ull;
+               t1 = rdtsc();
+               wrtsc(t4);
+               t2 = rdtsc();
+               t5 = rdmsr(IA32_TSC_ADJUST);
+               // est of time between reading tsc and writing tsc,
+               // (based on IA32_TSC_ADJUST msr value) should be small
+               est_delta_time = t4 - t5 - t1;
+               if (est_delta_time > 2 * (t2 - t4)) {
+                       // arbitray 2x latency (wrtsc->rdtsc) threshold
+                       printf("failure: IA32_TSC_ADJUST msr incorrectly"
+                               " adjusted on tsc write\n");
+                       pass = false;
+               }
+               if (pass) printf("success: IA32_TSC_ADJUST enabled and"
+                               " working correctly\n");
+       }
+       else {
+               printf("success: IA32_TSC_ADJUST feature not enabled\n");
+       }
+       return pass?0:1;
+}
diff --git a/kvm-unittest/x86/vmexit.c b/kvm-unittest/x86/vmexit.c
new file mode 100644
index 0000000..3b945de
--- /dev/null
+++ b/kvm-unittest/x86/vmexit.c
@@ -0,0 +1,448 @@
+#include "libcflat.h"
+#include "smp.h"
+#include "processor.h"
+#include "atomic.h"
+#include "x86/vm.h"
+#include "x86/desc.h"
+#include "x86/pci.h"
+
+struct test {
+       void (*func)(void);
+       const char *name;
+       int (*valid)(void);
+       int parallel;
+       bool (*next)(struct test *);
+};
+
+static void outb(unsigned short port, unsigned val)
+{
+    asm volatile("outb %b0, %w1" : : "a"(val), "Nd"(port));
+}
+
+static void outw(unsigned short port, unsigned val)
+{
+    asm volatile("outw %w0, %w1" : : "a"(val), "Nd"(port));
+}
+
+static void outl(unsigned short port, unsigned val)
+{
+    asm volatile("outl %d0, %w1" : : "a"(val), "Nd"(port));
+}
+
+static unsigned int inb(unsigned short port)
+{
+    unsigned int val;
+    asm volatile("xorl %0, %0; inb %w1, %b0" : "=a"(val) : "Nd"(port));
+    return val;
+}
+
+static unsigned int inl(unsigned short port)
+{
+    unsigned int val;
+    asm volatile("inl %w1, %0" : "=a"(val) : "Nd"(port));
+    return val;
+}
+
+#define GOAL (1ull << 30)
+
+static int nr_cpus;
+
+#ifdef __x86_64__
+#  define R "r"
+#else
+#  define R "e"
+#endif
+
+static void cpuid_test(void)
+{
+       asm volatile ("push %%"R "bx; cpuid; pop %%"R "bx"
+                     : : : "eax", "ecx", "edx");
+}
+
+static void vmcall(void)
+{
+       unsigned long a = 0, b, c, d;
+
+       asm volatile ("vmcall" : "+a"(a), "=b"(b), "=c"(c), "=d"(d));
+}
+
+#define MSR_TSC_ADJUST 0x3b
+#define MSR_EFER 0xc0000080
+#define EFER_NX_MASK            (1ull << 11)
+
+#ifdef __x86_64__
+static void mov_from_cr8(void)
+{
+       unsigned long cr8;
+
+       asm volatile ("mov %%cr8, %0" : "=r"(cr8));
+}
+
+static void mov_to_cr8(void)
+{
+       unsigned long cr8 = 0;
+
+       asm volatile ("mov %0, %%cr8" : : "r"(cr8));
+}
+#endif
+
+static int is_smp(void)
+{
+       return cpu_count() > 1;
+}
+
+static void nop(void *junk)
+{
+}
+
+static void ipi(void)
+{
+       on_cpu(1, nop, 0);
+}
+
+static void ipi_halt(void)
+{
+       unsigned long long t;
+
+       on_cpu(1, nop, 0);
+       t = rdtsc() + 2000;
+       while (rdtsc() < t)
+               ;
+}
+
+static void inl_pmtimer(void)
+{
+    inl(0xb008);
+}
+
+static void inl_nop_qemu(void)
+{
+    inl(0x1234);
+}
+
+static void inl_nop_kernel(void)
+{
+    inb(0x4d0);
+}
+
+static void outl_elcr_kernel(void)
+{
+    outb(0x4d0, 0);
+}
+
+static void ple_round_robin(void)
+{
+       struct counter {
+               volatile int n1;
+               int n2;
+       } __attribute__((aligned(64)));
+       static struct counter counters[64] = { { -1, 0 } };
+       int me = smp_id();
+       int you;
+       volatile struct counter *p = &counters[me];
+
+       while (p->n1 == p->n2)
+               asm volatile ("pause");
+
+       p->n2 = p->n1;
+       you = me + 1;
+       if (you == nr_cpus)
+               you = 0;
+       ++counters[you].n1;
+}
+
+static void rd_tsc_adjust_msr(void)
+{
+       rdmsr(MSR_TSC_ADJUST);
+}
+
+static void wr_tsc_adjust_msr(void)
+{
+       wrmsr(MSR_TSC_ADJUST, 0x0);
+}
+
+struct pci_test_dev_hdr {
+    uint8_t test;
+    uint8_t width;
+    uint8_t pad0[2];
+    uint32_t offset;
+    uint32_t data;
+    uint32_t count;
+    uint8_t name[];
+};
+
+static struct pci_test {
+       unsigned iobar;
+       unsigned ioport;
+       volatile void *memaddr;
+       volatile void *mem;
+       int test_idx;
+       uint32_t data;
+       uint32_t offset;
+} pci_test = {
+       .test_idx = -1
+};
+
+static void pci_mem_testb(void)
+{
+       *(volatile uint8_t *)pci_test.mem = pci_test.data;
+}
+
+static void pci_mem_testw(void)
+{
+       *(volatile uint16_t *)pci_test.mem = pci_test.data;
+}
+
+static void pci_mem_testl(void)
+{
+       *(volatile uint32_t *)pci_test.mem = pci_test.data;
+}
+
+static void pci_io_testb(void)
+{
+       outb(pci_test.ioport, pci_test.data);
+}
+
+static void pci_io_testw(void)
+{
+       outw(pci_test.ioport, pci_test.data);
+}
+
+static void pci_io_testl(void)
+{
+       outl(pci_test.ioport, pci_test.data);
+}
+
+static uint8_t ioreadb(unsigned long addr, bool io)
+{
+       if (io) {
+               return inb(addr);
+       } else {
+               return *(volatile uint8_t *)addr;
+       }
+}
+
+static uint32_t ioreadl(unsigned long addr, bool io)
+{
+       /* Note: assumes little endian */
+       if (io) {
+               return inl(addr);
+       } else {
+               return *(volatile uint32_t *)addr;
+       }
+}
+
+static void iowriteb(unsigned long addr, uint8_t data, bool io)
+{
+       if (io) {
+               outb(addr, data);
+       } else {
+               *(volatile uint8_t *)addr = data;
+       }
+}
+
+static bool pci_next(struct test *test, unsigned long addr, bool io)
+{
+       int i;
+       uint8_t width;
+
+       if (!pci_test.memaddr) {
+               test->func = NULL;
+               return true;
+       }
+       pci_test.test_idx++;
+       iowriteb(addr + offsetof(struct pci_test_dev_hdr, test),
+                pci_test.test_idx, io);
+       width = ioreadb(addr + offsetof(struct pci_test_dev_hdr, width),
+                       io);
+       switch (width) {
+               case 1:
+                       test->func = io ? pci_io_testb : pci_mem_testb;
+                       break;
+               case 2:
+                       test->func = io ? pci_io_testw : pci_mem_testw;
+                       break;
+               case 4:
+                       test->func = io ? pci_io_testl : pci_mem_testl;
+                       break;
+               default:
+                       /* Reset index for purposes of the next test */
+                       pci_test.test_idx = -1;
+                       test->func = NULL;
+                       return false;
+       }
+       pci_test.data = ioreadl(addr + offsetof(struct pci_test_dev_hdr, data),
+                               io);
+       pci_test.offset = ioreadl(addr + offsetof(struct pci_test_dev_hdr,
+                                                 offset), io);
+       for (i = 0; i < pci_test.offset; ++i) {
+               char c = ioreadb(addr + offsetof(struct pci_test_dev_hdr,
+                                                name) + i, io);
+               if (!c) {
+                       break;
+               }
+               printf("%c",c);
+       }
+       printf(":");
+       return true;
+}
+
+static bool pci_mem_next(struct test *test)
+{
+       bool ret;
+       ret = pci_next(test, ((unsigned long)pci_test.memaddr), false);
+       if (ret) {
+               pci_test.mem = pci_test.memaddr + pci_test.offset;
+       }
+       return ret;
+}
+
+static bool pci_io_next(struct test *test)
+{
+       bool ret;
+       ret = pci_next(test, ((unsigned long)pci_test.iobar), true);
+       if (ret) {
+               pci_test.ioport = pci_test.iobar + pci_test.offset;
+       }
+       return ret;
+}
+
+static struct test tests[] = {
+       { cpuid_test, "cpuid", .parallel = 1,  },
+       { vmcall, "vmcall", .parallel = 1, },
+#ifdef __x86_64__
+       { mov_from_cr8, "mov_from_cr8", .parallel = 1, },
+       { mov_to_cr8, "mov_to_cr8" , .parallel = 1, },
+#endif
+       { inl_pmtimer, "inl_from_pmtimer", .parallel = 1, },
+       { inl_nop_qemu, "inl_from_qemu", .parallel = 1 },
+       { inl_nop_kernel, "inl_from_kernel", .parallel = 1 },
+       { outl_elcr_kernel, "outl_to_kernel", .parallel = 1 },
+       { ipi, "ipi", is_smp, .parallel = 0, },
+       { ipi_halt, "ipi+halt", is_smp, .parallel = 0, },
+       { ple_round_robin, "ple-round-robin", .parallel = 1 },
+       { wr_tsc_adjust_msr, "wr_tsc_adjust_msr", .parallel = 1 },
+       { rd_tsc_adjust_msr, "rd_tsc_adjust_msr", .parallel = 1 },
+       { NULL, "pci-mem", .parallel = 0, .next = pci_mem_next },
+       { NULL, "pci-io", .parallel = 0, .next = pci_io_next },
+};
+
+unsigned iterations;
+static atomic_t nr_cpus_done;
+
+static void run_test(void *_func)
+{
+    int i;
+    void (*func)(void) = _func;
+
+    for (i = 0; i < iterations; ++i)
+        func();
+
+    atomic_inc(&nr_cpus_done);
+}
+
+static bool do_test(struct test *test)
+{
+       int i;
+       unsigned long long t1, t2;
+        void (*func)(void);
+
+        iterations = 32;
+
+        if (test->valid && !test->valid()) {
+               printf("%s (skipped)\n", test->name);
+               return false;
+       }
+
+       if (test->next && !test->next(test)) {
+               return false;
+       }
+
+       func = test->func;
+        if (!func) {
+               printf("%s (skipped)\n", test->name);
+               return false;
+       }
+
+       do {
+               iterations *= 2;
+               t1 = rdtsc();
+
+               if (!test->parallel) {
+                       for (i = 0; i < iterations; ++i)
+                               func();
+               } else {
+                       atomic_set(&nr_cpus_done, 0);
+                       for (i = cpu_count(); i > 0; i--)
+                               on_cpu_async(i-1, run_test, func);
+                       while (atomic_read(&nr_cpus_done) < cpu_count())
+                               ;
+               }
+               t2 = rdtsc();
+       } while ((t2 - t1) < GOAL);
+       printf("%s %d\n", test->name, (int)((t2 - t1) / iterations));
+       return test->next;
+}
+
+static void enable_nx(void *junk)
+{
+       if (cpuid(0x80000001).d & (1 << 20))
+               wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX_MASK);
+}
+
+bool test_wanted(struct test *test, char *wanted[], int nwanted)
+{
+       int i;
+
+       if (!nwanted)
+               return true;
+
+       for (i = 0; i < nwanted; ++i)
+               if (strcmp(wanted[i], test->name) == 0)
+                       return true;
+
+       return false;
+}
+
+int main(int ac, char **av)
+{
+       int i;
+       unsigned long membar = 0, base, offset;
+       void *m;
+       pcidevaddr_t pcidev;
+
+       smp_init();
+       setup_vm();
+       nr_cpus = cpu_count();
+
+       for (i = cpu_count(); i > 0; i--)
+               on_cpu(i-1, enable_nx, 0);
+
+       pcidev = pci_find_dev(0x1b36, 0x0005);
+       if (pcidev) {
+               for (i = 0; i < 2; i++) {
+                       if (!pci_bar_is_valid(pcidev, i)) {
+                               continue;
+                       }
+                       if (pci_bar_is_memory(pcidev, i)) {
+                               membar = pci_bar_addr(pcidev, i);
+                               base = membar & ~4095;
+                               offset = membar - base;
+                               m = alloc_vpages(1);
+                               
+                               install_page((void *)read_cr3(), base, m);
+                               pci_test.memaddr = m + offset;
+                       } else {
+                               pci_test.iobar = pci_bar_addr(pcidev, i);
+                       }
+               }
+               printf("pci-testdev at 0x%x membar %lx iobar %x\n",
+                      pcidev, membar, pci_test.iobar);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(tests); ++i)
+               if (test_wanted(&tests[i], av + 1, ac - 1))
+                       while (do_test(&tests[i])) {}
+
+       return 0;
+}
diff --git a/kvm-unittest/x86/vmx.c b/kvm-unittest/x86/vmx.c
new file mode 100644
index 0000000..ca36d35
--- /dev/null
+++ b/kvm-unittest/x86/vmx.c
@@ -0,0 +1,600 @@
+#include "libcflat.h"
+#include "processor.h"
+#include "vm.h"
+#include "desc.h"
+#include "vmx.h"
+#include "msr.h"
+#include "smp.h"
+#include "io.h"
+
+int fails, tests;
+u32 *vmxon_region;
+struct vmcs *vmcs_root;
+u32 vpid_cnt;
+void *guest_stack, *guest_syscall_stack;
+u32 ctrl_pin, ctrl_enter, ctrl_exit, ctrl_cpu[2];
+struct regs regs;
+struct vmx_test *current;
+u64 hypercall_field;
+bool launched;
+u64 host_rflags;
+
+union vmx_basic basic;
+union vmx_ctrl_pin ctrl_pin_rev;
+union vmx_ctrl_cpu ctrl_cpu_rev[2];
+union vmx_ctrl_exit ctrl_exit_rev;
+union vmx_ctrl_ent ctrl_enter_rev;
+union vmx_ept_vpid  ept_vpid;
+
+extern u64 gdt64_desc[];
+extern u64 idt_descr[];
+extern u64 tss_descr[];
+extern void *vmx_return;
+extern void *entry_sysenter;
+extern void *guest_entry;
+
+void report(const char *name, int result)
+{
+       ++tests;
+       if (result)
+               printf("PASS: %s\n", name);
+       else {
+               printf("FAIL: %s\n", name);
+               ++fails;
+       }
+}
+
+static int make_vmcs_current(struct vmcs *vmcs)
+{
+       bool ret;
+
+       asm volatile ("vmptrld %1; setbe %0" : "=q" (ret) : "m" (vmcs) : "cc");
+       return ret;
+}
+
+/* entry_sysenter */
+asm(
+       ".align 4, 0x90\n\t"
+       ".globl entry_sysenter\n\t"
+       "entry_sysenter:\n\t"
+       SAVE_GPR
+       "       and     $0xf, %rax\n\t"
+       "       mov     %rax, %rdi\n\t"
+       "       call    syscall_handler\n\t"
+       LOAD_GPR
+       "       vmresume\n\t"
+);
+
+static void __attribute__((__used__)) syscall_handler(u64 syscall_no)
+{
+       current->syscall_handler(syscall_no);
+}
+
+static inline int vmx_on()
+{
+       bool ret;
+       asm volatile ("vmxon %1; setbe %0\n\t"
+               : "=q"(ret) : "m"(vmxon_region) : "cc");
+       return ret;
+}
+
+static inline int vmx_off()
+{
+       bool ret;
+       asm volatile("vmxoff; setbe %0\n\t"
+               : "=q"(ret) : : "cc");
+       return ret;
+}
+
+void print_vmexit_info()
+{
+       u64 guest_rip, guest_rsp;
+       ulong reason = vmcs_read(EXI_REASON) & 0xff;
+       ulong exit_qual = vmcs_read(EXI_QUALIFICATION);
+       guest_rip = vmcs_read(GUEST_RIP);
+       guest_rsp = vmcs_read(GUEST_RSP);
+       printf("VMEXIT info:\n");
+       printf("\tvmexit reason = %d\n", reason);
+       printf("\texit qualification = 0x%x\n", exit_qual);
+       printf("\tBit 31 of reason = %x\n", (vmcs_read(EXI_REASON) >> 31) & 1);
+       printf("\tguest_rip = 0x%llx\n", guest_rip);
+       printf("\tRAX=0x%llx    RBX=0x%llx    RCX=0x%llx    RDX=0x%llx\n",
+               regs.rax, regs.rbx, regs.rcx, regs.rdx);
+       printf("\tRSP=0x%llx    RBP=0x%llx    RSI=0x%llx    RDI=0x%llx\n",
+               guest_rsp, regs.rbp, regs.rsi, regs.rdi);
+       printf("\tR8 =0x%llx    R9 =0x%llx    R10=0x%llx    R11=0x%llx\n",
+               regs.r8, regs.r9, regs.r10, regs.r11);
+       printf("\tR12=0x%llx    R13=0x%llx    R14=0x%llx    R15=0x%llx\n",
+               regs.r12, regs.r13, regs.r14, regs.r15);
+}
+
+static void test_vmclear(void)
+{
+       u64 rflags;
+
+       rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
+       write_rflags(rflags);
+       report("test vmclear", vmcs_clear(vmcs_root) == 0);
+}
+
+static void test_vmxoff(void)
+{
+       int ret;
+       u64 rflags;
+
+       rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
+       write_rflags(rflags);
+       ret = vmx_off();
+       report("test vmxoff", !ret);
+}
+
+static void __attribute__((__used__)) guest_main(void)
+{
+       current->guest_main();
+}
+
+/* guest_entry */
+asm(
+       ".align 4, 0x90\n\t"
+       ".globl entry_guest\n\t"
+       "guest_entry:\n\t"
+       "       call guest_main\n\t"
+       "       mov $1, %edi\n\t"
+       "       call hypercall\n\t"
+);
+
+static void init_vmcs_ctrl(void)
+{
+       /* 26.2 CHECKS ON VMX CONTROLS AND HOST-STATE AREA */
+       /* 26.2.1.1 */
+       vmcs_write(PIN_CONTROLS, ctrl_pin);
+       /* Disable VMEXIT of IO instruction */
+       vmcs_write(CPU_EXEC_CTRL0, ctrl_cpu[0]);
+       if (ctrl_cpu_rev[0].set & CPU_SECONDARY) {
+               ctrl_cpu[1] |= ctrl_cpu_rev[1].set & ctrl_cpu_rev[1].clr;
+               vmcs_write(CPU_EXEC_CTRL1, ctrl_cpu[1]);
+       }
+       vmcs_write(CR3_TARGET_COUNT, 0);
+       vmcs_write(VPID, ++vpid_cnt);
+}
+
+static void init_vmcs_host(void)
+{
+       /* 26.2 CHECKS ON VMX CONTROLS AND HOST-STATE AREA */
+       /* 26.2.1.2 */
+       vmcs_write(HOST_EFER, rdmsr(MSR_EFER));
+
+       /* 26.2.1.3 */
+       vmcs_write(ENT_CONTROLS, ctrl_enter);
+       vmcs_write(EXI_CONTROLS, ctrl_exit);
+
+       /* 26.2.2 */
+       vmcs_write(HOST_CR0, read_cr0());
+       vmcs_write(HOST_CR3, read_cr3());
+       vmcs_write(HOST_CR4, read_cr4());
+       vmcs_write(HOST_SYSENTER_EIP, (u64)(&entry_sysenter));
+       vmcs_write(HOST_SYSENTER_CS,  SEL_KERN_CODE_64);
+
+       /* 26.2.3 */
+       vmcs_write(HOST_SEL_CS, SEL_KERN_CODE_64);
+       vmcs_write(HOST_SEL_SS, SEL_KERN_DATA_64);
+       vmcs_write(HOST_SEL_DS, SEL_KERN_DATA_64);
+       vmcs_write(HOST_SEL_ES, SEL_KERN_DATA_64);
+       vmcs_write(HOST_SEL_FS, SEL_KERN_DATA_64);
+       vmcs_write(HOST_SEL_GS, SEL_KERN_DATA_64);
+       vmcs_write(HOST_SEL_TR, SEL_TSS_RUN);
+       vmcs_write(HOST_BASE_TR,   (u64)tss_descr);
+       vmcs_write(HOST_BASE_GDTR, (u64)gdt64_desc);
+       vmcs_write(HOST_BASE_IDTR, (u64)idt_descr);
+       vmcs_write(HOST_BASE_FS, 0);
+       vmcs_write(HOST_BASE_GS, 0);
+
+       /* Set other vmcs area */
+       vmcs_write(PF_ERROR_MASK, 0);
+       vmcs_write(PF_ERROR_MATCH, 0);
+       vmcs_write(VMCS_LINK_PTR, ~0ul);
+       vmcs_write(VMCS_LINK_PTR_HI, ~0ul);
+       vmcs_write(HOST_RIP, (u64)(&vmx_return));
+}
+
+static void init_vmcs_guest(void)
+{
+       /* 26.3 CHECKING AND LOADING GUEST STATE */
+       ulong guest_cr0, guest_cr4, guest_cr3;
+       /* 26.3.1.1 */
+       guest_cr0 = read_cr0();
+       guest_cr4 = read_cr4();
+       guest_cr3 = read_cr3();
+       if (ctrl_enter & ENT_GUEST_64) {
+               guest_cr0 |= X86_CR0_PG;
+               guest_cr4 |= X86_CR4_PAE;
+       }
+       if ((ctrl_enter & ENT_GUEST_64) == 0)
+               guest_cr4 &= (~X86_CR4_PCIDE);
+       if (guest_cr0 & X86_CR0_PG)
+               guest_cr0 |= X86_CR0_PE;
+       vmcs_write(GUEST_CR0, guest_cr0);
+       vmcs_write(GUEST_CR3, guest_cr3);
+       vmcs_write(GUEST_CR4, guest_cr4);
+       vmcs_write(GUEST_SYSENTER_CS,  SEL_KERN_CODE_64);
+       vmcs_write(GUEST_SYSENTER_ESP,
+               (u64)(guest_syscall_stack + PAGE_SIZE - 1));
+       vmcs_write(GUEST_SYSENTER_EIP, (u64)(&entry_sysenter));
+       vmcs_write(GUEST_DR7, 0);
+       vmcs_write(GUEST_EFER, rdmsr(MSR_EFER));
+
+       /* 26.3.1.2 */
+       vmcs_write(GUEST_SEL_CS, SEL_KERN_CODE_64);
+       vmcs_write(GUEST_SEL_SS, SEL_KERN_DATA_64);
+       vmcs_write(GUEST_SEL_DS, SEL_KERN_DATA_64);
+       vmcs_write(GUEST_SEL_ES, SEL_KERN_DATA_64);
+       vmcs_write(GUEST_SEL_FS, SEL_KERN_DATA_64);
+       vmcs_write(GUEST_SEL_GS, SEL_KERN_DATA_64);
+       vmcs_write(GUEST_SEL_TR, SEL_TSS_RUN);
+       vmcs_write(GUEST_SEL_LDTR, 0);
+
+       vmcs_write(GUEST_BASE_CS, 0);
+       vmcs_write(GUEST_BASE_ES, 0);
+       vmcs_write(GUEST_BASE_SS, 0);
+       vmcs_write(GUEST_BASE_DS, 0);
+       vmcs_write(GUEST_BASE_FS, 0);
+       vmcs_write(GUEST_BASE_GS, 0);
+       vmcs_write(GUEST_BASE_TR,   (u64)tss_descr);
+       vmcs_write(GUEST_BASE_LDTR, 0);
+
+       vmcs_write(GUEST_LIMIT_CS, 0xFFFFFFFF);
+       vmcs_write(GUEST_LIMIT_DS, 0xFFFFFFFF);
+       vmcs_write(GUEST_LIMIT_ES, 0xFFFFFFFF);
+       vmcs_write(GUEST_LIMIT_SS, 0xFFFFFFFF);
+       vmcs_write(GUEST_LIMIT_FS, 0xFFFFFFFF);
+       vmcs_write(GUEST_LIMIT_GS, 0xFFFFFFFF);
+       vmcs_write(GUEST_LIMIT_LDTR, 0xffff);
+       vmcs_write(GUEST_LIMIT_TR, ((struct descr *)tss_descr)->limit);
+
+       vmcs_write(GUEST_AR_CS, 0xa09b);
+       vmcs_write(GUEST_AR_DS, 0xc093);
+       vmcs_write(GUEST_AR_ES, 0xc093);
+       vmcs_write(GUEST_AR_FS, 0xc093);
+       vmcs_write(GUEST_AR_GS, 0xc093);
+       vmcs_write(GUEST_AR_SS, 0xc093);
+       vmcs_write(GUEST_AR_LDTR, 0x82);
+       vmcs_write(GUEST_AR_TR, 0x8b);
+
+       /* 26.3.1.3 */
+       vmcs_write(GUEST_BASE_GDTR, (u64)gdt64_desc);
+       vmcs_write(GUEST_BASE_IDTR, (u64)idt_descr);
+       vmcs_write(GUEST_LIMIT_GDTR,
+               ((struct descr *)gdt64_desc)->limit & 0xffff);
+       vmcs_write(GUEST_LIMIT_IDTR,
+               ((struct descr *)idt_descr)->limit & 0xffff);
+
+       /* 26.3.1.4 */
+       vmcs_write(GUEST_RIP, (u64)(&guest_entry));
+       vmcs_write(GUEST_RSP, (u64)(guest_stack + PAGE_SIZE - 1));
+       vmcs_write(GUEST_RFLAGS, 0x2);
+
+       /* 26.3.1.5 */
+       vmcs_write(GUEST_ACTV_STATE, 0);
+       vmcs_write(GUEST_INTR_STATE, 0);
+}
+
+static int init_vmcs(struct vmcs **vmcs)
+{
+       *vmcs = alloc_page();
+       memset(*vmcs, 0, PAGE_SIZE);
+       (*vmcs)->revision_id = basic.revision;
+       /* vmclear first to init vmcs */
+       if (vmcs_clear(*vmcs)) {
+               printf("%s : vmcs_clear error\n", __func__);
+               return 1;
+       }
+
+       if (make_vmcs_current(*vmcs)) {
+               printf("%s : make_vmcs_current error\n", __func__);
+               return 1;
+       }
+
+       /* All settings to pin/exit/enter/cpu
+          control fields should be placed here */
+       ctrl_pin |= PIN_EXTINT | PIN_NMI | PIN_VIRT_NMI;
+       ctrl_exit = EXI_LOAD_EFER | EXI_HOST_64;
+       ctrl_enter = (ENT_LOAD_EFER | ENT_GUEST_64);
+       ctrl_cpu[0] |= CPU_HLT;
+       /* DIsable IO instruction VMEXIT now */
+       ctrl_cpu[0] &= (~(CPU_IO | CPU_IO_BITMAP));
+       ctrl_cpu[1] = 0;
+
+       ctrl_pin = (ctrl_pin | ctrl_pin_rev.set) & ctrl_pin_rev.clr;
+       ctrl_enter = (ctrl_enter | ctrl_enter_rev.set) & ctrl_enter_rev.clr;
+       ctrl_exit = (ctrl_exit | ctrl_exit_rev.set) & ctrl_exit_rev.clr;
+       ctrl_cpu[0] = (ctrl_cpu[0] | ctrl_cpu_rev[0].set) & ctrl_cpu_rev[0].clr;
+
+       init_vmcs_ctrl();
+       init_vmcs_host();
+       init_vmcs_guest();
+       return 0;
+}
+
+static void init_vmx(void)
+{
+       ulong fix_cr0_set, fix_cr0_clr;
+       ulong fix_cr4_set, fix_cr4_clr;
+
+       vmxon_region = alloc_page();
+       memset(vmxon_region, 0, PAGE_SIZE);
+
+       fix_cr0_set =  rdmsr(MSR_IA32_VMX_CR0_FIXED0);
+       fix_cr0_clr =  rdmsr(MSR_IA32_VMX_CR0_FIXED1);
+       fix_cr4_set =  rdmsr(MSR_IA32_VMX_CR4_FIXED0);
+       fix_cr4_clr = rdmsr(MSR_IA32_VMX_CR4_FIXED1);
+       basic.val = rdmsr(MSR_IA32_VMX_BASIC);
+       ctrl_pin_rev.val = rdmsr(basic.ctrl ? MSR_IA32_VMX_TRUE_PIN
+                       : MSR_IA32_VMX_PINBASED_CTLS);
+       ctrl_exit_rev.val = rdmsr(basic.ctrl ? MSR_IA32_VMX_TRUE_EXIT
+                       : MSR_IA32_VMX_EXIT_CTLS);
+       ctrl_enter_rev.val = rdmsr(basic.ctrl ? MSR_IA32_VMX_TRUE_ENTRY
+                       : MSR_IA32_VMX_ENTRY_CTLS);
+       ctrl_cpu_rev[0].val = rdmsr(basic.ctrl ? MSR_IA32_VMX_TRUE_PROC
+                       : MSR_IA32_VMX_PROCBASED_CTLS);
+       if (ctrl_cpu_rev[0].set & CPU_SECONDARY)
+               ctrl_cpu_rev[1].val = rdmsr(MSR_IA32_VMX_PROCBASED_CTLS2);
+       if (ctrl_cpu_rev[1].set & CPU_EPT || ctrl_cpu_rev[1].set & CPU_VPID)
+               ept_vpid.val = rdmsr(MSR_IA32_VMX_EPT_VPID_CAP);
+
+       write_cr0((read_cr0() & fix_cr0_clr) | fix_cr0_set);
+       write_cr4((read_cr4() & fix_cr4_clr) | fix_cr4_set | X86_CR4_VMXE);
+
+       *vmxon_region = basic.revision;
+
+       guest_stack = alloc_page();
+       memset(guest_stack, 0, PAGE_SIZE);
+       guest_syscall_stack = alloc_page();
+       memset(guest_syscall_stack, 0, PAGE_SIZE);
+}
+
+static int test_vmx_capability(void)
+{
+       struct cpuid r;
+       u64 ret1, ret2;
+       u64 ia32_feature_control;
+       r = cpuid(1);
+       ret1 = ((r.c) >> 5) & 1;
+       ia32_feature_control = rdmsr(MSR_IA32_FEATURE_CONTROL);
+       ret2 = ((ia32_feature_control & 0x5) == 0x5);
+       if ((!ret2) && ((ia32_feature_control & 0x1) == 0)) {
+               wrmsr(MSR_IA32_FEATURE_CONTROL, 0x5);
+               ia32_feature_control = rdmsr(MSR_IA32_FEATURE_CONTROL);
+               ret2 = ((ia32_feature_control & 0x5) == 0x5);
+       }
+       report("test vmx capability", ret1 & ret2);
+       return !(ret1 & ret2);
+}
+
+static int test_vmxon(void)
+{
+       int ret;
+       u64 rflags;
+
+       rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
+       write_rflags(rflags);
+       ret = vmx_on();
+       report("test vmxon", !ret);
+       return ret;
+}
+
+static void test_vmptrld(void)
+{
+       u64 rflags;
+       struct vmcs *vmcs;
+
+       vmcs = alloc_page();
+       vmcs->revision_id = basic.revision;
+       rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
+       write_rflags(rflags);
+       report("test vmptrld", make_vmcs_current(vmcs) == 0);
+}
+
+static void test_vmptrst(void)
+{
+       u64 rflags;
+       int ret;
+       struct vmcs *vmcs1, *vmcs2;
+
+       vmcs1 = alloc_page();
+       memset(vmcs1, 0, PAGE_SIZE);
+       init_vmcs(&vmcs1);
+       rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
+       write_rflags(rflags);
+       ret = vmcs_save(&vmcs2);
+       report("test vmptrst", (!ret) && (vmcs1 == vmcs2));
+}
+
+/* This function can only be called in guest */
+static void __attribute__((__used__)) hypercall(u32 hypercall_no)
+{
+       u64 val = 0;
+       val = (hypercall_no & HYPERCALL_MASK) | HYPERCALL_BIT;
+       hypercall_field = val;
+       asm volatile("vmcall\n\t");
+}
+
+static bool is_hypercall()
+{
+       ulong reason, hyper_bit;
+
+       reason = vmcs_read(EXI_REASON) & 0xff;
+       hyper_bit = hypercall_field & HYPERCALL_BIT;
+       if (reason == VMX_VMCALL && hyper_bit)
+               return true;
+       return false;
+}
+
+static int handle_hypercall()
+{
+       ulong hypercall_no;
+
+       hypercall_no = hypercall_field & HYPERCALL_MASK;
+       hypercall_field = 0;
+       switch (hypercall_no) {
+       case HYPERCALL_VMEXIT:
+               return VMX_TEST_VMEXIT;
+       default:
+               printf("ERROR : Invalid hypercall number : %d\n", hypercall_no);
+       }
+       return VMX_TEST_EXIT;
+}
+
+static int exit_handler()
+{
+       int ret;
+
+       current->exits++;
+       regs.rflags = vmcs_read(GUEST_RFLAGS);
+       if (is_hypercall())
+               ret = handle_hypercall();
+       else
+               ret = current->exit_handler();
+       vmcs_write(GUEST_RFLAGS, regs.rflags);
+       switch (ret) {
+       case VMX_TEST_VMEXIT:
+       case VMX_TEST_RESUME:
+               return ret;
+       case VMX_TEST_EXIT:
+               break;
+       default:
+               printf("ERROR : Invalid exit_handler return val %d.\n"
+                       , ret);
+       }
+       print_vmexit_info();
+       exit(-1);
+       return 0;
+}
+
+static int vmx_run()
+{
+       u32 ret = 0, fail = 0;
+
+       while (1) {
+               asm volatile (
+                       "mov %%rsp, %%rsi\n\t"
+                       "mov %2, %%rdi\n\t"
+                       "vmwrite %%rsi, %%rdi\n\t"
+
+                       LOAD_GPR_C
+                       "cmpl $0, %1\n\t"
+                       "jne 1f\n\t"
+                       LOAD_RFLAGS
+                       "vmlaunch\n\t"
+                       "jmp 2f\n\t"
+                       "1: "
+                       "vmresume\n\t"
+                       "2: "
+                       "setbe %0\n\t"
+                       "vmx_return:\n\t"
+                       SAVE_GPR_C
+                       SAVE_RFLAGS
+                       : "=m"(fail)
+                       : "m"(launched), "i"(HOST_RSP)
+                       : "rdi", "rsi", "memory", "cc"
+
+               );
+               if (fail)
+                       ret = launched ? VMX_TEST_RESUME_ERR :
+                               VMX_TEST_LAUNCH_ERR;
+               else {
+                       launched = 1;
+                       ret = exit_handler();
+               }
+               if (ret != VMX_TEST_RESUME)
+                       break;
+       }
+       launched = 0;
+       switch (ret) {
+       case VMX_TEST_VMEXIT:
+               return 0;
+       case VMX_TEST_LAUNCH_ERR:
+               printf("%s : vmlaunch failed.\n", __func__);
+               if ((!(host_rflags & X86_EFLAGS_CF) && !(host_rflags & 
X86_EFLAGS_ZF))
+                       || ((host_rflags & X86_EFLAGS_CF) && (host_rflags & 
X86_EFLAGS_ZF)))
+                       printf("\tvmlaunch set wrong flags\n");
+               report("test vmlaunch", 0);
+               break;
+       case VMX_TEST_RESUME_ERR:
+               printf("%s : vmresume failed.\n", __func__);
+               if ((!(host_rflags & X86_EFLAGS_CF) && !(host_rflags & 
X86_EFLAGS_ZF))
+                       || ((host_rflags & X86_EFLAGS_CF) && (host_rflags & 
X86_EFLAGS_ZF)))
+                       printf("\tvmresume set wrong flags\n");
+               report("test vmresume", 0);
+               break;
+       default:
+               printf("%s : unhandled ret from exit_handler, ret=%d.\n", 
__func__, ret);
+               break;
+       }
+       return 1;
+}
+
+static int test_run(struct vmx_test *test)
+{
+       if (test->name == NULL)
+               test->name = "(no name)";
+       if (vmx_on()) {
+               printf("%s : vmxon failed.\n", __func__);
+               return 1;
+       }
+       init_vmcs(&(test->vmcs));
+       /* Directly call test->init is ok here, init_vmcs has done
+          vmcs init, vmclear and vmptrld*/
+       if (test->init)
+               test->init(test->vmcs);
+       test->exits = 0;
+       current = test;
+       regs = test->guest_regs;
+       vmcs_write(GUEST_RFLAGS, regs.rflags | 0x2);
+       launched = 0;
+       printf("\nTest suite : %s\n", test->name);
+       vmx_run();
+       if (vmx_off()) {
+               printf("%s : vmxoff failed.\n", __func__);
+               return 1;
+       }
+       return 0;
+}
+
+extern struct vmx_test vmx_tests[];
+
+int main(void)
+{
+       int i = 0;
+
+       setup_vm();
+       setup_idt();
+       fails = tests = 0;
+       hypercall_field = 0;
+
+       if (test_vmx_capability() != 0) {
+               printf("ERROR : vmx not supported, check +vmx option\n");
+               goto exit;
+       }
+       init_vmx();
+       /* Set basic test ctxt the same as "null" */
+       current = &vmx_tests[0];
+       if (test_vmxon() != 0)
+               goto exit;
+       test_vmptrld();
+       test_vmclear();
+       test_vmptrst();
+       init_vmcs(&vmcs_root);
+       if (vmx_run()) {
+               report("test vmlaunch", 0);
+               goto exit;
+       }
+       test_vmxoff();
+
+       while (vmx_tests[++i].name != NULL)
+               if (test_run(&vmx_tests[i]))
+                       goto exit;
+
+exit:
+       printf("\nSUMMARY: %d tests, %d failures\n", tests, fails);
+       return fails ? 1 : 0;
+}
diff --git a/kvm-unittest/x86/vmx_tests.c b/kvm-unittest/x86/vmx_tests.c
new file mode 100644
index 0000000..c1b39f4
--- /dev/null
+++ b/kvm-unittest/x86/vmx_tests.c
@@ -0,0 +1,87 @@
+#include "vmx.h"
+
+void basic_init()
+{
+}
+
+void basic_guest_main()
+{
+       /* Here is a basic guest_main, print Hello World */
+       printf("\tHello World, this is null_guest_main!\n");
+}
+
+int basic_exit_handler()
+{
+       u64 guest_rip;
+       ulong reason;
+
+       guest_rip = vmcs_read(GUEST_RIP);
+       reason = vmcs_read(EXI_REASON) & 0xff;
+
+       switch (reason) {
+       case VMX_VMCALL:
+               print_vmexit_info();
+               vmcs_write(GUEST_RIP, guest_rip + 3);
+               return VMX_TEST_RESUME;
+       default:
+               break;
+       }
+       printf("ERROR : Unhandled vmx exit.\n");
+       print_vmexit_info();
+       return VMX_TEST_EXIT;
+}
+
+void basic_syscall_handler(u64 syscall_no)
+{
+}
+
+void vmenter_main()
+{
+       u64 rax;
+       u64 rsp, resume_rsp;
+
+       report("test vmlaunch", 1);
+
+       asm volatile(
+               "mov %%rsp, %0\n\t"
+               "mov %3, %%rax\n\t"
+               "vmcall\n\t"
+               "mov %%rax, %1\n\t"
+               "mov %%rsp, %2\n\t"
+               : "=r"(rsp), "=r"(rax), "=r"(resume_rsp)
+               : "g"(0xABCD));
+       report("test vmresume", (rax == 0xFFFF) && (rsp == resume_rsp));
+}
+
+int vmenter_exit_handler()
+{
+       u64 guest_rip;
+       ulong reason;
+
+       guest_rip = vmcs_read(GUEST_RIP);
+       reason = vmcs_read(EXI_REASON) & 0xff;
+       switch (reason) {
+       case VMX_VMCALL:
+               if (regs.rax != 0xABCD) {
+                       report("test vmresume", 0);
+                       return VMX_TEST_VMEXIT;
+               }
+               regs.rax = 0xFFFF;
+               vmcs_write(GUEST_RIP, guest_rip + 3);
+               return VMX_TEST_RESUME;
+       default:
+               report("test vmresume", 0);
+               print_vmexit_info();
+       }
+       return VMX_TEST_VMEXIT;
+}
+
+/* name/init/guest_main/exit_handler/syscall_handler/guest_regs
+   basic_* just implement some basic functions */
+struct vmx_test vmx_tests[] = {
+       { "null", basic_init, basic_guest_main, basic_exit_handler,
+               basic_syscall_handler, {0} },
+       { "vmenter", basic_init, vmenter_main, vmenter_exit_handler,
+               basic_syscall_handler, {0} },
+       { NULL, NULL, NULL, NULL, NULL, {0} },
+};
diff --git a/kvm-unittest/x86/xsave.c b/kvm-unittest/x86/xsave.c
new file mode 100644
index 0000000..057b0ff
--- /dev/null
+++ b/kvm-unittest/x86/xsave.c
@@ -0,0 +1,262 @@
+#include "libcflat.h"
+#include "desc.h"
+
+#ifdef __x86_64__
+#define uint64_t unsigned long
+#else
+#define uint64_t unsigned long long
+#endif
+
+static inline void __cpuid(unsigned int *eax, unsigned int *ebx,
+        unsigned int *ecx, unsigned int *edx)
+{
+    /* ecx is often an input as well as an output. */
+    asm volatile("cpuid"
+            : "=a" (*eax),
+            "=b" (*ebx),
+            "=c" (*ecx),
+            "=d" (*edx)
+            : "0" (*eax), "2" (*ecx));
+}
+
+/*
+ * Generic CPUID function
+ * clear %ecx since some cpus (Cyrix MII) do not set or clear %ecx
+ * resulting in stale register contents being returned.
+ */
+void cpuid(unsigned int op,
+        unsigned int *eax, unsigned int *ebx,
+        unsigned int *ecx, unsigned int *edx)
+{
+    *eax = op;
+    *ecx = 0;
+    __cpuid(eax, ebx, ecx, edx);
+}
+
+/* Some CPUID calls want 'count' to be placed in ecx */
+void cpuid_count(unsigned int op, int count,
+        unsigned int *eax, unsigned int *ebx,
+        unsigned int *ecx, unsigned int *edx)
+{
+    *eax = op;
+    *ecx = count;
+    __cpuid(eax, ebx, ecx, edx);
+}
+
+int xgetbv_checking(u32 index, u64 *result)
+{
+    u32 eax, edx;
+
+    asm volatile(ASM_TRY("1f")
+            ".byte 0x0f,0x01,0xd0\n\t" /* xgetbv */
+            "1:"
+            : "=a" (eax), "=d" (edx)
+            : "c" (index));
+    *result = eax + ((u64)edx << 32);
+    return exception_vector();
+}
+
+int xsetbv_checking(u32 index, u64 value)
+{
+    u32 eax = value;
+    u32 edx = value >> 32;
+
+    asm volatile(ASM_TRY("1f")
+            ".byte 0x0f,0x01,0xd1\n\t" /* xsetbv */
+            "1:"
+            : : "a" (eax), "d" (edx), "c" (index));
+    return exception_vector();
+}
+
+unsigned long read_cr4(void)
+{
+    unsigned long val;
+    asm volatile("mov %%cr4,%0" : "=r" (val));
+    return val;
+}
+
+int write_cr4_checking(unsigned long val)
+{
+    asm volatile(ASM_TRY("1f")
+            "mov %0,%%cr4\n\t"
+            "1:": : "r" (val));
+    return exception_vector();
+}
+
+#define CPUID_1_ECX_XSAVE          (1 << 26)
+#define CPUID_1_ECX_OSXSAVE        (1 << 27)
+int check_cpuid_1_ecx(unsigned int bit)
+{
+    unsigned int eax, ebx, ecx, edx;
+    cpuid(1, &eax, &ebx, &ecx, &edx);
+    if (ecx & bit)
+        return 1;
+    return 0;
+}
+
+uint64_t get_supported_xcr0(void)
+{
+    unsigned int eax, ebx, ecx, edx;
+    cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx);
+    printf("eax %x, ebx %x, ecx %x, edx %x\n",
+            eax, ebx, ecx, edx);
+    return eax + ((u64)edx << 32);
+}
+
+#define X86_CR4_OSXSAVE                        0x00040000
+#define XCR_XFEATURE_ENABLED_MASK       0x00000000
+#define XCR_XFEATURE_ILLEGAL_MASK       0x00000010
+
+#define XSTATE_FP       0x1
+#define XSTATE_SSE      0x2
+#define XSTATE_YMM      0x4
+
+static int total_tests, fail_tests;
+
+void pass_if(int condition)
+{
+    total_tests ++;
+    if (condition)
+        printf("Pass!\n");
+    else {
+        printf("Fail!\n");
+        fail_tests ++;
+    }
+}
+
+void test_xsave(void)
+{
+    unsigned long cr4;
+    uint64_t supported_xcr0;
+    uint64_t test_bits;
+    u64 xcr0;
+    int r;
+
+    printf("Legal instruction testing:\n");
+    supported_xcr0 = get_supported_xcr0();
+    printf("Supported XCR0 bits: 0x%x\n", supported_xcr0);
+
+    printf("Check minimal XSAVE required bits: ");
+    test_bits = XSTATE_FP | XSTATE_SSE;
+    pass_if((supported_xcr0 & test_bits) == test_bits);
+
+    printf("Set CR4 OSXSAVE: ");
+    cr4 = read_cr4();
+    r = write_cr4_checking(cr4 | X86_CR4_OSXSAVE);
+    pass_if(r == 0);
+
+    printf("Check CPUID.1.ECX.OSXSAVE - expect 1: ");
+    pass_if(check_cpuid_1_ecx(CPUID_1_ECX_OSXSAVE));
+
+    printf("    Legal tests\n");
+    printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, XSTATE_FP): ");
+    test_bits = XSTATE_FP;
+    r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
+    pass_if(r == 0);
+    printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, "
+            "XSTATE_FP | XSTATE_SSE): ");
+    test_bits = XSTATE_FP | XSTATE_SSE;
+    r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
+    pass_if(r == 0);
+    printf("        xgetbv(XCR_XFEATURE_ENABLED_MASK): ");
+    r = xgetbv_checking(XCR_XFEATURE_ENABLED_MASK, &xcr0);
+    pass_if(r == 0);
+    printf("    Illegal tests\n");
+    printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, 0) - expect #GP: ");
+    test_bits = 0;
+    r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
+    pass_if(r == GP_VECTOR);
+    printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, XSTATE_SSE) "
+            "- expect #GP: ");
+    test_bits = XSTATE_SSE;
+    r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
+    pass_if(r == GP_VECTOR);
+    if (supported_xcr0 & XSTATE_YMM) {
+        printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, "
+                "XSTATE_YMM) - expect #GP: ");
+        test_bits = XSTATE_YMM;
+        r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
+        pass_if(r == GP_VECTOR);
+        printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, "
+                "XSTATE_FP | XSTATE_YMM) - expect #GP: ");
+        test_bits = XSTATE_FP | XSTATE_YMM;
+        r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
+        pass_if(r == GP_VECTOR);
+    }
+    printf("        xsetbv(XCR_XFEATURE_ILLEGAL_MASK, XSTATE_FP) "
+            "- expect #GP: ");
+    test_bits = XSTATE_SSE;
+    r = xsetbv_checking(XCR_XFEATURE_ILLEGAL_MASK, test_bits);
+    pass_if(r == GP_VECTOR);
+    printf("        xgetbv(XCR_XFEATURE_ILLEGAL_MASK, XSTATE_FP) "
+            "- expect #GP: ");
+    test_bits = XSTATE_SSE;
+    r = xsetbv_checking(XCR_XFEATURE_ILLEGAL_MASK, test_bits);
+    pass_if(r == GP_VECTOR);
+
+    printf("Unset CR4 OSXSAVE: ");
+    cr4 &= ~X86_CR4_OSXSAVE;
+    r = write_cr4_checking(cr4);
+    pass_if(r == 0);
+    printf("Check CPUID.1.ECX.OSXSAVE - expect 0: ");
+    pass_if(check_cpuid_1_ecx(CPUID_1_ECX_OSXSAVE) == 0);
+    printf("    Illegal tests:\n");
+    printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, XSTATE_FP) - expect #UD: 
");
+    test_bits = XSTATE_FP;
+    r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
+    pass_if(r == UD_VECTOR);
+    printf("        xsetbv(XCR_XFEATURE_ENABLED_MASK, "
+            "XSTATE_FP | XSTATE_SSE) - expect #UD: ");
+    test_bits = XSTATE_FP | XSTATE_SSE;
+    r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
+    pass_if(r == UD_VECTOR);
+    printf("    Illegal tests:\n");
+    printf("   xgetbv(XCR_XFEATURE_ENABLED_MASK) - expect #UD: ");
+    r = xgetbv_checking(XCR_XFEATURE_ENABLED_MASK, &xcr0);
+    pass_if(r == UD_VECTOR);
+}
+
+void test_no_xsave(void)
+{
+    unsigned long cr4;
+    u64 xcr0;
+    int r;
+
+    printf("Check CPUID.1.ECX.OSXSAVE - expect 0: ");
+    pass_if(check_cpuid_1_ecx(CPUID_1_ECX_OSXSAVE) == 0);
+
+    printf("Illegal instruction testing:\n");
+
+    printf("Set OSXSAVE in CR4 - expect #GP: ");
+    cr4 = read_cr4();
+    r = write_cr4_checking(cr4 | X86_CR4_OSXSAVE);
+    pass_if(r == GP_VECTOR);
+
+    printf("Execute xgetbv - expect #UD: ");
+    r = xgetbv_checking(XCR_XFEATURE_ENABLED_MASK, &xcr0);
+    pass_if(r == UD_VECTOR);
+
+    printf("Execute xsetbv - expect #UD: ");
+    r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, 0x3);
+    pass_if(r == UD_VECTOR);
+}
+
+int main(void)
+{
+    setup_idt();
+    if (check_cpuid_1_ecx(CPUID_1_ECX_XSAVE)) {
+        printf("CPU has XSAVE feature\n");
+        test_xsave();
+    } else {
+        printf("CPU don't has XSAVE feature\n");
+        test_no_xsave();
+    }
+    printf("Total test: %d\n", total_tests);
+    if (fail_tests == 0)
+        printf("ALL PASS!\n");
+    else {
+        printf("Fail %d tests.\n", fail_tests);
+        return 1;
+    }
+    return 0;
+}
diff --git a/kvm-unittest/.gitignore b/kvm-unittest/.gitignore
new file mode 100644
index 0000000..ed857b7
--- /dev/null
+++ b/kvm-unittest/.gitignore
@@ -0,0 +1,9 @@
+.gdbinit
+*.a
+*.d
+*.o
+*.flat
+.pc
+patches
+.stgit-*
+cscope.*
diff --git a/kvm-unittest/COPYRIGHT b/kvm-unittest/COPYRIGHT
new file mode 100644
index 0000000..d35649c
--- /dev/null
+++ b/kvm-unittest/COPYRIGHT
@@ -0,0 +1,4 @@
+Copyright (C) 2006 Qumranet.
+
+The files in this directory and its subdirectories are licensed under the
+GNU LGPL, version 2.
diff --git a/kvm-unittest/Makefile b/kvm-unittest/Makefile
new file mode 100644
index 0000000..b6e8759
--- /dev/null
+++ b/kvm-unittest/Makefile
@@ -0,0 +1,60 @@
+
+include config.mak
+
+DESTDIR := $(PREFIX)/share/qemu/tests
+
+.PHONY: arch_clean clean
+
+#make sure env CFLAGS variable is not used
+CFLAGS = -g
+
+libgcc := $(shell $(CC) --print-libgcc-file-name)
+
+libcflat := lib/libcflat.a
+cflatobjs := \
+       lib/panic.o \
+       lib/printf.o \
+       lib/string.o
+cflatobjs += lib/argv.o
+
+#include architecure specific make rules
+include config-$(ARCH).mak
+
+# cc-option
+# Usage: OP_CFLAGS+=$(call cc-option, -falign-functions=0, -malign-functions=0)
+
+cc-option = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null \
+              > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
+
+CFLAGS += -O1
+CFLAGS += $(autodepend-flags) -g -fomit-frame-pointer -Wall
+CFLAGS += $(call cc-option, -fno-stack-protector, "")
+CFLAGS += $(call cc-option, -fno-stack-protector-all, "")
+CFLAGS += -I.
+
+CXXFLAGS += $(CFLAGS)
+
+autodepend-flags = -MMD -MF $(dir $*).$(notdir $*).d
+
+LDFLAGS += $(CFLAGS)
+LDFLAGS += -pthread -lrt
+
+kvmtrace_objs= kvmtrace.o
+
+kvmtrace: $(kvmtrace_objs)
+       $(CC) $(LDFLAGS) $^ -o $@
+
+$(libcflat): $(cflatobjs)
+       $(AR) rcs $@ $^
+
+%.o: %.S
+       $(CC) $(CFLAGS) -c -nostdlib -o $@ $<
+
+-include .*.d */.*.d */*/.*.d
+
+install:
+       mkdir -p $(DESTDIR)
+       install $(tests_and_config) $(DESTDIR)
+
+clean: arch_clean
+       $(RM) kvmtrace *.o *.a .*.d $(libcflat) $(cflatobjs)
diff --git a/kvm-unittest/README b/kvm-unittest/README
new file mode 100644
index 0000000..db525e3
--- /dev/null
+++ b/kvm-unittest/README
@@ -0,0 +1,36 @@
+This directory contains sources for a kvm test suite.
+
+Tests for x86 architecture are run as kernel images for qemu that supports 
multiboot format.
+Tests uses an infrastructure called from the bios code. The infrastructure 
initialize the system/cpu's,
+switch to long-mode and calls the 'main' function of the individual test.
+Tests uses a qemu's virtual test device, named testdev, for services like 
printing, exiting, query memory size etc.
+See file testdev.txt for more details.
+
+To create the tests' images just type 'make' in this directory.
+Tests' images created in ./<ARCH>/*.flat
+
+An example of a test invocation:
+Using qemu-kvm:
+
+qemu-kvm -device testdev,chardev=testlog -chardev file,id=testlog,path=msr.out 
-serial stdio -kernel ./x86/msr.flat
+This invocation runs the msr test case. The test outputs to stdio.
+
+Using qemu (supported since qemu 1.3):
+qemu-system-x86_64 -enable-kvm -device pc-testdev -serial stdio -device 
isa-debug-exit,iobase=0xf4,iosize=0x4 -kernel ./x86/msr.flat
+
+Or use a runner script to detect the correct invocation:
+./x86-run ./x86/msr.flat
+To select a specific qemu binary, specify the QEMU=<path> environment:
+QEMU=/tmp/qemu/x86_64-softmmu/qemu-system-x86_64 ./x86-run ./x86/msr.flat
+
+The exit status of the binary (and the script) is inconsistent: with
+qemu-system, after the unittest is done, the exit status of qemu is 1,
+different from the 'old style' qemu-kvm, whose exit status in successful
+completion is 0.
+
+Directory structure:
+.:  Makefile and config files for the tests
+./lib: general services for the tests
+./lib/<ARCH>: architecture dependent services for the tests
+./<ARCH>: the sources of the tests and the created objects/images
+
diff --git a/kvm-unittest/api/api-sample.cc b/kvm-unittest/api/api-sample.cc
new file mode 100644
index 0000000..524ad7b
--- /dev/null
+++ b/kvm-unittest/api/api-sample.cc
@@ -0,0 +1,30 @@
+
+#include "api/kvmxx.hh"
+#include "api/identity.hh"
+#include "api/exception.hh"
+#include "stdio.h"
+
+static int global = 0;
+
+static void set_global()
+{
+    global = 1;
+}
+
+int test_main(int ac, char** av)
+{
+    kvm::system system;
+    kvm::vm vm(system);
+    mem_map memmap(vm);
+    identity::vm ident_vm(vm, memmap);
+    kvm::vcpu vcpu(vm, 0);
+    identity::vcpu thread(vcpu, set_global);
+    vcpu.run();
+    printf("global %d\n", global);
+    return global == 1 ? 0 : 1;
+}
+
+int main(int ac, char** av)
+{
+    return try_main(test_main, ac, av);
+}
diff --git a/kvm-unittest/api/dirty-log-perf.cc 
b/kvm-unittest/api/dirty-log-perf.cc
new file mode 100644
index 0000000..7f2488e
--- /dev/null
+++ b/kvm-unittest/api/dirty-log-perf.cc
@@ -0,0 +1,144 @@
+#include "kvmxx.hh"
+#include "memmap.hh"
+#include "identity.hh"
+#include <boost/thread/thread.hpp>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+
+namespace {
+
+const int page_size    = 4096;
+int64_t nr_total_pages = 256 * 1024;
+int64_t nr_slot_pages  = 256 * 1024;
+
+// Return the current time in nanoseconds.
+uint64_t time_ns()
+{
+    struct timespec ts;
+
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    return ts.tv_sec * (uint64_t)1000000000 + ts.tv_nsec;
+}
+
+// Update nr_to_write pages selected from nr_pages pages.
+void write_mem(void* slot_head, int64_t nr_to_write, int64_t nr_pages)
+{
+    char* var = static_cast<char*>(slot_head);
+    int64_t interval = nr_pages / nr_to_write;
+
+    for (int64_t i = 0; i < nr_to_write; ++i) {
+        ++(*var);
+        var += interval * page_size;
+    }
+}
+
+using boost::ref;
+using std::tr1::bind;
+
+// Let the guest update nr_to_write pages selected from nr_pages pages.
+void do_guest_write(kvm::vcpu& vcpu, void* slot_head,
+                    int64_t nr_to_write, int64_t nr_pages)
+{
+    identity::vcpu guest_write_thread(vcpu, bind(write_mem, ref(slot_head),
+                                                 nr_to_write, nr_pages));
+    vcpu.run();
+}
+
+// Check how long it takes to update dirty log.
+void check_dirty_log(kvm::vcpu& vcpu, mem_slot& slot, void* slot_head)
+{
+    slot.set_dirty_logging(true);
+    slot.update_dirty_log();
+
+    for (int64_t i = 1; i <= nr_slot_pages; i *= 2) {
+        do_guest_write(vcpu, slot_head, i, nr_slot_pages);
+
+        uint64_t start_ns = time_ns();
+        slot.update_dirty_log();
+        uint64_t end_ns = time_ns();
+
+        printf("get dirty log: %10lld ns for %10lld dirty pages\n",
+               end_ns - start_ns, i);
+    }
+
+    slot.set_dirty_logging(false);
+}
+
+}
+
+void parse_options(int ac, char **av)
+{
+    int opt;
+    char *endptr;
+
+    while ((opt = getopt(ac, av, "n:m:")) != -1) {
+        switch (opt) {
+        case 'n':
+            errno = 0;
+            nr_slot_pages = strtol(optarg, &endptr, 10);
+            if (errno || endptr == optarg) {
+                printf("dirty-log-perf: Invalid number: -n %s\n", optarg);
+                exit(1);
+            }
+            if (*endptr == 'k' || *endptr == 'K') {
+                nr_slot_pages *= 1024;
+            }
+            break;
+        case 'm':
+            errno = 0;
+            nr_total_pages = strtol(optarg, &endptr, 10);
+            if (errno || endptr == optarg) {
+                printf("dirty-log-perf: Invalid number: -m %s\n", optarg);
+                exit(1);
+            }
+            if (*endptr == 'k' || *endptr == 'K') {
+                nr_total_pages *= 1024;
+            }
+            break;
+        default:
+            printf("dirty-log-perf: Invalid option\n");
+            exit(1);
+        }
+    }
+
+    if (nr_slot_pages > nr_total_pages) {
+        printf("dirty-log-perf: Invalid setting: slot %lld > mem %lld\n",
+               nr_slot_pages, nr_total_pages);
+        exit(1);
+    }
+    printf("dirty-log-perf: %lld slot pages / %lld mem pages\n",
+           nr_slot_pages, nr_total_pages);
+}
+
+int main(int ac, char **av)
+{
+    kvm::system sys;
+    kvm::vm vm(sys);
+    mem_map memmap(vm);
+
+    parse_options(ac, av);
+
+    void* mem_head;
+    int64_t mem_size = nr_total_pages * page_size;
+    if (posix_memalign(&mem_head, page_size, mem_size)) {
+        printf("dirty-log-perf: Could not allocate guest memory.\n");
+        exit(1);
+    }
+    uint64_t mem_addr = reinterpret_cast<uint64_t>(mem_head);
+
+    identity::hole hole(mem_head, mem_size);
+    identity::vm ident_vm(vm, memmap, hole);
+    kvm::vcpu vcpu(vm, 0);
+
+    uint64_t slot_size = nr_slot_pages * page_size;
+    uint64_t next_size = mem_size - slot_size;
+    uint64_t next_addr = mem_addr + slot_size;
+    mem_slot slot(memmap, mem_addr, slot_size, mem_head);
+    mem_slot other_slot(memmap, next_addr, next_size, (void *)next_addr);
+
+    // pre-allocate shadow pages
+    do_guest_write(vcpu, mem_head, nr_total_pages, nr_total_pages);
+    check_dirty_log(vcpu, slot, mem_head);
+    return 0;
+}
diff --git a/kvm-unittest/api/dirty-log.cc b/kvm-unittest/api/dirty-log.cc
new file mode 100644
index 0000000..1e4ef9e
--- /dev/null
+++ b/kvm-unittest/api/dirty-log.cc
@@ -0,0 +1,78 @@
+#include "kvmxx.hh"
+#include "memmap.hh"
+#include "identity.hh"
+#include <boost/thread/thread.hpp>
+#include <stdlib.h>
+#include <stdio.h>
+
+namespace {
+
+void delay_loop(unsigned n)
+{
+    for (unsigned i = 0; i < n; ++i) {
+        asm volatile("pause");
+    }
+ }
+
+void write_mem(volatile bool& running, volatile int* shared_var)
+{
+    while (running) {
+        ++*shared_var;
+        delay_loop(1000);
+    }
+}
+
+void check_dirty_log(mem_slot& slot,
+                     volatile bool& running,
+                     volatile int* shared_var,
+                     int& nr_fail)
+{
+    uint64_t shared_var_gpa = reinterpret_cast<uint64_t>(shared_var);
+    slot.set_dirty_logging(true);
+    slot.update_dirty_log();
+    for (int i = 0; i < 10000000; ++i) {
+        int sample1 = *shared_var;
+        delay_loop(600);
+        int sample2 = *shared_var;
+        slot.update_dirty_log();
+        if (!slot.is_dirty(shared_var_gpa) && sample1 != sample2) {
+            ++nr_fail;
+        }
+    }
+    running = false;
+    slot.set_dirty_logging(false);
+}
+
+}
+
+using boost::ref;
+using std::tr1::bind;
+
+int main(int ac, char **av)
+{
+    kvm::system sys;
+    kvm::vm vm(sys);
+    mem_map memmap(vm);
+    void* logged_slot_virt;
+    posix_memalign(&logged_slot_virt, 4096, 4096);
+    int* shared_var = static_cast<int*>(logged_slot_virt);
+    identity::hole hole(logged_slot_virt, 4096);
+    identity::vm ident_vm(vm, memmap, hole);
+    kvm::vcpu vcpu(vm, 0);
+    bool running = true;
+    int nr_fail = 0;
+    mem_slot logged_slot(memmap,
+                         reinterpret_cast<uint64_t>(logged_slot_virt),
+                         4096, logged_slot_virt);
+    boost::thread host_poll_thread(check_dirty_log, ref(logged_slot),
+                                   ref(running),
+                                   ref(shared_var), ref(nr_fail));
+    identity::vcpu guest_write_thread(vcpu,
+                                      bind(write_mem,
+                                           ref(running),
+                                           ref(shared_var)));
+    vcpu.run();
+    host_poll_thread.join();
+    printf("Dirty bitmap failures: %d\n", nr_fail);
+    return nr_fail == 0 ? 0 : 1;
+}
diff --git a/kvm-unittest/api/exception.cc b/kvm-unittest/api/exception.cc
new file mode 100644
index 0000000..910bdff
--- /dev/null
+++ b/kvm-unittest/api/exception.cc
@@ -0,0 +1,33 @@
+#include "exception.hh"
+#include <cstdio>
+#include <cstring>
+
+errno_exception::errno_exception(int errno)
+    : _errno(errno)
+{
+}
+
+int errno_exception::errno() const
+{
+    return _errno;
+}
+
+const char *errno_exception::what()
+{
+    std::snprintf(_buf, sizeof _buf, "error: %s (%d)",
+                 std::strerror(_errno), _errno);
+    return _buf;
+}
+
+int try_main(int (*main)(int argc, char** argv), int argc, char** argv,
+            int ret_on_exception)
+{
+    try {
+        return main(argc, argv);
+    } catch (std::exception& e) {
+        std::fprintf(stderr, "exception: %s\n", e.what());
+    } catch (...) {
+        std::fprintf(stderr, "unknown exception\n");
+    }
+    return ret_on_exception;
+}
diff --git a/kvm-unittest/api/exception.hh b/kvm-unittest/api/exception.hh
new file mode 100644
index 0000000..f78d9a1
--- /dev/null
+++ b/kvm-unittest/api/exception.hh
@@ -0,0 +1,19 @@
+#ifndef EXCEPTION_HH
+#define EXCEPTION_HH
+
+#include <exception>
+
+class errno_exception : public std::exception {
+public:
+    explicit errno_exception(int err_no);
+    int errno() const;
+    virtual const char *what();
+private:
+    int _errno;
+    char _buf[1000];
+};
+
+int try_main(int (*main)(int argc, char** argv), int argc, char** argv,
+            int ret_on_exception = 127);
+
+#endif
diff --git a/kvm-unittest/api/identity.cc b/kvm-unittest/api/identity.cc
new file mode 100644
index 0000000..e04231b
--- /dev/null
+++ b/kvm-unittest/api/identity.cc
@@ -0,0 +1,95 @@
+
+#include "identity.hh"
+#include <stdio.h>
+
+namespace identity {
+
+typedef unsigned long ulong;
+
+hole::hole()
+    : address(), size()
+{
+}
+
+hole::hole(void* address, size_t size)
+    : address(address), size(size)
+{
+}
+
+vm::vm(kvm::vm& vm, mem_map& mmap, hole h)
+{
+    uint64_t hole_gpa = reinterpret_cast<uint64_t>(h.address);
+    char* hole_hva = static_cast<char*>(h.address);
+    if (h.address) {
+        _slots.push_back(mem_slot_ptr(new mem_slot(mmap, 0, hole_gpa, NULL)));
+    }
+    uint64_t hole_end = hole_gpa + h.size;
+    uint64_t end = 3U << 30;
+    _slots.push_back(mem_slot_ptr(new mem_slot(mmap, hole_end,
+                                               end - hole_end,
+                                               hole_hva + h.size)));
+    vm.set_tss_addr(3UL << 30);
+}
+
+void vcpu::setup_sregs()
+{
+    kvm_sregs sregs = { };
+    kvm_segment dseg = { };
+    dseg.base = 0; dseg.limit = -1U; dseg.type = 3; dseg.present = 1;
+    dseg.dpl = 3; dseg.db = 1; dseg.s = 1; dseg.l = 0; dseg.g = 1;
+    kvm_segment cseg = dseg;
+    cseg.type = 11;
+
+    sregs.cs = cseg; asm ("mov %%cs, %0" : "=rm"(sregs.cs.selector));
+    sregs.ds = dseg; asm ("mov %%ds, %0" : "=rm"(sregs.ds.selector));
+    sregs.es = dseg; asm ("mov %%es, %0" : "=rm"(sregs.es.selector));
+    sregs.fs = dseg; asm ("mov %%fs, %0" : "=rm"(sregs.fs.selector));
+    sregs.gs = dseg; asm ("mov %%gs, %0" : "=rm"(sregs.gs.selector));
+    sregs.ss = dseg; asm ("mov %%ss, %0" : "=rm"(sregs.ss.selector));
+
+    uint32_t gsbase;
+    asm ("mov %%gs:0, %0" : "=r"(gsbase));
+    sregs.gs.base = gsbase;
+
+    sregs.tr.base = reinterpret_cast<ulong>(&*_stack.begin());
+    sregs.tr.type = 11;
+    sregs.tr.s = 0;
+    sregs.tr.present = 1;
+
+    sregs.cr0 = 0x11; /* PE, ET, !PG */
+    sregs.cr4 = 0;
+    sregs.efer = 0;
+    sregs.apic_base = 0xfee00000;
+    _vcpu.set_sregs(sregs);
+}
+
+void vcpu::thunk(vcpu* zis)
+{
+    zis->_guest_func();
+    asm volatile("outb %%al, %%dx" : : "a"(0), "d"(0));
+}
+
+void vcpu::setup_regs()
+{
+    kvm_regs regs = {};
+    regs.rflags = 0x3202;
+    regs.rsp = reinterpret_cast<ulong>(&*_stack.end());
+    regs.rsp &= ~15UL;
+    ulong* sp = reinterpret_cast<ulong *>(regs.rsp);
+    *--sp = reinterpret_cast<ulong>((char*)this);
+    *--sp = 0;
+    regs.rsp = reinterpret_cast<ulong>(sp);
+    regs.rip = reinterpret_cast<ulong>(&vcpu::thunk);
+    printf("rip %llx\n", regs.rip);
+    _vcpu.set_regs(regs);
+}
+
+vcpu::vcpu(kvm::vcpu& vcpu, std::tr1::function<void ()> guest_func,
+           unsigned long stack_size)
+    : _vcpu(vcpu), _guest_func(guest_func), _stack(stack_size)
+{
+    setup_sregs();
+    setup_regs();
+}
+
+}
diff --git a/kvm-unittest/api/identity.hh b/kvm-unittest/api/identity.hh
new file mode 100644
index 0000000..4491043
--- /dev/null
+++ b/kvm-unittest/api/identity.hh
@@ -0,0 +1,43 @@
+#ifndef API_IDENTITY_HH
+#define API_IDENTITY_HH
+
+#include "kvmxx.hh"
+#include "memmap.hh"
+#include <tr1/functional>
+#include <tr1/memory>
+#include <vector>
+
+namespace identity {
+
+struct hole {
+    hole();
+    hole(void* address, size_t size);
+    void* address;
+    size_t size;
+};
+
+class vm {
+public:
+    vm(kvm::vm& vm, mem_map& mmap, hole address_space_hole = hole());
+private:
+    typedef std::tr1::shared_ptr<mem_slot> mem_slot_ptr;
+    std::vector<mem_slot_ptr> _slots;
+};
+
+class vcpu {
+public:
+    vcpu(kvm::vcpu& vcpu, std::tr1::function<void ()> guest_func,
+        unsigned long stack_size = 256 * 1024);
+private:
+    static void thunk(vcpu* vcpu);
+    void setup_regs();
+    void setup_sregs();
+private:
+    kvm::vcpu& _vcpu;
+    std::tr1::function<void ()> _guest_func;
+    std::vector<char> _stack;
+};
+
+}
+
+#endif
diff --git a/kvm-unittest/api/kvmxx.cc b/kvm-unittest/api/kvmxx.cc
new file mode 100644
index 0000000..7ebebb5
--- /dev/null
+++ b/kvm-unittest/api/kvmxx.cc
@@ -0,0 +1,194 @@
+#include "kvmxx.hh"
+#include "exception.hh"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <memory>
+#include <algorithm>
+
+namespace kvm {
+
+static long check_error(long r)
+{
+    if (r == -1) {
+       throw errno_exception(errno);
+    }
+    return r;
+}
+
+fd::fd(int fd)
+    : _fd(fd)
+{
+}
+
+fd::fd(const fd& other)
+    : _fd(::dup(other._fd))
+{
+    check_error(_fd);
+}
+
+fd::fd(std::string device_node, int flags)
+    : _fd(::open(device_node.c_str(), flags))
+{
+    check_error(_fd);
+}
+
+long fd::ioctl(unsigned nr, long arg)
+{
+    return check_error(::ioctl(_fd, nr, arg));
+}
+
+vcpu::vcpu(vm& vm, int id)
+    : _vm(vm), _fd(vm._fd.ioctl(KVM_CREATE_VCPU, id)), _shared(NULL)
+    , _mmap_size(_vm._system._fd.ioctl(KVM_GET_VCPU_MMAP_SIZE, 0))
+
+{
+    kvm_run *shared = static_cast<kvm_run*>(::mmap(NULL, _mmap_size,
+                                                  PROT_READ | PROT_WRITE,
+                                                  MAP_SHARED,
+                                                  _fd.get(), 0));
+    if (shared == MAP_FAILED) {
+       throw errno_exception(errno);
+    }
+    _shared = shared;
+}
+
+vcpu::~vcpu()
+{
+    munmap(_shared, _mmap_size);
+}
+
+void vcpu::run()
+{
+    _fd.ioctl(KVM_RUN, 0);
+}
+
+kvm_regs vcpu::regs()
+{
+    kvm_regs regs;
+    _fd.ioctlp(KVM_GET_REGS, &regs);
+    return regs;
+}
+
+void vcpu::set_regs(const kvm_regs& regs)
+{
+    _fd.ioctlp(KVM_SET_REGS, const_cast<kvm_regs*>(&regs));
+}
+
+kvm_sregs vcpu::sregs()
+{
+    kvm_sregs sregs;
+    _fd.ioctlp(KVM_GET_SREGS, &sregs);
+    return sregs;
+}
+
+void vcpu::set_sregs(const kvm_sregs& sregs)
+{
+    _fd.ioctlp(KVM_SET_SREGS, const_cast<kvm_sregs*>(&sregs));
+}
+
+class vcpu::kvm_msrs_ptr {
+public:
+    explicit kvm_msrs_ptr(size_t nmsrs);
+    ~kvm_msrs_ptr() { ::free(_kvm_msrs); }
+    kvm_msrs* operator->() { return _kvm_msrs; }
+    kvm_msrs* get() { return _kvm_msrs; }
+private:
+    kvm_msrs* _kvm_msrs;
+};
+
+vcpu::kvm_msrs_ptr::kvm_msrs_ptr(size_t nmsrs)
+    : _kvm_msrs(0)
+{
+    size_t size = sizeof(kvm_msrs) + sizeof(kvm_msr_entry) * nmsrs;
+    _kvm_msrs = static_cast<kvm_msrs*>(::malloc(size));
+    if (!_kvm_msrs) {
+       throw std::bad_alloc();
+    }
+}
+
+std::vector<kvm_msr_entry> vcpu::msrs(std::vector<uint32_t> indices)
+{
+    kvm_msrs_ptr msrs(indices.size());
+    msrs->nmsrs = indices.size();
+    for (unsigned i = 0; i < msrs->nmsrs; ++i) {
+       msrs->entries[i].index = indices[i];
+    }
+    _fd.ioctlp(KVM_GET_MSRS, msrs.get());
+    return std::vector<kvm_msr_entry>(msrs->entries,
+                                     msrs->entries + msrs->nmsrs);
+}
+
+void vcpu::set_msrs(const std::vector<kvm_msr_entry>& msrs)
+{
+    kvm_msrs_ptr _msrs(msrs.size());
+    _msrs->nmsrs = msrs.size();
+    std::copy(msrs.begin(), msrs.end(), _msrs->entries);
+    _fd.ioctlp(KVM_SET_MSRS, _msrs.get());
+}
+
+void vcpu::set_debug(uint64_t dr[8], bool enabled, bool singlestep)
+{
+    kvm_guest_debug gd;
+
+    gd.control = 0;
+    if (enabled) {
+       gd.control |= KVM_GUESTDBG_ENABLE;
+    }
+    if (singlestep) {
+       gd.control |= KVM_GUESTDBG_SINGLESTEP;
+    }
+    for (int i = 0; i < 8; ++i) {
+       gd.arch.debugreg[i] = dr[i];
+    }
+    _fd.ioctlp(KVM_SET_GUEST_DEBUG, &gd);
+}
+
+vm::vm(system& system)
+    : _system(system), _fd(system._fd.ioctl(KVM_CREATE_VM, 0))
+{
+}
+
+void vm::set_memory_region(int slot, void *addr, uint64_t gpa, size_t len,
+                           uint32_t flags)
+{
+    struct kvm_userspace_memory_region umr;
+
+    umr.slot = slot;
+    umr.flags = flags;
+    umr.guest_phys_addr = gpa;
+    umr.memory_size = len;
+    umr.userspace_addr = reinterpret_cast<uint64_t>(addr);
+    _fd.ioctlp(KVM_SET_USER_MEMORY_REGION, &umr);
+}
+
+void vm::get_dirty_log(int slot, void *log)
+{
+    struct kvm_dirty_log kdl;
+    kdl.slot = slot;
+    kdl.dirty_bitmap = log;
+    _fd.ioctlp(KVM_GET_DIRTY_LOG, &kdl);
+}
+
+void vm::set_tss_addr(uint32_t addr)
+{
+    _fd.ioctl(KVM_SET_TSS_ADDR, addr);
+}
+
+system::system(std::string device_node)
+    : _fd(device_node, O_RDWR)
+{
+}
+
+bool system::check_extension(int extension)
+{
+    return _fd.ioctl(KVM_CHECK_EXTENSION, extension);
+}
+
+int system::get_extension_int(int extension)
+{
+    return _fd.ioctl(KVM_CHECK_EXTENSION, extension);
+}
+
+};
diff --git a/kvm-unittest/api/kvmxx.hh b/kvm-unittest/api/kvmxx.hh
new file mode 100644
index 0000000..1dcb41d
--- /dev/null
+++ b/kvm-unittest/api/kvmxx.hh
@@ -0,0 +1,85 @@
+#ifndef KVMXX_H
+#define KVMXX_H
+
+#include <string>
+#include <signal.h>
+#include <unistd.h>
+#include <vector>
+#include <errno.h>
+#include <linux/kvm.h>
+#include <stdint.h>
+
+namespace kvm {
+
+class system;
+class vm;
+class vcpu;
+class fd;
+
+class fd {
+public:
+    explicit fd(int n);
+    explicit fd(std::string path, int flags);
+    fd(const fd& other);
+    ~fd() { ::close(_fd); }
+    int get() { return _fd; }
+    long ioctl(unsigned nr, long arg);
+    long ioctlp(unsigned nr, void *arg) {
+       return ioctl(nr, reinterpret_cast<long>(arg));
+    }
+private:
+    int _fd;
+};
+
+class vcpu {
+public:
+    vcpu(vm& vm, int fd);
+    ~vcpu();
+    void run();
+    kvm_run *shared();
+    kvm_regs regs();
+    void set_regs(const kvm_regs& regs);
+    kvm_sregs sregs();
+    void set_sregs(const kvm_sregs& sregs);
+    std::vector<kvm_msr_entry> msrs(std::vector<uint32_t> indices);
+    void set_msrs(const std::vector<kvm_msr_entry>& msrs);
+    void set_debug(uint64_t dr[8], bool enabled, bool singlestep);
+private:
+    class kvm_msrs_ptr;
+private:
+    vm& _vm;
+    fd _fd;
+    kvm_run *_shared;
+    unsigned _mmap_size;
+    friend class vm;
+};
+
+class vm {
+public:
+    explicit vm(system& system);
+    void set_memory_region(int slot, void *addr, uint64_t gpa, size_t len,
+                           uint32_t flags = 0);
+    void get_dirty_log(int slot, void *log);
+    void set_tss_addr(uint32_t addr);
+    system& sys() { return _system; }
+private:
+    system& _system;
+    fd _fd;
+    friend class system;
+    friend class vcpu;
+};
+
+class system {
+public:
+    explicit system(std::string device_node = "/dev/kvm");
+    bool check_extension(int extension);
+    int get_extension_int(int extension);
+private:
+    fd _fd;
+    friend class vcpu;
+    friend class vm;
+};
+
+};
+
+#endif
diff --git a/kvm-unittest/api/memmap.cc b/kvm-unittest/api/memmap.cc
new file mode 100644
index 0000000..c625852
--- /dev/null
+++ b/kvm-unittest/api/memmap.cc
@@ -0,0 +1,76 @@
+
+#include "memmap.hh"
+
+mem_slot::mem_slot(mem_map& map, uint64_t gpa, uint64_t size, void* hva)
+    : _map(map)
+    , _slot(map._free_slots.top())
+    , _gpa(gpa)
+    , _size(size)
+    , _hva(hva)
+    , _dirty_log_enabled(false)
+    , _log()
+{
+    map._free_slots.pop();
+    update();
+}
+
+mem_slot::~mem_slot()
+{
+    _size = 0;
+    try {
+        update();
+        _map._free_slots.push(_slot);
+    } catch (...) {
+        // can't do much if we can't undo slot registration - leak the slot
+    }
+}
+
+void mem_slot::set_dirty_logging(bool enabled)
+{
+    if (_dirty_log_enabled != enabled) {
+        _dirty_log_enabled = enabled;
+        if (enabled) {
+            int logsize = ((_size >> 12) + bits_per_word - 1) / bits_per_word;
+            _log.resize(logsize);
+        } else {
+            _log.resize(0);
+        }
+        update();
+    }
+}
+
+void mem_slot::update()
+{
+    uint32_t flags = 0;
+    if (_dirty_log_enabled) {
+        flags |= KVM_MEM_LOG_DIRTY_PAGES;
+    }
+    _map._vm.set_memory_region(_slot, _hva, _gpa, _size, flags);
+}
+
+bool mem_slot::dirty_logging() const
+{
+    return _dirty_log_enabled;
+}
+
+void mem_slot::update_dirty_log()
+{
+    _map._vm.get_dirty_log(_slot, &_log[0]);
+}
+
+bool mem_slot::is_dirty(uint64_t gpa) const
+{
+    uint64_t pagenr = (gpa - _gpa) >> 12;
+    ulong wordnr = pagenr / bits_per_word;
+    ulong bit = 1ULL << (pagenr % bits_per_word);
+    return _log[wordnr] & bit;
+}
+
+mem_map::mem_map(kvm::vm& vm)
+    : _vm(vm)
+{
+    int nr_slots = vm.sys().get_extension_int(KVM_CAP_NR_MEMSLOTS);
+    for (int i = 0; i < nr_slots; ++i) {
+        _free_slots.push(i);
+    }
+}
diff --git a/kvm-unittest/api/memmap.hh b/kvm-unittest/api/memmap.hh
new file mode 100644
index 0000000..59ec619
--- /dev/null
+++ b/kvm-unittest/api/memmap.hh
@@ -0,0 +1,43 @@
+#ifndef MEMMAP_HH
+#define MEMMAP_HH
+
+#include "kvmxx.hh"
+#include <stdint.h>
+#include <vector>
+#include <stack>
+
+class mem_map;
+class mem_slot;
+
+class mem_slot {
+public:
+    mem_slot(mem_map& map, uint64_t gpa, uint64_t size, void *hva);
+    ~mem_slot();
+    void set_dirty_logging(bool enabled);
+    bool dirty_logging() const;
+    void update_dirty_log();
+    bool is_dirty(uint64_t gpa) const;
+private:
+    void update();
+private:
+    typedef unsigned long ulong;
+    static const int bits_per_word = sizeof(ulong) * 8;
+    mem_map& _map;
+    int _slot;
+    uint64_t _gpa;
+    uint64_t _size;
+    void *_hva;
+    bool _dirty_log_enabled;
+    std::vector<ulong> _log;
+};
+
+class mem_map {
+public:
+    mem_map(kvm::vm& vm);
+private:
+    kvm::vm& _vm;
+    std::stack<int> _free_slots;
+    friend class mem_slot;
+};
+
+#endif
diff --git a/kvm-unittest/config-i386.mak b/kvm-unittest/config-i386.mak
new file mode 100644
index 0000000..de52f3d
--- /dev/null
+++ b/kvm-unittest/config-i386.mak
@@ -0,0 +1,13 @@
+TEST_DIR=x86
+cstart.o = $(TEST_DIR)/cstart.o
+bits = 32
+ldarch = elf32-i386
+CFLAGS += -D__i386__
+CFLAGS += -I $(KERNELDIR)/include
+
+tests = $(TEST_DIR)/taskswitch.flat $(TEST_DIR)/taskswitch2.flat
+
+include config-x86-common.mak
+
+$(TEST_DIR)/taskswitch.elf: $(cstart.o) $(TEST_DIR)/taskswitch.o
+$(TEST_DIR)/taskswitch2.elf: $(cstart.o) $(TEST_DIR)/taskswitch2.o
diff --git a/kvm-unittest/config-ia64.mak b/kvm-unittest/config-ia64.mak
new file mode 100644
index 0000000..d9350fc
--- /dev/null
+++ b/kvm-unittest/config-ia64.mak
@@ -0,0 +1,7 @@
+bits = 64
+CFLAGS += -m64
+CFLAGS += -D__ia64__
+CFLAGS += -I../include/ia64
+
+all:
+
diff --git a/kvm-unittest/config-powerpc-440.mak 
b/kvm-unittest/config-powerpc-440.mak
new file mode 100644
index 0000000..bb85971
--- /dev/null
+++ b/kvm-unittest/config-powerpc-440.mak
@@ -0,0 +1,15 @@
+
+
+# for some reason binutils hates tlbsx unless we say we're 405  :(
+CFLAGS += -Wa,-m405 -I lib/powerpc/44x
+
+cflatobjs += \
+       lib/powerpc/44x/map.o \
+       lib/powerpc/44x/tlbwe.o \
+       lib/powerpc/44x/timebase.o
+
+simpletests += \
+       powerpc/44x/tlbsx.bin \
+       powerpc/44x/tlbwe_16KB.bin \
+       powerpc/44x/tlbwe_hole.bin \
+       powerpc/44x/tlbwe.bin
diff --git a/kvm-unittest/config-powerpc.mak b/kvm-unittest/config-powerpc.mak
new file mode 100644
index 0000000..d053569
--- /dev/null
+++ b/kvm-unittest/config-powerpc.mak
@@ -0,0 +1,39 @@
+CFLAGS += -I../include/powerpc
+CFLAGS += -Wa,-mregnames -I lib
+CFLAGS += -ffreestanding
+
+cstart := powerpc/cstart.o
+
+cflatobjs += \
+       lib/powerpc/io.o
+
+$(libcflat): LDFLAGS += -nostdlib
+
+# these tests do not use libcflat
+simpletests := \
+       powerpc/spin.bin \
+       powerpc/io.bin \
+       powerpc/sprg.bin
+
+# theses tests use cstart.o, libcflat, and libgcc
+tests := \
+       powerpc/exit.bin \
+       powerpc/helloworld.bin
+
+include config-powerpc-$(PROCESSOR).mak
+
+
+all: kvmtrace kvmctl $(libcflat) $(simpletests) $(tests)
+
+$(simpletests): %.bin: %.o
+       $(CC) -nostdlib $^ -Wl,-T,flat.lds -o $@
+
+$(tests): %.bin: $(cstart) %.o $(libcflat)
+       $(CC) -nostdlib $^ $(libgcc) -Wl,-T,flat.lds -o $@
+
+kvmctl_objs = main-ppc.o iotable.o ../libkvm/libkvm.a
+
+arch_clean:
+       $(RM) $(simpletests) $(tests) $(cstart)
+       $(RM) $(patsubst %.bin, %.elf, $(simpletests) $(tests))
+       $(RM) $(patsubst %.bin, %.o, $(simpletests) $(tests))
diff --git a/kvm-unittest/config-x86-common.mak 
b/kvm-unittest/config-x86-common.mak
new file mode 100644
index 0000000..bf88c67
--- /dev/null
+++ b/kvm-unittest/config-x86-common.mak
@@ -0,0 +1,122 @@
+#This is a make file with common rules for both x86 & x86-64
+
+CFLAGS += -I../include/x86
+
+all: test_cases
+
+cflatobjs += \
+       lib/x86/io.o \
+       lib/x86/smp.o
+
+cflatobjs += lib/x86/vm.o
+cflatobjs += lib/x86/fwcfg.o
+cflatobjs += lib/x86/apic.o
+cflatobjs += lib/x86/atomic.o
+cflatobjs += lib/x86/desc.o
+cflatobjs += lib/x86/isr.o
+cflatobjs += lib/x86/pci.o
+
+$(libcflat): LDFLAGS += -nostdlib
+$(libcflat): CFLAGS += -ffreestanding -I lib
+
+CFLAGS += -m$(bits)
+
+libgcc := $(shell $(CC) -m$(bits) --print-libgcc-file-name)
+
+FLATLIBS = lib/libcflat.a $(libgcc)
+%.elf: %.o $(FLATLIBS) flat.lds
+       $(CC) $(CFLAGS) -nostdlib -o $@ -Wl,-T,flat.lds $(filter %.o, $^) 
$(FLATLIBS)
+
+%.flat: %.elf
+       objcopy -O elf32-i386 $^ $@
+
+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)/eventinj.flat \
+               $(TEST_DIR)/s3.flat $(TEST_DIR)/pmu.flat \
+               $(TEST_DIR)/tsc_adjust.flat $(TEST_DIR)/asyncpf.flat \
+               $(TEST_DIR)/init.flat
+
+ifdef API
+tests-common += api/api-sample
+tests-common += api/dirty-log
+tests-common += api/dirty-log-perf
+endif
+
+tests_and_config = $(TEST_DIR)/*.flat $(TEST_DIR)/unittests.cfg
+
+test_cases: $(tests-common) $(tests)
+
+$(TEST_DIR)/%.o: CFLAGS += -std=gnu99 -ffreestanding -I lib -I lib/x86
+
+$(TEST_DIR)/access.elf: $(cstart.o) $(TEST_DIR)/access.o
+
+$(TEST_DIR)/hypercall.elf: $(cstart.o) $(TEST_DIR)/hypercall.o
+
+$(TEST_DIR)/sieve.elf: $(cstart.o) $(TEST_DIR)/sieve.o
+
+$(TEST_DIR)/vmexit.elf: $(cstart.o) $(TEST_DIR)/vmexit.o
+
+$(TEST_DIR)/smptest.elf: $(cstart.o) $(TEST_DIR)/smptest.o
+
+$(TEST_DIR)/emulator.elf: $(cstart.o) $(TEST_DIR)/emulator.o
+
+$(TEST_DIR)/port80.elf: $(cstart.o) $(TEST_DIR)/port80.o
+
+$(TEST_DIR)/tsc.elf: $(cstart.o) $(TEST_DIR)/tsc.o
+
+$(TEST_DIR)/tsc_adjust.elf: $(cstart.o) $(TEST_DIR)/tsc_adjust.o
+
+$(TEST_DIR)/apic.elf: $(cstart.o) $(TEST_DIR)/apic.o
+
+$(TEST_DIR)/init.elf: $(cstart.o) $(TEST_DIR)/init.o
+
+$(TEST_DIR)/realmode.elf: $(TEST_DIR)/realmode.o
+       $(CC) -m32 -nostdlib -o $@ -Wl,-T,$(TEST_DIR)/realmode.lds $^
+
+$(TEST_DIR)/realmode.o: bits = 32
+
+$(TEST_DIR)/msr.elf: $(cstart.o) $(TEST_DIR)/msr.o
+
+$(TEST_DIR)/idt_test.elf: $(cstart.o) $(TEST_DIR)/idt_test.o
+
+$(TEST_DIR)/xsave.elf: $(cstart.o) $(TEST_DIR)/xsave.o
+
+$(TEST_DIR)/rmap_chain.elf: $(cstart.o) $(TEST_DIR)/rmap_chain.o
+
+$(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
+
+$(TEST_DIR)/s3.elf: $(cstart.o) $(TEST_DIR)/s3.o
+
+$(TEST_DIR)/pmu.elf: $(cstart.o) $(TEST_DIR)/pmu.o
+
+$(TEST_DIR)/asyncpf.elf: $(cstart.o) $(TEST_DIR)/asyncpf.o
+
+$(TEST_DIR)/pcid.elf: $(cstart.o) $(TEST_DIR)/pcid.o
+
+$(TEST_DIR)/vmx.elf: $(cstart.o) $(TEST_DIR)/vmx.o $(TEST_DIR)/vmx_tests.o
+
+arch_clean:
+       $(RM) $(TEST_DIR)/*.o $(TEST_DIR)/*.flat $(TEST_DIR)/*.elf \
+       $(TEST_DIR)/.*.d $(TEST_DIR)/lib/.*.d $(TEST_DIR)/lib/*.o
+
+api/%.o: CFLAGS += -m32
+
+api/%: LDLIBS += -lstdc++ -lboost_thread-mt -lpthread -lrt
+api/%: LDFLAGS += -m32
+
+api/libapi.a: api/kvmxx.o api/identity.o api/exception.o api/memmap.o
+       $(AR) rcs $@ $^
+
+api/api-sample: api/api-sample.o api/libapi.a
+
+api/dirty-log: api/dirty-log.o api/libapi.a
+
+api/dirty-log-perf: api/dirty-log-perf.o api/libapi.a
diff --git a/kvm-unittest/config-x86_64.mak b/kvm-unittest/config-x86_64.mak
new file mode 100644
index 0000000..bb8ee89
--- /dev/null
+++ b/kvm-unittest/config-x86_64.mak
@@ -0,0 +1,14 @@
+TEST_DIR=x86
+cstart.o = $(TEST_DIR)/cstart64.o
+bits = 64
+ldarch = elf64-x86-64
+CFLAGS += -D__x86_64__
+
+tests = $(TEST_DIR)/access.flat $(TEST_DIR)/apic.flat \
+         $(TEST_DIR)/emulator.flat $(TEST_DIR)/idt_test.flat \
+         $(TEST_DIR)/xsave.flat $(TEST_DIR)/rmap_chain.flat \
+         $(TEST_DIR)/pcid.flat
+tests += $(TEST_DIR)/svm.flat
+tests += $(TEST_DIR)/vmx.flat
+
+include config-x86-common.mak
diff --git a/kvm-unittest/configure b/kvm-unittest/configure
new file mode 100755
index 0000000..d0c62e2
--- /dev/null
+++ b/kvm-unittest/configure
@@ -0,0 +1,92 @@
+#!/bin/bash
+
+prefix=/usr/local
+kerneldir=/lib/modules/$(uname -r)/build
+cc=gcc
+ld=ld
+objcopy=objcopy
+ar=ar
+arch=`uname -m | sed -e s/i.86/i386/`
+processor="$arch"
+cross_prefix=
+
+usage() {
+    cat <<-EOF
+       Usage: $0 [options]
+
+       Options include:
+           --arch=ARCH            architecture to compile for ($arch)
+           --cross-prefix=PREFIX  cross compiler prefix
+           --cc=CC                c compiler to use ($cc)
+           --ld=LD                ld linker to use ($ld)
+           --prefix=PREFIX        where to install things ($prefix)
+           --kerneldir=DIR        kernel build directory for kvm.h ($kerneldir)
+EOF
+    exit 1
+}
+
+while [[ "$1" = -* ]]; do
+    opt="$1"; shift
+    arg=
+    if [[ "$opt" = *=* ]]; then
+       arg="${opt#*=}"
+       opt="${opt%%=*}"
+    fi
+    case "$opt" in
+       --prefix)
+           prefix="$arg"
+           ;;
+       --kerneldir)
+           kerneldir="$arg"
+           ;;
+        --arch)
+           arch="$arg"
+           ;;
+        --processor)
+           processor="$arg"
+           ;;
+       --cross-prefix)
+           cross_prefix="$arg"
+           ;;
+       --cc)
+           cc="$arg"
+           ;;
+       --ld)
+           ld="$arg"
+           ;;
+       --help)
+           usage
+           ;;
+       *)
+           usage
+           ;;
+    esac
+done
+
+# check for dependent 32 bit libraries
+cat << EOF > lib_test.c
+#include <stdc++.h>
+#include <boost_thread-mt.h>
+#include <pthread.h>
+
+int main ()
+{}
+EOF
+$cc -m32 -o /dev/null lib_test.c &> /dev/null
+exit=$?
+if [ $exit -eq 0 ]; then
+    api=true
+fi
+rm -f lib_test.c
+
+cat <<EOF > config.mak
+PREFIX=$prefix
+KERNELDIR=$(readlink -f $kerneldir)
+ARCH=$arch
+PROCESSOR=$processor
+CC=$cross_prefix$cc
+LD=$cross_prefix$ld
+OBJCOPY=$cross_prefix$objcopy
+AR=$cross_prefix$ar
+API=$api
+EOF
diff --git a/kvm-unittest/flat.lds b/kvm-unittest/flat.lds
new file mode 100644
index 0000000..a278b56
--- /dev/null
+++ b/kvm-unittest/flat.lds
@@ -0,0 +1,21 @@
+SECTIONS
+{
+    . = 4M + SIZEOF_HEADERS;
+    stext = .;
+    .text : { *(.init) *(.text) *(.text.*) }
+    . = ALIGN(4K);
+    .data : {
+          *(.data)
+          exception_table_start = .;
+          *(.data.ex)
+         exception_table_end = .;
+         }
+    . = ALIGN(16);
+    .rodata : { *(.rodata) }
+    . = ALIGN(16);
+    .bss : { *(.bss) }
+    . = ALIGN(4K);
+    edata = .;
+}
+
+ENTRY(start)
diff --git a/kvm-unittest/formats b/kvm-unittest/formats
new file mode 100644
index 0000000..7f4ebdb
--- /dev/null
+++ b/kvm-unittest/formats
@@ -0,0 +1,31 @@
+0x00000000  %(ts)d (+%(relts)12d)  unknown (0x%(event)016x) vcpu = 
0x%(vcpu)08x  pid = 0x%(pid)08x [ 0x%(1)08x 0x%(2)08x 0x%(3)08x 0x%(4)08x 
0x%(5)08x ]
+
+0x00010001  %(ts)d (+%(relts)12d)  VMENTRY       vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x
+0x00010002  %(ts)d (+%(relts)12d)  VMEXIT        vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ exitcode = 0x%(1)08x, rip = 0x%(3)08x %(2)08x ]
+0x00020001  %(ts)d (+%(relts)12d)  PAGE_FAULT    vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ errorcode = 0x%(1)08x, virt = 0x%(3)08x %(2)08x ]
+0x00020002  %(ts)d (+%(relts)12d)  INJ_VIRQ      vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ vector = 0x%(1)02x ]
+0x00020003  %(ts)d (+%(relts)12d)  REDELIVER_EVT vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ vector = 0x%(1)02x ]
+0x00020004  %(ts)d (+%(relts)12d)  PEND_INTR     vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ vector = 0x%(1)02x ]
+0x00020005  %(ts)d (+%(relts)12d)  IO_READ       vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ port = 0x%(1)04x, size = %(2)d ]
+0x00020006  %(ts)d (+%(relts)12d)  IO_WRITE      vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ port = 0x%(1)04x, size = %(2)d ]
+0x00020007  %(ts)d (+%(relts)12d)  CR_READ       vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ CR# = %(1)d, value = 0x%(3)08x %(2)08x ]
+0x00020008  %(ts)d (+%(relts)12d)  CR_WRITE      vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ CR# = %(1)d, value = 0x%(3)08x %(2)08x ]
+0x00020009  %(ts)d (+%(relts)12d)  DR_READ       vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ DR# = %(1)d, value = 0x%(2)08x ]
+0x0002000A  %(ts)d (+%(relts)12d)  DR_WRITE      vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ DR# = %(1)d, value = 0x%(2)08x ]
+0x0002000B  %(ts)d (+%(relts)12d)  MSR_READ      vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ MSR# = 0x%(1)08x, data = 0x%(3)08x %(2)08x ]
+0x0002000C  %(ts)d (+%(relts)12d)  MSR_WRITE     vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ MSR# = 0x%(1)08x, data = 0x%(3)08x %(2)08x ]
+0x0002000D  %(ts)d (+%(relts)12d)  CPUID         vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ func = 0x%(1)08x, eax = 0x%(2)08x, ebx = 0x%(3)08x, ecx = 
0x%(4)08x edx = 0x%(5)08x]
+0x0002000E  %(ts)d (+%(relts)12d)  INTR          vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ vector = 0x%(1)02x ]
+0x0002000F  %(ts)d (+%(relts)12d)  NMI           vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x
+0x00020010  %(ts)d (+%(relts)12d)  VMMCALL       vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ func = 0x%(1)08x ]
+0x00020011  %(ts)d (+%(relts)12d)  HLT           vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x
+0x00020012  %(ts)d (+%(relts)12d)  CLTS          vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x
+0x00020013  %(ts)d (+%(relts)12d)  LMSW          vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ value = 0x%(1)08x ]
+0x00020014  %(ts)d (+%(relts)12d)  APIC_ACCESS   vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ offset = 0x%(1)08x ]
+0x00020015  %(ts)d (+%(relts)12d)  TDP_FAULT     vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ errorcode = 0x%(1)08x, virt = 0x%(3)08x %(2)08x ]
+# ppc: tlb traces
+0x00020016  GTLB_WRITE    vcpu = 0x%(vcpu)08x  pid = 0x%(pid)08x [ index = 
0x%(1)08x, tid = 0x%(2)08x, word1=0x%(3)08x, word2=0x%(4)08x, word3=0x%(5)08x ]
+0x00020017  STLB_WRITE    vcpu = 0x%(vcpu)08x  pid = 0x%(pid)08x [ index = 
0x%(1)08x, tid = 0x%(2)08x, word1=0x%(3)08x, word2=0x%(4)08x, word3=0x%(5)08x ]
+0x00020018  STLB_INVAL    vcpu = 0x%(vcpu)08x  pid = 0x%(pid)08x [ index = 
0x%(1)08x, tid = 0x%(2)08x, word1=0x%(3)08x, word2=0x%(4)08x, word3=0x%(5)08x ]
+# ppc: instruction emulation - this type is handled more complex in 
kvmtrace_format, but listed to show the eventid and transported data
+#0x00020019  %(ts)d (+%(relts)12d)  PPC_INSTR     vcpu = 0x%(vcpu)08x  pid = 
0x%(pid)08x [ instr = 0x%(1)08x, pc = 0x%(2)08x, emul = 0x%(3)08x, nsec = 
%(4)08d ]
diff --git a/kvm-unittest/kvmtrace_format b/kvm-unittest/kvmtrace_format
new file mode 100755
index 0000000..6556475
--- /dev/null
+++ b/kvm-unittest/kvmtrace_format
@@ -0,0 +1,532 @@
+#!/usr/bin/env python
+
+# by Mark Williamson, (C) 2004 Intel Research Cambridge
+
+# Program for reformatting trace buffer output according to user-supplied rules
+
+import re, sys, string, signal, struct, os, getopt, operator
+
+PREFIX = '/usr'
+DATADIR = os.path.join(PREFIX, 'share')
+KVMDIR = os.path.join(DATADIR, 'kvm')
+FORMATS_FILE = os.path.join(KVMDIR, 'formats')
+
+def usage():
+    print >> sys.stderr, \
+          "Usage: " + sys.argv[0] + """ defs-file
+          Parses trace data in binary format, as output by kvmtrace and
+          reformats it according to the rules in a file of definitions.  The
+          rules in this file should have the format ({ and } show grouping
+          and are not part of the syntax):
+
+          {event_id}{whitespace}{text format string}
+
+          The textual format string may include format specifiers, such as:
+            %(ts)d, %(event)d, %(pid)d %(vcpu)d %(1)d, %(2)d,
+           %(3)d, %(4)d, %(5)d
+          [ the 'd' format specifier outputs in decimal, alternatively 'x'
+            will output in hexadecimal and 'o' will output in octal ]
+
+          Which correspond to the event ID, timestamp counter, pid
+         , vcpu and the 5 data fields from the trace record.  There should be
+         one such rule for each type of event.
+          Depending on your system and the volume of trace buffer data,
+          this script may not be able to keep up with the output of kvmtrace
+          if it is piped directly.  In these circumstances you should have
+          kvmtrace output to a file for processing off-line.
+
+          kvmtrace_format has the following additional switches
+          -s     - if this switch is set additional trace statistics are
+                   created and printed at the end of the output
+          """
+    sys.exit(1)
+
+def read_defs(defs_file):
+    defs = {}
+
+    fd = open(defs_file)
+
+    reg = re.compile('(\S+)\s+(\S.*)')
+
+    while True:
+        line = fd.readline()
+        if not line:
+            break
+
+        if line[0] == '#' or line[0] == '\n':
+            continue
+
+        m = reg.match(line)
+
+        if not m: print >> sys.stderr, "Bad format file" ; sys.exit(1)
+
+        defs[str(eval(m.group(1)))] = m.group(2)
+
+    return defs
+
+def sighand(x,y):
+    global interrupted
+    interrupted = 1
+
+# ppc instruction decoding for event type 0x00020019 (PPC_INSTR)
+# some globals for statistic summaries
+stat_ppc_instr_mnemonic = {};
+stat_ppc_instr_spr = {};
+stat_ppc_instr_dcr = {};
+stat_ppc_instr_tlb = {};
+
+def ppc_instr_print_summary(sortedlist, colname):
+       print "\n\n%14s + %10s" % (colname, "count")
+       print "%s" % (15*"-"+"+"+11*"-")
+       sum = 0
+       for value, key in sortedlist:
+               sum += key
+               print "%14s | %10d" % (value, key)
+       print "%14s = %10d" % ("sum", sum)
+
+
+def ppc_instr_summary():
+       # don't print empty statistics
+        if stat_ppc_instr_mnemonic:
+               
ppc_instr_print_summary(sorted(stat_ppc_instr_mnemonic.iteritems(), 
key=operator.itemgetter(1), reverse=True), "mnemonic")
+        if stat_ppc_instr_spr:
+               ppc_instr_print_summary(sorted(stat_ppc_instr_spr.iteritems(), 
key=operator.itemgetter(1), reverse=True), "mnemonic-spr")
+        if stat_ppc_instr_dcr:
+               ppc_instr_print_summary(sorted(stat_ppc_instr_dcr.iteritems(), 
key=operator.itemgetter(1), reverse=True), "mnemonic-dcr")
+        if stat_ppc_instr_tlb:
+               ppc_instr_print_summary(sorted(stat_ppc_instr_tlb.iteritems(), 
key=operator.itemgetter(1), reverse=True), "mnemonic-tlb")
+
+def get_op(instr):
+        return (instr >> 26);
+
+def get_xop(instr):
+        return (instr >> 1) & 0x3ff;
+
+def get_sprn(instr):
+       return ((instr >> 16) & 0x1f) | ((instr >> 6) & 0x3e0)
+
+def get_dcrn(instr):
+       return ((instr >> 16) & 0x1f) | ((instr >> 6) & 0x3e0);
+
+def get_tlbwe_type(instr):
+       ws = (instr >> 11) & 0x1f;
+       if ws == 0:
+               return "PAGEID"
+       elif ws == 1:
+               return "XLAT"
+       elif ws == 2:
+               return "ATTRIB"
+       else:
+               return "UNKNOWN"
+
+def get_name(instr):
+       if get_op(instr)==3:
+               return "trap"
+       elif get_op(instr)==19:
+               if get_xop(instr) == 50:
+                       return "rfi"
+               else:
+                       return "unknown"
+       elif get_op(instr)==31:
+               if get_xop(instr) == 83:
+                       return "mfmsr"
+
+               elif get_xop(instr) == 87:
+                       return "lbzx"
+
+               elif get_xop(instr) == 131:
+                       return "wrtee"
+
+               elif get_xop(instr) == 146:
+                       return "mtmsr"
+
+               elif get_xop(instr) == 163:
+                       return "wrteei"
+
+               elif get_xop(instr) == 215:
+                       return "stbx"
+
+               elif get_xop(instr) == 247:
+                       return "stbux"
+
+               elif get_xop(instr) == 279:
+                       return "lhzx"
+
+               elif get_xop(instr) == 311:
+                       return "lhzux"
+
+               elif get_xop(instr) == 323:
+                       return "mfdcr"
+
+               elif get_xop(instr) == 339:
+                       return "mfspr"
+
+               elif get_xop(instr) == 407:
+                       return "sthx"
+
+               elif get_xop(instr) == 439:
+                       return "sthux"
+
+               elif get_xop(instr) == 451:
+                       return "mtdcr"
+
+               elif get_xop(instr) == 467:
+                       return "mtspr"
+
+               elif get_xop(instr) == 470:
+                       return "dcbi"
+
+               elif get_xop(instr) == 534:
+                       return "lwbrx"
+
+               elif get_xop(instr) == 566:
+                       return "tlbsync"
+
+               elif get_xop(instr) == 662:
+                       return "stwbrx"
+
+               elif get_xop(instr) == 978:
+                       return "tlbwe"
+
+               elif get_xop(instr) == 914:
+                       return "tlbsx"
+
+               elif get_xop(instr) == 790:
+                       return "lhbrx"
+
+               elif get_xop(instr) == 918:
+                       return "sthbrx"
+
+               elif get_xop(instr) == 966:
+                       return "iccci"
+
+               else:
+                       return "unknown"
+
+       elif get_op(instr) == 32:
+               return "lwz"
+
+       elif get_op(instr) == 33:
+               return "lwzu"
+
+       elif get_op(instr) == 34:
+               return "lbz"
+
+       elif get_op(instr) == 35:
+               return "lbzu"
+
+       elif get_op(instr) == 36:
+               return "stw"
+
+       elif get_op(instr) == 37:
+               return "stwu"
+
+       elif get_op(instr) == 38:
+               return "stb"
+
+       elif get_op(instr) == 39:
+               return "stbu"
+
+       elif get_op(instr) == 40:
+               return "lhz"
+
+       elif get_op(instr) == 41:
+               return "lhzu"
+
+       elif get_op(instr) == 44:
+               return "sth"
+
+       elif get_op(instr) == 45:
+               return "sthu"
+
+       else:
+               return "unknown"
+
+def get_sprn_name(sprn):
+               if sprn == 0x01a:
+                       return "SRR0"
+               elif sprn == 0x01b:
+                       return "SRR1"
+               elif sprn == 0x3b2:
+                       return "MMUCR"
+               elif sprn == 0x030:
+                       return "PID"
+               elif sprn == 0x03f:
+                       return "IVPR"
+               elif sprn == 0x3b3:
+                       return "CCR0"
+               elif sprn == 0x378:
+                       return "CCR1"
+               elif sprn == 0x11f:
+                       return "PVR"
+               elif sprn == 0x03d:
+                       return "DEAR"
+               elif sprn == 0x03e:
+                       return "ESR"
+               elif sprn == 0x134:
+                       return "DBCR0"
+               elif sprn == 0x135:
+                       return "DBCR1"
+               elif sprn == 0x11c:
+                       return "TBWL"
+               elif sprn == 0x11d:
+                       return "TBWU"
+               elif sprn == 0x016:
+                       return "DEC"
+               elif sprn == 0x150:
+                       return "TSR"
+               elif sprn == 0x154:
+                       return "TCR"
+               elif sprn == 0x110:
+                       return "SPRG0"
+               elif sprn == 0x111:
+                       return "SPRG1"
+               elif sprn == 0x112:
+                       return "SPRG2"
+               elif sprn == 0x113:
+                       return "SPRG3"
+               elif sprn == 0x114:
+                       return "SPRG4"
+               elif sprn == 0x115:
+                       return "SPRG5"
+               elif sprn == 0x116:
+                       return "SPRG6"
+               elif sprn == 0x117:
+                       return "SPRG7"
+               elif sprn == 0x190:
+                       return "IVOR0"
+               elif sprn == 0x191:
+                       return "IVOR1"
+               elif sprn == 0x192:
+                       return "IVOR2"
+               elif sprn == 0x193:
+                       return "IVOR3"
+               elif sprn == 0x194:
+                       return "IVOR4"
+               elif sprn == 0x195:
+                       return "IVOR5"
+               elif sprn == 0x196:
+                       return "IVOR6"
+               elif sprn == 0x197:
+                       return "IVOR7"
+               elif sprn == 0x198:
+                       return "IVOR8"
+               elif sprn == 0x199:
+                       return "IVOR9"
+               elif sprn == 0x19a:
+                       return "IVOR10"
+               elif sprn == 0x19b:
+                       return "IVOR11"
+               elif sprn == 0x19c:
+                       return "IVOR12"
+               elif sprn == 0x19d:
+                       return "IVOR13"
+               elif sprn == 0x19e:
+                       return "IVOR14"
+               elif sprn == 0x19f:
+                       return "IVOR15"
+               else:
+                       return "UNKNOWN"
+
+def get_special(instr):
+       name = get_name(instr);
+       if stat_ppc_instr_mnemonic.has_key(name):
+               stat_ppc_instr_mnemonic[name] += 1
+       else:
+               stat_ppc_instr_mnemonic[name] = 1
+
+       if get_op(instr) == 31:
+               if (get_xop(instr) == 339) or (get_xop(instr) == 467):
+                       sprn = get_sprn(instr);
+                       sprn_name = get_sprn_name(sprn);
+                       stat_idx = name+"-"+sprn_name
+                       if stat_ppc_instr_spr.has_key(stat_idx):
+                               stat_ppc_instr_spr[stat_idx] += 1
+                       else:
+                               stat_ppc_instr_spr[stat_idx] = 1
+                       return ("- sprn 0x%03x %8s" % (sprn, sprn_name))
+               elif (get_xop(instr) == 323 ) or (get_xop(instr) == 451):
+                       dcrn = get_dcrn(instr);
+                       stat_idx = name+"-"+("%04X"%dcrn)
+                       if stat_ppc_instr_dcr.has_key(stat_idx):
+                               stat_ppc_instr_dcr[stat_idx] += 1
+                       else:
+                               stat_ppc_instr_dcr[stat_idx] = 1
+                       return ("- dcrn 0x%03x" % dcrn)
+               elif (get_xop(instr) == 978 ) or (get_xop(instr) == 451):
+                       tlbwe_type = get_tlbwe_type(instr)
+                       stat_idx = name+"-"+tlbwe_type
+                       if stat_ppc_instr_tlb.has_key(stat_idx):
+                               stat_ppc_instr_tlb[stat_idx] += 1
+                       else:
+                               stat_ppc_instr_tlb[stat_idx] = 1
+                       return ("- ws -> %8s" % tlbwe_type)
+       return ""
+
+##### Main code
+
+summary = False
+
+try:
+    opts, arg = getopt.getopt(sys.argv[1:], "sc:" )
+    for opt in opts:
+        if opt[0] == '-s' : summary = True
+
+except getopt.GetoptError:
+    usage()
+
+signal.signal(signal.SIGTERM, sighand)
+signal.signal(signal.SIGHUP,  sighand)
+signal.signal(signal.SIGINT,  sighand)
+
+interrupted = 0
+
+if len(arg) > 0:
+    defs = read_defs(arg[0])
+else:
+    defs = read_defs(FORMATS_FILE)
+
+# structure of trace record (as output by kvmtrace):
+# HDR(I) {TSC(Q)} D1(I) D2(I) D3(I) D4(I) D5(I)
+#
+# HDR consists of EVENT:28:, n_data:3:, ts_in:1:
+# pid:32, vcpu_id:32
+# EVENT means Event ID
+# n_data means number of data (like D1, D2, ...)
+# ts_in means Timestamp data exists(1) or not(0).
+# if ts_in == 0, TSC(Q) does not exists.
+#
+HDRREC = "<III"
+TSCREC = "<Q"
+D1REC  = "<I"
+D2REC  = "<II"
+D3REC  = "<III"
+D4REC  = "<IIII"
+D5REC  = "<IIIII"
+KMAGIC  = "<I"
+
+last_ts = 0
+
+i=0
+
+while not interrupted:
+    try:
+        i=i+1
+
+        if i == 1:
+            line = sys.stdin.read(struct.calcsize(KMAGIC))
+            if not line:
+                break
+            kmgc = struct.unpack(KMAGIC, line)[0]
+
+            #firstly try to parse data file as little endian
+            # if "kvmtrace-metadata".kmagic != kmagic
+            # then data file must be big endian"
+            if kmgc != 0x12345678:
+                if kmgc != 0x78563412:
+                    print >> sys.stderr, "Bad data file: magic number error."
+                    break;
+                else:
+                    HDRREC = ">III"
+                    TSCREC = ">Q"
+                    D1REC  = ">I"
+                    D2REC  = ">II"
+                    D3REC  = ">III"
+                    D4REC  = ">IIII"
+                    D5REC  = ">IIIII"
+            continue
+
+        line = sys.stdin.read(struct.calcsize(HDRREC))
+        if not line:
+            break
+       (event, pid, vcpu_id) = struct.unpack(HDRREC, line)
+
+        n_data = event >> 28 & 0x7
+        ts_in = event >> 31
+
+        d1 = 0
+        d2 = 0
+        d3 = 0
+        d4 = 0
+        d5 = 0
+
+        ts = 0
+
+        if ts_in == 1:
+            line = sys.stdin.read(struct.calcsize(TSCREC))
+            if not line:
+                break
+            ts = struct.unpack(TSCREC, line)[0]
+        if n_data == 1:
+            line = sys.stdin.read(struct.calcsize(D1REC))
+            if not line:
+                break
+            d1 = struct.unpack(D1REC, line)[0]
+        if n_data == 2:
+            line = sys.stdin.read(struct.calcsize(D2REC))
+            if not line:
+                break
+            (d1, d2) = struct.unpack(D2REC, line)
+        if n_data == 3:
+            line = sys.stdin.read(struct.calcsize(D3REC))
+            if not line:
+                break
+            (d1, d2, d3) = struct.unpack(D3REC, line)
+        if n_data == 4:
+            line = sys.stdin.read(struct.calcsize(D4REC))
+            if not line:
+                break
+            (d1, d2, d3, d4) = struct.unpack(D4REC, line)
+        if n_data == 5:
+            line = sys.stdin.read(struct.calcsize(D5REC))
+            if not line:
+                break
+            (d1, d2, d3, d4, d5) = struct.unpack(D5REC, line)
+
+       event &= 0x0fffffff
+
+        # provide relative TSC
+
+        if last_ts > 0 and ts_in == 1:
+            relts = ts - last_ts
+        else:
+            relts = 0
+
+        if ts_in == 1:
+            last_ts = ts
+
+        args = {'ts'   : ts,
+                'event' : event,
+                'relts': relts,
+               'pid'   : pid,
+               'vcpu'  : vcpu_id,
+                '1'     : d1,
+                '2'     : d2,
+                '3'     : d3,
+                '4'     : d4,
+                '5'     : d5    }
+
+        # some event types need more than just formats mapping they are if/elif
+        # chained here and the last default else is the mapping via formats
+        if event == 0x00020019:
+            pdata = (ts, relts, vcpu_id, pid, d1, d2, d3, get_name(d1), 
get_special(d1))
+            print "%d (+%12d)  PPC_INSTR vcpu = 0x%08x  pid = 0x%08x [ instr = 
0x%08x, pc = 0x%08x, emul = %01d, mnemonic = %8s %s" % pdata
+        else:
+            try:
+                if defs.has_key(str(event)):
+                    print defs[str(event)] % args
+                else:
+                    if defs.has_key(str(0)): print defs[str(0)] % args
+            except TypeError:
+                if defs.has_key(str(event)):
+                    print defs[str(event)]
+                    print args
+                else:
+                    if defs.has_key(str(0)):
+                        print defs[str(0)]
+                        print args
+
+    except IOError, struct.error: sys.exit()
+
+if summary:
+       ppc_instr_summary()
diff --git a/kvm-unittest/lib/powerpc/44x/timebase.S 
b/kvm-unittest/lib/powerpc/44x/timebase.S
new file mode 100644
index 0000000..385904d
--- /dev/null
+++ b/kvm-unittest/lib/powerpc/44x/timebase.S
@@ -0,0 +1,28 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <address@hidden>
+ */
+
+/* unsigned long long mftb(void); */
+.global mftb
+mftb:
+       mftbu   r5
+       mftbl   r4
+       mftbu   r3
+       cmpw    r3, r5
+       bne     mftb
+       blr
diff --git a/kvm-unittest/lib/powerpc/44x/tlbwe.S 
b/kvm-unittest/lib/powerpc/44x/tlbwe.S
new file mode 100644
index 0000000..3790374
--- /dev/null
+++ b/kvm-unittest/lib/powerpc/44x/tlbwe.S
@@ -0,0 +1,29 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <address@hidden>
+ */
+
+#define SPRN_MMUCR 0x3b2
+
+/* tlbwe(uint index, uint8_t tid, uint word0, uint word1, uint word2) */
+.global tlbwe
+tlbwe:
+       mtspr   SPRN_MMUCR, r4
+       tlbwe   r5, r3, 0
+       tlbwe   r6, r3, 1
+       tlbwe   r7, r3, 2
+       blr
diff --git a/kvm-unittest/powerpc/44x/tlbsx.S b/kvm-unittest/powerpc/44x/tlbsx.S
new file mode 100644
index 0000000..b15874b
--- /dev/null
+++ b/kvm-unittest/powerpc/44x/tlbsx.S
@@ -0,0 +1,33 @@
+#define SPRN_MMUCR 0x3b2
+
+#define TLBWORD0 0x10000210
+#define TLBWORD1 0x10000000
+#define TLBWORD2 0x00000003
+
+.global _start
+_start:
+       li      r4, 0
+       mtspr   SPRN_MMUCR, r4
+
+       li      r3, 23
+
+       lis     r4, address@hidden
+       ori     r4, r4, address@hidden
+       tlbwe   r4, r3, 0
+
+       lis     r4, address@hidden
+       ori     r4, r4, address@hidden
+       tlbwe   r4, r3, 1
+
+       lis     r4, address@hidden
+       ori     r4, r4, address@hidden
+       tlbwe   r4, r3, 2
+
+       lis     r4, 0x1000
+       tlbsx   r5, r4, r0
+       cmpwi   r5, 23
+       beq     good
+       trap
+
+good:
+       b       .
diff --git a/kvm-unittest/powerpc/44x/tlbwe.S b/kvm-unittest/powerpc/44x/tlbwe.S
new file mode 100644
index 0000000..ec6ef5c
--- /dev/null
+++ b/kvm-unittest/powerpc/44x/tlbwe.S
@@ -0,0 +1,27 @@
+#define SPRN_MMUCR 0x3b2
+
+/* Create a mapping at 4MB */
+#define TLBWORD0 0x00400210
+#define TLBWORD1 0x00400000
+#define TLBWORD2 0x00000003
+
+.global _start
+_start:
+       li      r4, 0
+       mtspr   SPRN_MMUCR, r4
+
+       li      r3, 23
+
+       lis     r4, address@hidden
+       ori     r4, r4, address@hidden
+       tlbwe   r4, r3, 0
+
+       lis     r4, address@hidden
+       ori     r4, r4, address@hidden
+       tlbwe   r4, r3, 1
+
+       lis     r4, address@hidden
+       ori     r4, r4, address@hidden
+       tlbwe   r4, r3, 2
+
+       b       .
diff --git a/kvm-unittest/powerpc/44x/tlbwe_16KB.S 
b/kvm-unittest/powerpc/44x/tlbwe_16KB.S
new file mode 100644
index 0000000..1bd10bf
--- /dev/null
+++ b/kvm-unittest/powerpc/44x/tlbwe_16KB.S
@@ -0,0 +1,35 @@
+#define SPRN_MMUCR 0x3b2
+
+/* 16KB mapping at 4MB */
+#define TLBWORD0 0x00400220
+#define TLBWORD1 0x00400000
+#define TLBWORD2 0x00000003
+
+.global _start
+_start:
+       li      r4, 0
+       mtspr   SPRN_MMUCR, r4
+
+       li      r3, 5
+
+       lis     r4, address@hidden
+       ori     r4, r4, address@hidden
+       tlbwe   r4, r3, 0
+
+       lis     r4, address@hidden
+       ori     r4, r4, address@hidden
+       tlbwe   r4, r3, 1
+
+       lis     r4, address@hidden
+       ori     r4, r4, address@hidden
+       tlbwe   r4, r3, 2
+
+       /* load from 4MB */
+       lis     r3, 0x0040
+       lwz     r4, 0(r3)
+
+       /* load from 4MB+8KB */
+       ori     r3, r3, 0x2000
+       lwz     r4, 0(r3)
+
+       b       .
diff --git a/kvm-unittest/powerpc/44x/tlbwe_hole.S 
b/kvm-unittest/powerpc/44x/tlbwe_hole.S
new file mode 100644
index 0000000..5efd303
--- /dev/null
+++ b/kvm-unittest/powerpc/44x/tlbwe_hole.S
@@ -0,0 +1,27 @@
+#define SPRN_MMUCR 0x3b2
+
+/* Try to map real address 1GB. */
+#define TLBWORD0 0x40000210
+#define TLBWORD1 0x40000000
+#define TLBWORD2 0x00000003
+
+.global _start
+_start:
+       li      r4, 0
+       mtspr   SPRN_MMUCR, r4
+
+       li      r3, 23
+
+       lis     r4, address@hidden
+       ori     r4, r4, address@hidden
+       tlbwe   r4, r3, 0
+
+       lis     r4, address@hidden
+       ori     r4, r4, address@hidden
+       tlbwe   r4, r3, 1
+
+       lis     r4, address@hidden
+       ori     r4, r4, address@hidden
+       tlbwe   r4, r3, 2
+
+       b       .
diff --git a/kvm-unittest/powerpc/cstart.S b/kvm-unittest/powerpc/cstart.S
new file mode 100644
index 0000000..70a0e9f
--- /dev/null
+++ b/kvm-unittest/powerpc/cstart.S
@@ -0,0 +1,38 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <address@hidden>
+ */
+
+#define OUTPUT_VADDR 0xf0000000
+#define OUTPUT_PADDR 0xf0000000
+
+.globl _start
+_start:
+       /* In the future we might need to assign a stack and zero BSS here. */
+
+       /* Map the debug page 1:1. */
+       lis     r3, address@hidden
+       ori     r3, r3, address@hidden
+       lis     r4, address@hidden
+       ori     r4, r4, address@hidden
+       bl      map
+
+       /* Call main() and pass return code to exit(). */
+       bl      main
+       bl      exit
+
+       b       .
diff --git a/kvm-unittest/powerpc/io.S b/kvm-unittest/powerpc/io.S
new file mode 100644
index 0000000..97567cb
--- /dev/null
+++ b/kvm-unittest/powerpc/io.S
@@ -0,0 +1,32 @@
+#define SPRN_MMUCR 0x3b2
+
+#define TLBWORD0 0xf0000210
+#define TLBWORD1 0xf0000000
+#define TLBWORD2 0x00000003
+
+.global _start
+_start:
+       li      r4, 0
+       mtspr   SPRN_MMUCR, r4
+
+       li      r3, 2
+
+       lis     r4, address@hidden
+       ori     r4, r4, address@hidden
+       tlbwe   r4, r3, 0
+
+       lis     r4, address@hidden
+       ori     r4, r4, address@hidden
+       tlbwe   r4, r3, 1
+
+       lis     r4, address@hidden
+       ori     r4, r4, address@hidden
+       tlbwe   r4, r3, 2
+
+       lis     r3, 0xf000
+       lis     r4, 0x1234
+       ori     r4, r4, 0x5678
+       stb     r4, 0(r3)
+       lbz     r5, 0(r3)
+
+       b       .
diff --git a/kvm-unittest/powerpc/spin.S b/kvm-unittest/powerpc/spin.S
new file mode 100644
index 0000000..4406641
--- /dev/null
+++ b/kvm-unittest/powerpc/spin.S
@@ -0,0 +1,4 @@
+
+.global _start
+_start:
+       b       .
diff --git a/kvm-unittest/powerpc/sprg.S b/kvm-unittest/powerpc/sprg.S
new file mode 100644
index 0000000..d0414a4
--- /dev/null
+++ b/kvm-unittest/powerpc/sprg.S
@@ -0,0 +1,7 @@
+
+.global _start
+_start:
+       li      r3, 42
+       mtsprg  0, r3
+       mfsprg  r4, 0
+       b       .
diff --git a/kvm-unittest/run_tests.sh b/kvm-unittest/run_tests.sh
new file mode 100755
index 0000000..55ecac5
--- /dev/null
+++ b/kvm-unittest/run_tests.sh
@@ -0,0 +1,123 @@
+#!/bin/bash
+
+testroot=x86
+config=$testroot/unittests.cfg
+qemu=${qemu:-qemu-system-x86_64}
+verbose=0
+
+function run()
+{
+    local testname="$1"
+    local groups="$2"
+    local smp="$3"
+    local kernel="$4"
+    local opts="$5"
+    local arch="$6"
+
+    if [ -z "$testname" ]; then
+        return
+    fi
+
+    if [ -n "$only_group" ] && ! grep -q "$only_group" <<<$groups; then
+        return
+    fi
+
+    if [ -n "$arch" ] && [ "$arch" != "$ARCH" ]; then
+        echo "skip $1 ($arch only)"
+        return
+    fi
+
+       cmdline="./x86-run $kernel -smp $smp -display none $opts"
+    if [ $verbose != 0 ]; then
+        echo $cmdline
+    fi
+
+    # extra_params in the config file may contain backticks that need to be
+    # expanded, so use eval to start qemu
+    eval $cmdline >> test.log
+
+    if [ $? -le 1 ]; then
+        echo -e "\e[32mPASS\e[0m $1"
+    else
+        echo -e "\e[31mFAIL\e[0m $1"
+    fi
+}
+
+function run_all()
+{
+    local config="$1"
+    local testname
+    local smp
+    local kernel
+    local opts
+    local groups
+    local arch
+
+    exec {config_fd}<$config
+
+    while read -u $config_fd line; do
+        if [[ "$line" =~ ^\[(.*)\]$ ]]; then
+            run "$testname" "$groups" "$smp" "$kernel" "$opts" "$arch"
+            testname=${BASH_REMATCH[1]}
+            smp=1
+            kernel=""
+            opts=""
+            groups=""
+            arch=""
+        elif [[ $line =~ ^file\ *=\ *(.*)$ ]]; then
+            kernel=$testroot/${BASH_REMATCH[1]}
+        elif [[ $line =~ ^smp\ *=\ *(.*)$ ]]; then
+            smp=${BASH_REMATCH[1]}
+        elif [[ $line =~ ^extra_params\ *=\ *(.*)$ ]]; then
+            opts=${BASH_REMATCH[1]}
+        elif [[ $line =~ ^groups\ *=\ *(.*)$ ]]; then
+            groups=${BASH_REMATCH[1]}
+        elif [[ $line =~ ^arch\ *=\ *(.*)$ ]]; then
+            arch=${BASH_REMATCH[1]}
+        fi
+    done
+
+    run "$testname" "$groups" "$smp" "$kernel" "$opts" "$arch"
+
+    exec {config_fd}<&-
+}
+
+function usage()
+{
+cat <<EOF
+
+Usage: $0 [-g group] [-h] [-v]
+
+    -g: Only execute tests in the given group
+    -h: Output this help text
+    -v: Enables verbose mode
+
+Set the environment variable QEMU=/path/to/qemu-system-x86_64 to allow the
+internally used x86-run to pick up the right qemu binary.
+
+EOF
+}
+
+# As it happens, config.mak is valid shell script code, too :-)
+source config.mak
+
+echo > test.log
+while getopts "g:hv" opt; do
+    case $opt in
+        g)
+            only_group=$OPTARG
+            ;;
+        h)
+            usage
+            exit
+            ;;
+        v)
+            verbose=1
+            ;;
+        *)
+            exit
+            ;;
+    esac
+done
+
+run_all $config
diff --git a/kvm-unittest/testdev.txt b/kvm-unittest/testdev.txt
new file mode 100644
index 0000000..ac436ef
--- /dev/null
+++ b/kvm-unittest/testdev.txt
@@ -0,0 +1,14 @@
+This file describes the virtual device of qemu for supporting this test suite.
+
+Services supplied by the testdev device:
+
+serial output: write only, on io port 0xf1
+exit process: write only, on io port 0xf4, value used as exit code
+ram size: read-only, on io port 0xd1, 4 bytes' size
+irq line setting: write only, on io ports 0x2000 - 0x2018, value to set/clear
+simple io: read/write, on io port 0xe0, 1/2/4 bytes
+
+Test device used a char device for actual output
+
+
+
diff --git a/kvm-unittest/x86-run b/kvm-unittest/x86-run
new file mode 100755
index 0000000..646c577
--- /dev/null
+++ b/kvm-unittest/x86-run
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+qemukvm="${QEMU:-qemu-kvm}"
+qemusystem="${QEMU:-qemu-system-x86_64}"
+if
+       ${qemukvm} -device '?' 2>&1 | grep -F -e \"testdev\" -e \"pc-testdev\" 
> /dev/null;
+then
+       qemu="${qemukvm}"
+else
+       if
+               ${qemusystem} -device '?' 2>&1 | grep -F -e \"testdev\" -e 
\"pc-testdev\" > /dev/null;
+       then
+               qemu="${qemusystem}"
+       else
+               echo QEMU binary ${QEMU} has no support for test device. 
Exiting.
+               exit 2
+       fi
+fi
+
+if
+       ${qemu} -device '?' 2>&1 | grep -F "pci-testdev" > /dev/null;
+then
+       pci_testdev="-device pci-testdev"
+else
+       pci_testdev=""
+fi
+
+if
+       ${qemu} -device '?' 2>&1 | grep -F "pc-testdev" > /dev/null;
+then
+       pc_testdev="-device pc-testdev -device 
isa-debug-exit,iobase=0xf4,iosize=0x4"
+else
+       pc_testdev="-device testdev,chardev=testlog -chardev 
file,id=testlog,path=msr.out"
+fi
+
+command="${qemu} -enable-kvm $pc_testdev -display none -serial stdio 
$pci_testdev -kernel"
+echo ${command} "$@"
+${command} "$@"
+ret=$?
+echo Return value from qemu: $ret
+exit $ret
diff --git a/kvm-unittest/x86/README b/kvm-unittest/x86/README
new file mode 100644
index 0000000..d644abd
--- /dev/null
+++ b/kvm-unittest/x86/README
@@ -0,0 +1,16 @@
+Tests in this directory and what they do:
+
+access: lots of page table related access (pte/pde) (read/write)
+apic: enable x2apic, self ipi, ioapic intr, ioapic simultaneous
+emulator: move to/from regs, cmps, push, pop, to/from cr8, smsw and lmsw
+hypercall: intel and amd hypercall insn
+msr: write to msr (only KERNEL_GS_BASE for now)
+port80: lots of out to port 80
+realmode: goes back to realmode, shld, push/pop, mov immediate, cmp immediate, 
add immediate,
+         io, eflags instructions (clc, cli, etc.), jcc short, jcc near, call, 
long jmp, xchg
+sieve: heavy memory access with no paging and with paging static and with 
paging vmalloc'ed
+smptest: run smp_id() on every cpu and compares return value to number
+tsc: write to tsc(0) and write to tsc(100000000000) and read it back
+vmexit: long loops for each: cpuid, vmcall, mov_from_cr8, mov_to_cr8, 
inl_pmtimer, ipi, ipi+halt
+kvmclock_test: test of wallclock, monotonic cycle and performance of kvmclock
+pcid: basic functionality test of PCID/INVPCID feature
\ No newline at end of file
diff --git a/kvm-unittest/x86/cstart.S b/kvm-unittest/x86/cstart.S
new file mode 100644
index 0000000..bc8d563
--- /dev/null
+++ b/kvm-unittest/x86/cstart.S
@@ -0,0 +1,192 @@
+
+#include "apic-defs.h"
+
+.globl boot_idt
+boot_idt = 0
+
+ipi_vector = 0x20
+
+max_cpus = 64
+
+.bss
+
+       . = . + 4096 * max_cpus
+       .align 16
+stacktop:
+
+       . = . + 4096
+       .align 16
+ring0stacktop:
+
+.data
+
+.align 4096
+pt:
+i = 0
+        .rept 1024
+        .long 0x1e7 | (i << 22)
+        i = i + 1
+        .endr
+
+gdt32:
+       .quad 0
+       .quad 0x00cf9b000000ffff // flat 32-bit code segment
+       .quad 0x00cf93000000ffff // flat 32-bit data segment
+
+tss_descr:
+        .rept max_cpus
+        .quad 0x000089000000ffff // 32-bit avail tss
+        .endr
+gdt32_end:
+
+i = 0
+tss:
+        .rept max_cpus
+        .long 0
+        .long ring0stacktop - i * 4096
+        .long 16
+        .quad 0, 0
+        .quad 0, 0, 0, 0, 0, 0, 0, 0
+        .long 0, 0, 0
+        i = i + 1
+        .endr
+tss_end:
+
+idt_descr:
+       .word 16 * 256 - 1
+       .long boot_idt
+
+.section .init
+
+.code32
+
+mb_magic = 0x1BADB002
+mb_flags = 0x0
+
+       # multiboot header
+       .long mb_magic, mb_flags, 0 - (mb_magic + mb_flags)
+mb_cmdline = 16
+
+MSR_GS_BASE = 0xc0000101
+
+.macro setup_percpu_area
+       lea -4096(%esp), %eax
+       mov $0, %edx
+       mov $MSR_GS_BASE, %ecx
+       wrmsr
+.endm
+
+.globl start
+start:
+        mov mb_cmdline(%ebx), %eax
+        mov %eax, __args
+        call __setup_args
+        mov $stacktop, %esp
+        setup_percpu_area
+        call prepare_32
+        jmpl $8, $start32
+
+prepare_32:
+        lgdtl gdt32_descr
+
+       mov %cr4, %eax
+       bts $4, %eax  // pse
+       mov %eax, %cr4
+
+       mov $pt, %eax
+       mov %eax, %cr3
+
+       mov %cr0, %eax
+       bts $0, %eax
+       bts $31, %eax
+       mov %eax, %cr0
+       ret
+
+smp_stacktop:  .long 0xa0000
+
+ap_start32:
+       mov $0x10, %ax
+       mov %ax, %ds
+       mov %ax, %es
+       mov %ax, %fs
+       mov %ax, %gs
+       mov %ax, %ss
+       mov $-4096, %esp
+       lock/xaddl %esp, smp_stacktop
+       setup_percpu_area
+       call prepare_32
+       call load_tss
+       call enable_apic
+       call enable_x2apic
+       sti
+       nop
+       lock incw cpu_online_count
+
+1:     hlt
+       jmp 1b
+
+start32:
+       call load_tss
+       call mask_pic_interrupts
+       call enable_apic
+       call smp_init
+       call enable_x2apic
+        push $__argv
+        push __argc
+        call main
+       push %eax
+       call exit
+
+load_tss:
+       lidt idt_descr
+       mov $16, %eax
+       mov %ax, %ss
+       mov $(APIC_DEFAULT_PHYS_BASE + APIC_ID), %eax
+       mov (%eax), %eax
+       shr $24, %eax
+       mov %eax, %ebx
+       shl $3, %ebx
+       mov $((tss_end - tss) / max_cpus), %edx
+       imul %edx
+       add $tss, %eax
+       mov %ax, tss_descr+2(%ebx)
+       shr $16, %eax
+       mov %al, tss_descr+4(%ebx)
+       shr $8, %eax
+       mov %al, tss_descr+7(%ebx)
+       lea tss_descr-gdt32(%ebx), %eax
+       ltr %ax
+       ret
+
+smp_init:
+       cld
+       lea sipi_entry, %esi
+       xor %edi, %edi
+       mov $(sipi_end - sipi_entry), %ecx
+       rep/movsb
+       mov $APIC_DEFAULT_PHYS_BASE, %eax
+       movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_INIT | 
APIC_INT_ASSERT), APIC_ICR(%eax)
+       movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_INIT), 
APIC_ICR(%eax)
+       movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_STARTUP), 
APIC_ICR(%eax)
+       call fwcfg_get_nb_cpus
+1:     pause
+       cmpw %ax, cpu_online_count
+       jne 1b
+smp_init_done:
+       ret
+
+cpu_online_count:      .word 1
+
+.code16
+sipi_entry:
+       mov %cr0, %eax
+       or $1, %eax
+       mov %eax, %cr0
+       lgdtl gdt32_descr - sipi_entry
+       ljmpl $8, $ap_start32
+
+gdt32_descr:
+       .word gdt32_end - gdt32 - 1
+       .long gdt32
+
+sipi_end:
diff --git a/kvm-unittest/x86/cstart64.S b/kvm-unittest/x86/cstart64.S
new file mode 100644
index 0000000..0fe76da
--- /dev/null
+++ b/kvm-unittest/x86/cstart64.S
@@ -0,0 +1,245 @@
+
+#include "apic-defs.h"
+
+.globl boot_idt
+boot_idt = 0
+
+.globl idt_descr
+.globl tss_descr
+.globl gdt64_desc
+
+ipi_vector = 0x20
+
+max_cpus = 64
+
+.bss
+
+       . = . + 4096 * max_cpus
+       .align 16
+stacktop:
+
+       . = . + 4096
+       .align 16
+ring0stacktop:
+
+.data
+
+.align 4096
+.globl ptl2
+ptl2:
+i = 0
+       .rept 512 * 4
+       .quad 0x1e7 | (i << 21)
+       i = i + 1
+       .endr
+
+.align 4096
+ptl3:
+       .quad ptl2 + 7 + 0 * 4096
+       .quad ptl2 + 7 + 1 * 4096
+       .quad ptl2 + 7 + 2 * 4096
+       .quad ptl2 + 7 + 3 * 4096
+
+.align 4096
+ptl4:
+       .quad ptl3 + 7
+
+.align 4096
+
+gdt64_desc:
+       .word gdt64_end - gdt64 - 1
+       .quad gdt64
+
+gdt64:
+       .quad 0
+       .quad 0x00af9b000000ffff // 64-bit code segment
+       .quad 0x00cf93000000ffff // 64-bit data segment
+       .quad 0x00affb000000ffff // 64-bit code segment (user)
+       .quad 0x00cff3000000ffff // 64-bit data segment (user)
+       .quad 0x00cf9b000000ffff // 32-bit code segment
+       .quad 0x00cf92000000ffff // 32-bit code segment
+       .quad 0x008F9A000000FFFF // 16-bit code segment
+       .quad 0x008F92000000FFFF // 16-bit data segment
+
+tss_descr:
+       .rept max_cpus
+       .quad 0x000089000000ffff // 64-bit avail tss
+       .quad 0                  // tss high addr
+       .endr
+gdt64_end:
+
+i = 0
+tss:
+       .rept max_cpus
+       .long 0
+       .quad ring0stacktop - i * 4096
+       .quad 0, 0
+       .quad 0, 0, 0, 0, 0, 0, 0, 0
+       .long 0, 0, 0
+i = i + 1
+       .endr
+tss_end:
+
+mb_boot_info:  .quad 0
+
+.section .init
+
+.code32
+
+mb_magic = 0x1BADB002
+mb_flags = 0x0
+
+       # multiboot header
+       .long mb_magic, mb_flags, 0 - (mb_magic + mb_flags)
+mb_cmdline = 16
+
+MSR_GS_BASE = 0xc0000101
+
+.macro setup_percpu_area
+       lea -4096(%esp), %eax
+       mov $0, %edx
+       mov $MSR_GS_BASE, %ecx
+       wrmsr
+.endm
+
+.globl start
+start:
+       mov %ebx, mb_boot_info
+       mov $stacktop, %esp
+       setup_percpu_area
+       call prepare_64
+       jmpl $8, $start64
+
+prepare_64:
+       lgdt gdt64_desc
+
+       mov %cr4, %eax
+       bts $5, %eax  // pae
+       mov %eax, %cr4
+
+       mov $ptl4, %eax
+       mov %eax, %cr3
+
+efer = 0xc0000080
+       mov $efer, %ecx
+       rdmsr
+       bts $8, %eax
+       wrmsr
+
+       mov %cr0, %eax
+       bts $0, %eax
+       bts $31, %eax
+       mov %eax, %cr0
+       ret
+
+smp_stacktop:  .long 0xa0000
+
+.align 16
+
+gdt32:
+       .quad 0
+       .quad 0x00cf9b000000ffff // flat 32-bit code segment
+       .quad 0x00cf93000000ffff // flat 32-bit data segment
+gdt32_end:
+
+.code16
+sipi_entry:
+       mov %cr0, %eax
+       or $1, %eax
+       mov %eax, %cr0
+       lgdtl gdt32_descr - sipi_entry
+       ljmpl $8, $ap_start32
+
+gdt32_descr:
+       .word gdt32_end - gdt32 - 1
+       .long gdt32
+
+sipi_end:
+
+.code32
+ap_start32:
+       mov $0x10, %ax
+       mov %ax, %ds
+       mov %ax, %es
+       mov %ax, %fs
+       mov %ax, %gs
+       mov %ax, %ss
+       mov $-4096, %esp
+       lock/xaddl %esp, smp_stacktop
+       setup_percpu_area
+       call prepare_64
+       ljmpl $8, $ap_start64
+
+.code64
+ap_start64:
+       call load_tss
+       call enable_apic
+       call enable_x2apic
+       sti
+       nop
+       lock incw cpu_online_count
+
+1:     hlt
+       jmp 1b
+
+start64:
+       call load_tss
+       call mask_pic_interrupts
+       call enable_apic
+       call smp_init
+       call enable_x2apic
+       mov mb_boot_info(%rip), %rax
+       mov mb_cmdline(%rax), %rax
+       mov %rax, __args(%rip)
+       call __setup_args
+       mov __argc(%rip), %edi
+       lea __argv(%rip), %rsi
+       call main
+       mov %eax, %edi
+       call exit
+
+idt_descr:
+       .word 16 * 256 - 1
+       .quad boot_idt
+
+load_tss:
+       lidtq idt_descr
+       mov $0, %eax
+       mov %ax, %ss
+       mov $(APIC_DEFAULT_PHYS_BASE + APIC_ID), %eax
+       mov (%rax), %eax
+       shr $24, %eax
+       mov %eax, %ebx
+       shl $4, %ebx
+       mov $((tss_end - tss) / max_cpus), %edx
+       imul %edx
+       add $tss, %rax
+       mov %ax, tss_descr+2(%rbx)
+       shr $16, %rax
+       mov %al, tss_descr+4(%rbx)
+       shr $8, %rax
+       mov %al, tss_descr+7(%rbx)
+       shr $8, %rax
+       mov %eax, tss_descr+8(%rbx)
+       lea tss_descr-gdt64(%rbx), %rax
+       ltr %ax
+       ret
+
+smp_init:
+       cld
+       lea sipi_entry, %rsi
+       xor %rdi, %rdi
+       mov $(sipi_end - sipi_entry), %rcx
+       rep/movsb
+       mov $APIC_DEFAULT_PHYS_BASE, %eax
+       movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_INIT | 
APIC_INT_ASSERT), APIC_ICR(%rax)
+       movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_INIT), 
APIC_ICR(%rax)
+       movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_STARTUP), 
APIC_ICR(%rax)
+       call fwcfg_get_nb_cpus
+1:     pause
+       cmpw %ax, cpu_online_count
+       jne 1b
+smp_init_done:
+       ret
+
+cpu_online_count:      .word 1
diff --git a/kvm-unittest/x86/realmode.lds b/kvm-unittest/x86/realmode.lds
new file mode 100644
index 0000000..0ed3063
--- /dev/null
+++ b/kvm-unittest/x86/realmode.lds
@@ -0,0 +1,12 @@
+SECTIONS
+{
+    . = 16K;
+    stext = .;
+    .text : { *(.init) *(.text) }
+    . = ALIGN(4K);
+    .data : { *(.data) *(.rodata*) }
+    . = ALIGN(16);
+    .bss : { *(.bss) }
+    edata = .;
+}
+ENTRY(start)
diff --git a/kvm-unittest/x86/run-kvm-unit-tests 
b/kvm-unittest/x86/run-kvm-unit-tests
new file mode 100644
index 0000000..fed925a
--- /dev/null
+++ b/kvm-unittest/x86/run-kvm-unit-tests
@@ -0,0 +1,6 @@
+#!/usr/bin/python
+
+import sys, os, os.path
+
+prog = sys.argv[0]
+dir = os.path.dirname(prog)
diff --git a/kvm-unittest/x86/unittests.cfg b/kvm-unittest/x86/unittests.cfg
new file mode 100644
index 0000000..85c36aa
--- /dev/null
+++ b/kvm-unittest/x86/unittests.cfg
@@ -0,0 +1,157 @@
+# Define your new unittest following the convention:
+# [unittest_name]
+# file = foo.flat # Name of the flat file to be used
+# smp = 2 # Number of processors the VM will use during this test
+# extra_params = -cpu qemu64,+x2apic # Additional parameters used
+# arch = i386/x86_64 # Only if the test case works only on one of them
+# groups = group1 group2 # Used to identify test cases with run_tests -g ...
+
+[apic]
+file = apic.flat
+smp = 2
+extra_params = -cpu qemu64,+x2apic
+arch = x86_64
+
+[smptest]
+file = smptest.flat
+smp = 2
+
+[smptest3]
+file = smptest.flat
+smp = 3
+
+[vmexit_cpuid]
+file = vmexit.flat
+extra_params = -append 'cpuid'
+groups = vmexit
+
+[vmexit_vmcall]
+file = vmexit.flat
+extra_params = -append 'vmcall'
+groups = vmexit
+
+[vmexit_mov_from_cr8]
+file = vmexit.flat
+extra_params = -append 'mov_from_cr8'
+groups = vmexit
+
+[vmexit_mov_to_cr8]
+file = vmexit.flat
+extra_params = -append 'mov_to_cr8'
+groups = vmexit
+
+[vmexit_inl_pmtimer]
+file = vmexit.flat
+extra_params = -append 'inl_from_pmtimer'
+groups = vmexit
+
+[vmexit_ipi]
+file = vmexit.flat
+smp = 2
+extra_params = -append 'ipi'
+groups = vmexit
+
+[vmexit_ipi_halt]
+file = vmexit.flat
+smp = 2
+extra_params = -append 'ipi_halt'
+groups = vmexit
+
+[vmexit_ple_round_robin]
+file = vmexit.flat
+extra_params = -append 'ple_round_robin'
+groups = vmexit
+
+[access]
+file = access.flat
+arch = x86_64
+
+#[asyncpf]
+#file = asyncpf.flat
+
+[emulator]
+file = emulator.flat
+arch = x86_64
+
+[eventinj]
+file = eventinj.flat
+
+[hypercall]
+file = hypercall.flat
+
+[idt_test]
+file = idt_test.flat
+arch = x86_64
+
+#[init]
+#file = init.flat
+
+[msr]
+file = msr.flat
+
+[pmu]
+file = pmu.flat
+
+[port80]
+file = port80.flat
+
+[realmode]
+file = realmode.flat
+
+[s3]
+file = s3.flat
+
+[sieve]
+file = sieve.flat
+
+[tsc]
+file = tsc.flat
+
+[tsc_adjust]
+file = tsc_adjust.flat
+
+[xsave]
+file = xsave.flat
+arch = x86_64
+
+[rmap_chain]
+file = rmap_chain.flat
+arch = x86_64
+
+[svm]
+file = svm.flat
+smp = 2
+extra_params = -cpu qemu64,+svm
+arch = x86_64
+
+[svm-disabled]
+file = svm.flat
+smp = 2
+extra_params = -cpu qemu64,-svm
+arch = x86_64
+
+[taskswitch]
+file = taskswitch.flat
+arch = i386
+groups = tasks
+
+[taskswitch2]
+file = taskswitch2.flat
+arch = i386
+groups = tasks
+
+[kvmclock_test]
+file = kvmclock_test.flat
+smp = 2
+extra_params = --append "10000000 `date +%s`"
+
+[pcid]
+file = pcid.flat
+extra_params = -cpu qemu64,+pcid
+arch = x86_64
+
+[vmx]
+file = vmx.flat
+extra_params = -cpu host,+vmx
+arch = x86_64
+



reply via email to

[Prev in Thread] Current Thread [Next in Thread]