109 lines
3.3 KiB
C
109 lines
3.3 KiB
C
/* 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();
|
|
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("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();
|
|
}
|