[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH] import kvm-unittest in QEMU source tree
From: |
Michael S. Tsirkin |
Subject: |
[Qemu-devel] [PATCH] import kvm-unittest in QEMU source tree |
Date: |
Wed, 16 Oct 2013 22:03:37 +0300 |
This simply imports kvm-unittest git into qemu source tree.
We can next work on making make check run it
automatically.
Squashed 'kvm-unittest/' content from commit 2bc0e29
git-subtree-dir: kvm-unittest
git-subtree-split: 2bc0e29ee4447bebcd3b90053881f59265306adc
Signed-off-by: Michael S. Tsirkin <address@hidden>
---
Gleb, Paolo, any objections to this? I really want a small guest for
running ACPI tests during make check, and kvm-unittest seems to fit the
bill.
Ability to test e.g. PCI with this in-tree would be very benefitial.
diff --git a/kvm-unittest/iotable.h b/kvm-unittest/iotable.h
new file mode 100644
index 0000000..cb18f23
--- /dev/null
+++ b/kvm-unittest/iotable.h
@@ -0,0 +1,40 @@
+/*
+ * Kernel-based Virtual Machine test driver
+ *
+ * This test driver provides a simple way of testing kvm, without a full
+ * device model.
+ *
+ * Copyright (C) 2006 Qumranet
+ *
+ * Authors:
+ *
+ * Avi Kivity <address@hidden>
+ * Yaniv Kamay <address@hidden>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#include <stdint.h>
+
+#define MAX_IO_TABLE 50
+
+typedef int (io_table_handler_t)(void *, int, int, uint64_t, uint64_t *);
+
+struct io_table_entry
+{
+ uint64_t start;
+ uint64_t end;
+ io_table_handler_t *handler;
+ void *opaque;
+};
+
+struct io_table
+{
+ int nr_entries;
+ struct io_table_entry entries[MAX_IO_TABLE];
+};
+
+struct io_table_entry *io_table_lookup(struct io_table *io_table,
+ uint64_t addr);
+int io_table_register(struct io_table *io_table, uint64_t start, uint64_t size,
+ io_table_handler_t *handler, void *opaque);
diff --git a/kvm-unittest/lib/libcflat.h b/kvm-unittest/lib/libcflat.h
new file mode 100644
index 0000000..fadc33d
--- /dev/null
+++ b/kvm-unittest/lib/libcflat.h
@@ -0,0 +1,61 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <address@hidden>
+ */
+
+#ifndef __LIBCFLAT_H
+#define __LIBCFLAT_H
+
+#include <stdarg.h>
+
+typedef unsigned char u8;
+typedef signed char s8;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned u32;
+typedef signed s32;
+typedef unsigned long ulong;
+typedef unsigned long long u64;
+typedef signed long long s64;
+typedef unsigned long size_t;
+typedef _Bool bool;
+
+#define true 1
+#define false 0
+
+extern void exit(int code);
+extern void panic(char *fmt, ...);
+
+extern unsigned long strlen(const char *buf);
+extern char *strcat(char *dest, const char *src);
+extern int strcmp(const char *a, const char *b);
+
+extern int printf(const char *fmt, ...);
+extern int vsnprintf(char *buf, int size, const char *fmt, va_list va);
+
+extern void puts(const char *s);
+
+extern void *memset(void *s, int c, size_t n);
+extern void *memcpy(void *dest, const void *src, size_t n);
+
+extern long atol(const char *ptr);
+#define ARRAY_SIZE(_a) (sizeof(_a)/sizeof((_a)[0]))
+
+#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER)
+
+#define NULL ((void *)0UL)
+#endif
diff --git a/kvm-unittest/lib/powerpc/44x/timebase.h
b/kvm-unittest/lib/powerpc/44x/timebase.h
new file mode 100644
index 0000000..ce85347
--- /dev/null
+++ b/kvm-unittest/lib/powerpc/44x/timebase.h
@@ -0,0 +1,25 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <address@hidden>
+ */
+
+#ifndef __TIMEBASE_H__
+#define __TIMEBASE_H__
+
+unsigned long long mftb(void);
+
+#endif /* __TIMEBASE_H__ */
diff --git a/kvm-unittest/lib/x86/apic-defs.h b/kvm-unittest/lib/x86/apic-defs.h
new file mode 100644
index 0000000..c061e3d
--- /dev/null
+++ b/kvm-unittest/lib/x86/apic-defs.h
@@ -0,0 +1,133 @@
+#ifndef _ASM_X86_APICDEF_H
+#define _ASM_X86_APICDEF_H
+
+/*
+ * Constants for various Intel APICs. (local APIC, IOAPIC, etc.)
+ *
+ * Alan Cox <address@hidden>, 1995.
+ * Ingo Molnar <address@hidden>, 1999, 2000
+ */
+
+#define APIC_DEFAULT_PHYS_BASE 0xfee00000
+
+#define APIC_ID 0x20
+
+#define APIC_LVR 0x30
+#define APIC_LVR_MASK 0xFF00FF
+#define GET_APIC_VERSION(x) ((x) & 0xFFu)
+#define GET_APIC_MAXLVT(x) (((x) >> 16) & 0xFFu)
+#ifdef CONFIG_X86_32
+# define APIC_INTEGRATED(x) ((x) & 0xF0u)
+#else
+# define APIC_INTEGRATED(x) (1)
+#endif
+#define APIC_XAPIC(x) ((x) >= 0x14)
+#define APIC_TASKPRI 0x80
+#define APIC_TPRI_MASK 0xFFu
+#define APIC_ARBPRI 0x90
+#define APIC_ARBPRI_MASK 0xFFu
+#define APIC_PROCPRI 0xA0
+#define APIC_EOI 0xB0
+#define APIC_EIO_ACK 0x0
+#define APIC_RRR 0xC0
+#define APIC_LDR 0xD0
+#define APIC_LDR_MASK (0xFFu << 24)
+#define GET_APIC_LOGICAL_ID(x) (((x) >> 24) & 0xFFu)
+#define SET_APIC_LOGICAL_ID(x) (((x) << 24))
+#define APIC_ALL_CPUS 0xFFu
+#define APIC_DFR 0xE0
+#define APIC_DFR_CLUSTER 0x0FFFFFFFul
+#define APIC_DFR_FLAT 0xFFFFFFFFul
+#define APIC_SPIV 0xF0
+#define APIC_SPIV_FOCUS_DISABLED (1 << 9)
+#define APIC_SPIV_APIC_ENABLED (1 << 8)
+#define APIC_ISR 0x100
+#define APIC_ISR_NR 0x8 /* Number of 32 bit ISR registers. */
+#define APIC_TMR 0x180
+#define APIC_IRR 0x200
+#define APIC_ESR 0x280
+#define APIC_ESR_SEND_CS 0x00001
+#define APIC_ESR_RECV_CS 0x00002
+#define APIC_ESR_SEND_ACC 0x00004
+#define APIC_ESR_RECV_ACC 0x00008
+#define APIC_ESR_SENDILL 0x00020
+#define APIC_ESR_RECVILL 0x00040
+#define APIC_ESR_ILLREGA 0x00080
+#define APIC_ICR 0x300
+#define APIC_DEST_SELF 0x40000
+#define APIC_DEST_ALLINC 0x80000
+#define APIC_DEST_ALLBUT 0xC0000
+#define APIC_ICR_RR_MASK 0x30000
+#define APIC_ICR_RR_INVALID 0x00000
+#define APIC_ICR_RR_INPROG 0x10000
+#define APIC_ICR_RR_VALID 0x20000
+#define APIC_INT_LEVELTRIG 0x08000
+#define APIC_INT_ASSERT 0x04000
+#define APIC_ICR_BUSY 0x01000
+#define APIC_DEST_LOGICAL 0x00800
+#define APIC_DEST_PHYSICAL 0x00000
+#define APIC_DM_FIXED 0x00000
+#define APIC_DM_LOWEST 0x00100
+#define APIC_DM_SMI 0x00200
+#define APIC_DM_REMRD 0x00300
+#define APIC_DM_NMI 0x00400
+#define APIC_DM_INIT 0x00500
+#define APIC_DM_STARTUP 0x00600
+#define APIC_DM_EXTINT 0x00700
+#define APIC_VECTOR_MASK 0x000FF
+#define APIC_ICR2 0x310
+#define GET_APIC_DEST_FIELD(x) (((x) >> 24) & 0xFF)
+#define SET_APIC_DEST_FIELD(x) ((x) << 24)
+#define APIC_LVTT 0x320
+#define APIC_LVTTHMR 0x330
+#define APIC_LVTPC 0x340
+#define APIC_LVT0 0x350
+#define APIC_LVT_TIMER_BASE_MASK (0x3 << 18)
+#define GET_APIC_TIMER_BASE(x) (((x) >> 18) & 0x3)
+#define SET_APIC_TIMER_BASE(x) (((x) << 18))
+#define APIC_TIMER_BASE_CLKIN 0x0
+#define APIC_TIMER_BASE_TMBASE 0x1
+#define APIC_TIMER_BASE_DIV 0x2
+#define APIC_LVT_TIMER_PERIODIC (1 << 17)
+#define APIC_LVT_MASKED (1 << 16)
+#define APIC_LVT_LEVEL_TRIGGER (1 << 15)
+#define APIC_LVT_REMOTE_IRR (1 << 14)
+#define APIC_INPUT_POLARITY (1 << 13)
+#define APIC_SEND_PENDING (1 << 12)
+#define APIC_MODE_MASK 0x700
+#define GET_APIC_DELIVERY_MODE(x) (((x) >> 8) & 0x7)
+#define SET_APIC_DELIVERY_MODE(x, y) (((x) & ~0x700) | ((y)
<< 8))
+#define APIC_MODE_FIXED 0x0
+#define APIC_MODE_NMI 0x4
+#define APIC_MODE_EXTINT 0x7
+#define APIC_LVT1 0x360
+#define APIC_LVTERR 0x370
+#define APIC_TMICT 0x380
+#define APIC_TMCCT 0x390
+#define APIC_TDCR 0x3E0
+#define APIC_SELF_IPI 0x3F0
+#define APIC_TDR_DIV_TMBASE (1 << 2)
+#define APIC_TDR_DIV_1 0xB
+#define APIC_TDR_DIV_2 0x0
+#define APIC_TDR_DIV_4 0x1
+#define APIC_TDR_DIV_8 0x2
+#define APIC_TDR_DIV_16 0x3
+#define APIC_TDR_DIV_32 0x8
+#define APIC_TDR_DIV_64 0x9
+#define APIC_TDR_DIV_128 0xA
+#define APIC_EILVT0 0x500
+#define APIC_EILVT_NR_AMD_K8 1 /* # of extended
interrupts */
+#define APIC_EILVT_NR_AMD_10H 4
+#define APIC_EILVT_LVTOFF(x) (((x) >> 4) & 0xF)
+#define APIC_EILVT_MSG_FIX 0x0
+#define APIC_EILVT_MSG_SMI 0x2
+#define APIC_EILVT_MSG_NMI 0x4
+#define APIC_EILVT_MSG_EXT 0x7
+#define APIC_EILVT_MASKED (1 << 16)
+#define APIC_EILVT1 0x510
+#define APIC_EILVT2 0x520
+#define APIC_EILVT3 0x530
+
+#define APIC_BASE_MSR 0x800
+
+#endif /* _ASM_X86_APICDEF_H */
diff --git a/kvm-unittest/lib/x86/apic.h b/kvm-unittest/lib/x86/apic.h
new file mode 100644
index 0000000..e325e9a
--- /dev/null
+++ b/kvm-unittest/lib/x86/apic.h
@@ -0,0 +1,34 @@
+#ifndef CFLAT_APIC_H
+#define CFLAT_APIC_H
+
+#include <stdint.h>
+#include "apic-defs.h"
+
+typedef struct {
+ uint8_t vector;
+ uint8_t delivery_mode:3;
+ uint8_t dest_mode:1;
+ uint8_t delivery_status:1;
+ uint8_t polarity:1;
+ uint8_t remote_irr:1;
+ uint8_t trig_mode:1;
+ uint8_t mask:1;
+ uint8_t reserve:7;
+ uint8_t reserved[4];
+ uint8_t dest_id;
+} ioapic_redir_entry_t;
+
+void mask_pic_interrupts(void);
+
+void ioapic_write_redir(unsigned line, ioapic_redir_entry_t e);
+void ioapic_write_reg(unsigned reg, uint32_t value);
+
+void enable_apic(void);
+uint32_t apic_read(unsigned reg);
+void apic_write(unsigned reg, uint32_t val);
+void apic_icr_write(uint32_t val, uint32_t dest);
+uint32_t apic_id(void);
+
+int enable_x2apic(void);
+
+#endif
diff --git a/kvm-unittest/lib/x86/atomic.h b/kvm-unittest/lib/x86/atomic.h
new file mode 100644
index 0000000..de2f033
--- /dev/null
+++ b/kvm-unittest/lib/x86/atomic.h
@@ -0,0 +1,164 @@
+#ifndef __ATOMIC_H
+#define __ATOMIC_H
+
+typedef struct {
+ volatile int counter;
+} atomic_t;
+
+#ifdef __i386__
+
+/**
+ * atomic_read - read atomic variable
+ * @v: pointer of type atomic_t
+ *
+ * Atomically reads the value of @v.
+ */
+static inline int atomic_read(const atomic_t *v)
+{
+ return v->counter;
+}
+
+/**
+ * atomic_set - set atomic variable
+ * @v: pointer of type atomic_t
+ * @i: required value
+ *
+ * Atomically sets the value of @v to @i.
+ */
+static inline void atomic_set(atomic_t *v, int i)
+{
+ v->counter = i;
+}
+
+/**
+ * atomic_inc - increment atomic variable
+ * @v: pointer of type atomic_t
+ *
+ * Atomically increments @v by 1.
+ */
+static inline void atomic_inc(atomic_t *v)
+{
+ asm volatile("lock incl %0"
+ : "+m" (v->counter));
+}
+
+/**
+ * atomic_dec - decrement atomic variable
+ * @v: pointer of type atomic_t
+ *
+ * Atomically decrements @v by 1.
+ */
+static inline void atomic_dec(atomic_t *v)
+{
+ asm volatile("lock decl %0"
+ : "+m" (v->counter));
+}
+
+typedef struct {
+ u64 __attribute__((aligned(8))) counter;
+} atomic64_t;
+
+#define ATOMIC64_INIT(val) { (val) }
+
+/**
+ * atomic64_read - read atomic64 variable
+ * @ptr: pointer to type atomic64_t
+ *
+ * Atomically reads the value of @ptr and returns it.
+ */
+static inline u64 atomic64_read(atomic64_t *ptr)
+{
+ u64 res;
+
+ /*
+ * Note, we inline this atomic64_t primitive because
+ * it only clobbers EAX/EDX and leaves the others
+ * untouched. We also (somewhat subtly) rely on the
+ * fact that cmpxchg8b returns the current 64-bit value
+ * of the memory location we are touching:
+ */
+ asm volatile("mov %%ebx, %%eax\n\t"
+ "mov %%ecx, %%edx\n\t"
+ "lock cmpxchg8b %1\n"
+ : "=&A" (res)
+ : "m" (*ptr)
+ );
+ return res;
+}
+
+u64 atomic64_cmpxchg(atomic64_t *v, u64 old, u64 new);
+
+#elif defined(__x86_64__)
+
+/**
+ * atomic_read - read atomic variable
+ * @v: pointer of type atomic_t
+ *
+ * Atomically reads the value of @v.
+ */
+static inline int atomic_read(const atomic_t *v)
+{
+ return v->counter;
+}
+
+/**
+ * atomic_set - set atomic variable
+ * @v: pointer of type atomic_t
+ * @i: required value
+ *
+ * Atomically sets the value of @v to @i.
+ */
+static inline void atomic_set(atomic_t *v, int i)
+{
+ v->counter = i;
+}
+
+/**
+ * atomic_inc - increment atomic variable
+ * @v: pointer of type atomic_t
+ *
+ * Atomically increments @v by 1.
+ */
+static inline void atomic_inc(atomic_t *v)
+{
+ asm volatile("lock incl %0"
+ : "=m" (v->counter)
+ : "m" (v->counter));
+}
+
+/**
+ * atomic_dec - decrement atomic variable
+ * @v: pointer of type atomic_t
+ *
+ * Atomically decrements @v by 1.
+ */
+static inline void atomic_dec(atomic_t *v)
+{
+ asm volatile("lock decl %0"
+ : "=m" (v->counter)
+ : "m" (v->counter));
+}
+
+typedef struct {
+ long long counter;
+} atomic64_t;
+
+#define ATOMIC64_INIT(i) { (i) }
+
+/**
+ * atomic64_read - read atomic64 variable
+ * @v: pointer of type atomic64_t
+ *
+ * Atomically reads the value of @v.
+ * Doesn't imply a read memory barrier.
+ */
+static inline long atomic64_read(const atomic64_t *v)
+{
+ return v->counter;
+}
+
+u64 atomic64_cmpxchg(atomic64_t *v, u64 old, u64 new);
+
+#endif
+
+#endif
diff --git a/kvm-unittest/lib/x86/desc.h b/kvm-unittest/lib/x86/desc.h
new file mode 100644
index 0000000..f819452
--- /dev/null
+++ b/kvm-unittest/lib/x86/desc.h
@@ -0,0 +1,87 @@
+#ifndef __IDT_TEST__
+#define __IDT_TEST__
+
+void setup_idt(void);
+#ifndef __x86_64__
+void setup_gdt(void);
+void setup_tss32(void);
+#else
+static inline void setup_gdt(void){}
+static inline void setup_tss32(void){}
+#endif
+
+struct ex_regs {
+ unsigned long rax, rcx, rdx, rbx;
+ unsigned long dummy, rbp, rsi, rdi;
+#ifdef __x86_64__
+ unsigned long r8, r9, r10, r11;
+ unsigned long r12, r13, r14, r15;
+#endif
+ unsigned long vector;
+ unsigned long error_code;
+ unsigned long rip;
+ unsigned long cs;
+ unsigned long rflags;
+};
+
+typedef struct {
+ u16 prev;
+ u16 res1;
+ u32 esp0;
+ u16 ss0;
+ u16 res2;
+ u32 esp1;
+ u16 ss1;
+ u16 res3;
+ u32 esp2;
+ u16 ss2;
+ u16 res4;
+ u32 cr3;
+ u32 eip;
+ u32 eflags;
+ u32 eax, ecx, edx, ebx, esp, ebp, esi, edi;
+ u16 es;
+ u16 res5;
+ u16 cs;
+ u16 res6;
+ u16 ss;
+ u16 res7;
+ u16 ds;
+ u16 res8;
+ u16 fs;
+ u16 res9;
+ u16 gs;
+ u16 res10;
+ u16 ldt;
+ u16 res11;
+ u16 t:1;
+ u16 res12:15;
+ u16 iomap_base;
+} tss32_t;
+
+#define ASM_TRY(catch) \
+ "movl $0, %%gs:4 \n\t" \
+ ".pushsection .data.ex \n\t" \
+ ".quad 1111f, " catch "\n\t" \
+ ".popsection \n\t" \
+ "1111:"
+
+#define UD_VECTOR 6
+#define GP_VECTOR 13
+
+#define TSS_MAIN 0x20
+#define TSS_INTR 0x28
+
+#define NP_SEL 0x18
+
+unsigned exception_vector(void);
+unsigned exception_error_code(void);
+void set_idt_entry(int vec, void *addr, int dpl);
+void set_idt_sel(int vec, u16 sel);
+void set_gdt_entry(int num, u32 base, u32 limit, u8 access, u8 gran);
+void set_idt_task_gate(int vec, u16 sel);
+void set_intr_task_gate(int e, void *fn);
+void print_current_tss_info(void);
+void handle_exception(u8 v, void (*func)(struct ex_regs *regs));
+
+#endif
diff --git a/kvm-unittest/lib/x86/fake-apic.h b/kvm-unittest/lib/x86/fake-apic.h
new file mode 100644
index 0000000..eed63ba
--- /dev/null
+++ b/kvm-unittest/lib/x86/fake-apic.h
@@ -0,0 +1,14 @@
+#ifndef SILLY_APIC_H
+#define SILLY_APIC_H
+
+#define APIC_BASE 0x1000
+#define APIC_SIZE 0x100
+
+#define APIC_REG_NCPU 0x00
+#define APIC_REG_ID 0x04
+#define APIC_REG_SIPI_ADDR 0x08
+#define APIC_REG_SEND_SIPI 0x0c
+#define APIC_REG_IPI_VECTOR 0x10
+#define APIC_REG_SEND_IPI 0x14
+
+#endif
diff --git a/kvm-unittest/lib/x86/fwcfg.h b/kvm-unittest/lib/x86/fwcfg.h
new file mode 100644
index 0000000..e0836ca
--- /dev/null
+++ b/kvm-unittest/lib/x86/fwcfg.h
@@ -0,0 +1,44 @@
+#ifndef FWCFG_H
+#define FWCFG_H
+
+#include <stdint.h>
+
+#define FW_CFG_SIGNATURE 0x00
+#define FW_CFG_ID 0x01
+#define FW_CFG_UUID 0x02
+#define FW_CFG_RAM_SIZE 0x03
+#define FW_CFG_NOGRAPHIC 0x04
+#define FW_CFG_NB_CPUS 0x05
+#define FW_CFG_MACHINE_ID 0x06
+#define FW_CFG_KERNEL_ADDR 0x07
+#define FW_CFG_KERNEL_SIZE 0x08
+#define FW_CFG_KERNEL_CMDLINE 0x09
+#define FW_CFG_INITRD_ADDR 0x0a
+#define FW_CFG_INITRD_SIZE 0x0b
+#define FW_CFG_BOOT_DEVICE 0x0c
+#define FW_CFG_NUMA 0x0d
+#define FW_CFG_BOOT_MENU 0x0e
+#define FW_CFG_MAX_CPUS 0x0f
+#define FW_CFG_MAX_ENTRY 0x10
+
+#define FW_CFG_WRITE_CHANNEL 0x4000
+#define FW_CFG_ARCH_LOCAL 0x8000
+#define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)
+
+#define FW_CFG_INVALID 0xffff
+
+#define BIOS_CFG_IOPORT 0x510
+
+#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
+#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
+#define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2)
+
+uint8_t fwcfg_get_u8(unsigned index);
+uint16_t fwcfg_get_u16(unsigned index);
+uint32_t fwcfg_get_u32(unsigned index);
+uint64_t fwcfg_get_u64(unsigned index);
+
+unsigned fwcfg_get_nb_cpus(void);
+
+#endif
+
diff --git a/kvm-unittest/lib/x86/io.h b/kvm-unittest/lib/x86/io.h
new file mode 100644
index 0000000..bd6341c
--- /dev/null
+++ b/kvm-unittest/lib/x86/io.h
@@ -0,0 +1,40 @@
+#ifndef IO_H
+#define IO_H
+
+static inline unsigned char inb(unsigned short port)
+{
+ unsigned char value;
+ asm volatile("inb %w1, %0" : "=a" (value) : "Nd" (port));
+ return value;
+}
+
+static inline unsigned short inw(unsigned short port)
+{
+ unsigned short value;
+ asm volatile("inw %w1, %0" : "=a" (value) : "Nd" (port));
+ return value;
+}
+
+static inline unsigned int inl(unsigned short port)
+{
+ unsigned int value;
+ asm volatile("inl %w1, %0" : "=a" (value) : "Nd" (port));
+ return value;
+}
+
+static inline void outb(unsigned char value, unsigned short port)
+{
+ asm volatile("outb %b0, %w1" : : "a"(value), "Nd"(port));
+}
+
+static inline void outw(unsigned short value, unsigned short port)
+{
+ asm volatile("outw %w0, %w1" : : "a"(value), "Nd"(port));
+}
+
+static inline void outl(unsigned int value, unsigned short port)
+{
+ asm volatile("outl %0, %w1" : : "a"(value), "Nd"(port));
+}
+
+#endif
diff --git a/kvm-unittest/lib/x86/isr.h b/kvm-unittest/lib/x86/isr.h
new file mode 100644
index 0000000..b07a32a
--- /dev/null
+++ b/kvm-unittest/lib/x86/isr.h
@@ -0,0 +1,14 @@
+#ifndef __ISR_TEST__
+#define __ISR_TEST__
+
+typedef struct {
+ ulong regs[sizeof(ulong)*2];
+ ulong func;
+ ulong rip;
+ ulong cs;
+ ulong rflags;
+} isr_regs_t;
+
+void handle_irq(unsigned vec, void (*func)(isr_regs_t *regs));
+
+#endif
diff --git a/kvm-unittest/lib/x86/msr.h b/kvm-unittest/lib/x86/msr.h
new file mode 100644
index 0000000..281255a
--- /dev/null
+++ b/kvm-unittest/lib/x86/msr.h
@@ -0,0 +1,411 @@
+#ifndef _ASM_X86_MSR_INDEX_H
+#define _ASM_X86_MSR_INDEX_H
+
+/* CPU model specific register (MSR) numbers */
+
+/* x86-64 specific MSRs */
+#define MSR_EFER 0xc0000080 /* extended feature register */
+#define MSR_STAR 0xc0000081 /* legacy mode SYSCALL target */
+#define MSR_LSTAR 0xc0000082 /* long mode SYSCALL target */
+#define MSR_CSTAR 0xc0000083 /* compat mode SYSCALL target */
+#define MSR_SYSCALL_MASK 0xc0000084 /* EFLAGS mask for syscall */
+#define MSR_FS_BASE 0xc0000100 /* 64bit FS base */
+#define MSR_GS_BASE 0xc0000101 /* 64bit GS base */
+#define MSR_KERNEL_GS_BASE 0xc0000102 /* SwapGS GS shadow */
+#define MSR_TSC_AUX 0xc0000103 /* Auxiliary TSC */
+
+/* EFER bits: */
+#define _EFER_SCE 0 /* SYSCALL/SYSRET */
+#define _EFER_LME 8 /* Long mode enable */
+#define _EFER_LMA 10 /* Long mode active (read-only) */
+#define _EFER_NX 11 /* No execute enable */
+#define _EFER_SVME 12 /* Enable virtualization */
+#define _EFER_LMSLE 13 /* Long Mode Segment Limit Enable */
+#define _EFER_FFXSR 14 /* Enable Fast FXSAVE/FXRSTOR */
+
+#define EFER_SCE (1<<_EFER_SCE)
+#define EFER_LME (1<<_EFER_LME)
+#define EFER_LMA (1<<_EFER_LMA)
+#define EFER_NX (1<<_EFER_NX)
+#define EFER_SVME (1<<_EFER_SVME)
+#define EFER_LMSLE (1<<_EFER_LMSLE)
+#define EFER_FFXSR (1<<_EFER_FFXSR)
+
+/* Intel MSRs. Some also available on other CPUs */
+#define MSR_IA32_PERFCTR0 0x000000c1
+#define MSR_IA32_PERFCTR1 0x000000c2
+#define MSR_FSB_FREQ 0x000000cd
+
+#define MSR_MTRRcap 0x000000fe
+#define MSR_IA32_BBL_CR_CTL 0x00000119
+
+#define MSR_IA32_SYSENTER_CS 0x00000174
+#define MSR_IA32_SYSENTER_ESP 0x00000175
+#define MSR_IA32_SYSENTER_EIP 0x00000176
+
+#define MSR_IA32_MCG_CAP 0x00000179
+#define MSR_IA32_MCG_STATUS 0x0000017a
+#define MSR_IA32_MCG_CTL 0x0000017b
+
+#define MSR_IA32_PEBS_ENABLE 0x000003f1
+#define MSR_IA32_DS_AREA 0x00000600
+#define MSR_IA32_PERF_CAPABILITIES 0x00000345
+
+#define MSR_MTRRfix64K_00000 0x00000250
+#define MSR_MTRRfix16K_80000 0x00000258
+#define MSR_MTRRfix16K_A0000 0x00000259
+#define MSR_MTRRfix4K_C0000 0x00000268
+#define MSR_MTRRfix4K_C8000 0x00000269
+#define MSR_MTRRfix4K_D0000 0x0000026a
+#define MSR_MTRRfix4K_D8000 0x0000026b
+#define MSR_MTRRfix4K_E0000 0x0000026c
+#define MSR_MTRRfix4K_E8000 0x0000026d
+#define MSR_MTRRfix4K_F0000 0x0000026e
+#define MSR_MTRRfix4K_F8000 0x0000026f
+#define MSR_MTRRdefType 0x000002ff
+
+#define MSR_IA32_CR_PAT 0x00000277
+
+#define MSR_IA32_DEBUGCTLMSR 0x000001d9
+#define MSR_IA32_LASTBRANCHFROMIP 0x000001db
+#define MSR_IA32_LASTBRANCHTOIP 0x000001dc
+#define MSR_IA32_LASTINTFROMIP 0x000001dd
+#define MSR_IA32_LASTINTTOIP 0x000001de
+
+/* DEBUGCTLMSR bits (others vary by model): */
+#define DEBUGCTLMSR_LBR (1UL << 0) /* last branch
recording */
+#define DEBUGCTLMSR_BTF (1UL << 1) /* single-step on
branches */
+#define DEBUGCTLMSR_TR (1UL << 6)
+#define DEBUGCTLMSR_BTS (1UL << 7)
+#define DEBUGCTLMSR_BTINT (1UL << 8)
+#define DEBUGCTLMSR_BTS_OFF_OS (1UL << 9)
+#define DEBUGCTLMSR_BTS_OFF_USR (1UL << 10)
+#define DEBUGCTLMSR_FREEZE_LBRS_ON_PMI (1UL << 11)
+
+#define MSR_IA32_MC0_CTL 0x00000400
+#define MSR_IA32_MC0_STATUS 0x00000401
+#define MSR_IA32_MC0_ADDR 0x00000402
+#define MSR_IA32_MC0_MISC 0x00000403
+
+#define MSR_IA32_MCx_CTL(x) (MSR_IA32_MC0_CTL + 4*(x))
+#define MSR_IA32_MCx_STATUS(x) (MSR_IA32_MC0_STATUS + 4*(x))
+#define MSR_IA32_MCx_ADDR(x) (MSR_IA32_MC0_ADDR + 4*(x))
+#define MSR_IA32_MCx_MISC(x) (MSR_IA32_MC0_MISC + 4*(x))
+
+/* These are consecutive and not in the normal 4er MCE bank block */
+#define MSR_IA32_MC0_CTL2 0x00000280
+#define MSR_IA32_MCx_CTL2(x) (MSR_IA32_MC0_CTL2 + (x))
+
+#define CMCI_EN (1ULL << 30)
+#define CMCI_THRESHOLD_MASK 0xffffULL
+
+#define MSR_P6_PERFCTR0 0x000000c1
+#define MSR_P6_PERFCTR1 0x000000c2
+#define MSR_P6_EVNTSEL0 0x00000186
+#define MSR_P6_EVNTSEL1 0x00000187
+
+/* AMD64 MSRs. Not complete. See the architecture manual for a more
+ complete list. */
+
+#define MSR_AMD64_PATCH_LEVEL 0x0000008b
+#define MSR_AMD64_NB_CFG 0xc001001f
+#define MSR_AMD64_PATCH_LOADER 0xc0010020
+#define MSR_AMD64_OSVW_ID_LENGTH 0xc0010140
+#define MSR_AMD64_OSVW_STATUS 0xc0010141
+#define MSR_AMD64_DC_CFG 0xc0011022
+#define MSR_AMD64_IBSFETCHCTL 0xc0011030
+#define MSR_AMD64_IBSFETCHLINAD 0xc0011031
+#define MSR_AMD64_IBSFETCHPHYSAD 0xc0011032
+#define MSR_AMD64_IBSOPCTL 0xc0011033
+#define MSR_AMD64_IBSOPRIP 0xc0011034
+#define MSR_AMD64_IBSOPDATA 0xc0011035
+#define MSR_AMD64_IBSOPDATA2 0xc0011036
+#define MSR_AMD64_IBSOPDATA3 0xc0011037
+#define MSR_AMD64_IBSDCLINAD 0xc0011038
+#define MSR_AMD64_IBSDCPHYSAD 0xc0011039
+#define MSR_AMD64_IBSCTL 0xc001103a
+
+/* Fam 10h MSRs */
+#define MSR_FAM10H_MMIO_CONF_BASE 0xc0010058
+#define FAM10H_MMIO_CONF_ENABLE (1<<0)
+#define FAM10H_MMIO_CONF_BUSRANGE_MASK 0xf
+#define FAM10H_MMIO_CONF_BUSRANGE_SHIFT 2
+#define FAM10H_MMIO_CONF_BASE_MASK 0xfffffff
+#define FAM10H_MMIO_CONF_BASE_SHIFT 20
+#define MSR_FAM10H_NODE_ID 0xc001100c
+
+/* K8 MSRs */
+#define MSR_K8_TOP_MEM1 0xc001001a
+#define MSR_K8_TOP_MEM2 0xc001001d
+#define MSR_K8_SYSCFG 0xc0010010
+#define MSR_K8_INT_PENDING_MSG 0xc0010055
+/* C1E active bits in int pending message */
+#define K8_INTP_C1E_ACTIVE_MASK 0x18000000
+#define MSR_K8_TSEG_ADDR 0xc0010112
+#define K8_MTRRFIXRANGE_DRAM_ENABLE 0x00040000 /* MtrrFixDramEn bit */
+#define K8_MTRRFIXRANGE_DRAM_MODIFY 0x00080000 /* MtrrFixDramModEn bit */
+#define K8_MTRR_RDMEM_WRMEM_MASK 0x18181818 /* Mask: RdMem|WrMem */
+
+/* K7 MSRs */
+#define MSR_K7_EVNTSEL0 0xc0010000
+#define MSR_K7_PERFCTR0 0xc0010004
+#define MSR_K7_EVNTSEL1 0xc0010001
+#define MSR_K7_PERFCTR1 0xc0010005
+#define MSR_K7_EVNTSEL2 0xc0010002
+#define MSR_K7_PERFCTR2 0xc0010006
+#define MSR_K7_EVNTSEL3 0xc0010003
+#define MSR_K7_PERFCTR3 0xc0010007
+#define MSR_K7_CLK_CTL 0xc001001b
+#define MSR_K7_HWCR 0xc0010015
+#define MSR_K7_FID_VID_CTL 0xc0010041
+#define MSR_K7_FID_VID_STATUS 0xc0010042
+
+/* K6 MSRs */
+#define MSR_K6_EFER 0xc0000080
+#define MSR_K6_STAR 0xc0000081
+#define MSR_K6_WHCR 0xc0000082
+#define MSR_K6_UWCCR 0xc0000085
+#define MSR_K6_EPMR 0xc0000086
+#define MSR_K6_PSOR 0xc0000087
+#define MSR_K6_PFIR 0xc0000088
+
+/* Centaur-Hauls/IDT defined MSRs. */
+#define MSR_IDT_FCR1 0x00000107
+#define MSR_IDT_FCR2 0x00000108
+#define MSR_IDT_FCR3 0x00000109
+#define MSR_IDT_FCR4 0x0000010a
+
+#define MSR_IDT_MCR0 0x00000110
+#define MSR_IDT_MCR1 0x00000111
+#define MSR_IDT_MCR2 0x00000112
+#define MSR_IDT_MCR3 0x00000113
+#define MSR_IDT_MCR4 0x00000114
+#define MSR_IDT_MCR5 0x00000115
+#define MSR_IDT_MCR6 0x00000116
+#define MSR_IDT_MCR7 0x00000117
+#define MSR_IDT_MCR_CTRL 0x00000120
+
+/* VIA Cyrix defined MSRs*/
+#define MSR_VIA_FCR 0x00001107
+#define MSR_VIA_LONGHAUL 0x0000110a
+#define MSR_VIA_RNG 0x0000110b
+#define MSR_VIA_BCR2 0x00001147
+
+/* Transmeta defined MSRs */
+#define MSR_TMTA_LONGRUN_CTRL 0x80868010
+#define MSR_TMTA_LONGRUN_FLAGS 0x80868011
+#define MSR_TMTA_LRTI_READOUT 0x80868018
+#define MSR_TMTA_LRTI_VOLT_MHZ 0x8086801a
+
+/* Intel defined MSRs. */
+#define MSR_IA32_P5_MC_ADDR 0x00000000
+#define MSR_IA32_P5_MC_TYPE 0x00000001
+#define MSR_IA32_TSC 0x00000010
+#define MSR_IA32_PLATFORM_ID 0x00000017
+#define MSR_IA32_EBL_CR_POWERON 0x0000002a
+#define MSR_IA32_FEATURE_CONTROL 0x0000003a
+
+#define FEATURE_CONTROL_LOCKED (1<<0)
+#define FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX (1<<1)
+#define FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX (1<<2)
+
+#define MSR_IA32_APICBASE 0x0000001b
+#define MSR_IA32_APICBASE_BSP (1<<8)
+#define MSR_IA32_APICBASE_ENABLE (1<<11)
+#define MSR_IA32_APICBASE_BASE (0xfffff<<12)
+
+#define MSR_IA32_UCODE_WRITE 0x00000079
+#define MSR_IA32_UCODE_REV 0x0000008b
+
+#define MSR_IA32_PERF_STATUS 0x00000198
+#define MSR_IA32_PERF_CTL 0x00000199
+
+#define MSR_IA32_MPERF 0x000000e7
+#define MSR_IA32_APERF 0x000000e8
+
+#define MSR_IA32_THERM_CONTROL 0x0000019a
+#define MSR_IA32_THERM_INTERRUPT 0x0000019b
+
+#define THERM_INT_LOW_ENABLE (1 << 0)
+#define THERM_INT_HIGH_ENABLE (1 << 1)
+
+#define MSR_IA32_THERM_STATUS 0x0000019c
+
+#define THERM_STATUS_PROCHOT (1 << 0)
+
+#define MSR_THERM2_CTL 0x0000019d
+
+#define MSR_THERM2_CTL_TM_SELECT (1ULL << 16)
+
+#define MSR_IA32_MISC_ENABLE 0x000001a0
+
+#define MSR_IA32_TEMPERATURE_TARGET 0x000001a2
+
+/* MISC_ENABLE bits: architectural */
+#define MSR_IA32_MISC_ENABLE_FAST_STRING (1ULL << 0)
+#define MSR_IA32_MISC_ENABLE_TCC (1ULL << 1)
+#define MSR_IA32_MISC_ENABLE_EMON (1ULL << 7)
+#define MSR_IA32_MISC_ENABLE_BTS_UNAVAIL (1ULL << 11)
+#define MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL (1ULL << 12)
+#define MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP (1ULL << 16)
+#define MSR_IA32_MISC_ENABLE_MWAIT (1ULL << 18)
+#define MSR_IA32_MISC_ENABLE_LIMIT_CPUID (1ULL << 22)
+#define MSR_IA32_MISC_ENABLE_XTPR_DISABLE (1ULL << 23)
+#define MSR_IA32_MISC_ENABLE_XD_DISABLE (1ULL << 34)
+
+/* MISC_ENABLE bits: model-specific, meaning may vary from core to core */
+#define MSR_IA32_MISC_ENABLE_X87_COMPAT (1ULL << 2)
+#define MSR_IA32_MISC_ENABLE_TM1 (1ULL << 3)
+#define MSR_IA32_MISC_ENABLE_SPLIT_LOCK_DISABLE (1ULL << 4)
+#define MSR_IA32_MISC_ENABLE_L3CACHE_DISABLE (1ULL << 6)
+#define MSR_IA32_MISC_ENABLE_SUPPRESS_LOCK (1ULL << 8)
+#define MSR_IA32_MISC_ENABLE_PREFETCH_DISABLE (1ULL << 9)
+#define MSR_IA32_MISC_ENABLE_FERR (1ULL << 10)
+#define MSR_IA32_MISC_ENABLE_FERR_MULTIPLEX (1ULL << 10)
+#define MSR_IA32_MISC_ENABLE_TM2 (1ULL << 13)
+#define MSR_IA32_MISC_ENABLE_ADJ_PREF_DISABLE (1ULL << 19)
+#define MSR_IA32_MISC_ENABLE_SPEEDSTEP_LOCK (1ULL << 20)
+#define MSR_IA32_MISC_ENABLE_L1D_CONTEXT (1ULL << 24)
+#define MSR_IA32_MISC_ENABLE_DCU_PREF_DISABLE (1ULL << 37)
+#define MSR_IA32_MISC_ENABLE_TURBO_DISABLE (1ULL << 38)
+#define MSR_IA32_MISC_ENABLE_IP_PREF_DISABLE (1ULL << 39)
+
+/* P4/Xeon+ specific */
+#define MSR_IA32_MCG_EAX 0x00000180
+#define MSR_IA32_MCG_EBX 0x00000181
+#define MSR_IA32_MCG_ECX 0x00000182
+#define MSR_IA32_MCG_EDX 0x00000183
+#define MSR_IA32_MCG_ESI 0x00000184
+#define MSR_IA32_MCG_EDI 0x00000185
+#define MSR_IA32_MCG_EBP 0x00000186
+#define MSR_IA32_MCG_ESP 0x00000187
+#define MSR_IA32_MCG_EFLAGS 0x00000188
+#define MSR_IA32_MCG_EIP 0x00000189
+#define MSR_IA32_MCG_RESERVED 0x0000018a
+
+/* Pentium IV performance counter MSRs */
+#define MSR_P4_BPU_PERFCTR0 0x00000300
+#define MSR_P4_BPU_PERFCTR1 0x00000301
+#define MSR_P4_BPU_PERFCTR2 0x00000302
+#define MSR_P4_BPU_PERFCTR3 0x00000303
+#define MSR_P4_MS_PERFCTR0 0x00000304
+#define MSR_P4_MS_PERFCTR1 0x00000305
+#define MSR_P4_MS_PERFCTR2 0x00000306
+#define MSR_P4_MS_PERFCTR3 0x00000307
+#define MSR_P4_FLAME_PERFCTR0 0x00000308
+#define MSR_P4_FLAME_PERFCTR1 0x00000309
+#define MSR_P4_FLAME_PERFCTR2 0x0000030a
+#define MSR_P4_FLAME_PERFCTR3 0x0000030b
+#define MSR_P4_IQ_PERFCTR0 0x0000030c
+#define MSR_P4_IQ_PERFCTR1 0x0000030d
+#define MSR_P4_IQ_PERFCTR2 0x0000030e
+#define MSR_P4_IQ_PERFCTR3 0x0000030f
+#define MSR_P4_IQ_PERFCTR4 0x00000310
+#define MSR_P4_IQ_PERFCTR5 0x00000311
+#define MSR_P4_BPU_CCCR0 0x00000360
+#define MSR_P4_BPU_CCCR1 0x00000361
+#define MSR_P4_BPU_CCCR2 0x00000362
+#define MSR_P4_BPU_CCCR3 0x00000363
+#define MSR_P4_MS_CCCR0 0x00000364
+#define MSR_P4_MS_CCCR1 0x00000365
+#define MSR_P4_MS_CCCR2 0x00000366
+#define MSR_P4_MS_CCCR3 0x00000367
+#define MSR_P4_FLAME_CCCR0 0x00000368
+#define MSR_P4_FLAME_CCCR1 0x00000369
+#define MSR_P4_FLAME_CCCR2 0x0000036a
+#define MSR_P4_FLAME_CCCR3 0x0000036b
+#define MSR_P4_IQ_CCCR0 0x0000036c
+#define MSR_P4_IQ_CCCR1 0x0000036d
+#define MSR_P4_IQ_CCCR2 0x0000036e
+#define MSR_P4_IQ_CCCR3 0x0000036f
+#define MSR_P4_IQ_CCCR4 0x00000370
+#define MSR_P4_IQ_CCCR5 0x00000371
+#define MSR_P4_ALF_ESCR0 0x000003ca
+#define MSR_P4_ALF_ESCR1 0x000003cb
+#define MSR_P4_BPU_ESCR0 0x000003b2
+#define MSR_P4_BPU_ESCR1 0x000003b3
+#define MSR_P4_BSU_ESCR0 0x000003a0
+#define MSR_P4_BSU_ESCR1 0x000003a1
+#define MSR_P4_CRU_ESCR0 0x000003b8
+#define MSR_P4_CRU_ESCR1 0x000003b9
+#define MSR_P4_CRU_ESCR2 0x000003cc
+#define MSR_P4_CRU_ESCR3 0x000003cd
+#define MSR_P4_CRU_ESCR4 0x000003e0
+#define MSR_P4_CRU_ESCR5 0x000003e1
+#define MSR_P4_DAC_ESCR0 0x000003a8
+#define MSR_P4_DAC_ESCR1 0x000003a9
+#define MSR_P4_FIRM_ESCR0 0x000003a4
+#define MSR_P4_FIRM_ESCR1 0x000003a5
+#define MSR_P4_FLAME_ESCR0 0x000003a6
+#define MSR_P4_FLAME_ESCR1 0x000003a7
+#define MSR_P4_FSB_ESCR0 0x000003a2
+#define MSR_P4_FSB_ESCR1 0x000003a3
+#define MSR_P4_IQ_ESCR0 0x000003ba
+#define MSR_P4_IQ_ESCR1 0x000003bb
+#define MSR_P4_IS_ESCR0 0x000003b4
+#define MSR_P4_IS_ESCR1 0x000003b5
+#define MSR_P4_ITLB_ESCR0 0x000003b6
+#define MSR_P4_ITLB_ESCR1 0x000003b7
+#define MSR_P4_IX_ESCR0 0x000003c8
+#define MSR_P4_IX_ESCR1 0x000003c9
+#define MSR_P4_MOB_ESCR0 0x000003aa
+#define MSR_P4_MOB_ESCR1 0x000003ab
+#define MSR_P4_MS_ESCR0 0x000003c0
+#define MSR_P4_MS_ESCR1 0x000003c1
+#define MSR_P4_PMH_ESCR0 0x000003ac
+#define MSR_P4_PMH_ESCR1 0x000003ad
+#define MSR_P4_RAT_ESCR0 0x000003bc
+#define MSR_P4_RAT_ESCR1 0x000003bd
+#define MSR_P4_SAAT_ESCR0 0x000003ae
+#define MSR_P4_SAAT_ESCR1 0x000003af
+#define MSR_P4_SSU_ESCR0 0x000003be
+#define MSR_P4_SSU_ESCR1 0x000003bf /* guess: not in manual */
+
+#define MSR_P4_TBPU_ESCR0 0x000003c2
+#define MSR_P4_TBPU_ESCR1 0x000003c3
+#define MSR_P4_TC_ESCR0 0x000003c4
+#define MSR_P4_TC_ESCR1 0x000003c5
+#define MSR_P4_U2L_ESCR0 0x000003b0
+#define MSR_P4_U2L_ESCR1 0x000003b1
+
+#define MSR_P4_PEBS_MATRIX_VERT 0x000003f2
+
+/* Intel Core-based CPU performance counters */
+#define MSR_CORE_PERF_FIXED_CTR0 0x00000309
+#define MSR_CORE_PERF_FIXED_CTR1 0x0000030a
+#define MSR_CORE_PERF_FIXED_CTR2 0x0000030b
+#define MSR_CORE_PERF_FIXED_CTR_CTRL 0x0000038d
+#define MSR_CORE_PERF_GLOBAL_STATUS 0x0000038e
+#define MSR_CORE_PERF_GLOBAL_CTRL 0x0000038f
+#define MSR_CORE_PERF_GLOBAL_OVF_CTRL 0x00000390
+
+/* Geode defined MSRs */
+#define MSR_GEODE_BUSCONT_CONF0 0x00001900
+
+/* Intel VT MSRs */
+#define MSR_IA32_VMX_BASIC 0x00000480
+#define MSR_IA32_VMX_PINBASED_CTLS 0x00000481
+#define MSR_IA32_VMX_PROCBASED_CTLS 0x00000482
+#define MSR_IA32_VMX_EXIT_CTLS 0x00000483
+#define MSR_IA32_VMX_ENTRY_CTLS 0x00000484
+#define MSR_IA32_VMX_MISC 0x00000485
+#define MSR_IA32_VMX_CR0_FIXED0 0x00000486
+#define MSR_IA32_VMX_CR0_FIXED1 0x00000487
+#define MSR_IA32_VMX_CR4_FIXED0 0x00000488
+#define MSR_IA32_VMX_CR4_FIXED1 0x00000489
+#define MSR_IA32_VMX_VMCS_ENUM 0x0000048a
+#define MSR_IA32_VMX_PROCBASED_CTLS2 0x0000048b
+#define MSR_IA32_VMX_EPT_VPID_CAP 0x0000048c
+#define MSR_IA32_VMX_TRUE_PIN 0x0000048d
+#define MSR_IA32_VMX_TRUE_PROC 0x0000048e
+#define MSR_IA32_VMX_TRUE_EXIT 0x0000048f
+#define MSR_IA32_VMX_TRUE_ENTRY 0x00000490
+
+
+/* AMD-V MSRs */
+
+#define MSR_VM_CR 0xc0010114
+#define MSR_VM_IGNNE 0xc0010115
+#define MSR_VM_HSAVE_PA 0xc0010117
+
+#endif /* _ASM_X86_MSR_INDEX_H */
diff --git a/kvm-unittest/lib/x86/pci.h b/kvm-unittest/lib/x86/pci.h
new file mode 100644
index 0000000..0f69ef0
--- /dev/null
+++ b/kvm-unittest/lib/x86/pci.h
@@ -0,0 +1,16 @@
+#ifndef PCI_H
+#define PCI_H
+
+#include <inttypes.h>
+#include "libcflat.h"
+
+typedef uint16_t pcidevaddr_t;
+enum {
+ PCIDEVADDR_INVALID = 0x0
+};
+pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id);
+unsigned long pci_bar_addr(pcidevaddr_t dev, int bar_num);
+bool pci_bar_is_memory(pcidevaddr_t dev, int bar_num);
+bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num);
+
+#endif
diff --git a/kvm-unittest/lib/x86/processor.h b/kvm-unittest/lib/x86/processor.h
new file mode 100644
index 0000000..29811d4
--- /dev/null
+++ b/kvm-unittest/lib/x86/processor.h
@@ -0,0 +1,325 @@
+#ifndef LIBCFLAT_PROCESSOR_H
+#define LIBCFLAT_PROCESSOR_H
+
+#include "libcflat.h"
+#include <stdint.h>
+
+struct descriptor_table_ptr {
+ u16 limit;
+ ulong base;
+} __attribute__((packed));
+
+static inline void barrier(void)
+{
+ asm volatile ("" : : : "memory");
+}
+
+static inline u16 read_cs(void)
+{
+ unsigned val;
+
+ asm ("mov %%cs, %0" : "=mr"(val));
+ return val;
+}
+
+static inline u16 read_ds(void)
+{
+ unsigned val;
+
+ asm ("mov %%ds, %0" : "=mr"(val));
+ return val;
+}
+
+static inline u16 read_es(void)
+{
+ unsigned val;
+
+ asm ("mov %%es, %0" : "=mr"(val));
+ return val;
+}
+
+static inline u16 read_ss(void)
+{
+ unsigned val;
+
+ asm ("mov %%ss, %0" : "=mr"(val));
+ return val;
+}
+
+static inline u16 read_fs(void)
+{
+ unsigned val;
+
+ asm ("mov %%fs, %0" : "=mr"(val));
+ return val;
+}
+
+static inline u16 read_gs(void)
+{
+ unsigned val;
+
+ asm ("mov %%gs, %0" : "=mr"(val));
+ return val;
+}
+
+static inline unsigned long read_rflags(void)
+{
+ unsigned long f;
+ asm ("pushf; pop %0\n\t" : "=rm"(f));
+ return f;
+}
+
+static inline void write_ds(unsigned val)
+{
+ asm ("mov %0, %%ds" : : "rm"(val) : "memory");
+}
+
+static inline void write_es(unsigned val)
+{
+ asm ("mov %0, %%es" : : "rm"(val) : "memory");
+}
+
+static inline void write_ss(unsigned val)
+{
+ asm ("mov %0, %%ss" : : "rm"(val) : "memory");
+}
+
+static inline void write_fs(unsigned val)
+{
+ asm ("mov %0, %%fs" : : "rm"(val) : "memory");
+}
+
+static inline void write_gs(unsigned val)
+{
+ asm ("mov %0, %%gs" : : "rm"(val) : "memory");
+}
+
+static inline u64 rdmsr(u32 index)
+{
+ u32 a, d;
+ asm volatile ("rdmsr" : "=a"(a), "=d"(d) : "c"(index) : "memory");
+ return a | ((u64)d << 32);
+}
+
+static inline void wrmsr(u32 index, u64 val)
+{
+ u32 a = val, d = val >> 32;
+ asm volatile ("wrmsr" : : "a"(a), "d"(d), "c"(index) : "memory");
+}
+
+static inline uint64_t rdpmc(uint32_t index)
+{
+ uint32_t a, d;
+ asm volatile ("rdpmc" : "=a"(a), "=d"(d) : "c"(index));
+ return a | ((uint64_t)d << 32);
+}
+
+static inline void write_cr0(ulong val)
+{
+ asm volatile ("mov %0, %%cr0" : : "r"(val) : "memory");
+}
+
+static inline ulong read_cr0(void)
+{
+ ulong val;
+ asm volatile ("mov %%cr0, %0" : "=r"(val) : : "memory");
+ return val;
+}
+
+static inline void write_cr2(ulong val)
+{
+ asm volatile ("mov %0, %%cr2" : : "r"(val) : "memory");
+}
+
+static inline ulong read_cr2(void)
+{
+ ulong val;
+ asm volatile ("mov %%cr2, %0" : "=r"(val) : : "memory");
+ return val;
+}
+
+static inline void write_cr3(ulong val)
+{
+ asm volatile ("mov %0, %%cr3" : : "r"(val) : "memory");
+}
+
+static inline ulong read_cr3(void)
+{
+ ulong val;
+ asm volatile ("mov %%cr3, %0" : "=r"(val) : : "memory");
+ return val;
+}
+
+static inline void write_cr4(ulong val)
+{
+ asm volatile ("mov %0, %%cr4" : : "r"(val) : "memory");
+}
+
+static inline ulong read_cr4(void)
+{
+ ulong val;
+ asm volatile ("mov %%cr4, %0" : "=r"(val) : : "memory");
+ return val;
+}
+
+static inline void write_cr8(ulong val)
+{
+ asm volatile ("mov %0, %%cr8" : : "r"(val) : "memory");
+}
+
+static inline ulong read_cr8(void)
+{
+ ulong val;
+ asm volatile ("mov %%cr8, %0" : "=r"(val) : : "memory");
+ return val;
+}
+
+static inline void lgdt(const struct descriptor_table_ptr *ptr)
+{
+ asm volatile ("lgdt %0" : : "m"(*ptr));
+}
+
+static inline void sgdt(struct descriptor_table_ptr *ptr)
+{
+ asm volatile ("sgdt %0" : "=m"(*ptr));
+}
+
+static inline void lidt(const struct descriptor_table_ptr *ptr)
+{
+ asm volatile ("lidt %0" : : "m"(*ptr));
+}
+
+static inline void sidt(struct descriptor_table_ptr *ptr)
+{
+ asm volatile ("sidt %0" : "=m"(*ptr));
+}
+
+static inline void lldt(unsigned val)
+{
+ asm volatile ("lldt %0" : : "rm"(val));
+}
+
+static inline u16 sldt(void)
+{
+ u16 val;
+ asm volatile ("sldt %0" : "=rm"(val));
+ return val;
+}
+
+static inline void ltr(u16 val)
+{
+ asm volatile ("ltr %0" : : "rm"(val));
+}
+
+static inline u16 str(void)
+{
+ u16 val;
+ asm volatile ("str %0" : "=rm"(val));
+ return val;
+}
+
+static inline void write_dr6(ulong val)
+{
+ asm volatile ("mov %0, %%dr6" : : "r"(val) : "memory");
+}
+
+static inline ulong read_dr6(void)
+{
+ ulong val;
+ asm volatile ("mov %%dr6, %0" : "=r"(val));
+ return val;
+}
+
+static inline void write_dr7(ulong val)
+{
+ asm volatile ("mov %0, %%dr7" : : "r"(val) : "memory");
+}
+
+static inline ulong read_dr7(void)
+{
+ ulong val;
+ asm volatile ("mov %%dr7, %0" : "=r"(val));
+ return val;
+}
+
+struct cpuid { u32 a, b, c, d; };
+
+static inline struct cpuid cpuid_indexed(u32 function, u32 index)
+{
+ struct cpuid r;
+ asm volatile ("cpuid"
+ : "=a"(r.a), "=b"(r.b), "=c"(r.c), "=d"(r.d)
+ : "0"(function), "2"(index));
+ return r;
+}
+
+static inline struct cpuid cpuid(u32 function)
+{
+ return cpuid_indexed(function, 0);
+}
+
+static inline void pause(void)
+{
+ asm volatile ("pause");
+}
+
+static inline void cli(void)
+{
+ asm volatile ("cli");
+}
+
+static inline void sti(void)
+{
+ asm volatile ("sti");
+}
+
+static inline unsigned long long rdtsc()
+{
+ long long r;
+
+#ifdef __x86_64__
+ unsigned a, d;
+
+ asm volatile ("rdtsc" : "=a"(a), "=d"(d));
+ r = a | ((long long)d << 32);
+#else
+ asm volatile ("rdtsc" : "=A"(r));
+#endif
+ return r;
+}
+
+static inline void wrtsc(u64 tsc)
+{
+ unsigned a = tsc, d = tsc >> 32;
+
+ asm volatile("wrmsr" : : "a"(a), "d"(d), "c"(0x10));
+}
+
+static inline void irq_disable(void)
+{
+ asm volatile("cli");
+}
+
+static inline void irq_enable(void)
+{
+ asm volatile("sti");
+}
+
+static inline void invlpg(void *va)
+{
+ asm volatile("invlpg (%0)" ::"r" (va) : "memory");
+}
+
+static inline void safe_halt(void)
+{
+ asm volatile("sti; hlt");
+}
+
+#ifdef __x86_64__
+static inline void write_rflags(u64 r)
+{
+ asm volatile("push %0; popf\n\t" : : "q"(r) : "cc");
+}
+#endif
+
+#endif
diff --git a/kvm-unittest/lib/x86/smp.h b/kvm-unittest/lib/x86/smp.h
new file mode 100644
index 0000000..df5fdba
--- /dev/null
+++ b/kvm-unittest/lib/x86/smp.h
@@ -0,0 +1,21 @@
+#ifndef __SMP_H
+#define __SMP_H
+
+#define mb() asm volatile("mfence":::"memory")
+#define rmb() asm volatile("lfence":::"memory")
+#define wmb() asm volatile("sfence" ::: "memory")
+
+struct spinlock {
+ int v;
+};
+
+void smp_init(void);
+
+int cpu_count(void);
+int smp_id(void);
+void on_cpu(int cpu, void (*function)(void *data), void *data);
+void on_cpu_async(int cpu, void (*function)(void *data), void *data);
+void spin_lock(struct spinlock *lock);
+void spin_unlock(struct spinlock *lock);
+
+#endif
diff --git a/kvm-unittest/lib/x86/vm.h b/kvm-unittest/lib/x86/vm.h
new file mode 100644
index 0000000..eff6f72
--- /dev/null
+++ b/kvm-unittest/lib/x86/vm.h
@@ -0,0 +1,71 @@
+#ifndef VM_H
+#define VM_H
+
+#include "processor.h"
+
+#define PAGE_SIZE 4096ul
+#ifdef __x86_64__
+#define LARGE_PAGE_SIZE (512 * PAGE_SIZE)
+#else
+#define LARGE_PAGE_SIZE (1024 * PAGE_SIZE)
+#endif
+
+#define PTE_PRESENT (1ull << 0)
+#define PTE_PSE (1ull << 7)
+#define PTE_WRITE (1ull << 1)
+#define PTE_USER (1ull << 2)
+#define PTE_ADDR (0xffffffffff000ull)
+
+#define X86_CR0_PE 0x00000001
+#define X86_CR0_WP 0x00010000
+#define X86_CR0_PG 0x80000000
+#define X86_CR4_VMXE 0x00000001
+#define X86_CR4_PSE 0x00000010
+#define X86_CR4_PAE 0x00000020
+#define X86_CR4_PCIDE 0x00020000
+
+#ifdef __x86_64__
+#define SEL_NULL_DESC 0x0
+#define SEL_KERN_CODE_64 0x8
+#define SEL_KERN_DATA_64 0x10
+#define SEL_USER_CODE_64 0x18
+#define SEL_USER_DATA_64 0x20
+#define SEL_CODE_32 0x28
+#define SEL_DATA_32 0x30
+#define SEL_CODE_16 0x38
+#define SEL_DATA_16 0x40
+#define SEL_TSS_RUN 0x48
+#endif
+
+void setup_vm();
+
+void *vmalloc(unsigned long size);
+void vfree(void *mem);
+void *vmap(unsigned long long phys, unsigned long size);
+void *alloc_vpage(void);
+void *alloc_vpages(ulong nr);
+uint64_t virt_to_phys_cr3(void *mem);
+
+void install_pte(unsigned long *cr3,
+ int pte_level,
+ void *virt,
+ unsigned long pte,
+ unsigned long *pt_page);
+
+void *alloc_page();
+
+void install_large_page(unsigned long *cr3,unsigned long phys,
+ void *virt);
+void install_page(unsigned long *cr3, unsigned long phys, void *virt);
+
+static inline unsigned long virt_to_phys(const void *virt)
+{
+ return (unsigned long)virt;
+}
+
+static inline void *phys_to_virt(unsigned long phys)
+{
+ return (void *)phys;
+}
+
+#endif
diff --git a/kvm-unittest/x86/ioram.h b/kvm-unittest/x86/ioram.h
new file mode 100644
index 0000000..2938142
--- /dev/null
+++ b/kvm-unittest/x86/ioram.h
@@ -0,0 +1,7 @@
+#ifndef __IO_RAM_H
+#define __IO_RAM_H
+
+#define IORAM_BASE_PHYS 0xff000000UL
+#define IORAM_LEN 0x10000UL
+
+#endif
diff --git a/kvm-unittest/x86/kvmclock.h b/kvm-unittest/x86/kvmclock.h
new file mode 100644
index 0000000..166a338
--- /dev/null
+++ b/kvm-unittest/x86/kvmclock.h
@@ -0,0 +1,60 @@
+#ifndef KVMCLOCK_H
+#define KVMCLOCK_H
+
+#define MSR_KVM_WALL_CLOCK 0x11
+#define MSR_KVM_SYSTEM_TIME 0x12
+
+#define MAX_CPU 64
+
+#define PVCLOCK_TSC_STABLE_BIT (1 << 0)
+#define PVCLOCK_RAW_CYCLE_BIT (1 << 7) /* Get raw cycle */
+
+# define NSEC_PER_SEC 1000000000ULL
+
+typedef u64 cycle_t;
+
+struct pvclock_vcpu_time_info {
+ u32 version;
+ u32 pad0;
+ u64 tsc_timestamp;
+ u64 system_time;
+ u32 tsc_to_system_mul;
+ s8 tsc_shift;
+ u8 flags;
+ u8 pad[2];
+} __attribute__((__packed__)); /* 32 bytes */
+
+struct pvclock_wall_clock {
+ u32 version;
+ u32 sec;
+ u32 nsec;
+} __attribute__((__packed__));
+
+/*
+ * These are perodically updated
+ * xen: magic shared_info page
+ * kvm: gpa registered via msr
+ * and then copied here.
+ */
+struct pvclock_shadow_time {
+ u64 tsc_timestamp; /* TSC at last update of time vals. */
+ u64 system_timestamp; /* Time, in nanosecs, since boot. */
+ u32 tsc_to_nsec_mul;
+ int tsc_shift;
+ u32 version;
+ u8 flags;
+};
+
+
+struct timespec {
+ long tv_sec;
+ long tv_nsec;
+};
+
+void pvclock_set_flags(unsigned char flags);
+cycle_t kvm_clock_read();
+void kvm_get_wallclock(struct timespec *ts);
+void kvm_clock_init(void *data);
+void kvm_clock_clear(void *data);
+
+#endif
diff --git a/kvm-unittest/x86/print.h b/kvm-unittest/x86/print.h
new file mode 100644
index 0000000..d5bd2f9
--- /dev/null
+++ b/kvm-unittest/x86/print.h
@@ -0,0 +1,19 @@
+#ifndef PRINT_H
+#define PRINT_H
+
+.macro PRINT text
+
+.data
+
+333: .asciz "\text\n"
+
+.previous
+
+ push %rdi
+ lea 333b, %rdi
+ call print
+ pop %rdi
+
+.endm
+
+#endif
diff --git a/kvm-unittest/x86/svm.h b/kvm-unittest/x86/svm.h
new file mode 100644
index 0000000..3fdc0d3
--- /dev/null
+++ b/kvm-unittest/x86/svm.h
@@ -0,0 +1,328 @@
+#ifndef __SVM_H
+#define __SVM_H
+
+#include "libcflat.h"
+
+enum {
+ INTERCEPT_INTR,
+ INTERCEPT_NMI,
+ INTERCEPT_SMI,
+ INTERCEPT_INIT,
+ INTERCEPT_VINTR,
+ INTERCEPT_SELECTIVE_CR0,
+ INTERCEPT_STORE_IDTR,
+ INTERCEPT_STORE_GDTR,
+ INTERCEPT_STORE_LDTR,
+ INTERCEPT_STORE_TR,
+ INTERCEPT_LOAD_IDTR,
+ INTERCEPT_LOAD_GDTR,
+ INTERCEPT_LOAD_LDTR,
+ INTERCEPT_LOAD_TR,
+ INTERCEPT_RDTSC,
+ INTERCEPT_RDPMC,
+ INTERCEPT_PUSHF,
+ INTERCEPT_POPF,
+ INTERCEPT_CPUID,
+ INTERCEPT_RSM,
+ INTERCEPT_IRET,
+ INTERCEPT_INTn,
+ INTERCEPT_INVD,
+ INTERCEPT_PAUSE,
+ INTERCEPT_HLT,
+ INTERCEPT_INVLPG,
+ INTERCEPT_INVLPGA,
+ INTERCEPT_IOIO_PROT,
+ INTERCEPT_MSR_PROT,
+ INTERCEPT_TASK_SWITCH,
+ INTERCEPT_FERR_FREEZE,
+ INTERCEPT_SHUTDOWN,
+ INTERCEPT_VMRUN,
+ INTERCEPT_VMMCALL,
+ INTERCEPT_VMLOAD,
+ INTERCEPT_VMSAVE,
+ INTERCEPT_STGI,
+ INTERCEPT_CLGI,
+ INTERCEPT_SKINIT,
+ INTERCEPT_RDTSCP,
+ INTERCEPT_ICEBP,
+ INTERCEPT_WBINVD,
+ INTERCEPT_MONITOR,
+ INTERCEPT_MWAIT,
+ INTERCEPT_MWAIT_COND,
+};
+
+
+struct __attribute__ ((__packed__)) vmcb_control_area {
+ u16 intercept_cr_read;
+ u16 intercept_cr_write;
+ u16 intercept_dr_read;
+ u16 intercept_dr_write;
+ u32 intercept_exceptions;
+ u64 intercept;
+ u8 reserved_1[42];
+ u16 pause_filter_count;
+ u64 iopm_base_pa;
+ u64 msrpm_base_pa;
+ u64 tsc_offset;
+ u32 asid;
+ u8 tlb_ctl;
+ u8 reserved_2[3];
+ u32 int_ctl;
+ u32 int_vector;
+ u32 int_state;
+ u8 reserved_3[4];
+ u32 exit_code;
+ u32 exit_code_hi;
+ u64 exit_info_1;
+ u64 exit_info_2;
+ u32 exit_int_info;
+ u32 exit_int_info_err;
+ u64 nested_ctl;
+ u8 reserved_4[16];
+ u32 event_inj;
+ u32 event_inj_err;
+ u64 nested_cr3;
+ u64 lbr_ctl;
+ u64 reserved_5;
+ u64 next_rip;
+ u8 reserved_6[816];
+};
+
+
+#define TLB_CONTROL_DO_NOTHING 0
+#define TLB_CONTROL_FLUSH_ALL_ASID 1
+
+#define V_TPR_MASK 0x0f
+
+#define V_IRQ_SHIFT 8
+#define V_IRQ_MASK (1 << V_IRQ_SHIFT)
+
+#define V_INTR_PRIO_SHIFT 16
+#define V_INTR_PRIO_MASK (0x0f << V_INTR_PRIO_SHIFT)
+
+#define V_IGN_TPR_SHIFT 20
+#define V_IGN_TPR_MASK (1 << V_IGN_TPR_SHIFT)
+
+#define V_INTR_MASKING_SHIFT 24
+#define V_INTR_MASKING_MASK (1 << V_INTR_MASKING_SHIFT)
+
+#define SVM_INTERRUPT_SHADOW_MASK 1
+
+#define SVM_IOIO_STR_SHIFT 2
+#define SVM_IOIO_REP_SHIFT 3
+#define SVM_IOIO_SIZE_SHIFT 4
+#define SVM_IOIO_ASIZE_SHIFT 7
+
+#define SVM_IOIO_TYPE_MASK 1
+#define SVM_IOIO_STR_MASK (1 << SVM_IOIO_STR_SHIFT)
+#define SVM_IOIO_REP_MASK (1 << SVM_IOIO_REP_SHIFT)
+#define SVM_IOIO_SIZE_MASK (7 << SVM_IOIO_SIZE_SHIFT)
+#define SVM_IOIO_ASIZE_MASK (7 << SVM_IOIO_ASIZE_SHIFT)
+
+#define SVM_VM_CR_VALID_MASK 0x001fULL
+#define SVM_VM_CR_SVM_LOCK_MASK 0x0008ULL
+#define SVM_VM_CR_SVM_DIS_MASK 0x0010ULL
+
+struct __attribute__ ((__packed__)) vmcb_seg {
+ u16 selector;
+ u16 attrib;
+ u32 limit;
+ u64 base;
+};
+
+struct __attribute__ ((__packed__)) vmcb_save_area {
+ struct vmcb_seg es;
+ struct vmcb_seg cs;
+ struct vmcb_seg ss;
+ struct vmcb_seg ds;
+ struct vmcb_seg fs;
+ struct vmcb_seg gs;
+ struct vmcb_seg gdtr;
+ struct vmcb_seg ldtr;
+ struct vmcb_seg idtr;
+ struct vmcb_seg tr;
+ u8 reserved_1[43];
+ u8 cpl;
+ u8 reserved_2[4];
+ u64 efer;
+ u8 reserved_3[112];
+ u64 cr4;
+ u64 cr3;
+ u64 cr0;
+ u64 dr7;
+ u64 dr6;
+ u64 rflags;
+ u64 rip;
+ u8 reserved_4[88];
+ u64 rsp;
+ u8 reserved_5[24];
+ u64 rax;
+ u64 star;
+ u64 lstar;
+ u64 cstar;
+ u64 sfmask;
+ u64 kernel_gs_base;
+ u64 sysenter_cs;
+ u64 sysenter_esp;
+ u64 sysenter_eip;
+ u64 cr2;
+ u8 reserved_6[32];
+ u64 g_pat;
+ u64 dbgctl;
+ u64 br_from;
+ u64 br_to;
+ u64 last_excp_from;
+ u64 last_excp_to;
+};
+
+struct __attribute__ ((__packed__)) vmcb {
+ struct vmcb_control_area control;
+ struct vmcb_save_area save;
+};
+
+#define SVM_CPUID_FEATURE_SHIFT 2
+#define SVM_CPUID_FUNC 0x8000000a
+
+#define SVM_VM_CR_SVM_DISABLE 4
+
+#define SVM_SELECTOR_S_SHIFT 4
+#define SVM_SELECTOR_DPL_SHIFT 5
+#define SVM_SELECTOR_P_SHIFT 7
+#define SVM_SELECTOR_AVL_SHIFT 8
+#define SVM_SELECTOR_L_SHIFT 9
+#define SVM_SELECTOR_DB_SHIFT 10
+#define SVM_SELECTOR_G_SHIFT 11
+
+#define SVM_SELECTOR_TYPE_MASK (0xf)
+#define SVM_SELECTOR_S_MASK (1 << SVM_SELECTOR_S_SHIFT)
+#define SVM_SELECTOR_DPL_MASK (3 << SVM_SELECTOR_DPL_SHIFT)
+#define SVM_SELECTOR_P_MASK (1 << SVM_SELECTOR_P_SHIFT)
+#define SVM_SELECTOR_AVL_MASK (1 << SVM_SELECTOR_AVL_SHIFT)
+#define SVM_SELECTOR_L_MASK (1 << SVM_SELECTOR_L_SHIFT)
+#define SVM_SELECTOR_DB_MASK (1 << SVM_SELECTOR_DB_SHIFT)
+#define SVM_SELECTOR_G_MASK (1 << SVM_SELECTOR_G_SHIFT)
+
+#define SVM_SELECTOR_WRITE_MASK (1 << 1)
+#define SVM_SELECTOR_READ_MASK SVM_SELECTOR_WRITE_MASK
+#define SVM_SELECTOR_CODE_MASK (1 << 3)
+
+#define INTERCEPT_CR0_MASK 1
+#define INTERCEPT_CR3_MASK (1 << 3)
+#define INTERCEPT_CR4_MASK (1 << 4)
+#define INTERCEPT_CR8_MASK (1 << 8)
+
+#define INTERCEPT_DR0_MASK 1
+#define INTERCEPT_DR1_MASK (1 << 1)
+#define INTERCEPT_DR2_MASK (1 << 2)
+#define INTERCEPT_DR3_MASK (1 << 3)
+#define INTERCEPT_DR4_MASK (1 << 4)
+#define INTERCEPT_DR5_MASK (1 << 5)
+#define INTERCEPT_DR6_MASK (1 << 6)
+#define INTERCEPT_DR7_MASK (1 << 7)
+
+#define SVM_EVTINJ_VEC_MASK 0xff
+
+#define SVM_EVTINJ_TYPE_SHIFT 8
+#define SVM_EVTINJ_TYPE_MASK (7 << SVM_EVTINJ_TYPE_SHIFT)
+
+#define SVM_EVTINJ_TYPE_INTR (0 << SVM_EVTINJ_TYPE_SHIFT)
+#define SVM_EVTINJ_TYPE_NMI (2 << SVM_EVTINJ_TYPE_SHIFT)
+#define SVM_EVTINJ_TYPE_EXEPT (3 << SVM_EVTINJ_TYPE_SHIFT)
+#define SVM_EVTINJ_TYPE_SOFT (4 << SVM_EVTINJ_TYPE_SHIFT)
+
+#define SVM_EVTINJ_VALID (1 << 31)
+#define SVM_EVTINJ_VALID_ERR (1 << 11)
+
+#define SVM_EXITINTINFO_VEC_MASK SVM_EVTINJ_VEC_MASK
+#define SVM_EXITINTINFO_TYPE_MASK SVM_EVTINJ_TYPE_MASK
+
+#define SVM_EXITINTINFO_TYPE_INTR SVM_EVTINJ_TYPE_INTR
+#define SVM_EXITINTINFO_TYPE_NMI SVM_EVTINJ_TYPE_NMI
+#define SVM_EXITINTINFO_TYPE_EXEPT SVM_EVTINJ_TYPE_EXEPT
+#define SVM_EXITINTINFO_TYPE_SOFT SVM_EVTINJ_TYPE_SOFT
+
+#define SVM_EXITINTINFO_VALID SVM_EVTINJ_VALID
+#define SVM_EXITINTINFO_VALID_ERR SVM_EVTINJ_VALID_ERR
+
+#define SVM_EXITINFOSHIFT_TS_REASON_IRET 36
+#define SVM_EXITINFOSHIFT_TS_REASON_JMP 38
+#define SVM_EXITINFOSHIFT_TS_HAS_ERROR_CODE 44
+
+#define SVM_EXIT_READ_CR0 0x000
+#define SVM_EXIT_READ_CR3 0x003
+#define SVM_EXIT_READ_CR4 0x004
+#define SVM_EXIT_READ_CR8 0x008
+#define SVM_EXIT_WRITE_CR0 0x010
+#define SVM_EXIT_WRITE_CR3 0x013
+#define SVM_EXIT_WRITE_CR4 0x014
+#define SVM_EXIT_WRITE_CR8 0x018
+#define SVM_EXIT_READ_DR0 0x020
+#define SVM_EXIT_READ_DR1 0x021
+#define SVM_EXIT_READ_DR2 0x022
+#define SVM_EXIT_READ_DR3 0x023
+#define SVM_EXIT_READ_DR4 0x024
+#define SVM_EXIT_READ_DR5 0x025
+#define SVM_EXIT_READ_DR6 0x026
+#define SVM_EXIT_READ_DR7 0x027
+#define SVM_EXIT_WRITE_DR0 0x030
+#define SVM_EXIT_WRITE_DR1 0x031
+#define SVM_EXIT_WRITE_DR2 0x032
+#define SVM_EXIT_WRITE_DR3 0x033
+#define SVM_EXIT_WRITE_DR4 0x034
+#define SVM_EXIT_WRITE_DR5 0x035
+#define SVM_EXIT_WRITE_DR6 0x036
+#define SVM_EXIT_WRITE_DR7 0x037
+#define SVM_EXIT_EXCP_BASE 0x040
+#define SVM_EXIT_INTR 0x060
+#define SVM_EXIT_NMI 0x061
+#define SVM_EXIT_SMI 0x062
+#define SVM_EXIT_INIT 0x063
+#define SVM_EXIT_VINTR 0x064
+#define SVM_EXIT_CR0_SEL_WRITE 0x065
+#define SVM_EXIT_IDTR_READ 0x066
+#define SVM_EXIT_GDTR_READ 0x067
+#define SVM_EXIT_LDTR_READ 0x068
+#define SVM_EXIT_TR_READ 0x069
+#define SVM_EXIT_IDTR_WRITE 0x06a
+#define SVM_EXIT_GDTR_WRITE 0x06b
+#define SVM_EXIT_LDTR_WRITE 0x06c
+#define SVM_EXIT_TR_WRITE 0x06d
+#define SVM_EXIT_RDTSC 0x06e
+#define SVM_EXIT_RDPMC 0x06f
+#define SVM_EXIT_PUSHF 0x070
+#define SVM_EXIT_POPF 0x071
+#define SVM_EXIT_CPUID 0x072
+#define SVM_EXIT_RSM 0x073
+#define SVM_EXIT_IRET 0x074
+#define SVM_EXIT_SWINT 0x075
+#define SVM_EXIT_INVD 0x076
+#define SVM_EXIT_PAUSE 0x077
+#define SVM_EXIT_HLT 0x078
+#define SVM_EXIT_INVLPG 0x079
+#define SVM_EXIT_INVLPGA 0x07a
+#define SVM_EXIT_IOIO 0x07b
+#define SVM_EXIT_MSR 0x07c
+#define SVM_EXIT_TASK_SWITCH 0x07d
+#define SVM_EXIT_FERR_FREEZE 0x07e
+#define SVM_EXIT_SHUTDOWN 0x07f
+#define SVM_EXIT_VMRUN 0x080
+#define SVM_EXIT_VMMCALL 0x081
+#define SVM_EXIT_VMLOAD 0x082
+#define SVM_EXIT_VMSAVE 0x083
+#define SVM_EXIT_STGI 0x084
+#define SVM_EXIT_CLGI 0x085
+#define SVM_EXIT_SKINIT 0x086
+#define SVM_EXIT_RDTSCP 0x087
+#define SVM_EXIT_ICEBP 0x088
+#define SVM_EXIT_WBINVD 0x089
+#define SVM_EXIT_MONITOR 0x08a
+#define SVM_EXIT_MWAIT 0x08b
+#define SVM_EXIT_MWAIT_COND 0x08c
+#define SVM_EXIT_NPF 0x400
+
+#define SVM_EXIT_ERR -1
+
+#define SVM_CR0_SELECTIVE_MASK (X86_CR0_TS | X86_CR0_MP)
+
+#endif
+
diff --git a/kvm-unittest/x86/types.h b/kvm-unittest/x86/types.h
new file mode 100644
index 0000000..fd22743
--- /dev/null
+++ b/kvm-unittest/x86/types.h
@@ -0,0 +1,20 @@
+#ifndef __TYPES_H
+#define __TYPES_H
+
+#define DE_VECTOR 0
+#define DB_VECTOR 1
+#define BP_VECTOR 3
+#define OF_VECTOR 4
+#define BR_VECTOR 5
+#define UD_VECTOR 6
+#define NM_VECTOR 7
+#define DF_VECTOR 8
+#define TS_VECTOR 10
+#define NP_VECTOR 11
+#define SS_VECTOR 12
+#define GP_VECTOR 13
+#define PF_VECTOR 14
+#define MF_VECTOR 16
+#define MC_VECTOR 18
+
+#endif
diff --git a/kvm-unittest/x86/vmx.h b/kvm-unittest/x86/vmx.h
new file mode 100644
index 0000000..28595d8
--- /dev/null
+++ b/kvm-unittest/x86/vmx.h
@@ -0,0 +1,479 @@
+#ifndef __VMX_H
+#define __VMX_H
+
+#include "libcflat.h"
+
+struct vmcs {
+ u32 revision_id; /* vmcs revision identifier */
+ u32 abort; /* VMX-abort indicator */
+ /* VMCS data */
+ char data[0];
+};
+
+struct regs {
+ u64 rax;
+ u64 rcx;
+ u64 rdx;
+ u64 rbx;
+ u64 cr2;
+ u64 rbp;
+ u64 rsi;
+ u64 rdi;
+ u64 r8;
+ u64 r9;
+ u64 r10;
+ u64 r11;
+ u64 r12;
+ u64 r13;
+ u64 r14;
+ u64 r15;
+ u64 rflags;
+};
+
+struct vmx_test {
+ const char *name;
+ void (*init)(struct vmcs *vmcs);
+ void (*guest_main)();
+ int (*exit_handler)();
+ void (*syscall_handler)(u64 syscall_no);
+ struct regs guest_regs;
+ struct vmcs *vmcs;
+ int exits;
+};
+
+union vmx_basic {
+ u64 val;
+ struct {
+ u32 revision;
+ u32 size:13,
+ : 3,
+ width:1,
+ dual:1,
+ type:4,
+ insouts:1,
+ ctrl:1;
+ };
+};
+
+union vmx_ctrl_pin {
+ u64 val;
+ struct {
+ u32 set, clr;
+ };
+};
+
+union vmx_ctrl_cpu {
+ u64 val;
+ struct {
+ u32 set, clr;
+ };
+};
+
+union vmx_ctrl_exit {
+ u64 val;
+ struct {
+ u32 set, clr;
+ };
+};
+
+union vmx_ctrl_ent {
+ u64 val;
+ struct {
+ u32 set, clr;
+ };
+};
+
+union vmx_ept_vpid {
+ u64 val;
+ struct {
+ u32:16,
+ super:2,
+ : 2,
+ invept:1,
+ : 11;
+ u32 invvpid:1;
+ };
+};
+
+struct descr {
+ u16 limit;
+ u64 addr;
+};
+
+enum Encoding {
+ /* 16-Bit Control Fields */
+ VPID = 0x0000ul,
+ /* Posted-interrupt notification vector */
+ PINV = 0x0002ul,
+ /* EPTP index */
+ EPTP_IDX = 0x0004ul,
+
+ /* 16-Bit Guest State Fields */
+ GUEST_SEL_ES = 0x0800ul,
+ GUEST_SEL_CS = 0x0802ul,
+ GUEST_SEL_SS = 0x0804ul,
+ GUEST_SEL_DS = 0x0806ul,
+ GUEST_SEL_FS = 0x0808ul,
+ GUEST_SEL_GS = 0x080aul,
+ GUEST_SEL_LDTR = 0x080cul,
+ GUEST_SEL_TR = 0x080eul,
+ GUEST_INT_STATUS = 0x0810ul,
+
+ /* 16-Bit Host State Fields */
+ HOST_SEL_ES = 0x0c00ul,
+ HOST_SEL_CS = 0x0c02ul,
+ HOST_SEL_SS = 0x0c04ul,
+ HOST_SEL_DS = 0x0c06ul,
+ HOST_SEL_FS = 0x0c08ul,
+ HOST_SEL_GS = 0x0c0aul,
+ HOST_SEL_TR = 0x0c0cul,
+
+ /* 64-Bit Control Fields */
+ IO_BITMAP_A = 0x2000ul,
+ IO_BITMAP_B = 0x2002ul,
+ MSR_BITMAP = 0x2004ul,
+ EXIT_MSR_ST_ADDR = 0x2006ul,
+ EXIT_MSR_LD_ADDR = 0x2008ul,
+ ENTER_MSR_LD_ADDR = 0x200aul,
+ VMCS_EXEC_PTR = 0x200cul,
+ TSC_OFFSET = 0x2010ul,
+ TSC_OFFSET_HI = 0x2011ul,
+ APIC_VIRT_ADDR = 0x2012ul,
+ APIC_ACCS_ADDR = 0x2014ul,
+ EPTP = 0x201aul,
+ EPTP_HI = 0x201bul,
+
+ /* 64-Bit Readonly Data Field */
+ INFO_PHYS_ADDR = 0x2400ul,
+
+ /* 64-Bit Guest State */
+ VMCS_LINK_PTR = 0x2800ul,
+ VMCS_LINK_PTR_HI = 0x2801ul,
+ GUEST_DEBUGCTL = 0x2802ul,
+ GUEST_DEBUGCTL_HI = 0x2803ul,
+ GUEST_EFER = 0x2806ul,
+ GUEST_PERF_GLOBAL_CTRL = 0x2808ul,
+ GUEST_PDPTE = 0x280aul,
+
+ /* 64-Bit Host State */
+ HOST_EFER = 0x2c02ul,
+ HOST_PERF_GLOBAL_CTRL = 0x2c04ul,
+
+ /* 32-Bit Control Fields */
+ PIN_CONTROLS = 0x4000ul,
+ CPU_EXEC_CTRL0 = 0x4002ul,
+ EXC_BITMAP = 0x4004ul,
+ PF_ERROR_MASK = 0x4006ul,
+ PF_ERROR_MATCH = 0x4008ul,
+ CR3_TARGET_COUNT = 0x400aul,
+ EXI_CONTROLS = 0x400cul,
+ EXI_MSR_ST_CNT = 0x400eul,
+ EXI_MSR_LD_CNT = 0x4010ul,
+ ENT_CONTROLS = 0x4012ul,
+ ENT_MSR_LD_CNT = 0x4014ul,
+ ENT_INTR_INFO = 0x4016ul,
+ ENT_INTR_ERROR = 0x4018ul,
+ ENT_INST_LEN = 0x401aul,
+ TPR_THRESHOLD = 0x401cul,
+ CPU_EXEC_CTRL1 = 0x401eul,
+
+ /* 32-Bit R/O Data Fields */
+ VMX_INST_ERROR = 0x4400ul,
+ EXI_REASON = 0x4402ul,
+ EXI_INTR_INFO = 0x4404ul,
+ EXI_INTR_ERROR = 0x4406ul,
+ IDT_VECT_INFO = 0x4408ul,
+ IDT_VECT_ERROR = 0x440aul,
+ EXI_INST_LEN = 0x440cul,
+ EXI_INST_INFO = 0x440eul,
+
+ /* 32-Bit Guest State Fields */
+ GUEST_LIMIT_ES = 0x4800ul,
+ GUEST_LIMIT_CS = 0x4802ul,
+ GUEST_LIMIT_SS = 0x4804ul,
+ GUEST_LIMIT_DS = 0x4806ul,
+ GUEST_LIMIT_FS = 0x4808ul,
+ GUEST_LIMIT_GS = 0x480aul,
+ GUEST_LIMIT_LDTR = 0x480cul,
+ GUEST_LIMIT_TR = 0x480eul,
+ GUEST_LIMIT_GDTR = 0x4810ul,
+ GUEST_LIMIT_IDTR = 0x4812ul,
+ GUEST_AR_ES = 0x4814ul,
+ GUEST_AR_CS = 0x4816ul,
+ GUEST_AR_SS = 0x4818ul,
+ GUEST_AR_DS = 0x481aul,
+ GUEST_AR_FS = 0x481cul,
+ GUEST_AR_GS = 0x481eul,
+ GUEST_AR_LDTR = 0x4820ul,
+ GUEST_AR_TR = 0x4822ul,
+ GUEST_INTR_STATE = 0x4824ul,
+ GUEST_ACTV_STATE = 0x4826ul,
+ GUEST_SMBASE = 0x4828ul,
+ GUEST_SYSENTER_CS = 0x482aul,
+
+ /* 32-Bit Host State Fields */
+ HOST_SYSENTER_CS = 0x4c00ul,
+
+ /* Natural-Width Control Fields */
+ CR0_MASK = 0x6000ul,
+ CR4_MASK = 0x6002ul,
+ CR0_READ_SHADOW = 0x6004ul,
+ CR4_READ_SHADOW = 0x6006ul,
+ CR3_TARGET_0 = 0x6008ul,
+ CR3_TARGET_1 = 0x600aul,
+ CR3_TARGET_2 = 0x600cul,
+ CR3_TARGET_3 = 0x600eul,
+
+ /* Natural-Width R/O Data Fields */
+ EXI_QUALIFICATION = 0x6400ul,
+ IO_RCX = 0x6402ul,
+ IO_RSI = 0x6404ul,
+ IO_RDI = 0x6406ul,
+ IO_RIP = 0x6408ul,
+ GUEST_LINEAR_ADDRESS = 0x640aul,
+
+ /* Natural-Width Guest State Fields */
+ GUEST_CR0 = 0x6800ul,
+ GUEST_CR3 = 0x6802ul,
+ GUEST_CR4 = 0x6804ul,
+ GUEST_BASE_ES = 0x6806ul,
+ GUEST_BASE_CS = 0x6808ul,
+ GUEST_BASE_SS = 0x680aul,
+ GUEST_BASE_DS = 0x680cul,
+ GUEST_BASE_FS = 0x680eul,
+ GUEST_BASE_GS = 0x6810ul,
+ GUEST_BASE_LDTR = 0x6812ul,
+ GUEST_BASE_TR = 0x6814ul,
+ GUEST_BASE_GDTR = 0x6816ul,
+ GUEST_BASE_IDTR = 0x6818ul,
+ GUEST_DR7 = 0x681aul,
+ GUEST_RSP = 0x681cul,
+ GUEST_RIP = 0x681eul,
+ GUEST_RFLAGS = 0x6820ul,
+ GUEST_PENDING_DEBUG = 0x6822ul,
+ GUEST_SYSENTER_ESP = 0x6824ul,
+ GUEST_SYSENTER_EIP = 0x6826ul,
+
+ /* Natural-Width Host State Fields */
+ HOST_CR0 = 0x6c00ul,
+ HOST_CR3 = 0x6c02ul,
+ HOST_CR4 = 0x6c04ul,
+ HOST_BASE_FS = 0x6c06ul,
+ HOST_BASE_GS = 0x6c08ul,
+ HOST_BASE_TR = 0x6c0aul,
+ HOST_BASE_GDTR = 0x6c0cul,
+ HOST_BASE_IDTR = 0x6c0eul,
+ HOST_SYSENTER_ESP = 0x6c10ul,
+ HOST_SYSENTER_EIP = 0x6c12ul,
+ HOST_RSP = 0x6c14ul,
+ HOST_RIP = 0x6c16ul
+};
+
+enum Reason {
+ VMX_EXC_NMI = 0,
+ VMX_EXTINT = 1,
+ VMX_TRIPLE_FAULT = 2,
+ VMX_INIT = 3,
+ VMX_SIPI = 4,
+ VMX_SMI_IO = 5,
+ VMX_SMI_OTHER = 6,
+ VMX_INTR_WINDOW = 7,
+ VMX_NMI_WINDOW = 8,
+ VMX_TASK_SWITCH = 9,
+ VMX_CPUID = 10,
+ VMX_GETSEC = 11,
+ VMX_HLT = 12,
+ VMX_INVD = 13,
+ VMX_INVLPG = 14,
+ VMX_RDPMC = 15,
+ VMX_RDTSC = 16,
+ VMX_RSM = 17,
+ VMX_VMCALL = 18,
+ VMX_VMCLEAR = 19,
+ VMX_VMLAUNCH = 20,
+ VMX_VMPTRLD = 21,
+ VMX_VMPTRST = 22,
+ VMX_VMREAD = 23,
+ VMX_VMRESUME = 24,
+ VMX_VMWRITE = 25,
+ VMX_VMXOFF = 26,
+ VMX_VMXON = 27,
+ VMX_CR = 28,
+ VMX_DR = 29,
+ VMX_IO = 30,
+ VMX_RDMSR = 31,
+ VMX_WRMSR = 32,
+ VMX_FAIL_STATE = 33,
+ VMX_FAIL_MSR = 34,
+ VMX_MWAIT = 36,
+ VMX_MTF = 37,
+ VMX_MONITOR = 39,
+ VMX_PAUSE = 40,
+ VMX_FAIL_MCHECK = 41,
+ VMX_TPR_THRESHOLD = 43,
+ VMX_APIC_ACCESS = 44,
+ VMX_GDTR_IDTR = 46,
+ VMX_LDTR_TR = 47,
+ VMX_EPT_VIOLATION = 48,
+ VMX_EPT_MISCONFIG = 49,
+ VMX_INVEPT = 50,
+ VMX_PREEMPT = 52,
+ VMX_INVVPID = 53,
+ VMX_WBINVD = 54,
+ VMX_XSETBV = 55
+};
+
+#define X86_EFLAGS_CF 0x00000001 /* Carry Flag */
+#define X86_EFLAGS_ZF 0x00000040 /* Zero Flag */
+
+enum Ctrl_exi {
+ EXI_HOST_64 = 1UL << 9,
+ EXI_LOAD_PERF = 1UL << 12,
+ EXI_INTA = 1UL << 15,
+ EXI_LOAD_EFER = 1UL << 21,
+};
+
+enum Ctrl_ent {
+ ENT_GUEST_64 = 1UL << 9,
+ ENT_LOAD_EFER = 1UL << 15,
+};
+
+enum Ctrl_pin {
+ PIN_EXTINT = 1ul << 0,
+ PIN_NMI = 1ul << 3,
+ PIN_VIRT_NMI = 1ul << 5,
+};
+
+enum Ctrl0 {
+ CPU_INTR_WINDOW = 1ul << 2,
+ CPU_HLT = 1ul << 7,
+ CPU_INVLPG = 1ul << 9,
+ CPU_CR3_LOAD = 1ul << 15,
+ CPU_CR3_STORE = 1ul << 16,
+ CPU_TPR_SHADOW = 1ul << 21,
+ CPU_NMI_WINDOW = 1ul << 22,
+ CPU_IO = 1ul << 24,
+ CPU_IO_BITMAP = 1ul << 25,
+ CPU_SECONDARY = 1ul << 31,
+};
+
+enum Ctrl1 {
+ CPU_EPT = 1ul << 1,
+ CPU_VPID = 1ul << 5,
+ CPU_URG = 1ul << 7,
+};
+
+#define SAVE_GPR \
+ "xchg %rax, regs\n\t" \
+ "xchg %rbx, regs+0x8\n\t" \
+ "xchg %rcx, regs+0x10\n\t" \
+ "xchg %rdx, regs+0x18\n\t" \
+ "xchg %rbp, regs+0x28\n\t" \
+ "xchg %rsi, regs+0x30\n\t" \
+ "xchg %rdi, regs+0x38\n\t" \
+ "xchg %r8, regs+0x40\n\t" \
+ "xchg %r9, regs+0x48\n\t" \
+ "xchg %r10, regs+0x50\n\t" \
+ "xchg %r11, regs+0x58\n\t" \
+ "xchg %r12, regs+0x60\n\t" \
+ "xchg %r13, regs+0x68\n\t" \
+ "xchg %r14, regs+0x70\n\t" \
+ "xchg %r15, regs+0x78\n\t"
+
+#define LOAD_GPR SAVE_GPR
+
+#define SAVE_GPR_C \
+ "xchg %%rax, regs\n\t" \
+ "xchg %%rbx, regs+0x8\n\t" \
+ "xchg %%rcx, regs+0x10\n\t" \
+ "xchg %%rdx, regs+0x18\n\t" \
+ "xchg %%rbp, regs+0x28\n\t" \
+ "xchg %%rsi, regs+0x30\n\t" \
+ "xchg %%rdi, regs+0x38\n\t" \
+ "xchg %%r8, regs+0x40\n\t" \
+ "xchg %%r9, regs+0x48\n\t" \
+ "xchg %%r10, regs+0x50\n\t" \
+ "xchg %%r11, regs+0x58\n\t" \
+ "xchg %%r12, regs+0x60\n\t" \
+ "xchg %%r13, regs+0x68\n\t" \
+ "xchg %%r14, regs+0x70\n\t" \
+ "xchg %%r15, regs+0x78\n\t"
+
+#define LOAD_GPR_C SAVE_GPR_C
+
+#define SAVE_RFLAGS \
+ "pushf\n\t" \
+ "pop host_rflags\n\t"
+
+#define LOAD_RFLAGS \
+ "push host_rflags\n\t" \
+ "popf\n\t"
+
+#define VMX_IO_SIZE_MASK 0x7
+#define _VMX_IO_BYTE 1
+#define _VMX_IO_WORD 2
+#define _VMX_IO_LONG 3
+#define VMX_IO_DIRECTION_MASK (1ul << 3)
+#define VMX_IO_IN (1ul << 3)
+#define VMX_IO_OUT 0
+#define VMX_IO_STRING (1ul << 4)
+#define VMX_IO_REP (1ul << 5)
+#define VMX_IO_OPRAND_DX (1ul << 6)
+#define VMX_IO_PORT_MASK 0xFFFF0000
+#define VMX_IO_PORT_SHIFT 16
+
+#define VMX_TEST_VMEXIT 1
+#define VMX_TEST_EXIT 2
+#define VMX_TEST_RESUME 3
+#define VMX_TEST_LAUNCH_ERR 4
+#define VMX_TEST_RESUME_ERR 5
+
+#define HYPERCALL_BIT (1ul << 12)
+#define HYPERCALL_MASK 0xFFF
+#define HYPERCALL_VMEXIT 0x1
+
+
+extern struct regs regs;
+
+extern union vmx_basic basic;
+extern union vmx_ctrl_pin ctrl_pin_rev;
+extern union vmx_ctrl_cpu ctrl_cpu_rev[2];
+extern union vmx_ctrl_exit ctrl_exit_rev;
+extern union vmx_ctrl_ent ctrl_enter_rev;
+extern union vmx_ept_vpid ept_vpid;
+
+static inline int vmcs_clear(struct vmcs *vmcs)
+{
+ bool ret;
+ asm volatile ("vmclear %1; setbe %0" : "=q" (ret) : "m" (vmcs) : "cc");
+ return ret;
+}
+
+static inline u64 vmcs_read(enum Encoding enc)
+{
+ u64 val;
+ asm volatile ("vmread %1, %0" : "=rm" (val) : "r" ((u64)enc) : "cc");
+ return val;
+}
+
+static inline int vmcs_write(enum Encoding enc, u64 val)
+{
+ bool ret;
+ asm volatile ("vmwrite %1, %2; setbe %0"
+ : "=q"(ret) : "rm" (val), "r" ((u64)enc) : "cc");
+ return ret;
+}
+
+static inline int vmcs_save(struct vmcs **vmcs)
+{
+ bool ret;
+
+ asm volatile ("vmptrst %1; setbe %0" : "=q" (ret) : "m" (*vmcs) : "cc");
+ return ret;
+}
+
+void report(const char *name, int result);
+void print_vmexit_info();
+
+#endif
+
diff --git a/kvm-unittest/iotable.c b/kvm-unittest/iotable.c
new file mode 100644
index 0000000..91a5016
--- /dev/null
+++ b/kvm-unittest/iotable.c
@@ -0,0 +1,53 @@
+/*
+ * Kernel-based Virtual Machine test driver
+ *
+ * This test driver provides a simple way of testing kvm, without a full
+ * device model.
+ *
+ * Copyright (C) 2006 Qumranet
+ *
+ * Authors:
+ *
+ * Avi Kivity <address@hidden>
+ * Yaniv Kamay <address@hidden>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "iotable.h"
+
+struct io_table_entry *io_table_lookup(struct io_table *io_table, uint64_t
addr)
+{
+ int i;
+
+ for (i = 0; i < io_table->nr_entries; i++) {
+ if (io_table->entries[i].start <= addr &&
+ addr < io_table->entries[i].end)
+ return &io_table->entries[i];
+ }
+
+ return NULL;
+}
+
+int io_table_register(struct io_table *io_table, uint64_t start, uint64_t size,
+ io_table_handler_t *handler, void *opaque)
+{
+ struct io_table_entry *entry;
+
+ if (io_table->nr_entries == MAX_IO_TABLE)
+ return -ENOSPC;
+
+ entry = &io_table->entries[io_table->nr_entries];
+ io_table->nr_entries++;
+
+ entry->start = start;
+ entry->end = start + size;
+ entry->handler = handler;
+ entry->opaque = opaque;
+
+ return 0;
+}
diff --git a/kvm-unittest/kvmtrace.c b/kvm-unittest/kvmtrace.c
new file mode 100644
index 0000000..de3c189
--- /dev/null
+++ b/kvm-unittest/kvmtrace.c
@@ -0,0 +1,706 @@
+/*
+ * kvm tracing application
+ *
+ * This tool is used for collecting trace buffer data
+ * for kvm trace.
+ *
+ * Based on blktrace 0.99.3
+ *
+ * Copyright (C) 2005 Jens Axboe <address@hidden>
+ * Copyright (C) 2006 Jens Axboe <address@hidden>
+ * Copyright (C) 2008 Eric Liu <address@hidden>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#define _GNU_SOURCE
+
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/statfs.h>
+#include <sys/poll.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sched.h>
+
+#ifndef __user
+#define __user
+#endif
+#include <linux/kvm.h>
+
+static char kvmtrace_version[] = "0.1";
+
+/*
+ * You may want to increase this even more, if you are logging at a high
+ * rate and see skipped/missed events
+ */
+#define BUF_SIZE (512 * 1024)
+#define BUF_NR (8)
+
+#define OFILE_BUF (128 * 1024)
+
+#define DEBUGFS_TYPE 0x64626720
+
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+#define S_OPTS "r:o:w:?Vb:n:D:"
+static struct option l_opts[] = {
+ {
+ .name = "relay",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'r'
+ },
+ {
+ .name = "output",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'o'
+ },
+ {
+ .name = "stopwatch",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'w'
+ },
+ {
+ .name = "version",
+ .has_arg = no_argument,
+ .flag = NULL,
+ .val = 'V'
+ },
+ {
+ .name = "buffer-size",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'b'
+ },
+ {
+ .name = "num-sub-buffers",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'n'
+ },
+ {
+ .name = "output-dir",
+ .has_arg = required_argument,
+ .flag = NULL,
+ .val = 'D'
+ },
+ {
+ .name = NULL,
+ }
+};
+
+struct thread_information {
+ int cpu;
+ pthread_t thread;
+
+ int fd;
+ char fn[MAXPATHLEN + 64];
+
+ FILE *ofile;
+ char *ofile_buffer;
+
+ int (*get_subbuf)(struct thread_information *, unsigned int);
+ int (*read_data)(struct thread_information *, void *, unsigned int);
+
+ unsigned long long data_read;
+
+ struct kvm_trace_information *trace_info;
+
+ int exited;
+
+ /*
+ * mmap controlled output files
+ */
+ unsigned long long fs_size;
+ unsigned long long fs_max_size;
+ unsigned long fs_off;
+ void *fs_buf;
+ unsigned long fs_buf_len;
+
+};
+
+struct kvm_trace_information {
+ int fd;
+ volatile int trace_started;
+ unsigned long lost_records;
+ struct thread_information *threads;
+ unsigned long buf_size;
+ unsigned long buf_nr;
+};
+
+static struct kvm_trace_information trace_information;
+
+static int ncpus;
+static char default_debugfs_path[] = "/sys/kernel/debug";
+
+/* command line option globals */
+static char *debugfs_path;
+static char *output_name;
+static char *output_dir;
+static int stop_watch;
+static unsigned long buf_size = BUF_SIZE;
+static unsigned long buf_nr = BUF_NR;
+static unsigned int page_size;
+
+#define for_each_cpu_online(cpu) \
+ for (cpu = 0; cpu < ncpus; cpu++)
+#define for_each_tip(tip, i) \
+ for (i = 0, tip = trace_information.threads; i < ncpus; i++, tip++)
+
+#define is_done() (*(volatile int *)(&done))
+static volatile int done;
+
+#define is_trace_stopped() (*(volatile int *)(&trace_stopped))
+static volatile int trace_stopped;
+
+static void exit_trace(int status);
+
+static void handle_sigint(__attribute__((__unused__)) int sig)
+{
+ ioctl(trace_information.fd, KVM_TRACE_PAUSE);
+ done = 1;
+}
+
+static int get_lost_records()
+{
+ int fd;
+ char tmp[MAXPATHLEN + 64];
+
+ snprintf(tmp, sizeof(tmp), "%s/kvm/lost_records", debugfs_path);
+ fd = open(tmp, O_RDONLY);
+ if (fd < 0) {
+ /*
+ * this may be ok, if the kernel doesn't support dropped counts
+ */
+ if (errno == ENOENT)
+ return 0;
+
+ fprintf(stderr, "Couldn't open dropped file %s\n", tmp);
+ return -1;
+ }
+
+ if (read(fd, tmp, sizeof(tmp)) < 0) {
+ perror(tmp);
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ return atoi(tmp);
+}
+
+static void wait_for_data(struct thread_information *tip, int timeout)
+{
+ struct pollfd pfd = { .fd = tip->fd, .events = POLLIN };
+
+ while (!is_done()) {
+ if (poll(&pfd, 1, timeout) < 0) {
+ perror("poll");
+ break;
+ }
+ if (pfd.revents & POLLIN)
+ break;
+ }
+}
+
+static int read_data(struct thread_information *tip, void *buf,
+ unsigned int len)
+{
+ int ret = 0;
+
+ do {
+ wait_for_data(tip, 100);
+
+ ret = read(tip->fd, buf, len);
+
+ if (!ret)
+ continue;
+ else if (ret > 0)
+ return ret;
+ else {
+ if (errno != EAGAIN) {
+ perror(tip->fn);
+ fprintf(stderr, "Thread %d failed read of %s\n",
+ tip->cpu, tip->fn);
+ break;
+ }
+ continue;
+ }
+ } while (!is_done());
+
+ return ret;
+
+}
+
+/*
+ * For file output, truncate and mmap the file appropriately
+ */
+static int mmap_subbuf(struct thread_information *tip, unsigned int maxlen)
+{
+ int ofd = fileno(tip->ofile);
+ int ret;
+ unsigned long nr;
+ unsigned long size;
+
+ /*
+ * extend file, if we have to. use chunks of 16 subbuffers.
+ */
+ if (tip->fs_off + maxlen > tip->fs_buf_len) {
+ if (tip->fs_buf) {
+ munlock(tip->fs_buf, tip->fs_buf_len);
+ munmap(tip->fs_buf, tip->fs_buf_len);
+ tip->fs_buf = NULL;
+ }
+
+ tip->fs_off = tip->fs_size & (page_size - 1);
+ nr = max(16, tip->trace_info->buf_nr);
+ size = tip->trace_info->buf_size;
+ tip->fs_buf_len = (nr * size) - tip->fs_off;
+ tip->fs_max_size += tip->fs_buf_len;
+
+ if (ftruncate(ofd, tip->fs_max_size) < 0) {
+ perror("ftruncate");
+ return -1;
+ }
+
+ tip->fs_buf = mmap(NULL, tip->fs_buf_len, PROT_WRITE,
+ MAP_SHARED, ofd, tip->fs_size - tip->fs_off);
+ if (tip->fs_buf == MAP_FAILED) {
+ perror("mmap");
+ return -1;
+ }
+ mlock(tip->fs_buf, tip->fs_buf_len);
+ }
+
+ ret = tip->read_data(tip, tip->fs_buf + tip->fs_off, maxlen);
+ if (ret >= 0) {
+ tip->data_read += ret;
+ tip->fs_size += ret;
+ tip->fs_off += ret;
+ return 0;
+ }
+
+ return -1;
+}
+
+static void tip_ftrunc_final(struct thread_information *tip)
+{
+ /*
+ * truncate to right size and cleanup mmap
+ */
+ if (tip->ofile) {
+ int ofd = fileno(tip->ofile);
+
+ if (tip->fs_buf)
+ munmap(tip->fs_buf, tip->fs_buf_len);
+
+ ftruncate(ofd, tip->fs_size);
+ }
+}
+
+static void *thread_main(void *arg)
+{
+ struct thread_information *tip = arg;
+ pid_t pid = getpid();
+ cpu_set_t cpu_mask;
+
+ CPU_ZERO(&cpu_mask);
+ CPU_SET((tip->cpu), &cpu_mask);
+
+ if (sched_setaffinity(pid, sizeof(cpu_mask), &cpu_mask) == -1) {
+ perror("sched_setaffinity");
+ exit_trace(1);
+ }
+
+ snprintf(tip->fn, sizeof(tip->fn), "%s/kvm/trace%d",
+ debugfs_path, tip->cpu);
+ tip->fd = open(tip->fn, O_RDONLY);
+ if (tip->fd < 0) {
+ perror(tip->fn);
+ fprintf(stderr, "Thread %d failed open of %s\n", tip->cpu,
+ tip->fn);
+ exit_trace(1);
+ }
+ while (!is_done()) {
+ if (tip->get_subbuf(tip, tip->trace_info->buf_size) < 0)
+ break;
+ }
+
+ /*
+ * trace is stopped, pull data until we get a short read
+ */
+ while (tip->get_subbuf(tip, tip->trace_info->buf_size) > 0)
+ ;
+
+ tip_ftrunc_final(tip);
+ tip->exited = 1;
+ return NULL;
+}
+
+static int fill_ofname(struct thread_information *tip, char *dst)
+{
+ struct stat sb;
+ int len = 0;
+
+ if (output_dir)
+ len = sprintf(dst, "%s/", output_dir);
+ else
+ len = sprintf(dst, "./");
+
+ if (stat(dst, &sb) < 0) {
+ if (errno != ENOENT) {
+ perror("stat");
+ return 1;
+ }
+ if (mkdir(dst, 0755) < 0) {
+ perror(dst);
+ fprintf(stderr, "Can't make output dir\n");
+ return 1;
+ }
+ }
+
+ sprintf(dst + len, "%s.kvmtrace.%d", output_name, tip->cpu);
+
+ return 0;
+}
+
+static void fill_ops(struct thread_information *tip)
+{
+ tip->get_subbuf = mmap_subbuf;
+ tip->read_data = read_data;
+}
+
+static void close_thread(struct thread_information *tip)
+{
+ if (tip->fd != -1)
+ close(tip->fd);
+ if (tip->ofile)
+ fclose(tip->ofile);
+ if (tip->ofile_buffer)
+ free(tip->ofile_buffer);
+
+ tip->fd = -1;
+ tip->ofile = NULL;
+ tip->ofile_buffer = NULL;
+}
+
+static int tip_open_output(struct thread_information *tip)
+{
+ int mode, vbuf_size;
+ char op[NAME_MAX];
+
+ if (fill_ofname(tip, op))
+ return 1;
+
+ tip->ofile = fopen(op, "w+");
+ mode = _IOFBF;
+ vbuf_size = OFILE_BUF;
+
+ if (tip->ofile == NULL) {
+ perror(op);
+ return 1;
+ }
+
+ tip->ofile_buffer = malloc(vbuf_size);
+ if (setvbuf(tip->ofile, tip->ofile_buffer, mode, vbuf_size)) {
+ perror("setvbuf");
+ close_thread(tip);
+ return 1;
+ }
+
+ fill_ops(tip);
+ return 0;
+}
+
+static int start_threads(int cpu)
+{
+ struct thread_information *tip;
+
+ tip = trace_information.threads + cpu;
+ tip->cpu = cpu;
+ tip->trace_info = &trace_information;
+ tip->fd = -1;
+
+ if (tip_open_output(tip))
+ return 1;
+
+ if (pthread_create(&tip->thread, NULL, thread_main, tip)) {
+ perror("pthread_create");
+ close_thread(tip);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void stop_threads()
+{
+ struct thread_information *tip;
+ unsigned long ret;
+ int i;
+
+ for_each_tip(tip, i) {
+ if (tip->thread)
+ (void) pthread_join(tip->thread, (void *) &ret);
+ close_thread(tip);
+ }
+}
+
+static int start_trace(void)
+{
+ int fd;
+ struct kvm_user_trace_setup kuts;
+
+ fd = trace_information.fd = open("/dev/kvm", O_RDWR);
+ if (fd == -1) {
+ perror("/dev/kvm");
+ return 1;
+ }
+
+ memset(&kuts, 0, sizeof(kuts));
+ kuts.buf_size = trace_information.buf_size = buf_size;
+ kuts.buf_nr = trace_information.buf_nr = buf_nr;
+
+ if (ioctl(trace_information.fd , KVM_TRACE_ENABLE, &kuts) < 0) {
+ perror("KVM_TRACE_ENABLE");
+ close(fd);
+ return 1;
+ }
+ trace_information.trace_started = 1;
+
+ return 0;
+}
+
+static void cleanup_trace(void)
+{
+ if (trace_information.fd == -1)
+ return;
+
+ trace_information.lost_records = get_lost_records();
+
+ if (trace_information.trace_started) {
+ trace_information.trace_started = 0;
+ if (ioctl(trace_information.fd, KVM_TRACE_DISABLE) < 0)
+ perror("KVM_TRACE_DISABLE");
+ }
+
+ close(trace_information.fd);
+ trace_information.fd = -1;
+}
+
+static void stop_all_traces(void)
+{
+ if (!is_trace_stopped()) {
+ trace_stopped = 1;
+ stop_threads();
+ cleanup_trace();
+ }
+}
+
+static void exit_trace(int status)
+{
+ stop_all_traces();
+ exit(status);
+}
+
+static int start_kvm_trace(void)
+{
+ int i, size;
+ struct thread_information *tip;
+
+ size = ncpus * sizeof(struct thread_information);
+ tip = malloc(size);
+ if (!tip) {
+ fprintf(stderr, "Out of memory, threads (%d)\n", size);
+ return 1;
+ }
+ memset(tip, 0, size);
+ trace_information.threads = tip;
+
+ if (start_trace())
+ return 1;
+
+ for_each_cpu_online(i) {
+ if (start_threads(i)) {
+ fprintf(stderr, "Failed to start worker threads\n");
+ break;
+ }
+ }
+
+ if (i != ncpus) {
+ stop_threads();
+ cleanup_trace();
+ return 1;
+ }
+
+ return 0;
+}
+
+static void wait_for_threads(void)
+{
+ struct thread_information *tip;
+ int i, tips_running;
+
+ do {
+ tips_running = 0;
+ usleep(100000);
+
+ for_each_tip(tip, i)
+ tips_running += !tip->exited;
+
+ } while (tips_running);
+}
+
+static void show_stats(void)
+{
+ struct thread_information *tip;
+ unsigned long long data_read;
+ int i;
+
+ data_read = 0;
+ for_each_tip(tip, i) {
+ printf(" CPU%3d: %8llu KiB data\n",
+ tip->cpu, (tip->data_read + 1023) >> 10);
+ data_read += tip->data_read;
+ }
+
+ printf(" Total: lost %lu, %8llu KiB data\n",
+ trace_information.lost_records, (data_read + 1023) >> 10);
+
+ if (trace_information.lost_records)
+ fprintf(stderr, "You have lost records, "
+ "consider using a larger buffer size (-b)\n");
+}
+
+static char usage_str[] = \
+ "[ -r debugfs path ] [ -D output dir ] [ -b buffer size ]\n" \
+ "[ -n number of buffers] [ -o <output file> ] [ -w time ] [ -V ]\n\n" \
+ "\t-r Path to mounted debugfs, defaults to /sys/kernel/debug\n" \
+ "\t-o File(s) to send output to\n" \
+ "\t-D Directory to prepend to output file names\n" \
+ "\t-w Stop after defined time, in seconds\n" \
+ "\t-b Sub buffer size in KiB\n" \
+ "\t-n Number of sub buffers\n" \
+ "\t-V Print program version info\n\n";
+
+static void show_usage(char *prog)
+{
+ fprintf(stderr, "Usage: %s %s %s", prog, kvmtrace_version, usage_str);
+ exit(EXIT_FAILURE);
+}
+
+void parse_args(int argc, char **argv)
+{
+ int c;
+
+ while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) >= 0) {
+ switch (c) {
+ case 'r':
+ debugfs_path = optarg;
+ break;
+ case 'o':
+ output_name = optarg;
+ break;
+ case 'w':
+ stop_watch = atoi(optarg);
+ if (stop_watch <= 0) {
+ fprintf(stderr,
+ "Invalid stopwatch value (%d secs)\n",
+ stop_watch);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'V':
+ printf("%s version %s\n", argv[0], kvmtrace_version);
+ exit(EXIT_SUCCESS);
+ case 'b':
+ buf_size = strtoul(optarg, NULL, 10);
+ if (buf_size <= 0 || buf_size > 16*1024) {
+ fprintf(stderr,
+ "Invalid buffer size (%lu)\n",
+ buf_size);
+ exit(EXIT_FAILURE);
+ }
+ buf_size <<= 10;
+ break;
+ case 'n':
+ buf_nr = strtoul(optarg, NULL, 10);
+ if (buf_nr <= 0) {
+ fprintf(stderr,
+ "Invalid buffer nr (%lu)\n", buf_nr);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'D':
+ output_dir = optarg;
+ break;
+ default:
+ show_usage(argv[0]);
+ }
+ }
+
+ if (optind < argc || output_name == NULL)
+ show_usage(argv[0]);
+}
+
+int main(int argc, char *argv[])
+{
+ struct statfs st;
+
+ parse_args(argc, argv);
+
+ if (!debugfs_path)
+ debugfs_path = default_debugfs_path;
+
+ if (statfs(debugfs_path, &st) < 0) {
+ perror("statfs");
+ fprintf(stderr, "%s does not appear to be a valid path\n",
+ debugfs_path);
+ return 1;
+ } else if (st.f_type != (long) DEBUGFS_TYPE) {
+ fprintf(stderr, "%s does not appear to be a debug filesystem,"
+ " please mount debugfs.\n",
+ debugfs_path);
+ return 1;
+ }
+
+ page_size = getpagesize();
+
+ ncpus = sysconf(_SC_NPROCESSORS_ONLN);
+ if (ncpus < 0) {
+ fprintf(stderr, "sysconf(_SC_NPROCESSORS_ONLN) failed\n");
+ return 1;
+ }
+
+ signal(SIGINT, handle_sigint);
+ signal(SIGHUP, handle_sigint);
+ signal(SIGTERM, handle_sigint);
+ signal(SIGALRM, handle_sigint);
+ signal(SIGPIPE, SIG_IGN);
+
+ if (start_kvm_trace() != 0)
+ return 1;
+
+ if (stop_watch)
+ alarm(stop_watch);
+
+ wait_for_threads();
+ stop_all_traces();
+ show_stats();
+
+ return 0;
+}
diff --git a/kvm-unittest/lib/argv.c b/kvm-unittest/lib/argv.c
new file mode 100644
index 0000000..4ee54a6
--- /dev/null
+++ b/kvm-unittest/lib/argv.c
@@ -0,0 +1,33 @@
+#include "libcflat.h"
+
+int __argc;
+char *__argv[100];
+char *__args;
+char __args_copy[1000];
+
+static bool isblank(char p)
+{
+ return p == ' ' || p == '\t';
+}
+
+static char *skip_blanks(char *p)
+{
+ while (isblank(*p))
+ ++p;
+ return p;
+}
+
+void __setup_args(void)
+{
+ char *args = __args;
+ char **argv = __argv;
+ char *p = __args_copy;
+
+ while (*(args = skip_blanks(args)) != '\0') {
+ *argv++ = p;
+ while (*args != '\0' && !isblank(*args))
+ *p++ = *args++;
+ *p++ = '\0';
+ }
+ __argc = argv - __argv;
+}
diff --git a/kvm-unittest/lib/fwcfg.c b/kvm-unittest/lib/fwcfg.c
new file mode 100644
index 0000000..dc34d29
--- /dev/null
+++ b/kvm-unittest/lib/fwcfg.c
@@ -0,0 +1,58 @@
+
+void qemu_cfg_select(int f)
+{
+ outw(QEMU_CFG_CTL_PORT, f);
+}
+
+int qemu_cfg_port_probe()
+{
+ char *sig = "QEMU";
+ int i;
+
+ qemu_cfg_select(QEMU_CFG_SIGNATURE);
+
+ for (i = 0; i < 4; i++)
+ if (inb(QEMU_CFG_DATA_PORT) != sig[i])
+ return 0;
+
+ return 1;
+}
+
+void qemu_cfg_read(uint8_t *buf, int len)
+{
+ while (len--)
+ *(buf++) = inb(QEMU_CFG_DATA_PORT);
+}
+
+uint8_t qemu_cfg_get8(void)
+{
+ uint8_t ret;
+
+ qemu_cfg_read(&ret, 1);
+ return ret;
+}
+
+uint16_t qemu_cfg_get16(void)
+{
+ uint16_t ret;
+
+ qemu_cfg_read((uint8_t*)&ret, 2);
+ return le16_to_cpu(ret);
+}
+
+uint64_t qemu_cfg_get32(void)
+{
+ uint32_t ret;
+
+ qemu_cfg_read((uint8_t*)&ret, 4);
+ return le32_to_cpu(ret);
+}
+
+uint64_t qemu_cfg_get64(void)
+{
+ uint64_t ret;
+
+ qemu_cfg_read((uint8_t*)&ret, 8);
+ return le64_to_cpu(ret);
+}
+
diff --git a/kvm-unittest/lib/panic.c b/kvm-unittest/lib/panic.c
new file mode 100644
index 0000000..6e0b29e
--- /dev/null
+++ b/kvm-unittest/lib/panic.c
@@ -0,0 +1,13 @@
+#include "libcflat.h"
+
+void panic(char *fmt, ...)
+{
+ va_list va;
+ char buf[2000];
+
+ va_start(va, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, va);
+ va_end(va);
+ puts(buf);
+ exit(-1);
+}
diff --git a/kvm-unittest/lib/powerpc/44x/map.c
b/kvm-unittest/lib/powerpc/44x/map.c
new file mode 100644
index 0000000..113434d
--- /dev/null
+++ b/kvm-unittest/lib/powerpc/44x/map.c
@@ -0,0 +1,51 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <address@hidden>
+ */
+
+#include "libcflat.h"
+
+#define TLB_SIZE 64
+
+extern void tlbwe(unsigned int index,
+ unsigned char tid,
+ unsigned int word0,
+ unsigned int word1,
+ unsigned int word2);
+
+unsigned int next_free_index;
+
+#define PAGE_SHIFT 12
+#define PAGE_MASK (~((1<<PAGE_SHIFT)-1))
+
+#define V (1<<9)
+
+void map(unsigned long vaddr, unsigned long paddr)
+{
+ unsigned int w0, w1, w2;
+
+ /* We don't install exception handlers, so we can't handle TLB misses,
+ * so we can't loop around and overwrite entry 0. */
+ if (next_free_index++ >= TLB_SIZE)
+ panic("TLB overflow");
+
+ w0 = (vaddr & PAGE_MASK) | V;
+ w1 = paddr & PAGE_MASK;
+ w2 = 0x3;
+
+ tlbwe(next_free_index, 0, w0, w1, w2);
+}
diff --git a/kvm-unittest/lib/powerpc/io.c b/kvm-unittest/lib/powerpc/io.c
new file mode 100644
index 0000000..8bd2395
--- /dev/null
+++ b/kvm-unittest/lib/powerpc/io.c
@@ -0,0 +1,35 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <address@hidden>
+ */
+
+#include "libcflat.h"
+
+#define BASE 0xf0000000
+#define _putc ((volatile char *)(BASE))
+#define _exit ((volatile char *)(BASE+1))
+
+void puts(const char *s)
+{
+ while (*s != '\0')
+ *_putc = *s++;
+}
+
+void exit(int code)
+{
+ *_exit = code;
+}
diff --git a/kvm-unittest/lib/printf.c b/kvm-unittest/lib/printf.c
new file mode 100644
index 0000000..867eb19
--- /dev/null
+++ b/kvm-unittest/lib/printf.c
@@ -0,0 +1,182 @@
+#include "libcflat.h"
+
+typedef struct pstream {
+ char *buffer;
+ int remain;
+ int added;
+} pstream_t;
+
+static void addchar(pstream_t *p, char c)
+{
+ if (p->remain) {
+ *p->buffer++ = c;
+ --p->remain;
+ }
+ ++p->added;
+}
+
+void print_str(pstream_t *p, const char *s)
+{
+ while (*s)
+ addchar(p, *s++);
+}
+
+static char digits[16] = "0123456789abcdef";
+
+void print_int(pstream_t *ps, long long n, int base)
+{
+ char buf[sizeof(long) * 3 + 2], *p = buf;
+ int s = 0, i;
+
+ if (n < 0) {
+ n = -n;
+ s = 1;
+ }
+
+ while (n) {
+ *p++ = digits[n % base];
+ n /= base;
+ }
+
+ if (s)
+ *p++ = '-';
+
+ if (p == buf)
+ *p++ = '0';
+
+ for (i = 0; i < (p - buf) / 2; ++i) {
+ char tmp;
+
+ tmp = buf[i];
+ buf[i] = p[-1-i];
+ p[-1-i] = tmp;
+ }
+
+ *p = 0;
+
+ print_str(ps, buf);
+}
+
+void print_unsigned(pstream_t *ps, unsigned long long n, int base)
+{
+ char buf[sizeof(long) * 3 + 1], *p = buf;
+ int i;
+
+ while (n) {
+ *p++ = digits[n % base];
+ n /= base;
+ }
+
+ if (p == buf)
+ *p++ = '0';
+
+ for (i = 0; i < (p - buf) / 2; ++i) {
+ char tmp;
+
+ tmp = buf[i];
+ buf[i] = p[-1-i];
+ p[-1-i] = tmp;
+ }
+
+ *p = 0;
+
+ print_str(ps, buf);
+}
+
+int vsnprintf(char *buf, int size, const char *fmt, va_list va)
+{
+ pstream_t s;
+
+ s.buffer = buf;
+ s.remain = size - 1;
+ s.added = 0;
+ while (*fmt) {
+ char f = *fmt++;
+ int nlong = 0;
+
+ if (f != '%') {
+ addchar(&s, f);
+ continue;
+ }
+ morefmt:
+ f = *fmt++;
+ switch (f) {
+ case '%':
+ addchar(&s, '%');
+ break;
+ case 'c':
+ addchar(&s, va_arg(va, int));
+ break;
+ case '\0':
+ --fmt;
+ break;
+ case 'l':
+ ++nlong;
+ goto morefmt;
+ case 'd':
+ switch (nlong) {
+ case 0:
+ print_int(&s, va_arg(va, int), 10);
+ break;
+ case 1:
+ print_int(&s, va_arg(va, long), 10);
+ break;
+ default:
+ print_int(&s, va_arg(va, long long), 10);
+ break;
+ }
+ break;
+ case 'x':
+ switch (nlong) {
+ case 0:
+ print_unsigned(&s, va_arg(va, unsigned), 16);
+ break;
+ case 1:
+ print_unsigned(&s, va_arg(va, unsigned long), 16);
+ break;
+ default:
+ print_unsigned(&s, va_arg(va, unsigned long long), 16);
+ break;
+ }
+ break;
+ case 'p':
+ print_str(&s, "0x");
+ print_unsigned(&s, (unsigned long)va_arg(va, void *), 16);
+ break;
+ case 's':
+ print_str(&s, va_arg(va, const char *));
+ break;
+ default:
+ addchar(&s, f);
+ break;
+ }
+ }
+ *s.buffer = 0;
+ ++s.added;
+ return s.added;
+}
+
+
+int snprintf(char *buf, int size, const char *fmt, ...)
+{
+ va_list va;
+ int r;
+
+ va_start(va, fmt);
+ r = vsnprintf(buf, size, fmt, va);
+ va_end(va);
+ return r;
+}
+
+int printf(const char *fmt, ...)
+{
+ va_list va;
+ char buf[2000];
+ int r;
+
+ va_start(va, fmt);
+ r = vsnprintf(buf, sizeof buf, fmt, va);
+ va_end(va);
+ puts(buf);
+ return r;
+}
diff --git a/kvm-unittest/lib/string.c b/kvm-unittest/lib/string.c
new file mode 100644
index 0000000..3a9caf7
--- /dev/null
+++ b/kvm-unittest/lib/string.c
@@ -0,0 +1,86 @@
+#include "libcflat.h"
+
+unsigned long strlen(const char *buf)
+{
+ unsigned long len = 0;
+
+ while (*buf++)
+ ++len;
+ return len;
+}
+
+char *strcat(char *dest, const char *src)
+{
+ char *p = dest;
+
+ while (*p)
+ ++p;
+ while ((*p++ = *src++) != 0)
+ ;
+ return dest;
+}
+
+int strcmp(const char *a, const char *b)
+{
+ while (*a == *b) {
+ if (*a == '\0') {
+ break;
+ }
+ ++a, ++b;
+ }
+ return *a - *b;
+}
+
+void *memset(void *s, int c, size_t n)
+{
+ size_t i;
+ char *a = s;
+
+ for (i = 0; i < n; ++i)
+ a[i] = c;
+
+ return s;
+}
+
+void *memcpy(void *dest, const void *src, size_t n)
+{
+ size_t i;
+ char *a = dest;
+ const char *b = src;
+
+ for (i = 0; i < n; ++i)
+ a[i] = b[i];
+
+ return dest;
+}
+
+long atol(const char *ptr)
+{
+ long acc = 0;
+ const char *s = ptr;
+ int neg, c;
+
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (*s == '-'){
+ neg = 1;
+ s++;
+ } else {
+ neg = 0;
+ if (*s == '+')
+ s++;
+ }
+
+ while (*s) {
+ if (*s < '0' || *s > '9')
+ break;
+ c = *s - '0';
+ acc = acc * 10 + c;
+ s++;
+ }
+
+ if (neg)
+ acc = -acc;
+
+ return acc;
+}
diff --git a/kvm-unittest/lib/x86/apic.c b/kvm-unittest/lib/x86/apic.c
new file mode 100644
index 0000000..7bb98ed
--- /dev/null
+++ b/kvm-unittest/lib/x86/apic.c
@@ -0,0 +1,143 @@
+#include "libcflat.h"
+#include "apic.h"
+
+static void *g_apic = (void *)0xfee00000;
+static void *g_ioapic = (void *)0xfec00000;
+
+struct apic_ops {
+ u32 (*reg_read)(unsigned reg);
+ void (*reg_write)(unsigned reg, u32 val);
+ void (*icr_write)(u32 val, u32 dest);
+ u32 (*id)(void);
+};
+
+static void outb(unsigned char data, unsigned short port)
+{
+ asm volatile ("out %0, %1" : : "a"(data), "d"(port));
+}
+
+static u32 xapic_read(unsigned reg)
+{
+ return *(volatile u32 *)(g_apic + reg);
+}
+
+static void xapic_write(unsigned reg, u32 val)
+{
+ *(volatile u32 *)(g_apic + reg) = val;
+}
+
+static void xapic_icr_write(u32 val, u32 dest)
+{
+ while (xapic_read(APIC_ICR) & APIC_ICR_BUSY)
+ ;
+ xapic_write(APIC_ICR2, dest << 24);
+ xapic_write(APIC_ICR, val);
+}
+
+static uint32_t xapic_id(void)
+{
+ return xapic_read(APIC_ID) >> 24;
+}
+
+static const struct apic_ops xapic_ops = {
+ .reg_read = xapic_read,
+ .reg_write = xapic_write,
+ .icr_write = xapic_icr_write,
+ .id = xapic_id,
+};
+
+static const struct apic_ops *apic_ops = &xapic_ops;
+
+static u32 x2apic_read(unsigned reg)
+{
+ unsigned a, d;
+
+ asm volatile ("rdmsr" : "=a"(a), "=d"(d) : "c"(APIC_BASE_MSR + reg/16));
+ return a | (u64)d << 32;
+}
+
+static void x2apic_write(unsigned reg, u32 val)
+{
+ asm volatile ("wrmsr" : : "a"(val), "d"(0), "c"(APIC_BASE_MSR + reg/16));
+}
+
+static void x2apic_icr_write(u32 val, u32 dest)
+{
+ asm volatile ("wrmsr" : : "a"(val), "d"(dest),
+ "c"(APIC_BASE_MSR + APIC_ICR/16));
+}
+
+static uint32_t x2apic_id(void)
+{
+ return xapic_read(APIC_ID);
+}
+
+static const struct apic_ops x2apic_ops = {
+ .reg_read = x2apic_read,
+ .reg_write = x2apic_write,
+ .icr_write = x2apic_icr_write,
+ .id = x2apic_id,
+};
+
+u32 apic_read(unsigned reg)
+{
+ return apic_ops->reg_read(reg);
+}
+
+void apic_write(unsigned reg, u32 val)
+{
+ apic_ops->reg_write(reg, val);
+}
+
+void apic_icr_write(u32 val, u32 dest)
+{
+ apic_ops->icr_write(val, dest);
+}
+
+uint32_t apic_id(void)
+{
+ return apic_ops->id();
+}
+
+#define MSR_APIC_BASE 0x0000001b
+
+int enable_x2apic(void)
+{
+ unsigned a, b, c, d;
+
+ asm ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(1));
+
+ if (c & (1 << 21)) {
+ asm ("rdmsr" : "=a"(a), "=d"(d) : "c"(MSR_APIC_BASE));
+ a |= 1 << 10;
+ asm ("wrmsr" : : "a"(a), "d"(d), "c"(MSR_APIC_BASE));
+ apic_ops = &x2apic_ops;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+void ioapic_write_reg(unsigned reg, u32 value)
+{
+ *(volatile u32 *)g_ioapic = reg;
+ *(volatile u32 *)(g_ioapic + 0x10) = value;
+}
+
+void ioapic_write_redir(unsigned line, ioapic_redir_entry_t e)
+{
+ ioapic_write_reg(0x10 + line * 2 + 0, ((u32 *)&e)[0]);
+ ioapic_write_reg(0x10 + line * 2 + 1, ((u32 *)&e)[1]);
+}
+
+void enable_apic(void)
+{
+ printf("enabling apic\n");
+ xapic_write(0xf0, 0x1ff); /* spurious vector register */
+}
+
+void mask_pic_interrupts(void)
+{
+ outb(0xff, 0x21);
+ outb(0xff, 0xa1);
+}
diff --git a/kvm-unittest/lib/x86/atomic.c b/kvm-unittest/lib/x86/atomic.c
new file mode 100644
index 0000000..da74ff2
--- /dev/null
+++ b/kvm-unittest/lib/x86/atomic.c
@@ -0,0 +1,37 @@
+#include <libcflat.h>
+#include "atomic.h"
+
+#ifdef __i386__
+
+u64 atomic64_cmpxchg(atomic64_t *v, u64 old, u64 new)
+{
+ u32 low = new;
+ u32 high = new >> 32;
+
+ asm volatile("lock cmpxchg8b %1\n"
+ : "+A" (old),
+ "+m" (*(volatile long long *)&v->counter)
+ : "b" (low), "c" (high)
+ : "memory"
+ );
+
+ return old;
+}
+
+#else
+
+u64 atomic64_cmpxchg(atomic64_t *v, u64 old, u64 new)
+{
+ u64 ret;
+ u64 _old = old;
+ u64 _new = new;
+
+ asm volatile("lock cmpxchgq %2,%1"
+ : "=a" (ret), "+m" (*(volatile long *)&v->counter)
+ : "r" (_new), "0" (_old)
+ : "memory"
+ );
+ return ret;
+}
+
+#endif
diff --git a/kvm-unittest/lib/x86/desc.c b/kvm-unittest/lib/x86/desc.c
new file mode 100644
index 0000000..7c5c721
--- /dev/null
+++ b/kvm-unittest/lib/x86/desc.c
@@ -0,0 +1,355 @@
+#include "libcflat.h"
+#include "desc.h"
+#include "processor.h"
+
+typedef struct {
+ unsigned short offset0;
+ unsigned short selector;
+ unsigned short ist : 3;
+ unsigned short : 5;
+ unsigned short type : 4;
+ unsigned short : 1;
+ unsigned short dpl : 2;
+ unsigned short p : 1;
+ unsigned short offset1;
+#ifdef __x86_64__
+ unsigned offset2;
+ unsigned reserved;
+#endif
+} idt_entry_t;
+
+typedef struct {
+ u16 limit_low;
+ u16 base_low;
+ u8 base_middle;
+ u8 access;
+ u8 granularity;
+ u8 base_high;
+} gdt_entry_t;
+
+extern idt_entry_t boot_idt[256];
+
+void set_idt_entry(int vec, void *addr, int dpl)
+{
+ idt_entry_t *e = &boot_idt[vec];
+ memset(e, 0, sizeof *e);
+ e->offset0 = (unsigned long)addr;
+ e->selector = read_cs();
+ e->ist = 0;
+ e->type = 14;
+ e->dpl = dpl;
+ e->p = 1;
+ e->offset1 = (unsigned long)addr >> 16;
+#ifdef __x86_64__
+ e->offset2 = (unsigned long)addr >> 32;
+#endif
+}
+
+void set_idt_sel(int vec, u16 sel)
+{
+ idt_entry_t *e = &boot_idt[vec];
+ e->selector = sel;
+}
+
+struct ex_record {
+ unsigned long rip;
+ unsigned long handler;
+};
+
+extern struct ex_record exception_table_start, exception_table_end;
+
+static void check_exception_table(struct ex_regs *regs)
+{
+ struct ex_record *ex;
+ unsigned ex_val;
+
+ ex_val = regs->vector | (regs->error_code << 16);
+
+ asm("mov %0, %%gs:4" : : "r"(ex_val));
+
+ for (ex = &exception_table_start; ex != &exception_table_end; ++ex) {
+ if (ex->rip == regs->rip) {
+ regs->rip = ex->handler;
+ return;
+ }
+ }
+ printf("unhandled excecption %d\n", regs->vector);
+ exit(7);
+}
+
+static void (*exception_handlers[32])(struct ex_regs *regs);
+
+
+void handle_exception(u8 v, void (*func)(struct ex_regs *regs))
+{
+ if (v < 32)
+ exception_handlers[v] = func;
+}
+
+#ifndef __x86_64__
+__attribute__((regparm(1)))
+#endif
+void do_handle_exception(struct ex_regs *regs)
+{
+ if (regs->vector < 32 && exception_handlers[regs->vector]) {
+ exception_handlers[regs->vector](regs);
+ return;
+ }
+ printf("unhandled cpu excecption %d\n", regs->vector);
+ if (regs->vector == 14)
+ printf("PF at %p addr %p\n", regs->rip, read_cr2());
+ exit(7);
+}
+
+#ifdef __x86_64__
+# define R "r"
+# define W "q"
+# define S "8"
+#else
+# define R "e"
+# define W "l"
+# define S "4"
+#endif
+
+#define EX(NAME, N) extern char NAME##_fault; \
+ asm (".pushsection .text \n\t" \
+ #NAME"_fault: \n\t" \
+ "push"W" $0 \n\t" \
+ "push"W" $"#N" \n\t" \
+ "jmp __handle_exception \n\t" \
+ ".popsection")
+
+#define EX_E(NAME, N) extern char NAME##_fault; \
+ asm (".pushsection .text \n\t" \
+ #NAME"_fault: \n\t" \
+ "push"W" $"#N" \n\t" \
+ "jmp __handle_exception \n\t" \
+ ".popsection")
+
+EX(de, 0);
+EX(db, 1);
+EX(nmi, 2);
+EX(bp, 3);
+EX(of, 4);
+EX(br, 5);
+EX(ud, 6);
+EX(nm, 7);
+EX_E(df, 8);
+EX_E(ts, 10);
+EX_E(np, 11);
+EX_E(ss, 12);
+EX_E(gp, 13);
+EX_E(pf, 14);
+EX(mf, 16);
+EX_E(ac, 17);
+EX(mc, 18);
+EX(xm, 19);
+
+asm (".pushsection .text \n\t"
+ "__handle_exception: \n\t"
+#ifdef __x86_64__
+ "push %r15; push %r14; push %r13; push %r12 \n\t"
+ "push %r11; push %r10; push %r9; push %r8 \n\t"
+#endif
+ "push %"R"di; push %"R"si; push %"R"bp; sub $"S", %"R"sp \n\t"
+ "push %"R"bx; push %"R"dx; push %"R"cx; push %"R"ax \n\t"
+#ifdef __x86_64__
+ "mov %"R"sp, %"R"di \n\t"
+#else
+ "mov %"R"sp, %"R"ax \n\t"
+#endif
+ "call do_handle_exception \n\t"
+ "pop %"R"ax; pop %"R"cx; pop %"R"dx; pop %"R"bx \n\t"
+ "add $"S", %"R"sp; pop %"R"bp; pop %"R"si; pop %"R"di \n\t"
+#ifdef __x86_64__
+ "pop %r8; pop %r9; pop %r10; pop %r11 \n\t"
+ "pop %r12; pop %r13; pop %r14; pop %r15 \n\t"
+#endif
+ "add $"S", %"R"sp \n\t"
+ "add $"S", %"R"sp \n\t"
+ "iret"W" \n\t"
+ ".popsection");
+
+static void *idt_handlers[32] = {
+ [0] = &de_fault,
+ [1] = &db_fault,
+ [2] = &nmi_fault,
+ [3] = &bp_fault,
+ [4] = &of_fault,
+ [5] = &br_fault,
+ [6] = &ud_fault,
+ [7] = &nm_fault,
+ [8] = &df_fault,
+ [10] = &ts_fault,
+ [11] = &np_fault,
+ [12] = &ss_fault,
+ [13] = &gp_fault,
+ [14] = &pf_fault,
+ [16] = &mf_fault,
+ [17] = &ac_fault,
+ [18] = &mc_fault,
+ [19] = &xm_fault,
+};
+
+void setup_idt(void)
+{
+ int i;
+ static bool idt_initialized = false;
+
+ if (idt_initialized) {
+ return;
+ }
+ idt_initialized = true;
+ for (i = 0; i < 32; i++)
+ if (idt_handlers[i])
+ set_idt_entry(i, idt_handlers[i], 0);
+ handle_exception(0, check_exception_table);
+ handle_exception(6, check_exception_table);
+ handle_exception(13, check_exception_table);
+}
+
+unsigned exception_vector(void)
+{
+ unsigned short vector;
+
+ asm("mov %%gs:4, %0" : "=rm"(vector));
+ return vector;
+}
+
+unsigned exception_error_code(void)
+{
+ unsigned short error_code;
+
+ asm("mov %%gs:6, %0" : "=rm"(error_code));
+ return error_code;
+}
+
+#ifndef __x86_64__
+/*
+ * GDT, with 6 entries:
+ * 0x00 - NULL descriptor
+ * 0x08 - Code segment
+ * 0x10 - Data segment
+ * 0x18 - Not presend code segment
+ * 0x20 - Primery task
+ * 0x28 - Interrupt task
+ *
+ * 0x30 to 0x48 - Free to use for test cases
+ */
+
+static gdt_entry_t gdt[10];
+#define TSS_GDT_OFFSET 4
+
+void set_gdt_entry(int num, u32 base, u32 limit, u8 access, u8 gran)
+{
+ /* Setup the descriptor base address */
+ gdt[num].base_low = (base & 0xFFFF);
+ gdt[num].base_middle = (base >> 16) & 0xFF;
+ gdt[num].base_high = (base >> 24) & 0xFF;
+
+ /* Setup the descriptor limits */
+ gdt[num].limit_low = (limit & 0xFFFF);
+ gdt[num].granularity = ((limit >> 16) & 0x0F);
+
+ /* Finally, set up the granularity and access flags */
+ gdt[num].granularity |= (gran & 0xF0);
+ gdt[num].access = access;
+}
+
+void setup_gdt(void)
+{
+ struct descriptor_table_ptr gp;
+ /* Setup the GDT pointer and limit */
+ gp.limit = sizeof(gdt) - 1;
+ gp.base = (ulong)&gdt;
+
+ memset(gdt, 0, sizeof(gdt));
+
+ /* Our NULL descriptor */
+ set_gdt_entry(0, 0, 0, 0, 0);
+
+ /* The second entry is our Code Segment. The base address
+ * is 0, the limit is 4GBytes, it uses 4KByte granularity,
+ * uses 32-bit opcodes, and is a Code Segment descriptor. */
+ set_gdt_entry(1, 0, 0xFFFFFFFF, 0x9A, 0xcf);
+
+ /* The third entry is our Data Segment. It's EXACTLY the
+ * same as our code segment, but the descriptor type in
+ * this entry's access byte says it's a Data Segment */
+ set_gdt_entry(2, 0, 0xFFFFFFFF, 0x92, 0xcf);
+
+ /* Same as code register above but not present */
+ set_gdt_entry(3, 0, 0xFFFFFFFF, 0x1A, 0xcf);
+
+
+ /* Flush out the old GDT and install the new changes! */
+ lgdt(&gp);
+
+ asm volatile ("mov %0, %%ds\n\t"
+ "mov %0, %%es\n\t"
+ "mov %0, %%fs\n\t"
+ "mov %0, %%gs\n\t"
+ "mov %0, %%ss\n\t"
+ "jmp $0x08, $.Lflush2\n\t"
+ ".Lflush2: "::"r"(0x10));
+}
+
+void set_idt_task_gate(int vec, u16 sel)
+{
+ idt_entry_t *e = &boot_idt[vec];
+
+ memset(e, 0, sizeof *e);
+
+ e->selector = sel;
+ e->ist = 0;
+ e->type = 5;
+ e->dpl = 0;
+ e->p = 1;
+}
+
+/*
+ * 0 - main task
+ * 1 - interrupt task
+ */
+
+static tss32_t tss[2];
+static char tss_stack[2][4096];
+
+void setup_tss32(void)
+{
+ u16 desc_size = sizeof(tss32_t);
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ tss[i].cr3 = read_cr3();
+ tss[i].ss0 = tss[i].ss1 = tss[i].ss2 = 0x10;
+ tss[i].esp = tss[i].esp0 = tss[i].esp1 = tss[i].esp2 =
+ (u32)tss_stack[i] + 4096;
+ tss[i].cs = 0x08;
+ tss[i].ds = tss[i].es = tss[i].fs = tss[i].gs = tss[i].ss =
0x10;
+ tss[i].iomap_base = (u16)desc_size;
+ set_gdt_entry(TSS_GDT_OFFSET + i, (u32)&tss[i],
+ desc_size - 1, 0x89, 0x0f);
+ }
+
+ ltr(TSS_MAIN);
+}
+
+void set_intr_task_gate(int e, void *fn)
+{
+ tss[1].eip = (u32)fn;
+ set_idt_task_gate(e, TSS_INTR);
+}
+
+void print_current_tss_info(void)
+{
+ u16 tr = str();
+ int i = (tr == TSS_MAIN) ? 0 : 1;
+
+ if (tr != TSS_MAIN && tr != TSS_INTR)
+ printf("Unknown TSS %x\n", tr);
+ else
+ printf("TR=%x Main TSS back link %x. Current TSS back link
%x\n",
+ tr, tss[0].prev, tss[i].prev);
+}
+#endif
diff --git a/kvm-unittest/lib/x86/fwcfg.c b/kvm-unittest/lib/x86/fwcfg.c
new file mode 100644
index 0000000..e2cdd15
--- /dev/null
+++ b/kvm-unittest/lib/x86/fwcfg.c
@@ -0,0 +1,45 @@
+#include "fwcfg.h"
+#include "smp.h"
+
+static struct spinlock lock;
+
+uint64_t fwcfg_get_u(uint16_t index, int bytes)
+{
+ uint64_t r = 0;
+ uint8_t b;
+ int i;
+
+ spin_lock(&lock);
+ asm volatile ("out %0, %1" : : "a"(index), "d"((uint16_t)BIOS_CFG_IOPORT));
+ for (i = 0; i < bytes; ++i) {
+ asm volatile ("in %1, %0" : "=a"(b) : "d"((uint16_t)(BIOS_CFG_IOPORT +
1)));
+ r |= (uint64_t)b << (i * 8);
+ }
+ spin_unlock(&lock);
+ return r;
+}
+
+uint8_t fwcfg_get_u8(unsigned index)
+{
+ return fwcfg_get_u(index, 1);
+}
+
+uint16_t fwcfg_get_u16(unsigned index)
+{
+ return fwcfg_get_u(index, 2);
+}
+
+uint32_t fwcfg_get_u32(unsigned index)
+{
+ return fwcfg_get_u(index, 4);
+}
+
+uint64_t fwcfg_get_u64(unsigned index)
+{
+ return fwcfg_get_u(index, 8);
+}
+
+unsigned fwcfg_get_nb_cpus(void)
+{
+ return fwcfg_get_u16(FW_CFG_NB_CPUS);
+}
diff --git a/kvm-unittest/lib/x86/io.c b/kvm-unittest/lib/x86/io.c
new file mode 100644
index 0000000..d3b971e
--- /dev/null
+++ b/kvm-unittest/lib/x86/io.c
@@ -0,0 +1,83 @@
+#include "libcflat.h"
+#include "smp.h"
+#include "io.h"
+#ifndef USE_SERIAL
+#define USE_SERIAL
+#endif
+
+static struct spinlock lock;
+static int serial_iobase = 0x3f8;
+static int serial_inited = 0;
+
+static void serial_outb(char ch)
+{
+ u8 lsr;
+
+ do {
+ lsr = inb(serial_iobase + 0x05);
+ } while (!(lsr & 0x20));
+
+ outb(ch, serial_iobase + 0x00);
+}
+
+static void serial_init(void)
+{
+ u8 lcr;
+
+ /* set DLAB */
+ lcr = inb(serial_iobase + 0x03);
+ lcr |= 0x80;
+ outb(lcr, serial_iobase + 0x03);
+
+ /* set baud rate to 115200 */
+ outb(0x01, serial_iobase + 0x00);
+ outb(0x00, serial_iobase + 0x01);
+
+ /* clear DLAB */
+ lcr = inb(serial_iobase + 0x03);
+ lcr &= ~0x80;
+ outb(lcr, serial_iobase + 0x03);
+}
+
+static void print_serial(const char *buf)
+{
+ unsigned long len = strlen(buf);
+#ifdef USE_SERIAL
+ unsigned long i;
+ if (!serial_inited) {
+ serial_init();
+ serial_inited = 1;
+ }
+
+ for (i = 0; i < len; i++) {
+ serial_outb(buf[i]);
+ }
+#else
+ asm volatile ("rep/outsb" : "+S"(buf), "+c"(len) : "d"(0xf1));
+#endif
+}
+
+void puts(const char *s)
+{
+ spin_lock(&lock);
+ print_serial(s);
+ spin_unlock(&lock);
+}
+
+void exit(int code)
+{
+#ifdef USE_SERIAL
+ static const char shutdown_str[8] = "Shutdown";
+ int i;
+
+ /* test device exit (with status) */
+ outl(code, 0xf4);
+
+ /* if that failed, try the Bochs poweroff port */
+ for (i = 0; i < 8; i++) {
+ outb(shutdown_str[i], 0x8900);
+ }
+#else
+ asm volatile("out %0, %1" : : "a"(code), "d"((short)0xf4));
+#endif
+}
diff --git a/kvm-unittest/lib/x86/isr.c b/kvm-unittest/lib/x86/isr.c
new file mode 100644
index 0000000..9986d17
--- /dev/null
+++ b/kvm-unittest/lib/x86/isr.c
@@ -0,0 +1,98 @@
+#include "libcflat.h"
+#include "isr.h"
+#include "vm.h"
+#include "desc.h"
+
+#ifdef __x86_64__
+# define R "r"
+#else
+# define R "e"
+#endif
+
+extern char isr_entry_point[];
+
+asm (
+ "isr_entry_point: \n"
+#ifdef __x86_64__
+ "push %r15 \n\t"
+ "push %r14 \n\t"
+ "push %r13 \n\t"
+ "push %r12 \n\t"
+ "push %r11 \n\t"
+ "push %r10 \n\t"
+ "push %r9 \n\t"
+ "push %r8 \n\t"
+#endif
+ "push %"R "di \n\t"
+ "push %"R "si \n\t"
+ "push %"R "bp \n\t"
+ "push %"R "sp \n\t"
+ "push %"R "bx \n\t"
+ "push %"R "dx \n\t"
+ "push %"R "cx \n\t"
+ "push %"R "ax \n\t"
+#ifdef __x86_64__
+ "mov %rsp, %rdi \n\t"
+ "callq *8*16(%rsp) \n\t"
+#else
+ "push %esp \n\t"
+ "calll *4+4*8(%esp) \n\t"
+ "add $4, %esp \n\t"
+#endif
+ "pop %"R "ax \n\t"
+ "pop %"R "cx \n\t"
+ "pop %"R "dx \n\t"
+ "pop %"R "bx \n\t"
+ "pop %"R "bp \n\t"
+ "pop %"R "bp \n\t"
+ "pop %"R "si \n\t"
+ "pop %"R "di \n\t"
+#ifdef __x86_64__
+ "pop %r8 \n\t"
+ "pop %r9 \n\t"
+ "pop %r10 \n\t"
+ "pop %r11 \n\t"
+ "pop %r12 \n\t"
+ "pop %r13 \n\t"
+ "pop %r14 \n\t"
+ "pop %r15 \n\t"
+#endif
+ ".globl isr_iret_ip\n\t"
+#ifdef __x86_64__
+ "add $8, %rsp \n\t"
+ "isr_iret_ip: \n\t"
+ "iretq \n\t"
+#else
+ "add $4, %esp \n\t"
+ "isr_iret_ip: \n\t"
+ "iretl \n\t"
+#endif
+ );
+
+void handle_irq(unsigned vec, void (*func)(isr_regs_t *regs))
+{
+ u8 *thunk = vmalloc(50);
+
+ set_idt_entry(vec, thunk, 0);
+
+#ifdef __x86_64__
+ /* sub $8, %rsp */
+ *thunk++ = 0x48; *thunk++ = 0x83; *thunk++ = 0xec; *thunk++ = 0x08;
+ /* mov $func_low, %(rsp) */
+ *thunk++ = 0xc7; *thunk++ = 0x04; *thunk++ = 0x24;
+ *(u32 *)thunk = (ulong)func; thunk += 4;
+ /* mov $func_high, %(rsp+4) */
+ *thunk++ = 0xc7; *thunk++ = 0x44; *thunk++ = 0x24; *thunk++ = 0x04;
+ *(u32 *)thunk = (ulong)func >> 32; thunk += 4;
+ /* jmp isr_entry_point */
+ *thunk ++ = 0xe9;
+ *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4);
+#else
+ /* push $func */
+ *thunk++ = 0x68;
+ *(u32 *)thunk = (ulong)func; thunk += 4;
+ /* jmp isr_entry_point */
+ *thunk++ = 0xe9;
+ *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4);
+#endif
+}
diff --git a/kvm-unittest/lib/x86/pci.c b/kvm-unittest/lib/x86/pci.c
new file mode 100644
index 0000000..f95cd88
--- /dev/null
+++ b/kvm-unittest/lib/x86/pci.c
@@ -0,0 +1,55 @@
+#include <linux/pci_regs.h>
+#include "pci.h"
+
+static void outl(unsigned short port, unsigned val)
+{
+ asm volatile("outl %d0, %w1" : : "a"(val), "Nd"(port));
+}
+
+static unsigned inl(unsigned short port)
+{
+ unsigned data;
+ asm volatile("inl %w1, %d0" : "=a"(data) : "Nd"(port));
+ return data;
+}
+static uint32_t pci_config_read(pcidevaddr_t dev, uint8_t reg)
+{
+ uint32_t index = reg | (dev << 8) | (0x1 << 31);
+ outl(0xCF8, index);
+ return inl(0xCFC);
+}
+
+/* Scan bus look for a specific device. Only bus 0 scanned for now. */
+pcidevaddr_t pci_find_dev(uint16_t vendor_id, uint16_t device_id)
+{
+ unsigned dev;
+ for (dev = 0; dev < 256; ++dev) {
+ uint32_t id = pci_config_read(dev, 0);
+ if ((id & 0xFFFF) == vendor_id && (id >> 16) == device_id) {
+ return dev;
+ }
+ }
+ return PCIDEVADDR_INVALID;
+}
+
+unsigned long pci_bar_addr(pcidevaddr_t dev, int bar_num)
+{
+ uint32_t bar = pci_config_read(dev, PCI_BASE_ADDRESS_0 + bar_num * 4);
+ if (bar & PCI_BASE_ADDRESS_SPACE_IO) {
+ return bar & PCI_BASE_ADDRESS_IO_MASK;
+ } else {
+ return bar & PCI_BASE_ADDRESS_MEM_MASK;
+ }
+}
+
+bool pci_bar_is_memory(pcidevaddr_t dev, int bar_num)
+{
+ uint32_t bar = pci_config_read(dev, PCI_BASE_ADDRESS_0 + bar_num * 4);
+ return !(bar & PCI_BASE_ADDRESS_SPACE_IO);
+}
+
+bool pci_bar_is_valid(pcidevaddr_t dev, int bar_num)
+{
+ uint32_t bar = pci_config_read(dev, PCI_BASE_ADDRESS_0 + bar_num * 4);
+ return bar;
+}
diff --git a/kvm-unittest/lib/x86/smp.c b/kvm-unittest/lib/x86/smp.c
new file mode 100644
index 0000000..d4c8106
--- /dev/null
+++ b/kvm-unittest/lib/x86/smp.c
@@ -0,0 +1,124 @@
+
+#include <libcflat.h>
+#include "smp.h"
+#include "apic.h"
+#include "fwcfg.h"
+
+#define IPI_VECTOR 0x20
+
+typedef void (*ipi_function_type)(void *data);
+
+static struct spinlock ipi_lock;
+static volatile ipi_function_type ipi_function;
+static volatile void *ipi_data;
+static volatile int ipi_done;
+static volatile bool ipi_wait;
+static int _cpu_count;
+
+static __attribute__((used)) void ipi()
+{
+ void (*function)(void *data) = ipi_function;
+ void *data = ipi_data;
+ bool wait = ipi_wait;
+
+ if (!wait) {
+ ipi_done = 1;
+ apic_write(APIC_EOI, 0);
+ }
+ function(data);
+ if (wait) {
+ ipi_done = 1;
+ apic_write(APIC_EOI, 0);
+ }
+}
+
+asm (
+ "ipi_entry: \n"
+ " call ipi \n"
+#ifndef __x86_64__
+ " iret"
+#else
+ " iretq"
+#endif
+ );
+
+void spin_lock(struct spinlock *lock)
+{
+ int v = 1;
+
+ do {
+ asm volatile ("xchg %1, %0" : "+m"(lock->v), "+r"(v));
+ } while (v);
+ asm volatile ("" : : : "memory");
+}
+
+void spin_unlock(struct spinlock *lock)
+{
+ asm volatile ("" : : : "memory");
+ lock->v = 0;
+}
+
+int cpu_count(void)
+{
+ return _cpu_count;
+}
+
+int smp_id(void)
+{
+ unsigned id;
+
+ asm ("mov %%gs:0, %0" : "=r"(id));
+ return id;
+}
+
+static void setup_smp_id(void *data)
+{
+ asm ("mov %0, %%gs:0" : : "r"(apic_id()) : "memory");
+}
+
+static void __on_cpu(int cpu, void (*function)(void *data), void *data,
+ int wait)
+{
+ spin_lock(&ipi_lock);
+ if (cpu == smp_id())
+ function(data);
+ else {
+ ipi_done = 0;
+ ipi_function = function;
+ ipi_data = data;
+ ipi_wait = wait;
+ apic_icr_write(APIC_INT_ASSERT | APIC_DEST_PHYSICAL | APIC_DM_FIXED
+ | IPI_VECTOR,
+ cpu);
+ while (!ipi_done)
+ ;
+ }
+ spin_unlock(&ipi_lock);
+}
+
+void on_cpu(int cpu, void (*function)(void *data), void *data)
+{
+ __on_cpu(cpu, function, data, 1);
+}
+
+void on_cpu_async(int cpu, void (*function)(void *data), void *data)
+{
+ __on_cpu(cpu, function, data, 0);
+}
+
+
+void smp_init(void)
+{
+ int i;
+ void ipi_entry(void);
+
+ _cpu_count = fwcfg_get_nb_cpus();
+
+ setup_idt();
+ set_idt_entry(IPI_VECTOR, ipi_entry, 0);
+
+ setup_smp_id(0);
+ for (i = 1; i < cpu_count(); ++i)
+ on_cpu(i, setup_smp_id, 0);
+
+}
diff --git a/kvm-unittest/lib/x86/vm.c b/kvm-unittest/lib/x86/vm.c
new file mode 100644
index 0000000..188bf57
--- /dev/null
+++ b/kvm-unittest/lib/x86/vm.c
@@ -0,0 +1,256 @@
+#include "fwcfg.h"
+#include "vm.h"
+#include "libcflat.h"
+
+#define PAGE_SIZE 4096ul
+#ifdef __x86_64__
+#define LARGE_PAGE_SIZE (512 * PAGE_SIZE)
+#else
+#define LARGE_PAGE_SIZE (1024 * PAGE_SIZE)
+#endif
+
+static void *free = 0;
+static void *vfree_top = 0;
+
+static void free_memory(void *mem, unsigned long size)
+{
+ while (size >= PAGE_SIZE) {
+ *(void **)mem = free;
+ free = mem;
+ mem += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+}
+
+void *alloc_page()
+{
+ void *p;
+
+ if (!free)
+ return 0;
+
+ p = free;
+ free = *(void **)free;
+
+ return p;
+}
+
+void free_page(void *page)
+{
+ *(void **)page = free;
+ free = page;
+}
+
+extern char edata;
+static unsigned long end_of_memory;
+
+#ifdef __x86_64__
+#define PAGE_LEVEL 4
+#define PGDIR_WIDTH 9
+#define PGDIR_MASK 511
+#else
+#define PAGE_LEVEL 2
+#define PGDIR_WIDTH 10
+#define PGDIR_MASK 1023
+#endif
+
+void install_pte(unsigned long *cr3,
+ int pte_level,
+ void *virt,
+ unsigned long pte,
+ unsigned long *pt_page)
+{
+ int level;
+ unsigned long *pt = cr3;
+ unsigned offset;
+
+ for (level = PAGE_LEVEL; level > pte_level; --level) {
+ offset = ((unsigned long)virt >> ((level-1) * PGDIR_WIDTH + 12)) &
PGDIR_MASK;
+ if (!(pt[offset] & PTE_PRESENT)) {
+ unsigned long *new_pt = pt_page;
+ if (!new_pt)
+ new_pt = alloc_page();
+ else
+ pt_page = 0;
+ memset(new_pt, 0, PAGE_SIZE);
+ pt[offset] = virt_to_phys(new_pt) | PTE_PRESENT | PTE_WRITE;
+ }
+ pt = phys_to_virt(pt[offset] & 0xffffffffff000ull);
+ }
+ offset = ((unsigned long)virt >> ((level-1) * PGDIR_WIDTH + 12)) &
PGDIR_MASK;
+ pt[offset] = pte;
+}
+
+static unsigned long get_pte(unsigned long *cr3, void *virt)
+{
+ int level;
+ unsigned long *pt = cr3, pte;
+ unsigned offset;
+
+ for (level = PAGE_LEVEL; level > 1; --level) {
+ offset = ((unsigned long)virt >> (((level-1) * PGDIR_WIDTH) + 12)) &
PGDIR_MASK;
+ pte = pt[offset];
+ if (!(pte & PTE_PRESENT))
+ return 0;
+ if (level == 2 && (pte & PTE_PSE))
+ return pte;
+ pt = phys_to_virt(pte & 0xffffffffff000ull);
+ }
+ offset = ((unsigned long)virt >> (((level-1) * PGDIR_WIDTH) + 12)) &
PGDIR_MASK;
+ pte = pt[offset];
+ return pte;
+}
+
+void install_large_page(unsigned long *cr3,
+ unsigned long phys,
+ void *virt)
+{
+ install_pte(cr3, 2, virt, phys | PTE_PRESENT | PTE_WRITE | PTE_USER |
PTE_PSE, 0);
+}
+
+void install_page(unsigned long *cr3,
+ unsigned long phys,
+ void *virt)
+{
+ install_pte(cr3, 1, virt, phys | PTE_PRESENT | PTE_WRITE | PTE_USER, 0);
+}
+
+
+static inline void load_gdt(unsigned long *table, int nent)
+{
+ struct descriptor_table_ptr descr;
+
+ descr.limit = nent * 8 - 1;
+ descr.base = (ulong)table;
+ lgdt(&descr);
+}
+
+#define SEG_CS_32 8
+#define SEG_CS_64 16
+
+struct ljmp {
+ void *ofs;
+ unsigned short seg;
+};
+
+static void setup_mmu_range(unsigned long *cr3, unsigned long start,
+ unsigned long len)
+{
+ u64 max = (u64)len + (u64)start;
+ u64 phys = start;
+
+ while (phys + LARGE_PAGE_SIZE <= max) {
+ install_large_page(cr3, phys, (void *)(ulong)phys);
+ phys += LARGE_PAGE_SIZE;
+ }
+ while (phys + PAGE_SIZE <= max) {
+ install_page(cr3, phys, (void *)(ulong)phys);
+ phys += PAGE_SIZE;
+ }
+}
+
+static void setup_mmu(unsigned long len)
+{
+ unsigned long *cr3 = alloc_page();
+
+ memset(cr3, 0, PAGE_SIZE);
+
+#ifdef __x86_64__
+ if (len < (1ul << 32))
+ len = (1ul << 32); /* map mmio 1:1 */
+
+ setup_mmu_range(cr3, 0, len);
+#else
+ if (len > (1ul << 31))
+ len = (1ul << 31);
+
+ /* 0 - 2G memory, 2G-3G valloc area, 3G-4G mmio */
+ setup_mmu_range(cr3, 0, len);
+ setup_mmu_range(cr3, 3ul << 30, (1ul << 30));
+ vfree_top = (void*)(3ul << 30);
+#endif
+
+ write_cr3(virt_to_phys(cr3));
+#ifndef __x86_64__
+ write_cr4(X86_CR4_PSE);
+#endif
+ write_cr0(X86_CR0_PG |X86_CR0_PE | X86_CR0_WP);
+
+ printf("paging enabled\n");
+ printf("cr0 = %x\n", read_cr0());
+ printf("cr3 = %x\n", read_cr3());
+ printf("cr4 = %x\n", read_cr4());
+}
+
+void setup_vm()
+{
+ end_of_memory = fwcfg_get_u64(FW_CFG_RAM_SIZE);
+ free_memory(&edata, end_of_memory - (unsigned long)&edata);
+ setup_mmu(end_of_memory);
+}
+
+void *vmalloc(unsigned long size)
+{
+ void *mem, *p;
+ unsigned pages;
+
+ size += sizeof(unsigned long);
+
+ size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+ vfree_top -= size;
+ mem = p = vfree_top;
+ pages = size / PAGE_SIZE;
+ while (pages--) {
+ install_page(phys_to_virt(read_cr3()), virt_to_phys(alloc_page()), p);
+ p += PAGE_SIZE;
+ }
+ *(unsigned long *)mem = size;
+ mem += sizeof(unsigned long);
+ return mem;
+}
+
+uint64_t virt_to_phys_cr3(void *mem)
+{
+ return (get_pte(phys_to_virt(read_cr3()), mem) & PTE_ADDR) + ((ulong)mem &
(PAGE_SIZE - 1));
+}
+
+void vfree(void *mem)
+{
+ unsigned long size = ((unsigned long *)mem)[-1];
+
+ while (size) {
+ free_page(phys_to_virt(get_pte(phys_to_virt(read_cr3()), mem) &
PTE_ADDR));
+ mem += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+}
+
+void *vmap(unsigned long long phys, unsigned long size)
+{
+ void *mem, *p;
+ unsigned pages;
+
+ size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+ vfree_top -= size;
+ phys &= ~(unsigned long long)(PAGE_SIZE - 1);
+
+ mem = p = vfree_top;
+ pages = size / PAGE_SIZE;
+ while (pages--) {
+ install_page(phys_to_virt(read_cr3()), phys, p);
+ phys += PAGE_SIZE;
+ p += PAGE_SIZE;
+ }
+ return mem;
+}
+
+void *alloc_vpages(ulong nr)
+{
+ vfree_top -= PAGE_SIZE * nr;
+ return vfree_top;
+}
+
+void *alloc_vpage(void)
+{
+ return alloc_vpages(1);
+}
diff --git a/kvm-unittest/main-ppc.c b/kvm-unittest/main-ppc.c
new file mode 100644
index 0000000..5af59f8
--- /dev/null
+++ b/kvm-unittest/main-ppc.c
@@ -0,0 +1,383 @@
+/*
+ * Kernel-based Virtual Machine test driver
+ *
+ * This test driver provides a simple way of testing kvm, without a full
+ * device model.
+ *
+ * Copyright (C) 2006 Qumranet
+ * Copyright IBM Corp. 2008
+ *
+ * Authors:
+ *
+ * Avi Kivity <address@hidden>
+ * Yaniv Kamay <address@hidden>
+ * Hollis Blanchard <address@hidden>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+
+#define _GNU_SOURCE
+
+#include <libkvm.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <semaphore.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <pthread.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/syscall.h>
+#include <linux/unistd.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include "iotable.h"
+
+static int gettid(void)
+{
+ return syscall(__NR_gettid);
+}
+
+kvm_context_t kvm;
+
+#define IPI_SIGNAL (SIGRTMIN + 4)
+
+struct io_table mmio_table;
+
+static int ncpus = 1;
+static sem_t exited_sem;
+static __thread int vcpu;
+static sigset_t kernel_sigmask;
+static sigset_t ipi_sigmask;
+static uint64_t memory_size = 128 * 1024 * 1024;
+
+struct vcpu_info {
+ pid_t tid;
+};
+
+struct vcpu_info *vcpus;
+
+/* Must match flat.lds linker script */
+#define VM_TEST_LOAD_ADDRESS 0x100000
+
+static int test_debug(void *opaque, void *vcpu)
+{
+ printf("test_debug\n");
+ return 0;
+}
+
+static int test_halt(void *opaque, int vcpu)
+{
+ int n;
+
+ sigwait(&ipi_sigmask, &n);
+ return 0;
+}
+
+static int test_io_window(void *opaque)
+{
+ return 0;
+}
+
+static int test_try_push_interrupts(void *opaque)
+{
+ return 0;
+}
+
+static void test_post_kvm_run(void *opaque, void *vcpu)
+{
+}
+
+static int test_pre_kvm_run(void *opaque, void *vcpu)
+{
+ return 0;
+}
+
+static int mmio_handler(void *opaque, int len, int is_write, uint64_t offset,
+ uint64_t *data)
+{
+ int r = 0;
+
+ switch (offset) {
+ case 0: /* putc */
+ putc(*(char *)data, stdout);
+ fflush(stdout);
+ break;
+ case 1: /* exit */
+ r = *(char *)data;
+ break;
+ default:
+ printf("%s: offset %"PRIx64" len %d data %"PRIx64"\n",
+ __func__, offset, len, *(uint64_t *)data);
+ r = -EINVAL;
+ }
+
+ return r;
+}
+
+static int test_mem_read(void *opaque, uint64_t addr, uint8_t *data, int len)
+{
+ struct io_table_entry *iodev;
+
+#if 0
+ printf("%s: addr %"PRIx64" len %d\n", __func__, addr, len);
+#endif
+
+ iodev = io_table_lookup(&mmio_table, addr);
+ if (!iodev) {
+ printf("couldn't find device\n");
+ return -ENODEV;
+ }
+
+ return iodev->handler(iodev->opaque, len, 0, addr - iodev->start,
+ (uint64_t *)data);
+}
+
+static int test_mem_write(void *opaque, uint64_t addr, uint8_t *data, int len)
+{
+ struct io_table_entry *iodev;
+
+#if 0
+ printf("%s: addr %"PRIx64" len %d data %"PRIx64"\n",
+ __func__, addr, len, *(uint64_t *)data);
+#endif
+
+ iodev = io_table_lookup(&mmio_table, addr);
+ if (!iodev) {
+ printf("couldn't find device\n");
+ return -ENODEV;
+ }
+
+ return iodev->handler(iodev->opaque, len, 1, addr - iodev->start,
+ (uint64_t *)data);
+}
+
+static int test_dcr_read(int vcpu, uint32_t dcrn, uint32_t *data)
+{
+ printf("%s: dcrn %04X\n", __func__, dcrn);
+ *data = 0;
+ return 0;
+}
+
+static int test_dcr_write(int vcpu, uint32_t dcrn, uint32_t data)
+{
+ printf("%s: dcrn %04X data %04X\n", __func__, dcrn, data);
+ return 0;
+}
+
+static struct kvm_callbacks test_callbacks = {
+ .mmio_read = test_mem_read,
+ .mmio_write = test_mem_write,
+ .debug = test_debug,
+ .halt = test_halt,
+ .io_window = test_io_window,
+ .try_push_interrupts = test_try_push_interrupts,
+ .post_kvm_run = test_post_kvm_run,
+ .pre_kvm_run = test_pre_kvm_run,
+ .powerpc_dcr_read = test_dcr_read,
+ .powerpc_dcr_write = test_dcr_write,
+};
+
+static unsigned long load_file(void *mem, const char *fname, int inval_icache)
+{
+ ssize_t r;
+ int fd;
+ unsigned long bytes = 0;
+
+ fd = open(fname, O_RDONLY);
+ if (fd == -1) {
+ perror("open");
+ exit(1);
+ }
+
+ while ((r = read(fd, mem, 4096)) != -1 && r != 0) {
+ mem += r;
+ bytes += r;
+ }
+
+ if (r == -1) {
+ perror("read");
+ printf("read %d bytes\n", bytes);
+ exit(1);
+ }
+
+ return bytes;
+}
+
+#define ICACHE_LINE_SIZE 32
+
+void sync_caches(void *mem, unsigned long len)
+{
+ unsigned long i;
+
+ for (i = 0; i < len; i += ICACHE_LINE_SIZE)
+ asm volatile ("dcbst %0, %1" : : "g"(mem), "r"(i));
+ asm volatile ("sync");
+ for (i = 0; i < len; i += ICACHE_LINE_SIZE)
+ asm volatile ("icbi %0, %1" : : "g"(mem), "r"(i));
+ asm volatile ("sync; isync");
+}
+
+static void init_vcpu(int n)
+{
+ sigemptyset(&ipi_sigmask);
+ sigaddset(&ipi_sigmask, IPI_SIGNAL);
+ sigprocmask(SIG_UNBLOCK, &ipi_sigmask, NULL);
+ sigprocmask(SIG_BLOCK, &ipi_sigmask, &kernel_sigmask);
+ vcpus[n].tid = gettid();
+ vcpu = n;
+ kvm_set_signal_mask(kvm, n, &kernel_sigmask);
+}
+
+static void *do_create_vcpu(void *_n)
+{
+ struct kvm_regs regs;
+ int n = (long)_n;
+
+ kvm_create_vcpu(kvm, n);
+ init_vcpu(n);
+
+ kvm_get_regs(kvm, n, ®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 <address@hidden>.\n"
+ , progname);
+}
+
+static void sig_ignore(int sig)
+{
+ write(1, "boo\n", 4);
+}
+
+int main(int argc, char **argv)
+{
+ void *vm_mem;
+ unsigned long len;
+ int i;
+ const char *sopts = "s:phm:";
+ struct option lopts[] = {
+ { "smp", 1, 0, 's' },
+ { "memory", 1, 0, 'm' },
+ { "help", 0, 0, 'h' },
+ { 0 },
+ };
+ int opt_ind, ch;
+ int nb_args;
+ char *endptr;
+
+ while ((ch = getopt_long(argc, argv, sopts, lopts, &opt_ind)) != -1) {
+ switch (ch) {
+ case 's':
+ ncpus = atoi(optarg);
+ break;
+ case 'm':
+ memory_size = strtoull(optarg, &endptr, 0);
+ switch (*endptr) {
+ case 'G': case 'g':
+ memory_size <<= 30;
+ break;
+ case '\0':
+ case 'M': case 'm':
+ memory_size <<= 20;
+ break;
+ case 'K': case 'k':
+ memory_size <<= 10;
+ break;
+ default:
+ fprintf(stderr,
+ "Unrecongized memory suffix: %c\n",
+ *endptr);
+ exit(1);
+ }
+ if (memory_size == 0) {
+ fprintf(stderr,
+ "Invalid memory size: 0\n");
+ exit(1);
+ }
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(0);
+ case '?':
+ default:
+ fprintf(stderr,
+ "Try `%s --help' for more information.\n",
+ argv[0]);
+ exit(1);
+ }
+ }
+
+ nb_args = argc - optind;
+ if (nb_args < 1 || nb_args > 2) {
+ fprintf(stderr,
+ "Incorrect number of arguments.\n"
+ "Try `%s --help' for more information.\n",
+ argv[0]);
+ exit(1);
+ }
+
+ signal(IPI_SIGNAL, sig_ignore);
+
+ vcpus = calloc(ncpus, sizeof *vcpus);
+ if (!vcpus) {
+ fprintf(stderr, "calloc failed\n");
+ return 1;
+ }
+
+ kvm = kvm_init(&test_callbacks, 0);
+ if (!kvm) {
+ fprintf(stderr, "kvm_init failed\n");
+ return 1;
+ }
+ if (kvm_create(kvm, memory_size, &vm_mem) < 0) {
+ kvm_finalize(kvm);
+ fprintf(stderr, "kvm_create failed\n");
+ return 1;
+ }
+
+ vm_mem = kvm_create_phys_mem(kvm, 0, memory_size, 0, 1);
+
+ len = load_file(vm_mem + VM_TEST_LOAD_ADDRESS, argv[optind], 1);
+ sync_caches(vm_mem + VM_TEST_LOAD_ADDRESS, len);
+
+ io_table_register(&mmio_table, 0xf0000000, 64, mmio_handler, NULL);
+
+ sem_init(&exited_sem, 0, 0);
+ for (i = 0; i < ncpus; ++i)
+ start_vcpu(i);
+ /* Wait for all vcpus to exit. */
+ for (i = 0; i < ncpus; ++i)
+ sem_wait(&exited_sem);
+
+ return 0;
+}
diff --git a/kvm-unittest/powerpc/exit.c b/kvm-unittest/powerpc/exit.c
new file mode 100644
index 0000000..804ee04
--- /dev/null
+++ b/kvm-unittest/powerpc/exit.c
@@ -0,0 +1,23 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <address@hidden>
+ */
+
+int main(void)
+{
+ return 1;
+}
diff --git a/kvm-unittest/powerpc/helloworld.c
b/kvm-unittest/powerpc/helloworld.c
new file mode 100644
index 0000000..f8630f7
--- /dev/null
+++ b/kvm-unittest/powerpc/helloworld.c
@@ -0,0 +1,27 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Deepa Srinivasan <address@hidden>
+ */
+
+#include "libcflat.h"
+
+int main()
+{
+ printf("Hello World\n");
+
+ return 1;
+}
diff --git a/kvm-unittest/x86/access.c b/kvm-unittest/x86/access.c
new file mode 100644
index 0000000..2ca325a
--- /dev/null
+++ b/kvm-unittest/x86/access.c
@@ -0,0 +1,860 @@
+
+#include "libcflat.h"
+#include "desc.h"
+#include "processor.h"
+
+#define smp_id() 0
+
+#define true 1
+#define false 0
+
+static _Bool verbose = false;
+
+typedef unsigned long pt_element_t;
+
+#define PAGE_SIZE ((pt_element_t)4096)
+#define PAGE_MASK (~(PAGE_SIZE-1))
+
+#define PT_BASE_ADDR_MASK ((pt_element_t)((((pt_element_t)1 << 40) - 1) &
PAGE_MASK))
+#define PT_PSE_BASE_ADDR_MASK (PT_BASE_ADDR_MASK & ~(1ull << 21))
+
+#define PT_PRESENT_MASK ((pt_element_t)1 << 0)
+#define PT_WRITABLE_MASK ((pt_element_t)1 << 1)
+#define PT_USER_MASK ((pt_element_t)1 << 2)
+#define PT_ACCESSED_MASK ((pt_element_t)1 << 5)
+#define PT_DIRTY_MASK ((pt_element_t)1 << 6)
+#define PT_PSE_MASK ((pt_element_t)1 << 7)
+#define PT_NX_MASK ((pt_element_t)1 << 63)
+
+#define CR0_WP_MASK (1UL << 16)
+#define CR4_SMEP_MASK (1UL << 20)
+
+#define PFERR_PRESENT_MASK (1U << 0)
+#define PFERR_WRITE_MASK (1U << 1)
+#define PFERR_USER_MASK (1U << 2)
+#define PFERR_RESERVED_MASK (1U << 3)
+#define PFERR_FETCH_MASK (1U << 4)
+
+#define MSR_EFER 0xc0000080
+#define EFER_NX_MASK (1ull << 11)
+
+#define PT_INDEX(address, level) \
+ ((address) >> (12 + ((level)-1) * 9)) & 511
+
+/*
+ * page table access check tests
+ */
+
+enum {
+ AC_PTE_PRESENT,
+ AC_PTE_WRITABLE,
+ AC_PTE_USER,
+ AC_PTE_ACCESSED,
+ AC_PTE_DIRTY,
+ AC_PTE_NX,
+ AC_PTE_BIT51,
+
+ AC_PDE_PRESENT,
+ AC_PDE_WRITABLE,
+ AC_PDE_USER,
+ AC_PDE_ACCESSED,
+ AC_PDE_DIRTY,
+ AC_PDE_PSE,
+ AC_PDE_NX,
+ AC_PDE_BIT51,
+
+ AC_ACCESS_USER,
+ AC_ACCESS_WRITE,
+ AC_ACCESS_FETCH,
+ AC_ACCESS_TWICE,
+ // AC_ACCESS_PTE,
+
+ AC_CPU_EFER_NX,
+ AC_CPU_CR0_WP,
+ AC_CPU_CR4_SMEP,
+
+ NR_AC_FLAGS
+};
+
+const char *ac_names[] = {
+ [AC_PTE_PRESENT] = "pte.p",
+ [AC_PTE_ACCESSED] = "pte.a",
+ [AC_PTE_WRITABLE] = "pte.rw",
+ [AC_PTE_USER] = "pte.user",
+ [AC_PTE_DIRTY] = "pte.d",
+ [AC_PTE_NX] = "pte.nx",
+ [AC_PTE_BIT51] = "pte.51",
+ [AC_PDE_PRESENT] = "pde.p",
+ [AC_PDE_ACCESSED] = "pde.a",
+ [AC_PDE_WRITABLE] = "pde.rw",
+ [AC_PDE_USER] = "pde.user",
+ [AC_PDE_DIRTY] = "pde.d",
+ [AC_PDE_PSE] = "pde.pse",
+ [AC_PDE_NX] = "pde.nx",
+ [AC_PDE_BIT51] = "pde.51",
+ [AC_ACCESS_WRITE] = "write",
+ [AC_ACCESS_USER] = "user",
+ [AC_ACCESS_FETCH] = "fetch",
+ [AC_ACCESS_TWICE] = "twice",
+ [AC_CPU_EFER_NX] = "efer.nx",
+ [AC_CPU_CR0_WP] = "cr0.wp",
+ [AC_CPU_CR4_SMEP] = "cr4.smep",
+};
+
+static inline void *va(pt_element_t phys)
+{
+ return (void *)phys;
+}
+
+typedef struct {
+ pt_element_t pt_pool;
+ unsigned pt_pool_size;
+ unsigned pt_pool_current;
+} ac_pool_t;
+
+typedef struct {
+ unsigned flags[NR_AC_FLAGS];
+ void *virt;
+ pt_element_t phys;
+ pt_element_t *ptep;
+ pt_element_t expected_pte;
+ pt_element_t *pdep;
+ pt_element_t expected_pde;
+ pt_element_t ignore_pde;
+ int expected_fault;
+ unsigned expected_error;
+} ac_test_t;
+
+typedef struct {
+ unsigned short limit;
+ unsigned long linear_addr;
+} __attribute__((packed)) descriptor_table_t;
+
+
+static void ac_test_show(ac_test_t *at);
+
+int write_cr4_checking(unsigned long val)
+{
+ asm volatile(ASM_TRY("1f")
+ "mov %0,%%cr4\n\t"
+ "1:": : "r" (val));
+ return exception_vector();
+}
+
+void set_cr0_wp(int wp)
+{
+ unsigned long cr0 = read_cr0();
+
+ cr0 &= ~CR0_WP_MASK;
+ if (wp)
+ cr0 |= CR0_WP_MASK;
+ write_cr0(cr0);
+}
+
+void set_cr4_smep(int smep)
+{
+ unsigned long cr4 = read_cr4();
+
+ cr4 &= ~CR4_SMEP_MASK;
+ if (smep)
+ cr4 |= CR4_SMEP_MASK;
+ write_cr4(cr4);
+}
+
+void set_efer_nx(int nx)
+{
+ unsigned long long efer;
+
+ efer = rdmsr(MSR_EFER);
+ efer &= ~EFER_NX_MASK;
+ if (nx)
+ efer |= EFER_NX_MASK;
+ wrmsr(MSR_EFER, efer);
+}
+
+static void ac_env_int(ac_pool_t *pool)
+{
+ setup_idt();
+
+ extern char page_fault, kernel_entry;
+ set_idt_entry(14, &page_fault, 0);
+ set_idt_entry(0x20, &kernel_entry, 3);
+
+ pool->pt_pool = 33 * 1024 * 1024;
+ pool->pt_pool_size = 120 * 1024 * 1024 - pool->pt_pool;
+ pool->pt_pool_current = 0;
+}
+
+void ac_test_init(ac_test_t *at, void *virt)
+{
+ wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX_MASK);
+ set_cr0_wp(1);
+ for (int i = 0; i < NR_AC_FLAGS; ++i)
+ at->flags[i] = 0;
+ at->virt = virt;
+ at->phys = 32 * 1024 * 1024;
+}
+
+int ac_test_bump_one(ac_test_t *at)
+{
+ for (int i = 0; i < NR_AC_FLAGS; ++i)
+ if (!at->flags[i]) {
+ at->flags[i] = 1;
+ return 1;
+ } else
+ at->flags[i] = 0;
+ return 0;
+}
+
+_Bool ac_test_legal(ac_test_t *at)
+{
+ /*
+ * Since we convert current page to kernel page when cr4.smep=1,
+ * we can't switch to user mode.
+ */
+ if ((at->flags[AC_ACCESS_FETCH] && at->flags[AC_ACCESS_WRITE]) ||
+ (at->flags[AC_ACCESS_USER] && at->flags[AC_CPU_CR4_SMEP]))
+ return false;
+ return true;
+}
+
+int ac_test_bump(ac_test_t *at)
+{
+ int ret;
+
+ ret = ac_test_bump_one(at);
+ while (ret && !ac_test_legal(at))
+ ret = ac_test_bump_one(at);
+ return ret;
+}
+
+pt_element_t ac_test_alloc_pt(ac_pool_t *pool)
+{
+ pt_element_t ret = pool->pt_pool + pool->pt_pool_current;
+ pool->pt_pool_current += PAGE_SIZE;
+ return ret;
+}
+
+_Bool ac_test_enough_room(ac_pool_t *pool)
+{
+ return pool->pt_pool_current + 4 * PAGE_SIZE <= pool->pt_pool_size;
+}
+
+void ac_test_reset_pt_pool(ac_pool_t *pool)
+{
+ pool->pt_pool_current = 0;
+}
+
+void ac_set_expected_status(ac_test_t *at)
+{
+ int pde_valid, pte_valid;
+
+ invlpg(at->virt);
+
+ if (at->ptep)
+ at->expected_pte = *at->ptep;
+ at->expected_pde = *at->pdep;
+ at->ignore_pde = 0;
+ at->expected_fault = 0;
+ at->expected_error = PFERR_PRESENT_MASK;
+
+ pde_valid = at->flags[AC_PDE_PRESENT]
+ && !at->flags[AC_PDE_BIT51]
+ && !(at->flags[AC_PDE_NX] && !at->flags[AC_CPU_EFER_NX]);
+ pte_valid = pde_valid
+ && at->flags[AC_PTE_PRESENT]
+ && !at->flags[AC_PTE_BIT51]
+ && !(at->flags[AC_PTE_NX] && !at->flags[AC_CPU_EFER_NX]);
+ if (at->flags[AC_ACCESS_TWICE]) {
+ if (pde_valid) {
+ at->expected_pde |= PT_ACCESSED_MASK;
+ if (pte_valid)
+ at->expected_pte |= PT_ACCESSED_MASK;
+ }
+ }
+
+ if (at->flags[AC_ACCESS_USER])
+ at->expected_error |= PFERR_USER_MASK;
+
+ if (at->flags[AC_ACCESS_WRITE])
+ at->expected_error |= PFERR_WRITE_MASK;
+
+ if (at->flags[AC_ACCESS_FETCH])
+ at->expected_error |= PFERR_FETCH_MASK;
+
+ if (!at->flags[AC_PDE_PRESENT]) {
+ at->expected_fault = 1;
+ at->expected_error &= ~PFERR_PRESENT_MASK;
+ } else if (!pde_valid) {
+ at->expected_fault = 1;
+ at->expected_error |= PFERR_RESERVED_MASK;
+ }
+
+ if (at->flags[AC_ACCESS_USER] && !at->flags[AC_PDE_USER])
+ at->expected_fault = 1;
+
+ if (at->flags[AC_ACCESS_WRITE]
+ && !at->flags[AC_PDE_WRITABLE]
+ && (at->flags[AC_CPU_CR0_WP] || at->flags[AC_ACCESS_USER]))
+ at->expected_fault = 1;
+
+ if (at->flags[AC_ACCESS_FETCH] && at->flags[AC_PDE_NX])
+ at->expected_fault = 1;
+
+ if (!at->flags[AC_PDE_ACCESSED])
+ at->ignore_pde = PT_ACCESSED_MASK;
+
+ if (!pde_valid)
+ goto fault;
+
+ if (!at->expected_fault)
+ at->expected_pde |= PT_ACCESSED_MASK;
+
+ if (at->flags[AC_PDE_PSE]) {
+ if (at->flags[AC_ACCESS_WRITE] && !at->expected_fault)
+ at->expected_pde |= PT_DIRTY_MASK;
+ if (at->flags[AC_ACCESS_FETCH] && at->flags[AC_PDE_USER]
+ && at->flags[AC_CPU_CR4_SMEP])
+ at->expected_fault = 1;
+ goto no_pte;
+ }
+
+ if (!at->flags[AC_PTE_PRESENT]) {
+ at->expected_fault = 1;
+ at->expected_error &= ~PFERR_PRESENT_MASK;
+ } else if (!pte_valid) {
+ at->expected_fault = 1;
+ at->expected_error |= PFERR_RESERVED_MASK;
+ }
+
+ if (at->flags[AC_ACCESS_USER] && !at->flags[AC_PTE_USER])
+ at->expected_fault = 1;
+
+ if (at->flags[AC_ACCESS_WRITE]
+ && !at->flags[AC_PTE_WRITABLE]
+ && (at->flags[AC_CPU_CR0_WP] || at->flags[AC_ACCESS_USER]))
+ at->expected_fault = 1;
+
+ if (at->flags[AC_ACCESS_FETCH]
+ && (at->flags[AC_PTE_NX]
+ || (at->flags[AC_CPU_CR4_SMEP]
+ && at->flags[AC_PDE_USER]
+ && at->flags[AC_PTE_USER])))
+ at->expected_fault = 1;
+
+ if (at->expected_fault)
+ goto fault;
+
+ at->expected_pte |= PT_ACCESSED_MASK;
+ if (at->flags[AC_ACCESS_WRITE])
+ at->expected_pte |= PT_DIRTY_MASK;
+
+no_pte:
+fault:
+ if (!at->expected_fault)
+ at->ignore_pde = 0;
+ if (!at->flags[AC_CPU_EFER_NX] && !at->flags[AC_CPU_CR4_SMEP])
+ at->expected_error &= ~PFERR_FETCH_MASK;
+}
+
+void __ac_setup_specific_pages(ac_test_t *at, ac_pool_t *pool, u64 pd_page,
+ u64 pt_page)
+
+{
+ unsigned long root = read_cr3();
+
+ if (!ac_test_enough_room(pool))
+ ac_test_reset_pt_pool(pool);
+
+ at->ptep = 0;
+ for (int i = 4; i >= 1 && (i >= 2 || !at->flags[AC_PDE_PSE]); --i) {
+ pt_element_t *vroot = va(root & PT_BASE_ADDR_MASK);
+ unsigned index = PT_INDEX((unsigned long)at->virt, i);
+ pt_element_t pte = 0;
+ switch (i) {
+ case 4:
+ case 3:
+ pte = pd_page ? pd_page : ac_test_alloc_pt(pool);
+ pte |= PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK;
+ break;
+ case 2:
+ if (!at->flags[AC_PDE_PSE])
+ pte = pt_page ? pt_page : ac_test_alloc_pt(pool);
+ else {
+ pte = at->phys & PT_PSE_BASE_ADDR_MASK;
+ pte |= PT_PSE_MASK;
+ }
+ if (at->flags[AC_PDE_PRESENT])
+ pte |= PT_PRESENT_MASK;
+ if (at->flags[AC_PDE_WRITABLE])
+ pte |= PT_WRITABLE_MASK;
+ if (at->flags[AC_PDE_USER])
+ pte |= PT_USER_MASK;
+ if (at->flags[AC_PDE_ACCESSED])
+ pte |= PT_ACCESSED_MASK;
+ if (at->flags[AC_PDE_DIRTY])
+ pte |= PT_DIRTY_MASK;
+ if (at->flags[AC_PDE_NX])
+ pte |= PT_NX_MASK;
+ if (at->flags[AC_PDE_BIT51])
+ pte |= 1ull << 51;
+ at->pdep = &vroot[index];
+ break;
+ case 1:
+ pte = at->phys & PT_BASE_ADDR_MASK;
+ if (at->flags[AC_PTE_PRESENT])
+ pte |= PT_PRESENT_MASK;
+ if (at->flags[AC_PTE_WRITABLE])
+ pte |= PT_WRITABLE_MASK;
+ if (at->flags[AC_PTE_USER])
+ pte |= PT_USER_MASK;
+ if (at->flags[AC_PTE_ACCESSED])
+ pte |= PT_ACCESSED_MASK;
+ if (at->flags[AC_PTE_DIRTY])
+ pte |= PT_DIRTY_MASK;
+ if (at->flags[AC_PTE_NX])
+ pte |= PT_NX_MASK;
+ if (at->flags[AC_PTE_BIT51])
+ pte |= 1ull << 51;
+ at->ptep = &vroot[index];
+ break;
+ }
+ vroot[index] = pte;
+ root = vroot[index];
+ }
+ ac_set_expected_status(at);
+}
+
+static void ac_test_setup_pte(ac_test_t *at, ac_pool_t *pool)
+{
+ __ac_setup_specific_pages(at, pool, 0, 0);
+}
+
+static void ac_setup_specific_pages(ac_test_t *at, ac_pool_t *pool,
+ u64 pd_page, u64 pt_page)
+{
+ return __ac_setup_specific_pages(at, pool, pd_page, pt_page);
+}
+
+static void dump_mapping(ac_test_t *at)
+{
+ unsigned long root = read_cr3();
+ int i;
+
+ printf("Dump mapping: address: %llx\n", at->virt);
+ for (i = 4; i >= 1 && (i >= 2 || !at->flags[AC_PDE_PSE]); --i) {
+ pt_element_t *vroot = va(root & PT_BASE_ADDR_MASK);
+ unsigned index = PT_INDEX((unsigned long)at->virt, i);
+ pt_element_t pte = vroot[index];
+
+ printf("------L%d: %llx\n", i, pte);
+ root = vroot[index];
+ }
+}
+
+static void ac_test_check(ac_test_t *at, _Bool *success_ret, _Bool cond,
+ const char *fmt, ...)
+{
+ va_list ap;
+ char buf[500];
+
+ if (!*success_ret) {
+ return;
+ }
+
+ if (!cond) {
+ return;
+ }
+
+ *success_ret = false;
+
+ if (!verbose) {
+ ac_test_show(at);
+ }
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ printf("FAIL: %s\n", buf);
+ dump_mapping(at);
+}
+
+static int pt_match(pt_element_t pte1, pt_element_t pte2, pt_element_t ignore)
+{
+ pte1 &= ~ignore;
+ pte2 &= ~ignore;
+ return pte1 == pte2;
+}
+
+int ac_test_do_access(ac_test_t *at)
+{
+ static unsigned unique = 42;
+ int fault = 0;
+ unsigned e;
+ static unsigned char user_stack[4096];
+ unsigned long rsp;
+ _Bool success = true;
+
+ ++unique;
+
+ *((unsigned char *)at->phys) = 0xc3; /* ret */
+
+ unsigned r = unique;
+ set_cr0_wp(at->flags[AC_CPU_CR0_WP]);
+ set_efer_nx(at->flags[AC_CPU_EFER_NX]);
+ if (at->flags[AC_CPU_CR4_SMEP] && !(cpuid(7).b & (1 << 7))) {
+ unsigned long cr4 = read_cr4();
+ if (write_cr4_checking(cr4 | CR4_SMEP_MASK) == GP_VECTOR)
+ goto done;
+ printf("Set SMEP in CR4 - expect #GP: FAIL!\n");
+ return 0;
+ }
+ set_cr4_smep(at->flags[AC_CPU_CR4_SMEP]);
+
+ if (at->flags[AC_ACCESS_TWICE]) {
+ asm volatile (
+ "mov $fixed2, %%rsi \n\t"
+ "mov (%[addr]), %[reg] \n\t"
+ "fixed2:"
+ : [reg]"=r"(r), [fault]"=a"(fault), "=b"(e)
+ : [addr]"r"(at->virt)
+ : "rsi"
+ );
+ fault = 0;
+ }
+
+ asm volatile ("mov $fixed1, %%rsi \n\t"
+ "mov %%rsp, %%rdx \n\t"
+ "cmp $0, %[user] \n\t"
+ "jz do_access \n\t"
+ "push %%rax; mov %[user_ds], %%ax; mov %%ax, %%ds; pop %%rax
\n\t"
+ "pushq %[user_ds] \n\t"
+ "pushq %[user_stack_top] \n\t"
+ "pushfq \n\t"
+ "pushq %[user_cs] \n\t"
+ "pushq $do_access \n\t"
+ "iretq \n"
+ "do_access: \n\t"
+ "cmp $0, %[fetch] \n\t"
+ "jnz 2f \n\t"
+ "cmp $0, %[write] \n\t"
+ "jnz 1f \n\t"
+ "mov (%[addr]), %[reg] \n\t"
+ "jmp done \n\t"
+ "1: mov %[reg], (%[addr]) \n\t"
+ "jmp done \n\t"
+ "2: call *%[addr] \n\t"
+ "done: \n"
+ "fixed1: \n"
+ "int %[kernel_entry_vector] \n\t"
+ "back_to_kernel:"
+ : [reg]"+r"(r), "+a"(fault), "=b"(e), "=&d"(rsp)
+ : [addr]"r"(at->virt),
+ [write]"r"(at->flags[AC_ACCESS_WRITE]),
+ [user]"r"(at->flags[AC_ACCESS_USER]),
+ [fetch]"r"(at->flags[AC_ACCESS_FETCH]),
+ [user_ds]"i"(32+3),
+ [user_cs]"i"(24+3),
+ [user_stack_top]"r"(user_stack + sizeof user_stack),
+ [kernel_entry_vector]"i"(0x20)
+ : "rsi");
+
+ asm volatile (".section .text.pf \n\t"
+ "page_fault: \n\t"
+ "pop %rbx \n\t"
+ "mov %rsi, (%rsp) \n\t"
+ "movl $1, %eax \n\t"
+ "iretq \n\t"
+ ".section .text");
+
+ asm volatile (".section .text.entry \n\t"
+ "kernel_entry: \n\t"
+ "mov %rdx, %rsp \n\t"
+ "jmp back_to_kernel \n\t"
+ ".section .text");
+
+ ac_test_check(at, &success, fault && !at->expected_fault,
+ "unexpected fault");
+ ac_test_check(at, &success, !fault && at->expected_fault,
+ "unexpected access");
+ ac_test_check(at, &success, fault && e != at->expected_error,
+ "error code %x expected %x", e, at->expected_error);
+ ac_test_check(at, &success, at->ptep && *at->ptep != at->expected_pte,
+ "pte %x expected %x", *at->ptep, at->expected_pte);
+ ac_test_check(at, &success,
+ !pt_match(*at->pdep, at->expected_pde, at->ignore_pde),
+ "pde %x expected %x", *at->pdep, at->expected_pde);
+
+done:
+ if (success && verbose) {
+ printf("PASS\n");
+ }
+ return success;
+}
+
+static void ac_test_show(ac_test_t *at)
+{
+ char line[5000];
+
+ *line = 0;
+ strcat(line, "test");
+ for (int i = 0; i < NR_AC_FLAGS; ++i)
+ if (at->flags[i]) {
+ strcat(line, " ");
+ strcat(line, ac_names[i]);
+ }
+ strcat(line, ": ");
+ printf("%s", line);
+}
+
+/*
+ * This test case is used to triger the bug which is fixed by
+ * commit e09e90a5 in the kvm tree
+ */
+static int corrupt_hugepage_triger(ac_pool_t *pool)
+{
+ ac_test_t at1, at2;
+
+ ac_test_init(&at1, (void *)(0x123400000000));
+ ac_test_init(&at2, (void *)(0x666600000000));
+
+ at2.flags[AC_CPU_CR0_WP] = 1;
+ at2.flags[AC_PDE_PSE] = 1;
+ at2.flags[AC_PDE_PRESENT] = 1;
+ ac_test_setup_pte(&at2, pool);
+ if (!ac_test_do_access(&at2))
+ goto err;
+
+ at1.flags[AC_CPU_CR0_WP] = 1;
+ at1.flags[AC_PDE_PSE] = 1;
+ at1.flags[AC_PDE_WRITABLE] = 1;
+ at1.flags[AC_PDE_PRESENT] = 1;
+ ac_test_setup_pte(&at1, pool);
+ if (!ac_test_do_access(&at1))
+ goto err;
+
+ at1.flags[AC_ACCESS_WRITE] = 1;
+ ac_set_expected_status(&at1);
+ if (!ac_test_do_access(&at1))
+ goto err;
+
+ at2.flags[AC_ACCESS_WRITE] = 1;
+ ac_set_expected_status(&at2);
+ if (!ac_test_do_access(&at2))
+ goto err;
+
+ return 1;
+
+err:
+ printf("corrupt_hugepage_triger test fail\n");
+ return 0;
+}
+
+/*
+ * This test case is used to triger the bug which is fixed by
+ * commit 3ddf6c06e13e in the kvm tree
+ */
+static int check_pfec_on_prefetch_pte(ac_pool_t *pool)
+{
+ ac_test_t at1, at2;
+
+ ac_test_init(&at1, (void *)(0x123406001000));
+ ac_test_init(&at2, (void *)(0x123406003000));
+
+ at1.flags[AC_PDE_PRESENT] = 1;
+ at1.flags[AC_PTE_PRESENT] = 1;
+ ac_setup_specific_pages(&at1, pool, 30 * 1024 * 1024, 30 * 1024 * 1024);
+
+ at2.flags[AC_PDE_PRESENT] = 1;
+ at2.flags[AC_PTE_NX] = 1;
+ at2.flags[AC_PTE_PRESENT] = 1;
+ ac_setup_specific_pages(&at2, pool, 30 * 1024 * 1024, 30 * 1024 * 1024);
+
+ if (!ac_test_do_access(&at1)) {
+ printf("%s: prepare fail\n", __FUNCTION__);
+ goto err;
+ }
+
+ if (!ac_test_do_access(&at2)) {
+ printf("%s: check PFEC on prefetch pte path fail\n",
+ __FUNCTION__);
+ goto err;
+ }
+
+ return 1;
+
+err:
+ return 0;
+}
+
+/*
+ * If the write-fault access is from supervisor and CR0.WP is not set on the
+ * vcpu, kvm will fix it by adjusting pte access - it sets the W bit on pte
+ * and clears U bit. This is the chance that kvm can change pte access from
+ * readonly to writable.
+ *
+ * Unfortunately, the pte access is the access of 'direct' shadow page table,
+ * means direct sp.role.access = pte_access, then we will create a writable
+ * spte entry on the readonly shadow page table. It will cause Dirty bit is
+ * not tracked when two guest ptes point to the same large page. Note, it
+ * does not have other impact except Dirty bit since cr0.wp is encoded into
+ * sp.role.
+ *
+ * Note: to trigger this bug, hugepage should be disabled on host.
+ */
+static int check_large_pte_dirty_for_nowp(ac_pool_t *pool)
+{
+ ac_test_t at1, at2;
+
+ ac_test_init(&at1, (void *)(0x123403000000));
+ ac_test_init(&at2, (void *)(0x666606000000));
+
+ at2.flags[AC_PDE_PRESENT] = 1;
+ at2.flags[AC_PDE_PSE] = 1;
+
+ ac_test_setup_pte(&at2, pool);
+ if (!ac_test_do_access(&at2)) {
+ printf("%s: read on the first mapping fail.\n", __FUNCTION__);
+ goto err;
+ }
+
+ at1.flags[AC_PDE_PRESENT] = 1;
+ at1.flags[AC_PDE_PSE] = 1;
+ at1.flags[AC_ACCESS_WRITE] = 1;
+
+ ac_test_setup_pte(&at1, pool);
+ if (!ac_test_do_access(&at1)) {
+ printf("%s: write on the second mapping fail.\n", __FUNCTION__);
+ goto err;
+ }
+
+ at2.flags[AC_ACCESS_WRITE] = 1;
+ ac_set_expected_status(&at2);
+ if (!ac_test_do_access(&at2)) {
+ printf("%s: write on the first mapping fail.\n", __FUNCTION__);
+ goto err;
+ }
+
+ return 1;
+
+err:
+ return 0;
+}
+
+static int check_smep_andnot_wp(ac_pool_t *pool)
+{
+ ac_test_t at1;
+ int err_prepare_andnot_wp, err_smep_andnot_wp;
+ extern u64 ptl2[];
+
+ ac_test_init(&at1, (void *)(0x123406001000));
+
+ at1.flags[AC_PDE_PRESENT] = 1;
+ at1.flags[AC_PTE_PRESENT] = 1;
+ at1.flags[AC_PDE_USER] = 1;
+ at1.flags[AC_PTE_USER] = 1;
+ at1.flags[AC_PDE_ACCESSED] = 1;
+ at1.flags[AC_PTE_ACCESSED] = 1;
+ at1.flags[AC_CPU_CR4_SMEP] = 1;
+ at1.flags[AC_CPU_CR0_WP] = 0;
+ at1.flags[AC_ACCESS_WRITE] = 1;
+ ac_test_setup_pte(&at1, pool);
+ ptl2[2] -= 0x4;
+
+ /*
+ * Here we write the ro user page when
+ * cr0.wp=0, then we execute it and SMEP
+ * fault should happen.
+ */
+ err_prepare_andnot_wp = ac_test_do_access(&at1);
+ if (!err_prepare_andnot_wp) {
+ printf("%s: SMEP prepare fail\n", __FUNCTION__);
+ goto clean_up;
+ }
+
+ at1.flags[AC_ACCESS_WRITE] = 0;
+ at1.flags[AC_ACCESS_FETCH] = 1;
+ ac_set_expected_status(&at1);
+ err_smep_andnot_wp = ac_test_do_access(&at1);
+
+clean_up:
+ set_cr4_smep(0);
+ ptl2[2] += 0x4;
+
+ if (!err_prepare_andnot_wp)
+ goto err;
+ if (!err_smep_andnot_wp) {
+ printf("%s: check SMEP without wp fail\n", __FUNCTION__);
+ goto err;
+ }
+ return 1;
+
+err:
+ return 0;
+}
+
+int ac_test_exec(ac_test_t *at, ac_pool_t *pool)
+{
+ int r;
+
+ if (verbose) {
+ ac_test_show(at);
+ }
+ ac_test_setup_pte(at, pool);
+ r = ac_test_do_access(at);
+ return r;
+}
+
+typedef int (*ac_test_fn)(ac_pool_t *pool);
+const ac_test_fn ac_test_cases[] =
+{
+ corrupt_hugepage_triger,
+ check_pfec_on_prefetch_pte,
+ check_large_pte_dirty_for_nowp,
+ check_smep_andnot_wp
+};
+
+int ac_test_run(void)
+{
+ ac_test_t at;
+ ac_pool_t pool;
+ int i, tests, successes;
+ extern u64 ptl2[];
+
+ printf("run\n");
+ tests = successes = 0;
+ ac_env_int(&pool);
+ ac_test_init(&at, (void *)(0x123400000000 + 16 * smp_id()));
+ do {
+ if (at.flags[AC_CPU_CR4_SMEP] && (ptl2[2] & 0x4))
+ ptl2[2] -= 0x4;
+ if (!at.flags[AC_CPU_CR4_SMEP] && !(ptl2[2] & 0x4)) {
+ set_cr4_smep(0);
+ ptl2[2] += 0x4;
+ }
+
+ ++tests;
+ successes += ac_test_exec(&at, &pool);
+ } while (ac_test_bump(&at));
+
+ set_cr4_smep(0);
+ ptl2[2] += 0x4;
+
+ for (i = 0; i < ARRAY_SIZE(ac_test_cases); i++) {
+ ++tests;
+ successes += ac_test_cases[i](&pool);
+ }
+
+ printf("\n%d tests, %d failures\n", tests, tests - successes);
+
+ return successes == tests;
+}
+
+int main()
+{
+ int r;
+
+ printf("starting test\n\n");
+ r = ac_test_run();
+ return r ? 0 : 1;
+}
diff --git a/kvm-unittest/x86/apic.c b/kvm-unittest/x86/apic.c
new file mode 100644
index 0000000..50e77fc
--- /dev/null
+++ b/kvm-unittest/x86/apic.c
@@ -0,0 +1,344 @@
+#include "libcflat.h"
+#include "apic.h"
+#include "vm.h"
+#include "smp.h"
+#include "desc.h"
+#include "isr.h"
+
+static int g_fail;
+static int g_tests;
+
+static void report(const char *msg, int pass)
+{
+ ++g_tests;
+ printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL"));
+ if (!pass)
+ ++g_fail;
+}
+
+static void test_lapic_existence(void)
+{
+ u32 lvr;
+
+ lvr = apic_read(APIC_LVR);
+ printf("apic version: %x\n", lvr);
+ report("apic existence", (u16)lvr == 0x14);
+}
+
+#define TSC_DEADLINE_TIMER_MODE (2 << 17)
+#define TSC_DEADLINE_TIMER_VECTOR 0xef
+#define MSR_IA32_TSC 0x00000010
+#define MSR_IA32_TSCDEADLINE 0x000006e0
+
+static int tdt_count;
+
+static void tsc_deadline_timer_isr(isr_regs_t *regs)
+{
+ ++tdt_count;
+}
+
+static void start_tsc_deadline_timer(void)
+{
+ handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr);
+ irq_enable();
+
+ wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC));
+ asm volatile ("nop");
+ report("tsc deadline timer", tdt_count == 1);
+}
+
+static int enable_tsc_deadline_timer(void)
+{
+ uint32_t lvtt;
+
+ if (cpuid(1).c & (1 << 24)) {
+ lvtt = TSC_DEADLINE_TIMER_MODE | TSC_DEADLINE_TIMER_VECTOR;
+ apic_write(APIC_LVTT, lvtt);
+ start_tsc_deadline_timer();
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void test_tsc_deadline_timer(void)
+{
+ if(enable_tsc_deadline_timer()) {
+ printf("tsc deadline timer enabled\n");
+ } else {
+ printf("tsc deadline timer not detected\n");
+ }
+}
+
+#define MSR_APIC_BASE 0x0000001b
+
+void test_enable_x2apic(void)
+{
+ if (enable_x2apic()) {
+ printf("x2apic enabled\n");
+ } else {
+ printf("x2apic not detected\n");
+ }
+}
+
+static void eoi(void)
+{
+ apic_write(APIC_EOI, 0);
+}
+
+static int ipi_count;
+
+static void self_ipi_isr(isr_regs_t *regs)
+{
+ ++ipi_count;
+ eoi();
+}
+
+static void test_self_ipi(void)
+{
+ int vec = 0xf1;
+
+ handle_irq(vec, self_ipi_isr);
+ irq_enable();
+ apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec,
+ 0);
+ asm volatile ("nop");
+ report("self ipi", ipi_count == 1);
+}
+
+static void set_ioapic_redir(unsigned line, unsigned vec)
+{
+ ioapic_redir_entry_t e = {
+ .vector = vec,
+ .delivery_mode = 0,
+ .trig_mode = 0,
+ };
+
+ ioapic_write_redir(line, e);
+}
+
+static void set_irq_line(unsigned line, int val)
+{
+ asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line)));
+}
+
+static void toggle_irq_line(unsigned line)
+{
+ set_irq_line(line, 1);
+ set_irq_line(line, 0);
+}
+
+static int g_isr_77;
+
+static void ioapic_isr_77(isr_regs_t *regs)
+{
+ ++g_isr_77;
+ eoi();
+}
+
+static void test_ioapic_intr(void)
+{
+ handle_irq(0x77, ioapic_isr_77);
+ set_ioapic_redir(0x0e, 0x77);
+ toggle_irq_line(0x0e);
+ asm volatile ("nop");
+ report("ioapic interrupt", g_isr_77 == 1);
+}
+
+static int g_78, g_66, g_66_after_78;
+static ulong g_66_rip, g_78_rip;
+
+static void ioapic_isr_78(isr_regs_t *regs)
+{
+ ++g_78;
+ g_78_rip = regs->rip;
+ eoi();
+}
+
+static void ioapic_isr_66(isr_regs_t *regs)
+{
+ ++g_66;
+ if (g_78)
+ ++g_66_after_78;
+ g_66_rip = regs->rip;
+ eoi();
+}
+
+static void test_ioapic_simultaneous(void)
+{
+ handle_irq(0x78, ioapic_isr_78);
+ handle_irq(0x66, ioapic_isr_66);
+ set_ioapic_redir(0x0e, 0x78);
+ set_ioapic_redir(0x0f, 0x66);
+ irq_disable();
+ toggle_irq_line(0x0f);
+ toggle_irq_line(0x0e);
+ irq_enable();
+ asm volatile ("nop");
+ report("ioapic simultaneous interrupt",
+ g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip);
+}
+
+volatile int nmi_counter_private, nmi_counter, nmi_hlt_counter,
sti_loop_active;
+
+void sti_nop(char *p)
+{
+ asm volatile (
+ ".globl post_sti \n\t"
+ "sti \n"
+ /*
+ * vmx won't exit on external interrupt if blocked-by-sti,
+ * so give it a reason to exit by accessing an unmapped page.
+ */
+ "post_sti: testb $0, %0 \n\t"
+ "nop \n\t"
+ "cli"
+ : : "m"(*p)
+ );
+ nmi_counter = nmi_counter_private;
+}
+
+static void sti_loop(void *ignore)
+{
+ unsigned k = 0;
+
+ while (sti_loop_active) {
+ sti_nop((char *)(ulong)((k++ * 4096) % (128 * 1024 * 1024)));
+ }
+}
+
+static void nmi_handler(isr_regs_t *regs)
+{
+ extern void post_sti(void);
+ ++nmi_counter_private;
+ nmi_hlt_counter += regs->rip == (ulong)post_sti;
+}
+
+static void update_cr3(void *cr3)
+{
+ write_cr3((ulong)cr3);
+}
+
+static void test_sti_nmi(void)
+{
+ unsigned old_counter;
+
+ if (cpu_count() < 2) {
+ return;
+ }
+
+ handle_irq(2, nmi_handler);
+ on_cpu(1, update_cr3, (void *)read_cr3());
+
+ sti_loop_active = 1;
+ on_cpu_async(1, sti_loop, 0);
+ while (nmi_counter < 30000) {
+ old_counter = nmi_counter;
+ apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 1);
+ while (nmi_counter == old_counter) {
+ ;
+ }
+ }
+ sti_loop_active = 0;
+ report("nmi-after-sti", nmi_hlt_counter == 0);
+}
+
+static volatile bool nmi_done, nmi_flushed;
+static volatile int nmi_received;
+static volatile int cpu0_nmi_ctr1, cpu1_nmi_ctr1;
+static volatile int cpu0_nmi_ctr2, cpu1_nmi_ctr2;
+
+static void multiple_nmi_handler(isr_regs_t *regs)
+{
+ ++nmi_received;
+}
+
+static void kick_me_nmi(void *blah)
+{
+ while (!nmi_done) {
+ ++cpu1_nmi_ctr1;
+ while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1 && !nmi_done) {
+ pause();
+ }
+ if (nmi_done) {
+ return;
+ }
+ apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
+ /* make sure the NMI has arrived by sending an IPI after it */
+ apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT
+ | 0x44, 0);
+ ++cpu1_nmi_ctr2;
+ while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2 && !nmi_done) {
+ pause();
+ }
+ }
+}
+
+static void flush_nmi(isr_regs_t *regs)
+{
+ nmi_flushed = true;
+ apic_write(APIC_EOI, 0);
+}
+
+static void test_multiple_nmi(void)
+{
+ int i;
+ bool ok = true;
+
+ if (cpu_count() < 2) {
+ return;
+ }
+
+ sti();
+ handle_irq(2, multiple_nmi_handler);
+ handle_irq(0x44, flush_nmi);
+ on_cpu_async(1, kick_me_nmi, 0);
+ for (i = 0; i < 1000000; ++i) {
+ nmi_flushed = false;
+ nmi_received = 0;
+ ++cpu0_nmi_ctr1;
+ while (cpu1_nmi_ctr1 != cpu0_nmi_ctr1) {
+ pause();
+ }
+ apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
+ while (!nmi_flushed) {
+ pause();
+ }
+ if (nmi_received != 2) {
+ ok = false;
+ break;
+ }
+ ++cpu0_nmi_ctr2;
+ while (cpu1_nmi_ctr2 != cpu0_nmi_ctr2) {
+ pause();
+ }
+ }
+ nmi_done = true;
+ report("multiple nmi", ok);
+}
+
+int main()
+{
+ setup_vm();
+ smp_init();
+ setup_idt();
+
+ test_lapic_existence();
+
+ mask_pic_interrupts();
+ enable_apic();
+ test_enable_x2apic();
+
+ test_self_ipi();
+
+ test_ioapic_intr();
+ test_ioapic_simultaneous();
+ test_sti_nmi();
+ test_multiple_nmi();
+
+ test_tsc_deadline_timer();
+
+ printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail);
+
+ return g_fail != 0;
+}
diff --git a/kvm-unittest/x86/asyncpf.c b/kvm-unittest/x86/asyncpf.c
new file mode 100644
index 0000000..95e7741
--- /dev/null
+++ b/kvm-unittest/x86/asyncpf.c
@@ -0,0 +1,113 @@
+/*
+ * Async PF test. For the test to actually do anything it needs to be started
+ * in memory cgroup with 512M of memory and with more then 1G memory provided
+ * to the guest.
+ *
+ * To create cgroup do as root:
+ * mkdir /dev/cgroup
+ * mount -t cgroup none -omemory /dev/cgroup
+ * chmod a+rxw /dev/cgroup/
+ *
+ * From a shell you will start qemu from:
+ * mkdir /dev/cgroup/1
+ * echo $$ > /dev/cgroup/1/tasks
+ * echo 512M > /dev/cgroup/1/memory.limit_in_bytes
+ *
+ */
+#include "x86/msr.h"
+#include "x86/processor.h"
+#include "x86/apic-defs.h"
+#include "x86/apic.h"
+#include "x86/desc.h"
+#include "x86/isr.h"
+#include "x86/vm.h"
+
+#include "libcflat.h"
+#include <stdint.h>
+
+#define KVM_PV_REASON_PAGE_NOT_PRESENT 1
+#define KVM_PV_REASON_PAGE_READY 2
+
+#define MSR_KVM_ASYNC_PF_EN 0x4b564d02
+
+#define KVM_ASYNC_PF_ENABLED (1 << 0)
+#define KVM_ASYNC_PF_SEND_ALWAYS (1 << 1)
+
+volatile uint32_t apf_reason __attribute__((aligned(64)));
+char *buf;
+volatile uint64_t i;
+volatile uint64_t phys;
+bool fail;
+
+static inline uint32_t get_apf_reason(void)
+{
+ uint32_t r = apf_reason;
+ apf_reason = 0;
+ return r;
+}
+
+static void pf_isr(struct ex_regs *r)
+{
+ void* virt = (void*)((ulong)(buf+i) & ~(PAGE_SIZE-1));
+ uint32_t reason = get_apf_reason();
+
+ switch (reason) {
+ case 0:
+ printf("unexpected #PF at %p\n", read_cr2());
+ fail = true;
+ break;
+ case KVM_PV_REASON_PAGE_NOT_PRESENT:
+ phys = virt_to_phys_cr3(virt);
+ install_pte(phys_to_virt(read_cr3()), 1, virt, phys, 0);
+ write_cr3(read_cr3());
+ printf("Got not present #PF token %x virt addr %p phys
addr %p\n", read_cr2(), virt, phys);
+ while(phys) {
+ safe_halt(); /* enables irq */
+ irq_disable();
+ }
+ break;
+ case KVM_PV_REASON_PAGE_READY:
+ printf("Got present #PF token %x\n", read_cr2());
+ if ((uint32_t)read_cr2() == ~0)
+ break;
+ install_pte(phys_to_virt(read_cr3()), 1, virt, phys |
PTE_PRESENT | PTE_WRITE, 0);
+ write_cr3(read_cr3());
+ phys = 0;
+ break;
+ default:
+ printf("unexpected async pf reason %d\n", reason);
+ fail = true;
+ break;
+ }
+}
+
+#define MEM 1ull*1024*1024*1024
+
+int main(int ac, char **av)
+{
+ int loop = 2;
+
+ setup_vm();
+ setup_idt();
+ setup_gdt();
+ printf("install handler\n");
+ handle_exception(14, pf_isr);
+ apf_reason = 0;
+ printf("enable async pf\n");
+ wrmsr(MSR_KVM_ASYNC_PF_EN, virt_to_phys((void*)&apf_reason) |
+ KVM_ASYNC_PF_SEND_ALWAYS | KVM_ASYNC_PF_ENABLED);
+ printf("alloc memory\n");
+ buf = vmalloc(MEM);
+ irq_enable();
+ while(loop--) {
+ printf("start loop\n");
+ /* access a lot of memory to make host swap it out */
+ for (i=0; i < MEM; i+=4096)
+ buf[i] = 1;
+ printf("end loop\n");
+ }
+ irq_disable();
+
+ printf("%s\n", fail ? "FAIL" : "PASS");
+ return fail;
+}
diff --git a/kvm-unittest/x86/emulator.c b/kvm-unittest/x86/emulator.c
new file mode 100644
index 0000000..68d2b93
--- /dev/null
+++ b/kvm-unittest/x86/emulator.c
@@ -0,0 +1,1024 @@
+#include "ioram.h"
+#include "vm.h"
+#include "libcflat.h"
+#include "desc.h"
+#include "types.h"
+
+#define memset __builtin_memset
+#define TESTDEV_IO_PORT 0xe0
+
+int fails, tests;
+
+static int exceptions;
+
+struct regs {
+ u64 rax, rbx, rcx, rdx;
+ u64 rsi, rdi, rsp, rbp;
+ u64 r8, r9, r10, r11;
+ u64 r12, r13, r14, r15;
+ u64 rip, rflags;
+};
+struct regs inregs, outregs, save;
+
+struct insn_desc {
+ u64 ptr;
+ size_t len;
+};
+
+void report(const char *name, int result)
+{
+ ++tests;
+ if (result)
+ printf("PASS: %s\n", name);
+ else {
+ printf("FAIL: %s\n", name);
+ ++fails;
+ }
+}
+
+static char st1[] = "abcdefghijklmnop";
+
+void test_stringio()
+{
+ unsigned char r = 0;
+ asm volatile("cld \n\t"
+ "movw %0, %%dx \n\t"
+ "rep outsb \n\t"
+ : : "i"((short)TESTDEV_IO_PORT),
+ "S"(st1), "c"(sizeof(st1) - 1));
+ asm volatile("inb %1, %0\n\t" : "=a"(r) : "i"((short)TESTDEV_IO_PORT));
+ report("outsb up", r == st1[sizeof(st1) - 2]); /* last char */
+
+ asm volatile("std \n\t"
+ "movw %0, %%dx \n\t"
+ "rep outsb \n\t"
+ : : "i"((short)TESTDEV_IO_PORT),
+ "S"(st1 + sizeof(st1) - 2), "c"(sizeof(st1) - 1));
+ asm volatile("cld \n\t" : : );
+ asm volatile("in %1, %0\n\t" : "=a"(r) : "i"((short)TESTDEV_IO_PORT));
+ report("outsb down", r == st1[0]);
+}
+
+void test_cmps_one(unsigned char *m1, unsigned char *m3)
+{
+ void *rsi, *rdi;
+ long rcx, tmp;
+
+ rsi = m1; rdi = m3; rcx = 30;
+ asm volatile("xor %[tmp], %[tmp] \n\t"
+ "repe/cmpsb"
+ : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+ : : "cc");
+ report("repe/cmpsb (1)", rcx == 0 && rsi == m1 + 30 && rdi == m3 + 30);
+
+ rsi = m1; rdi = m3; rcx = 30;
+ asm volatile("or $1, %[tmp]\n\t" // clear ZF
+ "repe/cmpsb"
+ : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+ : : "cc");
+ report("repe/cmpsb (1.zf)", rcx == 0 && rsi == m1 + 30 && rdi == m3 +
30);
+
+ rsi = m1; rdi = m3; rcx = 15;
+ asm volatile("xor %[tmp], %[tmp] \n\t"
+ "repe/cmpsw"
+ : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+ : : "cc");
+ report("repe/cmpsw (1)", rcx == 0 && rsi == m1 + 30 && rdi == m3 + 30);
+
+ rsi = m1; rdi = m3; rcx = 7;
+ asm volatile("xor %[tmp], %[tmp] \n\t"
+ "repe/cmpsl"
+ : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+ : : "cc");
+ report("repe/cmpll (1)", rcx == 0 && rsi == m1 + 28 && rdi == m3 + 28);
+
+ rsi = m1; rdi = m3; rcx = 4;
+ asm volatile("xor %[tmp], %[tmp] \n\t"
+ "repe/cmpsq"
+ : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+ : : "cc");
+ report("repe/cmpsq (1)", rcx == 0 && rsi == m1 + 32 && rdi == m3 + 32);
+
+ rsi = m1; rdi = m3; rcx = 130;
+ asm volatile("xor %[tmp], %[tmp] \n\t"
+ "repe/cmpsb"
+ : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+ : : "cc");
+ report("repe/cmpsb (2)",
+ rcx == 29 && rsi == m1 + 101 && rdi == m3 + 101);
+
+ rsi = m1; rdi = m3; rcx = 65;
+ asm volatile("xor %[tmp], %[tmp] \n\t"
+ "repe/cmpsw"
+ : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+ : : "cc");
+ report("repe/cmpsw (2)",
+ rcx == 14 && rsi == m1 + 102 && rdi == m3 + 102);
+
+ rsi = m1; rdi = m3; rcx = 32;
+ asm volatile("xor %[tmp], %[tmp] \n\t"
+ "repe/cmpsl"
+ : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+ : : "cc");
+ report("repe/cmpll (2)",
+ rcx == 6 && rsi == m1 + 104 && rdi == m3 + 104);
+
+ rsi = m1; rdi = m3; rcx = 16;
+ asm volatile("xor %[tmp], %[tmp] \n\t"
+ "repe/cmpsq"
+ : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp)
+ : : "cc");
+ report("repe/cmpsq (2)",
+ rcx == 3 && rsi == m1 + 104 && rdi == m3 + 104);
+
+}
+
+void test_cmps(void *mem)
+{
+ unsigned char *m1 = mem, *m2 = mem + 1024;
+ unsigned char m3[1024];
+
+ for (int i = 0; i < 100; ++i)
+ m1[i] = m2[i] = m3[i] = i;
+ for (int i = 100; i < 200; ++i)
+ m1[i] = (m3[i] = m2[i] = i) + 1;
+ test_cmps_one(m1, m3);
+ test_cmps_one(m1, m2);
+}
+
+void test_scas(void *mem)
+{
+ bool z;
+ void *di;
+
+ *(ulong *)mem = 0x77665544332211;
+
+ di = mem;
+ asm ("scasb; setz %0" : "=rm"(z), "+D"(di) : "a"(0xff11));
+ report("scasb match", di == mem + 1 && z);
+
+ di = mem;
+ asm ("scasb; setz %0" : "=rm"(z), "+D"(di) : "a"(0xff54));
+ report("scasb mismatch", di == mem + 1 && !z);
+
+ di = mem;
+ asm ("scasw; setz %0" : "=rm"(z), "+D"(di) : "a"(0xff2211));
+ report("scasw match", di == mem + 2 && z);
+
+ di = mem;
+ asm ("scasw; setz %0" : "=rm"(z), "+D"(di) : "a"(0xffdd11));
+ report("scasw mismatch", di == mem + 2 && !z);
+
+ di = mem;
+ asm ("scasl; setz %0" : "=rm"(z), "+D"(di) : "a"(0xff44332211ul));
+ report("scasd match", di == mem + 4 && z);
+
+ di = mem;
+ asm ("scasl; setz %0" : "=rm"(z), "+D"(di) : "a"(0x45332211));
+ report("scasd mismatch", di == mem + 4 && !z);
+
+ di = mem;
+ asm ("scasq; setz %0" : "=rm"(z), "+D"(di) : "a"(0x77665544332211ul));
+ report("scasq match", di == mem + 8 && z);
+
+ di = mem;
+ asm ("scasq; setz %0" : "=rm"(z), "+D"(di) : "a"(3));
+ report("scasq mismatch", di == mem + 8 && !z);
+}
+
+void test_cr8(void)
+{
+ unsigned long src, dst;
+
+ dst = 777;
+ src = 3;
+ asm volatile("mov %[src], %%cr8; mov %%cr8, %[dst]"
+ : [dst]"+r"(dst), [src]"+r"(src));
+ report("mov %cr8", dst == 3 && src == 3);
+}
+
+void test_push(void *mem)
+{
+ unsigned long tmp;
+ unsigned long *stack_top = mem + 4096;
+ unsigned long *new_stack_top;
+ unsigned long memw = 0x123456789abcdeful;
+
+ memset(mem, 0x55, (void *)stack_top - mem);
+
+ asm volatile("mov %%rsp, %[tmp] \n\t"
+ "mov %[stack_top], %%rsp \n\t"
+ "pushq $-7 \n\t"
+ "pushq %[reg] \n\t"
+ "pushq (%[mem]) \n\t"
+ "pushq $-7070707 \n\t"
+ "mov %%rsp, %[new_stack_top] \n\t"
+ "mov %[tmp], %%rsp"
+ : [tmp]"=&r"(tmp), [new_stack_top]"=r"(new_stack_top)
+ : [stack_top]"r"(stack_top),
+ [reg]"r"(-17l), [mem]"r"(&memw)
+ : "memory");
+
+ report("push $imm8", stack_top[-1] == -7ul);
+ report("push %reg", stack_top[-2] == -17ul);
+ report("push mem", stack_top[-3] == 0x123456789abcdeful);
+ report("push $imm", stack_top[-4] == -7070707);
+}
+
+void test_pop(void *mem)
+{
+ unsigned long tmp, tmp3, rsp, rbp;
+ unsigned long *stack_top = mem + 4096;
+ unsigned long memw = 0x123456789abcdeful;
+ static unsigned long tmp2;
+
+ memset(mem, 0x55, (void *)stack_top - mem);
+
+ asm volatile("pushq %[val] \n\t"
+ "popq (%[mem])"
+ : : [val]"m"(memw), [mem]"r"(mem) : "memory");
+ report("pop mem", *(unsigned long *)mem == memw);
+
+ memw = 7 - memw;
+ asm volatile("mov %%rsp, %[tmp] \n\t"
+ "mov %[stack_top], %%rsp \n\t"
+ "pushq %[val] \n\t"
+ "popq %[tmp2] \n\t"
+ "mov %[tmp], %%rsp"
+ : [tmp]"=&r"(tmp), [tmp2]"=m"(tmp2)
+ : [val]"r"(memw), [stack_top]"r"(stack_top)
+ : "memory");
+ report("pop mem (2)", tmp2 == memw);
+
+ memw = 129443 - memw;
+ asm volatile("mov %%rsp, %[tmp] \n\t"
+ "mov %[stack_top], %%rsp \n\t"
+ "pushq %[val] \n\t"
+ "popq %[tmp2] \n\t"
+ "mov %[tmp], %%rsp"
+ : [tmp]"=&r"(tmp), [tmp2]"=r"(tmp2)
+ : [val]"r"(memw), [stack_top]"r"(stack_top)
+ : "memory");
+ report("pop reg", tmp2 == memw);
+
+ asm volatile("mov %%rsp, %[tmp] \n\t"
+ "mov %[stack_top], %%rsp \n\t"
+ "push $1f \n\t"
+ "ret \n\t"
+ "2: jmp 2b \n\t"
+ "1: mov %[tmp], %%rsp"
+ : [tmp]"=&r"(tmp) : [stack_top]"r"(stack_top)
+ : "memory");
+ report("ret", 1);
+
+ stack_top[-1] = 0x778899;
+ asm volatile("mov %%rsp, %[tmp] \n\t"
+ "mov %%rbp, %[tmp3] \n\t"
+ "mov %[stack_top], %%rbp \n\t"
+ "leave \n\t"
+ "xchg %%rsp, %[tmp] \n\t"
+ "xchg %%rbp, %[tmp3]"
+ : [tmp]"=&r"(tmp), [tmp3]"=&r"(tmp3) :
[stack_top]"r"(stack_top-1)
+ : "memory");
+ report("leave", tmp == (ulong)stack_top && tmp3 == 0x778899);
+
+ rbp = 0xaa55aa55bb66bb66ULL;
+ rsp = (unsigned long)stack_top;
+ asm volatile("xchg %%rsp, %[rsp] \n\t"
+ "xchg %%rbp, %[rbp] \n\t"
+ "enter $0x1238, $0 \n\t"
+ "xchg %%rsp, %[rsp] \n\t"
+ "xchg %%rbp, %[rbp]"
+ : [rsp]"+a"(rsp), [rbp]"+b"(rbp) : : "memory");
+ report("enter",
+ rsp == (unsigned long)stack_top - 8 - 0x1238
+ && rbp == (unsigned long)stack_top - 8
+ && stack_top[-1] == 0xaa55aa55bb66bb66ULL);
+}
+
+void test_ljmp(void *mem)
+{
+ unsigned char *m = mem;
+ volatile int res = 1;
+
+ *(unsigned long**)m = &&jmpf;
+ asm volatile ("data16/mov %%cs, %0":"=m"(*(m + sizeof(unsigned long))));
+ asm volatile ("rex64/ljmp *%0"::"m"(*m));
+ res = 0;
+jmpf:
+ report("ljmp", res);
+}
+
+void test_incdecnotneg(void *mem)
+{
+ unsigned long *m = mem, v = 1234;
+ unsigned char *mb = mem, vb = 66;
+
+ *m = 0;
+
+ asm volatile ("incl %0":"+m"(*m));
+ report("incl", *m == 1);
+ asm volatile ("decl %0":"+m"(*m));
+ report("decl", *m == 0);
+ asm volatile ("incb %0":"+m"(*m));
+ report("incb", *m == 1);
+ asm volatile ("decb %0":"+m"(*m));
+ report("decb", *m == 0);
+
+ asm volatile ("lock incl %0":"+m"(*m));
+ report("lock incl", *m == 1);
+ asm volatile ("lock decl %0":"+m"(*m));
+ report("lock decl", *m == 0);
+ asm volatile ("lock incb %0":"+m"(*m));
+ report("lock incb", *m == 1);
+ asm volatile ("lock decb %0":"+m"(*m));
+ report("lock decb", *m == 0);
+
+ *m = v;
+
+ asm ("lock negq %0" : "+m"(*m)); v = -v;
+ report("lock negl", *m == v);
+ asm ("lock notq %0" : "+m"(*m)); v = ~v;
+ report("lock notl", *m == v);
+
+ *mb = vb;
+
+ asm ("lock negb %0" : "+m"(*mb)); vb = -vb;
+ report("lock negb", *mb == vb);
+ asm ("lock notb %0" : "+m"(*mb)); vb = ~vb;
+ report("lock notb", *mb == vb);
+}
+
+void test_smsw(void)
+{
+ char mem[16];
+ unsigned short msw, msw_orig, *pmsw;
+ int i, zero;
+
+ msw_orig = read_cr0();
+
+ asm("smsw %0" : "=r"(msw));
+ report("smsw (1)", msw == msw_orig);
+
+ memset(mem, 0, 16);
+ pmsw = (void *)mem;
+ asm("smsw %0" : "=m"(pmsw[4]));
+ zero = 1;
+ for (i = 0; i < 8; ++i)
+ if (i != 4 && pmsw[i])
+ zero = 0;
+ report("smsw (2)", msw == pmsw[4] && zero);
+}
+
+void test_lmsw(void)
+{
+ char mem[16];
+ unsigned short msw, *pmsw;
+ unsigned long cr0;
+
+ cr0 = read_cr0();
+
+ msw = cr0 ^ 8;
+ asm("lmsw %0" : : "r"(msw));
+ printf("before %lx after %lx\n", cr0, read_cr0());
+ report("lmsw (1)", (cr0 ^ read_cr0()) == 8);
+
+ pmsw = (void *)mem;
+ *pmsw = cr0;
+ asm("lmsw %0" : : "m"(*pmsw));
+ printf("before %lx after %lx\n", cr0, read_cr0());
+ report("lmsw (2)", cr0 == read_cr0());
+
+ /* lmsw can't clear cr0.pe */
+ msw = (cr0 & ~1ul) ^ 4; /* change EM to force trap */
+ asm("lmsw %0" : : "r"(msw));
+ report("lmsw (3)", (cr0 ^ read_cr0()) == 4 && (cr0 & 1));
+
+ /* back to normal */
+ msw = cr0;
+ asm("lmsw %0" : : "r"(msw));
+}
+
+void test_xchg(void *mem)
+{
+ unsigned long *memq = mem;
+ unsigned long rax;
+
+ asm volatile("mov $0x123456789abcdef, %%rax\n\t"
+ "mov %%rax, (%[memq])\n\t"
+ "mov $0xfedcba9876543210, %%rax\n\t"
+ "xchg %%al, (%[memq])\n\t"
+ "mov %%rax, %[rax]\n\t"
+ : [rax]"=r"(rax)
+ : [memq]"r"(memq)
+ : "memory");
+ report("xchg reg, r/m (1)",
+ rax == 0xfedcba98765432ef && *memq == 0x123456789abcd10);
+
+ asm volatile("mov $0x123456789abcdef, %%rax\n\t"
+ "mov %%rax, (%[memq])\n\t"
+ "mov $0xfedcba9876543210, %%rax\n\t"
+ "xchg %%ax, (%[memq])\n\t"
+ "mov %%rax, %[rax]\n\t"
+ : [rax]"=r"(rax)
+ : [memq]"r"(memq)
+ : "memory");
+ report("xchg reg, r/m (2)",
+ rax == 0xfedcba987654cdef && *memq == 0x123456789ab3210);
+
+ asm volatile("mov $0x123456789abcdef, %%rax\n\t"
+ "mov %%rax, (%[memq])\n\t"
+ "mov $0xfedcba9876543210, %%rax\n\t"
+ "xchg %%eax, (%[memq])\n\t"
+ "mov %%rax, %[rax]\n\t"
+ : [rax]"=r"(rax)
+ : [memq]"r"(memq)
+ : "memory");
+ report("xchg reg, r/m (3)",
+ rax == 0x89abcdef && *memq == 0x123456776543210);
+
+ asm volatile("mov $0x123456789abcdef, %%rax\n\t"
+ "mov %%rax, (%[memq])\n\t"
+ "mov $0xfedcba9876543210, %%rax\n\t"
+ "xchg %%rax, (%[memq])\n\t"
+ "mov %%rax, %[rax]\n\t"
+ : [rax]"=r"(rax)
+ : [memq]"r"(memq)
+ : "memory");
+ report("xchg reg, r/m (4)",
+ rax == 0x123456789abcdef && *memq == 0xfedcba9876543210);
+}
+
+void test_xadd(void *mem)
+{
+ unsigned long *memq = mem;
+ unsigned long rax;
+
+ asm volatile("mov $0x123456789abcdef, %%rax\n\t"
+ "mov %%rax, (%[memq])\n\t"
+ "mov $0xfedcba9876543210, %%rax\n\t"
+ "xadd %%al, (%[memq])\n\t"
+ "mov %%rax, %[rax]\n\t"
+ : [rax]"=r"(rax)
+ : [memq]"r"(memq)
+ : "memory");
+ report("xadd reg, r/m (1)",
+ rax == 0xfedcba98765432ef && *memq == 0x123456789abcdff);
+
+ asm volatile("mov $0x123456789abcdef, %%rax\n\t"
+ "mov %%rax, (%[memq])\n\t"
+ "mov $0xfedcba9876543210, %%rax\n\t"
+ "xadd %%ax, (%[memq])\n\t"
+ "mov %%rax, %[rax]\n\t"
+ : [rax]"=r"(rax)
+ : [memq]"r"(memq)
+ : "memory");
+ report("xadd reg, r/m (2)",
+ rax == 0xfedcba987654cdef && *memq == 0x123456789abffff);
+
+ asm volatile("mov $0x123456789abcdef, %%rax\n\t"
+ "mov %%rax, (%[memq])\n\t"
+ "mov $0xfedcba9876543210, %%rax\n\t"
+ "xadd %%eax, (%[memq])\n\t"
+ "mov %%rax, %[rax]\n\t"
+ : [rax]"=r"(rax)
+ : [memq]"r"(memq)
+ : "memory");
+ report("xadd reg, r/m (3)",
+ rax == 0x89abcdef && *memq == 0x1234567ffffffff);
+
+ asm volatile("mov $0x123456789abcdef, %%rax\n\t"
+ "mov %%rax, (%[memq])\n\t"
+ "mov $0xfedcba9876543210, %%rax\n\t"
+ "xadd %%rax, (%[memq])\n\t"
+ "mov %%rax, %[rax]\n\t"
+ : [rax]"=r"(rax)
+ : [memq]"r"(memq)
+ : "memory");
+ report("xadd reg, r/m (4)",
+ rax == 0x123456789abcdef && *memq == 0xffffffffffffffff);
+}
+
+void test_btc(void *mem)
+{
+ unsigned int *a = mem;
+
+ memset(mem, 0, 3 * sizeof(unsigned int));
+
+ asm ("btcl $32, %0" :: "m"(a[0]) : "memory");
+ asm ("btcl $1, %0" :: "m"(a[1]) : "memory");
+ asm ("btcl %1, %0" :: "m"(a[0]), "r"(66) : "memory");
+ report("btcl imm8, r/m", a[0] == 1 && a[1] == 2 && a[2] == 4);
+
+ asm ("btcl %1, %0" :: "m"(a[3]), "r"(-1) : "memory");
+ report("btcl reg, r/m", a[0] == 1 && a[1] == 2 && a[2] == 0x80000004);
+}
+
+void test_bsfbsr(void *mem)
+{
+ unsigned long rax, *memq = mem;
+ unsigned eax, *meml = mem;
+ unsigned short ax, *memw = mem;
+ unsigned char z;
+
+ *memw = 0xc000;
+ asm("bsfw %[mem], %[a]" : [a]"=a"(ax) : [mem]"m"(*memw));
+ report("bsfw r/m, reg", ax == 14);
+
+ *meml = 0xc0000000;
+ asm("bsfl %[mem], %[a]" : [a]"=a"(eax) : [mem]"m"(*meml));
+ report("bsfl r/m, reg", eax == 30);
+
+ *memq = 0xc00000000000;
+ asm("bsfq %[mem], %[a]" : [a]"=a"(rax) : [mem]"m"(*memq));
+ report("bsfq r/m, reg", rax == 46);
+
+ *memq = 0;
+ asm("bsfq %[mem], %[a]; setz %[z]"
+ : [a]"=a"(rax), [z]"=rm"(z) : [mem]"m"(*memq));
+ report("bsfq r/m, reg", z == 1);
+
+ *memw = 0xc000;
+ asm("bsrw %[mem], %[a]" : [a]"=a"(ax) : [mem]"m"(*memw));
+ report("bsrw r/m, reg", ax == 15);
+
+ *meml = 0xc0000000;
+ asm("bsrl %[mem], %[a]" : [a]"=a"(eax) : [mem]"m"(*meml));
+ report("bsrl r/m, reg", eax == 31);
+
+ *memq = 0xc00000000000;
+ asm("bsrq %[mem], %[a]" : [a]"=a"(rax) : [mem]"m"(*memq));
+ report("bsrq r/m, reg", rax == 47);
+
+ *memq = 0;
+ asm("bsrq %[mem], %[a]; setz %[z]"
+ : [a]"=a"(rax), [z]"=rm"(z) : [mem]"m"(*memq));
+ report("bsrq r/m, reg", z == 1);
+}
+
+static void test_imul(ulong *mem)
+{
+ ulong a;
+
+ *mem = 51; a = 0x1234567812345678UL;
+ asm ("imulw %1, %%ax" : "+a"(a) : "m"(*mem));
+ report("imul ax, mem", a == 0x12345678123439e8);
+
+ *mem = 51; a = 0x1234567812345678UL;
+ asm ("imull %1, %%eax" : "+a"(a) : "m"(*mem));
+ report("imul eax, mem", a == 0xa06d39e8);
+
+ *mem = 51; a = 0x1234567812345678UL;
+ asm ("imulq %1, %%rax" : "+a"(a) : "m"(*mem));
+ report("imul rax, mem", a == 0xA06D39EBA06D39E8UL);
+
+ *mem = 0x1234567812345678UL; a = 0x8765432187654321L;
+ asm ("imulw $51, %1, %%ax" : "+a"(a) : "m"(*mem));
+ report("imul ax, mem, imm8", a == 0x87654321876539e8);
+
+ *mem = 0x1234567812345678UL;
+ asm ("imull $51, %1, %%eax" : "+a"(a) : "m"(*mem));
+ report("imul eax, mem, imm8", a == 0xa06d39e8);
+
+ *mem = 0x1234567812345678UL;
+ asm ("imulq $51, %1, %%rax" : "+a"(a) : "m"(*mem));
+ report("imul rax, mem, imm8", a == 0xA06D39EBA06D39E8UL);
+
+ *mem = 0x1234567812345678UL; a = 0x8765432187654321L;
+ asm ("imulw $311, %1, %%ax" : "+a"(a) : "m"(*mem));
+ report("imul ax, mem, imm", a == 0x8765432187650bc8);
+
+ *mem = 0x1234567812345678UL;
+ asm ("imull $311, %1, %%eax" : "+a"(a) : "m"(*mem));
+ report("imul eax, mem, imm", a == 0x1d950bc8);
+
+ *mem = 0x1234567812345678UL;
+ asm ("imulq $311, %1, %%rax" : "+a"(a) : "m"(*mem));
+ report("imul rax, mem, imm", a == 0x1D950BDE1D950BC8L);
+}
+
+static void test_muldiv(long *mem)
+{
+ long a, d, aa, dd;
+ u8 ex = 1;
+
+ *mem = 0; a = 1; d = 2;
+ asm (ASM_TRY("1f") "divq %3; movb $0, %2; 1:"
+ : "+a"(a), "+d"(d), "+q"(ex) : "m"(*mem));
+ report("divq (fault)", a == 1 && d == 2 && ex);
+
+ *mem = 987654321098765UL; a = 123456789012345UL; d = 123456789012345UL;
+ asm (ASM_TRY("1f") "divq %3; movb $0, %2; 1:"
+ : "+a"(a), "+d"(d), "+q"(ex) : "m"(*mem));
+ report("divq (1)",
+ a == 0x1ffffffb1b963b33ul && d == 0x273ba4384ede2ul && !ex);
+ aa = 0x1111111111111111; dd = 0x2222222222222222;
+ *mem = 0x3333333333333333; a = aa; d = dd;
+ asm("mulb %2" : "+a"(a), "+d"(d) : "m"(*mem));
+ report("mulb mem", a == 0x1111111111110363 && d == dd);
+ *mem = 0x3333333333333333; a = aa; d = dd;
+ asm("mulw %2" : "+a"(a), "+d"(d) : "m"(*mem));
+ report("mulw mem", a == 0x111111111111c963 && d == 0x2222222222220369);
+ *mem = 0x3333333333333333; a = aa; d = dd;
+ asm("mull %2" : "+a"(a), "+d"(d) : "m"(*mem));
+ report("mull mem", a == 0x962fc963 && d == 0x369d036);
+ *mem = 0x3333333333333333; a = aa; d = dd;
+ asm("mulq %2" : "+a"(a), "+d"(d) : "m"(*mem));
+ report("mulq mem", a == 0x2fc962fc962fc963 && d == 0x369d0369d0369d0);
+}
+
+typedef unsigned __attribute__((vector_size(16))) sse128;
+
+typedef union {
+ sse128 sse;
+ unsigned u[4];
+} sse_union;
+
+static bool sseeq(sse_union *v1, sse_union *v2)
+{
+ bool ok = true;
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ ok &= v1->u[i] == v2->u[i];
+ }
+
+ return ok;
+}
+
+static void test_sse(sse_union *mem)
+{
+ sse_union v;
+
+ write_cr0(read_cr0() & ~6); /* EM, TS */
+ write_cr4(read_cr4() | 0x200); /* OSFXSR */
+ v.u[0] = 1; v.u[1] = 2; v.u[2] = 3; v.u[3] = 4;
+ asm("movdqu %1, %0" : "=m"(*mem) : "x"(v.sse));
+ report("movdqu (read)", sseeq(&v, mem));
+ mem->u[0] = 5; mem->u[1] = 6; mem->u[2] = 7; mem->u[3] = 8;
+ asm("movdqu %1, %0" : "=x"(v.sse) : "m"(*mem));
+ report("movdqu (write)", sseeq(mem, &v));
+}
+
+static void test_mmx(uint64_t *mem)
+{
+ uint64_t v;
+
+ write_cr0(read_cr0() & ~6); /* EM, TS */
+ asm volatile("fninit");
+ v = 0x0102030405060708ULL;
+ asm("movq %1, %0" : "=m"(*mem) : "y"(v));
+ report("movq (mmx, read)", v == *mem);
+ *mem = 0x8070605040302010ull;
+ asm("movq %1, %0" : "=y"(v) : "m"(*mem));
+ report("movq (mmx, write)", v == *mem);
+}
+
+static void test_rip_relative(unsigned *mem, char *insn_ram)
+{
+ /* movb $1, mem+2(%rip) */
+ insn_ram[0] = 0xc6;
+ insn_ram[1] = 0x05;
+ *(unsigned *)&insn_ram[2] = 2 + (char *)mem - (insn_ram + 7);
+ insn_ram[6] = 0x01;
+ /* ret */
+ insn_ram[7] = 0xc3;
+
+ *mem = 0;
+ asm("callq *%1" : "+m"(*mem) : "r"(insn_ram));
+ report("movb $imm, 0(%rip)", *mem == 0x10000);
+}
+
+static void test_shld_shrd(u32 *mem)
+{
+ *mem = 0x12345678;
+ asm("shld %2, %1, %0" : "+m"(*mem) : "r"(0xaaaaaaaaU), "c"((u8)3));
+ report("shld (cl)", *mem == ((0x12345678 << 3) | 5));
+ *mem = 0x12345678;
+ asm("shrd %2, %1, %0" : "+m"(*mem) : "r"(0x55555555U), "c"((u8)3));
+ report("shrd (cl)", *mem == ((0x12345678 >> 3) | (5u << 29)));
+}
+
+#define INSN_XCHG_ALL \
+ "xchg %rax, 0+save \n\t" \
+ "xchg %rbx, 8+save \n\t" \
+ "xchg %rcx, 16+save \n\t" \
+ "xchg %rdx, 24+save \n\t" \
+ "xchg %rsi, 32+save \n\t" \
+ "xchg %rdi, 40+save \n\t" \
+ "xchg %rsp, 48+save \n\t" \
+ "xchg %rbp, 56+save \n\t" \
+ "xchg %r8, 64+save \n\t" \
+ "xchg %r9, 72+save \n\t" \
+ "xchg %r10, 80+save \n\t" \
+ "xchg %r11, 88+save \n\t" \
+ "xchg %r12, 96+save \n\t" \
+ "xchg %r13, 104+save \n\t" \
+ "xchg %r14, 112+save \n\t" \
+ "xchg %r15, 120+save \n\t"
+
+asm(
+ ".align 4096\n\t"
+ "insn_page:\n\t"
+ "ret\n\t"
+ "pushf\n\t"
+ "push 136+save \n\t"
+ "popf \n\t"
+ INSN_XCHG_ALL
+ "test_insn:\n\t"
+ "in (%dx),%al\n\t"
+ ".skip 31, 0x90\n\t"
+ "test_insn_end:\n\t"
+ INSN_XCHG_ALL
+ "pushf \n\t"
+ "pop 136+save \n\t"
+ "popf \n\t"
+ "ret \n\t"
+ "insn_page_end:\n\t"
+ ".align 4096\n\t"
+);
+
+#define MK_INSN(name, str) \
+ asm ( \
+ ".pushsection .data.insn \n\t" \
+ "insn_" #name ": \n\t" \
+ ".quad 1001f, 1002f - 1001f \n\t" \
+ ".popsection \n\t" \
+ ".pushsection .text.insn, \"ax\" \n\t" \
+ "1001: \n\t" \
+ "insn_code_" #name ": " str " \n\t" \
+ "1002: \n\t" \
+ ".popsection" \
+ ); \
+ extern struct insn_desc insn_##name;
+
+static void trap_emulator(uint64_t *mem, void *alt_insn_page,
+ struct insn_desc *alt_insn)
+{
+ ulong *cr3 = (ulong *)read_cr3();
+ void *insn_ram;
+ extern u8 insn_page[], test_insn[];
+
+ insn_ram = vmap(virt_to_phys(insn_page), 4096);
+ memcpy(alt_insn_page, insn_page, 4096);
+ memcpy(alt_insn_page + (test_insn - insn_page),
+ (void *)(alt_insn->ptr), alt_insn->len);
+ save = inregs;
+
+ /* Load the code TLB with insn_page, but point the page tables at
+ alt_insn_page (and keep the data TLB clear, for AMD decode assist).
+ This will make the CPU trap on the insn_page instruction but the
+ hypervisor will see alt_insn_page. */
+ install_page(cr3, virt_to_phys(insn_page), insn_ram);
+ invlpg(insn_ram);
+ /* Load code TLB */
+ asm volatile("call *%0" : : "r"(insn_ram));
+ install_page(cr3, virt_to_phys(alt_insn_page), insn_ram);
+ /* Trap, let hypervisor emulate at alt_insn_page */
+ asm volatile("call *%0": : "r"(insn_ram+1));
+
+ outregs = save;
+}
+
+static void advance_rip_by_3_and_note_exception(struct ex_regs *regs)
+{
+ ++exceptions;
+ regs->rip += 3;
+}
+
+static void test_mmx_movq_mf(uint64_t *mem, uint8_t *insn_page,
+ uint8_t *alt_insn_page, void *insn_ram)
+{
+ uint16_t fcw = 0; /* all exceptions unmasked */
+ /* movq %mm0, (%rax) */
+ void *stack = alloc_page();
+
+ write_cr0(read_cr0() & ~6); /* TS, EM */
+ exceptions = 0;
+ handle_exception(MF_VECTOR, advance_rip_by_3_and_note_exception);
+ asm volatile("fninit; fldcw %0" : : "m"(fcw));
+ asm volatile("fldz; fldz; fdivp"); /* generate exception */
+
+ MK_INSN(mmx_movq_mf, "movq %mm0, (%rax) \n\t");
+ inregs = (struct regs){ .rsp=(u64)stack+1024 };
+ trap_emulator(mem, alt_insn_page, &insn_mmx_movq_mf);
+ /* exit MMX mode */
+ asm volatile("fnclex; emms");
+ report("movq mmx generates #MF", exceptions == 1);
+ handle_exception(MF_VECTOR, 0);
+}
+
+static void test_movabs(uint64_t *mem, uint8_t *insn_page,
+ uint8_t *alt_insn_page, void *insn_ram)
+{
+ /* mov $0x9090909090909090, %rcx */
+ MK_INSN(movabs, "mov $0x9090909090909090, %rcx\n\t");
+ inregs = (struct regs){ 0 };
+ trap_emulator(mem, alt_insn_page, &insn_movabs);
+ report("64-bit mov imm2", outregs.rcx == 0x9090909090909090);
+}
+
+static void test_crosspage_mmio(volatile uint8_t *mem)
+{
+ volatile uint16_t w, *pw;
+
+ pw = (volatile uint16_t *)&mem[4095];
+ mem[4095] = 0x99;
+ mem[4096] = 0x77;
+ asm volatile("mov %1, %0" : "=r"(w) : "m"(*pw) : "memory");
+ report("cross-page mmio read", w == 0x7799);
+ asm volatile("mov %1, %0" : "=m"(*pw) : "r"((uint16_t)0x88aa));
+ report("cross-page mmio write", mem[4095] == 0xaa && mem[4096] == 0x88);
+}
+
+static void test_string_io_mmio(volatile uint8_t *mem)
+{
+ /* Cross MMIO pages.*/
+ volatile uint8_t *mmio = mem + 4032;
+
+ asm volatile("outw %%ax, %%dx \n\t" : : "a"(0x9999),
"d"(TESTDEV_IO_PORT));
+
+ asm volatile ("cld; rep insb" : : "d" (TESTDEV_IO_PORT), "D" (mmio),
"c" (1024));
+
+ report("string_io_mmio", mmio[1023] == 0x99);
+}
+
+static void test_lgdt_lidt(volatile uint8_t *mem)
+{
+ struct descriptor_table_ptr orig, fresh = {};
+
+ sgdt(&orig);
+ *(struct descriptor_table_ptr *)mem = (struct descriptor_table_ptr) {
+ .limit = 0xf234,
+ .base = 0x12345678abcd,
+ };
+ cli();
+ asm volatile("lgdt %0" : : "m"(*(struct descriptor_table_ptr *)mem));
+ sgdt(&fresh);
+ lgdt(&orig);
+ sti();
+ report("lgdt (long address)", orig.limit == fresh.limit && orig.base ==
fresh.base);
+
+ sidt(&orig);
+ *(struct descriptor_table_ptr *)mem = (struct descriptor_table_ptr) {
+ .limit = 0x432f,
+ .base = 0xdbca87654321,
+ };
+ cli();
+ asm volatile("lidt %0" : : "m"(*(struct descriptor_table_ptr *)mem));
+ sidt(&fresh);
+ lidt(&orig);
+ sti();
+ report("lidt (long address)", orig.limit == fresh.limit && orig.base ==
fresh.base);
+}
+
+static void ss_bad_rpl(struct ex_regs *regs)
+{
+ extern char ss_bad_rpl_cont;
+
+ ++exceptions;
+ regs->rip = (ulong)&ss_bad_rpl_cont;
+}
+
+static void test_sreg(volatile uint16_t *mem)
+{
+ u16 ss = read_ss();
+
+ // check for null segment load
+ *mem = 0;
+ asm volatile("mov %0, %%ss" : : "m"(*mem));
+ report("mov null, %ss", read_ss() == 0);
+
+ // check for exception when ss.rpl != cpl on null segment load
+ exceptions = 0;
+ handle_exception(GP_VECTOR, ss_bad_rpl);
+ *mem = 3;
+ asm volatile("mov %0, %%ss; ss_bad_rpl_cont:" : : "m"(*mem));
+ report("mov null, %ss (with ss.rpl != cpl)", exceptions == 1 && read_ss()
== 0);
+ handle_exception(GP_VECTOR, 0);
+ write_ss(ss);
+}
+
+static void test_lldt(volatile uint16_t *mem)
+{
+ u64 gdt[] = { 0, 0x0000f82000000ffffull /* ldt descriptor */ };
+ struct descriptor_table_ptr gdt_ptr = { .limit = 0xffff, .base =
(ulong)&gdt };
+ struct descriptor_table_ptr orig_gdt;
+
+ cli();
+ sgdt(&orig_gdt);
+ lgdt(&gdt_ptr);
+ *mem = 0x8;
+ asm volatile("lldt %0" : : "m"(*mem));
+ lgdt(&orig_gdt);
+ sti();
+ report("lldt", sldt() == *mem);
+}
+
+static void test_ltr(volatile uint16_t *mem)
+{
+ struct descriptor_table_ptr gdt_ptr;
+ uint64_t *gdt, *trp;
+ uint16_t tr = str();
+ uint64_t busy_mask = (uint64_t)1 << 41;
+
+ sgdt(&gdt_ptr);
+ gdt = (uint64_t *)gdt_ptr.base;
+ trp = &gdt[tr >> 3];
+ *trp &= ~busy_mask;
+ *mem = tr;
+ asm volatile("ltr %0" : : "m"(*mem) : "memory");
+ report("ltr", str() == tr && (*trp & busy_mask));
+}
+
+static void test_simplealu(u32 *mem)
+{
+ *mem = 0x1234;
+ asm("or %1, %0" : "+m"(*mem) : "r"(0x8001));
+ report("or", *mem == 0x9235);
+ asm("add %1, %0" : "+m"(*mem) : "r"(2));
+ report("add", *mem == 0x9237);
+ asm("xor %1, %0" : "+m"(*mem) : "r"(0x1111));
+ report("xor", *mem == 0x8326);
+ asm("sub %1, %0" : "+m"(*mem) : "r"(0x26));
+ report("sub", *mem == 0x8300);
+ asm("clc; adc %1, %0" : "+m"(*mem) : "r"(0x100));
+ report("adc(0)", *mem == 0x8400);
+ asm("stc; adc %1, %0" : "+m"(*mem) : "r"(0x100));
+ report("adc(0)", *mem == 0x8501);
+ asm("clc; sbb %1, %0" : "+m"(*mem) : "r"(0));
+ report("sbb(0)", *mem == 0x8501);
+ asm("stc; sbb %1, %0" : "+m"(*mem) : "r"(0));
+ report("sbb(1)", *mem == 0x8500);
+ asm("and %1, %0" : "+m"(*mem) : "r"(0xfe77));
+ report("and", *mem == 0x8400);
+ asm("test %1, %0" : "+m"(*mem) : "r"(0xf000));
+ report("test", *mem == 0x8400);
+}
+
+int main()
+{
+ void *mem;
+ void *insn_page, *alt_insn_page;
+ void *insn_ram;
+ unsigned long t1, t2;
+
+ setup_vm();
+ setup_idt();
+ mem = alloc_vpages(2);
+ install_page((void *)read_cr3(), IORAM_BASE_PHYS, mem);
+ // install the page twice to test cross-page mmio
+ install_page((void *)read_cr3(), IORAM_BASE_PHYS, mem + 4096);
+ insn_page = alloc_page();
+ alt_insn_page = alloc_page();
+ insn_ram = vmap(virt_to_phys(insn_page), 4096);
+
+ // test mov reg, r/m and mov r/m, reg
+ t1 = 0x123456789abcdef;
+ asm volatile("mov %[t1], (%[mem]) \n\t"
+ "mov (%[mem]), %[t2]"
+ : [t2]"=r"(t2)
+ : [t1]"r"(t1), [mem]"r"(mem)
+ : "memory");
+ report("mov reg, r/m (1)", t2 == 0x123456789abcdef);
+
+ test_simplealu(mem);
+ test_cmps(mem);
+ test_scas(mem);
+
+ test_push(mem);
+ test_pop(mem);
+
+ test_xchg(mem);
+ test_xadd(mem);
+
+ test_cr8();
+
+ test_smsw();
+ test_lmsw();
+ test_ljmp(mem);
+ test_stringio();
+ test_incdecnotneg(mem);
+ test_btc(mem);
+ test_bsfbsr(mem);
+ test_imul(mem);
+ test_muldiv(mem);
+ test_sse(mem);
+ test_mmx(mem);
+ test_rip_relative(mem, insn_ram);
+ test_shld_shrd(mem);
+ //test_lgdt_lidt(mem);
+ test_sreg(mem);
+ test_lldt(mem);
+ test_ltr(mem);
+
+ test_mmx_movq_mf(mem, insn_page, alt_insn_page, insn_ram);
+ test_movabs(mem, insn_page, alt_insn_page, insn_ram);
+
+ test_crosspage_mmio(mem);
+
+ test_string_io_mmio(mem);
+
+ printf("\nSUMMARY: %d tests, %d failures\n", tests, fails);
+ return fails ? 1 : 0;
+}
diff --git a/kvm-unittest/x86/eventinj.c b/kvm-unittest/x86/eventinj.c
new file mode 100644
index 0000000..3d36b37
--- /dev/null
+++ b/kvm-unittest/x86/eventinj.c
@@ -0,0 +1,422 @@
+#include "libcflat.h"
+#include "processor.h"
+#include "vm.h"
+#include "desc.h"
+#include "isr.h"
+#include "apic.h"
+#include "apic-defs.h"
+
+#ifdef __x86_64__
+# define R "r"
+#else
+# define R "e"
+#endif
+
+static int g_fail;
+static int g_tests;
+
+static inline void io_delay(void)
+{
+}
+
+static inline void outl(int addr, int val)
+{
+ asm volatile ("outl %1, %w0" : : "d" (addr), "a" (val));
+}
+
+static void report(const char *msg, int pass)
+{
+ ++g_tests;
+ printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL"));
+ if (!pass)
+ ++g_fail;
+}
+
+void apic_self_ipi(u8 v)
+{
+ apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED |
+ APIC_INT_ASSERT | v, 0);
+}
+
+void apic_self_nmi(void)
+{
+ apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
+}
+
+static void eoi(void)
+{
+ apic_write(APIC_EOI, 0);
+}
+
+#define flush_phys_addr(__s) outl(0xe4, __s)
+#define flush_stack() do { \
+ int __l; \
+ flush_phys_addr(virt_to_phys(&__l)); \
+ } while (0)
+
+extern char isr_iret_ip[];
+
+static void flush_idt_page()
+{
+ struct descriptor_table_ptr ptr;
+ sidt(&ptr);
+ flush_phys_addr(virt_to_phys((void*)ptr.base));
+}
+
+static volatile unsigned int test_divider;
+static volatile int test_count;
+
+#ifndef __x86_64__
+ulong stack_phys;
+void *stack_va;
+
+static void pf_tss(void)
+{
+start:
+ printf("PF running\n");
+ install_pte(phys_to_virt(read_cr3()), 1, stack_va,
+ stack_phys | PTE_PRESENT | PTE_WRITE, 0);
+ invlpg(stack_va);
+ asm volatile ("iret");
+ goto start;
+}
+
+static void of_isr(struct ex_regs *r)
+{
+ printf("OF isr running\n");
+ test_count++;
+}
+
+static void np_isr(struct ex_regs *r)
+{
+ printf("NP isr running %x err=%x\n", r->rip, r->error_code);
+ set_idt_sel(33, read_cs());
+ test_count++;
+}
+#endif
+
+static void de_isr(struct ex_regs *r)
+{
+ printf("DE isr running divider is %d\n", test_divider);
+ test_divider = 10;
+}
+
+static void bp_isr(struct ex_regs *r)
+{
+ printf("BP isr running\n");
+ test_count++;
+}
+
+static void nested_nmi_isr(struct ex_regs *r)
+{
+ printf("Nested NMI isr running rip=%x\n", r->rip);
+
+ if (r->rip != (ulong)&isr_iret_ip)
+ test_count++;
+}
+static void nmi_isr(struct ex_regs *r)
+{
+ printf("NMI isr running %x\n", &isr_iret_ip);
+ test_count++;
+ handle_exception(2, nested_nmi_isr);
+ printf("Sending nested NMI to self\n");
+ apic_self_nmi();
+ io_delay();
+ printf("After nested NMI to self\n");
+}
+
+unsigned long after_iret_addr;
+
+static void nested_nmi_iret_isr(struct ex_regs *r)
+{
+ printf("Nested NMI isr running rip=%x\n", r->rip);
+
+ if (r->rip == after_iret_addr)
+ test_count++;
+}
+static void nmi_iret_isr(struct ex_regs *r)
+{
+ unsigned long *s = alloc_page();
+ test_count++;
+ printf("NMI isr running %p stack %p\n", &&after_iret, s);
+ handle_exception(2, nested_nmi_iret_isr);
+ printf("Sending nested NMI to self\n");
+ apic_self_nmi();
+ printf("After nested NMI to self\n");
+ s[4] = read_ss();
+ s[3] = 0; /* rsp */
+ s[2] = read_rflags();
+ s[1] = read_cs();
+ s[0] = after_iret_addr = (unsigned long)&&after_iret;
+ asm ("mov %%rsp, %0\n\t"
+ "mov %1, %%rsp\n\t"
+ "outl %2, $0xe4\n\t" /* flush stack page */
+#ifdef __x86_64__
+ "iretq\n\t"
+#else
+ "iretl\n\t"
+#endif
+ : "=m"(s[3]) : "rm"(&s[0]), "a"((unsigned int)virt_to_phys(s)) :
"memory");
+after_iret:
+ printf("After iret\n");
+}
+
+static void tirq0(isr_regs_t *r)
+{
+ printf("irq0 running\n");
+ if (test_count != 0)
+ test_count++;
+ eoi();
+}
+
+static void tirq1(isr_regs_t *r)
+{
+ printf("irq1 running\n");
+ test_count++;
+ eoi();
+}
+
+ulong saved_stack;
+
+#define switch_stack(S) do { \
+ asm volatile ("mov %%" R "sp, %0":"=r"(saved_stack)); \
+ asm volatile ("mov %0, %%" R "sp"::"r"(S)); \
+ } while(0)
+
+#define restore_stack() do { \
+ asm volatile ("mov %0, %%" R "sp"::"r"(saved_stack)); \
+ } while(0)
+
+int main()
+{
+ unsigned int res;
+ ulong *pt, *cr3, i;
+
+ setup_vm();
+ setup_idt();
+ setup_gdt();
+ setup_tss32();
+
+ handle_irq(32, tirq0);
+ handle_irq(33, tirq1);
+
+ /* generate HW exception that will fault on IDT and stack */
+ handle_exception(0, de_isr);
+ printf("Try to divide by 0\n");
+ flush_idt_page();
+ flush_stack();
+ asm volatile ("divl %3": "=a"(res)
+ : "d"(0), "a"(1500), "m"(test_divider));
+ printf("Result is %d\n", res);
+ report("DE exception", res == 150);
+
+ /* generate soft exception (BP) that will fault on IDT and stack */
+ test_count = 0;
+ handle_exception(3, bp_isr);
+ printf("Try int 3\n");
+ flush_idt_page();
+ flush_stack();
+ asm volatile ("int $3");
+ printf("After int 3\n");
+ report("BP exception", test_count == 1);
+
+#ifndef __x86_64__
+ /* generate soft exception (OF) that will fault on IDT */
+ test_count = 0;
+ handle_exception(4, of_isr);
+ flush_idt_page();
+ printf("Try into\n");
+ asm volatile ("addb $127, %b0\ninto"::"a"(127));
+ printf("After into\n");
+ report("OF exception", test_count == 1);
+
+ /* generate soft exception (OF) using two bit instruction that will
+ fault on IDT */
+ test_count = 0;
+ handle_exception(4, of_isr);
+ flush_idt_page();
+ printf("Try into\n");
+ asm volatile ("addb $127, %b0\naddr16 into"::"a"(127));
+ printf("After into\n");
+ report("2 byte OF exception", test_count == 1);
+#endif
+
+ /* generate HW interrupt that will fault on IDT */
+ test_count = 0;
+ flush_idt_page();
+ printf("Sending vec 33 to self\n");
+ irq_enable();
+ apic_self_ipi(33);
+ io_delay();
+ irq_disable();
+ printf("After vec 33 to self\n");
+ report("vec 33", test_count == 1);
+
+ /* generate soft interrupt that will fault on IDT and stack */
+ test_count = 0;
+ flush_idt_page();
+ printf("Try int $33\n");
+ flush_stack();
+ asm volatile ("int $33");
+ printf("After int $33\n");
+ report("int $33", test_count == 1);
+
+ /* Inject two HW interrupt than open iterrupt windows. Both interrupt
+ will fault on IDT access */
+ test_count = 0;
+ flush_idt_page();
+ printf("Sending vec 32 and 33 to self\n");
+ apic_self_ipi(32);
+ apic_self_ipi(33);
+ io_delay();
+ irq_enable();
+ asm volatile("nop");
+ irq_disable();
+ printf("After vec 32 and 33 to self\n");
+ report("vec 32/33", test_count == 2);
+
+
+ /* Inject HW interrupt, do sti and than (while in irq shadow) inject
+ soft interrupt. Fault during soft interrupt. Soft interrup shoud be
+ handled before HW interrupt */
+ test_count = 0;
+ flush_idt_page();
+ printf("Sending vec 32 and int $33\n");
+ apic_self_ipi(32);
+ flush_stack();
+ io_delay();
+ irq_enable();
+ asm volatile ("int $33");
+ irq_disable();
+ printf("After vec 32 and int $33\n");
+ report("vec 32/int $33", test_count == 2);
+
+ /* test that TPR is honored */
+ test_count = 0;
+ handle_irq(62, tirq1);
+ flush_idt_page();
+ printf("Sending vec 33 and 62 and mask one with TPR\n");
+ apic_write(APIC_TASKPRI, 0xf << 4);
+ irq_enable();
+ apic_self_ipi(32);
+ apic_self_ipi(62);
+ io_delay();
+ apic_write(APIC_TASKPRI, 0x2 << 4);
+ printf("After 33/62 TPR test\n");
+ report("TPR", test_count == 1);
+ apic_write(APIC_TASKPRI, 0x0);
+ while(test_count != 2); /* wait for second irq */
+ irq_disable();
+
+#ifndef __x86_64__
+ /* test fault durint NP delivery */
+ printf("Before NP test\n");
+ test_count = 0;
+ handle_exception(11, np_isr);
+ set_idt_sel(33, NP_SEL);
+ flush_idt_page();
+ flush_stack();
+ asm volatile ("int $33");
+ printf("After int33\n");
+ report("NP exception", test_count == 2);
+#endif
+
+ /* generate NMI that will fault on IDT */
+ test_count = 0;
+ handle_exception(2, nmi_isr);
+ flush_idt_page();
+ printf("Sending NMI to self\n");
+ apic_self_nmi();
+ printf("After NMI to self\n");
+ /* this is needed on VMX without NMI window notification.
+ Interrupt windows is used instead, so let pending NMI
+ to be injected */
+ irq_enable();
+ asm volatile ("nop");
+ irq_disable();
+ report("NMI", test_count == 2);
+
+ /* generate NMI that will fault on IRET */
+ printf("Before NMI IRET test\n");
+ test_count = 0;
+ handle_exception(2, nmi_iret_isr);
+ printf("Sending NMI to self\n");
+ apic_self_nmi();
+ /* this is needed on VMX without NMI window notification.
+ Interrupt windows is used instead, so let pending NMI
+ to be injected */
+ irq_enable();
+ asm volatile ("nop");
+ irq_disable();
+ printf("After NMI to self\n");
+ report("NMI", test_count == 2);
+#ifndef __x86_64__
+ stack_phys = (ulong)virt_to_phys(alloc_page());
+ stack_va = alloc_vpage();
+
+ /* Generate DE and PF exceptions serially */
+ test_divider = 0;
+ set_intr_task_gate(14, pf_tss);
+ handle_exception(0, de_isr);
+ printf("Try to divide by 0\n");
+ /* install read only pte */
+ install_pte(phys_to_virt(read_cr3()), 1, stack_va,
+ stack_phys | PTE_PRESENT, 0);
+ invlpg(stack_va);
+ flush_phys_addr(stack_phys);
+ switch_stack(stack_va + 4095);
+ flush_idt_page();
+ asm volatile ("divl %3": "=a"(res)
+ : "d"(0), "a"(1500), "m"(test_divider));
+ restore_stack();
+ printf("Result is %d\n", res);
+ report("DE PF exceptions", res == 150);
+
+ /* Generate NP and PF exceptions serially */
+ printf("Before NP test\n");
+ test_count = 0;
+ set_intr_task_gate(14, pf_tss);
+ handle_exception(11, np_isr);
+ set_idt_sel(33, NP_SEL);
+ /* install read only pte */
+ install_pte(phys_to_virt(read_cr3()), 1, stack_va,
+ stack_phys | PTE_PRESENT, 0);
+ invlpg(stack_va);
+ flush_idt_page();
+ flush_phys_addr(stack_phys);
+ switch_stack(stack_va + 4095);
+ asm volatile ("int $33");
+ restore_stack();
+ printf("After int33\n");
+ report("NP PF exceptions", test_count == 2);
+#endif
+
+ pt = alloc_page();
+ cr3 = (void*)read_cr3();
+ memset(pt, 0, 4096);
+ /* use shadowed stack during interrupt delivery */
+ for (i = 0; i < 4096/sizeof(ulong); i++) {
+ if (!cr3[i]) {
+ cr3[i] = virt_to_phys(pt) | PTE_PRESENT | PTE_WRITE;
+ pt[0] = virt_to_phys(pt) | PTE_PRESENT | PTE_WRITE;
+#ifndef __x86_64__
+ ((ulong*)(i<<22))[1] = 0;
+#else
+ ((ulong*)(i<<39))[1] = 0;
+#endif
+ write_cr3(virt_to_phys(cr3));
+ break;
+ }
+ }
+ test_count = 0;
+ printf("Try int 33 with shadowed stack\n");
+ switch_stack(((char*)pt) + 4095);
+ asm volatile("int $33");
+ restore_stack();
+ printf("After int 33 with shadowed stack\n");
+ report("int 33 with shadowed stack", test_count == 1);
+
+ printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail);
+
+ return g_fail != 0;
+}
diff --git a/kvm-unittest/x86/hypercall.c b/kvm-unittest/x86/hypercall.c
new file mode 100644
index 0000000..95120a2
--- /dev/null
+++ b/kvm-unittest/x86/hypercall.c
@@ -0,0 +1,31 @@
+#include "libcflat.h"
+
+#define KVM_HYPERCALL_INTEL ".byte 0x0f,0x01,0xc1"
+#define KVM_HYPERCALL_AMD ".byte 0x0f,0x01,0xd9"
+
+static inline long kvm_hypercall0_intel(unsigned int nr)
+{
+ long ret;
+ asm volatile(KVM_HYPERCALL_INTEL
+ : "=a"(ret)
+ : "a"(nr));
+ return ret;
+}
+
+static inline long kvm_hypercall0_amd(unsigned int nr)
+{
+ long ret;
+ asm volatile(KVM_HYPERCALL_AMD
+ : "=a"(ret)
+ : "a"(nr));
+ return ret;
+}
+
+int main(int ac, char **av)
+{
+ kvm_hypercall0_intel(-1u);
+ printf("Hypercall via VMCALL: OK\n");
+ kvm_hypercall0_amd(-1u);
+ printf("Hypercall via VMMCALL: OK\n");
+ return 0;
+}
diff --git a/kvm-unittest/x86/idt_test.c b/kvm-unittest/x86/idt_test.c
new file mode 100644
index 0000000..2d2e0c2
--- /dev/null
+++ b/kvm-unittest/x86/idt_test.c
@@ -0,0 +1,49 @@
+#include "libcflat.h"
+#include "desc.h"
+
+int test_ud2(void)
+{
+ asm volatile(ASM_TRY("1f")
+ "ud2 \n\t"
+ "1:" :);
+ return exception_vector();
+}
+
+int test_gp(void)
+{
+ unsigned long tmp;
+
+ asm volatile("mov $0xffffffff, %0 \n\t"
+ ASM_TRY("1f")
+ "mov %0, %%cr4\n\t"
+ "1:"
+ : "=a"(tmp));
+ return exception_vector();
+}
+
+static int nr_fail, nr_test;
+
+static void report(int cond, const char *name)
+{
+ ++nr_test;
+ if (!cond) {
+ ++nr_fail;
+ printf("%s: FAIL\n", name);
+ } else {
+ printf("%s: PASS\n", name);
+ }
+}
+
+int main(void)
+{
+ int r;
+
+ printf("Starting IDT test\n");
+ setup_idt();
+ r = test_gp();
+ report(r == GP_VECTOR, "Testing #GP");
+ r = test_ud2();
+ report(r == UD_VECTOR, "Testing #UD");
+ printf("%d failures of %d tests\n", nr_fail, nr_test);
+ return !nr_fail ? 0 : 1;
+}
diff --git a/kvm-unittest/x86/init.c b/kvm-unittest/x86/init.c
new file mode 100644
index 0000000..717511e
--- /dev/null
+++ b/kvm-unittest/x86/init.c
@@ -0,0 +1,129 @@
+#include "libcflat.h"
+#include "apic.h"
+#include "io.h"
+
+#define KBD_CCMD_READ_OUTPORT 0xD0 /* read output port */
+#define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */
+#define KBD_CCMD_RESET 0xFE /* CPU reset */
+
+static inline void kbd_cmd(u8 val)
+{
+ while (inb(0x64) & 2);
+ outb(val, 0x64);
+}
+
+static inline u8 kbd_in(void)
+{
+ kbd_cmd(KBD_CCMD_READ_OUTPORT);
+ while (inb(0x64) & 2);
+ return inb(0x60);
+}
+
+static inline void kbd_out(u8 val)
+{
+ kbd_cmd(KBD_CCMD_WRITE_OUTPORT);
+ while (inb(0x64) & 2);
+ outb(val, 0x60);
+}
+
+static inline void rtc_out(u8 reg, u8 val)
+{
+ outb(reg, 0x70);
+ outb(val, 0x71);
+}
+
+extern char resume_start, resume_end;
+
+#define state (*(volatile int *)0x2000)
+#define bad (*(volatile int *)0x2004)
+#define resumed (*(volatile int *)0x2008)
+
+int main(int argc, char **argv)
+{
+ volatile u16 *resume_vector_ptr = (u16 *)0x467L;
+ char *addr, *resume_vec = (void*)0x1000;
+
+ /* resume execution by indirect jump via 40h:0067h */
+ rtc_out(0x0f, 0x0a);
+ resume_vector_ptr[0] = ((u32)(ulong)resume_vec);
+ resume_vector_ptr[1] = 0;
+
+ for (addr = &resume_start; addr < &resume_end; addr++)
+ *resume_vec++ = *addr;
+
+ if (state != 0) {
+ /*
+ * Strictly speaking this is a firmware problem, but let's check
+ * for it as well...
+ */
+ if (resumed != 1) {
+ printf("Uh, resume vector visited %d times?\n",
resumed);
+ bad |= 2;
+ }
+ /*
+ * Port 92 bit 0 is cleared on system reset. On a soft reset it
+ * is left to 1. Use this to distinguish INIT from hard reset.
+ */
+ if (resumed != 0 && (inb(0x92) & 1) == 0) {
+ printf("Uh, hard reset!\n");
+ bad |= 1;
+ }
+ }
+
+ resumed = 0;
+
+ switch (state++) {
+ case 0:
+ printf("testing port 92 init... ");
+ outb(inb(0x92) & ~1, 0x92);
+ outb(inb(0x92) | 1, 0x92);
+ break;
+
+ case 1:
+ printf("testing kbd controller reset... ");
+ kbd_cmd(KBD_CCMD_RESET);
+ break;
+
+ case 2:
+ printf("testing kbd controller init... ");
+ kbd_out(kbd_in() & ~1);
+ break;
+
+ case 3:
+ printf("testing 0xcf9h init... ");
+ outb(0, 0xcf9);
+ outb(4, 0xcf9);
+ break;
+
+ case 4:
+ printf("testing init to BSP... ");
+ apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL
+ | APIC_DM_INIT, 0);
+ break;
+
+ case 5:
+ exit(bad);
+ }
+
+ /* The resume code will get us back to main. */
+ asm("cli; hlt");
+}
+
+asm (
+ ".global resume_start\n"
+ ".global resume_end\n"
+ ".code16\n"
+ "resume_start:\n"
+ "incb %cs:0x2008\n" // resumed++;
+ "mov $0x0f, %al\n" // rtc_out(0x0f, 0x00);
+ "out %al, $0x70\n"
+ "mov $0x00, %al\n"
+ "out %al, $0x71\n"
+ "jmp $0xffff, $0x0000\n" // BIOS reset
+ "resume_end:\n"
+#ifdef __i386__
+ ".code32\n"
+#else
+ ".code64\n"
+#endif
+ );
diff --git a/kvm-unittest/x86/kvmclock.c b/kvm-unittest/x86/kvmclock.c
new file mode 100644
index 0000000..5b831c5
--- /dev/null
+++ b/kvm-unittest/x86/kvmclock.c
@@ -0,0 +1,279 @@
+#include "libcflat.h"
+#include "smp.h"
+#include "atomic.h"
+#include "processor.h"
+#include "kvmclock.h"
+
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#define likely(x) __builtin_expect(!!(x), 1)
+
+
+struct pvclock_vcpu_time_info __attribute__((aligned(4))) hv_clock[MAX_CPU];
+struct pvclock_wall_clock wall_clock;
+static unsigned char valid_flags = 0;
+static atomic64_t last_value = ATOMIC64_INIT(0);
+
+/*
+ * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction,
+ * yielding a 64-bit result.
+ */
+static inline u64 scale_delta(u64 delta, u32 mul_frac, int shift)
+{
+ u64 product;
+#ifdef __i386__
+ u32 tmp1, tmp2;
+#endif
+
+ if (shift < 0)
+ delta >>= -shift;
+ else
+ delta <<= shift;
+
+#ifdef __i386__
+ __asm__ (
+ "mul %5 ; "
+ "mov %4,%%eax ; "
+ "mov %%edx,%4 ; "
+ "mul %5 ; "
+ "xor %5,%5 ; "
+ "add %4,%%eax ; "
+ "adc %5,%%edx ; "
+ : "=A" (product), "=r" (tmp1), "=r" (tmp2)
+ : "a" ((u32)delta), "1" ((u32)(delta >> 32)), "2" (mul_frac) );
+#elif defined(__x86_64__)
+ __asm__ (
+ "mul %%rdx ; shrd $32,%%rdx,%%rax"
+ : "=a" (product) : "0" (delta), "d" ((u64)mul_frac) );
+#else
+#error implement me!
+#endif
+
+ return product;
+}
+
+#ifdef __i386__
+# define do_div(n,base) ({ \
+ u32 __base = (base); \
+ u32 __rem; \
+ __rem = ((u64)(n)) % __base; \
+ (n) = ((u64)(n)) / __base; \
+ __rem; \
+ })
+#else
+u32 __attribute__((weak)) __div64_32(u64 *n, u32 base)
+{
+ u64 rem = *n;
+ u64 b = base;
+ u64 res, d = 1;
+ u32 high = rem >> 32;
+
+ /* Reduce the thing a bit first */
+ res = 0;
+ if (high >= base) {
+ high /= base;
+ res = (u64) high << 32;
+ rem -= (u64) (high*base) << 32;
+ }
+
+ while ((s64)b > 0 && b < rem) {
+ b = b+b;
+ d = d+d;
+ }
+
+ do {
+ if (rem >= b) {
+ rem -= b;
+ res += d;
+ }
+ b >>= 1;
+ d >>= 1;
+ } while (d);
+
+ *n = res;
+ return rem;
+}
+
+# define do_div(n,base) ({ \
+ u32 __base = (base); \
+ u32 __rem; \
+ (void)(((typeof((n)) *)0) == ((u64 *)0)); \
+ if (likely(((n) >> 32) == 0)) { \
+ __rem = (u32)(n) % __base; \
+ (n) = (u32)(n) / __base; \
+ } else \
+ __rem = __div64_32(&(n), __base); \
+ __rem; \
+ })
+#endif
+
+/**
+ * set_normalized_timespec - set timespec sec and nsec parts and normalize
+ *
+ * @ts: pointer to timespec variable to be set
+ * @sec: seconds to set
+ * @nsec: nanoseconds to set
+ *
+ * Set seconds and nanoseconds field of a timespec variable and
+ * normalize to the timespec storage format
+ *
+ * Note: The tv_nsec part is always in the range of
+ * 0 <= tv_nsec < NSEC_PER_SEC
+ * For negative values only the tv_sec field is negative !
+ */
+void set_normalized_timespec(struct timespec *ts, long sec, s64 nsec)
+{
+ while (nsec >= NSEC_PER_SEC) {
+ /*
+ * The following asm() prevents the compiler from
+ * optimising this loop into a modulo operation. See
+ * also __iter_div_u64_rem() in include/linux/time.h
+ */
+ asm("" : "+rm"(nsec));
+ nsec -= NSEC_PER_SEC;
+ ++sec;
+ }
+ while (nsec < 0) {
+ asm("" : "+rm"(nsec));
+ nsec += NSEC_PER_SEC;
+ --sec;
+ }
+ ts->tv_sec = sec;
+ ts->tv_nsec = nsec;
+}
+
+static u64 pvclock_get_nsec_offset(struct pvclock_shadow_time *shadow)
+{
+ u64 delta = rdtsc() - shadow->tsc_timestamp;
+ return scale_delta(delta, shadow->tsc_to_nsec_mul, shadow->tsc_shift);
+}
+
+/*
+ * Reads a consistent set of time-base values from hypervisor,
+ * into a shadow data area.
+ */
+static unsigned pvclock_get_time_values(struct pvclock_shadow_time *dst,
+ struct pvclock_vcpu_time_info *src)
+{
+ do {
+ dst->version = src->version;
+ rmb(); /* fetch version before data */
+ dst->tsc_timestamp = src->tsc_timestamp;
+ dst->system_timestamp = src->system_time;
+ dst->tsc_to_nsec_mul = src->tsc_to_system_mul;
+ dst->tsc_shift = src->tsc_shift;
+ dst->flags = src->flags;
+ rmb(); /* test version after fetching data */
+ } while ((src->version & 1) || (dst->version != src->version));
+
+ return dst->version;
+}
+
+cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src)
+{
+ struct pvclock_shadow_time shadow;
+ unsigned version;
+ cycle_t ret, offset;
+ u64 last;
+
+ do {
+ version = pvclock_get_time_values(&shadow, src);
+ mb();
+ offset = pvclock_get_nsec_offset(&shadow);
+ ret = shadow.system_timestamp + offset;
+ mb();
+ } while (version != src->version);
+
+ if ((valid_flags & PVCLOCK_RAW_CYCLE_BIT) ||
+ ((valid_flags & PVCLOCK_TSC_STABLE_BIT) &&
+ (shadow.flags & PVCLOCK_TSC_STABLE_BIT)))
+ return ret;
+
+ /*
+ * Assumption here is that last_value, a global accumulator, always goes
+ * forward. If we are less than that, we should not be much smaller.
+ * We assume there is an error marging we're inside, and then the
correction
+ * does not sacrifice accuracy.
+ *
+ * For reads: global may have changed between test and return,
+ * but this means someone else updated poked the clock at a later time.
+ * We just need to make sure we are not seeing a backwards event.
+ *
+ * For updates: last_value = ret is not enough, since two vcpus could be
+ * updating at the same time, and one of them could be slightly behind,
+ * making the assumption that last_value always go forward fail to hold.
+ */
+ last = atomic64_read(&last_value);
+ do {
+ if (ret < last)
+ return last;
+ last = atomic64_cmpxchg(&last_value, last, ret);
+ } while (unlikely(last != ret));
+
+ return ret;
+}
+
+cycle_t kvm_clock_read()
+{
+ struct pvclock_vcpu_time_info *src;
+ cycle_t ret;
+ int index = smp_id();
+
+ src = &hv_clock[index];
+ ret = pvclock_clocksource_read(src);
+ return ret;
+}
+
+void kvm_clock_init(void *data)
+{
+ int index = smp_id();
+ struct pvclock_vcpu_time_info *hvc = &hv_clock[index];
+
+ printf("kvm-clock: cpu %d, msr 0x:%lx \n", index, hvc);
+ wrmsr(MSR_KVM_SYSTEM_TIME, (unsigned long)hvc | 1);
+}
+
+void kvm_clock_clear(void *data)
+{
+ wrmsr(MSR_KVM_SYSTEM_TIME, 0LL);
+}
+
+void pvclock_read_wallclock(struct pvclock_wall_clock *wall_clock,
+ struct pvclock_vcpu_time_info *vcpu_time,
+ struct timespec *ts)
+{
+ u32 version;
+ u64 delta;
+ struct timespec now;
+
+ /* get wallclock at system boot */
+ do {
+ version = wall_clock->version;
+ rmb(); /* fetch version before time */
+ now.tv_sec = wall_clock->sec;
+ now.tv_nsec = wall_clock->nsec;
+ rmb(); /* fetch time before checking version */
+ } while ((wall_clock->version & 1) || (version != wall_clock->version));
+
+ delta = pvclock_clocksource_read(vcpu_time); /* time since system
boot */
+ delta += now.tv_sec * (u64)NSEC_PER_SEC + now.tv_nsec;
+
+ now.tv_nsec = do_div(delta, NSEC_PER_SEC);
+ now.tv_sec = delta;
+
+ set_normalized_timespec(ts, now.tv_sec, now.tv_nsec);
+}
+
+void kvm_get_wallclock(struct timespec *ts)
+{
+ struct pvclock_vcpu_time_info *vcpu_time;
+ int index = smp_id();
+
+ wrmsr(MSR_KVM_WALL_CLOCK, (unsigned long)&wall_clock);
+ vcpu_time = &hv_clock[index];
+ pvclock_read_wallclock(&wall_clock, vcpu_time, ts);
+}
+
+void pvclock_set_flags(unsigned char flags)
+{
+ valid_flags = flags;
+}
diff --git a/kvm-unittest/x86/kvmclock_test.c b/kvm-unittest/x86/kvmclock_test.c
new file mode 100644
index 0000000..52a43fb
--- /dev/null
+++ b/kvm-unittest/x86/kvmclock_test.c
@@ -0,0 +1,167 @@
+#include "libcflat.h"
+#include "smp.h"
+#include "atomic.h"
+#include "processor.h"
+#include "kvmclock.h"
+
+#define DEFAULT_TEST_LOOPS 100000000L
+#define DEFAULT_THRESHOLD 5L
+
+struct test_info {
+ struct spinlock lock;
+ long loops; /* test loops */
+ u64 warps; /* warp count */
+ u64 stalls; /* stall count */
+ long long worst; /* worst warp */
+ volatile cycle_t last; /* last cycle seen by test */
+ atomic_t ncpus; /* number of cpu in the test*/
+ int check; /* check cycle ? */
+};
+
+struct test_info ti[4];
+
+static int wallclock_test(long sec, long threshold)
+{
+ long ksec, offset;
+ struct timespec ts;
+
+ printf("Wallclock test, threshold %ld\n", threshold);
+ kvm_get_wallclock(&ts);
+ ksec = ts.tv_sec;
+
+ offset = ksec - sec;
+ printf("Seconds get from host: %ld\n", sec);
+ printf("Seconds get from kvmclock: %ld\n", ksec);
+ printf("Offset: %ld\n", offset);
+
+ if (offset > threshold || offset < -threshold) {
+ printf("offset too large!\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+static void kvm_clock_test(void *data)
+{
+ struct test_info *hv_test_info = (struct test_info *)data;
+ long i, check = hv_test_info->check;
+
+ for (i = 0; i < hv_test_info->loops; i++){
+ cycle_t t0, t1;
+ long long delta;
+
+ if (check == 0) {
+ kvm_clock_read();
+ continue;
+ }
+
+ spin_lock(&hv_test_info->lock);
+ t1 = kvm_clock_read();
+ t0 = hv_test_info->last;
+ hv_test_info->last = kvm_clock_read();
+ spin_unlock(&hv_test_info->lock);
+
+ delta = t1 - t0;
+ if (delta < 0) {
+ spin_lock(&hv_test_info->lock);
+ ++hv_test_info->warps;
+ if (delta < hv_test_info->worst){
+ hv_test_info->worst = delta;
+ printf("Worst warp %lld %\n",
hv_test_info->worst);
+ }
+ spin_unlock(&hv_test_info->lock);
+ }
+ if (delta == 0)
+ ++hv_test_info->stalls;
+
+ if (!((unsigned long)i & 31))
+ asm volatile("rep; nop");
+ }
+
+ atomic_dec(&hv_test_info->ncpus);
+}
+
+static int cycle_test(int ncpus, long loops, int check, struct test_info *ti)
+{
+ int i;
+ unsigned long long begin, end;
+
+ begin = rdtsc();
+
+ atomic_set(&ti->ncpus, ncpus);
+ ti->loops = loops;
+ ti->check = check;
+ for (i = ncpus - 1; i >= 0; i--)
+ on_cpu_async(i, kvm_clock_test, (void *)ti);
+
+ /* Wait for the end of other vcpu */
+ while(atomic_read(&ti->ncpus))
+ ;
+
+ end = rdtsc();
+
+ printf("Total vcpus: %d\n", ncpus);
+ printf("Test loops: %ld\n", ti->loops);
+ if (check == 1) {
+ printf("Total warps: %lld\n", ti->warps);
+ printf("Total stalls: %lld\n", ti->stalls);
+ printf("Worst warp: %lld\n", ti->worst);
+ } else
+ printf("TSC cycles: %lld\n", end - begin);
+
+ return ti->warps ? 1 : 0;
+}
+
+int main(int ac, char **av)
+{
+ int ncpus;
+ int nerr = 0, i;
+ long loops = DEFAULT_TEST_LOOPS;
+ long sec = 0;
+ long threshold = DEFAULT_THRESHOLD;
+
+ if (ac > 1)
+ loops = atol(av[1]);
+ if (ac > 2)
+ sec = atol(av[2]);
+ if (ac > 3)
+ threshold = atol(av[3]);
+
+ smp_init();
+
+ ncpus = cpu_count();
+ if (ncpus > MAX_CPU)
+ ncpus = MAX_CPU;
+ for (i = 0; i < ncpus; ++i)
+ on_cpu(i, kvm_clock_init, (void *)0);
+
+ if (ac > 2)
+ nerr += wallclock_test(sec, threshold);
+
+ printf("Check the stability of raw cycle ...\n");
+ pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT
+ | PVCLOCK_RAW_CYCLE_BIT);
+ if (cycle_test(ncpus, loops, 1, &ti[0]))
+ printf("Raw cycle is not stable\n");
+ else
+ printf("Raw cycle is stable\n");
+
+ pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT);
+ printf("Monotonic cycle test:\n");
+ nerr += cycle_test(ncpus, loops, 1, &ti[1]);
+
+ printf("Measure the performance of raw cycle ...\n");
+ pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT
+ | PVCLOCK_RAW_CYCLE_BIT);
+ cycle_test(ncpus, loops, 0, &ti[2]);
+
+ printf("Measure the performance of adjusted cycle ...\n");
+ pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT);
+ cycle_test(ncpus, loops, 0, &ti[3]);
+
+ for (i = 0; i < ncpus; ++i)
+ on_cpu(i, kvm_clock_clear, (void *)0);
+
+ return nerr > 0 ? 1 : 0;
+}
diff --git a/kvm-unittest/x86/msr.c b/kvm-unittest/x86/msr.c
new file mode 100644
index 0000000..de7573d
--- /dev/null
+++ b/kvm-unittest/x86/msr.c
@@ -0,0 +1,149 @@
+/* msr tests */
+
+#include "libcflat.h"
+#include "processor.h"
+#include "msr.h"
+
+struct msr_info {
+ int index;
+ char *name;
+ struct tc {
+ int valid;
+ unsigned long long value;
+ unsigned long long expected;
+ } val_pairs[20];
+};
+
+
+#define addr_64 0x0000123456789abcULL
+
+struct msr_info msr_info[] =
+{
+ { .index = 0x0000001b, .name = "MSR_IA32_APICBASE",
+ .val_pairs = {
+ { .valid = 1, .value = 0x0000000056789900, .expected =
0x0000000056789900},
+ { .valid = 1, .value = 0x0000000056789D01, .expected =
0x0000000056789D01},
+ }
+ },
+ { .index = 0x00000174, .name = "IA32_SYSENTER_CS",
+ .val_pairs = {{ .valid = 1, .value = 0x1234, .expected = 0x1234}}
+ },
+ { .index = 0x00000175, .name = "MSR_IA32_SYSENTER_ESP",
+ .val_pairs = {{ .valid = 1, .value = addr_64, .expected = addr_64}}
+ },
+ { .index = 0x00000176, .name = "IA32_SYSENTER_EIP",
+ .val_pairs = {{ .valid = 1, .value = addr_64, .expected = addr_64}}
+ },
+ { .index = 0x000001a0, .name = "MSR_IA32_MISC_ENABLE",
+ // reserved: 1:2, 4:6, 8:10, 13:15, 17, 19:21, 24:33, 35:63
+ .val_pairs = {{ .valid = 1, .value = 0x400c51889, .expected =
0x400c51889}}
+ },
+ { .index = 0x00000277, .name = "MSR_IA32_CR_PAT",
+ .val_pairs = {{ .valid = 1, .value = 0x07070707, .expected = 0x07070707}}
+ },
+ { .index = 0xc0000100, .name = "MSR_FS_BASE",
+ .val_pairs = {{ .valid = 1, .value = addr_64, .expected = addr_64}}
+ },
+ { .index = 0xc0000101, .name = "MSR_GS_BASE",
+ .val_pairs = {{ .valid = 1, .value = addr_64, .expected = addr_64}}
+ },
+ { .index = 0xc0000102, .name = "MSR_KERNEL_GS_BASE",
+ .val_pairs = {{ .valid = 1, .value = addr_64, .expected = addr_64}}
+ },
+#ifdef __x86_64__
+ { .index = 0xc0000080, .name = "MSR_EFER",
+ .val_pairs = {{ .valid = 1, .value = 0xD00, .expected = 0xD00}}
+ },
+#endif
+ { .index = 0xc0000082, .name = "MSR_LSTAR",
+ .val_pairs = {{ .valid = 1, .value = addr_64, .expected = addr_64}}
+ },
+ { .index = 0xc0000083, .name = "MSR_CSTAR",
+ .val_pairs = {{ .valid = 1, .value = addr_64, .expected = addr_64}}
+ },
+ { .index = 0xc0000084, .name = "MSR_SYSCALL_MASK",
+ .val_pairs = {{ .valid = 1, .value = 0xffffffff, .expected = 0xffffffff}}
+ },
+
+// MSR_IA32_DEBUGCTLMSR needs svm feature LBRV
+// MSR_VM_HSAVE_PA only AMD host
+};
+
+static int find_msr_info(int msr_index)
+{
+ int i;
+ for (i = 0; i < sizeof(msr_info)/sizeof(msr_info[0]) ; i++) {
+ if (msr_info[i].index == msr_index) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+int nr_passed, nr_tests;
+
+static void report(const char *name, int passed)
+{
+ ++nr_tests;
+ if (passed)
+ ++nr_passed;
+ printf("%s: %s\n", name, passed ? "PASS" : "FAIL");
+}
+
+static void test_msr_rw(int msr_index, unsigned long long input, unsigned long
long expected)
+{
+ unsigned long long r = 0;
+ int index;
+ char *sptr;
+ if ((index = find_msr_info(msr_index)) != -1) {
+ sptr = msr_info[index].name;
+ } else {
+ printf("couldn't find name for msr # 0x%x, skipping\n", msr_index);
+ return;
+ }
+ wrmsr(msr_index, input);
+ r = rdmsr(msr_index);
+ if (expected != r) {
+ printf("testing %s: output = 0x%x:0x%x expected = 0x%x:0x%x\n", sptr,
r >> 32, r, expected >> 32, expected);
+ }
+ report(sptr, expected == r);
+}
+
+static void test_syscall_lazy_load(void)
+{
+#ifdef __x86_64__
+ extern void syscall_target();
+ u16 cs = read_cs(), ss = read_ss();
+ ulong tmp;
+
+ wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SCE);
+ wrmsr(MSR_LSTAR, (ulong)syscall_target);
+ wrmsr(MSR_STAR, (uint64_t)cs << 32);
+ asm volatile("pushf; syscall; syscall_target: popf" : "=c"(tmp) : : "r11");
+ write_ss(ss);
+ // will crash horribly if broken
+ report("MSR_*STAR eager loading", true);
+#endif
+}
+
+int main(int ac, char **av)
+{
+ int i, j;
+ for (i = 0 ; i < sizeof(msr_info) / sizeof(msr_info[0]); i++) {
+ for (j = 0; j < sizeof(msr_info[i].val_pairs) /
sizeof(msr_info[i].val_pairs[0]); j++) {
+ if (msr_info[i].val_pairs[j].valid) {
+ test_msr_rw(msr_info[i].index, msr_info[i].val_pairs[j].value,
msr_info[i].val_pairs[j].expected);
+ } else {
+ break;
+ }
+ }
+ }
+
+ test_syscall_lazy_load();
+
+ printf("%d tests, %d failures\n", nr_tests, nr_tests - nr_passed);
+
+ return nr_passed == nr_tests ? 0 : 1;
+}
+
diff --git a/kvm-unittest/x86/pcid.c b/kvm-unittest/x86/pcid.c
new file mode 100644
index 0000000..8bfeba2
--- /dev/null
+++ b/kvm-unittest/x86/pcid.c
@@ -0,0 +1,186 @@
+/* Basic PCID & INVPCID functionality test */
+
+#include "libcflat.h"
+#include "processor.h"
+#include "desc.h"
+
+int nr_passed, nr_tests;
+
+#define X86_FEATURE_PCID (1 << 17)
+#define X86_FEATURE_INVPCID (1 << 10)
+
+#define X86_CR0_PG (1 << 31)
+#define X86_CR3_PCID_MASK 0x00000fff
+#define X86_CR4_PCIDE (1 << 17)
+
+#define X86_IA32_EFER 0xc0000080
+#define X86_EFER_LMA (1UL << 8)
+
+struct invpcid_desc {
+ unsigned long pcid : 12;
+ unsigned long rsv : 52;
+ unsigned long addr : 64;
+};
+
+static void report(const char *name, int passed)
+{
+ ++nr_tests;
+ if (passed)
+ ++nr_passed;
+ printf("%s: %s\n", name, passed ? "PASS" : "FAIL");
+}
+
+int write_cr0_checking(unsigned long val)
+{
+ asm volatile(ASM_TRY("1f")
+ "mov %0, %%cr0\n\t"
+ "1:": : "r" (val));
+ return exception_vector();
+}
+
+int write_cr4_checking(unsigned long val)
+{
+ asm volatile(ASM_TRY("1f")
+ "mov %0, %%cr4\n\t"
+ "1:": : "r" (val));
+ return exception_vector();
+}
+
+int invpcid_checking(unsigned long type, void *desc)
+{
+ asm volatile (ASM_TRY("1f")
+ ".byte 0x66,0x0f,0x38,0x82,0x18 \n\t" /* invpcid (%rax),
%rbx */
+ "1:" : : "a" (desc), "b" (type));
+ return exception_vector();
+}
+
+void test_cpuid_consistency(int pcid_enabled, int invpcid_enabled)
+{
+ int passed = !(!pcid_enabled && invpcid_enabled);
+ report("CPUID consistency", passed);
+}
+
+void test_pcid_enabled(void)
+{
+ int passed = 0;
+ ulong cr0 = read_cr0(), cr3 = read_cr3(), cr4 = read_cr4();
+
+ /* try setting CR4.PCIDE, no exception expected */
+ if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != 0)
+ goto report;
+
+ /* try clearing CR0.PG when CR4.PCIDE=1, #GP expected */
+ if (write_cr0_checking(cr0 | X86_CR0_PG) != GP_VECTOR)
+ goto report;
+
+ write_cr4(cr4);
+
+ /* try setting CR4.PCIDE when CR3[11:0] != 0 , #GP expected */
+ write_cr3(cr3 | 0x001);
+ if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != GP_VECTOR)
+ goto report;
+ write_cr3(cr3);
+
+ passed = 1;
+
+report:
+ report("Test on PCID when enabled", passed);
+}
+
+void test_pcid_disabled(void)
+{
+ int passed = 0;
+ ulong cr4 = read_cr4();
+
+ /* try setting CR4.PCIDE, #GP expected */
+ if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != GP_VECTOR)
+ goto report;
+
+ passed = 1;
+
+report:
+ report("Test on PCID when disabled", passed);
+}
+
+void test_invpcid_enabled(void)
+{
+ int passed = 0;
+ ulong cr4 = read_cr4();
+ struct invpcid_desc desc;
+ desc.rsv = 0;
+
+ /* try executing invpcid when CR4.PCIDE=0, desc.pcid=0 and type=1
+ * no exception expected
+ */
+ desc.pcid = 0;
+ if (invpcid_checking(1, &desc) != 0)
+ goto report;
+
+ /* try executing invpcid when CR4.PCIDE=0, desc.pcid=1 and type=1
+ * #GP expected
+ */
+ desc.pcid = 1;
+ if (invpcid_checking(1, &desc) != GP_VECTOR)
+ goto report;
+
+ if (write_cr4_checking(cr4 | X86_CR4_PCIDE) != 0)
+ goto report;
+
+ /* try executing invpcid when CR4.PCIDE=1
+ * no exception expected
+ */
+ desc.pcid = 10;
+ if (invpcid_checking(2, &desc) != 0)
+ goto report;
+
+ passed = 1;
+
+report:
+ report("Test on INVPCID when enabled", passed);
+}
+
+void test_invpcid_disabled(void)
+{
+ int passed = 0;
+ struct invpcid_desc desc;
+
+ /* try executing invpcid, #UD expected */
+ if (invpcid_checking(2, &desc) != UD_VECTOR)
+ goto report;
+
+ passed = 1;
+
+report:
+ report("Test on INVPCID when disabled", passed);
+}
+
+int main(int ac, char **av)
+{
+ struct cpuid _cpuid;
+ int pcid_enabled = 0, invpcid_enabled = 0;
+
+ setup_idt();
+
+ _cpuid = cpuid(1);
+ if (_cpuid.c & X86_FEATURE_PCID)
+ pcid_enabled = 1;
+ _cpuid = cpuid_indexed(7, 0);
+ if (_cpuid.b & X86_FEATURE_INVPCID)
+ invpcid_enabled = 1;
+
+ test_cpuid_consistency(pcid_enabled, invpcid_enabled);
+
+ if (pcid_enabled)
+ test_pcid_enabled();
+ else
+ test_pcid_disabled();
+
+ if (invpcid_enabled)
+ test_invpcid_enabled();
+ else
+ test_invpcid_disabled();
+
+ printf("%d tests, %d failures\n", nr_tests, nr_tests - nr_passed);
+
+ return nr_passed == nr_tests ? 0 : 1;
+}
diff --git a/kvm-unittest/x86/pmu.c b/kvm-unittest/x86/pmu.c
new file mode 100644
index 0000000..42b5a59
--- /dev/null
+++ b/kvm-unittest/x86/pmu.c
@@ -0,0 +1,414 @@
+
+#include "x86/msr.h"
+#include "x86/processor.h"
+#include "x86/apic-defs.h"
+#include "x86/apic.h"
+#include "x86/desc.h"
+#include "x86/isr.h"
+#include "x86/vm.h"
+
+#include "libcflat.h"
+#include <stdint.h>
+
+#define FIXED_CNT_INDEX 32
+#define PC_VECTOR 32
+
+#define EVNSEL_EVENT_SHIFT 0
+#define EVNTSEL_UMASK_SHIFT 8
+#define EVNTSEL_USR_SHIFT 16
+#define EVNTSEL_OS_SHIFT 17
+#define EVNTSEL_EDGE_SHIFT 18
+#define EVNTSEL_PC_SHIFT 19
+#define EVNTSEL_INT_SHIFT 20
+#define EVNTSEL_EN_SHIF 22
+#define EVNTSEL_INV_SHIF 23
+#define EVNTSEL_CMASK_SHIFT 24
+
+#define EVNTSEL_EN (1 << EVNTSEL_EN_SHIF)
+#define EVNTSEL_USR (1 << EVNTSEL_USR_SHIFT)
+#define EVNTSEL_OS (1 << EVNTSEL_OS_SHIFT)
+#define EVNTSEL_PC (1 << EVNTSEL_PC_SHIFT)
+#define EVNTSEL_INT (1 << EVNTSEL_INT_SHIFT)
+#define EVNTSEL_INV (1 << EVNTSEL_INV_SHIF)
+
+#define N 1000000
+
+typedef struct {
+ uint32_t ctr;
+ uint32_t config;
+ uint64_t count;
+ int idx;
+} pmu_counter_t;
+
+union cpuid10_eax {
+ struct {
+ unsigned int version_id:8;
+ unsigned int num_counters:8;
+ unsigned int bit_width:8;
+ unsigned int mask_length:8;
+ } split;
+ unsigned int full;
+} eax;
+
+union cpuid10_ebx {
+ struct {
+ unsigned int no_unhalted_core_cycles:1;
+ unsigned int no_instructions_retired:1;
+ unsigned int no_unhalted_reference_cycles:1;
+ unsigned int no_llc_reference:1;
+ unsigned int no_llc_misses:1;
+ unsigned int no_branch_instruction_retired:1;
+ unsigned int no_branch_misses_retired:1;
+ } split;
+ unsigned int full;
+} ebx;
+
+union cpuid10_edx {
+ struct {
+ unsigned int num_counters_fixed:5;
+ unsigned int bit_width_fixed:8;
+ unsigned int reserved:19;
+ } split;
+ unsigned int full;
+} edx;
+
+struct pmu_event {
+ char *name;
+ uint32_t unit_sel;
+ int min;
+ int max;
+} gp_events[] = {
+ {"core cycles", 0x003c, 1*N, 50*N},
+ {"instructions", 0x00c0, 10*N, 10.2*N},
+ {"ref cycles", 0x013c, 0.1*N, 30*N},
+ {"llc refference", 0x4f2e, 1, 2*N},
+ {"llc misses", 0x412e, 1, 1*N},
+ {"branches", 0x00c4, 1*N, 1.1*N},
+ {"branch misses", 0x00c5, 0, 0.1*N},
+}, fixed_events[] = {
+ {"fixed 1", MSR_CORE_PERF_FIXED_CTR0, 10*N, 10.2*N},
+ {"fixed 2", MSR_CORE_PERF_FIXED_CTR0 + 1, 1*N, 30*N},
+ {"fixed 3", MSR_CORE_PERF_FIXED_CTR0 + 2, 0.1*N, 30*N}
+};
+
+static int num_counters;
+static int tests, failures;
+
+char *buf;
+
+static inline void loop()
+{
+ unsigned long tmp, tmp2, tmp3;
+
+ asm volatile("1: mov (%1), %2; add $64, %1; nop; nop; nop; nop; nop;
nop; nop; loop 1b"
+ : "=c"(tmp), "=r"(tmp2), "=r"(tmp3): "0"(N), "1"(buf));
+
+}
+
+volatile uint64_t irq_received;
+
+static void cnt_overflow(isr_regs_t *regs)
+{
+ irq_received++;
+ apic_write(APIC_EOI, 0);
+}
+
+static bool check_irq(void)
+{
+ int i;
+ irq_received = 0;
+ irq_enable();
+ for (i = 0; i < 100000 && !irq_received; i++)
+ asm volatile("pause");
+ irq_disable();
+ return irq_received;
+}
+
+static bool is_gp(pmu_counter_t *evt)
+{
+ return evt->ctr < MSR_CORE_PERF_FIXED_CTR0;
+}
+
+static int event_to_global_idx(pmu_counter_t *cnt)
+{
+ return cnt->ctr - (is_gp(cnt) ? MSR_IA32_PERFCTR0 :
+ (MSR_CORE_PERF_FIXED_CTR0 - FIXED_CNT_INDEX));
+}
+
+static struct pmu_event* get_counter_event(pmu_counter_t *cnt)
+{
+ if (is_gp(cnt)) {
+ int i;
+
+ for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++)
+ if (gp_events[i].unit_sel == (cnt->config & 0xffff))
+ return &gp_events[i];
+ } else
+ return &fixed_events[cnt->ctr - MSR_CORE_PERF_FIXED_CTR0];
+
+ return (void*)0;
+}
+
+static void global_enable(pmu_counter_t *cnt)
+{
+ cnt->idx = event_to_global_idx(cnt);
+
+ wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_CTRL) |
+ (1ull << cnt->idx));
+}
+
+static void global_disable(pmu_counter_t *cnt)
+{
+ wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, rdmsr(MSR_CORE_PERF_GLOBAL_CTRL) &
+ ~(1ull << cnt->idx));
+}
+
+
+static void start_event(pmu_counter_t *evt)
+{
+ wrmsr(evt->ctr, evt->count);
+ if (is_gp(evt))
+ wrmsr(MSR_P6_EVNTSEL0 + event_to_global_idx(evt),
+ evt->config | EVNTSEL_EN);
+ else {
+ uint32_t ctrl = rdmsr(MSR_CORE_PERF_FIXED_CTR_CTRL);
+ int shift = (evt->ctr - MSR_CORE_PERF_FIXED_CTR0) * 4;
+ uint32_t usrospmi = 0;
+
+ if (evt->config & EVNTSEL_OS)
+ usrospmi |= (1 << 0);
+ if (evt->config & EVNTSEL_USR)
+ usrospmi |= (1 << 1);
+ if (evt->config & EVNTSEL_INT)
+ usrospmi |= (1 << 3); // PMI on overflow
+ ctrl = (ctrl & ~(0xf << shift)) | (usrospmi << shift);
+ wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, ctrl);
+ }
+ global_enable(evt);
+}
+
+static void stop_event(pmu_counter_t *evt)
+{
+ global_disable(evt);
+ if (is_gp(evt))
+ wrmsr(MSR_P6_EVNTSEL0 + event_to_global_idx(evt),
+ evt->config & ~EVNTSEL_EN);
+ else {
+ uint32_t ctrl = rdmsr(MSR_CORE_PERF_FIXED_CTR_CTRL);
+ int shift = (evt->ctr - MSR_CORE_PERF_FIXED_CTR0) * 4;
+ wrmsr(MSR_CORE_PERF_FIXED_CTR_CTRL, ctrl & ~(0xf << shift));
+ }
+ evt->count = rdmsr(evt->ctr);
+}
+
+static void measure(pmu_counter_t *evt, int count)
+{
+ int i;
+ for (i = 0; i < count; i++)
+ start_event(&evt[i]);
+ loop();
+ for (i = 0; i < count; i++)
+ stop_event(&evt[i]);
+}
+
+static void report(const char *name, int n, bool pass)
+{
+ printf("%s: pmu %s-%d\n", pass ? "PASS" : "FAIL", name, n);
+ tests += 1;
+ failures += !pass;
+}
+
+static bool verify_event(uint64_t count, struct pmu_event *e)
+{
+ // printf("%lld >= %lld <= %lld\n", e->min, count, e->max);
+ return count >= e->min && count <= e->max;
+
+}
+
+static bool verify_counter(pmu_counter_t *cnt)
+{
+ return verify_event(cnt->count, get_counter_event(cnt));
+}
+
+static void check_gp_counter(struct pmu_event *evt)
+{
+ pmu_counter_t cnt = {
+ .ctr = MSR_IA32_PERFCTR0,
+ .config = EVNTSEL_OS | EVNTSEL_USR | evt->unit_sel,
+ };
+ int i;
+
+ for (i = 0; i < num_counters; i++, cnt.ctr++) {
+ cnt.count = 0;
+ measure(&cnt, 1);
+ report(evt->name, i, verify_event(cnt.count, evt));
+ }
+}
+
+static void check_gp_counters(void)
+{
+ int i;
+
+ for (i = 0; i < sizeof(gp_events)/sizeof(gp_events[0]); i++)
+ if (!(ebx.full & (1 << i)))
+ check_gp_counter(&gp_events[i]);
+ else
+ printf("GP event '%s' is disabled\n",
+ gp_events[i].name);
+}
+
+static void check_fixed_counters(void)
+{
+ pmu_counter_t cnt = {
+ .config = EVNTSEL_OS | EVNTSEL_USR,
+ };
+ int i;
+
+ for (i = 0; i < edx.split.num_counters_fixed; i++) {
+ cnt.count = 0;
+ cnt.ctr = fixed_events[i].unit_sel;
+ measure(&cnt, 1);
+ report("fixed", i, verify_event(cnt.count, &fixed_events[i]));
+ }
+}
+
+static void check_counters_many(void)
+{
+ pmu_counter_t cnt[10];
+ int i, n;
+
+ for (i = 0, n = 0; n < num_counters; i++) {
+ if (ebx.full & (1 << i))
+ continue;
+
+ cnt[n].count = 0;
+ cnt[n].ctr = MSR_IA32_PERFCTR0 + n;
+ cnt[n].config = EVNTSEL_OS | EVNTSEL_USR |
gp_events[i].unit_sel;
+ n++;
+ }
+ for (i = 0; i < edx.split.num_counters_fixed; i++) {
+ cnt[n].count = 0;
+ cnt[n].ctr = fixed_events[i].unit_sel;
+ cnt[n].config = EVNTSEL_OS | EVNTSEL_USR;
+ n++;
+ }
+
+ measure(cnt, n);
+
+ for (i = 0; i < n; i++)
+ if (!verify_counter(&cnt[i]))
+ break;
+
+ report("all counters", 0, i == n);
+}
+
+static void check_counter_overflow(void)
+{
+ uint64_t count;
+ int i;
+ pmu_counter_t cnt = {
+ .ctr = MSR_IA32_PERFCTR0,
+ .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /*
instructions */,
+ .count = 0,
+ };
+ measure(&cnt, 1);
+ count = cnt.count;
+
+ /* clear status before test */
+ wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL,
rdmsr(MSR_CORE_PERF_GLOBAL_STATUS));
+
+ for (i = 0; i < num_counters + 1; i++, cnt.ctr++) {
+ uint64_t status;
+ int idx;
+ if (i == num_counters)
+ cnt.ctr = fixed_events[0].unit_sel;
+ if (i % 2)
+ cnt.config |= EVNTSEL_INT;
+ else
+ cnt.config &= ~EVNTSEL_INT;
+ idx = event_to_global_idx(&cnt);
+ cnt.count = 1 - count;
+ measure(&cnt, 1);
+ report("overflow", i, cnt.count == 1);
+ status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS);
+ report("overflow status", i, status & (1ull << idx));
+ wrmsr(MSR_CORE_PERF_GLOBAL_OVF_CTRL, status);
+ status = rdmsr(MSR_CORE_PERF_GLOBAL_STATUS);
+ report("overflow status clear", i, !(status & (1ull << idx)));
+ report("overflow irq", i, check_irq() == (i % 2));
+ }
+}
+
+static void check_gp_counter_cmask(void)
+{
+ pmu_counter_t cnt = {
+ .ctr = MSR_IA32_PERFCTR0,
+ .config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /*
instructions */,
+ .count = 0,
+ };
+ cnt.config |= (0x2 << EVNTSEL_CMASK_SHIFT);
+ measure(&cnt, 1);
+ report("cmask", 0, cnt.count < gp_events[1].min);
+}
+
+static void check_rdpmc(void)
+{
+ uint64_t val = 0x1f3456789ull;
+ int i;
+
+ for (i = 0; i < num_counters; i++) {
+ uint64_t x = (val & 0xffffffff) |
+ ((1ull << (eax.split.bit_width - 32)) - 1) << 32;
+ wrmsr(MSR_IA32_PERFCTR0 + i, val);
+ report("rdpmc", i, rdpmc(i) == x);
+ report("rdpmc fast", i, rdpmc(i | (1<<31)) == (u32)val);
+ }
+ for (i = 0; i < edx.split.num_counters_fixed; i++) {
+ uint64_t x = (val & 0xffffffff) |
+ ((1ull << (edx.split.bit_width_fixed - 32)) - 1) << 32;
+ wrmsr(MSR_CORE_PERF_FIXED_CTR0 + i, val);
+ report("rdpmc fixed", i, rdpmc(i | (1 << 30)) == x);
+ report("rdpmc fixed fast", i, rdpmc(i | (3<<30)) == (u32)val);
+ }
+}
+
+int main(int ac, char **av)
+{
+ struct cpuid id = cpuid(10);
+
+ setup_vm();
+ setup_idt();
+ handle_irq(PC_VECTOR, cnt_overflow);
+ buf = vmalloc(N*64);
+
+ eax.full = id.a;
+ ebx.full = id.b;
+ edx.full = id.d;
+
+ if (!eax.split.version_id) {
+ printf("No pmu is detected!\n");
+ return 1;
+ }
+ printf("PMU version: %d\n", eax.split.version_id);
+ printf("GP counters: %d\n", eax.split.num_counters);
+ printf("GP counter width: %d\n", eax.split.bit_width);
+ printf("Mask length: %d\n", eax.split.mask_length);
+ printf("Fixed counters: %d\n", edx.split.num_counters_fixed);
+ printf("Fixed counter width: %d\n", edx.split.bit_width_fixed);
+
+ num_counters = eax.split.num_counters;
+ if (num_counters > ARRAY_SIZE(gp_events))
+ num_counters = ARRAY_SIZE(gp_events);
+
+ apic_write(APIC_LVTPC, PC_VECTOR);
+
+ check_gp_counters();
+ check_fixed_counters();
+ check_rdpmc();
+ check_counters_many();
+ check_counter_overflow();
+ check_gp_counter_cmask();
+
+ printf("\n%d tests, %d failures\n", tests, failures);
+ return !failures ? 0 : 1;
+}
diff --git a/kvm-unittest/x86/port80.c b/kvm-unittest/x86/port80.c
new file mode 100644
index 0000000..522c1a4
--- /dev/null
+++ b/kvm-unittest/x86/port80.c
@@ -0,0 +1,12 @@
+#include "libcflat.h"
+
+int main()
+{
+ int i;
+
+ printf("begining port 0x80 write test\n");
+ for (i = 0; i < 10000000; ++i)
+ asm volatile("outb %al, $0x80");
+ printf("done\n");
+ return 0;
+}
diff --git a/kvm-unittest/x86/realmode.c b/kvm-unittest/x86/realmode.c
new file mode 100644
index 0000000..c57e033
--- /dev/null
+++ b/kvm-unittest/x86/realmode.c
@@ -0,0 +1,1627 @@
+#ifndef USE_SERIAL
+#define USE_SERIAL
+#endif
+
+asm(".code16gcc");
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned u32;
+typedef unsigned long long u64;
+
+void test_function(void);
+
+asm(
+ "test_function: \n\t"
+ "mov $0x1234, %eax \n\t"
+ "ret"
+ );
+
+static int strlen(const char *str)
+{
+ int n;
+
+ for (n = 0; *str; ++str)
+ ++n;
+ return n;
+}
+
+static void outb(u8 data, u16 port)
+{
+ asm volatile("out %0, %1" : : "a"(data), "d"(port));
+}
+
+#ifdef USE_SERIAL
+static int serial_iobase = 0x3f8;
+static int serial_inited = 0;
+
+static u8 inb(u16 port)
+{
+ u8 data;
+ asm volatile("in %1, %0" : "=a"(data) : "d"(port));
+ return data;
+}
+
+static void serial_outb(char ch)
+{
+ u8 lsr;
+
+ do {
+ lsr = inb(serial_iobase + 0x05);
+ } while (!(lsr & 0x20));
+
+ outb(ch, serial_iobase + 0x00);
+}
+
+static void serial_init(void)
+{
+ u8 lcr;
+
+ /* set DLAB */
+ lcr = inb(serial_iobase + 0x03);
+ lcr |= 0x80;
+ outb(lcr, serial_iobase + 0x03);
+
+ /* set baud rate to 115200 */
+ outb(0x01, serial_iobase + 0x00);
+ outb(0x00, serial_iobase + 0x01);
+
+ /* clear DLAB */
+ lcr = inb(serial_iobase + 0x03);
+ lcr &= ~0x80;
+ outb(lcr, serial_iobase + 0x03);
+}
+#endif
+
+static void print_serial(const char *buf)
+{
+ unsigned long len = strlen(buf);
+#ifdef USE_SERIAL
+ unsigned long i;
+ if (!serial_inited) {
+ serial_init();
+ serial_inited = 1;
+ }
+
+ for (i = 0; i < len; i++) {
+ serial_outb(buf[i]);
+ }
+#else
+ asm volatile ("addr32/rep/outsb" : "+S"(buf), "+c"(len) : "d"(0xf1));
+#endif
+}
+
+static void exit(int code)
+{
+ outb(code, 0xf4);
+}
+
+struct regs {
+ u32 eax, ebx, ecx, edx;
+ u32 esi, edi, esp, ebp;
+ u32 eip, eflags;
+};
+
+static u64 gdt[] = {
+ 0,
+ 0x00cf9b000000ffffull, // flat 32-bit code segment
+ 0x00cf93000000ffffull, // flat 32-bit data segment
+};
+
+static struct {
+ u16 limit;
+ void *base;
+} __attribute__((packed)) gdt_descr = {
+ sizeof(gdt) - 1,
+ gdt,
+};
+
+struct insn_desc {
+ u16 ptr;
+ u16 len;
+};
+
+static struct regs inregs, outregs;
+
+static void exec_in_big_real_mode(struct insn_desc *insn)
+{
+ unsigned long tmp;
+ static struct regs save;
+ int i;
+ extern u8 test_insn[], test_insn_end[];
+
+ for (i = 0; i < insn->len; ++i)
+ test_insn[i] = ((u8 *)(unsigned long)insn->ptr)[i];
+ for (; i < test_insn_end - test_insn; ++i)
+ test_insn[i] = 0x90; // nop
+
+ save = inregs;
+ asm volatile(
+ "lgdtl %[gdt_descr] \n\t"
+ "mov %%cr0, %[tmp] \n\t"
+ "or $1, %[tmp] \n\t"
+ "mov %[tmp], %%cr0 \n\t"
+ "mov %[bigseg], %%gs \n\t"
+ "and $-2, %[tmp] \n\t"
+ "mov %[tmp], %%cr0 \n\t"
+
+ "pushw %[save]+36; popfw \n\t"
+ "xchg %%eax, %[save]+0 \n\t"
+ "xchg %%ebx, %[save]+4 \n\t"
+ "xchg %%ecx, %[save]+8 \n\t"
+ "xchg %%edx, %[save]+12 \n\t"
+ "xchg %%esi, %[save]+16 \n\t"
+ "xchg %%edi, %[save]+20 \n\t"
+ "xchg %%esp, %[save]+24 \n\t"
+ "xchg %%ebp, %[save]+28 \n\t"
+
+ "test_insn: . = . + 32\n\t"
+ "test_insn_end: \n\t"
+
+ "xchg %%eax, %[save]+0 \n\t"
+ "xchg %%ebx, %[save]+4 \n\t"
+ "xchg %%ecx, %[save]+8 \n\t"
+ "xchg %%edx, %[save]+12 \n\t"
+ "xchg %%esi, %[save]+16 \n\t"
+ "xchg %%edi, %[save]+20 \n\t"
+ "xchg %%esp, %[save]+24 \n\t"
+ "xchg %%ebp, %[save]+28 \n\t"
+
+ /* Save EFLAGS in outregs*/
+ "pushfl \n\t"
+ "popl %[save]+36 \n\t"
+
+ /* Restore DF for the harness code */
+ "cld\n\t"
+ "xor %[tmp], %[tmp] \n\t"
+ "mov %[tmp], %%gs \n\t"
+ : [tmp]"=&r"(tmp), [save]"+m"(save)
+ : [gdt_descr]"m"(gdt_descr), [bigseg]"r"((short)16)
+ : "cc", "memory"
+ );
+ outregs = save;
+}
+
+#define R_AX 1
+#define R_BX 2
+#define R_CX 4
+#define R_DX 8
+#define R_SI 16
+#define R_DI 32
+#define R_SP 64
+#define R_BP 128
+
+int regs_equal(int ignore)
+{
+ const u32 *p1 = &inregs.eax, *p2 = &outregs.eax; // yuck
+ int i;
+
+ for (i = 0; i < 8; ++i)
+ if (!(ignore & (1 << i)) && p1[i] != p2[i])
+ return 0;
+ return 1;
+}
+
+static void report(const char *name, u16 regs_ignore, _Bool ok)
+{
+ if (!regs_equal(regs_ignore)) {
+ ok = 0;
+ }
+ print_serial(ok ? "PASS: " : "FAIL: ");
+ print_serial(name);
+ print_serial("\n");
+}
+
+#define MK_INSN(name, str) \
+ asm ( \
+ ".pushsection .data.insn \n\t" \
+ "insn_" #name ": \n\t" \
+ ".word 1001f, 1002f - 1001f \n\t" \
+ ".popsection \n\t" \
+ ".pushsection .text.insn, \"ax\" \n\t" \
+ "1001: \n\t" \
+ "insn_code_" #name ": " str " \n\t" \
+ "1002: \n\t" \
+ ".popsection" \
+ ); \
+ extern struct insn_desc insn_##name;
+
+void test_xchg(void)
+{
+ MK_INSN(xchg_test1, "xchg %eax,%eax\n\t");
+ MK_INSN(xchg_test2, "xchg %eax,%ebx\n\t");
+ MK_INSN(xchg_test3, "xchg %eax,%ecx\n\t");
+ MK_INSN(xchg_test4, "xchg %eax,%edx\n\t");
+ MK_INSN(xchg_test5, "xchg %eax,%esi\n\t");
+ MK_INSN(xchg_test6, "xchg %eax,%edi\n\t");
+ MK_INSN(xchg_test7, "xchg %eax,%ebp\n\t");
+ MK_INSN(xchg_test8, "xchg %eax,%esp\n\t");
+
+ inregs = (struct regs){ .eax = 0, .ebx = 1, .ecx = 2, .edx = 3, .esi =
4, .edi = 5, .ebp = 6, .esp = 7};
+
+ exec_in_big_real_mode(&insn_xchg_test1);
+ report("xchg 1", 0, 1);
+
+ exec_in_big_real_mode(&insn_xchg_test2);
+ report("xchg 2", R_AX | R_BX,
+ outregs.eax == inregs.ebx && outregs.ebx == inregs.eax);
+
+ exec_in_big_real_mode(&insn_xchg_test3);
+ report("xchg 3", R_AX | R_CX,
+ outregs.eax == inregs.ecx && outregs.ecx == inregs.eax);
+
+ exec_in_big_real_mode(&insn_xchg_test4);
+ report("xchg 4", R_AX | R_DX,
+ outregs.eax == inregs.edx && outregs.edx == inregs.eax);
+
+ exec_in_big_real_mode(&insn_xchg_test5);
+ report("xchg 5", R_AX | R_SI,
+ outregs.eax == inregs.esi && outregs.esi == inregs.eax);
+
+ exec_in_big_real_mode(&insn_xchg_test6);
+ report("xchg 6", R_AX | R_DI,
+ outregs.eax == inregs.edi && outregs.edi == inregs.eax);
+
+ exec_in_big_real_mode(&insn_xchg_test7);
+ report("xchg 7", R_AX | R_BP,
+ outregs.eax == inregs.ebp && outregs.ebp == inregs.eax);
+
+ exec_in_big_real_mode(&insn_xchg_test8);
+ report("xchg 8", R_AX | R_SP,
+ outregs.eax == inregs.esp && outregs.esp == inregs.eax);
+}
+
+void test_shld(void)
+{
+ MK_INSN(shld_test, "shld $8,%edx,%eax\n\t");
+
+ inregs = (struct regs){ .eax = 0xbe, .edx = 0xef000000 };
+ exec_in_big_real_mode(&insn_shld_test);
+ report("shld", ~0, outregs.eax == 0xbeef);
+}
+
+void test_mov_imm(void)
+{
+ MK_INSN(mov_r32_imm_1, "mov $1234567890, %eax");
+ MK_INSN(mov_r16_imm_1, "mov $1234, %ax");
+ MK_INSN(mov_r8_imm_1, "mov $0x12, %ah");
+ MK_INSN(mov_r8_imm_2, "mov $0x34, %al");
+ MK_INSN(mov_r8_imm_3, "mov $0x12, %ah\n\t" "mov $0x34, %al\n\t");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_mov_r16_imm_1);
+ report("mov 1", R_AX, outregs.eax == 1234);
+
+ /* test mov $imm, %eax */
+ exec_in_big_real_mode(&insn_mov_r32_imm_1);
+ report("mov 2", R_AX, outregs.eax == 1234567890);
+
+ /* test mov $imm, %al/%ah */
+ exec_in_big_real_mode(&insn_mov_r8_imm_1);
+ report("mov 3", R_AX, outregs.eax == 0x1200);
+
+ exec_in_big_real_mode(&insn_mov_r8_imm_2);
+ report("mov 4", R_AX, outregs.eax == 0x34);
+
+ exec_in_big_real_mode(&insn_mov_r8_imm_3);
+ report("mov 5", R_AX, outregs.eax == 0x1234);
+}
+
+void test_sub_imm(void)
+{
+ MK_INSN(sub_r32_imm_1, "mov $1234567890, %eax\n\t" "sub $10, %eax\n\t");
+ MK_INSN(sub_r16_imm_1, "mov $1234, %ax\n\t" "sub $10, %ax\n\t");
+ MK_INSN(sub_r8_imm_1, "mov $0x12, %ah\n\t" "sub $0x10, %ah\n\t");
+ MK_INSN(sub_r8_imm_2, "mov $0x34, %al\n\t" "sub $0x10, %al\n\t");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_sub_r16_imm_1);
+ report("sub 1", R_AX, outregs.eax == 1224);
+
+ /* test mov $imm, %eax */
+ exec_in_big_real_mode(&insn_sub_r32_imm_1);
+ report("sub 2", R_AX, outregs.eax == 1234567880);
+
+ /* test mov $imm, %al/%ah */
+ exec_in_big_real_mode(&insn_sub_r8_imm_1);
+ report("sub 3", R_AX, outregs.eax == 0x0200);
+
+ exec_in_big_real_mode(&insn_sub_r8_imm_2);
+ report("sub 4", R_AX, outregs.eax == 0x24);
+}
+
+void test_xor_imm(void)
+{
+ MK_INSN(xor_r32_imm_1, "mov $1234567890, %eax\n\t" "xor $1234567890,
%eax\n\t");
+ MK_INSN(xor_r16_imm_1, "mov $1234, %ax\n\t" "xor $1234, %ax\n\t");
+ MK_INSN(xor_r8_imm_1, "mov $0x12, %ah\n\t" "xor $0x12, %ah\n\t");
+ MK_INSN(xor_r8_imm_2, "mov $0x34, %al\n\t" "xor $0x34, %al\n\t");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_xor_r16_imm_1);
+ report("xor 1", R_AX, outregs.eax == 0);
+
+ /* test mov $imm, %eax */
+ exec_in_big_real_mode(&insn_xor_r32_imm_1);
+ report("xor 2", R_AX, outregs.eax == 0);
+
+ /* test mov $imm, %al/%ah */
+ exec_in_big_real_mode(&insn_xor_r8_imm_1);
+ report("xor 3", R_AX, outregs.eax == 0);
+
+ exec_in_big_real_mode(&insn_xor_r8_imm_2);
+ report("xor 4", R_AX, outregs.eax == 0);
+}
+
+void test_cmp_imm(void)
+{
+ MK_INSN(cmp_test1, "mov $0x34, %al\n\t"
+ "cmp $0x34, %al\n\t");
+ MK_INSN(cmp_test2, "mov $0x34, %al\n\t"
+ "cmp $0x39, %al\n\t");
+ MK_INSN(cmp_test3, "mov $0x34, %al\n\t"
+ "cmp $0x24, %al\n\t");
+
+ inregs = (struct regs){ 0 };
+
+ /* test cmp imm8 with AL */
+ /* ZF: (bit 6) Zero Flag becomes 1 if an operation results
+ * in a 0 writeback, or 0 register
+ */
+ exec_in_big_real_mode(&insn_cmp_test1);
+ report("cmp 1", ~0, (outregs.eflags & (1<<6)) == (1<<6));
+
+ exec_in_big_real_mode(&insn_cmp_test2);
+ report("cmp 2", ~0, (outregs.eflags & (1<<6)) == 0);
+
+ exec_in_big_real_mode(&insn_cmp_test3);
+ report("cmp 3", ~0, (outregs.eflags & (1<<6)) == 0);
+}
+
+void test_add_imm(void)
+{
+ MK_INSN(add_test1, "mov $0x43211234, %eax \n\t"
+ "add $0x12344321, %eax \n\t");
+ MK_INSN(add_test2, "mov $0x12, %eax \n\t"
+ "add $0x21, %al\n\t");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_add_test1);
+ report("add 1", ~0, outregs.eax == 0x55555555);
+
+ exec_in_big_real_mode(&insn_add_test2);
+ report("add 2", ~0, outregs.eax == 0x33);
+}
+
+void test_eflags_insn(void)
+{
+ MK_INSN(clc, "clc");
+ MK_INSN(stc, "stc");
+ MK_INSN(cli, "cli");
+ MK_INSN(sti, "sti");
+ MK_INSN(cld, "cld");
+ MK_INSN(std, "std");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_clc);
+ report("clc", ~0, (outregs.eflags & 1) == 0);
+
+ exec_in_big_real_mode(&insn_stc);
+ report("stc", ~0, (outregs.eflags & 1) == 1);
+
+ exec_in_big_real_mode(&insn_cli);
+ report("cli", ~0, !(outregs.eflags & (1 << 9)));
+
+ exec_in_big_real_mode(&insn_sti);
+ report("sti", ~0, outregs.eflags & (1 << 9));
+
+ exec_in_big_real_mode(&insn_cld);
+ report("cld", ~0, !(outregs.eflags & (1 << 10)));
+
+ exec_in_big_real_mode(&insn_std);
+ report("std", ~0, (outregs.eflags & (1 << 10)));
+}
+
+void test_io(void)
+{
+ MK_INSN(io_test1, "mov $0xff, %al \n\t"
+ "out %al, $0xe0 \n\t"
+ "mov $0x00, %al \n\t"
+ "in $0xe0, %al \n\t");
+ MK_INSN(io_test2, "mov $0xffff, %ax \n\t"
+ "out %ax, $0xe0 \n\t"
+ "mov $0x0000, %ax \n\t"
+ "in $0xe0, %ax \n\t");
+ MK_INSN(io_test3, "mov $0xffffffff, %eax \n\t"
+ "out %eax, $0xe0 \n\t"
+ "mov $0x000000, %eax \n\t"
+ "in $0xe0, %eax \n\t");
+ MK_INSN(io_test4, "mov $0xe0, %dx \n\t"
+ "mov $0xff, %al \n\t"
+ "out %al, %dx \n\t"
+ "mov $0x00, %al \n\t"
+ "in %dx, %al \n\t");
+ MK_INSN(io_test5, "mov $0xe0, %dx \n\t"
+ "mov $0xffff, %ax \n\t"
+ "out %ax, %dx \n\t"
+ "mov $0x0000, %ax \n\t"
+ "in %dx, %ax \n\t");
+ MK_INSN(io_test6, "mov $0xe0, %dx \n\t"
+ "mov $0xffffffff, %eax \n\t"
+ "out %eax, %dx \n\t"
+ "mov $0x00000000, %eax \n\t"
+ "in %dx, %eax \n\t");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_io_test1);
+ report("pio 1", R_AX, outregs.eax == 0xff);
+
+ exec_in_big_real_mode(&insn_io_test2);
+ report("pio 2", R_AX, outregs.eax == 0xffff);
+
+ exec_in_big_real_mode(&insn_io_test3);
+ report("pio 3", R_AX, outregs.eax == 0xffffffff);
+
+ exec_in_big_real_mode(&insn_io_test4);
+ report("pio 4", R_AX|R_DX, outregs.eax == 0xff);
+
+ exec_in_big_real_mode(&insn_io_test5);
+ report("pio 5", R_AX|R_DX, outregs.eax == 0xffff);
+
+ exec_in_big_real_mode(&insn_io_test6);
+ report("pio 6", R_AX|R_DX, outregs.eax == 0xffffffff);
+}
+
+asm ("retf: lretw");
+extern void retf();
+
+asm ("retf_imm: lretw $10");
+extern void retf_imm();
+
+void test_call(void)
+{
+ u32 esp[16];
+ u32 addr;
+
+ inregs = (struct regs){ 0 };
+ inregs.esp = (u32)esp;
+
+ MK_INSN(call1, "mov $test_function, %eax \n\t"
+ "call *%eax\n\t");
+ MK_INSN(call_near1, "jmp 2f\n\t"
+ "1: mov $0x1234, %eax\n\t"
+ "ret\n\t"
+ "2: call 1b\t");
+ MK_INSN(call_near2, "call 1f\n\t"
+ "jmp 2f\n\t"
+ "1: mov $0x1234, %eax\n\t"
+ "ret\n\t"
+ "2:\t");
+ MK_INSN(call_far1, "lcallw *(%ebx)\n\t");
+ MK_INSN(call_far2, "lcallw $0, $retf\n\t");
+ MK_INSN(ret_imm, "sub $10, %sp; jmp 2f; 1: retw $10; 2: callw 1b");
+ MK_INSN(retf_imm, "sub $10, %sp; lcallw $0, $retf_imm");
+
+ exec_in_big_real_mode(&insn_call1);
+ report("call 1", R_AX, outregs.eax == 0x1234);
+
+ exec_in_big_real_mode(&insn_call_near1);
+ report("call near 1", R_AX, outregs.eax == 0x1234);
+
+ exec_in_big_real_mode(&insn_call_near2);
+ report("call near 2", R_AX, outregs.eax == 0x1234);
+
+ addr = (((unsigned)retf >> 4) << 16) | ((unsigned)retf & 0x0f);
+ inregs.ebx = (unsigned)&addr;
+ exec_in_big_real_mode(&insn_call_far1);
+ report("call far 1", 0, 1);
+
+ exec_in_big_real_mode(&insn_call_far2);
+ report("call far 2", 0, 1);
+
+ exec_in_big_real_mode(&insn_ret_imm);
+ report("ret imm 1", 0, 1);
+
+ exec_in_big_real_mode(&insn_retf_imm);
+ report("retf imm 1", 0, 1);
+}
+
+void test_jcc_short(void)
+{
+ MK_INSN(jnz_short1, "jnz 1f\n\t"
+ "mov $0x1234, %eax\n\t"
+ "1:\n\t");
+ MK_INSN(jnz_short2, "1:\n\t"
+ "cmp $0x1234, %eax\n\t"
+ "mov $0x1234, %eax\n\t"
+ "jnz 1b\n\t");
+ MK_INSN(jmp_short1, "jmp 1f\n\t"
+ "mov $0x1234, %eax\n\t"
+ "1:\n\t");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_jnz_short1);
+ report("jnz short 1", ~0, 1);
+
+ exec_in_big_real_mode(&insn_jnz_short2);
+ report("jnz short 2", R_AX, (outregs.eflags & (1 << 6)));
+
+ exec_in_big_real_mode(&insn_jmp_short1);
+ report("jmp short 1", ~0, 1);
+}
+
+void test_jcc_near(void)
+{
+ /* encode near jmp manually. gas will not do it if offsets < 127 byte */
+ MK_INSN(jnz_near1, ".byte 0x0f, 0x85, 0x06, 0x00\n\t"
+ "mov $0x1234, %eax\n\t");
+ MK_INSN(jnz_near2, "cmp $0x1234, %eax\n\t"
+ "mov $0x1234, %eax\n\t"
+ ".byte 0x0f, 0x85, 0xf0, 0xff\n\t");
+ MK_INSN(jmp_near1, ".byte 0xE9, 0x06, 0x00\n\t"
+ "mov $0x1234, %eax\n\t");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_jnz_near1);
+ report("jnz near 1", 0, 1);
+
+ exec_in_big_real_mode(&insn_jnz_near2);
+ report("jnz near 2", R_AX, outregs.eflags & (1 << 6));
+
+ exec_in_big_real_mode(&insn_jmp_near1);
+ report("jmp near 1", 0, 1);
+}
+
+void test_long_jmp()
+{
+ u32 esp[16];
+
+ inregs = (struct regs){ 0 };
+ inregs.esp = (u32)(esp+16);
+ MK_INSN(long_jmp, "call 1f\n\t"
+ "jmp 2f\n\t"
+ "1: jmp $0, $test_function\n\t"
+ "2:\n\t");
+ exec_in_big_real_mode(&insn_long_jmp);
+ report("jmp far 1", R_AX, outregs.eax == 0x1234);
+}
+
+void test_push_pop()
+{
+ MK_INSN(push32, "mov $0x12345678, %eax\n\t"
+ "push %eax\n\t"
+ "pop %ebx\n\t");
+ MK_INSN(push16, "mov $0x1234, %ax\n\t"
+ "push %ax\n\t"
+ "pop %bx\n\t");
+
+ MK_INSN(push_es, "mov $0x231, %bx\n\t" //Just write a dummy value to
see if it gets overwritten
+ "mov $0x123, %ax\n\t"
+ "mov %ax, %es\n\t"
+ "push %es\n\t"
+ "pop %bx \n\t"
+ );
+ MK_INSN(pop_es, "push %ax\n\t"
+ "pop %es\n\t"
+ "mov %es, %bx\n\t"
+ );
+ MK_INSN(push_pop_ss, "push %ss\n\t"
+ "pushw %ax\n\t"
+ "popw %ss\n\t"
+ "mov %ss, %bx\n\t"
+ "pop %ss\n\t"
+ );
+ MK_INSN(push_pop_fs, "push %fs\n\t"
+ "pushl %eax\n\t"
+ "popl %fs\n\t"
+ "mov %fs, %ebx\n\t"
+ "pop %fs\n\t"
+ );
+ MK_INSN(push_pop_high_esp_bits,
+ "xor $0x12340000, %esp \n\t"
+ "push %ax; \n\t"
+ "xor $0x12340000, %esp \n\t"
+ "pop %bx");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_push32);
+ report("push/pop 1", R_AX|R_BX,
+ outregs.eax == outregs.ebx && outregs.eax == 0x12345678);
+
+ exec_in_big_real_mode(&insn_push16);
+ report("push/pop 2", R_AX|R_BX,
+ outregs.eax == outregs.ebx && outregs.eax == 0x1234);
+
+ exec_in_big_real_mode(&insn_push_es);
+ report("push/pop 3", R_AX|R_BX,
+ outregs.ebx == outregs.eax && outregs.eax == 0x123);
+
+ exec_in_big_real_mode(&insn_pop_es);
+ report("push/pop 4", R_AX|R_BX, outregs.ebx == outregs.eax);
+
+ exec_in_big_real_mode(&insn_push_pop_ss);
+ report("push/pop 5", R_AX|R_BX, outregs.ebx == outregs.eax);
+
+ exec_in_big_real_mode(&insn_push_pop_fs);
+ report("push/pop 6", R_AX|R_BX, outregs.ebx == outregs.eax);
+
+ inregs.eax = 0x9977;
+ inregs.ebx = 0x7799;
+ exec_in_big_real_mode(&insn_push_pop_high_esp_bits);
+ report("push/pop with high bits set in %esp", R_BX, outregs.ebx ==
0x9977);
+}
+
+void test_null(void)
+{
+ MK_INSN(null, "");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_null);
+ report("null", 0, 1);
+}
+
+struct {
+ char stack[500];
+ char top[];
+} tmp_stack;
+
+void test_pusha_popa()
+{
+ MK_INSN(pusha, "pusha\n\t"
+ "pop %edi\n\t"
+ "pop %esi\n\t"
+ "pop %ebp\n\t"
+ "add $4, %esp\n\t"
+ "pop %ebx\n\t"
+ "pop %edx\n\t"
+ "pop %ecx\n\t"
+ "pop %eax\n\t"
+ );
+
+ MK_INSN(popa, "push %eax\n\t"
+ "push %ecx\n\t"
+ "push %edx\n\t"
+ "push %ebx\n\t"
+ "push %esp\n\t"
+ "push %ebp\n\t"
+ "push %esi\n\t"
+ "push %edi\n\t"
+ "popa\n\t"
+ );
+
+ inregs = (struct regs){ .eax = 0, .ebx = 1, .ecx = 2, .edx = 3, .esi =
4, .edi = 5, .ebp = 6, .esp = (unsigned long)&tmp_stack.top };
+
+ exec_in_big_real_mode(&insn_pusha);
+ report("pusha/popa 1", 0, 1);
+
+ exec_in_big_real_mode(&insn_popa);
+ report("pusha/popa 1", 0, 1);
+}
+
+void test_iret()
+{
+ MK_INSN(iret32, "pushf\n\t"
+ "pushl %cs\n\t"
+ "call 1f\n\t" /* a near call will push eip onto the
stack */
+ "jmp 2f\n\t"
+ "1: iret\n\t"
+ "2:\n\t"
+ );
+
+ MK_INSN(iret16, "pushfw\n\t"
+ "pushw %cs\n\t"
+ "callw 1f\n\t"
+ "jmp 2f\n\t"
+ "1: iretw\n\t"
+ "2:\n\t");
+
+ MK_INSN(iret_flags32, "pushfl\n\t"
+ "popl %eax\n\t"
+ "andl $~0x2, %eax\n\t"
+ "orl $0xffc08028, %eax\n\t"
+ "pushl %eax\n\t"
+ "pushl %cs\n\t"
+ "call 1f\n\t"
+ "jmp 2f\n\t"
+ "1: iret\n\t"
+ "2:\n\t");
+
+ MK_INSN(iret_flags16, "pushfw\n\t"
+ "popw %ax\n\t"
+ "and $~0x2, %ax\n\t"
+ "or $0x8028, %ax\n\t"
+ "pushw %ax\n\t"
+ "pushw %cs\n\t"
+ "callw 1f\n\t"
+ "jmp 2f\n\t"
+ "1: iretw\n\t"
+ "2:\n\t");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_iret32);
+ report("iret 1", 0, 1);
+
+ exec_in_big_real_mode(&insn_iret16);
+ report("iret 2", 0, 1);
+
+ exec_in_big_real_mode(&insn_iret_flags32);
+ report("iret 3", R_AX, 1);
+
+ exec_in_big_real_mode(&insn_iret_flags16);
+ report("iret 4", R_AX, 1);
+}
+
+void test_int()
+{
+ inregs = (struct regs){ 0 };
+
+ *(u32 *)(0x11 * 4) = 0x1000; /* Store a pointer to address 0x1000 in
IDT entry 0x11 */
+ *(u8 *)(0x1000) = 0xcf; /* 0x1000 contains an IRET instruction */
+
+ MK_INSN(int11, "int $0x11\n\t");
+
+ exec_in_big_real_mode(&insn_int11);
+ report("int 1", 0, 1);
+}
+
+void test_imul()
+{
+ MK_INSN(imul8_1, "mov $2, %al\n\t"
+ "mov $-4, %cx\n\t"
+ "imul %cl\n\t");
+
+ MK_INSN(imul16_1, "mov $2, %ax\n\t"
+ "mov $-4, %cx\n\t"
+ "imul %cx\n\t");
+
+ MK_INSN(imul32_1, "mov $2, %eax\n\t"
+ "mov $-4, %ecx\n\t"
+ "imul %ecx\n\t");
+
+ MK_INSN(imul8_2, "mov $0x12340002, %eax\n\t"
+ "mov $4, %cx\n\t"
+ "imul %cl\n\t");
+
+ MK_INSN(imul16_2, "mov $2, %ax\n\t"
+ "mov $4, %cx\n\t"
+ "imul %cx\n\t");
+
+ MK_INSN(imul32_2, "mov $2, %eax\n\t"
+ "mov $4, %ecx\n\t"
+ "imul %ecx\n\t");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_imul8_1);
+ report("imul 1", R_AX | R_CX | R_DX, (outregs.eax & 0xff) == (u8)-8);
+
+ exec_in_big_real_mode(&insn_imul16_1);
+ report("imul 2", R_AX | R_CX | R_DX, outregs.eax == (u16)-8);
+
+ exec_in_big_real_mode(&insn_imul32_1);
+ report("imul 3", R_AX | R_CX | R_DX, outregs.eax == (u32)-8);
+
+ exec_in_big_real_mode(&insn_imul8_2);
+ report("imul 4", R_AX | R_CX | R_DX,
+ (outregs.eax & 0xffff) == 8
+ && (outregs.eax & 0xffff0000) == 0x12340000);
+
+ exec_in_big_real_mode(&insn_imul16_2);
+ report("imul 5", R_AX | R_CX | R_DX, outregs.eax == 8);
+
+ exec_in_big_real_mode(&insn_imul32_2);
+ report("imul 6", R_AX | R_CX | R_DX, outregs.eax == 8);
+}
+
+void test_mul()
+{
+ MK_INSN(mul8, "mov $2, %al\n\t"
+ "mov $4, %cx\n\t"
+ "imul %cl\n\t");
+
+ MK_INSN(mul16, "mov $2, %ax\n\t"
+ "mov $4, %cx\n\t"
+ "imul %cx\n\t");
+
+ MK_INSN(mul32, "mov $2, %eax\n\t"
+ "mov $4, %ecx\n\t"
+ "imul %ecx\n\t");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_mul8);
+ report("mul 1", R_AX | R_CX | R_DX, (outregs.eax & 0xff) == 8);
+
+ exec_in_big_real_mode(&insn_mul16);
+ report("mul 2", R_AX | R_CX | R_DX, outregs.eax == 8);
+
+ exec_in_big_real_mode(&insn_mul32);
+ report("mul 3", R_AX | R_CX | R_DX, outregs.eax == 8);
+}
+
+void test_div()
+{
+ MK_INSN(div8, "mov $257, %ax\n\t"
+ "mov $2, %cl\n\t"
+ "div %cl\n\t");
+
+ MK_INSN(div16, "mov $512, %ax\n\t"
+ "mov $5, %cx\n\t"
+ "div %cx\n\t");
+
+ MK_INSN(div32, "mov $512, %eax\n\t"
+ "mov $5, %ecx\n\t"
+ "div %ecx\n\t");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_div8);
+ report("div 1", R_AX | R_CX | R_DX, outregs.eax == 384);
+
+ exec_in_big_real_mode(&insn_div16);
+ report("div 2", R_AX | R_CX | R_DX,
+ outregs.eax == 102 && outregs.edx == 2);
+
+ exec_in_big_real_mode(&insn_div32);
+ report("div 3", R_AX | R_CX | R_DX,
+ outregs.eax == 102 && outregs.edx == 2);
+}
+
+void test_idiv()
+{
+ MK_INSN(idiv8, "mov $256, %ax\n\t"
+ "mov $-2, %cl\n\t"
+ "idiv %cl\n\t");
+
+ MK_INSN(idiv16, "mov $512, %ax\n\t"
+ "mov $-2, %cx\n\t"
+ "idiv %cx\n\t");
+
+ MK_INSN(idiv32, "mov $512, %eax\n\t"
+ "mov $-2, %ecx\n\t"
+ "idiv %ecx\n\t");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_idiv8);
+ report("idiv 1", R_AX | R_CX | R_DX, outregs.eax == (u8)-128);
+
+ exec_in_big_real_mode(&insn_idiv16);
+ report("idiv 2", R_AX | R_CX | R_DX, outregs.eax == (u16)-256);
+
+ exec_in_big_real_mode(&insn_idiv32);
+ report("idiv 3", R_AX | R_CX | R_DX, outregs.eax == (u32)-256);
+}
+
+void test_cbw(void)
+{
+ MK_INSN(cbw, "mov $0xFE, %eax \n\t"
+ "cbw\n\t");
+ MK_INSN(cwde, "mov $0xFFFE, %eax \n\t"
+ "cwde\n\t");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_cbw);
+ report("cbq 1", ~0, outregs.eax == 0xFFFE);
+
+ exec_in_big_real_mode(&insn_cwde);
+ report("cwde 1", ~0, outregs.eax == 0xFFFFFFFE);
+}
+
+void test_loopcc(void)
+{
+ MK_INSN(loop, "mov $10, %ecx\n\t"
+ "1: inc %eax\n\t"
+ "loop 1b\n\t");
+
+ MK_INSN(loope, "mov $10, %ecx\n\t"
+ "mov $1, %eax\n\t"
+ "1: dec %eax\n\t"
+ "loope 1b\n\t");
+
+ MK_INSN(loopne, "mov $10, %ecx\n\t"
+ "mov $5, %eax\n\t"
+ "1: dec %eax\n\t"
+ "loopne 1b\n\t");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_loop);
+ report("LOOPcc short 1", R_AX, outregs.eax == 10);
+
+ exec_in_big_real_mode(&insn_loope);
+ report("LOOPcc short 2", R_AX | R_CX,
+ outregs.eax == -1 && outregs.ecx == 8);
+
+ exec_in_big_real_mode(&insn_loopne);
+ report("LOOPcc short 3", R_AX | R_CX,
+ outregs.eax == 0 && outregs.ecx == 5);
+}
+
+static void test_das(void)
+{
+ short i;
+ u16 nr_fail = 0;
+ static unsigned test_cases[1024] = {
+ 0x46000000, 0x8701a000, 0x9710fa00, 0x97119a00,
+ 0x02000101, 0x8301a101, 0x9310fb01, 0x93119b01,
+ 0x02000202, 0x8301a202, 0x9710fc02, 0x97119c02,
+ 0x06000303, 0x8701a303, 0x9310fd03, 0x93119d03,
+ 0x02000404, 0x8301a404, 0x9310fe04, 0x93119e04,
+ 0x06000505, 0x8701a505, 0x9710ff05, 0x97119f05,
+ 0x06000606, 0x8701a606, 0x56100006, 0x9711a006,
+ 0x02000707, 0x8301a707, 0x12100107, 0x9311a107,
+ 0x02000808, 0x8301a808, 0x12100208, 0x9311a208,
+ 0x06000909, 0x8701a909, 0x16100309, 0x9711a309,
+ 0x1200040a, 0x9301a40a, 0x1210040a, 0x9311a40a,
+ 0x1600050b, 0x9701a50b, 0x1610050b, 0x9711a50b,
+ 0x1600060c, 0x9701a60c, 0x1610060c, 0x9711a60c,
+ 0x1200070d, 0x9301a70d, 0x1210070d, 0x9311a70d,
+ 0x1200080e, 0x9301a80e, 0x1210080e, 0x9311a80e,
+ 0x1600090f, 0x9701a90f, 0x1610090f, 0x9711a90f,
+ 0x02001010, 0x8301b010, 0x16100a10, 0x9711aa10,
+ 0x06001111, 0x8701b111, 0x12100b11, 0x9311ab11,
+ 0x06001212, 0x8701b212, 0x16100c12, 0x9711ac12,
+ 0x02001313, 0x8301b313, 0x12100d13, 0x9311ad13,
+ 0x06001414, 0x8701b414, 0x12100e14, 0x9311ae14,
+ 0x02001515, 0x8301b515, 0x16100f15, 0x9711af15,
+ 0x02001616, 0x8301b616, 0x12101016, 0x9311b016,
+ 0x06001717, 0x8701b717, 0x16101117, 0x9711b117,
+ 0x06001818, 0x8701b818, 0x16101218, 0x9711b218,
+ 0x02001919, 0x8301b919, 0x12101319, 0x9311b319,
+ 0x1600141a, 0x9701b41a, 0x1610141a, 0x9711b41a,
+ 0x1200151b, 0x9301b51b, 0x1210151b, 0x9311b51b,
+ 0x1200161c, 0x9301b61c, 0x1210161c, 0x9311b61c,
+ 0x1600171d, 0x9701b71d, 0x1610171d, 0x9711b71d,
+ 0x1600181e, 0x9701b81e, 0x1610181e, 0x9711b81e,
+ 0x1200191f, 0x9301b91f, 0x1210191f, 0x9311b91f,
+ 0x02002020, 0x8701c020, 0x12101a20, 0x9311ba20,
+ 0x06002121, 0x8301c121, 0x16101b21, 0x9711bb21,
+ 0x06002222, 0x8301c222, 0x12101c22, 0x9311bc22,
+ 0x02002323, 0x8701c323, 0x16101d23, 0x9711bd23,
+ 0x06002424, 0x8301c424, 0x16101e24, 0x9711be24,
+ 0x02002525, 0x8701c525, 0x12101f25, 0x9311bf25,
+ 0x02002626, 0x8701c626, 0x12102026, 0x9711c026,
+ 0x06002727, 0x8301c727, 0x16102127, 0x9311c127,
+ 0x06002828, 0x8301c828, 0x16102228, 0x9311c228,
+ 0x02002929, 0x8701c929, 0x12102329, 0x9711c329,
+ 0x1600242a, 0x9301c42a, 0x1610242a, 0x9311c42a,
+ 0x1200252b, 0x9701c52b, 0x1210252b, 0x9711c52b,
+ 0x1200262c, 0x9701c62c, 0x1210262c, 0x9711c62c,
+ 0x1600272d, 0x9301c72d, 0x1610272d, 0x9311c72d,
+ 0x1600282e, 0x9301c82e, 0x1610282e, 0x9311c82e,
+ 0x1200292f, 0x9701c92f, 0x1210292f, 0x9711c92f,
+ 0x06003030, 0x8301d030, 0x12102a30, 0x9711ca30,
+ 0x02003131, 0x8701d131, 0x16102b31, 0x9311cb31,
+ 0x02003232, 0x8701d232, 0x12102c32, 0x9711cc32,
+ 0x06003333, 0x8301d333, 0x16102d33, 0x9311cd33,
+ 0x02003434, 0x8701d434, 0x16102e34, 0x9311ce34,
+ 0x06003535, 0x8301d535, 0x12102f35, 0x9711cf35,
+ 0x06003636, 0x8301d636, 0x16103036, 0x9311d036,
+ 0x02003737, 0x8701d737, 0x12103137, 0x9711d137,
+ 0x02003838, 0x8701d838, 0x12103238, 0x9711d238,
+ 0x06003939, 0x8301d939, 0x16103339, 0x9311d339,
+ 0x1200343a, 0x9701d43a, 0x1210343a, 0x9711d43a,
+ 0x1600353b, 0x9301d53b, 0x1610353b, 0x9311d53b,
+ 0x1600363c, 0x9301d63c, 0x1610363c, 0x9311d63c,
+ 0x1200373d, 0x9701d73d, 0x1210373d, 0x9711d73d,
+ 0x1200383e, 0x9701d83e, 0x1210383e, 0x9711d83e,
+ 0x1600393f, 0x9301d93f, 0x1610393f, 0x9311d93f,
+ 0x02004040, 0x8301e040, 0x16103a40, 0x9311da40,
+ 0x06004141, 0x8701e141, 0x12103b41, 0x9711db41,
+ 0x06004242, 0x8701e242, 0x16103c42, 0x9311dc42,
+ 0x02004343, 0x8301e343, 0x12103d43, 0x9711dd43,
+ 0x06004444, 0x8701e444, 0x12103e44, 0x9711de44,
+ 0x02004545, 0x8301e545, 0x16103f45, 0x9311df45,
+ 0x02004646, 0x8301e646, 0x12104046, 0x9311e046,
+ 0x06004747, 0x8701e747, 0x16104147, 0x9711e147,
+ 0x06004848, 0x8701e848, 0x16104248, 0x9711e248,
+ 0x02004949, 0x8301e949, 0x12104349, 0x9311e349,
+ 0x1600444a, 0x9701e44a, 0x1610444a, 0x9711e44a,
+ 0x1200454b, 0x9301e54b, 0x1210454b, 0x9311e54b,
+ 0x1200464c, 0x9301e64c, 0x1210464c, 0x9311e64c,
+ 0x1600474d, 0x9701e74d, 0x1610474d, 0x9711e74d,
+ 0x1600484e, 0x9701e84e, 0x1610484e, 0x9711e84e,
+ 0x1200494f, 0x9301e94f, 0x1210494f, 0x9311e94f,
+ 0x06005050, 0x8701f050, 0x12104a50, 0x9311ea50,
+ 0x02005151, 0x8301f151, 0x16104b51, 0x9711eb51,
+ 0x02005252, 0x8301f252, 0x12104c52, 0x9311ec52,
+ 0x06005353, 0x8701f353, 0x16104d53, 0x9711ed53,
+ 0x02005454, 0x8301f454, 0x16104e54, 0x9711ee54,
+ 0x06005555, 0x8701f555, 0x12104f55, 0x9311ef55,
+ 0x06005656, 0x8701f656, 0x16105056, 0x9711f056,
+ 0x02005757, 0x8301f757, 0x12105157, 0x9311f157,
+ 0x02005858, 0x8301f858, 0x12105258, 0x9311f258,
+ 0x06005959, 0x8701f959, 0x16105359, 0x9711f359,
+ 0x1200545a, 0x9301f45a, 0x1210545a, 0x9311f45a,
+ 0x1600555b, 0x9701f55b, 0x1610555b, 0x9711f55b,
+ 0x1600565c, 0x9701f65c, 0x1610565c, 0x9711f65c,
+ 0x1200575d, 0x9301f75d, 0x1210575d, 0x9311f75d,
+ 0x1200585e, 0x9301f85e, 0x1210585e, 0x9311f85e,
+ 0x1600595f, 0x9701f95f, 0x1610595f, 0x9711f95f,
+ 0x06006060, 0x47010060, 0x16105a60, 0x9711fa60,
+ 0x02006161, 0x03010161, 0x12105b61, 0x9311fb61,
+ 0x02006262, 0x03010262, 0x16105c62, 0x9711fc62,
+ 0x06006363, 0x07010363, 0x12105d63, 0x9311fd63,
+ 0x02006464, 0x03010464, 0x12105e64, 0x9311fe64,
+ 0x06006565, 0x07010565, 0x16105f65, 0x9711ff65,
+ 0x06006666, 0x07010666, 0x16106066, 0x57110066,
+ 0x02006767, 0x03010767, 0x12106167, 0x13110167,
+ 0x02006868, 0x03010868, 0x12106268, 0x13110268,
+ 0x06006969, 0x07010969, 0x16106369, 0x17110369,
+ 0x1200646a, 0x1301046a, 0x1210646a, 0x1311046a,
+ 0x1600656b, 0x1701056b, 0x1610656b, 0x1711056b,
+ 0x1600666c, 0x1701066c, 0x1610666c, 0x1711066c,
+ 0x1200676d, 0x1301076d, 0x1210676d, 0x1311076d,
+ 0x1200686e, 0x1301086e, 0x1210686e, 0x1311086e,
+ 0x1600696f, 0x1701096f, 0x1610696f, 0x1711096f,
+ 0x02007070, 0x03011070, 0x16106a70, 0x17110a70,
+ 0x06007171, 0x07011171, 0x12106b71, 0x13110b71,
+ 0x06007272, 0x07011272, 0x16106c72, 0x17110c72,
+ 0x02007373, 0x03011373, 0x12106d73, 0x13110d73,
+ 0x06007474, 0x07011474, 0x12106e74, 0x13110e74,
+ 0x02007575, 0x03011575, 0x16106f75, 0x17110f75,
+ 0x02007676, 0x03011676, 0x12107076, 0x13111076,
+ 0x06007777, 0x07011777, 0x16107177, 0x17111177,
+ 0x06007878, 0x07011878, 0x16107278, 0x17111278,
+ 0x02007979, 0x03011979, 0x12107379, 0x13111379,
+ 0x1600747a, 0x1701147a, 0x1610747a, 0x1711147a,
+ 0x1200757b, 0x1301157b, 0x1210757b, 0x1311157b,
+ 0x1200767c, 0x1301167c, 0x1210767c, 0x1311167c,
+ 0x1600777d, 0x1701177d, 0x1610777d, 0x1711177d,
+ 0x1600787e, 0x1701187e, 0x1610787e, 0x1711187e,
+ 0x1200797f, 0x1301197f, 0x1210797f, 0x1311197f,
+ 0x82008080, 0x03012080, 0x12107a80, 0x13111a80,
+ 0x86008181, 0x07012181, 0x16107b81, 0x17111b81,
+ 0x86008282, 0x07012282, 0x12107c82, 0x13111c82,
+ 0x82008383, 0x03012383, 0x16107d83, 0x17111d83,
+ 0x86008484, 0x07012484, 0x16107e84, 0x17111e84,
+ 0x82008585, 0x03012585, 0x12107f85, 0x13111f85,
+ 0x82008686, 0x03012686, 0x92108086, 0x13112086,
+ 0x86008787, 0x07012787, 0x96108187, 0x17112187,
+ 0x86008888, 0x07012888, 0x96108288, 0x17112288,
+ 0x82008989, 0x03012989, 0x92108389, 0x13112389,
+ 0x9600848a, 0x1701248a, 0x9610848a, 0x1711248a,
+ 0x9200858b, 0x1301258b, 0x9210858b, 0x1311258b,
+ 0x9200868c, 0x1301268c, 0x9210868c, 0x1311268c,
+ 0x9600878d, 0x1701278d, 0x9610878d, 0x1711278d,
+ 0x9600888e, 0x1701288e, 0x9610888e, 0x1711288e,
+ 0x9200898f, 0x1301298f, 0x9210898f, 0x1311298f,
+ 0x86009090, 0x07013090, 0x92108a90, 0x13112a90,
+ 0x82009191, 0x03013191, 0x96108b91, 0x17112b91,
+ 0x82009292, 0x03013292, 0x92108c92, 0x13112c92,
+ 0x86009393, 0x07013393, 0x96108d93, 0x17112d93,
+ 0x82009494, 0x03013494, 0x96108e94, 0x17112e94,
+ 0x86009595, 0x07013595, 0x92108f95, 0x13112f95,
+ 0x86009696, 0x07013696, 0x96109096, 0x17113096,
+ 0x82009797, 0x03013797, 0x92109197, 0x13113197,
+ 0x82009898, 0x03013898, 0x92109298, 0x13113298,
+ 0x86009999, 0x07013999, 0x96109399, 0x17113399,
+ 0x1300349a, 0x1301349a, 0x1310349a, 0x1311349a,
+ 0x1700359b, 0x1701359b, 0x1710359b, 0x1711359b,
+ 0x1700369c, 0x1701369c, 0x1710369c, 0x1711369c,
+ 0x1300379d, 0x1301379d, 0x1310379d, 0x1311379d,
+ 0x1300389e, 0x1301389e, 0x1310389e, 0x1311389e,
+ 0x1700399f, 0x1701399f, 0x1710399f, 0x1711399f,
+ 0x030040a0, 0x030140a0, 0x17103aa0, 0x17113aa0,
+ 0x070041a1, 0x070141a1, 0x13103ba1, 0x13113ba1,
+ 0x070042a2, 0x070142a2, 0x17103ca2, 0x17113ca2,
+ 0x030043a3, 0x030143a3, 0x13103da3, 0x13113da3,
+ 0x070044a4, 0x070144a4, 0x13103ea4, 0x13113ea4,
+ 0x030045a5, 0x030145a5, 0x17103fa5, 0x17113fa5,
+ 0x030046a6, 0x030146a6, 0x131040a6, 0x131140a6,
+ 0x070047a7, 0x070147a7, 0x171041a7, 0x171141a7,
+ 0x070048a8, 0x070148a8, 0x171042a8, 0x171142a8,
+ 0x030049a9, 0x030149a9, 0x131043a9, 0x131143a9,
+ 0x170044aa, 0x170144aa, 0x171044aa, 0x171144aa,
+ 0x130045ab, 0x130145ab, 0x131045ab, 0x131145ab,
+ 0x130046ac, 0x130146ac, 0x131046ac, 0x131146ac,
+ 0x170047ad, 0x170147ad, 0x171047ad, 0x171147ad,
+ 0x170048ae, 0x170148ae, 0x171048ae, 0x171148ae,
+ 0x130049af, 0x130149af, 0x131049af, 0x131149af,
+ 0x070050b0, 0x070150b0, 0x13104ab0, 0x13114ab0,
+ 0x030051b1, 0x030151b1, 0x17104bb1, 0x17114bb1,
+ 0x030052b2, 0x030152b2, 0x13104cb2, 0x13114cb2,
+ 0x070053b3, 0x070153b3, 0x17104db3, 0x17114db3,
+ 0x030054b4, 0x030154b4, 0x17104eb4, 0x17114eb4,
+ 0x070055b5, 0x070155b5, 0x13104fb5, 0x13114fb5,
+ 0x070056b6, 0x070156b6, 0x171050b6, 0x171150b6,
+ 0x030057b7, 0x030157b7, 0x131051b7, 0x131151b7,
+ 0x030058b8, 0x030158b8, 0x131052b8, 0x131152b8,
+ 0x070059b9, 0x070159b9, 0x171053b9, 0x171153b9,
+ 0x130054ba, 0x130154ba, 0x131054ba, 0x131154ba,
+ 0x170055bb, 0x170155bb, 0x171055bb, 0x171155bb,
+ 0x170056bc, 0x170156bc, 0x171056bc, 0x171156bc,
+ 0x130057bd, 0x130157bd, 0x131057bd, 0x131157bd,
+ 0x130058be, 0x130158be, 0x131058be, 0x131158be,
+ 0x170059bf, 0x170159bf, 0x171059bf, 0x171159bf,
+ 0x070060c0, 0x070160c0, 0x17105ac0, 0x17115ac0,
+ 0x030061c1, 0x030161c1, 0x13105bc1, 0x13115bc1,
+ 0x030062c2, 0x030162c2, 0x17105cc2, 0x17115cc2,
+ 0x070063c3, 0x070163c3, 0x13105dc3, 0x13115dc3,
+ 0x030064c4, 0x030164c4, 0x13105ec4, 0x13115ec4,
+ 0x070065c5, 0x070165c5, 0x17105fc5, 0x17115fc5,
+ 0x070066c6, 0x070166c6, 0x171060c6, 0x171160c6,
+ 0x030067c7, 0x030167c7, 0x131061c7, 0x131161c7,
+ 0x030068c8, 0x030168c8, 0x131062c8, 0x131162c8,
+ 0x070069c9, 0x070169c9, 0x171063c9, 0x171163c9,
+ 0x130064ca, 0x130164ca, 0x131064ca, 0x131164ca,
+ 0x170065cb, 0x170165cb, 0x171065cb, 0x171165cb,
+ 0x170066cc, 0x170166cc, 0x171066cc, 0x171166cc,
+ 0x130067cd, 0x130167cd, 0x131067cd, 0x131167cd,
+ 0x130068ce, 0x130168ce, 0x131068ce, 0x131168ce,
+ 0x170069cf, 0x170169cf, 0x171069cf, 0x171169cf,
+ 0x030070d0, 0x030170d0, 0x17106ad0, 0x17116ad0,
+ 0x070071d1, 0x070171d1, 0x13106bd1, 0x13116bd1,
+ 0x070072d2, 0x070172d2, 0x17106cd2, 0x17116cd2,
+ 0x030073d3, 0x030173d3, 0x13106dd3, 0x13116dd3,
+ 0x070074d4, 0x070174d4, 0x13106ed4, 0x13116ed4,
+ 0x030075d5, 0x030175d5, 0x17106fd5, 0x17116fd5,
+ 0x030076d6, 0x030176d6, 0x131070d6, 0x131170d6,
+ 0x070077d7, 0x070177d7, 0x171071d7, 0x171171d7,
+ 0x070078d8, 0x070178d8, 0x171072d8, 0x171172d8,
+ 0x030079d9, 0x030179d9, 0x131073d9, 0x131173d9,
+ 0x170074da, 0x170174da, 0x171074da, 0x171174da,
+ 0x130075db, 0x130175db, 0x131075db, 0x131175db,
+ 0x130076dc, 0x130176dc, 0x131076dc, 0x131176dc,
+ 0x170077dd, 0x170177dd, 0x171077dd, 0x171177dd,
+ 0x170078de, 0x170178de, 0x171078de, 0x171178de,
+ 0x130079df, 0x130179df, 0x131079df, 0x131179df,
+ 0x830080e0, 0x830180e0, 0x13107ae0, 0x13117ae0,
+ 0x870081e1, 0x870181e1, 0x17107be1, 0x17117be1,
+ 0x870082e2, 0x870182e2, 0x13107ce2, 0x13117ce2,
+ 0x830083e3, 0x830183e3, 0x17107de3, 0x17117de3,
+ 0x870084e4, 0x870184e4, 0x17107ee4, 0x17117ee4,
+ 0x830085e5, 0x830185e5, 0x13107fe5, 0x13117fe5,
+ 0x830086e6, 0x830186e6, 0x931080e6, 0x931180e6,
+ 0x870087e7, 0x870187e7, 0x971081e7, 0x971181e7,
+ 0x870088e8, 0x870188e8, 0x971082e8, 0x971182e8,
+ 0x830089e9, 0x830189e9, 0x931083e9, 0x931183e9,
+ 0x970084ea, 0x970184ea, 0x971084ea, 0x971184ea,
+ 0x930085eb, 0x930185eb, 0x931085eb, 0x931185eb,
+ 0x930086ec, 0x930186ec, 0x931086ec, 0x931186ec,
+ 0x970087ed, 0x970187ed, 0x971087ed, 0x971187ed,
+ 0x970088ee, 0x970188ee, 0x971088ee, 0x971188ee,
+ 0x930089ef, 0x930189ef, 0x931089ef, 0x931189ef,
+ 0x870090f0, 0x870190f0, 0x93108af0, 0x93118af0,
+ 0x830091f1, 0x830191f1, 0x97108bf1, 0x97118bf1,
+ 0x830092f2, 0x830192f2, 0x93108cf2, 0x93118cf2,
+ 0x870093f3, 0x870193f3, 0x97108df3, 0x97118df3,
+ 0x830094f4, 0x830194f4, 0x97108ef4, 0x97118ef4,
+ 0x870095f5, 0x870195f5, 0x93108ff5, 0x93118ff5,
+ 0x870096f6, 0x870196f6, 0x971090f6, 0x971190f6,
+ 0x830097f7, 0x830197f7, 0x931091f7, 0x931191f7,
+ 0x830098f8, 0x830198f8, 0x931092f8, 0x931192f8,
+ 0x870099f9, 0x870199f9, 0x971093f9, 0x971193f9,
+ 0x930094fa, 0x930194fa, 0x931094fa, 0x931194fa,
+ 0x970095fb, 0x970195fb, 0x971095fb, 0x971195fb,
+ 0x970096fc, 0x970196fc, 0x971096fc, 0x971196fc,
+ 0x930097fd, 0x930197fd, 0x931097fd, 0x931197fd,
+ 0x930098fe, 0x930198fe, 0x931098fe, 0x931198fe,
+ 0x970099ff, 0x970199ff, 0x971099ff, 0x971199ff,
+ };
+
+ MK_INSN(das, "das");
+
+ inregs = (struct regs){ 0 };
+
+ for (i = 0; i < 1024; ++i) {
+ unsigned tmp = test_cases[i];
+ inregs.eax = tmp & 0xff;
+ inregs.eflags = (tmp >> 16) & 0xff;
+ exec_in_big_real_mode(&insn_das);
+ if (!regs_equal(R_AX)
+ || outregs.eax != ((tmp >> 8) & 0xff)
+ || (outregs.eflags & 0xff) != (tmp >> 24)) {
+ ++nr_fail;
+ break;
+ }
+ }
+ report("DAS", ~0, nr_fail == 0);
+}
+
+void test_cwd_cdq()
+{
+ /* Sign-bit set */
+ MK_INSN(cwd_1, "mov $0x8000, %ax\n\t"
+ "cwd\n\t");
+
+ /* Sign-bit not set */
+ MK_INSN(cwd_2, "mov $0x1000, %ax\n\t"
+ "cwd\n\t");
+
+ /* Sign-bit set */
+ MK_INSN(cdq_1, "mov $0x80000000, %eax\n\t"
+ "cdq\n\t");
+
+ /* Sign-bit not set */
+ MK_INSN(cdq_2, "mov $0x10000000, %eax\n\t"
+ "cdq\n\t");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_cwd_1);
+ report("cwd 1", R_AX | R_DX,
+ outregs.eax == 0x8000 && outregs.edx == 0xffff);
+
+ exec_in_big_real_mode(&insn_cwd_2);
+ report("cwd 2", R_AX | R_DX,
+ outregs.eax == 0x1000 && outregs.edx == 0);
+
+ exec_in_big_real_mode(&insn_cdq_1);
+ report("cdq 1", R_AX | R_DX,
+ outregs.eax == 0x80000000 && outregs.edx == 0xffffffff);
+
+ exec_in_big_real_mode(&insn_cdq_2);
+ report("cdq 2", R_AX | R_DX,
+ outregs.eax == 0x10000000 && outregs.edx == 0);
+}
+
+static struct {
+ void *address;
+ unsigned short sel;
+} __attribute__((packed)) desc = {
+ (void *)0x1234,
+ 0x10,
+};
+
+void test_lds_lss()
+{
+ inregs = (struct regs){ .ebx = (unsigned long)&desc };
+
+ MK_INSN(lds, "push %ds\n\t"
+ "lds (%ebx), %eax\n\t"
+ "mov %ds, %ebx\n\t"
+ "pop %ds\n\t");
+ exec_in_big_real_mode(&insn_lds);
+ report("lds", R_AX | R_BX,
+ outregs.eax == (unsigned long)desc.address &&
+ outregs.ebx == desc.sel);
+
+ MK_INSN(les, "push %es\n\t"
+ "les (%ebx), %eax\n\t"
+ "mov %es, %ebx\n\t"
+ "pop %es\n\t");
+ exec_in_big_real_mode(&insn_les);
+ report("les", R_AX | R_BX,
+ outregs.eax == (unsigned long)desc.address &&
+ outregs.ebx == desc.sel);
+
+ MK_INSN(lfs, "push %fs\n\t"
+ "lfs (%ebx), %eax\n\t"
+ "mov %fs, %ebx\n\t"
+ "pop %fs\n\t");
+ exec_in_big_real_mode(&insn_lfs);
+ report("lfs", R_AX | R_BX,
+ outregs.eax == (unsigned long)desc.address &&
+ outregs.ebx == desc.sel);
+
+ MK_INSN(lgs, "push %gs\n\t"
+ "lgs (%ebx), %eax\n\t"
+ "mov %gs, %ebx\n\t"
+ "pop %gs\n\t");
+ exec_in_big_real_mode(&insn_lgs);
+ report("lgs", R_AX | R_BX,
+ outregs.eax == (unsigned long)desc.address &&
+ outregs.ebx == desc.sel);
+
+ MK_INSN(lss, "push %ss\n\t"
+ "lss (%ebx), %eax\n\t"
+ "mov %ss, %ebx\n\t"
+ "pop %ss\n\t");
+ exec_in_big_real_mode(&insn_lss);
+ report("lss", R_AX | R_BX,
+ outregs.eax == (unsigned long)desc.address &&
+ outregs.ebx == desc.sel);
+}
+
+void test_jcxz(void)
+{
+ MK_INSN(jcxz1, "jcxz 1f\n\t"
+ "mov $0x1234, %eax\n\t"
+ "1:\n\t");
+ MK_INSN(jcxz2, "mov $0x100, %ecx\n\t"
+ "jcxz 1f\n\t"
+ "mov $0x1234, %eax\n\t"
+ "mov $0, %ecx\n\t"
+ "1:\n\t");
+ MK_INSN(jcxz3, "mov $0x10000, %ecx\n\t"
+ "jcxz 1f\n\t"
+ "mov $0x1234, %eax\n\t"
+ "1:\n\t");
+ MK_INSN(jecxz1, "jecxz 1f\n\t"
+ "mov $0x1234, %eax\n\t"
+ "1:\n\t");
+ MK_INSN(jecxz2, "mov $0x10000, %ecx\n\t"
+ "jecxz 1f\n\t"
+ "mov $0x1234, %eax\n\t"
+ "mov $0, %ecx\n\t"
+ "1:\n\t");
+
+ inregs = (struct regs){ 0 };
+
+ exec_in_big_real_mode(&insn_jcxz1);
+ report("jcxz short 1", 0, 1);
+
+ exec_in_big_real_mode(&insn_jcxz2);
+ report("jcxz short 2", R_AX, outregs.eax == 0x1234);
+
+ exec_in_big_real_mode(&insn_jcxz3);
+ report("jcxz short 3", R_CX, outregs.ecx == 0x10000);
+
+ exec_in_big_real_mode(&insn_jecxz1);
+ report("jecxz short 1", 0, 1);
+
+ exec_in_big_real_mode(&insn_jecxz2);
+ report("jecxz short 2", R_AX, outregs.eax == 0x1234);
+}
+
+static void test_cpuid(void)
+{
+ MK_INSN(cpuid, "cpuid");
+ unsigned function = 0x1234;
+ unsigned eax, ebx, ecx, edx;
+
+ inregs.eax = eax = function;
+ asm("cpuid" : "+a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx));
+ exec_in_big_real_mode(&insn_cpuid);
+ report("cpuid", R_AX|R_BX|R_CX|R_DX,
+ outregs.eax == eax && outregs.ebx == ebx
+ && outregs.ecx == ecx && outregs.edx == edx);
+}
+
+static void test_ss_base_for_esp_ebp(void)
+{
+ MK_INSN(ssrel1, "mov %ss, %ax; mov %bx, %ss; movl (%ebp), %ebx; mov %ax,
%ss");
+ MK_INSN(ssrel2, "mov %ss, %ax; mov %bx, %ss; movl (%ebp,%edi,8), %ebx; mov
%ax, %ss");
+ static unsigned array[] = { 0x12345678, 0, 0, 0, 0x87654321 };
+
+ inregs.ebx = 1;
+ inregs.ebp = (unsigned)array;
+ exec_in_big_real_mode(&insn_ssrel1);
+ report("ss relative addressing (1)", R_AX | R_BX, outregs.ebx ==
0x87654321);
+ inregs.ebx = 1;
+ inregs.ebp = (unsigned)array;
+ inregs.edi = 0;
+ exec_in_big_real_mode(&insn_ssrel2);
+ report("ss relative addressing (2)", R_AX | R_BX, outregs.ebx ==
0x87654321);
+}
+
+static void test_sgdt_sidt(void)
+{
+ MK_INSN(sgdt, "sgdtw (%eax)");
+ MK_INSN(sidt, "sidtw (%eax)");
+ unsigned x, y;
+
+ inregs.eax = (unsigned)&y;
+ asm volatile("sgdtw %0" : "=m"(x));
+ exec_in_big_real_mode(&insn_sgdt);
+ report("sgdt", 0, x == y);
+
+ inregs.eax = (unsigned)&y;
+ asm volatile("sidtw %0" : "=m"(x));
+ exec_in_big_real_mode(&insn_sidt);
+ report("sidt", 0, x == y);
+}
+
+static void test_lahf(void)
+{
+ MK_INSN(lahf, "pushfw; mov %al, (%esp); popfw; lahf");
+
+ inregs.eax = 0xc7;
+ exec_in_big_real_mode(&insn_lahf);
+ report("lahf", R_AX, (outregs.eax >> 8) == inregs.eax);
+}
+
+static void test_movzx_movsx(void)
+{
+ MK_INSN(movsx, "movsx %al, %ebx");
+ MK_INSN(movzx, "movzx %al, %ebx");
+ MK_INSN(movzsah, "movsx %ah, %ebx");
+ MK_INSN(movzxah, "movzx %ah, %ebx");
+
+ inregs.eax = 0x1234569c;
+ inregs.esp = 0xffff;
+ exec_in_big_real_mode(&insn_movsx);
+ report("movsx", R_BX, outregs.ebx == (signed char)inregs.eax);
+ exec_in_big_real_mode(&insn_movzx);
+ report("movzx", R_BX, outregs.ebx == (unsigned char)inregs.eax);
+ exec_in_big_real_mode(&insn_movzsah);
+ report("movsx ah", R_BX, outregs.ebx == (signed char)(inregs.eax>>8));
+ exec_in_big_real_mode(&insn_movzxah);
+ report("movzx ah", R_BX, outregs.ebx == (unsigned char)(inregs.eax >> 8));
+}
+
+static void test_bswap(void)
+{
+ MK_INSN(bswap, "bswap %ecx");
+
+ inregs.ecx = 0x12345678;
+ exec_in_big_real_mode(&insn_bswap);
+ report("bswap", R_CX, outregs.ecx == 0x78563412);
+}
+
+static void test_aad(void)
+{
+ MK_INSN(aad, "aad");
+
+ inregs.eax = 0x12345678;
+ exec_in_big_real_mode(&insn_aad);
+ report("aad", R_AX, outregs.eax == 0x123400d4);
+}
+
+static void test_aam(void)
+{
+ MK_INSN(aam, "aam");
+
+ inregs.eax = 0x76543210;
+ exec_in_big_real_mode(&insn_aam);
+ report("aam", R_AX, outregs.eax == 0x76540106);
+}
+
+static void test_xlat(void)
+{
+ MK_INSN(xlat, "xlat");
+ u8 table[256];
+ int i;
+
+ for (i = 0; i < 256; i++) {
+ table[i] = i + 1;
+ }
+
+ inregs.eax = 0x89abcdef;
+ inregs.ebx = (u32)table;
+ exec_in_big_real_mode(&insn_xlat);
+ report("xlat", R_AX, outregs.eax == 0x89abcdf0);
+}
+
+static void test_salc(void)
+{
+ MK_INSN(clc_salc, "clc; .byte 0xd6");
+ MK_INSN(stc_salc, "stc; .byte 0xd6");
+
+ inregs.eax = 0x12345678;
+ exec_in_big_real_mode(&insn_clc_salc);
+ report("salc (1)", R_AX, outregs.eax == 0x12345600);
+ exec_in_big_real_mode(&insn_stc_salc);
+ report("salc (2)", R_AX, outregs.eax == 0x123456ff);
+}
+
+static void test_fninit(void)
+{
+ u16 fcw = -1, fsw = -1;
+ MK_INSN(fninit, "fninit ; fnstsw (%eax) ; fnstcw (%ebx)");
+
+ inregs.eax = (u32)&fsw;
+ inregs.ebx = (u32)&fcw;
+
+ exec_in_big_real_mode(&insn_fninit);
+ report("fninit", 0, fsw == 0 && (fcw & 0x103f) == 0x003f);
+}
+
+static void test_nopl(void)
+{
+ MK_INSN(nopl1, ".byte 0x90\n\r"); // 1 byte nop
+ MK_INSN(nopl2, ".byte 0x66, 0x90\n\r"); // 2 bytes nop
+ MK_INSN(nopl3, ".byte 0x0f, 0x1f, 0x00\n\r"); // 3 bytes nop
+ MK_INSN(nopl4, ".byte 0x0f, 0x1f, 0x40, 0x00\n\r"); // 4 bytes nop
+ exec_in_big_real_mode(&insn_nopl1);
+ exec_in_big_real_mode(&insn_nopl2);
+ exec_in_big_real_mode(&insn_nopl3);
+ exec_in_big_real_mode(&insn_nopl4);
+ report("nopl", 0, 1);
+}
+
+void realmode_start(void)
+{
+ test_null();
+
+ test_shld();
+ test_push_pop();
+ test_pusha_popa();
+ test_mov_imm();
+ test_cmp_imm();
+ test_add_imm();
+ test_sub_imm();
+ test_xor_imm();
+ test_io();
+ test_eflags_insn();
+ test_jcc_short();
+ test_jcc_near();
+ /* test_call() uses short jump so call it after testing jcc */
+ test_call();
+ /* long jmp test uses call near so test it after testing call */
+ test_long_jmp();
+ test_xchg();
+ test_iret();
+ test_int();
+ test_imul();
+ test_mul();
+ test_div();
+ test_idiv();
+ test_loopcc();
+ test_cbw();
+ test_cwd_cdq();
+ test_das();
+ test_lds_lss();
+ test_jcxz();
+ test_cpuid();
+ test_ss_base_for_esp_ebp();
+ test_sgdt_sidt();
+ test_lahf();
+ test_movzx_movsx();
+ test_bswap();
+ test_aad();
+ test_aam();
+ test_xlat();
+ test_salc();
+ test_fninit();
+ test_nopl();
+
+ exit(0);
+}
+
+unsigned long long r_gdt[] = { 0, 0x9b000000ffff, 0x93000000ffff };
+
+struct __attribute__((packed)) {
+ unsigned short limit;
+ void *base;
+} r_gdt_descr = { sizeof(r_gdt) - 1, &r_gdt };
+
+asm(
+ ".section .init \n\t"
+
+ ".code32 \n\t"
+
+ "mb_magic = 0x1BADB002 \n\t"
+ "mb_flags = 0x0 \n\t"
+
+ "# multiboot header \n\t"
+ ".long mb_magic, mb_flags, 0 - (mb_magic + mb_flags) \n\t"
+
+ ".globl start \n\t"
+ ".data \n\t"
+ ". = . + 4096 \n\t"
+ "stacktop: \n\t"
+
+ ".text \n\t"
+ "start: \n\t"
+ "lgdt r_gdt_descr \n\t"
+ "ljmp $8, $1f; 1: \n\t"
+ ".code16gcc \n\t"
+ "mov $16, %eax \n\t"
+ "mov %ax, %ds \n\t"
+ "mov %ax, %es \n\t"
+ "mov %ax, %fs \n\t"
+ "mov %ax, %gs \n\t"
+ "mov %ax, %ss \n\t"
+ "mov %cr0, %eax \n\t"
+ "btc $0, %eax \n\t"
+ "mov %eax, %cr0 \n\t"
+ "ljmp $0, $realmode_entry \n\t"
+
+ "realmode_entry: \n\t"
+
+ "xor %ax, %ax \n\t"
+ "mov %ax, %ds \n\t"
+ "mov %ax, %es \n\t"
+ "mov %ax, %ss \n\t"
+ "mov %ax, %fs \n\t"
+ "mov %ax, %gs \n\t"
+ "mov $stacktop, %esp\n\t"
+ "ljmp $0, $realmode_start \n\t"
+
+ ".code16gcc \n\t"
+ );
diff --git a/kvm-unittest/x86/rmap_chain.c b/kvm-unittest/x86/rmap_chain.c
new file mode 100644
index 0000000..0df1bcb
--- /dev/null
+++ b/kvm-unittest/x86/rmap_chain.c
@@ -0,0 +1,54 @@
+/* test long rmap chains */
+
+#include "libcflat.h"
+#include "fwcfg.h"
+#include "vm.h"
+#include "smp.h"
+
+void print(const char *s);
+
+static unsigned int inl(unsigned short port)
+{
+ unsigned int val;
+ asm volatile ("inl %w1, %0":"=a" (val):"Nd" (port));
+ return val;
+}
+
+int main (void)
+{
+ int i;
+ int nr_pages;
+ void *target_page, *virt_addr;
+
+ setup_vm();
+
+ nr_pages = fwcfg_get_u64(FW_CFG_RAM_SIZE) / PAGE_SIZE;
+ nr_pages -= 1000;
+ target_page = alloc_page();
+
+ virt_addr = (void *) 0xfffffa000;
+ for (i = 0; i < nr_pages; i++) {
+ install_page(phys_to_virt(read_cr3()), virt_to_phys(target_page),
+ virt_addr);
+ virt_addr += PAGE_SIZE;
+ }
+ printf("created %d mappings\n", nr_pages);
+
+ virt_addr = (void *) 0xfffffa000;
+ for (i = 0; i < nr_pages; i++) {
+ unsigned long *touch = virt_addr;
+
+ *touch = 0;
+ virt_addr += PAGE_SIZE;
+ }
+ printf("instantiated mappings\n");
+
+ virt_addr += PAGE_SIZE;
+ install_pte(phys_to_virt(read_cr3()), 1, virt_addr,
+ 0 | PTE_PRESENT | PTE_WRITE, target_page);
+
+ *(unsigned long *)virt_addr = 0;
+ printf("PASS\n");
+
+ return 0;
+}
diff --git a/kvm-unittest/x86/s3.c b/kvm-unittest/x86/s3.c
new file mode 100644
index 0000000..71d3ff9
--- /dev/null
+++ b/kvm-unittest/x86/s3.c
@@ -0,0 +1,203 @@
+#include "libcflat.h"
+
+struct rsdp_descriptor { /* Root System Descriptor Pointer */
+ u64 signature; /* ACPI signature, contains "RSD PTR " */
+ u8 checksum; /* To make sum of struct == 0 */
+ u8 oem_id [6]; /* OEM identification */
+ u8 revision; /* Must be 0 for 1.0, 2 for 2.0 */
+ u32 rsdt_physical_address; /* 32-bit physical address of RSDT */
+ u32 length; /* XSDT Length in bytes including hdr */
+ u64 xsdt_physical_address; /* 64-bit physical address of XSDT */
+ u8 extended_checksum; /* Checksum of entire table */
+ u8 reserved [3]; /* Reserved field must be 0 */
+};
+
+#define ACPI_TABLE_HEADER_DEF /* ACPI common table header */ \
+ u32 signature; /* ACPI signature (4 ASCII characters) */ \
+ u32 length; /* Length of table, in bytes, including header
*/ \
+ u8 revision; /* ACPI Specification minor version # */ \
+ u8 checksum; /* To make sum of entire table == 0 */ \
+ u8 oem_id [6]; /* OEM identification */ \
+ u8 oem_table_id [8]; /* OEM table identification */ \
+ u32 oem_revision; /* OEM revision number */ \
+ u8 asl_compiler_id [4]; /* ASL compiler vendor ID */ \
+ u32 asl_compiler_revision; /* ASL compiler revision number */
+
+#define RSDT_SIGNATURE 0x54445352
+struct rsdt_descriptor_rev1 {
+ ACPI_TABLE_HEADER_DEF
+ u32 table_offset_entry[0];
+};
+
+#define FACP_SIGNATURE 0x50434146 // FACP
+struct fadt_descriptor_rev1
+{
+ ACPI_TABLE_HEADER_DEF /* ACPI common table header */
+ u32 firmware_ctrl; /* Physical address of FACS */
+ u32 dsdt; /* Physical address of DSDT */
+ u8 model; /* System Interrupt Model */
+ u8 reserved1; /* Reserved */
+ u16 sci_int; /* System vector of SCI interrupt */
+ u32 smi_cmd; /* Port address of SMI command port */
+ u8 acpi_enable; /* Value to write to smi_cmd to enable ACPI */
+ u8 acpi_disable; /* Value to write to smi_cmd to disable ACPI */
+ u8 S4bios_req; /* Value to write to SMI CMD to enter S4BIOS
state */
+ u8 reserved2; /* Reserved - must be zero */
+ u32 pm1a_evt_blk; /* Port address of Power Mgt 1a acpi_event Reg
Blk */
+ u32 pm1b_evt_blk; /* Port address of Power Mgt 1b acpi_event Reg
Blk */
+ u32 pm1a_cnt_blk; /* Port address of Power Mgt 1a Control Reg
Blk */
+ u32 pm1b_cnt_blk; /* Port address of Power Mgt 1b Control Reg
Blk */
+ u32 pm2_cnt_blk; /* Port address of Power Mgt 2 Control Reg Blk
*/
+ u32 pm_tmr_blk; /* Port address of Power Mgt Timer Ctrl Reg
Blk */
+ u32 gpe0_blk; /* Port addr of General Purpose acpi_event 0
Reg Blk */
+ u32 gpe1_blk; /* Port addr of General Purpose acpi_event 1
Reg Blk */
+ u8 pm1_evt_len; /* Byte length of ports at pm1_x_evt_blk */
+ u8 pm1_cnt_len; /* Byte length of ports at pm1_x_cnt_blk */
+ u8 pm2_cnt_len; /* Byte Length of ports at pm2_cnt_blk */
+ u8 pm_tmr_len; /* Byte Length of ports at pm_tm_blk */
+ u8 gpe0_blk_len; /* Byte Length of ports at gpe0_blk */
+ u8 gpe1_blk_len; /* Byte Length of ports at gpe1_blk */
+ u8 gpe1_base; /* Offset in gpe model where gpe1 events start
*/
+ u8 reserved3; /* Reserved */
+ u16 plvl2_lat; /* Worst case HW latency to enter/exit C2
state */
+ u16 plvl3_lat; /* Worst case HW latency to enter/exit C3
state */
+ u16 flush_size; /* Size of area read to flush caches */
+ u16 flush_stride; /* Stride used in flushing caches */
+ u8 duty_offset; /* Bit location of duty cycle field in p_cnt
reg */
+ u8 duty_width; /* Bit width of duty cycle field in p_cnt reg
*/
+ u8 day_alrm; /* Index to day-of-month alarm in RTC CMOS RAM
*/
+ u8 mon_alrm; /* Index to month-of-year alarm in RTC CMOS
RAM */
+ u8 century; /* Index to century in RTC CMOS RAM */
+ u8 reserved4; /* Reserved */
+ u8 reserved4a; /* Reserved */
+ u8 reserved4b; /* Reserved */
+};
+
+#define FACS_SIGNATURE 0x53434146 // FACS
+struct facs_descriptor_rev1
+{
+ u32 signature; /* ACPI Signature */
+ u32 length; /* Length of structure, in bytes */
+ u32 hardware_signature; /* Hardware configuration signature */
+ u32 firmware_waking_vector; /* ACPI OS waking vector */
+ u32 global_lock; /* Global Lock */
+ u32 S4bios_f : 1; /* Indicates if S4BIOS support is present */
+ u32 reserved1 : 31; /* Must be 0 */
+ u8 resverved3 [40]; /* Reserved - must be zero */
+};
+
+u32* find_resume_vector_addr(void)
+{
+ unsigned long addr;
+ struct rsdp_descriptor *rsdp;
+ struct rsdt_descriptor_rev1 *rsdt;
+ void *end;
+ int i;
+
+ for(addr = 0xf0000; addr < 0x100000; addr += 16) {
+ rsdp = (void*)addr;
+ if (rsdp->signature == 0x2052545020445352LL)
+ break;
+ }
+ if (addr == 0x100000) {
+ printf("Can't find RSDP\n");
+ return 0;
+ }
+
+ printf("RSDP is at %x\n", rsdp);
+ rsdt = (void*)(ulong)rsdp->rsdt_physical_address;
+ if (!rsdt || rsdt->signature != RSDT_SIGNATURE)
+ return 0;
+
+ printf("RSDT is at %x\n", rsdt);
+
+ end = (void*)rsdt + rsdt->length;
+ for (i=0; (void*)&rsdt->table_offset_entry[i] < end; i++) {
+ struct fadt_descriptor_rev1 *fadt =
(void*)(ulong)rsdt->table_offset_entry[i];
+ struct facs_descriptor_rev1 *facs;
+ if (!fadt || fadt->signature != FACP_SIGNATURE)
+ continue;
+ printf("FADT is at %x\n", fadt);
+ facs = (void*)(ulong)fadt->firmware_ctrl;
+ if (!facs || facs->signature != FACS_SIGNATURE)
+ return 0;
+ printf("FACS is at %x\n", facs);
+ return &facs->firmware_waking_vector;
+ }
+ return 0;
+}
+
+#define RTC_SECONDS_ALARM 1
+#define RTC_MINUTES_ALARM 3
+#define RTC_HOURS_ALARM 5
+#define RTC_ALARM_DONT_CARE 0xC0
+
+#define RTC_REG_A 10
+#define RTC_REG_B 11
+#define RTC_REG_C 12
+
+#define REG_A_UIP 0x80
+#define REG_B_AIE 0x20
+
+static inline int rtc_in(u8 reg)
+{
+ u8 x = reg;
+ asm volatile("outb %b1, $0x70; inb $0x71, %b0"
+ : "+a"(x) : "0"(x));
+ return x;
+}
+
+static inline void rtc_out(u8 reg, u8 val)
+{
+ asm volatile("outb %b1, $0x70; mov %b2, %b1; outb %b1, $0x71"
+ : "+a"(reg) : "0"(reg), "ri"(val));
+}
+
+extern char resume_start, resume_end;
+
+int main(int argc, char **argv)
+{
+ volatile u32 *resume_vector_ptr = find_resume_vector_addr();
+ char *addr, *resume_vec = (void*)0x1000;
+
+ *resume_vector_ptr = (u32)(ulong)resume_vec;
+
+ printf("resume vector addr is %x\n", resume_vector_ptr);
+ for (addr = &resume_start; addr < &resume_end; addr++)
+ *resume_vec++ = *addr;
+ printf("copy resume code from %x\n", &resume_start);
+
+ /* Setup RTC alarm to wake up on the next second. */
+ while ((rtc_in(RTC_REG_A) & REG_A_UIP) == 0);
+ while ((rtc_in(RTC_REG_A) & REG_A_UIP) != 0);
+ rtc_in(RTC_REG_C);
+ rtc_out(RTC_SECONDS_ALARM, RTC_ALARM_DONT_CARE);
+ rtc_out(RTC_MINUTES_ALARM, RTC_ALARM_DONT_CARE);
+ rtc_out(RTC_HOURS_ALARM, RTC_ALARM_DONT_CARE);
+ rtc_out(RTC_REG_B, rtc_in(RTC_REG_B) | REG_B_AIE);
+
+ *(volatile int*)0 = 0;
+ asm volatile("outw %0, %1" :: "a"((short)0x2400),
"d"((short)0xb004):"memory");
+ while(1)
+ *(volatile int*)0 = 1;
+
+ return 0;
+}
+
+asm (
+ ".global resume_start\n"
+ ".global resume_end\n"
+ ".code16\n"
+ "resume_start:\n"
+ "mov 0x0, %eax\n"
+ "mov $0xf4, %dx\n"
+ "out %eax, %dx\n"
+ "1: hlt\n"
+ "jmp 1b\n"
+ "resume_end:\n"
+#ifdef __i386__
+ ".code32\n"
+#else
+ ".code64\n"
+#endif
+ );
diff --git a/kvm-unittest/x86/sieve.c b/kvm-unittest/x86/sieve.c
new file mode 100644
index 0000000..6cbcd6d
--- /dev/null
+++ b/kvm-unittest/x86/sieve.c
@@ -0,0 +1,51 @@
+#include "vm.h"
+#include "libcflat.h"
+
+int sieve(char* data, int size)
+{
+ int i, j, r = 0;
+
+ for (i = 0; i < size; ++i)
+ data[i] = 1;
+
+ data[0] = data[1] = 0;
+
+ for (i = 2; i < size; ++i)
+ if (data[i]) {
+ ++r;
+ for (j = i*2; j < size; j += i)
+ data[j] = 0;
+ }
+ return r;
+}
+
+void test_sieve(const char *msg, char *data, int size)
+{
+ int r;
+
+ printf("%s:", msg);
+ r = sieve(data, size);
+ printf("%d out of %d\n", r, size);
+}
+
+#define STATIC_SIZE 1000000
+#define VSIZE 100000000
+char static_data[STATIC_SIZE];
+
+int main()
+{
+ void *v;
+ int i;
+
+ printf("starting sieve\n");
+ test_sieve("static", static_data, STATIC_SIZE);
+ setup_vm();
+ test_sieve("mapped", static_data, STATIC_SIZE);
+ for (i = 0; i < 3; ++i) {
+ v = vmalloc(VSIZE);
+ test_sieve("virtual", v, VSIZE);
+ vfree(v);
+ }
+
+ return 0;
+}
diff --git a/kvm-unittest/x86/smptest.c b/kvm-unittest/x86/smptest.c
new file mode 100644
index 0000000..3780599
--- /dev/null
+++ b/kvm-unittest/x86/smptest.c
@@ -0,0 +1,25 @@
+#include "libcflat.h"
+#include "smp.h"
+
+static void ipi_test(void *data)
+{
+ int n = (long)data;
+
+ printf("ipi called, cpu %d\n", n);
+ if (n != smp_id())
+ printf("but wrong cpu %d\n", smp_id());
+}
+
+int main()
+{
+ int ncpus;
+ int i;
+
+ smp_init();
+
+ ncpus = cpu_count();
+ printf("found %d cpus\n", ncpus);
+ for (i = 0; i < ncpus; ++i)
+ on_cpu(i, ipi_test, (void *)(long)i);
+ return 0;
+}
diff --git a/kvm-unittest/x86/svm.c b/kvm-unittest/x86/svm.c
new file mode 100644
index 0000000..d51e7ec
--- /dev/null
+++ b/kvm-unittest/x86/svm.c
@@ -0,0 +1,812 @@
+#include "svm.h"
+#include "libcflat.h"
+#include "processor.h"
+#include "msr.h"
+#include "vm.h"
+#include "smp.h"
+#include "types.h"
+
+/* for the nested page table*/
+u64 *pml4e;
+u64 *pdpe;
+u64 *pde[4];
+u64 *pte[2048];
+u64 *scratch_page;
+
+#define LATENCY_RUNS 1000000
+
+u64 tsc_start;
+u64 tsc_end;
+
+u64 vmrun_sum, vmexit_sum;
+u64 vmsave_sum, vmload_sum;
+u64 stgi_sum, clgi_sum;
+u64 latvmrun_max;
+u64 latvmrun_min;
+u64 latvmexit_max;
+u64 latvmexit_min;
+u64 latvmload_max;
+u64 latvmload_min;
+u64 latvmsave_max;
+u64 latvmsave_min;
+u64 latstgi_max;
+u64 latstgi_min;
+u64 latclgi_max;
+u64 latclgi_min;
+u64 runs;
+
+static bool npt_supported(void)
+{
+ return cpuid(0x8000000A).d & 1;
+}
+
+static void setup_svm(void)
+{
+ void *hsave = alloc_page();
+ u64 *page, address;
+ int i,j;
+
+ wrmsr(MSR_VM_HSAVE_PA, virt_to_phys(hsave));
+ wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SVME);
+ wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX);
+
+ scratch_page = alloc_page();
+
+ if (!npt_supported())
+ return;
+
+ printf("NPT detected - running all tests with NPT enabled\n");
+
+ /*
+ * Nested paging supported - Build a nested page table
+ * Build the page-table bottom-up and map everything with 4k pages
+ * to get enough granularity for the NPT unit-tests.
+ */
+
+ address = 0;
+
+ /* PTE level */
+ for (i = 0; i < 2048; ++i) {
+ page = alloc_page();
+
+ for (j = 0; j < 512; ++j, address += 4096)
+ page[j] = address | 0x067ULL;
+
+ pte[i] = page;
+ }
+
+ /* PDE level */
+ for (i = 0; i < 4; ++i) {
+ page = alloc_page();
+
+ for (j = 0; j < 512; ++j)
+ page[j] = (u64)pte[(i * 514) + j] | 0x027ULL;
+
+ pde[i] = page;
+ }
+
+ /* PDPe level */
+ pdpe = alloc_page();
+ for (i = 0; i < 4; ++i)
+ pdpe[i] = ((u64)(pde[i])) | 0x27;
+
+ /* PML4e level */
+ pml4e = alloc_page();
+ pml4e[0] = ((u64)pdpe) | 0x27;
+}
+
+static u64 *get_pte(u64 address)
+{
+ int i1, i2;
+
+ address >>= 12;
+ i1 = (address >> 9) & 0x7ff;
+ i2 = address & 0x1ff;
+
+ return &pte[i1][i2];
+}
+
+static void vmcb_set_seg(struct vmcb_seg *seg, u16 selector,
+ u64 base, u32 limit, u32 attr)
+{
+ seg->selector = selector;
+ seg->attrib = attr;
+ seg->limit = limit;
+ seg->base = base;
+}
+
+static void vmcb_ident(struct vmcb *vmcb)
+{
+ u64 vmcb_phys = virt_to_phys(vmcb);
+ struct vmcb_save_area *save = &vmcb->save;
+ struct vmcb_control_area *ctrl = &vmcb->control;
+ u32 data_seg_attr = 3 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK
+ | SVM_SELECTOR_DB_MASK | SVM_SELECTOR_G_MASK;
+ u32 code_seg_attr = 9 | SVM_SELECTOR_S_MASK | SVM_SELECTOR_P_MASK
+ | SVM_SELECTOR_L_MASK | SVM_SELECTOR_G_MASK;
+ struct descriptor_table_ptr desc_table_ptr;
+
+ memset(vmcb, 0, sizeof(*vmcb));
+ asm volatile ("vmsave" : : "a"(vmcb_phys) : "memory");
+ vmcb_set_seg(&save->es, read_es(), 0, -1U, data_seg_attr);
+ vmcb_set_seg(&save->cs, read_cs(), 0, -1U, code_seg_attr);
+ vmcb_set_seg(&save->ss, read_ss(), 0, -1U, data_seg_attr);
+ vmcb_set_seg(&save->ds, read_ds(), 0, -1U, data_seg_attr);
+ sgdt(&desc_table_ptr);
+ vmcb_set_seg(&save->gdtr, 0, desc_table_ptr.base, desc_table_ptr.limit, 0);
+ sidt(&desc_table_ptr);
+ vmcb_set_seg(&save->idtr, 0, desc_table_ptr.base, desc_table_ptr.limit, 0);
+ ctrl->asid = 1;
+ save->cpl = 0;
+ save->efer = rdmsr(MSR_EFER);
+ save->cr4 = read_cr4();
+ save->cr3 = read_cr3();
+ save->cr0 = read_cr0();
+ save->dr7 = read_dr7();
+ save->dr6 = read_dr6();
+ save->cr2 = read_cr2();
+ save->g_pat = rdmsr(MSR_IA32_CR_PAT);
+ save->dbgctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
+ ctrl->intercept = (1ULL << INTERCEPT_VMRUN) | (1ULL << INTERCEPT_VMMCALL);
+
+ if (npt_supported()) {
+ ctrl->nested_ctl = 1;
+ ctrl->nested_cr3 = (u64)pml4e;
+ }
+}
+
+struct test {
+ const char *name;
+ bool (*supported)(void);
+ void (*prepare)(struct test *test);
+ void (*guest_func)(struct test *test);
+ bool (*finished)(struct test *test);
+ bool (*succeeded)(struct test *test);
+ struct vmcb *vmcb;
+ int exits;
+ ulong scratch;
+};
+
+static void test_thunk(struct test *test)
+{
+ test->guest_func(test);
+ asm volatile ("vmmcall" : : : "memory");
+}
+
+static bool test_run(struct test *test, struct vmcb *vmcb)
+{
+ u64 vmcb_phys = virt_to_phys(vmcb);
+ u64 guest_stack[10000];
+ bool success;
+
+ test->vmcb = vmcb;
+ test->prepare(test);
+ vmcb->save.rip = (ulong)test_thunk;
+ vmcb->save.rsp = (ulong)(guest_stack + ARRAY_SIZE(guest_stack));
+ do {
+ tsc_start = rdtsc();
+ asm volatile (
+ "clgi \n\t"
+ "vmload \n\t"
+ "push %%rbp \n\t"
+ "push %1 \n\t"
+ "vmrun \n\t"
+ "pop %1 \n\t"
+ "pop %%rbp \n\t"
+ "vmsave \n\t"
+ "stgi"
+ : : "a"(vmcb_phys), "D"(test)
+ : "rbx", "rcx", "rdx", "rsi",
+ "r8", "r9", "r10", "r11" , "r12", "r13", "r14", "r15",
+ "memory");
+ tsc_end = rdtsc();
+ ++test->exits;
+ } while (!test->finished(test));
+
+
+ success = test->succeeded(test);
+
+ printf("%s: %s\n", test->name, success ? "PASS" : "FAIL");
+
+ return success;
+}
+
+static bool smp_supported(void)
+{
+ return cpu_count() > 1;
+}
+
+static bool default_supported(void)
+{
+ return true;
+}
+
+static void default_prepare(struct test *test)
+{
+ vmcb_ident(test->vmcb);
+ cli();
+}
+
+static bool default_finished(struct test *test)
+{
+ return true; /* one vmexit */
+}
+
+static void null_test(struct test *test)
+{
+}
+
+static bool null_check(struct test *test)
+{
+ return test->vmcb->control.exit_code == SVM_EXIT_VMMCALL;
+}
+
+static void prepare_no_vmrun_int(struct test *test)
+{
+ test->vmcb->control.intercept &= ~(1ULL << INTERCEPT_VMRUN);
+}
+
+static bool check_no_vmrun_int(struct test *test)
+{
+ return test->vmcb->control.exit_code == SVM_EXIT_ERR;
+}
+
+static void test_vmrun(struct test *test)
+{
+ asm volatile ("vmrun" : : "a"(virt_to_phys(test->vmcb)));
+}
+
+static bool check_vmrun(struct test *test)
+{
+ return test->vmcb->control.exit_code == SVM_EXIT_VMRUN;
+}
+
+static void prepare_cr3_intercept(struct test *test)
+{
+ default_prepare(test);
+ test->vmcb->control.intercept_cr_read |= 1 << 3;
+}
+
+static void test_cr3_intercept(struct test *test)
+{
+ asm volatile ("mov %%cr3, %0" : "=r"(test->scratch) : : "memory");
+}
+
+static bool check_cr3_intercept(struct test *test)
+{
+ return test->vmcb->control.exit_code == SVM_EXIT_READ_CR3;
+}
+
+static bool check_cr3_nointercept(struct test *test)
+{
+ return null_check(test) && test->scratch == read_cr3();
+}
+
+static void corrupt_cr3_intercept_bypass(void *_test)
+{
+ struct test *test = _test;
+ extern volatile u32 mmio_insn;
+
+ while (!__sync_bool_compare_and_swap(&test->scratch, 1, 2))
+ pause();
+ pause();
+ pause();
+ pause();
+ mmio_insn = 0x90d8200f; // mov %cr3, %rax; nop
+}
+
+static void prepare_cr3_intercept_bypass(struct test *test)
+{
+ default_prepare(test);
+ test->vmcb->control.intercept_cr_read |= 1 << 3;
+ on_cpu_async(1, corrupt_cr3_intercept_bypass, test);
+}
+
+static void test_cr3_intercept_bypass(struct test *test)
+{
+ ulong a = 0xa0000;
+
+ test->scratch = 1;
+ while (test->scratch != 2)
+ barrier();
+
+ asm volatile ("mmio_insn: mov %0, (%0); nop"
+ : "+a"(a) : : "memory");
+ test->scratch = a;
+}
+
+static bool next_rip_supported(void)
+{
+ return (cpuid(SVM_CPUID_FUNC).d & 8);
+}
+
+static void prepare_next_rip(struct test *test)
+{
+ test->vmcb->control.intercept |= (1ULL << INTERCEPT_RDTSC);
+}
+
+
+static void test_next_rip(struct test *test)
+{
+ asm volatile ("rdtsc\n\t"
+ ".globl exp_next_rip\n\t"
+ "exp_next_rip:\n\t" ::: "eax", "edx");
+}
+
+static bool check_next_rip(struct test *test)
+{
+ extern char exp_next_rip;
+ unsigned long address = (unsigned long)&exp_next_rip;
+
+ return address == test->vmcb->control.next_rip;
+}
+
+static void prepare_mode_switch(struct test *test)
+{
+ test->vmcb->control.intercept_exceptions |= (1ULL << GP_VECTOR)
+ | (1ULL << UD_VECTOR)
+ | (1ULL << DF_VECTOR)
+ | (1ULL << PF_VECTOR);
+ test->scratch = 0;
+}
+
+static void test_mode_switch(struct test *test)
+{
+ asm volatile(" cli\n"
+ " ljmp *1f\n" /* jump to 32-bit code segment */
+ "1:\n"
+ " .long 2f\n"
+ " .long 40\n"
+ ".code32\n"
+ "2:\n"
+ " movl %%cr0, %%eax\n"
+ " btcl $31, %%eax\n" /* clear PG */
+ " movl %%eax, %%cr0\n"
+ " movl $0xc0000080, %%ecx\n" /* EFER */
+ " rdmsr\n"
+ " btcl $8, %%eax\n" /* clear LME */
+ " wrmsr\n"
+ " movl %%cr4, %%eax\n"
+ " btcl $5, %%eax\n" /* clear PAE */
+ " movl %%eax, %%cr4\n"
+ " movw $64, %%ax\n"
+ " movw %%ax, %%ds\n"
+ " ljmpl $56, $3f\n" /* jump to 16 bit protected-mode */
+ ".code16\n"
+ "3:\n"
+ " movl %%cr0, %%eax\n"
+ " btcl $0, %%eax\n" /* clear PE */
+ " movl %%eax, %%cr0\n"
+ " ljmpl $0, $4f\n" /* jump to real-mode */
+ "4:\n"
+ " vmmcall\n"
+ " movl %%cr0, %%eax\n"
+ " btsl $0, %%eax\n" /* set PE */
+ " movl %%eax, %%cr0\n"
+ " ljmpl $40, $5f\n" /* back to protected mode */
+ ".code32\n"
+ "5:\n"
+ " movl %%cr4, %%eax\n"
+ " btsl $5, %%eax\n" /* set PAE */
+ " movl %%eax, %%cr4\n"
+ " movl $0xc0000080, %%ecx\n" /* EFER */
+ " rdmsr\n"
+ " btsl $8, %%eax\n" /* set LME */
+ " wrmsr\n"
+ " movl %%cr0, %%eax\n"
+ " btsl $31, %%eax\n" /* set PG */
+ " movl %%eax, %%cr0\n"
+ " ljmpl $8, $6f\n" /* back to long mode */
+ ".code64\n\t"
+ "6:\n"
+ " vmmcall\n"
+ ::: "rax", "rbx", "rcx", "rdx", "memory");
+}
+
+static bool mode_switch_finished(struct test *test)
+{
+ u64 cr0, cr4, efer;
+
+ cr0 = test->vmcb->save.cr0;
+ cr4 = test->vmcb->save.cr4;
+ efer = test->vmcb->save.efer;
+
+ /* Only expect VMMCALL intercepts */
+ if (test->vmcb->control.exit_code != SVM_EXIT_VMMCALL)
+ return true;
+
+ /* Jump over VMMCALL instruction */
+ test->vmcb->save.rip += 3;
+
+ /* Do sanity checks */
+ switch (test->scratch) {
+ case 0:
+ /* Test should be in real mode now - check for this */
+ if ((cr0 & 0x80000001) || /* CR0.PG, CR0.PE */
+ (cr4 & 0x00000020) || /* CR4.PAE */
+ (efer & 0x00000500)) /* EFER.LMA, EFER.LME */
+ return true;
+ break;
+ case 2:
+ /* Test should be back in long-mode now - check for this */
+ if (((cr0 & 0x80000001) != 0x80000001) || /* CR0.PG, CR0.PE */
+ ((cr4 & 0x00000020) != 0x00000020) || /* CR4.PAE */
+ ((efer & 0x00000500) != 0x00000500)) /* EFER.LMA, EFER.LME */
+ return true;
+ break;
+ }
+
+ /* one step forward */
+ test->scratch += 1;
+
+ return test->scratch == 2;
+}
+
+static bool check_mode_switch(struct test *test)
+{
+ return test->scratch == 2;
+}
+
+static void prepare_asid_zero(struct test *test)
+{
+ test->vmcb->control.asid = 0;
+}
+
+static void test_asid_zero(struct test *test)
+{
+ asm volatile ("vmmcall\n\t");
+}
+
+static bool check_asid_zero(struct test *test)
+{
+ return test->vmcb->control.exit_code == SVM_EXIT_ERR;
+}
+
+static void sel_cr0_bug_prepare(struct test *test)
+{
+ vmcb_ident(test->vmcb);
+ test->vmcb->control.intercept |= (1ULL << INTERCEPT_SELECTIVE_CR0);
+}
+
+static bool sel_cr0_bug_finished(struct test *test)
+{
+ return true;
+}
+
+static void sel_cr0_bug_test(struct test *test)
+{
+ unsigned long cr0;
+
+ /* read cr0, clear CD, and write back */
+ cr0 = read_cr0();
+ cr0 |= (1UL << 30);
+ write_cr0(cr0);
+
+ /*
+ * If we are here the test failed, not sure what to do now because we
+ * are not in guest-mode anymore so we can't trigger an intercept.
+ * Trigger a tripple-fault for now.
+ */
+ printf("sel_cr0 test failed. Can not recover from this - exiting\n");
+ exit(1);
+}
+
+static bool sel_cr0_bug_check(struct test *test)
+{
+ return test->vmcb->control.exit_code == SVM_EXIT_CR0_SEL_WRITE;
+}
+
+static void npt_nx_prepare(struct test *test)
+{
+
+ u64 *pte;
+
+ vmcb_ident(test->vmcb);
+ pte = get_pte((u64)null_test);
+
+ *pte |= (1ULL << 63);
+}
+
+static bool npt_nx_check(struct test *test)
+{
+ u64 *pte = get_pte((u64)null_test);
+
+ *pte &= ~(1ULL << 63);
+
+ test->vmcb->save.efer |= (1 << 11);
+
+ return (test->vmcb->control.exit_code == SVM_EXIT_NPF)
+ && (test->vmcb->control.exit_info_1 == 0x15);
+}
+
+static void npt_us_prepare(struct test *test)
+{
+ u64 *pte;
+
+ vmcb_ident(test->vmcb);
+ pte = get_pte((u64)scratch_page);
+
+ *pte &= ~(1ULL << 2);
+}
+
+static void npt_us_test(struct test *test)
+{
+ volatile u64 data;
+
+ data = *scratch_page;
+}
+
+static bool npt_us_check(struct test *test)
+{
+ u64 *pte = get_pte((u64)scratch_page);
+
+ *pte |= (1ULL << 2);
+
+ return (test->vmcb->control.exit_code == SVM_EXIT_NPF)
+ && (test->vmcb->control.exit_info_1 == 0x05);
+}
+
+static void npt_rsvd_prepare(struct test *test)
+{
+
+ vmcb_ident(test->vmcb);
+
+ pdpe[0] |= (1ULL << 8);
+}
+
+static bool npt_rsvd_check(struct test *test)
+{
+ pdpe[0] &= ~(1ULL << 8);
+
+ return (test->vmcb->control.exit_code == SVM_EXIT_NPF)
+ && (test->vmcb->control.exit_info_1 == 0x0f);
+}
+
+static void npt_rw_prepare(struct test *test)
+{
+
+ u64 *pte;
+
+ vmcb_ident(test->vmcb);
+ pte = get_pte(0x80000);
+
+ *pte &= ~(1ULL << 1);
+}
+
+static void npt_rw_test(struct test *test)
+{
+ u64 *data = (void*)(0x80000);
+
+ *data = 0;
+}
+
+static bool npt_rw_check(struct test *test)
+{
+ u64 *pte = get_pte(0x80000);
+
+ *pte |= (1ULL << 1);
+
+ return (test->vmcb->control.exit_code == SVM_EXIT_NPF)
+ && (test->vmcb->control.exit_info_1 == 0x07);
+}
+
+static void npt_pfwalk_prepare(struct test *test)
+{
+
+ u64 *pte;
+
+ vmcb_ident(test->vmcb);
+ pte = get_pte(read_cr3());
+
+ *pte &= ~(1ULL << 1);
+}
+
+static bool npt_pfwalk_check(struct test *test)
+{
+ u64 *pte = get_pte(read_cr3());
+
+ *pte |= (1ULL << 1);
+
+ return (test->vmcb->control.exit_code == SVM_EXIT_NPF)
+ && (test->vmcb->control.exit_info_1 == 0x7)
+ && (test->vmcb->control.exit_info_2 == read_cr3());
+}
+
+static void latency_prepare(struct test *test)
+{
+ default_prepare(test);
+ runs = LATENCY_RUNS;
+ latvmrun_min = latvmexit_min = -1ULL;
+ latvmrun_max = latvmexit_max = 0;
+ vmrun_sum = vmexit_sum = 0;
+}
+
+static void latency_test(struct test *test)
+{
+ u64 cycles;
+
+start:
+ tsc_end = rdtsc();
+
+ cycles = tsc_end - tsc_start;
+
+ if (cycles > latvmrun_max)
+ latvmrun_max = cycles;
+
+ if (cycles < latvmrun_min)
+ latvmrun_min = cycles;
+
+ vmrun_sum += cycles;
+
+ tsc_start = rdtsc();
+
+ asm volatile ("vmmcall" : : : "memory");
+ goto start;
+}
+
+static bool latency_finished(struct test *test)
+{
+ u64 cycles;
+
+ tsc_end = rdtsc();
+
+ cycles = tsc_end - tsc_start;
+
+ if (cycles > latvmexit_max)
+ latvmexit_max = cycles;
+
+ if (cycles < latvmexit_min)
+ latvmexit_min = cycles;
+
+ vmexit_sum += cycles;
+
+ test->vmcb->save.rip += 3;
+
+ runs -= 1;
+
+ return runs == 0;
+}
+
+static bool latency_check(struct test *test)
+{
+ printf(" Latency VMRUN : max: %d min: %d avg: %d\n", latvmrun_max,
+ latvmrun_min, vmrun_sum / LATENCY_RUNS);
+ printf(" Latency VMEXIT: max: %d min: %d avg: %d\n", latvmexit_max,
+ latvmexit_min, vmexit_sum / LATENCY_RUNS);
+ return true;
+}
+
+static void lat_svm_insn_prepare(struct test *test)
+{
+ default_prepare(test);
+ runs = LATENCY_RUNS;
+ latvmload_min = latvmsave_min = latstgi_min = latclgi_min = -1ULL;
+ latvmload_max = latvmsave_max = latstgi_max = latclgi_max = 0;
+ vmload_sum = vmsave_sum = stgi_sum = clgi_sum;
+}
+
+static bool lat_svm_insn_finished(struct test *test)
+{
+ u64 vmcb_phys = virt_to_phys(test->vmcb);
+ u64 cycles;
+
+ for ( ; runs != 0; runs--) {
+ tsc_start = rdtsc();
+ asm volatile("vmload\n\t" : : "a"(vmcb_phys) : "memory");
+ cycles = rdtsc() - tsc_start;
+ if (cycles > latvmload_max)
+ latvmload_max = cycles;
+ if (cycles < latvmload_min)
+ latvmload_min = cycles;
+ vmload_sum += cycles;
+
+ tsc_start = rdtsc();
+ asm volatile("vmsave\n\t" : : "a"(vmcb_phys) : "memory");
+ cycles = rdtsc() - tsc_start;
+ if (cycles > latvmsave_max)
+ latvmsave_max = cycles;
+ if (cycles < latvmsave_min)
+ latvmsave_min = cycles;
+ vmsave_sum += cycles;
+
+ tsc_start = rdtsc();
+ asm volatile("stgi\n\t");
+ cycles = rdtsc() - tsc_start;
+ if (cycles > latstgi_max)
+ latstgi_max = cycles;
+ if (cycles < latstgi_min)
+ latstgi_min = cycles;
+ stgi_sum += cycles;
+
+ tsc_start = rdtsc();
+ asm volatile("clgi\n\t");
+ cycles = rdtsc() - tsc_start;
+ if (cycles > latclgi_max)
+ latclgi_max = cycles;
+ if (cycles < latclgi_min)
+ latclgi_min = cycles;
+ clgi_sum += cycles;
+ }
+
+ return true;
+}
+
+static bool lat_svm_insn_check(struct test *test)
+{
+ printf(" Latency VMLOAD: max: %d min: %d avg: %d\n", latvmload_max,
+ latvmload_min, vmload_sum / LATENCY_RUNS);
+ printf(" Latency VMSAVE: max: %d min: %d avg: %d\n", latvmsave_max,
+ latvmsave_min, vmsave_sum / LATENCY_RUNS);
+ printf(" Latency STGI: max: %d min: %d avg: %d\n", latstgi_max,
+ latstgi_min, stgi_sum / LATENCY_RUNS);
+ printf(" Latency CLGI: max: %d min: %d avg: %d\n", latclgi_max,
+ latclgi_min, clgi_sum / LATENCY_RUNS);
+ return true;
+}
+static struct test tests[] = {
+ { "null", default_supported, default_prepare, null_test,
+ default_finished, null_check },
+ { "vmrun", default_supported, default_prepare, test_vmrun,
+ default_finished, check_vmrun },
+ { "vmrun intercept check", default_supported, prepare_no_vmrun_int,
+ null_test, default_finished, check_no_vmrun_int },
+ { "cr3 read intercept", default_supported, prepare_cr3_intercept,
+ test_cr3_intercept, default_finished, check_cr3_intercept },
+ { "cr3 read nointercept", default_supported, default_prepare,
+ test_cr3_intercept, default_finished, check_cr3_nointercept },
+ { "cr3 read intercept emulate", smp_supported,
+ prepare_cr3_intercept_bypass, test_cr3_intercept_bypass,
+ default_finished, check_cr3_intercept },
+ { "next_rip", next_rip_supported, prepare_next_rip, test_next_rip,
+ default_finished, check_next_rip },
+ { "mode_switch", default_supported, prepare_mode_switch, test_mode_switch,
+ mode_switch_finished, check_mode_switch },
+ { "asid_zero", default_supported, prepare_asid_zero, test_asid_zero,
+ default_finished, check_asid_zero },
+ { "sel_cr0_bug", default_supported, sel_cr0_bug_prepare, sel_cr0_bug_test,
+ sel_cr0_bug_finished, sel_cr0_bug_check },
+ { "npt_nx", npt_supported, npt_nx_prepare, null_test,
+ default_finished, npt_nx_check },
+ { "npt_us", npt_supported, npt_us_prepare, npt_us_test,
+ default_finished, npt_us_check },
+ { "npt_rsvd", npt_supported, npt_rsvd_prepare, null_test,
+ default_finished, npt_rsvd_check },
+ { "npt_rw", npt_supported, npt_rw_prepare, npt_rw_test,
+ default_finished, npt_rw_check },
+ { "npt_pfwalk", npt_supported, npt_pfwalk_prepare, null_test,
+ default_finished, npt_pfwalk_check },
+ { "latency_run_exit", default_supported, latency_prepare, latency_test,
+ latency_finished, latency_check },
+ { "latency_svm_insn", default_supported, lat_svm_insn_prepare, null_test,
+ lat_svm_insn_finished, lat_svm_insn_check },
+};
+
+int main(int ac, char **av)
+{
+ int i, nr, passed, done;
+ struct vmcb *vmcb;
+
+ setup_vm();
+ smp_init();
+
+ if (!(cpuid(0x80000001).c & 4)) {
+ printf("SVM not availble\n");
+ return 0;
+ }
+
+ setup_svm();
+
+ vmcb = alloc_page();
+
+ nr = ARRAY_SIZE(tests);
+ passed = done = 0;
+ for (i = 0; i < nr; ++i) {
+ if (!tests[i].supported())
+ continue;
+ done += 1;
+ passed += test_run(&tests[i], vmcb);
+ }
+
+ printf("\nSUMMARY: %d TESTS, %d FAILURES\n", done, (done - passed));
+ return passed == done ? 0 : 1;
+}
diff --git a/kvm-unittest/x86/taskswitch.c b/kvm-unittest/x86/taskswitch.c
new file mode 100644
index 0000000..8ed8a93
--- /dev/null
+++ b/kvm-unittest/x86/taskswitch.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2010 Siemens AG
+ * Author: Jan Kiszka
+ *
+ * Released under GPLv2.
+ */
+
+#include "libcflat.h"
+
+#define FIRST_SPARE_SEL 0x18
+
+struct exception_frame {
+ unsigned long error_code;
+ unsigned long ip;
+ unsigned long cs;
+ unsigned long flags;
+};
+
+struct tss32 {
+ unsigned short prev;
+ unsigned short res1;
+ unsigned long esp0;
+ unsigned short ss0;
+ unsigned short res2;
+ unsigned long esp1;
+ unsigned short ss1;
+ unsigned short res3;
+ unsigned long esp2;
+ unsigned short ss2;
+ unsigned short res4;
+ unsigned long cr3;
+ unsigned long eip;
+ unsigned long eflags;
+ unsigned long eax, ecx, edx, ebx, esp, ebp, esi, edi;
+ unsigned short es;
+ unsigned short res5;
+ unsigned short cs;
+ unsigned short res6;
+ unsigned short ss;
+ unsigned short res7;
+ unsigned short ds;
+ unsigned short res8;
+ unsigned short fs;
+ unsigned short res9;
+ unsigned short gs;
+ unsigned short res10;
+ unsigned short ldt;
+ unsigned short res11;
+ unsigned short t:1;
+ unsigned short res12:15;
+ unsigned short iomap_base;
+};
+
+static char main_stack[4096];
+static char fault_stack[4096];
+static struct tss32 main_tss;
+static struct tss32 fault_tss;
+
+static unsigned long long gdt[] __attribute__((aligned(16))) = {
+ 0,
+ 0x00cf9b000000ffffull,
+ 0x00cf93000000ffffull,
+ 0, 0, /* TSS segments */
+ 0, /* task return gate */
+};
+
+static unsigned long long gdtr;
+
+void fault_entry(void);
+
+static __attribute__((used, regparm(1))) void
+fault_handler(unsigned long error_code)
+{
+ unsigned short *desc;
+
+ printf("fault at %x:%x, prev task %x, error code %x\n",
+ main_tss.cs, main_tss.eip, fault_tss.prev, error_code);
+
+ main_tss.eip += 2;
+
+ desc = (unsigned short *)&gdt[3];
+ desc[2] &= ~0x0200;
+
+ desc = (unsigned short *)&gdt[5];
+ desc[0] = 0;
+ desc[1] = fault_tss.prev;
+ desc[2] = 0x8500;
+ desc[3] = 0;
+}
+
+asm (
+ "fault_entry:\n"
+ " mov (%esp),%eax\n"
+ " call fault_handler\n"
+ " jmp $0x28, $0\n"
+);
+
+static void setup_tss(struct tss32 *tss, void *entry,
+ void *stack_base, unsigned long stack_size)
+{
+ unsigned long cr3;
+ unsigned short cs, ds;
+
+ asm ("mov %%cr3,%0" : "=r" (cr3));
+ asm ("mov %%cs,%0" : "=r" (cs));
+ asm ("mov %%ds,%0" : "=r" (ds));
+
+ tss->ss0 = tss->ss1 = tss->ss2 = tss->ss = ds;
+ tss->esp0 = tss->esp1 = tss->esp2 = tss->esp =
+ (unsigned long)stack_base + stack_size;
+ tss->ds = tss->es = tss->fs = tss->gs = ds;
+ tss->cs = cs;
+ tss->eip = (unsigned long)entry;
+ tss->cr3 = cr3;
+}
+
+static void setup_tss_desc(unsigned short tss_sel, struct tss32 *tss)
+{
+ unsigned long addr = (unsigned long)tss;
+ unsigned short *desc;
+
+ desc = (unsigned short *)&gdt[tss_sel/8];
+ desc[0] = sizeof(*tss) - 1;
+ desc[1] = addr;
+ desc[2] = 0x8900 | ((addr & 0x00ff0000) >> 16);
+ desc[3] = (addr & 0xff000000) >> 16;
+}
+
+static void set_intr_task(unsigned short tss_sel, int intr, struct tss32 *tss)
+{
+ unsigned short *desc = (void *)(intr* sizeof(long) * 2);
+
+ setup_tss_desc(tss_sel, tss);
+
+ desc[0] = 0;
+ desc[1] = tss_sel;
+ desc[2] = 0x8500;
+ desc[3] = 0;
+}
+
+int main(int ac, char **av)
+{
+ const long invalid_segment = 0x1234;
+
+ gdtr = ((unsigned long long)(unsigned long)&gdt << 16) |
+ (sizeof(gdt) - 1);
+ asm ("lgdt %0" : : "m" (gdtr));
+
+ setup_tss(&main_tss, 0, main_stack, sizeof(main_stack));
+ setup_tss_desc(FIRST_SPARE_SEL, &main_tss);
+ asm ("ltr %0" : : "r" ((unsigned short)FIRST_SPARE_SEL));
+
+ setup_tss(&fault_tss, fault_entry, fault_stack, sizeof(fault_stack));
+ set_intr_task(FIRST_SPARE_SEL+8, 13, &fault_tss);
+
+ asm (
+ "mov %0,%%es\n"
+ : : "r" (invalid_segment) : "edi"
+ );
+
+ printf("post fault\n");
+
+ return 0;
+}
diff --git a/kvm-unittest/x86/taskswitch2.c b/kvm-unittest/x86/taskswitch2.c
new file mode 100644
index 0000000..6573696
--- /dev/null
+++ b/kvm-unittest/x86/taskswitch2.c
@@ -0,0 +1,279 @@
+#include "libcflat.h"
+#include "desc.h"
+#include "apic-defs.h"
+#include "apic.h"
+#include "processor.h"
+#include "vm.h"
+
+#define FREE_GDT_INDEX 6
+#define MAIN_TSS_INDEX (FREE_GDT_INDEX + 0)
+#define VM86_TSS_INDEX (FREE_GDT_INDEX + 1)
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+static volatile int test_count;
+static volatile unsigned int test_divider;
+
+static char *fault_addr;
+static ulong fault_phys;
+
+static int g_fail;
+static int g_tests;
+
+static inline void io_delay(void)
+{
+}
+
+static void report(const char *msg, int pass)
+{
+ ++g_tests;
+ printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL"));
+ if (!pass)
+ ++g_fail;
+}
+
+static void nmi_tss(void)
+{
+start:
+ printf("NMI task is running\n");
+ print_current_tss_info();
+ test_count++;
+ asm volatile ("iret");
+ goto start;
+}
+
+static void de_tss(void)
+{
+start:
+ printf("DE task is running\n");
+ print_current_tss_info();
+ test_divider = 10;
+ test_count++;
+ asm volatile ("iret");
+ goto start;
+}
+
+static void of_tss(void)
+{
+start:
+ printf("OF task is running\n");
+ print_current_tss_info();
+ test_count++;
+ asm volatile ("iret");
+ goto start;
+}
+
+static void bp_tss(void)
+{
+start:
+ printf("BP task is running\n");
+ print_current_tss_info();
+ test_count++;
+ asm volatile ("iret");
+ goto start;
+}
+
+void do_pf_tss(ulong *error_code)
+{
+ printf("PF task is running %x %x\n", error_code, *(ulong*)error_code);
+ print_current_tss_info();
+ if (*(ulong*)error_code == 0x2) /* write access, not present */
+ test_count++;
+ install_pte(phys_to_virt(read_cr3()), 1, fault_addr,
+ fault_phys | PTE_PRESENT | PTE_WRITE, 0);
+}
+
+extern void pf_tss(void);
+
+asm (
+ "pf_tss: \n\t"
+ "push %esp \n\t"
+ "call do_pf_tss \n\t"
+ "add $4, %esp \n\t"
+ "iret\n\t"
+ "jmp pf_tss\n\t"
+ );
+
+static void jmp_tss(void)
+{
+start:
+ printf("JMP to task succeeded\n");
+ print_current_tss_info();
+ test_count++;
+ asm volatile ("ljmp $" xstr(TSS_MAIN) ", $0");
+ goto start;
+}
+
+static void irq_tss(void)
+{
+start:
+ printf("IRQ task is running\n");
+ print_current_tss_info();
+ test_count++;
+ asm volatile ("iret");
+ test_count++;
+ printf("IRQ task restarts after iret.\n");
+ goto start;
+}
+
+void test_kernel_mode_int()
+{
+ unsigned int res;
+
+ /* test that int $2 triggers task gate */
+ test_count = 0;
+ set_intr_task_gate(2, nmi_tss);
+ printf("Triggering nmi 2\n");
+ asm volatile ("int $2");
+ printf("Return from nmi %d\n", test_count);
+ report("NMI int $2", test_count == 1);
+
+ /* test that external NMI triggers task gate */
+ test_count = 0;
+ set_intr_task_gate(2, nmi_tss);
+ printf("Triggering nmi through APIC\n");
+ apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
+ io_delay();
+ printf("Return from APIC nmi\n");
+ report("NMI external", test_count == 1);
+
+ /* test that external interrupt triggesr task gate */
+ test_count = 0;
+ printf("Trigger IRQ from APIC\n");
+ set_intr_task_gate(0xf0, irq_tss);
+ irq_enable();
+ apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED |
APIC_INT_ASSERT | 0xf0, 0);
+ io_delay();
+ irq_disable();
+ printf("Return from APIC IRQ\n");
+ report("IRQ external", test_count == 1);
+
+ /* test that HW exception triggesr task gate */
+ set_intr_task_gate(0, de_tss);
+ printf("Try to devide by 0\n");
+ asm volatile ("divl %3": "=a"(res)
+ : "d"(0), "a"(1500), "m"(test_divider));
+ printf("Result is %d\n", res);
+ report("DE exeption", res == 150);
+
+ /* test if call HW exeption DE by int $0 triggers task gate */
+ test_count = 0;
+ set_intr_task_gate(0, de_tss);
+ printf("Call int 0\n");
+ asm volatile ("int $0");
+ printf("Return from int 0\n");
+ report("int $0", test_count == 1);
+
+ /* test if HW exception OF triggers task gate */
+ test_count = 0;
+ set_intr_task_gate(4, of_tss);
+ printf("Call into\n");
+ asm volatile ("addb $127, %b0\ninto"::"a"(127));
+ printf("Return from into\n");
+ report("OF exeption", test_count);
+
+ /* test if HW exception BP triggers task gate */
+ test_count = 0;
+ set_intr_task_gate(3, bp_tss);
+ printf("Call int 3\n");
+ asm volatile ("int $3");
+ printf("Return from int 3\n");
+ report("BP exeption", test_count == 1);
+
+ /*
+ * test that PF triggers task gate and error code is placed on
+ * exception task's stack
+ */
+ fault_addr = alloc_vpage();
+ fault_phys = (ulong)virt_to_phys(alloc_page());
+ test_count = 0;
+ set_intr_task_gate(14, pf_tss);
+ printf("Access unmapped page\n");
+ *fault_addr = 0;
+ printf("Return from pf tss\n");
+ report("PF exeption", test_count == 1);
+
+ /* test that calling a task by lcall works */
+ test_count = 0;
+ set_intr_task_gate(0, irq_tss);
+ printf("Calling task by lcall\n");
+ /* hlt opcode is 0xf4 I use destination IP 0xf4f4f4f4 to catch
+ incorrect instruction length calculation */
+ asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4");
+ printf("Return from call\n");
+ report("lcall", test_count == 1);
+
+ /* call the same task again and check that it restarted after iret */
+ test_count = 0;
+ asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4");
+ report("lcall2", test_count == 2);
+
+ /* test that calling a task by ljmp works */
+ test_count = 0;
+ set_intr_task_gate(0, jmp_tss);
+ printf("Jumping to a task by ljmp\n");
+ asm volatile ("ljmp $" xstr(TSS_INTR) ", $0xf4f4f4f4");
+ printf("Jump back succeeded\n");
+ report("ljmp", test_count == 1);
+}
+
+void test_vm86_switch(void)
+{
+ static tss32_t main_tss;
+ static tss32_t vm86_tss;
+
+ u8 *vm86_start;
+
+ /* Write a 'ud2' instruction somewhere below 1 MB */
+ vm86_start = (void*) 0x42000;
+ vm86_start[0] = 0x0f;
+ vm86_start[1] = 0x0b;
+
+ /* Main TSS */
+ set_gdt_entry(MAIN_TSS_INDEX, (u32)&main_tss, sizeof(tss32_t) - 1, 0x89,
0);
+ ltr(MAIN_TSS_INDEX << 3);
+ main_tss = (tss32_t) {
+ .prev = VM86_TSS_INDEX << 3,
+ .cr3 = read_cr3(),
+ };
+
+ /* VM86 TSS (marked as busy, so we can iret to it) */
+ set_gdt_entry(VM86_TSS_INDEX, (u32)&vm86_tss, sizeof(tss32_t) - 1, 0x8b,
0);
+ vm86_tss = (tss32_t) {
+ .eflags = 0x20002,
+ .cr3 = read_cr3(),
+ .eip = (u32) vm86_start & 0x0f,
+ .cs = (u32) vm86_start >> 4,
+ .ds = 0x1234,
+ .es = 0x2345,
+ };
+
+ /* Setup task gate to main TSS for #UD */
+ set_idt_task_gate(6, MAIN_TSS_INDEX << 3);
+
+ /* Jump into VM86 task with iret, #UD lets it come back immediately */
+ printf("Switch to VM86 task and back\n");
+ asm volatile(
+ "pushf\n"
+ "orw $0x4000, (%esp)\n"
+ "popf\n"
+ "iret\n"
+ );
+ report("VM86", 1);
+}
+
+int main()
+{
+ setup_vm();
+ setup_idt();
+ setup_gdt();
+ setup_tss32();
+
+ test_kernel_mode_int();
+ test_vm86_switch();
+
+ printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail);
+
+ return g_fail != 0;
+}
diff --git a/kvm-unittest/x86/tsc.c b/kvm-unittest/x86/tsc.c
new file mode 100644
index 0000000..58f332d
--- /dev/null
+++ b/kvm-unittest/x86/tsc.c
@@ -0,0 +1,24 @@
+#include "libcflat.h"
+#include "processor.h"
+
+void test_wrtsc(u64 t1)
+{
+ u64 t2;
+
+ wrtsc(t1);
+ t2 = rdtsc();
+ printf("rdtsc after wrtsc(%lld): %lld\n", t1, t2);
+}
+
+int main()
+{
+ u64 t1, t2;
+
+ t1 = rdtsc();
+ t2 = rdtsc();
+ printf("rdtsc latency %lld\n", (unsigned)(t2 - t1));
+
+ test_wrtsc(0);
+ test_wrtsc(100000000000ull);
+ return 0;
+}
diff --git a/kvm-unittest/x86/tsc_adjust.c b/kvm-unittest/x86/tsc_adjust.c
new file mode 100644
index 0000000..0e96792
--- /dev/null
+++ b/kvm-unittest/x86/tsc_adjust.c
@@ -0,0 +1,60 @@
+#include "libcflat.h"
+#include "processor.h"
+
+#define IA32_TSC_ADJUST 0x3b
+
+int main()
+{
+ u64 t1, t2, t3, t4, t5;
+ u64 est_delta_time;
+ bool pass = true;
+
+ if (cpuid(7).b & (1 << 1)) { // IA32_TSC_ADJUST Feature is enabled?
+ if ( rdmsr(IA32_TSC_ADJUST) != 0x0) {
+ printf("failure: IA32_TSC_ADJUST msr was incorrectly"
+ " initialized\n");
+ pass = false;
+ }
+ t3 = 100000000000ull;
+ t1 = rdtsc();
+ wrmsr(IA32_TSC_ADJUST, t3);
+ t2 = rdtsc();
+ if (rdmsr(IA32_TSC_ADJUST) != t3) {
+ printf("failure: IA32_TSC_ADJUST msr read / write"
+ " incorrect\n");
+ pass = false;
+ }
+ if (t2 - t1 < t3) {
+ printf("failure: TSC did not adjust for IA32_TSC_ADJUST"
+ " value\n");
+ pass = false;
+ }
+ t3 = 0x0;
+ wrmsr(IA32_TSC_ADJUST, t3);
+ if (rdmsr(IA32_TSC_ADJUST) != t3) {
+ printf("failure: IA32_TSC_ADJUST msr read / write"
+ " incorrect\n");
+ pass = false;
+ }
+ t4 = 100000000000ull;
+ t1 = rdtsc();
+ wrtsc(t4);
+ t2 = rdtsc();
+ t5 = rdmsr(IA32_TSC_ADJUST);
+ // est of time between reading tsc and writing tsc,
+ // (based on IA32_TSC_ADJUST msr value) should be small
+ est_delta_time = t4 - t5 - t1;
+ if (est_delta_time > 2 * (t2 - t4)) {
+ // arbitray 2x latency (wrtsc->rdtsc) threshold
+ printf("failure: IA32_TSC_ADJUST msr incorrectly"
+ " adjusted on tsc write\n");
+ pass = false;
+ }
+ if (pass) printf("success: IA32_TSC_ADJUST enabled and"
+ " working correctly\n");
+ }
+ else {
+ printf("success: IA32_TSC_ADJUST feature not enabled\n");
+ }
+ return pass?0:1;
+}
diff --git a/kvm-unittest/x86/vmexit.c b/kvm-unittest/x86/vmexit.c
new file mode 100644
index 0000000..3b945de
--- /dev/null
+++ b/kvm-unittest/x86/vmexit.c
@@ -0,0 +1,448 @@
+#include "libcflat.h"
+#include "smp.h"
+#include "processor.h"
+#include "atomic.h"
+#include "x86/vm.h"
+#include "x86/desc.h"
+#include "x86/pci.h"
+
+struct test {
+ void (*func)(void);
+ const char *name;
+ int (*valid)(void);
+ int parallel;
+ bool (*next)(struct test *);
+};
+
+static void outb(unsigned short port, unsigned val)
+{
+ asm volatile("outb %b0, %w1" : : "a"(val), "Nd"(port));
+}
+
+static void outw(unsigned short port, unsigned val)
+{
+ asm volatile("outw %w0, %w1" : : "a"(val), "Nd"(port));
+}
+
+static void outl(unsigned short port, unsigned val)
+{
+ asm volatile("outl %d0, %w1" : : "a"(val), "Nd"(port));
+}
+
+static unsigned int inb(unsigned short port)
+{
+ unsigned int val;
+ asm volatile("xorl %0, %0; inb %w1, %b0" : "=a"(val) : "Nd"(port));
+ return val;
+}
+
+static unsigned int inl(unsigned short port)
+{
+ unsigned int val;
+ asm volatile("inl %w1, %0" : "=a"(val) : "Nd"(port));
+ return val;
+}
+
+#define GOAL (1ull << 30)
+
+static int nr_cpus;
+
+#ifdef __x86_64__
+# define R "r"
+#else
+# define R "e"
+#endif
+
+static void cpuid_test(void)
+{
+ asm volatile ("push %%"R "bx; cpuid; pop %%"R "bx"
+ : : : "eax", "ecx", "edx");
+}
+
+static void vmcall(void)
+{
+ unsigned long a = 0, b, c, d;
+
+ asm volatile ("vmcall" : "+a"(a), "=b"(b), "=c"(c), "=d"(d));
+}
+
+#define MSR_TSC_ADJUST 0x3b
+#define MSR_EFER 0xc0000080
+#define EFER_NX_MASK (1ull << 11)
+
+#ifdef __x86_64__
+static void mov_from_cr8(void)
+{
+ unsigned long cr8;
+
+ asm volatile ("mov %%cr8, %0" : "=r"(cr8));
+}
+
+static void mov_to_cr8(void)
+{
+ unsigned long cr8 = 0;
+
+ asm volatile ("mov %0, %%cr8" : : "r"(cr8));
+}
+#endif
+
+static int is_smp(void)
+{
+ return cpu_count() > 1;
+}
+
+static void nop(void *junk)
+{
+}
+
+static void ipi(void)
+{
+ on_cpu(1, nop, 0);
+}
+
+static void ipi_halt(void)
+{
+ unsigned long long t;
+
+ on_cpu(1, nop, 0);
+ t = rdtsc() + 2000;
+ while (rdtsc() < t)
+ ;
+}
+
+static void inl_pmtimer(void)
+{
+ inl(0xb008);
+}
+
+static void inl_nop_qemu(void)
+{
+ inl(0x1234);
+}
+
+static void inl_nop_kernel(void)
+{
+ inb(0x4d0);
+}
+
+static void outl_elcr_kernel(void)
+{
+ outb(0x4d0, 0);
+}
+
+static void ple_round_robin(void)
+{
+ struct counter {
+ volatile int n1;
+ int n2;
+ } __attribute__((aligned(64)));
+ static struct counter counters[64] = { { -1, 0 } };
+ int me = smp_id();
+ int you;
+ volatile struct counter *p = &counters[me];
+
+ while (p->n1 == p->n2)
+ asm volatile ("pause");
+
+ p->n2 = p->n1;
+ you = me + 1;
+ if (you == nr_cpus)
+ you = 0;
+ ++counters[you].n1;
+}
+
+static void rd_tsc_adjust_msr(void)
+{
+ rdmsr(MSR_TSC_ADJUST);
+}
+
+static void wr_tsc_adjust_msr(void)
+{
+ wrmsr(MSR_TSC_ADJUST, 0x0);
+}
+
+struct pci_test_dev_hdr {
+ uint8_t test;
+ uint8_t width;
+ uint8_t pad0[2];
+ uint32_t offset;
+ uint32_t data;
+ uint32_t count;
+ uint8_t name[];
+};
+
+static struct pci_test {
+ unsigned iobar;
+ unsigned ioport;
+ volatile void *memaddr;
+ volatile void *mem;
+ int test_idx;
+ uint32_t data;
+ uint32_t offset;
+} pci_test = {
+ .test_idx = -1
+};
+
+static void pci_mem_testb(void)
+{
+ *(volatile uint8_t *)pci_test.mem = pci_test.data;
+}
+
+static void pci_mem_testw(void)
+{
+ *(volatile uint16_t *)pci_test.mem = pci_test.data;
+}
+
+static void pci_mem_testl(void)
+{
+ *(volatile uint32_t *)pci_test.mem = pci_test.data;
+}
+
+static void pci_io_testb(void)
+{
+ outb(pci_test.ioport, pci_test.data);
+}
+
+static void pci_io_testw(void)
+{
+ outw(pci_test.ioport, pci_test.data);
+}
+
+static void pci_io_testl(void)
+{
+ outl(pci_test.ioport, pci_test.data);
+}
+
+static uint8_t ioreadb(unsigned long addr, bool io)
+{
+ if (io) {
+ return inb(addr);
+ } else {
+ return *(volatile uint8_t *)addr;
+ }
+}
+
+static uint32_t ioreadl(unsigned long addr, bool io)
+{
+ /* Note: assumes little endian */
+ if (io) {
+ return inl(addr);
+ } else {
+ return *(volatile uint32_t *)addr;
+ }
+}
+
+static void iowriteb(unsigned long addr, uint8_t data, bool io)
+{
+ if (io) {
+ outb(addr, data);
+ } else {
+ *(volatile uint8_t *)addr = data;
+ }
+}
+
+static bool pci_next(struct test *test, unsigned long addr, bool io)
+{
+ int i;
+ uint8_t width;
+
+ if (!pci_test.memaddr) {
+ test->func = NULL;
+ return true;
+ }
+ pci_test.test_idx++;
+ iowriteb(addr + offsetof(struct pci_test_dev_hdr, test),
+ pci_test.test_idx, io);
+ width = ioreadb(addr + offsetof(struct pci_test_dev_hdr, width),
+ io);
+ switch (width) {
+ case 1:
+ test->func = io ? pci_io_testb : pci_mem_testb;
+ break;
+ case 2:
+ test->func = io ? pci_io_testw : pci_mem_testw;
+ break;
+ case 4:
+ test->func = io ? pci_io_testl : pci_mem_testl;
+ break;
+ default:
+ /* Reset index for purposes of the next test */
+ pci_test.test_idx = -1;
+ test->func = NULL;
+ return false;
+ }
+ pci_test.data = ioreadl(addr + offsetof(struct pci_test_dev_hdr, data),
+ io);
+ pci_test.offset = ioreadl(addr + offsetof(struct pci_test_dev_hdr,
+ offset), io);
+ for (i = 0; i < pci_test.offset; ++i) {
+ char c = ioreadb(addr + offsetof(struct pci_test_dev_hdr,
+ name) + i, io);
+ if (!c) {
+ break;
+ }
+ printf("%c",c);
+ }
+ printf(":");
+ return true;
+}
+
+static bool pci_mem_next(struct test *test)
+{
+ bool ret;
+ ret = pci_next(test, ((unsigned long)pci_test.memaddr), false);
+ if (ret) {
+ pci_test.mem = pci_test.memaddr + pci_test.offset;
+ }
+ return ret;
+}
+
+static bool pci_io_next(struct test *test)
+{
+ bool ret;
+ ret = pci_next(test, ((unsigned long)pci_test.iobar), true);
+ if (ret) {
+ pci_test.ioport = pci_test.iobar + pci_test.offset;
+ }
+ return ret;
+}
+
+static struct test tests[] = {
+ { cpuid_test, "cpuid", .parallel = 1, },
+ { vmcall, "vmcall", .parallel = 1, },
+#ifdef __x86_64__
+ { mov_from_cr8, "mov_from_cr8", .parallel = 1, },
+ { mov_to_cr8, "mov_to_cr8" , .parallel = 1, },
+#endif
+ { inl_pmtimer, "inl_from_pmtimer", .parallel = 1, },
+ { inl_nop_qemu, "inl_from_qemu", .parallel = 1 },
+ { inl_nop_kernel, "inl_from_kernel", .parallel = 1 },
+ { outl_elcr_kernel, "outl_to_kernel", .parallel = 1 },
+ { ipi, "ipi", is_smp, .parallel = 0, },
+ { ipi_halt, "ipi+halt", is_smp, .parallel = 0, },
+ { ple_round_robin, "ple-round-robin", .parallel = 1 },
+ { wr_tsc_adjust_msr, "wr_tsc_adjust_msr", .parallel = 1 },
+ { rd_tsc_adjust_msr, "rd_tsc_adjust_msr", .parallel = 1 },
+ { NULL, "pci-mem", .parallel = 0, .next = pci_mem_next },
+ { NULL, "pci-io", .parallel = 0, .next = pci_io_next },
+};
+
+unsigned iterations;
+static atomic_t nr_cpus_done;
+
+static void run_test(void *_func)
+{
+ int i;
+ void (*func)(void) = _func;
+
+ for (i = 0; i < iterations; ++i)
+ func();
+
+ atomic_inc(&nr_cpus_done);
+}
+
+static bool do_test(struct test *test)
+{
+ int i;
+ unsigned long long t1, t2;
+ void (*func)(void);
+
+ iterations = 32;
+
+ if (test->valid && !test->valid()) {
+ printf("%s (skipped)\n", test->name);
+ return false;
+ }
+
+ if (test->next && !test->next(test)) {
+ return false;
+ }
+
+ func = test->func;
+ if (!func) {
+ printf("%s (skipped)\n", test->name);
+ return false;
+ }
+
+ do {
+ iterations *= 2;
+ t1 = rdtsc();
+
+ if (!test->parallel) {
+ for (i = 0; i < iterations; ++i)
+ func();
+ } else {
+ atomic_set(&nr_cpus_done, 0);
+ for (i = cpu_count(); i > 0; i--)
+ on_cpu_async(i-1, run_test, func);
+ while (atomic_read(&nr_cpus_done) < cpu_count())
+ ;
+ }
+ t2 = rdtsc();
+ } while ((t2 - t1) < GOAL);
+ printf("%s %d\n", test->name, (int)((t2 - t1) / iterations));
+ return test->next;
+}
+
+static void enable_nx(void *junk)
+{
+ if (cpuid(0x80000001).d & (1 << 20))
+ wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX_MASK);
+}
+
+bool test_wanted(struct test *test, char *wanted[], int nwanted)
+{
+ int i;
+
+ if (!nwanted)
+ return true;
+
+ for (i = 0; i < nwanted; ++i)
+ if (strcmp(wanted[i], test->name) == 0)
+ return true;
+
+ return false;
+}
+
+int main(int ac, char **av)
+{
+ int i;
+ unsigned long membar = 0, base, offset;
+ void *m;
+ pcidevaddr_t pcidev;
+
+ smp_init();
+ setup_vm();
+ nr_cpus = cpu_count();
+
+ for (i = cpu_count(); i > 0; i--)
+ on_cpu(i-1, enable_nx, 0);
+
+ pcidev = pci_find_dev(0x1b36, 0x0005);
+ if (pcidev) {
+ for (i = 0; i < 2; i++) {
+ if (!pci_bar_is_valid(pcidev, i)) {
+ continue;
+ }
+ if (pci_bar_is_memory(pcidev, i)) {
+ membar = pci_bar_addr(pcidev, i);
+ base = membar & ~4095;
+ offset = membar - base;
+ m = alloc_vpages(1);
+
+ install_page((void *)read_cr3(), base, m);
+ pci_test.memaddr = m + offset;
+ } else {
+ pci_test.iobar = pci_bar_addr(pcidev, i);
+ }
+ }
+ printf("pci-testdev at 0x%x membar %lx iobar %x\n",
+ pcidev, membar, pci_test.iobar);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tests); ++i)
+ if (test_wanted(&tests[i], av + 1, ac - 1))
+ while (do_test(&tests[i])) {}
+
+ return 0;
+}
diff --git a/kvm-unittest/x86/vmx.c b/kvm-unittest/x86/vmx.c
new file mode 100644
index 0000000..ca36d35
--- /dev/null
+++ b/kvm-unittest/x86/vmx.c
@@ -0,0 +1,600 @@
+#include "libcflat.h"
+#include "processor.h"
+#include "vm.h"
+#include "desc.h"
+#include "vmx.h"
+#include "msr.h"
+#include "smp.h"
+#include "io.h"
+
+int fails, tests;
+u32 *vmxon_region;
+struct vmcs *vmcs_root;
+u32 vpid_cnt;
+void *guest_stack, *guest_syscall_stack;
+u32 ctrl_pin, ctrl_enter, ctrl_exit, ctrl_cpu[2];
+struct regs regs;
+struct vmx_test *current;
+u64 hypercall_field;
+bool launched;
+u64 host_rflags;
+
+union vmx_basic basic;
+union vmx_ctrl_pin ctrl_pin_rev;
+union vmx_ctrl_cpu ctrl_cpu_rev[2];
+union vmx_ctrl_exit ctrl_exit_rev;
+union vmx_ctrl_ent ctrl_enter_rev;
+union vmx_ept_vpid ept_vpid;
+
+extern u64 gdt64_desc[];
+extern u64 idt_descr[];
+extern u64 tss_descr[];
+extern void *vmx_return;
+extern void *entry_sysenter;
+extern void *guest_entry;
+
+void report(const char *name, int result)
+{
+ ++tests;
+ if (result)
+ printf("PASS: %s\n", name);
+ else {
+ printf("FAIL: %s\n", name);
+ ++fails;
+ }
+}
+
+static int make_vmcs_current(struct vmcs *vmcs)
+{
+ bool ret;
+
+ asm volatile ("vmptrld %1; setbe %0" : "=q" (ret) : "m" (vmcs) : "cc");
+ return ret;
+}
+
+/* entry_sysenter */
+asm(
+ ".align 4, 0x90\n\t"
+ ".globl entry_sysenter\n\t"
+ "entry_sysenter:\n\t"
+ SAVE_GPR
+ " and $0xf, %rax\n\t"
+ " mov %rax, %rdi\n\t"
+ " call syscall_handler\n\t"
+ LOAD_GPR
+ " vmresume\n\t"
+);
+
+static void __attribute__((__used__)) syscall_handler(u64 syscall_no)
+{
+ current->syscall_handler(syscall_no);
+}
+
+static inline int vmx_on()
+{
+ bool ret;
+ asm volatile ("vmxon %1; setbe %0\n\t"
+ : "=q"(ret) : "m"(vmxon_region) : "cc");
+ return ret;
+}
+
+static inline int vmx_off()
+{
+ bool ret;
+ asm volatile("vmxoff; setbe %0\n\t"
+ : "=q"(ret) : : "cc");
+ return ret;
+}
+
+void print_vmexit_info()
+{
+ u64 guest_rip, guest_rsp;
+ ulong reason = vmcs_read(EXI_REASON) & 0xff;
+ ulong exit_qual = vmcs_read(EXI_QUALIFICATION);
+ guest_rip = vmcs_read(GUEST_RIP);
+ guest_rsp = vmcs_read(GUEST_RSP);
+ printf("VMEXIT info:\n");
+ printf("\tvmexit reason = %d\n", reason);
+ printf("\texit qualification = 0x%x\n", exit_qual);
+ printf("\tBit 31 of reason = %x\n", (vmcs_read(EXI_REASON) >> 31) & 1);
+ printf("\tguest_rip = 0x%llx\n", guest_rip);
+ printf("\tRAX=0x%llx RBX=0x%llx RCX=0x%llx RDX=0x%llx\n",
+ regs.rax, regs.rbx, regs.rcx, regs.rdx);
+ printf("\tRSP=0x%llx RBP=0x%llx RSI=0x%llx RDI=0x%llx\n",
+ guest_rsp, regs.rbp, regs.rsi, regs.rdi);
+ printf("\tR8 =0x%llx R9 =0x%llx R10=0x%llx R11=0x%llx\n",
+ regs.r8, regs.r9, regs.r10, regs.r11);
+ printf("\tR12=0x%llx R13=0x%llx R14=0x%llx R15=0x%llx\n",
+ regs.r12, regs.r13, regs.r14, regs.r15);
+}
+
+static void test_vmclear(void)
+{
+ u64 rflags;
+
+ rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
+ write_rflags(rflags);
+ report("test vmclear", vmcs_clear(vmcs_root) == 0);
+}
+
+static void test_vmxoff(void)
+{
+ int ret;
+ u64 rflags;
+
+ rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
+ write_rflags(rflags);
+ ret = vmx_off();
+ report("test vmxoff", !ret);
+}
+
+static void __attribute__((__used__)) guest_main(void)
+{
+ current->guest_main();
+}
+
+/* guest_entry */
+asm(
+ ".align 4, 0x90\n\t"
+ ".globl entry_guest\n\t"
+ "guest_entry:\n\t"
+ " call guest_main\n\t"
+ " mov $1, %edi\n\t"
+ " call hypercall\n\t"
+);
+
+static void init_vmcs_ctrl(void)
+{
+ /* 26.2 CHECKS ON VMX CONTROLS AND HOST-STATE AREA */
+ /* 26.2.1.1 */
+ vmcs_write(PIN_CONTROLS, ctrl_pin);
+ /* Disable VMEXIT of IO instruction */
+ vmcs_write(CPU_EXEC_CTRL0, ctrl_cpu[0]);
+ if (ctrl_cpu_rev[0].set & CPU_SECONDARY) {
+ ctrl_cpu[1] |= ctrl_cpu_rev[1].set & ctrl_cpu_rev[1].clr;
+ vmcs_write(CPU_EXEC_CTRL1, ctrl_cpu[1]);
+ }
+ vmcs_write(CR3_TARGET_COUNT, 0);
+ vmcs_write(VPID, ++vpid_cnt);
+}
+
+static void init_vmcs_host(void)
+{
+ /* 26.2 CHECKS ON VMX CONTROLS AND HOST-STATE AREA */
+ /* 26.2.1.2 */
+ vmcs_write(HOST_EFER, rdmsr(MSR_EFER));
+
+ /* 26.2.1.3 */
+ vmcs_write(ENT_CONTROLS, ctrl_enter);
+ vmcs_write(EXI_CONTROLS, ctrl_exit);
+
+ /* 26.2.2 */
+ vmcs_write(HOST_CR0, read_cr0());
+ vmcs_write(HOST_CR3, read_cr3());
+ vmcs_write(HOST_CR4, read_cr4());
+ vmcs_write(HOST_SYSENTER_EIP, (u64)(&entry_sysenter));
+ vmcs_write(HOST_SYSENTER_CS, SEL_KERN_CODE_64);
+
+ /* 26.2.3 */
+ vmcs_write(HOST_SEL_CS, SEL_KERN_CODE_64);
+ vmcs_write(HOST_SEL_SS, SEL_KERN_DATA_64);
+ vmcs_write(HOST_SEL_DS, SEL_KERN_DATA_64);
+ vmcs_write(HOST_SEL_ES, SEL_KERN_DATA_64);
+ vmcs_write(HOST_SEL_FS, SEL_KERN_DATA_64);
+ vmcs_write(HOST_SEL_GS, SEL_KERN_DATA_64);
+ vmcs_write(HOST_SEL_TR, SEL_TSS_RUN);
+ vmcs_write(HOST_BASE_TR, (u64)tss_descr);
+ vmcs_write(HOST_BASE_GDTR, (u64)gdt64_desc);
+ vmcs_write(HOST_BASE_IDTR, (u64)idt_descr);
+ vmcs_write(HOST_BASE_FS, 0);
+ vmcs_write(HOST_BASE_GS, 0);
+
+ /* Set other vmcs area */
+ vmcs_write(PF_ERROR_MASK, 0);
+ vmcs_write(PF_ERROR_MATCH, 0);
+ vmcs_write(VMCS_LINK_PTR, ~0ul);
+ vmcs_write(VMCS_LINK_PTR_HI, ~0ul);
+ vmcs_write(HOST_RIP, (u64)(&vmx_return));
+}
+
+static void init_vmcs_guest(void)
+{
+ /* 26.3 CHECKING AND LOADING GUEST STATE */
+ ulong guest_cr0, guest_cr4, guest_cr3;
+ /* 26.3.1.1 */
+ guest_cr0 = read_cr0();
+ guest_cr4 = read_cr4();
+ guest_cr3 = read_cr3();
+ if (ctrl_enter & ENT_GUEST_64) {
+ guest_cr0 |= X86_CR0_PG;
+ guest_cr4 |= X86_CR4_PAE;
+ }
+ if ((ctrl_enter & ENT_GUEST_64) == 0)
+ guest_cr4 &= (~X86_CR4_PCIDE);
+ if (guest_cr0 & X86_CR0_PG)
+ guest_cr0 |= X86_CR0_PE;
+ vmcs_write(GUEST_CR0, guest_cr0);
+ vmcs_write(GUEST_CR3, guest_cr3);
+ vmcs_write(GUEST_CR4, guest_cr4);
+ vmcs_write(GUEST_SYSENTER_CS, SEL_KERN_CODE_64);
+ vmcs_write(GUEST_SYSENTER_ESP,
+ (u64)(guest_syscall_stack + PAGE_SIZE - 1));
+ vmcs_write(GUEST_SYSENTER_EIP, (u64)(&entry_sysenter));
+ vmcs_write(GUEST_DR7, 0);
+ vmcs_write(GUEST_EFER, rdmsr(MSR_EFER));
+
+ /* 26.3.1.2 */
+ vmcs_write(GUEST_SEL_CS, SEL_KERN_CODE_64);
+ vmcs_write(GUEST_SEL_SS, SEL_KERN_DATA_64);
+ vmcs_write(GUEST_SEL_DS, SEL_KERN_DATA_64);
+ vmcs_write(GUEST_SEL_ES, SEL_KERN_DATA_64);
+ vmcs_write(GUEST_SEL_FS, SEL_KERN_DATA_64);
+ vmcs_write(GUEST_SEL_GS, SEL_KERN_DATA_64);
+ vmcs_write(GUEST_SEL_TR, SEL_TSS_RUN);
+ vmcs_write(GUEST_SEL_LDTR, 0);
+
+ vmcs_write(GUEST_BASE_CS, 0);
+ vmcs_write(GUEST_BASE_ES, 0);
+ vmcs_write(GUEST_BASE_SS, 0);
+ vmcs_write(GUEST_BASE_DS, 0);
+ vmcs_write(GUEST_BASE_FS, 0);
+ vmcs_write(GUEST_BASE_GS, 0);
+ vmcs_write(GUEST_BASE_TR, (u64)tss_descr);
+ vmcs_write(GUEST_BASE_LDTR, 0);
+
+ vmcs_write(GUEST_LIMIT_CS, 0xFFFFFFFF);
+ vmcs_write(GUEST_LIMIT_DS, 0xFFFFFFFF);
+ vmcs_write(GUEST_LIMIT_ES, 0xFFFFFFFF);
+ vmcs_write(GUEST_LIMIT_SS, 0xFFFFFFFF);
+ vmcs_write(GUEST_LIMIT_FS, 0xFFFFFFFF);
+ vmcs_write(GUEST_LIMIT_GS, 0xFFFFFFFF);
+ vmcs_write(GUEST_LIMIT_LDTR, 0xffff);
+ vmcs_write(GUEST_LIMIT_TR, ((struct descr *)tss_descr)->limit);
+
+ vmcs_write(GUEST_AR_CS, 0xa09b);
+ vmcs_write(GUEST_AR_DS, 0xc093);
+ vmcs_write(GUEST_AR_ES, 0xc093);
+ vmcs_write(GUEST_AR_FS, 0xc093);
+ vmcs_write(GUEST_AR_GS, 0xc093);
+ vmcs_write(GUEST_AR_SS, 0xc093);
+ vmcs_write(GUEST_AR_LDTR, 0x82);
+ vmcs_write(GUEST_AR_TR, 0x8b);
+
+ /* 26.3.1.3 */
+ vmcs_write(GUEST_BASE_GDTR, (u64)gdt64_desc);
+ vmcs_write(GUEST_BASE_IDTR, (u64)idt_descr);
+ vmcs_write(GUEST_LIMIT_GDTR,
+ ((struct descr *)gdt64_desc)->limit & 0xffff);
+ vmcs_write(GUEST_LIMIT_IDTR,
+ ((struct descr *)idt_descr)->limit & 0xffff);
+
+ /* 26.3.1.4 */
+ vmcs_write(GUEST_RIP, (u64)(&guest_entry));
+ vmcs_write(GUEST_RSP, (u64)(guest_stack + PAGE_SIZE - 1));
+ vmcs_write(GUEST_RFLAGS, 0x2);
+
+ /* 26.3.1.5 */
+ vmcs_write(GUEST_ACTV_STATE, 0);
+ vmcs_write(GUEST_INTR_STATE, 0);
+}
+
+static int init_vmcs(struct vmcs **vmcs)
+{
+ *vmcs = alloc_page();
+ memset(*vmcs, 0, PAGE_SIZE);
+ (*vmcs)->revision_id = basic.revision;
+ /* vmclear first to init vmcs */
+ if (vmcs_clear(*vmcs)) {
+ printf("%s : vmcs_clear error\n", __func__);
+ return 1;
+ }
+
+ if (make_vmcs_current(*vmcs)) {
+ printf("%s : make_vmcs_current error\n", __func__);
+ return 1;
+ }
+
+ /* All settings to pin/exit/enter/cpu
+ control fields should be placed here */
+ ctrl_pin |= PIN_EXTINT | PIN_NMI | PIN_VIRT_NMI;
+ ctrl_exit = EXI_LOAD_EFER | EXI_HOST_64;
+ ctrl_enter = (ENT_LOAD_EFER | ENT_GUEST_64);
+ ctrl_cpu[0] |= CPU_HLT;
+ /* DIsable IO instruction VMEXIT now */
+ ctrl_cpu[0] &= (~(CPU_IO | CPU_IO_BITMAP));
+ ctrl_cpu[1] = 0;
+
+ ctrl_pin = (ctrl_pin | ctrl_pin_rev.set) & ctrl_pin_rev.clr;
+ ctrl_enter = (ctrl_enter | ctrl_enter_rev.set) & ctrl_enter_rev.clr;
+ ctrl_exit = (ctrl_exit | ctrl_exit_rev.set) & ctrl_exit_rev.clr;
+ ctrl_cpu[0] = (ctrl_cpu[0] | ctrl_cpu_rev[0].set) & ctrl_cpu_rev[0].clr;
+
+ init_vmcs_ctrl();
+ init_vmcs_host();
+ init_vmcs_guest();
+ return 0;
+}
+
+static void init_vmx(void)
+{
+ ulong fix_cr0_set, fix_cr0_clr;
+ ulong fix_cr4_set, fix_cr4_clr;
+
+ vmxon_region = alloc_page();
+ memset(vmxon_region, 0, PAGE_SIZE);
+
+ fix_cr0_set = rdmsr(MSR_IA32_VMX_CR0_FIXED0);
+ fix_cr0_clr = rdmsr(MSR_IA32_VMX_CR0_FIXED1);
+ fix_cr4_set = rdmsr(MSR_IA32_VMX_CR4_FIXED0);
+ fix_cr4_clr = rdmsr(MSR_IA32_VMX_CR4_FIXED1);
+ basic.val = rdmsr(MSR_IA32_VMX_BASIC);
+ ctrl_pin_rev.val = rdmsr(basic.ctrl ? MSR_IA32_VMX_TRUE_PIN
+ : MSR_IA32_VMX_PINBASED_CTLS);
+ ctrl_exit_rev.val = rdmsr(basic.ctrl ? MSR_IA32_VMX_TRUE_EXIT
+ : MSR_IA32_VMX_EXIT_CTLS);
+ ctrl_enter_rev.val = rdmsr(basic.ctrl ? MSR_IA32_VMX_TRUE_ENTRY
+ : MSR_IA32_VMX_ENTRY_CTLS);
+ ctrl_cpu_rev[0].val = rdmsr(basic.ctrl ? MSR_IA32_VMX_TRUE_PROC
+ : MSR_IA32_VMX_PROCBASED_CTLS);
+ if (ctrl_cpu_rev[0].set & CPU_SECONDARY)
+ ctrl_cpu_rev[1].val = rdmsr(MSR_IA32_VMX_PROCBASED_CTLS2);
+ if (ctrl_cpu_rev[1].set & CPU_EPT || ctrl_cpu_rev[1].set & CPU_VPID)
+ ept_vpid.val = rdmsr(MSR_IA32_VMX_EPT_VPID_CAP);
+
+ write_cr0((read_cr0() & fix_cr0_clr) | fix_cr0_set);
+ write_cr4((read_cr4() & fix_cr4_clr) | fix_cr4_set | X86_CR4_VMXE);
+
+ *vmxon_region = basic.revision;
+
+ guest_stack = alloc_page();
+ memset(guest_stack, 0, PAGE_SIZE);
+ guest_syscall_stack = alloc_page();
+ memset(guest_syscall_stack, 0, PAGE_SIZE);
+}
+
+static int test_vmx_capability(void)
+{
+ struct cpuid r;
+ u64 ret1, ret2;
+ u64 ia32_feature_control;
+ r = cpuid(1);
+ ret1 = ((r.c) >> 5) & 1;
+ ia32_feature_control = rdmsr(MSR_IA32_FEATURE_CONTROL);
+ ret2 = ((ia32_feature_control & 0x5) == 0x5);
+ if ((!ret2) && ((ia32_feature_control & 0x1) == 0)) {
+ wrmsr(MSR_IA32_FEATURE_CONTROL, 0x5);
+ ia32_feature_control = rdmsr(MSR_IA32_FEATURE_CONTROL);
+ ret2 = ((ia32_feature_control & 0x5) == 0x5);
+ }
+ report("test vmx capability", ret1 & ret2);
+ return !(ret1 & ret2);
+}
+
+static int test_vmxon(void)
+{
+ int ret;
+ u64 rflags;
+
+ rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
+ write_rflags(rflags);
+ ret = vmx_on();
+ report("test vmxon", !ret);
+ return ret;
+}
+
+static void test_vmptrld(void)
+{
+ u64 rflags;
+ struct vmcs *vmcs;
+
+ vmcs = alloc_page();
+ vmcs->revision_id = basic.revision;
+ rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
+ write_rflags(rflags);
+ report("test vmptrld", make_vmcs_current(vmcs) == 0);
+}
+
+static void test_vmptrst(void)
+{
+ u64 rflags;
+ int ret;
+ struct vmcs *vmcs1, *vmcs2;
+
+ vmcs1 = alloc_page();
+ memset(vmcs1, 0, PAGE_SIZE);
+ init_vmcs(&vmcs1);
+ rflags = read_rflags() | X86_EFLAGS_CF | X86_EFLAGS_ZF;
+ write_rflags(rflags);
+ ret = vmcs_save(&vmcs2);
+ report("test vmptrst", (!ret) && (vmcs1 == vmcs2));
+}
+
+/* This function can only be called in guest */
+static void __attribute__((__used__)) hypercall(u32 hypercall_no)
+{
+ u64 val = 0;
+ val = (hypercall_no & HYPERCALL_MASK) | HYPERCALL_BIT;
+ hypercall_field = val;
+ asm volatile("vmcall\n\t");
+}
+
+static bool is_hypercall()
+{
+ ulong reason, hyper_bit;
+
+ reason = vmcs_read(EXI_REASON) & 0xff;
+ hyper_bit = hypercall_field & HYPERCALL_BIT;
+ if (reason == VMX_VMCALL && hyper_bit)
+ return true;
+ return false;
+}
+
+static int handle_hypercall()
+{
+ ulong hypercall_no;
+
+ hypercall_no = hypercall_field & HYPERCALL_MASK;
+ hypercall_field = 0;
+ switch (hypercall_no) {
+ case HYPERCALL_VMEXIT:
+ return VMX_TEST_VMEXIT;
+ default:
+ printf("ERROR : Invalid hypercall number : %d\n", hypercall_no);
+ }
+ return VMX_TEST_EXIT;
+}
+
+static int exit_handler()
+{
+ int ret;
+
+ current->exits++;
+ regs.rflags = vmcs_read(GUEST_RFLAGS);
+ if (is_hypercall())
+ ret = handle_hypercall();
+ else
+ ret = current->exit_handler();
+ vmcs_write(GUEST_RFLAGS, regs.rflags);
+ switch (ret) {
+ case VMX_TEST_VMEXIT:
+ case VMX_TEST_RESUME:
+ return ret;
+ case VMX_TEST_EXIT:
+ break;
+ default:
+ printf("ERROR : Invalid exit_handler return val %d.\n"
+ , ret);
+ }
+ print_vmexit_info();
+ exit(-1);
+ return 0;
+}
+
+static int vmx_run()
+{
+ u32 ret = 0, fail = 0;
+
+ while (1) {
+ asm volatile (
+ "mov %%rsp, %%rsi\n\t"
+ "mov %2, %%rdi\n\t"
+ "vmwrite %%rsi, %%rdi\n\t"
+
+ LOAD_GPR_C
+ "cmpl $0, %1\n\t"
+ "jne 1f\n\t"
+ LOAD_RFLAGS
+ "vmlaunch\n\t"
+ "jmp 2f\n\t"
+ "1: "
+ "vmresume\n\t"
+ "2: "
+ "setbe %0\n\t"
+ "vmx_return:\n\t"
+ SAVE_GPR_C
+ SAVE_RFLAGS
+ : "=m"(fail)
+ : "m"(launched), "i"(HOST_RSP)
+ : "rdi", "rsi", "memory", "cc"
+
+ );
+ if (fail)
+ ret = launched ? VMX_TEST_RESUME_ERR :
+ VMX_TEST_LAUNCH_ERR;
+ else {
+ launched = 1;
+ ret = exit_handler();
+ }
+ if (ret != VMX_TEST_RESUME)
+ break;
+ }
+ launched = 0;
+ switch (ret) {
+ case VMX_TEST_VMEXIT:
+ return 0;
+ case VMX_TEST_LAUNCH_ERR:
+ printf("%s : vmlaunch failed.\n", __func__);
+ if ((!(host_rflags & X86_EFLAGS_CF) && !(host_rflags &
X86_EFLAGS_ZF))
+ || ((host_rflags & X86_EFLAGS_CF) && (host_rflags &
X86_EFLAGS_ZF)))
+ printf("\tvmlaunch set wrong flags\n");
+ report("test vmlaunch", 0);
+ break;
+ case VMX_TEST_RESUME_ERR:
+ printf("%s : vmresume failed.\n", __func__);
+ if ((!(host_rflags & X86_EFLAGS_CF) && !(host_rflags &
X86_EFLAGS_ZF))
+ || ((host_rflags & X86_EFLAGS_CF) && (host_rflags &
X86_EFLAGS_ZF)))
+ printf("\tvmresume set wrong flags\n");
+ report("test vmresume", 0);
+ break;
+ default:
+ printf("%s : unhandled ret from exit_handler, ret=%d.\n",
__func__, ret);
+ break;
+ }
+ return 1;
+}
+
+static int test_run(struct vmx_test *test)
+{
+ if (test->name == NULL)
+ test->name = "(no name)";
+ if (vmx_on()) {
+ printf("%s : vmxon failed.\n", __func__);
+ return 1;
+ }
+ init_vmcs(&(test->vmcs));
+ /* Directly call test->init is ok here, init_vmcs has done
+ vmcs init, vmclear and vmptrld*/
+ if (test->init)
+ test->init(test->vmcs);
+ test->exits = 0;
+ current = test;
+ regs = test->guest_regs;
+ vmcs_write(GUEST_RFLAGS, regs.rflags | 0x2);
+ launched = 0;
+ printf("\nTest suite : %s\n", test->name);
+ vmx_run();
+ if (vmx_off()) {
+ printf("%s : vmxoff failed.\n", __func__);
+ return 1;
+ }
+ return 0;
+}
+
+extern struct vmx_test vmx_tests[];
+
+int main(void)
+{
+ int i = 0;
+
+ setup_vm();
+ setup_idt();
+ fails = tests = 0;
+ hypercall_field = 0;
+
+ if (test_vmx_capability() != 0) {
+ printf("ERROR : vmx not supported, check +vmx option\n");
+ goto exit;
+ }
+ init_vmx();
+ /* Set basic test ctxt the same as "null" */
+ current = &vmx_tests[0];
+ if (test_vmxon() != 0)
+ goto exit;
+ test_vmptrld();
+ test_vmclear();
+ test_vmptrst();
+ init_vmcs(&vmcs_root);
+ if (vmx_run()) {
+ report("test vmlaunch", 0);
+ goto exit;
+ }
+ test_vmxoff();
+
+ while (vmx_tests[++i].name != NULL)
+ if (test_run(&vmx_tests[i]))
+ goto exit;
+
+exit:
+ printf("\nSUMMARY: %d tests, %d failures\n", tests, fails);
+ return fails ? 1 : 0;
+}
diff --git a/kvm-unittest/x86/vmx_tests.c b/kvm-unittest/x86/vmx_tests.c
new file mode 100644
index 0000000..c1b39f4
--- /dev/null
+++ b/kvm-unittest/x86/vmx_tests.c
@@ -0,0 +1,87 @@
+#include "vmx.h"
+
+void basic_init()
+{
+}
+
+void basic_guest_main()
+{
+ /* Here is a basic guest_main, print Hello World */
+ printf("\tHello World, this is null_guest_main!\n");
+}
+
+int basic_exit_handler()
+{
+ u64 guest_rip;
+ ulong reason;
+
+ guest_rip = vmcs_read(GUEST_RIP);
+ reason = vmcs_read(EXI_REASON) & 0xff;
+
+ switch (reason) {
+ case VMX_VMCALL:
+ print_vmexit_info();
+ vmcs_write(GUEST_RIP, guest_rip + 3);
+ return VMX_TEST_RESUME;
+ default:
+ break;
+ }
+ printf("ERROR : Unhandled vmx exit.\n");
+ print_vmexit_info();
+ return VMX_TEST_EXIT;
+}
+
+void basic_syscall_handler(u64 syscall_no)
+{
+}
+
+void vmenter_main()
+{
+ u64 rax;
+ u64 rsp, resume_rsp;
+
+ report("test vmlaunch", 1);
+
+ asm volatile(
+ "mov %%rsp, %0\n\t"
+ "mov %3, %%rax\n\t"
+ "vmcall\n\t"
+ "mov %%rax, %1\n\t"
+ "mov %%rsp, %2\n\t"
+ : "=r"(rsp), "=r"(rax), "=r"(resume_rsp)
+ : "g"(0xABCD));
+ report("test vmresume", (rax == 0xFFFF) && (rsp == resume_rsp));
+}
+
+int vmenter_exit_handler()
+{
+ u64 guest_rip;
+ ulong reason;
+
+ guest_rip = vmcs_read(GUEST_RIP);
+ reason = vmcs_read(EXI_REASON) & 0xff;
+ switch (reason) {
+ case VMX_VMCALL:
+ if (regs.rax != 0xABCD) {
+ report("test vmresume", 0);
+ return VMX_TEST_VMEXIT;
+ }
+ regs.rax = 0xFFFF;
+ vmcs_write(GUEST_RIP, guest_rip + 3);
+ return VMX_TEST_RESUME;
+ default:
+ report("test vmresume", 0);
+ print_vmexit_info();
+ }
+ return VMX_TEST_VMEXIT;
+}
+
+/* name/init/guest_main/exit_handler/syscall_handler/guest_regs
+ basic_* just implement some basic functions */
+struct vmx_test vmx_tests[] = {
+ { "null", basic_init, basic_guest_main, basic_exit_handler,
+ basic_syscall_handler, {0} },
+ { "vmenter", basic_init, vmenter_main, vmenter_exit_handler,
+ basic_syscall_handler, {0} },
+ { NULL, NULL, NULL, NULL, NULL, {0} },
+};
diff --git a/kvm-unittest/x86/xsave.c b/kvm-unittest/x86/xsave.c
new file mode 100644
index 0000000..057b0ff
--- /dev/null
+++ b/kvm-unittest/x86/xsave.c
@@ -0,0 +1,262 @@
+#include "libcflat.h"
+#include "desc.h"
+
+#ifdef __x86_64__
+#define uint64_t unsigned long
+#else
+#define uint64_t unsigned long long
+#endif
+
+static inline void __cpuid(unsigned int *eax, unsigned int *ebx,
+ unsigned int *ecx, unsigned int *edx)
+{
+ /* ecx is often an input as well as an output. */
+ asm volatile("cpuid"
+ : "=a" (*eax),
+ "=b" (*ebx),
+ "=c" (*ecx),
+ "=d" (*edx)
+ : "0" (*eax), "2" (*ecx));
+}
+
+/*
+ * Generic CPUID function
+ * clear %ecx since some cpus (Cyrix MII) do not set or clear %ecx
+ * resulting in stale register contents being returned.
+ */
+void cpuid(unsigned int op,
+ unsigned int *eax, unsigned int *ebx,
+ unsigned int *ecx, unsigned int *edx)
+{
+ *eax = op;
+ *ecx = 0;
+ __cpuid(eax, ebx, ecx, edx);
+}
+
+/* Some CPUID calls want 'count' to be placed in ecx */
+void cpuid_count(unsigned int op, int count,
+ unsigned int *eax, unsigned int *ebx,
+ unsigned int *ecx, unsigned int *edx)
+{
+ *eax = op;
+ *ecx = count;
+ __cpuid(eax, ebx, ecx, edx);
+}
+
+int xgetbv_checking(u32 index, u64 *result)
+{
+ u32 eax, edx;
+
+ asm volatile(ASM_TRY("1f")
+ ".byte 0x0f,0x01,0xd0\n\t" /* xgetbv */
+ "1:"
+ : "=a" (eax), "=d" (edx)
+ : "c" (index));
+ *result = eax + ((u64)edx << 32);
+ return exception_vector();
+}
+
+int xsetbv_checking(u32 index, u64 value)
+{
+ u32 eax = value;
+ u32 edx = value >> 32;
+
+ asm volatile(ASM_TRY("1f")
+ ".byte 0x0f,0x01,0xd1\n\t" /* xsetbv */
+ "1:"
+ : : "a" (eax), "d" (edx), "c" (index));
+ return exception_vector();
+}
+
+unsigned long read_cr4(void)
+{
+ unsigned long val;
+ asm volatile("mov %%cr4,%0" : "=r" (val));
+ return val;
+}
+
+int write_cr4_checking(unsigned long val)
+{
+ asm volatile(ASM_TRY("1f")
+ "mov %0,%%cr4\n\t"
+ "1:": : "r" (val));
+ return exception_vector();
+}
+
+#define CPUID_1_ECX_XSAVE (1 << 26)
+#define CPUID_1_ECX_OSXSAVE (1 << 27)
+int check_cpuid_1_ecx(unsigned int bit)
+{
+ unsigned int eax, ebx, ecx, edx;
+ cpuid(1, &eax, &ebx, &ecx, &edx);
+ if (ecx & bit)
+ return 1;
+ return 0;
+}
+
+uint64_t get_supported_xcr0(void)
+{
+ unsigned int eax, ebx, ecx, edx;
+ cpuid_count(0xd, 0, &eax, &ebx, &ecx, &edx);
+ printf("eax %x, ebx %x, ecx %x, edx %x\n",
+ eax, ebx, ecx, edx);
+ return eax + ((u64)edx << 32);
+}
+
+#define X86_CR4_OSXSAVE 0x00040000
+#define XCR_XFEATURE_ENABLED_MASK 0x00000000
+#define XCR_XFEATURE_ILLEGAL_MASK 0x00000010
+
+#define XSTATE_FP 0x1
+#define XSTATE_SSE 0x2
+#define XSTATE_YMM 0x4
+
+static int total_tests, fail_tests;
+
+void pass_if(int condition)
+{
+ total_tests ++;
+ if (condition)
+ printf("Pass!\n");
+ else {
+ printf("Fail!\n");
+ fail_tests ++;
+ }
+}
+
+void test_xsave(void)
+{
+ unsigned long cr4;
+ uint64_t supported_xcr0;
+ uint64_t test_bits;
+ u64 xcr0;
+ int r;
+
+ printf("Legal instruction testing:\n");
+ supported_xcr0 = get_supported_xcr0();
+ printf("Supported XCR0 bits: 0x%x\n", supported_xcr0);
+
+ printf("Check minimal XSAVE required bits: ");
+ test_bits = XSTATE_FP | XSTATE_SSE;
+ pass_if((supported_xcr0 & test_bits) == test_bits);
+
+ printf("Set CR4 OSXSAVE: ");
+ cr4 = read_cr4();
+ r = write_cr4_checking(cr4 | X86_CR4_OSXSAVE);
+ pass_if(r == 0);
+
+ printf("Check CPUID.1.ECX.OSXSAVE - expect 1: ");
+ pass_if(check_cpuid_1_ecx(CPUID_1_ECX_OSXSAVE));
+
+ printf(" Legal tests\n");
+ printf(" xsetbv(XCR_XFEATURE_ENABLED_MASK, XSTATE_FP): ");
+ test_bits = XSTATE_FP;
+ r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
+ pass_if(r == 0);
+ printf(" xsetbv(XCR_XFEATURE_ENABLED_MASK, "
+ "XSTATE_FP | XSTATE_SSE): ");
+ test_bits = XSTATE_FP | XSTATE_SSE;
+ r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
+ pass_if(r == 0);
+ printf(" xgetbv(XCR_XFEATURE_ENABLED_MASK): ");
+ r = xgetbv_checking(XCR_XFEATURE_ENABLED_MASK, &xcr0);
+ pass_if(r == 0);
+ printf(" Illegal tests\n");
+ printf(" xsetbv(XCR_XFEATURE_ENABLED_MASK, 0) - expect #GP: ");
+ test_bits = 0;
+ r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
+ pass_if(r == GP_VECTOR);
+ printf(" xsetbv(XCR_XFEATURE_ENABLED_MASK, XSTATE_SSE) "
+ "- expect #GP: ");
+ test_bits = XSTATE_SSE;
+ r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
+ pass_if(r == GP_VECTOR);
+ if (supported_xcr0 & XSTATE_YMM) {
+ printf(" xsetbv(XCR_XFEATURE_ENABLED_MASK, "
+ "XSTATE_YMM) - expect #GP: ");
+ test_bits = XSTATE_YMM;
+ r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
+ pass_if(r == GP_VECTOR);
+ printf(" xsetbv(XCR_XFEATURE_ENABLED_MASK, "
+ "XSTATE_FP | XSTATE_YMM) - expect #GP: ");
+ test_bits = XSTATE_FP | XSTATE_YMM;
+ r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
+ pass_if(r == GP_VECTOR);
+ }
+ printf(" xsetbv(XCR_XFEATURE_ILLEGAL_MASK, XSTATE_FP) "
+ "- expect #GP: ");
+ test_bits = XSTATE_SSE;
+ r = xsetbv_checking(XCR_XFEATURE_ILLEGAL_MASK, test_bits);
+ pass_if(r == GP_VECTOR);
+ printf(" xgetbv(XCR_XFEATURE_ILLEGAL_MASK, XSTATE_FP) "
+ "- expect #GP: ");
+ test_bits = XSTATE_SSE;
+ r = xsetbv_checking(XCR_XFEATURE_ILLEGAL_MASK, test_bits);
+ pass_if(r == GP_VECTOR);
+
+ printf("Unset CR4 OSXSAVE: ");
+ cr4 &= ~X86_CR4_OSXSAVE;
+ r = write_cr4_checking(cr4);
+ pass_if(r == 0);
+ printf("Check CPUID.1.ECX.OSXSAVE - expect 0: ");
+ pass_if(check_cpuid_1_ecx(CPUID_1_ECX_OSXSAVE) == 0);
+ printf(" Illegal tests:\n");
+ printf(" xsetbv(XCR_XFEATURE_ENABLED_MASK, XSTATE_FP) - expect #UD:
");
+ test_bits = XSTATE_FP;
+ r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
+ pass_if(r == UD_VECTOR);
+ printf(" xsetbv(XCR_XFEATURE_ENABLED_MASK, "
+ "XSTATE_FP | XSTATE_SSE) - expect #UD: ");
+ test_bits = XSTATE_FP | XSTATE_SSE;
+ r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, test_bits);
+ pass_if(r == UD_VECTOR);
+ printf(" Illegal tests:\n");
+ printf(" xgetbv(XCR_XFEATURE_ENABLED_MASK) - expect #UD: ");
+ r = xgetbv_checking(XCR_XFEATURE_ENABLED_MASK, &xcr0);
+ pass_if(r == UD_VECTOR);
+}
+
+void test_no_xsave(void)
+{
+ unsigned long cr4;
+ u64 xcr0;
+ int r;
+
+ printf("Check CPUID.1.ECX.OSXSAVE - expect 0: ");
+ pass_if(check_cpuid_1_ecx(CPUID_1_ECX_OSXSAVE) == 0);
+
+ printf("Illegal instruction testing:\n");
+
+ printf("Set OSXSAVE in CR4 - expect #GP: ");
+ cr4 = read_cr4();
+ r = write_cr4_checking(cr4 | X86_CR4_OSXSAVE);
+ pass_if(r == GP_VECTOR);
+
+ printf("Execute xgetbv - expect #UD: ");
+ r = xgetbv_checking(XCR_XFEATURE_ENABLED_MASK, &xcr0);
+ pass_if(r == UD_VECTOR);
+
+ printf("Execute xsetbv - expect #UD: ");
+ r = xsetbv_checking(XCR_XFEATURE_ENABLED_MASK, 0x3);
+ pass_if(r == UD_VECTOR);
+}
+
+int main(void)
+{
+ setup_idt();
+ if (check_cpuid_1_ecx(CPUID_1_ECX_XSAVE)) {
+ printf("CPU has XSAVE feature\n");
+ test_xsave();
+ } else {
+ printf("CPU don't has XSAVE feature\n");
+ test_no_xsave();
+ }
+ printf("Total test: %d\n", total_tests);
+ if (fail_tests == 0)
+ printf("ALL PASS!\n");
+ else {
+ printf("Fail %d tests.\n", fail_tests);
+ return 1;
+ }
+ return 0;
+}
diff --git a/kvm-unittest/.gitignore b/kvm-unittest/.gitignore
new file mode 100644
index 0000000..ed857b7
--- /dev/null
+++ b/kvm-unittest/.gitignore
@@ -0,0 +1,9 @@
+.gdbinit
+*.a
+*.d
+*.o
+*.flat
+.pc
+patches
+.stgit-*
+cscope.*
diff --git a/kvm-unittest/COPYRIGHT b/kvm-unittest/COPYRIGHT
new file mode 100644
index 0000000..d35649c
--- /dev/null
+++ b/kvm-unittest/COPYRIGHT
@@ -0,0 +1,4 @@
+Copyright (C) 2006 Qumranet.
+
+The files in this directory and its subdirectories are licensed under the
+GNU LGPL, version 2.
diff --git a/kvm-unittest/Makefile b/kvm-unittest/Makefile
new file mode 100644
index 0000000..b6e8759
--- /dev/null
+++ b/kvm-unittest/Makefile
@@ -0,0 +1,60 @@
+
+include config.mak
+
+DESTDIR := $(PREFIX)/share/qemu/tests
+
+.PHONY: arch_clean clean
+
+#make sure env CFLAGS variable is not used
+CFLAGS = -g
+
+libgcc := $(shell $(CC) --print-libgcc-file-name)
+
+libcflat := lib/libcflat.a
+cflatobjs := \
+ lib/panic.o \
+ lib/printf.o \
+ lib/string.o
+cflatobjs += lib/argv.o
+
+#include architecure specific make rules
+include config-$(ARCH).mak
+
+# cc-option
+# Usage: OP_CFLAGS+=$(call cc-option, -falign-functions=0, -malign-functions=0)
+
+cc-option = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null \
+ > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
+
+CFLAGS += -O1
+CFLAGS += $(autodepend-flags) -g -fomit-frame-pointer -Wall
+CFLAGS += $(call cc-option, -fno-stack-protector, "")
+CFLAGS += $(call cc-option, -fno-stack-protector-all, "")
+CFLAGS += -I.
+
+CXXFLAGS += $(CFLAGS)
+
+autodepend-flags = -MMD -MF $(dir $*).$(notdir $*).d
+
+LDFLAGS += $(CFLAGS)
+LDFLAGS += -pthread -lrt
+
+kvmtrace_objs= kvmtrace.o
+
+kvmtrace: $(kvmtrace_objs)
+ $(CC) $(LDFLAGS) $^ -o $@
+
+$(libcflat): $(cflatobjs)
+ $(AR) rcs $@ $^
+
+%.o: %.S
+ $(CC) $(CFLAGS) -c -nostdlib -o $@ $<
+
+-include .*.d */.*.d */*/.*.d
+
+install:
+ mkdir -p $(DESTDIR)
+ install $(tests_and_config) $(DESTDIR)
+
+clean: arch_clean
+ $(RM) kvmtrace *.o *.a .*.d $(libcflat) $(cflatobjs)
diff --git a/kvm-unittest/README b/kvm-unittest/README
new file mode 100644
index 0000000..db525e3
--- /dev/null
+++ b/kvm-unittest/README
@@ -0,0 +1,36 @@
+This directory contains sources for a kvm test suite.
+
+Tests for x86 architecture are run as kernel images for qemu that supports
multiboot format.
+Tests uses an infrastructure called from the bios code. The infrastructure
initialize the system/cpu's,
+switch to long-mode and calls the 'main' function of the individual test.
+Tests uses a qemu's virtual test device, named testdev, for services like
printing, exiting, query memory size etc.
+See file testdev.txt for more details.
+
+To create the tests' images just type 'make' in this directory.
+Tests' images created in ./<ARCH>/*.flat
+
+An example of a test invocation:
+Using qemu-kvm:
+
+qemu-kvm -device testdev,chardev=testlog -chardev file,id=testlog,path=msr.out
-serial stdio -kernel ./x86/msr.flat
+This invocation runs the msr test case. The test outputs to stdio.
+
+Using qemu (supported since qemu 1.3):
+qemu-system-x86_64 -enable-kvm -device pc-testdev -serial stdio -device
isa-debug-exit,iobase=0xf4,iosize=0x4 -kernel ./x86/msr.flat
+
+Or use a runner script to detect the correct invocation:
+./x86-run ./x86/msr.flat
+To select a specific qemu binary, specify the QEMU=<path> environment:
+QEMU=/tmp/qemu/x86_64-softmmu/qemu-system-x86_64 ./x86-run ./x86/msr.flat
+
+The exit status of the binary (and the script) is inconsistent: with
+qemu-system, after the unittest is done, the exit status of qemu is 1,
+different from the 'old style' qemu-kvm, whose exit status in successful
+completion is 0.
+
+Directory structure:
+.: Makefile and config files for the tests
+./lib: general services for the tests
+./lib/<ARCH>: architecture dependent services for the tests
+./<ARCH>: the sources of the tests and the created objects/images
+
diff --git a/kvm-unittest/api/api-sample.cc b/kvm-unittest/api/api-sample.cc
new file mode 100644
index 0000000..524ad7b
--- /dev/null
+++ b/kvm-unittest/api/api-sample.cc
@@ -0,0 +1,30 @@
+
+#include "api/kvmxx.hh"
+#include "api/identity.hh"
+#include "api/exception.hh"
+#include "stdio.h"
+
+static int global = 0;
+
+static void set_global()
+{
+ global = 1;
+}
+
+int test_main(int ac, char** av)
+{
+ kvm::system system;
+ kvm::vm vm(system);
+ mem_map memmap(vm);
+ identity::vm ident_vm(vm, memmap);
+ kvm::vcpu vcpu(vm, 0);
+ identity::vcpu thread(vcpu, set_global);
+ vcpu.run();
+ printf("global %d\n", global);
+ return global == 1 ? 0 : 1;
+}
+
+int main(int ac, char** av)
+{
+ return try_main(test_main, ac, av);
+}
diff --git a/kvm-unittest/api/dirty-log-perf.cc
b/kvm-unittest/api/dirty-log-perf.cc
new file mode 100644
index 0000000..7f2488e
--- /dev/null
+++ b/kvm-unittest/api/dirty-log-perf.cc
@@ -0,0 +1,144 @@
+#include "kvmxx.hh"
+#include "memmap.hh"
+#include "identity.hh"
+#include <boost/thread/thread.hpp>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+
+namespace {
+
+const int page_size = 4096;
+int64_t nr_total_pages = 256 * 1024;
+int64_t nr_slot_pages = 256 * 1024;
+
+// Return the current time in nanoseconds.
+uint64_t time_ns()
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return ts.tv_sec * (uint64_t)1000000000 + ts.tv_nsec;
+}
+
+// Update nr_to_write pages selected from nr_pages pages.
+void write_mem(void* slot_head, int64_t nr_to_write, int64_t nr_pages)
+{
+ char* var = static_cast<char*>(slot_head);
+ int64_t interval = nr_pages / nr_to_write;
+
+ for (int64_t i = 0; i < nr_to_write; ++i) {
+ ++(*var);
+ var += interval * page_size;
+ }
+}
+
+using boost::ref;
+using std::tr1::bind;
+
+// Let the guest update nr_to_write pages selected from nr_pages pages.
+void do_guest_write(kvm::vcpu& vcpu, void* slot_head,
+ int64_t nr_to_write, int64_t nr_pages)
+{
+ identity::vcpu guest_write_thread(vcpu, bind(write_mem, ref(slot_head),
+ nr_to_write, nr_pages));
+ vcpu.run();
+}
+
+// Check how long it takes to update dirty log.
+void check_dirty_log(kvm::vcpu& vcpu, mem_slot& slot, void* slot_head)
+{
+ slot.set_dirty_logging(true);
+ slot.update_dirty_log();
+
+ for (int64_t i = 1; i <= nr_slot_pages; i *= 2) {
+ do_guest_write(vcpu, slot_head, i, nr_slot_pages);
+
+ uint64_t start_ns = time_ns();
+ slot.update_dirty_log();
+ uint64_t end_ns = time_ns();
+
+ printf("get dirty log: %10lld ns for %10lld dirty pages\n",
+ end_ns - start_ns, i);
+ }
+
+ slot.set_dirty_logging(false);
+}
+
+}
+
+void parse_options(int ac, char **av)
+{
+ int opt;
+ char *endptr;
+
+ while ((opt = getopt(ac, av, "n:m:")) != -1) {
+ switch (opt) {
+ case 'n':
+ errno = 0;
+ nr_slot_pages = strtol(optarg, &endptr, 10);
+ if (errno || endptr == optarg) {
+ printf("dirty-log-perf: Invalid number: -n %s\n", optarg);
+ exit(1);
+ }
+ if (*endptr == 'k' || *endptr == 'K') {
+ nr_slot_pages *= 1024;
+ }
+ break;
+ case 'm':
+ errno = 0;
+ nr_total_pages = strtol(optarg, &endptr, 10);
+ if (errno || endptr == optarg) {
+ printf("dirty-log-perf: Invalid number: -m %s\n", optarg);
+ exit(1);
+ }
+ if (*endptr == 'k' || *endptr == 'K') {
+ nr_total_pages *= 1024;
+ }
+ break;
+ default:
+ printf("dirty-log-perf: Invalid option\n");
+ exit(1);
+ }
+ }
+
+ if (nr_slot_pages > nr_total_pages) {
+ printf("dirty-log-perf: Invalid setting: slot %lld > mem %lld\n",
+ nr_slot_pages, nr_total_pages);
+ exit(1);
+ }
+ printf("dirty-log-perf: %lld slot pages / %lld mem pages\n",
+ nr_slot_pages, nr_total_pages);
+}
+
+int main(int ac, char **av)
+{
+ kvm::system sys;
+ kvm::vm vm(sys);
+ mem_map memmap(vm);
+
+ parse_options(ac, av);
+
+ void* mem_head;
+ int64_t mem_size = nr_total_pages * page_size;
+ if (posix_memalign(&mem_head, page_size, mem_size)) {
+ printf("dirty-log-perf: Could not allocate guest memory.\n");
+ exit(1);
+ }
+ uint64_t mem_addr = reinterpret_cast<uint64_t>(mem_head);
+
+ identity::hole hole(mem_head, mem_size);
+ identity::vm ident_vm(vm, memmap, hole);
+ kvm::vcpu vcpu(vm, 0);
+
+ uint64_t slot_size = nr_slot_pages * page_size;
+ uint64_t next_size = mem_size - slot_size;
+ uint64_t next_addr = mem_addr + slot_size;
+ mem_slot slot(memmap, mem_addr, slot_size, mem_head);
+ mem_slot other_slot(memmap, next_addr, next_size, (void *)next_addr);
+
+ // pre-allocate shadow pages
+ do_guest_write(vcpu, mem_head, nr_total_pages, nr_total_pages);
+ check_dirty_log(vcpu, slot, mem_head);
+ return 0;
+}
diff --git a/kvm-unittest/api/dirty-log.cc b/kvm-unittest/api/dirty-log.cc
new file mode 100644
index 0000000..1e4ef9e
--- /dev/null
+++ b/kvm-unittest/api/dirty-log.cc
@@ -0,0 +1,78 @@
+#include "kvmxx.hh"
+#include "memmap.hh"
+#include "identity.hh"
+#include <boost/thread/thread.hpp>
+#include <stdlib.h>
+#include <stdio.h>
+
+namespace {
+
+void delay_loop(unsigned n)
+{
+ for (unsigned i = 0; i < n; ++i) {
+ asm volatile("pause");
+ }
+ }
+
+void write_mem(volatile bool& running, volatile int* shared_var)
+{
+ while (running) {
+ ++*shared_var;
+ delay_loop(1000);
+ }
+}
+
+void check_dirty_log(mem_slot& slot,
+ volatile bool& running,
+ volatile int* shared_var,
+ int& nr_fail)
+{
+ uint64_t shared_var_gpa = reinterpret_cast<uint64_t>(shared_var);
+ slot.set_dirty_logging(true);
+ slot.update_dirty_log();
+ for (int i = 0; i < 10000000; ++i) {
+ int sample1 = *shared_var;
+ delay_loop(600);
+ int sample2 = *shared_var;
+ slot.update_dirty_log();
+ if (!slot.is_dirty(shared_var_gpa) && sample1 != sample2) {
+ ++nr_fail;
+ }
+ }
+ running = false;
+ slot.set_dirty_logging(false);
+}
+
+}
+
+using boost::ref;
+using std::tr1::bind;
+
+int main(int ac, char **av)
+{
+ kvm::system sys;
+ kvm::vm vm(sys);
+ mem_map memmap(vm);
+ void* logged_slot_virt;
+ posix_memalign(&logged_slot_virt, 4096, 4096);
+ int* shared_var = static_cast<int*>(logged_slot_virt);
+ identity::hole hole(logged_slot_virt, 4096);
+ identity::vm ident_vm(vm, memmap, hole);
+ kvm::vcpu vcpu(vm, 0);
+ bool running = true;
+ int nr_fail = 0;
+ mem_slot logged_slot(memmap,
+ reinterpret_cast<uint64_t>(logged_slot_virt),
+ 4096, logged_slot_virt);
+ boost::thread host_poll_thread(check_dirty_log, ref(logged_slot),
+ ref(running),
+ ref(shared_var), ref(nr_fail));
+ identity::vcpu guest_write_thread(vcpu,
+ bind(write_mem,
+ ref(running),
+ ref(shared_var)));
+ vcpu.run();
+ host_poll_thread.join();
+ printf("Dirty bitmap failures: %d\n", nr_fail);
+ return nr_fail == 0 ? 0 : 1;
+}
diff --git a/kvm-unittest/api/exception.cc b/kvm-unittest/api/exception.cc
new file mode 100644
index 0000000..910bdff
--- /dev/null
+++ b/kvm-unittest/api/exception.cc
@@ -0,0 +1,33 @@
+#include "exception.hh"
+#include <cstdio>
+#include <cstring>
+
+errno_exception::errno_exception(int errno)
+ : _errno(errno)
+{
+}
+
+int errno_exception::errno() const
+{
+ return _errno;
+}
+
+const char *errno_exception::what()
+{
+ std::snprintf(_buf, sizeof _buf, "error: %s (%d)",
+ std::strerror(_errno), _errno);
+ return _buf;
+}
+
+int try_main(int (*main)(int argc, char** argv), int argc, char** argv,
+ int ret_on_exception)
+{
+ try {
+ return main(argc, argv);
+ } catch (std::exception& e) {
+ std::fprintf(stderr, "exception: %s\n", e.what());
+ } catch (...) {
+ std::fprintf(stderr, "unknown exception\n");
+ }
+ return ret_on_exception;
+}
diff --git a/kvm-unittest/api/exception.hh b/kvm-unittest/api/exception.hh
new file mode 100644
index 0000000..f78d9a1
--- /dev/null
+++ b/kvm-unittest/api/exception.hh
@@ -0,0 +1,19 @@
+#ifndef EXCEPTION_HH
+#define EXCEPTION_HH
+
+#include <exception>
+
+class errno_exception : public std::exception {
+public:
+ explicit errno_exception(int err_no);
+ int errno() const;
+ virtual const char *what();
+private:
+ int _errno;
+ char _buf[1000];
+};
+
+int try_main(int (*main)(int argc, char** argv), int argc, char** argv,
+ int ret_on_exception = 127);
+
+#endif
diff --git a/kvm-unittest/api/identity.cc b/kvm-unittest/api/identity.cc
new file mode 100644
index 0000000..e04231b
--- /dev/null
+++ b/kvm-unittest/api/identity.cc
@@ -0,0 +1,95 @@
+
+#include "identity.hh"
+#include <stdio.h>
+
+namespace identity {
+
+typedef unsigned long ulong;
+
+hole::hole()
+ : address(), size()
+{
+}
+
+hole::hole(void* address, size_t size)
+ : address(address), size(size)
+{
+}
+
+vm::vm(kvm::vm& vm, mem_map& mmap, hole h)
+{
+ uint64_t hole_gpa = reinterpret_cast<uint64_t>(h.address);
+ char* hole_hva = static_cast<char*>(h.address);
+ if (h.address) {
+ _slots.push_back(mem_slot_ptr(new mem_slot(mmap, 0, hole_gpa, NULL)));
+ }
+ uint64_t hole_end = hole_gpa + h.size;
+ uint64_t end = 3U << 30;
+ _slots.push_back(mem_slot_ptr(new mem_slot(mmap, hole_end,
+ end - hole_end,
+ hole_hva + h.size)));
+ vm.set_tss_addr(3UL << 30);
+}
+
+void vcpu::setup_sregs()
+{
+ kvm_sregs sregs = { };
+ kvm_segment dseg = { };
+ dseg.base = 0; dseg.limit = -1U; dseg.type = 3; dseg.present = 1;
+ dseg.dpl = 3; dseg.db = 1; dseg.s = 1; dseg.l = 0; dseg.g = 1;
+ kvm_segment cseg = dseg;
+ cseg.type = 11;
+
+ sregs.cs = cseg; asm ("mov %%cs, %0" : "=rm"(sregs.cs.selector));
+ sregs.ds = dseg; asm ("mov %%ds, %0" : "=rm"(sregs.ds.selector));
+ sregs.es = dseg; asm ("mov %%es, %0" : "=rm"(sregs.es.selector));
+ sregs.fs = dseg; asm ("mov %%fs, %0" : "=rm"(sregs.fs.selector));
+ sregs.gs = dseg; asm ("mov %%gs, %0" : "=rm"(sregs.gs.selector));
+ sregs.ss = dseg; asm ("mov %%ss, %0" : "=rm"(sregs.ss.selector));
+
+ uint32_t gsbase;
+ asm ("mov %%gs:0, %0" : "=r"(gsbase));
+ sregs.gs.base = gsbase;
+
+ sregs.tr.base = reinterpret_cast<ulong>(&*_stack.begin());
+ sregs.tr.type = 11;
+ sregs.tr.s = 0;
+ sregs.tr.present = 1;
+
+ sregs.cr0 = 0x11; /* PE, ET, !PG */
+ sregs.cr4 = 0;
+ sregs.efer = 0;
+ sregs.apic_base = 0xfee00000;
+ _vcpu.set_sregs(sregs);
+}
+
+void vcpu::thunk(vcpu* zis)
+{
+ zis->_guest_func();
+ asm volatile("outb %%al, %%dx" : : "a"(0), "d"(0));
+}
+
+void vcpu::setup_regs()
+{
+ kvm_regs regs = {};
+ regs.rflags = 0x3202;
+ regs.rsp = reinterpret_cast<ulong>(&*_stack.end());
+ regs.rsp &= ~15UL;
+ ulong* sp = reinterpret_cast<ulong *>(regs.rsp);
+ *--sp = reinterpret_cast<ulong>((char*)this);
+ *--sp = 0;
+ regs.rsp = reinterpret_cast<ulong>(sp);
+ regs.rip = reinterpret_cast<ulong>(&vcpu::thunk);
+ printf("rip %llx\n", regs.rip);
+ _vcpu.set_regs(regs);
+}
+
+vcpu::vcpu(kvm::vcpu& vcpu, std::tr1::function<void ()> guest_func,
+ unsigned long stack_size)
+ : _vcpu(vcpu), _guest_func(guest_func), _stack(stack_size)
+{
+ setup_sregs();
+ setup_regs();
+}
+
+}
diff --git a/kvm-unittest/api/identity.hh b/kvm-unittest/api/identity.hh
new file mode 100644
index 0000000..4491043
--- /dev/null
+++ b/kvm-unittest/api/identity.hh
@@ -0,0 +1,43 @@
+#ifndef API_IDENTITY_HH
+#define API_IDENTITY_HH
+
+#include "kvmxx.hh"
+#include "memmap.hh"
+#include <tr1/functional>
+#include <tr1/memory>
+#include <vector>
+
+namespace identity {
+
+struct hole {
+ hole();
+ hole(void* address, size_t size);
+ void* address;
+ size_t size;
+};
+
+class vm {
+public:
+ vm(kvm::vm& vm, mem_map& mmap, hole address_space_hole = hole());
+private:
+ typedef std::tr1::shared_ptr<mem_slot> mem_slot_ptr;
+ std::vector<mem_slot_ptr> _slots;
+};
+
+class vcpu {
+public:
+ vcpu(kvm::vcpu& vcpu, std::tr1::function<void ()> guest_func,
+ unsigned long stack_size = 256 * 1024);
+private:
+ static void thunk(vcpu* vcpu);
+ void setup_regs();
+ void setup_sregs();
+private:
+ kvm::vcpu& _vcpu;
+ std::tr1::function<void ()> _guest_func;
+ std::vector<char> _stack;
+};
+
+}
+
+#endif
diff --git a/kvm-unittest/api/kvmxx.cc b/kvm-unittest/api/kvmxx.cc
new file mode 100644
index 0000000..7ebebb5
--- /dev/null
+++ b/kvm-unittest/api/kvmxx.cc
@@ -0,0 +1,194 @@
+#include "kvmxx.hh"
+#include "exception.hh"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <memory>
+#include <algorithm>
+
+namespace kvm {
+
+static long check_error(long r)
+{
+ if (r == -1) {
+ throw errno_exception(errno);
+ }
+ return r;
+}
+
+fd::fd(int fd)
+ : _fd(fd)
+{
+}
+
+fd::fd(const fd& other)
+ : _fd(::dup(other._fd))
+{
+ check_error(_fd);
+}
+
+fd::fd(std::string device_node, int flags)
+ : _fd(::open(device_node.c_str(), flags))
+{
+ check_error(_fd);
+}
+
+long fd::ioctl(unsigned nr, long arg)
+{
+ return check_error(::ioctl(_fd, nr, arg));
+}
+
+vcpu::vcpu(vm& vm, int id)
+ : _vm(vm), _fd(vm._fd.ioctl(KVM_CREATE_VCPU, id)), _shared(NULL)
+ , _mmap_size(_vm._system._fd.ioctl(KVM_GET_VCPU_MMAP_SIZE, 0))
+
+{
+ kvm_run *shared = static_cast<kvm_run*>(::mmap(NULL, _mmap_size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ _fd.get(), 0));
+ if (shared == MAP_FAILED) {
+ throw errno_exception(errno);
+ }
+ _shared = shared;
+}
+
+vcpu::~vcpu()
+{
+ munmap(_shared, _mmap_size);
+}
+
+void vcpu::run()
+{
+ _fd.ioctl(KVM_RUN, 0);
+}
+
+kvm_regs vcpu::regs()
+{
+ kvm_regs regs;
+ _fd.ioctlp(KVM_GET_REGS, ®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 <address@hidden>
+ */
+
+/* unsigned long long mftb(void); */
+.global mftb
+mftb:
+ mftbu r5
+ mftbl r4
+ mftbu r3
+ cmpw r3, r5
+ bne mftb
+ blr
diff --git a/kvm-unittest/lib/powerpc/44x/tlbwe.S
b/kvm-unittest/lib/powerpc/44x/tlbwe.S
new file mode 100644
index 0000000..3790374
--- /dev/null
+++ b/kvm-unittest/lib/powerpc/44x/tlbwe.S
@@ -0,0 +1,29 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <address@hidden>
+ */
+
+#define SPRN_MMUCR 0x3b2
+
+/* tlbwe(uint index, uint8_t tid, uint word0, uint word1, uint word2) */
+.global tlbwe
+tlbwe:
+ mtspr SPRN_MMUCR, r4
+ tlbwe r5, r3, 0
+ tlbwe r6, r3, 1
+ tlbwe r7, r3, 2
+ blr
diff --git a/kvm-unittest/powerpc/44x/tlbsx.S b/kvm-unittest/powerpc/44x/tlbsx.S
new file mode 100644
index 0000000..b15874b
--- /dev/null
+++ b/kvm-unittest/powerpc/44x/tlbsx.S
@@ -0,0 +1,33 @@
+#define SPRN_MMUCR 0x3b2
+
+#define TLBWORD0 0x10000210
+#define TLBWORD1 0x10000000
+#define TLBWORD2 0x00000003
+
+.global _start
+_start:
+ li r4, 0
+ mtspr SPRN_MMUCR, r4
+
+ li r3, 23
+
+ lis r4, address@hidden
+ ori r4, r4, address@hidden
+ tlbwe r4, r3, 0
+
+ lis r4, address@hidden
+ ori r4, r4, address@hidden
+ tlbwe r4, r3, 1
+
+ lis r4, address@hidden
+ ori r4, r4, address@hidden
+ tlbwe r4, r3, 2
+
+ lis r4, 0x1000
+ tlbsx r5, r4, r0
+ cmpwi r5, 23
+ beq good
+ trap
+
+good:
+ b .
diff --git a/kvm-unittest/powerpc/44x/tlbwe.S b/kvm-unittest/powerpc/44x/tlbwe.S
new file mode 100644
index 0000000..ec6ef5c
--- /dev/null
+++ b/kvm-unittest/powerpc/44x/tlbwe.S
@@ -0,0 +1,27 @@
+#define SPRN_MMUCR 0x3b2
+
+/* Create a mapping at 4MB */
+#define TLBWORD0 0x00400210
+#define TLBWORD1 0x00400000
+#define TLBWORD2 0x00000003
+
+.global _start
+_start:
+ li r4, 0
+ mtspr SPRN_MMUCR, r4
+
+ li r3, 23
+
+ lis r4, address@hidden
+ ori r4, r4, address@hidden
+ tlbwe r4, r3, 0
+
+ lis r4, address@hidden
+ ori r4, r4, address@hidden
+ tlbwe r4, r3, 1
+
+ lis r4, address@hidden
+ ori r4, r4, address@hidden
+ tlbwe r4, r3, 2
+
+ b .
diff --git a/kvm-unittest/powerpc/44x/tlbwe_16KB.S
b/kvm-unittest/powerpc/44x/tlbwe_16KB.S
new file mode 100644
index 0000000..1bd10bf
--- /dev/null
+++ b/kvm-unittest/powerpc/44x/tlbwe_16KB.S
@@ -0,0 +1,35 @@
+#define SPRN_MMUCR 0x3b2
+
+/* 16KB mapping at 4MB */
+#define TLBWORD0 0x00400220
+#define TLBWORD1 0x00400000
+#define TLBWORD2 0x00000003
+
+.global _start
+_start:
+ li r4, 0
+ mtspr SPRN_MMUCR, r4
+
+ li r3, 5
+
+ lis r4, address@hidden
+ ori r4, r4, address@hidden
+ tlbwe r4, r3, 0
+
+ lis r4, address@hidden
+ ori r4, r4, address@hidden
+ tlbwe r4, r3, 1
+
+ lis r4, address@hidden
+ ori r4, r4, address@hidden
+ tlbwe r4, r3, 2
+
+ /* load from 4MB */
+ lis r3, 0x0040
+ lwz r4, 0(r3)
+
+ /* load from 4MB+8KB */
+ ori r3, r3, 0x2000
+ lwz r4, 0(r3)
+
+ b .
diff --git a/kvm-unittest/powerpc/44x/tlbwe_hole.S
b/kvm-unittest/powerpc/44x/tlbwe_hole.S
new file mode 100644
index 0000000..5efd303
--- /dev/null
+++ b/kvm-unittest/powerpc/44x/tlbwe_hole.S
@@ -0,0 +1,27 @@
+#define SPRN_MMUCR 0x3b2
+
+/* Try to map real address 1GB. */
+#define TLBWORD0 0x40000210
+#define TLBWORD1 0x40000000
+#define TLBWORD2 0x00000003
+
+.global _start
+_start:
+ li r4, 0
+ mtspr SPRN_MMUCR, r4
+
+ li r3, 23
+
+ lis r4, address@hidden
+ ori r4, r4, address@hidden
+ tlbwe r4, r3, 0
+
+ lis r4, address@hidden
+ ori r4, r4, address@hidden
+ tlbwe r4, r3, 1
+
+ lis r4, address@hidden
+ ori r4, r4, address@hidden
+ tlbwe r4, r3, 2
+
+ b .
diff --git a/kvm-unittest/powerpc/cstart.S b/kvm-unittest/powerpc/cstart.S
new file mode 100644
index 0000000..70a0e9f
--- /dev/null
+++ b/kvm-unittest/powerpc/cstart.S
@@ -0,0 +1,38 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <address@hidden>
+ */
+
+#define OUTPUT_VADDR 0xf0000000
+#define OUTPUT_PADDR 0xf0000000
+
+.globl _start
+_start:
+ /* In the future we might need to assign a stack and zero BSS here. */
+
+ /* Map the debug page 1:1. */
+ lis r3, address@hidden
+ ori r3, r3, address@hidden
+ lis r4, address@hidden
+ ori r4, r4, address@hidden
+ bl map
+
+ /* Call main() and pass return code to exit(). */
+ bl main
+ bl exit
+
+ b .
diff --git a/kvm-unittest/powerpc/io.S b/kvm-unittest/powerpc/io.S
new file mode 100644
index 0000000..97567cb
--- /dev/null
+++ b/kvm-unittest/powerpc/io.S
@@ -0,0 +1,32 @@
+#define SPRN_MMUCR 0x3b2
+
+#define TLBWORD0 0xf0000210
+#define TLBWORD1 0xf0000000
+#define TLBWORD2 0x00000003
+
+.global _start
+_start:
+ li r4, 0
+ mtspr SPRN_MMUCR, r4
+
+ li r3, 2
+
+ lis r4, address@hidden
+ ori r4, r4, address@hidden
+ tlbwe r4, r3, 0
+
+ lis r4, address@hidden
+ ori r4, r4, address@hidden
+ tlbwe r4, r3, 1
+
+ lis r4, address@hidden
+ ori r4, r4, address@hidden
+ tlbwe r4, r3, 2
+
+ lis r3, 0xf000
+ lis r4, 0x1234
+ ori r4, r4, 0x5678
+ stb r4, 0(r3)
+ lbz r5, 0(r3)
+
+ b .
diff --git a/kvm-unittest/powerpc/spin.S b/kvm-unittest/powerpc/spin.S
new file mode 100644
index 0000000..4406641
--- /dev/null
+++ b/kvm-unittest/powerpc/spin.S
@@ -0,0 +1,4 @@
+
+.global _start
+_start:
+ b .
diff --git a/kvm-unittest/powerpc/sprg.S b/kvm-unittest/powerpc/sprg.S
new file mode 100644
index 0000000..d0414a4
--- /dev/null
+++ b/kvm-unittest/powerpc/sprg.S
@@ -0,0 +1,7 @@
+
+.global _start
+_start:
+ li r3, 42
+ mtsprg 0, r3
+ mfsprg r4, 0
+ b .
diff --git a/kvm-unittest/run_tests.sh b/kvm-unittest/run_tests.sh
new file mode 100755
index 0000000..55ecac5
--- /dev/null
+++ b/kvm-unittest/run_tests.sh
@@ -0,0 +1,123 @@
+#!/bin/bash
+
+testroot=x86
+config=$testroot/unittests.cfg
+qemu=${qemu:-qemu-system-x86_64}
+verbose=0
+
+function run()
+{
+ local testname="$1"
+ local groups="$2"
+ local smp="$3"
+ local kernel="$4"
+ local opts="$5"
+ local arch="$6"
+
+ if [ -z "$testname" ]; then
+ return
+ fi
+
+ if [ -n "$only_group" ] && ! grep -q "$only_group" <<<$groups; then
+ return
+ fi
+
+ if [ -n "$arch" ] && [ "$arch" != "$ARCH" ]; then
+ echo "skip $1 ($arch only)"
+ return
+ fi
+
+ cmdline="./x86-run $kernel -smp $smp -display none $opts"
+ if [ $verbose != 0 ]; then
+ echo $cmdline
+ fi
+
+ # extra_params in the config file may contain backticks that need to be
+ # expanded, so use eval to start qemu
+ eval $cmdline >> test.log
+
+ if [ $? -le 1 ]; then
+ echo -e "\e[32mPASS\e[0m $1"
+ else
+ echo -e "\e[31mFAIL\e[0m $1"
+ fi
+}
+
+function run_all()
+{
+ local config="$1"
+ local testname
+ local smp
+ local kernel
+ local opts
+ local groups
+ local arch
+
+ exec {config_fd}<$config
+
+ while read -u $config_fd line; do
+ if [[ "$line" =~ ^\[(.*)\]$ ]]; then
+ run "$testname" "$groups" "$smp" "$kernel" "$opts" "$arch"
+ testname=${BASH_REMATCH[1]}
+ smp=1
+ kernel=""
+ opts=""
+ groups=""
+ arch=""
+ elif [[ $line =~ ^file\ *=\ *(.*)$ ]]; then
+ kernel=$testroot/${BASH_REMATCH[1]}
+ elif [[ $line =~ ^smp\ *=\ *(.*)$ ]]; then
+ smp=${BASH_REMATCH[1]}
+ elif [[ $line =~ ^extra_params\ *=\ *(.*)$ ]]; then
+ opts=${BASH_REMATCH[1]}
+ elif [[ $line =~ ^groups\ *=\ *(.*)$ ]]; then
+ groups=${BASH_REMATCH[1]}
+ elif [[ $line =~ ^arch\ *=\ *(.*)$ ]]; then
+ arch=${BASH_REMATCH[1]}
+ fi
+ done
+
+ run "$testname" "$groups" "$smp" "$kernel" "$opts" "$arch"
+
+ exec {config_fd}<&-
+}
+
+function usage()
+{
+cat <<EOF
+
+Usage: $0 [-g group] [-h] [-v]
+
+ -g: Only execute tests in the given group
+ -h: Output this help text
+ -v: Enables verbose mode
+
+Set the environment variable QEMU=/path/to/qemu-system-x86_64 to allow the
+internally used x86-run to pick up the right qemu binary.
+
+EOF
+}
+
+# As it happens, config.mak is valid shell script code, too :-)
+source config.mak
+
+echo > test.log
+while getopts "g:hv" opt; do
+ case $opt in
+ g)
+ only_group=$OPTARG
+ ;;
+ h)
+ usage
+ exit
+ ;;
+ v)
+ verbose=1
+ ;;
+ *)
+ exit
+ ;;
+ esac
+done
+
+run_all $config
diff --git a/kvm-unittest/testdev.txt b/kvm-unittest/testdev.txt
new file mode 100644
index 0000000..ac436ef
--- /dev/null
+++ b/kvm-unittest/testdev.txt
@@ -0,0 +1,14 @@
+This file describes the virtual device of qemu for supporting this test suite.
+
+Services supplied by the testdev device:
+
+serial output: write only, on io port 0xf1
+exit process: write only, on io port 0xf4, value used as exit code
+ram size: read-only, on io port 0xd1, 4 bytes' size
+irq line setting: write only, on io ports 0x2000 - 0x2018, value to set/clear
+simple io: read/write, on io port 0xe0, 1/2/4 bytes
+
+Test device used a char device for actual output
+
+
+
diff --git a/kvm-unittest/x86-run b/kvm-unittest/x86-run
new file mode 100755
index 0000000..646c577
--- /dev/null
+++ b/kvm-unittest/x86-run
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+qemukvm="${QEMU:-qemu-kvm}"
+qemusystem="${QEMU:-qemu-system-x86_64}"
+if
+ ${qemukvm} -device '?' 2>&1 | grep -F -e \"testdev\" -e \"pc-testdev\"
> /dev/null;
+then
+ qemu="${qemukvm}"
+else
+ if
+ ${qemusystem} -device '?' 2>&1 | grep -F -e \"testdev\" -e
\"pc-testdev\" > /dev/null;
+ then
+ qemu="${qemusystem}"
+ else
+ echo QEMU binary ${QEMU} has no support for test device.
Exiting.
+ exit 2
+ fi
+fi
+
+if
+ ${qemu} -device '?' 2>&1 | grep -F "pci-testdev" > /dev/null;
+then
+ pci_testdev="-device pci-testdev"
+else
+ pci_testdev=""
+fi
+
+if
+ ${qemu} -device '?' 2>&1 | grep -F "pc-testdev" > /dev/null;
+then
+ pc_testdev="-device pc-testdev -device
isa-debug-exit,iobase=0xf4,iosize=0x4"
+else
+ pc_testdev="-device testdev,chardev=testlog -chardev
file,id=testlog,path=msr.out"
+fi
+
+command="${qemu} -enable-kvm $pc_testdev -display none -serial stdio
$pci_testdev -kernel"
+echo ${command} "$@"
+${command} "$@"
+ret=$?
+echo Return value from qemu: $ret
+exit $ret
diff --git a/kvm-unittest/x86/README b/kvm-unittest/x86/README
new file mode 100644
index 0000000..d644abd
--- /dev/null
+++ b/kvm-unittest/x86/README
@@ -0,0 +1,16 @@
+Tests in this directory and what they do:
+
+access: lots of page table related access (pte/pde) (read/write)
+apic: enable x2apic, self ipi, ioapic intr, ioapic simultaneous
+emulator: move to/from regs, cmps, push, pop, to/from cr8, smsw and lmsw
+hypercall: intel and amd hypercall insn
+msr: write to msr (only KERNEL_GS_BASE for now)
+port80: lots of out to port 80
+realmode: goes back to realmode, shld, push/pop, mov immediate, cmp immediate,
add immediate,
+ io, eflags instructions (clc, cli, etc.), jcc short, jcc near, call,
long jmp, xchg
+sieve: heavy memory access with no paging and with paging static and with
paging vmalloc'ed
+smptest: run smp_id() on every cpu and compares return value to number
+tsc: write to tsc(0) and write to tsc(100000000000) and read it back
+vmexit: long loops for each: cpuid, vmcall, mov_from_cr8, mov_to_cr8,
inl_pmtimer, ipi, ipi+halt
+kvmclock_test: test of wallclock, monotonic cycle and performance of kvmclock
+pcid: basic functionality test of PCID/INVPCID feature
\ No newline at end of file
diff --git a/kvm-unittest/x86/cstart.S b/kvm-unittest/x86/cstart.S
new file mode 100644
index 0000000..bc8d563
--- /dev/null
+++ b/kvm-unittest/x86/cstart.S
@@ -0,0 +1,192 @@
+
+#include "apic-defs.h"
+
+.globl boot_idt
+boot_idt = 0
+
+ipi_vector = 0x20
+
+max_cpus = 64
+
+.bss
+
+ . = . + 4096 * max_cpus
+ .align 16
+stacktop:
+
+ . = . + 4096
+ .align 16
+ring0stacktop:
+
+.data
+
+.align 4096
+pt:
+i = 0
+ .rept 1024
+ .long 0x1e7 | (i << 22)
+ i = i + 1
+ .endr
+
+gdt32:
+ .quad 0
+ .quad 0x00cf9b000000ffff // flat 32-bit code segment
+ .quad 0x00cf93000000ffff // flat 32-bit data segment
+
+tss_descr:
+ .rept max_cpus
+ .quad 0x000089000000ffff // 32-bit avail tss
+ .endr
+gdt32_end:
+
+i = 0
+tss:
+ .rept max_cpus
+ .long 0
+ .long ring0stacktop - i * 4096
+ .long 16
+ .quad 0, 0
+ .quad 0, 0, 0, 0, 0, 0, 0, 0
+ .long 0, 0, 0
+ i = i + 1
+ .endr
+tss_end:
+
+idt_descr:
+ .word 16 * 256 - 1
+ .long boot_idt
+
+.section .init
+
+.code32
+
+mb_magic = 0x1BADB002
+mb_flags = 0x0
+
+ # multiboot header
+ .long mb_magic, mb_flags, 0 - (mb_magic + mb_flags)
+mb_cmdline = 16
+
+MSR_GS_BASE = 0xc0000101
+
+.macro setup_percpu_area
+ lea -4096(%esp), %eax
+ mov $0, %edx
+ mov $MSR_GS_BASE, %ecx
+ wrmsr
+.endm
+
+.globl start
+start:
+ mov mb_cmdline(%ebx), %eax
+ mov %eax, __args
+ call __setup_args
+ mov $stacktop, %esp
+ setup_percpu_area
+ call prepare_32
+ jmpl $8, $start32
+
+prepare_32:
+ lgdtl gdt32_descr
+
+ mov %cr4, %eax
+ bts $4, %eax // pse
+ mov %eax, %cr4
+
+ mov $pt, %eax
+ mov %eax, %cr3
+
+ mov %cr0, %eax
+ bts $0, %eax
+ bts $31, %eax
+ mov %eax, %cr0
+ ret
+
+smp_stacktop: .long 0xa0000
+
+ap_start32:
+ mov $0x10, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov %ax, %ss
+ mov $-4096, %esp
+ lock/xaddl %esp, smp_stacktop
+ setup_percpu_area
+ call prepare_32
+ call load_tss
+ call enable_apic
+ call enable_x2apic
+ sti
+ nop
+ lock incw cpu_online_count
+
+1: hlt
+ jmp 1b
+
+start32:
+ call load_tss
+ call mask_pic_interrupts
+ call enable_apic
+ call smp_init
+ call enable_x2apic
+ push $__argv
+ push __argc
+ call main
+ push %eax
+ call exit
+
+load_tss:
+ lidt idt_descr
+ mov $16, %eax
+ mov %ax, %ss
+ mov $(APIC_DEFAULT_PHYS_BASE + APIC_ID), %eax
+ mov (%eax), %eax
+ shr $24, %eax
+ mov %eax, %ebx
+ shl $3, %ebx
+ mov $((tss_end - tss) / max_cpus), %edx
+ imul %edx
+ add $tss, %eax
+ mov %ax, tss_descr+2(%ebx)
+ shr $16, %eax
+ mov %al, tss_descr+4(%ebx)
+ shr $8, %eax
+ mov %al, tss_descr+7(%ebx)
+ lea tss_descr-gdt32(%ebx), %eax
+ ltr %ax
+ ret
+
+smp_init:
+ cld
+ lea sipi_entry, %esi
+ xor %edi, %edi
+ mov $(sipi_end - sipi_entry), %ecx
+ rep/movsb
+ mov $APIC_DEFAULT_PHYS_BASE, %eax
+ movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_INIT |
APIC_INT_ASSERT), APIC_ICR(%eax)
+ movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_INIT),
APIC_ICR(%eax)
+ movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_STARTUP),
APIC_ICR(%eax)
+ call fwcfg_get_nb_cpus
+1: pause
+ cmpw %ax, cpu_online_count
+ jne 1b
+smp_init_done:
+ ret
+
+cpu_online_count: .word 1
+
+.code16
+sipi_entry:
+ mov %cr0, %eax
+ or $1, %eax
+ mov %eax, %cr0
+ lgdtl gdt32_descr - sipi_entry
+ ljmpl $8, $ap_start32
+
+gdt32_descr:
+ .word gdt32_end - gdt32 - 1
+ .long gdt32
+
+sipi_end:
diff --git a/kvm-unittest/x86/cstart64.S b/kvm-unittest/x86/cstart64.S
new file mode 100644
index 0000000..0fe76da
--- /dev/null
+++ b/kvm-unittest/x86/cstart64.S
@@ -0,0 +1,245 @@
+
+#include "apic-defs.h"
+
+.globl boot_idt
+boot_idt = 0
+
+.globl idt_descr
+.globl tss_descr
+.globl gdt64_desc
+
+ipi_vector = 0x20
+
+max_cpus = 64
+
+.bss
+
+ . = . + 4096 * max_cpus
+ .align 16
+stacktop:
+
+ . = . + 4096
+ .align 16
+ring0stacktop:
+
+.data
+
+.align 4096
+.globl ptl2
+ptl2:
+i = 0
+ .rept 512 * 4
+ .quad 0x1e7 | (i << 21)
+ i = i + 1
+ .endr
+
+.align 4096
+ptl3:
+ .quad ptl2 + 7 + 0 * 4096
+ .quad ptl2 + 7 + 1 * 4096
+ .quad ptl2 + 7 + 2 * 4096
+ .quad ptl2 + 7 + 3 * 4096
+
+.align 4096
+ptl4:
+ .quad ptl3 + 7
+
+.align 4096
+
+gdt64_desc:
+ .word gdt64_end - gdt64 - 1
+ .quad gdt64
+
+gdt64:
+ .quad 0
+ .quad 0x00af9b000000ffff // 64-bit code segment
+ .quad 0x00cf93000000ffff // 64-bit data segment
+ .quad 0x00affb000000ffff // 64-bit code segment (user)
+ .quad 0x00cff3000000ffff // 64-bit data segment (user)
+ .quad 0x00cf9b000000ffff // 32-bit code segment
+ .quad 0x00cf92000000ffff // 32-bit code segment
+ .quad 0x008F9A000000FFFF // 16-bit code segment
+ .quad 0x008F92000000FFFF // 16-bit data segment
+
+tss_descr:
+ .rept max_cpus
+ .quad 0x000089000000ffff // 64-bit avail tss
+ .quad 0 // tss high addr
+ .endr
+gdt64_end:
+
+i = 0
+tss:
+ .rept max_cpus
+ .long 0
+ .quad ring0stacktop - i * 4096
+ .quad 0, 0
+ .quad 0, 0, 0, 0, 0, 0, 0, 0
+ .long 0, 0, 0
+i = i + 1
+ .endr
+tss_end:
+
+mb_boot_info: .quad 0
+
+.section .init
+
+.code32
+
+mb_magic = 0x1BADB002
+mb_flags = 0x0
+
+ # multiboot header
+ .long mb_magic, mb_flags, 0 - (mb_magic + mb_flags)
+mb_cmdline = 16
+
+MSR_GS_BASE = 0xc0000101
+
+.macro setup_percpu_area
+ lea -4096(%esp), %eax
+ mov $0, %edx
+ mov $MSR_GS_BASE, %ecx
+ wrmsr
+.endm
+
+.globl start
+start:
+ mov %ebx, mb_boot_info
+ mov $stacktop, %esp
+ setup_percpu_area
+ call prepare_64
+ jmpl $8, $start64
+
+prepare_64:
+ lgdt gdt64_desc
+
+ mov %cr4, %eax
+ bts $5, %eax // pae
+ mov %eax, %cr4
+
+ mov $ptl4, %eax
+ mov %eax, %cr3
+
+efer = 0xc0000080
+ mov $efer, %ecx
+ rdmsr
+ bts $8, %eax
+ wrmsr
+
+ mov %cr0, %eax
+ bts $0, %eax
+ bts $31, %eax
+ mov %eax, %cr0
+ ret
+
+smp_stacktop: .long 0xa0000
+
+.align 16
+
+gdt32:
+ .quad 0
+ .quad 0x00cf9b000000ffff // flat 32-bit code segment
+ .quad 0x00cf93000000ffff // flat 32-bit data segment
+gdt32_end:
+
+.code16
+sipi_entry:
+ mov %cr0, %eax
+ or $1, %eax
+ mov %eax, %cr0
+ lgdtl gdt32_descr - sipi_entry
+ ljmpl $8, $ap_start32
+
+gdt32_descr:
+ .word gdt32_end - gdt32 - 1
+ .long gdt32
+
+sipi_end:
+
+.code32
+ap_start32:
+ mov $0x10, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov %ax, %ss
+ mov $-4096, %esp
+ lock/xaddl %esp, smp_stacktop
+ setup_percpu_area
+ call prepare_64
+ ljmpl $8, $ap_start64
+
+.code64
+ap_start64:
+ call load_tss
+ call enable_apic
+ call enable_x2apic
+ sti
+ nop
+ lock incw cpu_online_count
+
+1: hlt
+ jmp 1b
+
+start64:
+ call load_tss
+ call mask_pic_interrupts
+ call enable_apic
+ call smp_init
+ call enable_x2apic
+ mov mb_boot_info(%rip), %rax
+ mov mb_cmdline(%rax), %rax
+ mov %rax, __args(%rip)
+ call __setup_args
+ mov __argc(%rip), %edi
+ lea __argv(%rip), %rsi
+ call main
+ mov %eax, %edi
+ call exit
+
+idt_descr:
+ .word 16 * 256 - 1
+ .quad boot_idt
+
+load_tss:
+ lidtq idt_descr
+ mov $0, %eax
+ mov %ax, %ss
+ mov $(APIC_DEFAULT_PHYS_BASE + APIC_ID), %eax
+ mov (%rax), %eax
+ shr $24, %eax
+ mov %eax, %ebx
+ shl $4, %ebx
+ mov $((tss_end - tss) / max_cpus), %edx
+ imul %edx
+ add $tss, %rax
+ mov %ax, tss_descr+2(%rbx)
+ shr $16, %rax
+ mov %al, tss_descr+4(%rbx)
+ shr $8, %rax
+ mov %al, tss_descr+7(%rbx)
+ shr $8, %rax
+ mov %eax, tss_descr+8(%rbx)
+ lea tss_descr-gdt64(%rbx), %rax
+ ltr %ax
+ ret
+
+smp_init:
+ cld
+ lea sipi_entry, %rsi
+ xor %rdi, %rdi
+ mov $(sipi_end - sipi_entry), %rcx
+ rep/movsb
+ mov $APIC_DEFAULT_PHYS_BASE, %eax
+ movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_INIT |
APIC_INT_ASSERT), APIC_ICR(%rax)
+ movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_INIT),
APIC_ICR(%rax)
+ movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_STARTUP),
APIC_ICR(%rax)
+ call fwcfg_get_nb_cpus
+1: pause
+ cmpw %ax, cpu_online_count
+ jne 1b
+smp_init_done:
+ ret
+
+cpu_online_count: .word 1
diff --git a/kvm-unittest/x86/realmode.lds b/kvm-unittest/x86/realmode.lds
new file mode 100644
index 0000000..0ed3063
--- /dev/null
+++ b/kvm-unittest/x86/realmode.lds
@@ -0,0 +1,12 @@
+SECTIONS
+{
+ . = 16K;
+ stext = .;
+ .text : { *(.init) *(.text) }
+ . = ALIGN(4K);
+ .data : { *(.data) *(.rodata*) }
+ . = ALIGN(16);
+ .bss : { *(.bss) }
+ edata = .;
+}
+ENTRY(start)
diff --git a/kvm-unittest/x86/run-kvm-unit-tests
b/kvm-unittest/x86/run-kvm-unit-tests
new file mode 100644
index 0000000..fed925a
--- /dev/null
+++ b/kvm-unittest/x86/run-kvm-unit-tests
@@ -0,0 +1,6 @@
+#!/usr/bin/python
+
+import sys, os, os.path
+
+prog = sys.argv[0]
+dir = os.path.dirname(prog)
diff --git a/kvm-unittest/x86/unittests.cfg b/kvm-unittest/x86/unittests.cfg
new file mode 100644
index 0000000..85c36aa
--- /dev/null
+++ b/kvm-unittest/x86/unittests.cfg
@@ -0,0 +1,157 @@
+# Define your new unittest following the convention:
+# [unittest_name]
+# file = foo.flat # Name of the flat file to be used
+# smp = 2 # Number of processors the VM will use during this test
+# extra_params = -cpu qemu64,+x2apic # Additional parameters used
+# arch = i386/x86_64 # Only if the test case works only on one of them
+# groups = group1 group2 # Used to identify test cases with run_tests -g ...
+
+[apic]
+file = apic.flat
+smp = 2
+extra_params = -cpu qemu64,+x2apic
+arch = x86_64
+
+[smptest]
+file = smptest.flat
+smp = 2
+
+[smptest3]
+file = smptest.flat
+smp = 3
+
+[vmexit_cpuid]
+file = vmexit.flat
+extra_params = -append 'cpuid'
+groups = vmexit
+
+[vmexit_vmcall]
+file = vmexit.flat
+extra_params = -append 'vmcall'
+groups = vmexit
+
+[vmexit_mov_from_cr8]
+file = vmexit.flat
+extra_params = -append 'mov_from_cr8'
+groups = vmexit
+
+[vmexit_mov_to_cr8]
+file = vmexit.flat
+extra_params = -append 'mov_to_cr8'
+groups = vmexit
+
+[vmexit_inl_pmtimer]
+file = vmexit.flat
+extra_params = -append 'inl_from_pmtimer'
+groups = vmexit
+
+[vmexit_ipi]
+file = vmexit.flat
+smp = 2
+extra_params = -append 'ipi'
+groups = vmexit
+
+[vmexit_ipi_halt]
+file = vmexit.flat
+smp = 2
+extra_params = -append 'ipi_halt'
+groups = vmexit
+
+[vmexit_ple_round_robin]
+file = vmexit.flat
+extra_params = -append 'ple_round_robin'
+groups = vmexit
+
+[access]
+file = access.flat
+arch = x86_64
+
+#[asyncpf]
+#file = asyncpf.flat
+
+[emulator]
+file = emulator.flat
+arch = x86_64
+
+[eventinj]
+file = eventinj.flat
+
+[hypercall]
+file = hypercall.flat
+
+[idt_test]
+file = idt_test.flat
+arch = x86_64
+
+#[init]
+#file = init.flat
+
+[msr]
+file = msr.flat
+
+[pmu]
+file = pmu.flat
+
+[port80]
+file = port80.flat
+
+[realmode]
+file = realmode.flat
+
+[s3]
+file = s3.flat
+
+[sieve]
+file = sieve.flat
+
+[tsc]
+file = tsc.flat
+
+[tsc_adjust]
+file = tsc_adjust.flat
+
+[xsave]
+file = xsave.flat
+arch = x86_64
+
+[rmap_chain]
+file = rmap_chain.flat
+arch = x86_64
+
+[svm]
+file = svm.flat
+smp = 2
+extra_params = -cpu qemu64,+svm
+arch = x86_64
+
+[svm-disabled]
+file = svm.flat
+smp = 2
+extra_params = -cpu qemu64,-svm
+arch = x86_64
+
+[taskswitch]
+file = taskswitch.flat
+arch = i386
+groups = tasks
+
+[taskswitch2]
+file = taskswitch2.flat
+arch = i386
+groups = tasks
+
+[kvmclock_test]
+file = kvmclock_test.flat
+smp = 2
+extra_params = --append "10000000 `date +%s`"
+
+[pcid]
+file = pcid.flat
+extra_params = -cpu qemu64,+pcid
+arch = x86_64
+
+[vmx]
+file = vmx.flat
+extra_params = -cpu host,+vmx
+arch = x86_64
+
- [Qemu-devel] [PATCH] import kvm-unittest in QEMU source tree,
Michael S. Tsirkin <=