qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] import kvm-unittest in QEMU source tree
@ 2013-10-16 19:03 Michael S. Tsirkin
  2013-10-30 23:06 ` Andrew Jones
  0 siblings, 1 reply; 5+ messages in thread
From: Michael S. Tsirkin @ 2013-10-16 19:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: pbonzini, gleb, Anthony Liguori

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 <mst@redhat.com>

---

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 <avi@qumranet.com>
+ *  Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * 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 <hollisb@us.ibm.com>
+ */
+
+#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 <hollisb@us.ibm.com>
+ */
+
+#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 <Alan.Cox@linux.org>, 1995.
+ * Ingo Molnar <mingo@redhat.com>, 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 <avi@qumranet.com>
+ *  Yaniv Kamay <yaniv@qumranet.com>
+ *
+ * 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 <axboe@suse.de>
+ * Copyright (C) 2006 Jens Axboe <axboe@kernel.dk>
+ * Copyright (C) 2008 Eric Liu <eric.e.liu@intel.com>
+ *
+ * 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 <hollisb@us.ibm.com>
+ */
+
+#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 <hollisb@us.ibm.com>
+ */
+
+#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 <avi@qumranet.com>
+ *  Yaniv Kamay <yaniv@qumranet.com>
+ *  Hollis Blanchard <hollisb@us.ibm.com>
+ *
+ * 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 <kvm-ppc@vger.kernel.org>.\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 <hollisb@us.ibm.com>
+ */
+
+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 <deepas@us.ibm.com>
+ */
+
+#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 <hollisb@us.ibm.com>
+ */
+
+/* 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 <hollisb@us.ibm.com>
+ */
+
+#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, TLBWORD0@h
+	ori	r4, r4, TLBWORD0@l
+	tlbwe	r4, r3, 0
+
+	lis	r4, TLBWORD1@h
+	ori	r4, r4, TLBWORD1@l
+	tlbwe	r4, r3, 1
+
+	lis	r4, TLBWORD2@h
+	ori	r4, r4, TLBWORD2@l
+	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, TLBWORD0@h
+	ori	r4, r4, TLBWORD0@l
+	tlbwe	r4, r3, 0
+
+	lis	r4, TLBWORD1@h
+	ori	r4, r4, TLBWORD1@l
+	tlbwe	r4, r3, 1
+
+	lis	r4, TLBWORD2@h
+	ori	r4, r4, TLBWORD2@l
+	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, TLBWORD0@h
+	ori	r4, r4, TLBWORD0@l
+	tlbwe	r4, r3, 0
+
+	lis	r4, TLBWORD1@h
+	ori	r4, r4, TLBWORD1@l
+	tlbwe	r4, r3, 1
+
+	lis	r4, TLBWORD2@h
+	ori	r4, r4, TLBWORD2@l
+	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, TLBWORD0@h
+	ori	r4, r4, TLBWORD0@l
+	tlbwe	r4, r3, 0
+
+	lis	r4, TLBWORD1@h
+	ori	r4, r4, TLBWORD1@l
+	tlbwe	r4, r3, 1
+
+	lis	r4, TLBWORD2@h
+	ori	r4, r4, TLBWORD2@l
+	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 <hollisb@us.ibm.com>
+ */
+
+#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, OUTPUT_VADDR@h
+	ori	r3, r3, OUTPUT_VADDR@l
+	lis	r4, OUTPUT_PADDR@h
+	ori	r4, r4, OUTPUT_PADDR@l
+	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, TLBWORD0@h
+	ori	r4, r4, TLBWORD0@l
+	tlbwe	r4, r3, 0
+
+	lis	r4, TLBWORD1@h
+	ori	r4, r4, TLBWORD1@l
+	tlbwe	r4, r3, 1
+
+	lis	r4, TLBWORD2@h
+	ori	r4, r4, TLBWORD2@l
+	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
+

^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2013-10-31 14:23 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-10-16 19:03 [Qemu-devel] [PATCH] import kvm-unittest in QEMU source tree Michael S. Tsirkin
2013-10-30 23:06 ` Andrew Jones
2013-10-31  7:48   ` Gleb Natapov
2013-10-31 14:17     ` Anthony Liguori
2013-10-31 14:26       ` Michael S. Tsirkin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).