[PATCH kvm-unit-tests] x86: add UMIP test

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The UMIP feature can be emulated by KVM, so it's useful to add
a test that it works properly.

Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx>
---
 lib/x86/desc.c      |   2 +-
 lib/x86/desc.h      |   1 +
 lib/x86/processor.h |   9 +++
 x86/Makefile.common |   4 +-
 x86/umip.c          | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 x86/unittests.cfg   |   4 ++
 6 files changed, 220 insertions(+), 3 deletions(-)
 create mode 100644 x86/umip.c

diff --git a/lib/x86/desc.c b/lib/x86/desc.c
index a527291..23a0d5a 100644
--- a/lib/x86/desc.c
+++ b/lib/x86/desc.c
@@ -63,7 +63,7 @@ static const char* exception_mnemonic(int vector)
 	}
 }
 
-static void unhandled_exception(struct ex_regs *regs, bool cpu)
+void unhandled_exception(struct ex_regs *regs, bool cpu)
 {
 	printf("Unhandled %sexception %ld %s at ip %016lx\n",
 	       cpu ? "cpu " : "", regs->vector,
diff --git a/lib/x86/desc.h b/lib/x86/desc.h
index 3bf8fbe..41989c9 100644
--- a/lib/x86/desc.h
+++ b/lib/x86/desc.h
@@ -191,6 +191,7 @@ void set_gdt_entry(int sel, u32 base,  u32 limit, u8 access, u8 gran);
 void set_intr_alt_stack(int e, void *fn);
 void print_current_tss_info(void);
 handler handle_exception(u8 v, handler fn);
+void unhandled_exception(struct ex_regs *regs, bool cpu);
 
 bool test_for_exception(unsigned int ex, void (*trigger_func)(void *data),
 			void *data);
diff --git a/lib/x86/processor.h b/lib/x86/processor.h
index 9ada26b..503289f 100644
--- a/lib/x86/processor.h
+++ b/lib/x86/processor.h
@@ -27,6 +27,7 @@
 #define X86_CR4_PSE    0x00000010
 #define X86_CR4_PAE    0x00000020
 #define X86_CR4_MCE    0x00000040
+#define X86_CR4_UMIP   0x00000800
 #define X86_CR4_VMXE   0x00002000
 #define X86_CR4_PCIDE  0x00020000
 #define X86_CR4_SMAP   0x00200000
@@ -43,6 +44,7 @@
 #define X86_EFLAGS_DF    0x00000400
 #define X86_EFLAGS_OF    0x00000800
 #define X86_EFLAGS_NT    0x00004000
+#define X86_EFLAGS_IOPL  0x00003000
 #define X86_EFLAGS_AC    0x00040000
 
 #define X86_IA32_EFER          0xc0000080
@@ -158,6 +160,13 @@ static inline void write_rflags(unsigned long f)
     asm volatile ("push %0; popf\n\t" : : "rm"(f));
 }
 
+static inline void set_iopl(int iopl)
+{
+	unsigned long flags = read_rflags() & ~X86_EFLAGS_IOPL;
+	flags |= iopl * (X86_EFLAGS_IOPL / 3);
+	write_rflags(flags);
+}
+
 static inline u64 rdmsr(u32 index)
 {
     u32 a, d;
diff --git a/x86/Makefile.common b/x86/Makefile.common
index e96812b..0b38a65 100644
--- a/x86/Makefile.common
+++ b/x86/Makefile.common
@@ -47,9 +47,9 @@ tests-common = $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \
                $(TEST_DIR)/kvmclock_test.flat  $(TEST_DIR)/eventinj.flat \
                $(TEST_DIR)/s3.flat $(TEST_DIR)/pmu.flat $(TEST_DIR)/setjmp.flat \
                $(TEST_DIR)/tsc_adjust.flat $(TEST_DIR)/asyncpf.flat \
-               $(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat \
+               $(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat $(TEST_DIR)/umip.flat \
                $(TEST_DIR)/hyperv_synic.flat $(TEST_DIR)/hyperv_stimer.flat \
-               $(TEST_DIR)/hyperv_connections.flat \
+               $(TEST_DIR)/hyperv_connections.flat
 
 ifdef API
 tests-api = api/api-sample api/dirty-log api/dirty-log-perf
diff --git a/x86/umip.c b/x86/umip.c
new file mode 100644
index 0000000..b11c42c
--- /dev/null
+++ b/x86/umip.c
@@ -0,0 +1,203 @@
+
+#include "libcflat.h"
+#include "desc.h"
+#include "processor.h"
+
+#define CPUID_7_ECX_UMIP (1 << 2)
+static int cpuid_7_ecx;
+
+
+/* GP handler to skip over faulting instructions */
+
+static unsigned long expected_rip;
+static int skip_count;
+static volatile int gp_count;
+
+void gp_handler(struct ex_regs *regs)
+{
+    if (regs->rip == expected_rip) {
+        gp_count++;
+        regs->rip += skip_count;
+    } else {
+        unhandled_exception(regs, false);
+    }
+}
+
+
+#define GP_ASM(stmt, in, clobber)                  \
+     asm ("mov" W " $1f, %[expected_rip]\n\t"      \
+          "movl $2f-1f, %[skip_count]\n\t"         \
+          "1: " stmt "\n\t"                        \
+          "2: "                                    \
+          : [expected_rip] "=m" (expected_rip),    \
+            [skip_count] "=m" (skip_count)         \
+          : in : clobber)
+
+static void do_smsw(void)
+{
+    gp_count = 0;
+    GP_ASM("smsw %%ax", , "eax");
+}
+
+static void do_sldt(void)
+{
+    gp_count = 0;
+    GP_ASM("sldt %%ax", , "eax");
+}
+
+static void do_str(void)
+{
+    gp_count = 0;
+    GP_ASM("str %%ax", , "eax");
+}
+
+static void do_sgdt(void)
+{
+    struct descriptor_table_ptr dt;
+    gp_count = 0;
+    GP_ASM("sgdt %[dt]", [dt]"m"(dt), );
+}
+
+static void do_sidt(void)
+{
+    struct descriptor_table_ptr dt;
+    gp_count = 0;
+    GP_ASM("sidt %[dt]", [dt]"m"(dt), );
+}
+
+static void do_movcr(void)
+{
+    gp_count = 0;
+    GP_ASM("mov %%cr0, %%" R "ax", , "eax");
+}
+
+static void test_umip_nogp(const char *msg)
+{
+    puts(msg);
+
+    do_smsw();
+    report("no exception from smsw", gp_count == 0);
+    do_sgdt();
+    report("no exception from sgdt", gp_count == 0);
+    do_sidt();
+    report("no exception from sidt", gp_count == 0);
+    do_sldt();
+    report("no exception from sldt", gp_count == 0);
+    do_str();
+    report("no exception from str", gp_count == 0);
+    if (read_cs() & 3) {
+        do_movcr();
+        report("exception from mov %%cr0, %%eax", gp_count == 1);
+    }
+}
+
+static void test_umip_gp(const char *msg)
+{
+    puts(msg);
+
+#if 0
+    /*
+     * FIXME: this cannot be trapped by vmx, so it fails spuriously on current
+     * generation processors.  Once we know the CPUID model of the earliest
+     * machine which actually supports UMIP in hardware, test it and change
+     */
+    do_smsw();
+    report("exception from smsw", gp_count == 1);
+#endif
+    do_sgdt();
+    report("exception from sgdt", gp_count == 1);
+    do_sidt();
+    report("exception from sidt", gp_count == 1);
+    do_sldt();
+    report("exception from sldt", gp_count == 1);
+    do_str();
+    report("exception from str", gp_count == 1);
+    if (read_cs() & 3) {
+        do_movcr();
+        report("exception from mov %%cr0, %%eax", gp_count == 1);
+    }
+}
+
+/* The ugly mode switching code */
+
+int do_ring3(void (*fn)(const char *), const char *arg)
+{
+    static unsigned char user_stack[4096];
+    int ret;
+
+    asm volatile ("mov %[user_ds], %%" R "dx\n\t"
+		  "mov %%dx, %%ds\n\t"
+		  "mov %%dx, %%es\n\t"
+		  "mov %%dx, %%fs\n\t"
+		  "mov %%dx, %%gs\n\t"
+		  "mov %%" R "sp, %%" R "cx\n\t"
+		  "push" W " %%" R "dx \n\t"
+		  "lea %[user_stack_top], %%" R "dx \n\t"
+		  "push" W " %%" R "dx \n\t"
+		  "pushf" W "\n\t"
+		  "push" W " %[user_cs] \n\t"
+		  "push" W " $1f \n\t"
+		  "iret" W "\n"
+		  "1: \n\t"
+		  "push %%" R "cx\n\t"   /* save kernel SP */
+
+#ifndef __x86_64__
+		  "push %[arg]\n\t"
+#endif
+		  "call *%[fn]\n\t"
+#ifndef __x86_64__
+		  "pop %%ecx\n\t"
+#endif
+
+		  "pop %%" R "cx\n\t"
+		  "mov $1f, %%" R "dx\n\t"
+		  "int %[kernel_entry_vector]\n\t"
+		  ".section .text.entry \n\t"
+		  "kernel_entry: \n\t"
+		  "mov %%" R "cx, %%" R "sp \n\t"
+		  "mov %[kernel_ds], %%cx\n\t"
+		  "mov %%cx, %%ds\n\t"
+		  "mov %%cx, %%es\n\t"
+		  "mov %%cx, %%fs\n\t"
+		  "mov %%cx, %%gs\n\t"
+		  "jmp *%%" R "dx \n\t"
+		  ".section .text\n\t"
+		  "1:\n\t"
+		  : [ret] "=&a" (ret)
+		  : [user_ds] "i" (USER_DS),
+		    [user_cs] "i" (USER_CS),
+		    [user_stack_top]"m"(user_stack[sizeof user_stack]),
+		    [fn]"r"(fn),
+		    [arg]"D"(arg),
+		    [kernel_ds]"i"(KERNEL_DS),
+		    [kernel_entry_vector]"i"(0x20)
+		  : "rcx", "rdx");
+    return ret;
+}
+
+int main()
+{
+    extern unsigned char kernel_entry;
+
+    setup_idt();
+    set_idt_entry(0x20, &kernel_entry, 3);
+    handle_exception(13, gp_handler);
+    set_iopl(3);
+
+    test_umip_nogp("UMIP=0, CPL=0\n");
+    do_ring3(test_umip_nogp, "UMIP=0, CPL=3\n");
+
+    cpuid_7_ecx = cpuid(7).c;
+#if 0
+    if (!(cpuid_7_ecx & CPUID_7_ECX_UMIP)) {
+        printf("UMIP not available\n");
+        return report_summary();
+    }
+#endif
+    write_cr4(read_cr4() | X86_CR4_UMIP);
+
+    test_umip_nogp("UMIP=1, CPL=0\n");
+    do_ring3(test_umip_gp, "UMIP=1, CPL=3\n");
+
+    return report_summary();
+}
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index 91d1c75..f21cc75 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -209,6 +209,10 @@ file = pcid.flat
 extra_params = -cpu qemu64,+pcid
 arch = x86_64
 
+[umip]
+file = umip.flat
+extra_params = -cpu qemu64,+umip
+
 [vmx]
 file = vmx.flat
 extra_params = -cpu host,+vmx -m 2048 -append -exit_monitor_from_l2_test
-- 
1.8.3.1




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux