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_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; 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 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 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