From: Paolo Bonzini <pbonzini@xxxxxxxxxx> Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx> Signed-off-by: Radim Krčmář <rkrcmar@xxxxxxxxxx> --- lib/x86/processor.h | 4 +++ x86/syscall.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++ x86/unittests.cfg | 1 + 3 files changed, 85 insertions(+) diff --git a/lib/x86/processor.h b/lib/x86/processor.h index 895d992a6943..3e1a853b392b 100644 --- a/lib/x86/processor.h +++ b/lib/x86/processor.h @@ -35,7 +35,11 @@ #define X86_EFLAGS_AF 0x00000010 #define X86_EFLAGS_ZF 0x00000040 #define X86_EFLAGS_SF 0x00000080 +#define X86_EFLAGS_TF 0x00000100 +#define X86_EFLAGS_IF 0x00000200 +#define X86_EFLAGS_DF 0x00000400 #define X86_EFLAGS_OF 0x00000800 +#define X86_EFLAGS_NT 0x00004000 #define X86_EFLAGS_AC 0x00040000 #define X86_IA32_EFER 0xc0000080 diff --git a/x86/syscall.c b/x86/syscall.c index d791edd6155d..8c3992d39b76 100644 --- a/x86/syscall.c +++ b/x86/syscall.c @@ -20,9 +20,89 @@ static void test_syscall_lazy_load(void) report("MSR_*STAR eager loading", true); } +/* + * 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" + " cmp $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(); + extern void syscall_tf_user32(); + 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("syscall TF handling", code_segment_upon_db == USER_CS32); +} + int main(int ac, char **av) { + setup_idt(); test_syscall_lazy_load(); + test_syscall_tf(); return report_summary(); } diff --git a/x86/unittests.cfg b/x86/unittests.cfg index 2575ae6b3d5b..6455ee50f27f 100644 --- a/x86/unittests.cfg +++ b/x86/unittests.cfg @@ -153,6 +153,7 @@ file = sieve.flat [syscall] file = syscall.flat +extra_params = -cpu Opteron_G1,vendor=AuthenticAMD [tsc] file = tsc.flat -- 2.13.1