xref: /kvm-unit-tests/x86/syscall.c (revision 80e8b3d82fc53befe77728786530d286a78d0d78)
1 /* msr tests */
2 
3 #include "libcflat.h"
4 #include "processor.h"
5 #include "msr.h"
6 #include "desc.h"
7 #include "fwcfg.h"
8 
9 static void test_syscall_lazy_load(void)
10 {
11     extern void syscall_target(void);
12     u16 cs = read_cs(), ss = read_ss();
13     ulong tmp;
14 
15     wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SCE);
16     wrmsr(MSR_LSTAR, (ulong)syscall_target);
17     wrmsr(MSR_STAR, (uint64_t)cs << 32);
18     asm volatile("pushf; syscall; syscall_target: popf" : "=c"(tmp) : : "r11");
19     write_ss(ss);
20     // will crash horribly if broken
21     report(true, "MSR_*STAR eager loading");
22 }
23 
24 /*
25  * test handling of TF in syscall/sysret: #DB is raised if TF
26  * is 1 at the *end* of syscall/sysret.
27  *
28  * This uses 32-bit syscall/sysret because KVM emulates it on Intel processors.
29  * However, the same bug happens with 64-bit syscall/sysret if two vCPUs
30  * "race" to force the emulation of syscall/sysret.
31  */
32 
33 static uint16_t code_segment_upon_db;
34 static void handle_db(struct ex_regs *regs)
35 {
36     code_segment_upon_db = regs->cs;
37     regs->rflags &= ~(1 << 8);
38 }
39 
40 /* expects desired ring 3 flags in rax */
41 asm("syscall32_target:\n"
42     "   cmpl $0, code_segment_upon_db(%rip)\n"
43     "   jne back_to_test\n"
44     "   mov %eax,%r11d\n"
45     "   sysretl\n");
46 
47 /* 32-bit, ring-3 part of test_syscall_tf */
48 asm("   .code32\n"
49     "syscall_tf_user32:\n"
50     "   pushf\n"
51     "   pop %eax\n"
52     "   or $(1<<8),%eax\n"
53     "   push %eax\n"
54     "   popf\n"
55     "   syscall\n"  /* singlestep trap taken after syscall */
56     "   syscall\n"  /* jumps back to test_syscall_tf's body */
57     "   .code64\n");
58 
59 static void test_syscall_tf(void)
60 {
61     extern void syscall32_target(void);
62     extern void syscall_tf_user32(void);
63     ulong rcx;
64 
65     wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SCE);
66     wrmsr(MSR_CSTAR, (ulong)syscall32_target);
67     wrmsr(MSR_STAR, ((uint64_t)USER_CS32 << 48) | ((uint64_t)KERNEL_CS64 << 32));
68     wrmsr(MSR_SYSCALL_MASK, X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_IF|X86_EFLAGS_NT);
69     handle_exception(DB_VECTOR, handle_db);
70 
71     /* good:
72      *   sysret to syscall_tf_user32
73      *   popf sets TF (singlestep starts on the next instruction)
74      *   syscall to syscall32_target -> TF cleared and no singlestep
75      *   sysretl sets TF
76      *   handle_db sets code_segment_upon_db to USER_CS32 and clears TF
77      *   syscall to syscall32_target
78      *   syscall32_target jumps to back_to_test
79      *
80      * bad:
81      *   sysret to syscall_tf_user32
82      *   popf sets TF (singlestep starts on the next instruction)
83      *   syscall to syscall32_target, TF cleared and wrong singlestep exception
84      *   handle_db sets code_segment_upon_db to KERNEL_CS64
85      *   syscall32_target jumps to back_to_test
86      */
87     rcx = (ulong)syscall_tf_user32;
88     asm volatile("  push %%rbp\n"
89                  "  pushf; pop %%rax\n"   // expected by syscall32_target
90                  "  sysret\n"
91                  "back_to_test:\n"
92                  "  pop %%rbp"
93                  : "+c"(rcx) :
94                  : "rax", "rbx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11",
95                    "r12", "r13", "r14", "r15");
96     if (code_segment_upon_db != USER_CS32) {
97         printf("wrong CS (%#04x)!\n", code_segment_upon_db);
98     }
99     report(code_segment_upon_db == USER_CS32, "syscall TF handling");
100 }
101 
102 int main(int ac, char **av)
103 {
104     test_syscall_lazy_load();
105 
106     if (!no_test_device || !is_intel())
107         test_syscall_tf();
108     else
109         report_skip("syscall TF handling");
110 
111     return report_summary();
112 }
113