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