* [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, ®s);
+ regs.pc = VM_TEST_LOAD_ADDRESS;
+ kvm_set_regs(kvm, n, ®s);
+
+ 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, ®s);
+ return regs;
+}
+
+void vcpu::set_regs(const kvm_regs& regs)
+{
+ _fd.ioctlp(KVM_SET_REGS, const_cast<kvm_regs*>(®s));
+}
+
+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
* Re: [Qemu-devel] [PATCH] import kvm-unittest in QEMU source tree
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
0 siblings, 1 reply; 5+ messages in thread
From: Andrew Jones @ 2013-10-30 23:06 UTC (permalink / raw)
To: Michael S. Tsirkin; +Cc: Anthony Liguori, pbonzini, qemu-devel, gleb
On Wed, Oct 16, 2013 at 10:03:37PM +0300, Michael S. Tsirkin wrote:
> 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.
>
Sorry for slow reply, I just noticed this mail now.
So far qemu is a dependency for kvm-unit-tests (both x86 and arm use it),
but at some point that could change (if another arch doesn't want to use
qemu). So if that happens, then it won't make much sense for the tests to
live in qemu. Thus I'm not 100% in favor of moving them. However, if the
consensus is to move them, then I have two comments on this patch.
1) There are a couple pendings patches on the kvm list that tidy up the
kvm-unit-tests repo - removing lots of the files. That should be
committed first to avoid importing a bunch of files right before deleting
them.
2) The name shouldn't change from kvm-unit-tests to kvm-unittest.
drew
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [Qemu-devel] [PATCH] import kvm-unittest in QEMU source tree
2013-10-30 23:06 ` Andrew Jones
@ 2013-10-31 7:48 ` Gleb Natapov
2013-10-31 14:17 ` Anthony Liguori
0 siblings, 1 reply; 5+ messages in thread
From: Gleb Natapov @ 2013-10-31 7:48 UTC (permalink / raw)
To: Andrew Jones; +Cc: pbonzini, qemu-devel, Anthony Liguori, Michael S. Tsirkin
On Wed, Oct 30, 2013 at 04:06:19PM -0700, Andrew Jones wrote:
> On Wed, Oct 16, 2013 at 10:03:37PM +0300, Michael S. Tsirkin wrote:
> > 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.
> >
>
> Sorry for slow reply, I just noticed this mail now.
>
> So far qemu is a dependency for kvm-unit-tests (both x86 and arm use it),
> but at some point that could change (if another arch doesn't want to use
> qemu). So if that happens, then it won't make much sense for the tests to
> live in qemu. Thus I'm not 100% in favor of moving them. However, if the
> consensus is to move them, then I have two comments on this patch.
>
Do not worry, we are far from such consensus :)
> 1) There are a couple pendings patches on the kvm list that tidy up the
> kvm-unit-tests repo - removing lots of the files. That should be
> committed first to avoid importing a bunch of files right before deleting
> them.
>
> 2) The name shouldn't change from kvm-unit-tests to kvm-unittest.
>
> drew
--
Gleb.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [Qemu-devel] [PATCH] import kvm-unittest in QEMU source tree
2013-10-31 7:48 ` Gleb Natapov
@ 2013-10-31 14:17 ` Anthony Liguori
2013-10-31 14:26 ` Michael S. Tsirkin
0 siblings, 1 reply; 5+ messages in thread
From: Anthony Liguori @ 2013-10-31 14:17 UTC (permalink / raw)
To: Gleb Natapov; +Cc: Paolo Bonzini, Andrew Jones, qemu-devel, Michael S. Tsirkin
On Thu, Oct 31, 2013 at 8:48 AM, Gleb Natapov <gleb@redhat.com> wrote:
> On Wed, Oct 30, 2013 at 04:06:19PM -0700, Andrew Jones wrote:
>> On Wed, Oct 16, 2013 at 10:03:37PM +0300, Michael S. Tsirkin wrote:
>> > 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.
>> >
>>
>> Sorry for slow reply, I just noticed this mail now.
>>
>> So far qemu is a dependency for kvm-unit-tests (both x86 and arm use it),
>> but at some point that could change (if another arch doesn't want to use
>> qemu). So if that happens, then it won't make much sense for the tests to
>> live in qemu. Thus I'm not 100% in favor of moving them. However, if the
>> consensus is to move them, then I have two comments on this patch.
>>
> Do not worry, we are far from such consensus :)
I don't think kvm-unit-tests belong in QEMU either. They are tied to
the kernel side of things, not QEMU and many of the tests don't make
sense for TCG.
If you want to execute guest code within QEMU's make check, we can
copy the basic infrastructure in kvm-unit-tests. It's so simple that
a "fork" isn't really much of a concern to me.
Regards,
Anthony Liguori
>> 1) There are a couple pendings patches on the kvm list that tidy up the
>> kvm-unit-tests repo - removing lots of the files. That should be
>> committed first to avoid importing a bunch of files right before deleting
>> them.
>>
>> 2) The name shouldn't change from kvm-unit-tests to kvm-unittest.
>>
>> drew
>
> --
> Gleb.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [Qemu-devel] [PATCH] import kvm-unittest in QEMU source tree
2013-10-31 14:17 ` Anthony Liguori
@ 2013-10-31 14:26 ` Michael S. Tsirkin
0 siblings, 0 replies; 5+ messages in thread
From: Michael S. Tsirkin @ 2013-10-31 14:26 UTC (permalink / raw)
To: Anthony Liguori; +Cc: Paolo Bonzini, Andrew Jones, qemu-devel, Gleb Natapov
On Thu, Oct 31, 2013 at 03:17:57PM +0100, Anthony Liguori wrote:
> On Thu, Oct 31, 2013 at 8:48 AM, Gleb Natapov <gleb@redhat.com> wrote:
> > On Wed, Oct 30, 2013 at 04:06:19PM -0700, Andrew Jones wrote:
> >> On Wed, Oct 16, 2013 at 10:03:37PM +0300, Michael S. Tsirkin wrote:
> >> > 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.
> >> >
> >>
> >> Sorry for slow reply, I just noticed this mail now.
> >>
> >> So far qemu is a dependency for kvm-unit-tests (both x86 and arm use it),
> >> but at some point that could change (if another arch doesn't want to use
> >> qemu). So if that happens, then it won't make much sense for the tests to
> >> live in qemu. Thus I'm not 100% in favor of moving them. However, if the
> >> consensus is to move them, then I have two comments on this patch.
> >>
> > Do not worry, we are far from such consensus :)
>
> I don't think kvm-unit-tests belong in QEMU either. They are tied to
> the kernel side of things, not QEMU and many of the tests don't make
> sense for TCG.
>
> If you want to execute guest code within QEMU's make check, we can
> copy the basic infrastructure in kvm-unit-tests. It's so simple that
> a "fork" isn't really much of a concern to me.
>
> Regards,
>
> Anthony Liguori
For now I'm only testing the bios so it's even simpler,
I just put 16 bit code in the boot sector.
> >> 1) There are a couple pendings patches on the kvm list that tidy up the
> >> kvm-unit-tests repo - removing lots of the files. That should be
> >> committed first to avoid importing a bunch of files right before deleting
> >> them.
> >>
> >> 2) The name shouldn't change from kvm-unit-tests to kvm-unittest.
> >>
> >> drew
> >
> > --
> > Gleb.
^ permalink raw reply [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).