@@ -340,6 +340,19 @@ static inline void write_cr4(unsigned long cr4)
asm volatile ("mov %0, %%cr4" :: "r" (cr4));
}
+static inline bool write_cr4_safe(unsigned long cr4)
+{
+ exinfo_t fault = 0;
+
+ asm volatile ("1: mov %1, %%cr4; 2:"
+ _ASM_EXTABLE_HANDLER(1b, 2b, ex_record_fault_edi)
+ : "+D" (fault)
+ : "r" (cr4),
+ "X" (ex_record_fault_edi));
+
+ return fault;
+}
+
static inline void write_cr8(unsigned long cr8)
{
asm volatile ("mov %0, %%cr8" :: "r" (cr8));
@@ -111,4 +111,6 @@ guest breakout.
@section index-in-development In Development
@subpage test-vvmx - Nested VT-x tests.
+
+@subpage test-umip - User-Mode Instruction Prevention
*/
new file mode 100644
@@ -0,0 +1,9 @@
+include $(ROOT)/build/common.mk
+
+NAME := umip
+CATEGORY := functional
+TEST-ENVS := hvm32 hvm64
+
+obj-perenv += main.o
+
+include $(ROOT)/build/gen.mk
new file mode 100644
@@ -0,0 +1,214 @@
+/**
+ * @file tests/umip/main.c
+ * @ref test-umip
+ *
+ * @page test-umip umip
+ *
+ * @todo Docs for test-umip
+ *
+ * @see tests/umip/main.c
+ */
+#include <xtf.h>
+#include <arch/exinfo.h>
+#include <arch/processor.h>
+
+const char test_title[] = "User-Mode Instruction Prevention Test";
+bool test_wants_user_mappings = true;
+
+static unsigned long stub_sgdt(unsigned long force)
+{
+ exinfo_t fault = 0;
+ desc_ptr tmp;
+
+ asm volatile("test %[fep], %[fep];"
+ "jz 1f;"
+ _ASM_XEN_FEP
+ "1: sgdt %[tmp]; 2:"
+ _ASM_EXTABLE_HANDLER(1b,2b, ex_record_fault_edi)
+ : "+D" (fault), [tmp] "=m" (tmp)
+ : [fep] "q" (force),
+ "X" (ex_record_fault_edi));
+
+ return fault;
+}
+static unsigned long stub_sidt(unsigned long force)
+{
+ exinfo_t fault = 0;
+ desc_ptr tmp;
+
+ asm volatile("test %[fep], %[fep];"
+ "jz 1f;"
+ _ASM_XEN_FEP
+ "1: sidt %[tmp]; 2:"
+ _ASM_EXTABLE_HANDLER(1b,2b, ex_record_fault_edi)
+ : "+D" (fault), [tmp] "=m" (tmp)
+ : [fep] "q" (force),
+ "X" (ex_record_fault_edi));
+
+ return fault;
+}
+
+static unsigned long stub_sldt(unsigned long force)
+{
+ exinfo_t fault = 0;
+ unsigned int tmp;
+
+ asm volatile("test %[fep], %[fep];"
+ "jz 1f;"
+ _ASM_XEN_FEP
+ "1: sldt %[tmp]; 2:"
+ _ASM_EXTABLE_HANDLER(1b,2b, ex_record_fault_edi)
+ : "+D" (fault), [tmp] "=r" (tmp)
+ : [fep] "q" (force),
+ "X" (ex_record_fault_edi));
+
+ return fault;
+}
+
+static unsigned long stub_str(unsigned long force)
+{
+ exinfo_t fault = 0;
+ unsigned int tmp;
+
+ asm volatile("test %[fep], %[fep];"
+ "jz 1f;"
+ _ASM_XEN_FEP
+ "1: str %[tmp]; 2:"
+ _ASM_EXTABLE_HANDLER(1b,2b, ex_record_fault_edi)
+ : "+D" (fault), [tmp] "=r" (tmp)
+ : [fep] "q" (force),
+ "X" (ex_record_fault_edi));
+
+ return fault;
+}
+
+static unsigned long stub_smsw(unsigned long force)
+{
+ exinfo_t fault = 0;
+ unsigned int tmp;
+
+ asm volatile("test %[fep], %[fep];"
+ "jz 1f;"
+ _ASM_XEN_FEP
+ "1: smsw %[tmp]; 2:"
+ _ASM_EXTABLE_HANDLER(1b,2b, ex_record_fault_edi)
+ : "+D" (fault), [tmp] "=r" (tmp)
+ : [fep] "q" (force),
+ "X" (ex_record_fault_edi));
+
+ return fault;
+}
+
+static const struct stub {
+ unsigned long (*fn)(unsigned long);
+ const char *name;
+} stubs[] = {
+ { stub_sgdt, "SGDT" },
+ { stub_sidt, "SIDT" },
+ { stub_sldt, "SLDT" },
+ { stub_str, "STR" },
+ { stub_smsw, "SMSW" },
+};
+
+static void test_insns(bool umip_active, bool force)
+{
+ unsigned int i;
+ bool user;
+
+ for ( user = false; ; user = true )
+ {
+ exinfo_t exp = user && umip_active ? EXINFO_SYM(GP, 0) : 0;
+
+ for ( i = 0; i < ARRAY_SIZE(stubs); i++)
+ {
+ const struct stub *s = &stubs[i];
+ exinfo_t ret;
+
+ ret = user ? exec_user_param(s->fn, force) : s->fn(force);
+
+ /*
+ * Tolerate the instruction emulator not understanding these
+ * instructions in older releases of Xen.
+ */
+
+ if ( force && ret == EXINFO_SYM(UD, 0) )
+ {
+ static bool once;
+
+ if ( !once )
+ {
+ xtf_skip("Skip: Emulator doesn't implement %s\n", s->name);
+ once = true;
+ }
+
+ continue;
+ }
+
+ if ( ret != exp )
+ xtf_failure("Fail: %s %s\n"
+ " expected %pe\n"
+ " got %pe\n",
+ user ? "user" : "supervisor", s->name,
+ _p(exp), _p(ret));
+ }
+
+ if ( user )
+ break;
+ }
+}
+
+static void test_umip(bool umip_active)
+{
+ test_insns(umip_active, false);
+
+ if ( xtf_has_fep )
+ test_insns(umip_active, true);
+}
+
+void test_main(void)
+{
+ if ( !xtf_has_fep )
+ xtf_skip("FEP support not detected - some tests will be skipped\n");
+
+ test_umip(false);
+
+ if ( !cpu_has_umip )
+ {
+ xtf_skip("UMIP is not supported, skip the rest of test\n");
+
+ if ( !write_cr4_safe(read_cr4() | X86_CR4_UMIP) )
+ xtf_failure("UMIP unsupported, but setting CR4 bit succeeded\n");
+
+ return;
+ }
+
+ /* activate UMIP */
+ if ( write_cr4_safe(read_cr4() | X86_CR4_UMIP) )
+ {
+ xtf_failure("Fail: Unable to activate UMIP\n");
+ return;
+ }
+
+ test_umip(true);
+
+ /* deactivate UMIP */
+ if ( write_cr4_safe(read_cr4() & ~X86_CR4_UMIP) )
+ {
+ xtf_failure("Fail: Unable to deactivate UMIP\n");
+ return;
+ }
+
+ test_umip(false);
+
+ xtf_success(NULL);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
Add a "umip" test for the User-Model Instruction Prevention. The test simply tries to run sgdt/sidt/sldt/str/smsw in guest user-mode with CR4_UMIP = 1. Signed-off-by: Boqun Feng (Intel) <boqun.feng@gmail.com> --- v1 --> v2: * add a new write_cr4_safe() * use %pe for exception print * refactor the code based on Andrew's guide and advice v2 --> v3: * add test_insns() to simplify test_main() logic * make write_cr4_safe() return 0 for success. * add UMIP activation test even if !cpu_has_umip Test results: * With UMIP patch: ** boot with hvm_fep: SUCCESS ** boot without hvm_fep: SKIP * Without UMIP patch: ** boot with hvm_fep: SKIP ** boot without hvm_fep: SKIP (illed implementation) * With UMIP cpuid exposed but hvm_cr4_guest_valid_bits() didn't include X86_CR4_UMIP: ** boot with hvm_fep: FAILURE, due to "Fail: Unable to activate UMIP.." ** boot without hvm_fep: FAILURE, due to "Fail: Unable to activate UMIP.." * With UMIP cpuid not exposed but hvm_cr4_guest_valid_bits() included X86_CR4_UMIP: ** boot with hvm_fep: FAILURE, due to "UMIP unsupported, but setting CR4 succeeded" ** boot without hvm_fep: FAILURE, due to "UMIP unsupported, but setting CR4 succeeded" arch/x86/include/arch/lib.h | 13 +++ docs/all-tests.dox | 2 + tests/umip/Makefile | 9 ++ tests/umip/main.c | 214 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 238 insertions(+) create mode 100644 tests/umip/Makefile create mode 100644 tests/umip/main.c