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