blob: afb373dfcd7c955f7ad8ae95919a2e55eaaaaa1a [file] [log] [blame]
#include "libcflat.h"
#include "desc.h"
#include "processor.h"
/* GP handler to skip over faulting instructions */
static unsigned long expected_rip;
static int skip_count;
static volatile int gp_count;
static void gp_handler(struct ex_regs *regs)
{
if (regs->rip == expected_rip) {
gp_count++;
regs->rip += skip_count;
} else {
unhandled_exception(regs, false);
}
}
#define GP_ASM(stmt, in, clobber) \
asm ("mov" W " $1f, %[expected_rip]\n\t" \
"movl $2f-1f, %[skip_count]\n\t" \
"1: " stmt "\n\t" \
"2: " \
: [expected_rip] "=m" (expected_rip), \
[skip_count] "=m" (skip_count) \
: in : clobber)
static void do_smsw(void)
{
gp_count = 0;
GP_ASM("smsw %%ax", , "eax");
}
static void do_sldt(void)
{
gp_count = 0;
GP_ASM("sldt %%ax", , "eax");
}
static void do_str(void)
{
gp_count = 0;
GP_ASM("str %%ax", , "eax");
}
static void do_sgdt(void)
{
struct descriptor_table_ptr dt;
gp_count = 0;
GP_ASM("sgdt %[dt]", [dt]"m"(dt), );
}
static void do_sidt(void)
{
struct descriptor_table_ptr dt;
gp_count = 0;
GP_ASM("sidt %[dt]", [dt]"m"(dt), );
}
static void do_movcr(void)
{
gp_count = 0;
GP_ASM("mov %%cr0, %%" R "ax", , "eax");
}
static void test_umip_nogp(const char *msg)
{
puts(msg);
do_smsw();
report(gp_count == 0, "no exception from smsw");
do_sgdt();
report(gp_count == 0, "no exception from sgdt");
do_sidt();
report(gp_count == 0, "no exception from sidt");
do_sldt();
report(gp_count == 0, "no exception from sldt");
do_str();
report(gp_count == 0, "no exception from str");
if (read_cs() & 3) {
do_movcr();
report(gp_count == 1, "exception from mov %%cr0, %%eax");
}
}
static void test_umip_gp(const char *msg)
{
puts(msg);
#if 0
/* Skip this, because it cannot be emulated correctly. */
do_smsw();
report(gp_count == 1, "exception from smsw");
#endif
do_sgdt();
report(gp_count == 1, "exception from sgdt");
do_sidt();
report(gp_count == 1, "exception from sidt");
do_sldt();
report(gp_count == 1, "exception from sldt");
do_str();
report(gp_count == 1, "exception from str");
if (read_cs() & 3) {
do_movcr();
report(gp_count == 1, "exception from mov %%cr0, %%eax");
}
}
/* The ugly mode switching code */
static int do_ring3(void (*fn)(const char *), const char *arg)
{
static unsigned char user_stack[4096];
int ret;
asm volatile ("mov %[user_ds], %%" R "dx\n\t"
"mov %%dx, %%ds\n\t"
"mov %%dx, %%es\n\t"
"mov %%dx, %%fs\n\t"
"mov %%dx, %%gs\n\t"
"mov %%" R "sp, %%" R "cx\n\t"
"push" W " %%" R "dx \n\t"
"lea %[user_stack_top], %%" R "dx \n\t"
"push" W " %%" R "dx \n\t"
"pushf" W "\n\t"
"push" W " %[user_cs] \n\t"
"push" W " $1f \n\t"
"iret" W "\n"
"1: \n\t"
"push %%" R "cx\n\t" /* save kernel SP */
#ifndef __x86_64__
"push %[arg]\n\t"
#endif
"call *%[fn]\n\t"
#ifndef __x86_64__
"pop %%ecx\n\t"
#endif
"pop %%" R "cx\n\t"
"mov $1f, %%" R "dx\n\t"
"int %[kernel_entry_vector]\n\t"
".section .text.entry \n\t"
"kernel_entry: \n\t"
"mov %%" R "cx, %%" R "sp \n\t"
"mov %[kernel_ds], %%cx\n\t"
"mov %%cx, %%ds\n\t"
"mov %%cx, %%es\n\t"
"mov %%cx, %%fs\n\t"
"mov %%cx, %%gs\n\t"
"jmp *%%" R "dx \n\t"
".section .text\n\t"
"1:\n\t"
: [ret] "=&a" (ret)
: [user_ds] "i" (USER_DS),
[user_cs] "i" (USER_CS),
[user_stack_top]"m"(user_stack[sizeof user_stack]),
[fn]"r"(fn),
[arg]"D"(arg),
[kernel_ds]"i"(KERNEL_DS),
[kernel_entry_vector]"i"(0x20)
: "rcx", "rdx");
return ret;
}
int main(void)
{
extern unsigned char kernel_entry;
set_idt_entry(0x20, &kernel_entry, 3);
handle_exception(13, gp_handler);
set_iopl(3);
test_umip_nogp("UMIP=0, CPL=0\n");
do_ring3(test_umip_nogp, "UMIP=0, CPL=3\n");
if (!this_cpu_has(X86_FEATURE_UMIP)) {
printf("UMIP not available\n");
return report_summary();
}
write_cr4(read_cr4() | X86_CR4_UMIP);
test_umip_nogp("UMIP=1, CPL=0\n");
do_ring3(test_umip_gp, "UMIP=1, CPL=3\n");
return report_summary();
}