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
test_syscall_lazy_load(void)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_pass("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;
handle_db(struct ex_regs * regs)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
test_syscall_tf(void)59 static void test_syscall_tf(void)
60 {
61 extern void syscall32_target(void);
62 extern void syscall_tf_user32(void);
63 ulong rax;
64 ulong rcx;
65
66 wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SCE);
67 wrmsr(MSR_CSTAR, (ulong)syscall32_target);
68 wrmsr(MSR_STAR, ((uint64_t)USER_CS32 << 48) | ((uint64_t)KERNEL_CS64 << 32));
69 wrmsr(MSR_SYSCALL_MASK, X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_IF|X86_EFLAGS_NT);
70 handle_exception(DB_VECTOR, handle_db);
71
72 /* good:
73 * sysret to syscall_tf_user32
74 * popf sets TF (singlestep starts on the next instruction)
75 * syscall to syscall32_target -> TF cleared and no singlestep
76 * sysretl sets TF
77 * handle_db sets code_segment_upon_db to USER_CS32 and clears TF
78 * syscall to syscall32_target
79 * syscall32_target jumps to back_to_test
80 *
81 * bad:
82 * sysret to syscall_tf_user32
83 * popf sets TF (singlestep starts on the next instruction)
84 * syscall to syscall32_target, TF cleared and wrong singlestep exception
85 * handle_db sets code_segment_upon_db to KERNEL_CS64
86 * syscall32_target jumps to back_to_test
87 */
88 rax = (ulong)&tss[0].rsp0;
89 rcx = (ulong)syscall_tf_user32;
90 asm volatile(" push %%rbp\n"
91 " mov %%rsp, (%%rax)\n" // stack pointer for exception handler
92 " pushf; pop %%rax\n" // expected by syscall32_target
93 " sysretl\n"
94 "back_to_test:\n"
95 " pop %%rbp"
96 : [sysret_target] "+c"(rcx), [sp0] "+a" (rax) :
97 : "rbx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11",
98 "r12", "r13", "r14", "r15", "memory");
99 if (code_segment_upon_db != USER_CS32) {
100 printf("wrong CS (%#04x)!\n", code_segment_upon_db);
101 }
102 report(code_segment_upon_db == USER_CS32, "syscall TF handling");
103 }
104
main(int ac,char ** av)105 int main(int ac, char **av)
106 {
107 test_syscall_lazy_load();
108
109 if (!no_test_device || !is_intel())
110 test_syscall_tf();
111 else
112 report_skip("syscall TF handling");
113
114 return report_summary();
115 }
116