]> xenbits.xensource.com Git - people/royger/xen-test-framework.git/commitdiff
Software interrupt emulation testing
authorAndrew Cooper <andrew.cooper3@citrix.com>
Thu, 6 Nov 2014 14:16:42 +0000 (14:16 +0000)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Thu, 14 Jan 2016 16:48:07 +0000 (16:48 +0000)
Test documentation is at the head of main.c

Additionally

* Don't warn on unused parameters - there are legitimate reasons for a
  parameter to be unused.
* Make xtf/extable.h safe for inclusion in assembly files.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
build/common.mk
docs/all-tests.dox
include/xtf/types.h
tests/swint-emulation/Makefile [new file with mode: 0644]
tests/swint-emulation/lowlevel.S [new file with mode: 0644]
tests/swint-emulation/lowlevel.h [new file with mode: 0644]
tests/swint-emulation/main.c [new file with mode: 0644]

index fd2cd869f156ab990e310ec506f2015b79d3682b..4d878dd3d2579df9e588858d7372867fdbdeac6d 100644 (file)
@@ -18,6 +18,7 @@ COMMON_CFLAGS := $(COMMON_FLAGS) -Wall -Wextra -Werror -std=gnu99 -Wstrict-proto
 COMMON_CFLAGS += -fno-common -fno-asynchronous-unwind-tables -fno-strict-aliasing
 COMMON_CFLAGS += -fno-stack-protector -ffreestanding
 COMMON_CFLAGS += -mno-red-zone -mno-sse
+COMMON_CFLAGS += -Wno-unused-parameter
 
 COMMON_AFLAGS-x86_32 := -m32
 COMMON_AFLAGS-x86_64 := -m64
index 13e527fc5cff92252a44a09d95c3a1e294572789..2f8c853012417427b3f16a1c88ae1893d3a3d9b9 100644 (file)
@@ -12,4 +12,9 @@ than useful test.
 
 @subpage test-selftest - A set of sanity tests of the framework environment
 and functionality.
+
+
+@section index-functional Functional tests
+
+@subpage test-swint-emulation - Software interrupt emulation for HVM guests.
 */
index 4e4b153b8e3ad4ab36ceb4e90da6d024de643f26..e622f6d8f711aa35a702bb9f1ece0643aa887256 100644 (file)
@@ -1,12 +1,16 @@
 #ifndef XTF_TYPES_H
 #define XTF_TYPES_H
 
+#ifndef __ASSEMBLY__
+
 #include <stdint.h>
 #include <stdbool.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <inttypes.h>
 
+#endif /* !__ASSEMBLY__ */
+
 #endif /* XTF_TYPES_H */
 
 /*
diff --git a/tests/swint-emulation/Makefile b/tests/swint-emulation/Makefile
new file mode 100644 (file)
index 0000000..6a14774
--- /dev/null
@@ -0,0 +1,10 @@
+ROOT := $(abspath $(CURDIR)/../..)
+
+include $(ROOT)/build/common.mk
+
+NAME      := swint-emulation
+TEST-ENVS := hvm64 hvm32
+
+obj-perenv += main.o lowlevel.o
+
+include $(ROOT)/build/gen.mk
diff --git a/tests/swint-emulation/lowlevel.S b/tests/swint-emulation/lowlevel.S
new file mode 100644 (file)
index 0000000..564f162
--- /dev/null
@@ -0,0 +1,67 @@
+#include <xtf/extable.h>
+#include <xtf/asm_macros.h>
+#include <xen/arch-x86/xen.h>
+
+        /* Macro to generate a single test function. */
+        /* See lowlevel.h for a description of nomenclature. */
+.macro GEN_SINGLE insn, type
+
+        /* Function label.  e.g. stub_int3_red(). */
+ENTRY(stub_\insn\()_\type)
+
+        /* Avoid function label and fault label possibly aliasing. */
+        nop
+
+        /* Possibly insert a Xen Forced Emulation prefix. */
+        .if \type == force || \type == forcered
+            _ASM_XEN_FEP
+        .endif
+
+        /* Label where a fault should occur.  e.g. label_int3_red_fault. */
+GLOBAL(label_\insn\()_\type\()_fault)
+
+        /*
+         * Possibly insert a redundant prefix.
+         *
+         * Uses 'address size override' which has no effect on any of the
+         * target instructions, but in a form which GAS won't complain about.
+         */
+        .if \type == red || \type == forcered
+            .byte 0x67
+        .endif
+
+        /* Target instruction to test. */
+        .if \insn == int3
+            int3
+        .elseif \insn == int_0x3
+            int $0x3
+        .elseif \insn == icebp
+            .byte 0xf1
+        .elseif \insn == int_0x1
+            int $0x1
+        .endif
+
+        /* Label where a trap should occur.  e.g. label_int3_red_trap. */
+GLOBAL(label_\insn\()_\type\()_trap)
+
+        /* Function return. */
+        ret
+
+        /* Fixup from fault label to trap label. */
+        _ASM_EXTABLE(label_\insn\()_\type\()_fault,
+                     label_\insn\()_\type\()_trap)
+.endm
+
+        /* For a single instruction, generate each test variant. */
+.macro GEN_SEQUENCE insn
+        GEN_SINGLE \insn reg
+        GEN_SINGLE \insn red
+        GEN_SINGLE \insn force
+        GEN_SINGLE \insn forcered
+.endm
+
+        /* Generate test sequences for each instruction. */
+GEN_SEQUENCE int3
+GEN_SEQUENCE int_0x3
+GEN_SEQUENCE icebp
+GEN_SEQUENCE int_0x1
diff --git a/tests/swint-emulation/lowlevel.h b/tests/swint-emulation/lowlevel.h
new file mode 100644 (file)
index 0000000..94ed1e4
--- /dev/null
@@ -0,0 +1,81 @@
+/**
+ * @file tests/swint-emulation/lowlevel.h
+ *
+ * Declarations of stubs and labels in generated in lowlevel.S
+ *
+ * Nomaclature:
+ * - `stub_$X_$Y()`
+ *   - Stub function executing instruction `$X` with prefix `$Y`.
+ * - `label_$X_$Y_{trap, fault}:`
+ *   - Labels for where `$X` is expected to trap or fault.
+ *
+ * Instructions `$X`:
+ * - int3
+ *   - Breakpoint (`0xcc`)
+ * - int_0x3
+ *   - `int $3` (`0xcd 0x03`)
+ * - icebp
+ *   - Ice Breakpoint (`0xf1`)
+ * - int_0x1
+ *   - `int $1` (`0xcd 0x01`)
+ *
+ * Prefixes `$Y`:
+ * - reg
+ *   - Regular - no prefix
+ * - red
+ *   - Redundant - address size override prefix (`0x67`)
+ * - force
+ *   - Forced Emulation prefix - see @ref _ASM_XEN_FEP
+ * - forcered
+ *   - Forced Emulation and redundant prefixes
+ */
+#ifndef __LOWLEVEL_H__
+#define __LOWLEVEL_H__
+
+void stub_int3_reg(void);
+void stub_int3_red(void);
+void stub_int3_force(void);
+void stub_int3_forcered(void);
+extern unsigned long label_int3_reg_trap[], label_int3_reg_fault[];
+extern unsigned long label_int3_red_trap[], label_int3_red_fault[];
+extern unsigned long label_int3_force_trap[], label_int3_force_fault[];
+extern unsigned long label_int3_forcered_trap[], label_int3_forcered_fault[];
+
+void stub_int_0x3_reg(void);
+void stub_int_0x3_red(void);
+void stub_int_0x3_force(void);
+void stub_int_0x3_forcered(void);
+extern unsigned long label_int_0x3_reg_trap[], label_int_0x3_reg_fault[];
+extern unsigned long label_int_0x3_red_trap[], label_int_0x3_red_fault[];
+extern unsigned long label_int_0x3_force_trap[], label_int_0x3_force_fault[];
+extern unsigned long label_int_0x3_forcered_trap[], label_int_0x3_forcered_fault[];
+
+void stub_icebp_reg(void);
+void stub_icebp_red(void);
+void stub_icebp_force(void);
+void stub_icebp_forcered(void);
+extern unsigned long label_icebp_reg_trap[], label_icebp_reg_fault[];
+extern unsigned long label_icebp_red_trap[], label_icebp_red_fault[];
+extern unsigned long label_icebp_force_trap[], label_icebp_force_fault[];
+extern unsigned long label_icebp_forcered_trap[], label_icebp_forcered_fault[];
+
+void stub_int_0x1_reg(void);
+void stub_int_0x1_red(void);
+void stub_int_0x1_force(void);
+void stub_int_0x1_forcered(void);
+extern unsigned long label_int_0x1_reg_trap[], label_int_0x1_reg_fault[];
+extern unsigned long label_int_0x1_red_trap[], label_int_0x1_red_fault[];
+extern unsigned long label_int_0x1_force_trap[], label_int_0x1_force_fault[];
+extern unsigned long label_int_0x1_forcered_trap[], label_int_0x1_forcered_fault[];
+
+#endif /* __LOWLEVEL_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tests/swint-emulation/main.c b/tests/swint-emulation/main.c
new file mode 100644 (file)
index 0000000..1156f54
--- /dev/null
@@ -0,0 +1,344 @@
+/**
+ * @file tests/swint-emulation/main.c
+ * @ref test-swint-emulation - Emulation of software interrupts.
+ *
+ * @page test-swint-emulation Software Interrupt Emulation
+ *
+ * Tests for the exception frame generated from emulated software interrupt
+ * instructions, in situations which will results in traps and faults.
+ *
+ * This test requires a debug Xen, booted with `"hvm_fep"` to allow the test
+ * to directly invoke the x86 instruction emulator.
+ *
+ * Each of the following instructions are tested:
+ * - `int3`   (`0xcc`)
+ * - `int $3` (`0xcd 0x03`)
+ * - `icebp`  (`0xf1`)
+ * - `int $1` (`0xcd 0x01`)
+ *
+ * with and without a redundant prefix (address size override specifically, as
+ * it has no effect on instructs like these).  Each combination is executed
+ * both normally, and via the instruction emulator, to confirm that hardware
+ * and the instruction emulator are behaving identically.
+ *
+ * Injection of software interrupts by Xen is complicated by differing
+ * available of hardware support.  Intel VT-x and AMD SVM (both with and
+ * without NextRIP support) all provide different levels of hardware support,
+ * which affects how much Xen must emulate in software to cause the guest to
+ * observe architectural behaviour.
+ *
+ * Each instruction/prefix combination is executed in the following
+ * conditions:
+ * - cpl0, all permissions ok - expect traps
+ * - cpl0, descriptors not present - expect #%NP faults
+ * - cpl3, all permissions ok - expect traps
+ * - cpl3, descriptors dpl0 - expect #%GP faults (except `icebp`)
+ * - cpl3, descriptors not present - expect #%NP faults
+ *
+ * In all cases, the exception frame is verified to be correct.
+ */
+#include <xtf/lib.h>
+
+#include <arch/x86/processor.h>
+#include <arch/x86/symbolic-const.h>
+
+#include "lowlevel.h"
+
+/** Single stub's worth of information. */
+struct single
+{
+    const char *type;
+    void (*fn)(void);
+    void *trap, *fault;
+};
+
+/** A collection of subs for an instruction. */
+struct sequence
+{
+    const char *name;
+    struct single tests[4];
+};
+
+/** Sequence for `int3`. */
+struct sequence int3 =
+{ "int3",
+  {
+      {"regular", stub_int3_reg,
+       label_int3_reg_trap, label_int3_reg_fault},
+
+      {"redundant", stub_int3_red,
+       label_int3_red_trap, label_int3_red_fault},
+
+      {"forced", stub_int3_force,
+       label_int3_force_trap, label_int3_force_fault},
+
+      {"forced redundant", stub_int3_forcered,
+       label_int3_forcered_trap, label_int3_forcered_fault},
+  },
+};
+
+/** Sequence for `int $3`. */
+struct sequence int_0x3 =
+{ "int $3",
+  {
+      {"regular", stub_int_0x3_reg,
+       label_int_0x3_reg_trap, label_int_0x3_reg_fault},
+
+      {"redundant", stub_int_0x3_red,
+       label_int_0x3_red_trap, label_int_0x3_red_fault},
+
+      {"forced", stub_int_0x3_force,
+       label_int_0x3_force_trap, label_int_0x3_force_fault},
+
+      {"forced redundant", stub_int_0x3_forcered,
+       label_int_0x3_forcered_trap, label_int_0x3_forcered_fault},
+  },
+};
+
+/** Sequence for `icebp`. */
+struct sequence icebp =
+{ "icebp",
+  {
+      {"regular", stub_icebp_reg,
+       label_icebp_reg_trap, label_icebp_reg_fault},
+
+      {"redundant", stub_icebp_red,
+       label_icebp_red_trap, label_icebp_red_fault},
+
+      {"forced", stub_icebp_force,
+       label_icebp_force_trap, label_icebp_force_fault},
+
+      {"forced redundant", stub_icebp_forcered,
+       label_icebp_forcered_trap, label_icebp_forcered_fault},
+  },
+};
+
+/** Sequence for `int $1`. */
+struct sequence int_0x1 =
+{ "int $1",
+  {
+      {"regular", stub_int_0x1_reg,
+       label_int_0x1_reg_trap, label_int_0x1_reg_fault},
+
+      {"redundant", stub_int_0x1_red,
+       label_int_0x1_red_trap, label_int_0x1_red_fault},
+
+      {"forced", stub_int_0x1_force,
+       label_int_0x1_force_trap, label_int_0x1_force_fault},
+
+      {"forced redundant", stub_int_0x1_forcered,
+       label_int_0x1_forcered_trap, label_int_0x1_forcered_fault},
+  },
+};
+
+/** Whether to run the stub in user or supervisor mode. */
+static bool user = false;
+
+struct expectation {
+    const char *prefix;
+    const void *ip;
+    unsigned int ev, ec;
+} /** Expected %%eip, vector and error code from the stub under test. */
+    expectation;
+
+/** Latch details of the stub under test. */
+void expect(const void *prefix, const void *ip,
+            unsigned int ev, unsigned int ec)
+{
+    expectation = (struct expectation){prefix, ip, ev, ec};
+    xtf_exlog_reset();
+}
+
+/** Check the exception long against the expected details. */
+void check(void)
+{
+    unsigned int entries = xtf_exlog_entries();
+
+    if ( entries != 1 )
+    {
+        xtf_failure("Fail %s: Expected 1 exception (vec %u at %p), got %u\n",
+                    expectation.prefix, expectation.ev,
+                    expectation.ip, entries);
+        xtf_exlog_dump_log();
+        return;
+    }
+
+    exlog_entry_t *entry = xtf_exlog_entry(0);
+    if ( !entry )
+    {
+        xtf_failure("Fail %s: Unable to retrieve exception log\n",
+                    expectation.prefix);
+        return;
+    }
+
+    if ( (_p(entry->ip) != expectation.ip) ||
+         (entry->ev != expectation.ev) ||
+         (entry->ec != expectation.ec) )
+    {
+        xtf_failure("Fail %s:\n"
+                    "  Expected vec %2u[%04x] at %p\n"
+                    "       got vec %2u[%04x] at %p\n",
+                    expectation.prefix,
+                    expectation.ev, expectation.ec, expectation.ip,
+                    entry->ev, entry->ec, _p(entry->ip));
+        return;
+    }
+}
+
+/** Print expected information in the case of an unexpected exception. */
+bool unhandled_exception(struct cpu_regs *regs)
+{
+    printk("Unhandled Exception at %p\n", _p(regs));
+    check();
+
+    return false;
+}
+
+/** Test a single sequence of related instructions. */
+void test_seq(struct sequence *seq, unsigned int vector, unsigned int error)
+{
+    unsigned int i;
+
+    printk("  Testing %s\n", seq->name);
+
+    for ( i = 0; i < ARRAY_SIZE(seq->tests); ++i )
+    {
+        struct single *s = &seq->tests[i];
+
+        expect(s->type,
+               error == 0 ? s->trap : s->fault,
+               vector, error);
+
+        user ? exec_user(s->fn) : s->fn();
+
+        check();
+
+        /* Avoid 'force' and 'forcered' stubs if FEP isn't available. */
+        if ( i == 1 && !xtf_has_fep )
+            break;
+    }
+}
+
+/** test_seq() wrapper, for caller clarity. */
+static void test_trap(struct sequence *seq, unsigned int vector)
+{
+    test_seq(seq, vector, 0);
+}
+
+/** test_seq() wrapper, for caller clarity. */
+static void test_fault(struct sequence *seq,
+                       unsigned int vector, unsigned int error)
+{
+    test_seq(seq, vector, error);
+}
+
+/** Modify the present flag on the IDT entries under test. */
+static void set_idt_entries_present(bool present)
+{
+    idt[X86_EXC_DB].p = present;
+    idt[X86_EXC_BP].p = present;
+}
+
+/** Modify the descriptor privilege level on the IDT entries under test. */
+static void set_idt_entries_dpl(unsigned int dpl)
+{
+    idt[X86_EXC_DB].dpl = dpl;
+    idt[X86_EXC_BP].dpl = dpl;
+}
+
+/** Tests run in user mode. */
+void cpl3_tests(void)
+{
+    user = true;
+
+    printk("Test cpl3: all perms ok\n");
+    {
+        test_trap(&int3,    X86_EXC_BP);
+        test_trap(&int_0x3, X86_EXC_BP);
+        test_trap(&icebp,   X86_EXC_DB);
+        test_trap(&int_0x1, X86_EXC_DB);
+    }
+
+    printk("Test cpl3: p=0\n");
+    {
+        set_idt_entries_present(false);
+
+        test_fault(&int3,    X86_EXC_NP, EXC_EC_SYM(BP));
+        test_fault(&int_0x3, X86_EXC_NP, EXC_EC_SYM(BP));
+        test_fault(&icebp,   X86_EXC_NP, EXC_EC_SYM(DB, EXT));
+        test_fault(&int_0x1, X86_EXC_NP, EXC_EC_SYM(DB));
+
+        set_idt_entries_present(true);
+    }
+
+    printk("Test cpl3: dpl=0\n");
+    {
+        set_idt_entries_dpl(0);
+
+        test_fault(&int3,    X86_EXC_GP, EXC_EC_SYM(BP));
+        test_fault(&int_0x3, X86_EXC_GP, EXC_EC_SYM(BP));
+         /* icebp count as external, so no dpl check. */
+        test_trap (&icebp,   X86_EXC_DB);
+        test_fault(&int_0x1, X86_EXC_GP, EXC_EC_SYM(DB));
+
+        set_idt_entries_dpl(3);
+    }
+
+    user = false;
+}
+
+/** Tests run in supervisor mode. */
+void cpl0_tests(void)
+{
+    printk("Test cpl0: all perms ok\n");
+    {
+        test_trap(&int3,    X86_EXC_BP);
+        test_trap(&int_0x3, X86_EXC_BP);
+        test_trap(&icebp,   X86_EXC_DB);
+        test_trap(&int_0x1, X86_EXC_DB);
+    }
+
+    printk("Test cpl0: p=0\n");
+    {
+        set_idt_entries_present(false);
+
+        test_fault(&int3,    X86_EXC_NP, EXC_EC_SYM(BP));
+        test_fault(&int_0x3, X86_EXC_NP, EXC_EC_SYM(BP));
+        test_fault(&icebp,   X86_EXC_NP, EXC_EC_SYM(DB, EXT));
+        test_fault(&int_0x1, X86_EXC_NP, EXC_EC_SYM(DB));
+
+        set_idt_entries_present(true);
+    }
+}
+
+void test_main(void)
+{
+    printk("Trap emulation\n");
+
+    if ( !xtf_has_fep )
+        xtf_warning("Warning: FEP support not detected - some tests will be skipped\n");
+
+    /* Setup.  Hook unhandled exceptions for debugging purposes. */
+    xtf_unhandled_exception_hook = unhandled_exception;
+    set_idt_entries_present(true);
+    set_idt_entries_dpl(3);
+
+    xtf_exlog_start();
+
+    cpl0_tests();
+    cpl3_tests();
+
+    xtf_exlog_stop();
+
+    xtf_success();
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */