]> xenbits.xensource.com Git - people/royger/xen-test-framework.git/commitdiff
PV IOPL emulation testing
authorAndrew Cooper <andrew.cooper3@citrix.com>
Sat, 19 Mar 2016 15:36:16 +0000 (15:36 +0000)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Tue, 26 Apr 2016 16:22:23 +0000 (17:22 +0100)
A test to verify correct behaviour of vIOPL shadowing in Xen.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
docs/all-tests.dox
include/xen/physdev.h [new file with mode: 0644]
include/xtf/hypercall.h
tests/pv-iopl/Makefile [new file with mode: 0644]
tests/pv-iopl/main.c [new file with mode: 0644]

index 537e9e6684e7769838b32798f255dc70fdc70494..dae450df4c1076958c11572f96e8ab948e6c620c 100644 (file)
@@ -16,6 +16,8 @@ and functionality.
 
 @section index-functional Functional tests
 
+@subpage test-pv-iopl - IOPL emulation for PV guests.
+
 @subpage test-swint-emulation - Software interrupt emulation for HVM guests.
 Coveres XSA-106 and XSA-156.
 
diff --git a/include/xen/physdev.h b/include/xen/physdev.h
new file mode 100644 (file)
index 0000000..9245535
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Xen public physdev_op hypercall interface
+ */
+
+#ifndef XEN_PUBLIC_PHYSDEV_H
+#define XEN_PUBLIC_PHYSDEV_H
+
+/*
+ * Set the current VCPU's I/O privilege level.
+ * @arg == pointer to physdev_set_iopl structure.
+ */
+#define PHYSDEVOP_set_iopl               6
+#ifndef __ASSEMBLY__
+struct physdev_set_iopl {
+    /* IN */
+    uint32_t iopl;
+};
+#endif
+
+#endif /* XEN_PUBLIC_PHYSDEV_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
index 885586dea01299e692d56e9bd23e9e6349c53eeb..c48541d04e33d8edc1a875843b5fa1ef95aa6eba 100644 (file)
@@ -31,6 +31,7 @@ extern uint8_t hypercall_page[PAGE_SIZE];
 #include <xen/sched.h>
 #include <xen/errno.h>
 #include <xen/event_channel.h>
+#include <xen/physdev.h>
 #include <xen/memory.h>
 #include <xen/version.h>
 #include <xen/hvm/hvm_op.h>
@@ -89,6 +90,11 @@ static inline long hypercall_event_channel_op(unsigned int cmd, void *arg)
     return HYPERCALL2(long, __HYPERVISOR_event_channel_op, cmd, arg);
 }
 
+static inline long hypercall_physdev_op(unsigned int cmd, void *arg)
+{
+    return HYPERCALL2(long, __HYPERVISOR_physdev_op, cmd, arg);
+}
+
 static inline long hypercall_hvm_op(unsigned int cmd, void *arg)
 {
     return HYPERCALL2(long, __HYPERVISOR_hvm_op, cmd, arg);
diff --git a/tests/pv-iopl/Makefile b/tests/pv-iopl/Makefile
new file mode 100644 (file)
index 0000000..2458d0e
--- /dev/null
@@ -0,0 +1,12 @@
+MAKEFLAGS += -r
+ROOT := $(abspath $(CURDIR)/../..)
+
+include $(ROOT)/build/common.mk
+
+NAME      := pv-iopl
+CATEGORY  := functional
+TEST-ENVS := $(PV_ENVIRONMENTS)
+
+obj-perenv += main.o
+
+include $(ROOT)/build/gen.mk
diff --git a/tests/pv-iopl/main.c b/tests/pv-iopl/main.c
new file mode 100644 (file)
index 0000000..d4e0a5b
--- /dev/null
@@ -0,0 +1,216 @@
+/**
+ * @file tests/pv-iopl/main.c
+ * @ref test-pv-iopl - PV IOPL Emulation
+ *
+ * @page test-pv-iopl PV IOPL Emulation
+ *
+ * Tests for the behaviour of Xen's virtual IOPL handling for PV guests.
+ *
+ * Xen cannot actually run PV guests with an IOPL other than 0, or PV guests
+ * would be able to play with the real interrupt flag behind Xen's back.
+ * Therefore, the guests desired IOPL is shadowed, and consulted when a IO
+ * related fault occurs.  Xen either completes the IO on behalf of the guest,
+ * or bounces the @#GP fault back to the guest kernel.
+ *
+ * The instructions:
+ * - `cli`
+ * - `outb %al, $0x80`
+ *
+ * are tested in both user and kernel context, while varying IOPL, and
+ * verifying that a @#GP fault was correctly (or not) delivered.
+ *
+ * Methods of varying IOPL:
+ * 1. PHYSDEVOP_set_iopl
+ *
+ *    The PHYSDEVOP_set_iopl has existed in the Xen ABI for a very long time.
+ *    Kernel context is considered to have a cpl of 1, even for 64bit PV
+ *    guests executing in ring3.  A side effect of this is that a PV guest
+ *    kernel will suffer faults from IO accesses until it sets an IOPL of 1.
+ *    A guest may set the current IOPL, but cannot query the value.
+ *
+ * @sa tests/pv-iopl/main.c
+ */
+#include <xtf/lib.h>
+
+#include <arch/x86/processor.h>
+
+/** Stub CLI instruction with @#GP fixup. */
+static void stub_cli(void)
+{
+    asm volatile ("1: cli; 2:"
+                  _ASM_EXTABLE(1b, 2b));
+}
+
+/** Stub OUTB instruction with @#GP fixup. */
+static void stub_outb(void)
+{
+    asm volatile ("1: outb %b0, $0x80; 2:"
+                  _ASM_EXTABLE(1b, 2b)
+                  :: "a" (0));
+}
+
+static const struct insn
+{
+    const char *name;
+    void (*fn)(void);
+} /** Sequence of instructions to run. */
+    insn_sequence[] =
+{
+    { "cli",  stub_cli,  },
+    { "outb", stub_outb, },
+};
+
+static struct expectation
+{
+    const char *insn;
+    bool user;
+    bool fault;
+} /** Details about the stub under test. */
+expectation;
+
+/** Latch details of the stub under test. */
+static void expect(const char *insn, bool user, bool fault)
+{
+    expectation = (struct expectation){insn, user, fault, };
+    xtf_exlog_reset();
+}
+
+/** Check the exception long against the expected details. */
+static void check(void)
+{
+    unsigned int entries = xtf_exlog_entries();
+    const char *mode = expectation.user ? "user" : "kernel";
+
+    if ( expectation.fault )
+    {
+        if ( entries != 1 )
+        {
+            xtf_failure("Fail (%s %s): Expected 1 exception, got %u\n",
+                        mode, expectation.insn, entries);
+            xtf_exlog_dump_log();
+            return;
+        }
+
+        exlog_entry_t *entry = xtf_exlog_entry(0);
+        if ( !entry )
+        {
+            xtf_failure("Fail (%s %s): Unable to retrieve exception log\n",
+                        mode, expectation.insn);
+            return;
+        }
+
+        if ( (entry->ev != X86_EXC_GP) || (entry->ec != 0) )
+        {
+            xtf_failure("Fail (%s %s): Expected #GP[0], got %2u[%04x]\n",
+                        mode, expectation.insn, entry->ev, entry->ec);
+            return;
+        }
+    }
+    else
+    {
+        if ( entries != 0 )
+        {
+            xtf_failure("Fail (%s %s): Expected no exceptions, got %u\n",
+                        mode, expectation.insn, entries);
+            xtf_exlog_dump_log();
+            return;
+        }
+    }
+}
+
+struct test
+{
+    void (*set_iopl)(unsigned int iopl);
+    bool (*should_fault)(bool user, unsigned int iopl);
+};
+
+/** Test the instruction sequence using a specific iopl interface. */
+static void run_test(const struct test *t)
+{
+    unsigned int i, iopl;
+
+    for ( iopl = 0; iopl <= 3; ++iopl )
+    {
+        /* vIOPL 2 is not interesting to test. */
+        if ( iopl == 2 )
+            continue;
+
+        printk("  vIOPL %u\n", iopl);
+        t->set_iopl(iopl);
+
+        for ( i = 0; i < ARRAY_SIZE(insn_sequence); ++i )
+        {
+            const struct insn *seq = &insn_sequence[i];
+
+            /* Run insn in kernel. */
+            expect(seq->name, 0, t->should_fault(0, iopl));
+            seq->fn();
+            check();
+
+            /* Run insn in userspace. */
+            expect(seq->name, 1, t->should_fault(1, iopl));
+            exec_user(seq->fn);
+            check();
+        }
+    }
+}
+
+static void hypercall_set_iopl(unsigned int iopl)
+{
+    hypercall_physdev_op(PHYSDEVOP_set_iopl, &iopl);
+}
+
+static bool hypercall_should_fault(bool user, unsigned int iopl)
+{
+    /*
+     * Kernel has vCPL 1, userspace has vCPL 3
+     */
+    switch ( iopl )
+    {
+    case 0:
+        /* Both kernel and userspace should fault. */
+        return true;
+
+    case 1:
+    case 2:
+        /* Kernel should succeed, user should fault. */
+        return user;
+
+    case 3:
+        /* Both kernel and userspace should succeed. */
+        return false;
+
+    default:
+        panic("Bad vIOPL %u\n", iopl);
+    }
+}
+
+/** Hypercall based IOPL interface. */
+static const struct test hypercall =
+{
+    .set_iopl     = hypercall_set_iopl,
+    .should_fault = hypercall_should_fault,
+};
+
+void test_main(void)
+{
+    printk("PV IOPL emulation\n");
+
+    xtf_exlog_start();
+
+    printk("Test: PHYSDEVOP_set_iopl\n");
+    run_test(&hypercall);
+
+    xtf_exlog_stop();
+    xtf_success(NULL);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */