| /* msr tests */ |
| |
| #include "libcflat.h" |
| #include "processor.h" |
| #include "msr.h" |
| #include "desc.h" |
| |
| static void test_syscall_lazy_load(void) |
| { |
| extern void syscall_target(void); |
| u16 cs = read_cs(), ss = read_ss(); |
| ulong tmp; |
| |
| wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SCE); |
| wrmsr(MSR_LSTAR, (ulong)syscall_target); |
| wrmsr(MSR_STAR, (uint64_t)cs << 32); |
| asm volatile("pushf; syscall; syscall_target: popf" : "=c"(tmp) : : "r11"); |
| write_ss(ss); |
| // will crash horribly if broken |
| report(true, "MSR_*STAR eager loading"); |
| } |
| |
| /* |
| * test handling of TF in syscall/sysret: #DB is raised if TF |
| * is 1 at the *end* of syscall/sysret. |
| * |
| * This uses 32-bit syscall/sysret because KVM emulates it on Intel processors. |
| * However, the same bug happens with 64-bit syscall/sysret if two vCPUs |
| * "race" to force the emulation of syscall/sysret. |
| */ |
| |
| static uint16_t code_segment_upon_db; |
| static void handle_db(struct ex_regs *regs) |
| { |
| code_segment_upon_db = regs->cs; |
| regs->rflags &= ~(1 << 8); |
| } |
| |
| /* expects desired ring 3 flags in rax */ |
| asm("syscall32_target:\n" |
| " cmpl $0, code_segment_upon_db(%rip)\n" |
| " jne back_to_test\n" |
| " mov %eax,%r11d\n" |
| " sysretl\n"); |
| |
| /* 32-bit, ring-3 part of test_syscall_tf */ |
| asm(" .code32\n" |
| "syscall_tf_user32:\n" |
| " pushf\n" |
| " pop %eax\n" |
| " or $(1<<8),%eax\n" |
| " push %eax\n" |
| " popf\n" |
| " syscall\n" /* singlestep trap taken after syscall */ |
| " syscall\n" /* jumps back to test_syscall_tf's body */ |
| " .code64\n"); |
| |
| static void test_syscall_tf(void) |
| { |
| extern void syscall32_target(void); |
| extern void syscall_tf_user32(void); |
| ulong rcx; |
| |
| wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_SCE); |
| wrmsr(MSR_CSTAR, (ulong)syscall32_target); |
| wrmsr(MSR_STAR, ((uint64_t)USER_CS32 << 48) | ((uint64_t)KERNEL_CS64 << 32)); |
| wrmsr(MSR_SYSCALL_MASK, X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_IF|X86_EFLAGS_NT); |
| handle_exception(DB_VECTOR, handle_db); |
| |
| /* good: |
| * sysret to syscall_tf_user32 |
| * popf sets TF (singlestep starts on the next instruction) |
| * syscall to syscall32_target -> TF cleared and no singlestep |
| * sysretl sets TF |
| * handle_db sets code_segment_upon_db to USER_CS32 and clears TF |
| * syscall to syscall32_target |
| * syscall32_target jumps to back_to_test |
| * |
| * bad: |
| * sysret to syscall_tf_user32 |
| * popf sets TF (singlestep starts on the next instruction) |
| * syscall to syscall32_target, TF cleared and wrong singlestep exception |
| * handle_db sets code_segment_upon_db to KERNEL_CS64 |
| * syscall32_target jumps to back_to_test |
| */ |
| rcx = (ulong)syscall_tf_user32; |
| asm volatile(" push %%rbp\n" |
| " pushf; pop %%rax\n" // expected by syscall32_target |
| " sysret\n" |
| "back_to_test:\n" |
| " pop %%rbp" |
| : "+c"(rcx) : |
| : "rax", "rbx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", |
| "r12", "r13", "r14", "r15"); |
| if (code_segment_upon_db != USER_CS32) { |
| printf("wrong CS (%#04x)!\n", code_segment_upon_db); |
| } |
| report(code_segment_upon_db == USER_CS32, "syscall TF handling"); |
| } |
| |
| int main(int ac, char **av) |
| { |
| test_syscall_lazy_load(); |
| test_syscall_tf(); |
| |
| return report_summary(); |
| } |