diff mbox

[kvm-unit-tests,2/2] x86: add testcase for syscall/sysret TF handling

Message ID 20170622152707.18399-3-rkrcmar@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Radim Krčmář June 22, 2017, 3:27 p.m. UTC
From: Paolo Bonzini <pbonzini@redhat.com>

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
---
 lib/x86/processor.h |  4 +++
 x86/syscall.c       | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 x86/unittests.cfg   |  1 +
 3 files changed, 85 insertions(+)
diff mbox

Patch

diff --git a/lib/x86/processor.h b/lib/x86/processor.h
index 895d992a6943..3e1a853b392b 100644
--- a/lib/x86/processor.h
+++ b/lib/x86/processor.h
@@ -35,7 +35,11 @@ 
 #define X86_EFLAGS_AF  0x00000010
 #define X86_EFLAGS_ZF  0x00000040
 #define X86_EFLAGS_SF  0x00000080
+#define X86_EFLAGS_TF  0x00000100
+#define X86_EFLAGS_IF  0x00000200
+#define X86_EFLAGS_DF  0x00000400
 #define X86_EFLAGS_OF  0x00000800
+#define X86_EFLAGS_NT  0x00004000
 #define X86_EFLAGS_AC  0x00040000
 
 #define X86_IA32_EFER          0xc0000080
diff --git a/x86/syscall.c b/x86/syscall.c
index d791edd6155d..8c3992d39b76 100644
--- a/x86/syscall.c
+++ b/x86/syscall.c
@@ -20,9 +20,89 @@  static void test_syscall_lazy_load(void)
     report("MSR_*STAR eager loading", true);
 }
 
+/*
+ * test handling of TF in syscall/sysret: #DB is raised if TF
+ * is 1 at the *end* of syscall/sysret.
+ *
+ * This uses 32-bit syscall/sysret because KVM emulates it on Intel processors.
+ * However, the same bug happens with 64-bit syscall/sysret if two vCPUs
+ * "race" to force the emulation of syscall/sysret.
+ */
+
+static uint16_t code_segment_upon_db;
+static void handle_db(struct ex_regs *regs)
+{
+    code_segment_upon_db = regs->cs;
+    regs->rflags &= ~(1 << 8);
+}
+
+/* expects desired ring 3 flags in rax */
+asm("syscall32_target:\n"
+    "   cmp $0, code_segment_upon_db(%rip)\n"
+    "   jne back_to_test\n"
+    "   mov %eax,%r11d\n"
+    "   sysretl\n");
+
+/* 32-bit, ring-3 part of test_syscall_tf */
+asm("   .code32\n"
+    "syscall_tf_user32:\n"
+    "   pushf\n"
+    "   pop %eax\n"
+    "   or $(1<<8),%eax\n"
+    "   push %eax\n"
+    "   popf\n"
+    "   syscall\n"  /* singlestep trap taken after syscall */
+    "   syscall\n"  /* jumps back to test_syscall_tf's body */
+    "   .code64\n");
+
+static void test_syscall_tf(void)
+{
+    extern void syscall32_target();
+    extern void syscall_tf_user32();
+    ulong rcx;
+
+    wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SCE);
+    wrmsr(MSR_CSTAR, (ulong)syscall32_target);
+    wrmsr(MSR_STAR, ((uint64_t)USER_CS32 << 48) | ((uint64_t)KERNEL_CS64 << 32));
+    wrmsr(MSR_SYSCALL_MASK, X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_IF|X86_EFLAGS_NT);
+    handle_exception(DB_VECTOR, handle_db);
+
+    /* good:
+     *   sysret to syscall_tf_user32
+     *   popf sets TF (singlestep starts on the next instruction)
+     *   syscall to syscall32_target -> TF cleared and no singlestep
+     *   sysretl sets TF
+     *   handle_db sets code_segment_upon_db to USER_CS32 and clears TF
+     *   syscall to syscall32_target
+     *   syscall32_target jumps to back_to_test
+     *
+     * bad:
+     *   sysret to syscall_tf_user32
+     *   popf sets TF (singlestep starts on the next instruction)
+     *   syscall to syscall32_target, TF cleared and wrong singlestep exception
+     *   handle_db sets code_segment_upon_db to KERNEL_CS64
+     *   syscall32_target jumps to back_to_test
+     */
+    rcx = (ulong)syscall_tf_user32;
+    asm volatile("  push %%rbp\n"
+                 "  pushf; pop %%rax\n"   // expected by syscall32_target
+                 "  sysret\n"
+                 "back_to_test:\n"
+                 "  pop %%rbp"
+                 : "+c"(rcx) :
+                 : "rax", "rbx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11",
+                   "r12", "r13", "r14", "r15");
+    if (code_segment_upon_db != USER_CS32) {
+        printf("wrong CS (%#04x)!\n", code_segment_upon_db);
+    }
+    report("syscall TF handling", code_segment_upon_db == USER_CS32);
+}
+
 int main(int ac, char **av)
 {
+    setup_idt();
     test_syscall_lazy_load();
+    test_syscall_tf();
 
     return report_summary();
 }
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index 2575ae6b3d5b..6455ee50f27f 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -153,6 +153,7 @@  file = sieve.flat
 
 [syscall]
 file = syscall.flat
+extra_params = -cpu Opteron_G1,vendor=AuthenticAMD
 
 [tsc]
 file = tsc.flat