1728e71eeSPaolo Bonzini 2728e71eeSPaolo Bonzini #include "libcflat.h" 3728e71eeSPaolo Bonzini #include "desc.h" 4728e71eeSPaolo Bonzini #include "processor.h" 5728e71eeSPaolo Bonzini 6728e71eeSPaolo Bonzini #define CPUID_7_ECX_UMIP (1 << 2) 7728e71eeSPaolo Bonzini static int cpuid_7_ecx; 8728e71eeSPaolo Bonzini 9728e71eeSPaolo Bonzini 10728e71eeSPaolo Bonzini /* GP handler to skip over faulting instructions */ 11728e71eeSPaolo Bonzini 12728e71eeSPaolo Bonzini static unsigned long expected_rip; 13728e71eeSPaolo Bonzini static int skip_count; 14728e71eeSPaolo Bonzini static volatile int gp_count; 15728e71eeSPaolo Bonzini 16728e71eeSPaolo Bonzini void gp_handler(struct ex_regs *regs) 17728e71eeSPaolo Bonzini { 18728e71eeSPaolo Bonzini if (regs->rip == expected_rip) { 19728e71eeSPaolo Bonzini gp_count++; 20728e71eeSPaolo Bonzini regs->rip += skip_count; 21728e71eeSPaolo Bonzini } else { 22728e71eeSPaolo Bonzini unhandled_exception(regs, false); 23728e71eeSPaolo Bonzini } 24728e71eeSPaolo Bonzini } 25728e71eeSPaolo Bonzini 26728e71eeSPaolo Bonzini 27728e71eeSPaolo Bonzini #define GP_ASM(stmt, in, clobber) \ 28728e71eeSPaolo Bonzini asm ("mov" W " $1f, %[expected_rip]\n\t" \ 29728e71eeSPaolo Bonzini "movl $2f-1f, %[skip_count]\n\t" \ 30728e71eeSPaolo Bonzini "1: " stmt "\n\t" \ 31728e71eeSPaolo Bonzini "2: " \ 32728e71eeSPaolo Bonzini : [expected_rip] "=m" (expected_rip), \ 33728e71eeSPaolo Bonzini [skip_count] "=m" (skip_count) \ 34728e71eeSPaolo Bonzini : in : clobber) 35728e71eeSPaolo Bonzini 36728e71eeSPaolo Bonzini static void do_smsw(void) 37728e71eeSPaolo Bonzini { 38728e71eeSPaolo Bonzini gp_count = 0; 39728e71eeSPaolo Bonzini GP_ASM("smsw %%ax", , "eax"); 40728e71eeSPaolo Bonzini } 41728e71eeSPaolo Bonzini 42728e71eeSPaolo Bonzini static void do_sldt(void) 43728e71eeSPaolo Bonzini { 44728e71eeSPaolo Bonzini gp_count = 0; 45728e71eeSPaolo Bonzini GP_ASM("sldt %%ax", , "eax"); 46728e71eeSPaolo Bonzini } 47728e71eeSPaolo Bonzini 48728e71eeSPaolo Bonzini static void do_str(void) 49728e71eeSPaolo Bonzini { 50728e71eeSPaolo Bonzini gp_count = 0; 51728e71eeSPaolo Bonzini GP_ASM("str %%ax", , "eax"); 52728e71eeSPaolo Bonzini } 53728e71eeSPaolo Bonzini 54728e71eeSPaolo Bonzini static void do_sgdt(void) 55728e71eeSPaolo Bonzini { 56728e71eeSPaolo Bonzini struct descriptor_table_ptr dt; 57728e71eeSPaolo Bonzini gp_count = 0; 58728e71eeSPaolo Bonzini GP_ASM("sgdt %[dt]", [dt]"m"(dt), ); 59728e71eeSPaolo Bonzini } 60728e71eeSPaolo Bonzini 61728e71eeSPaolo Bonzini static void do_sidt(void) 62728e71eeSPaolo Bonzini { 63728e71eeSPaolo Bonzini struct descriptor_table_ptr dt; 64728e71eeSPaolo Bonzini gp_count = 0; 65728e71eeSPaolo Bonzini GP_ASM("sidt %[dt]", [dt]"m"(dt), ); 66728e71eeSPaolo Bonzini } 67728e71eeSPaolo Bonzini 68728e71eeSPaolo Bonzini static void do_movcr(void) 69728e71eeSPaolo Bonzini { 70728e71eeSPaolo Bonzini gp_count = 0; 71728e71eeSPaolo Bonzini GP_ASM("mov %%cr0, %%" R "ax", , "eax"); 72728e71eeSPaolo Bonzini } 73728e71eeSPaolo Bonzini 7449024be1SPeter Xu static void test_umip_nogp(const char *msg) 75728e71eeSPaolo Bonzini { 76728e71eeSPaolo Bonzini puts(msg); 77728e71eeSPaolo Bonzini 78728e71eeSPaolo Bonzini do_smsw(); 79728e71eeSPaolo Bonzini report("no exception from smsw", gp_count == 0); 80728e71eeSPaolo Bonzini do_sgdt(); 81728e71eeSPaolo Bonzini report("no exception from sgdt", gp_count == 0); 82728e71eeSPaolo Bonzini do_sidt(); 83728e71eeSPaolo Bonzini report("no exception from sidt", gp_count == 0); 84728e71eeSPaolo Bonzini do_sldt(); 85728e71eeSPaolo Bonzini report("no exception from sldt", gp_count == 0); 86728e71eeSPaolo Bonzini do_str(); 87728e71eeSPaolo Bonzini report("no exception from str", gp_count == 0); 88728e71eeSPaolo Bonzini if (read_cs() & 3) { 89728e71eeSPaolo Bonzini do_movcr(); 90728e71eeSPaolo Bonzini report("exception from mov %%cr0, %%eax", gp_count == 1); 91728e71eeSPaolo Bonzini } 92728e71eeSPaolo Bonzini } 93728e71eeSPaolo Bonzini 9449024be1SPeter Xu static void test_umip_gp(const char *msg) 95728e71eeSPaolo Bonzini { 96728e71eeSPaolo Bonzini puts(msg); 97728e71eeSPaolo Bonzini 98*d5d04459SPaolo Bonzini #if 0 99*d5d04459SPaolo Bonzini /* Skip this, because it cannot be emulated correctly. */ 100728e71eeSPaolo Bonzini do_smsw(); 101728e71eeSPaolo Bonzini report("exception from smsw", gp_count == 1); 102*d5d04459SPaolo Bonzini #endif 103728e71eeSPaolo Bonzini do_sgdt(); 104728e71eeSPaolo Bonzini report("exception from sgdt", gp_count == 1); 105728e71eeSPaolo Bonzini do_sidt(); 106728e71eeSPaolo Bonzini report("exception from sidt", gp_count == 1); 107728e71eeSPaolo Bonzini do_sldt(); 108728e71eeSPaolo Bonzini report("exception from sldt", gp_count == 1); 109728e71eeSPaolo Bonzini do_str(); 110728e71eeSPaolo Bonzini report("exception from str", gp_count == 1); 111728e71eeSPaolo Bonzini if (read_cs() & 3) { 112728e71eeSPaolo Bonzini do_movcr(); 113728e71eeSPaolo Bonzini report("exception from mov %%cr0, %%eax", gp_count == 1); 114728e71eeSPaolo Bonzini } 115728e71eeSPaolo Bonzini } 116728e71eeSPaolo Bonzini 117728e71eeSPaolo Bonzini /* The ugly mode switching code */ 118728e71eeSPaolo Bonzini 11949024be1SPeter Xu int do_ring3(void (*fn)(const char *), const char *arg) 120728e71eeSPaolo Bonzini { 121728e71eeSPaolo Bonzini static unsigned char user_stack[4096]; 122728e71eeSPaolo Bonzini int ret; 123728e71eeSPaolo Bonzini 124728e71eeSPaolo Bonzini asm volatile ("mov %[user_ds], %%" R "dx\n\t" 125728e71eeSPaolo Bonzini "mov %%dx, %%ds\n\t" 126728e71eeSPaolo Bonzini "mov %%dx, %%es\n\t" 127728e71eeSPaolo Bonzini "mov %%dx, %%fs\n\t" 128728e71eeSPaolo Bonzini "mov %%dx, %%gs\n\t" 129728e71eeSPaolo Bonzini "mov %%" R "sp, %%" R "cx\n\t" 130728e71eeSPaolo Bonzini "push" W " %%" R "dx \n\t" 131728e71eeSPaolo Bonzini "lea %[user_stack_top], %%" R "dx \n\t" 132728e71eeSPaolo Bonzini "push" W " %%" R "dx \n\t" 133728e71eeSPaolo Bonzini "pushf" W "\n\t" 134728e71eeSPaolo Bonzini "push" W " %[user_cs] \n\t" 135728e71eeSPaolo Bonzini "push" W " $1f \n\t" 136728e71eeSPaolo Bonzini "iret" W "\n" 137728e71eeSPaolo Bonzini "1: \n\t" 138728e71eeSPaolo Bonzini "push %%" R "cx\n\t" /* save kernel SP */ 139728e71eeSPaolo Bonzini 140728e71eeSPaolo Bonzini #ifndef __x86_64__ 141728e71eeSPaolo Bonzini "push %[arg]\n\t" 142728e71eeSPaolo Bonzini #endif 143728e71eeSPaolo Bonzini "call *%[fn]\n\t" 144728e71eeSPaolo Bonzini #ifndef __x86_64__ 145728e71eeSPaolo Bonzini "pop %%ecx\n\t" 146728e71eeSPaolo Bonzini #endif 147728e71eeSPaolo Bonzini 148728e71eeSPaolo Bonzini "pop %%" R "cx\n\t" 149728e71eeSPaolo Bonzini "mov $1f, %%" R "dx\n\t" 150728e71eeSPaolo Bonzini "int %[kernel_entry_vector]\n\t" 151728e71eeSPaolo Bonzini ".section .text.entry \n\t" 152728e71eeSPaolo Bonzini "kernel_entry: \n\t" 153728e71eeSPaolo Bonzini "mov %%" R "cx, %%" R "sp \n\t" 154728e71eeSPaolo Bonzini "mov %[kernel_ds], %%cx\n\t" 155728e71eeSPaolo Bonzini "mov %%cx, %%ds\n\t" 156728e71eeSPaolo Bonzini "mov %%cx, %%es\n\t" 157728e71eeSPaolo Bonzini "mov %%cx, %%fs\n\t" 158728e71eeSPaolo Bonzini "mov %%cx, %%gs\n\t" 159728e71eeSPaolo Bonzini "jmp *%%" R "dx \n\t" 160728e71eeSPaolo Bonzini ".section .text\n\t" 161728e71eeSPaolo Bonzini "1:\n\t" 162728e71eeSPaolo Bonzini : [ret] "=&a" (ret) 163728e71eeSPaolo Bonzini : [user_ds] "i" (USER_DS), 164728e71eeSPaolo Bonzini [user_cs] "i" (USER_CS), 165728e71eeSPaolo Bonzini [user_stack_top]"m"(user_stack[sizeof user_stack]), 166728e71eeSPaolo Bonzini [fn]"r"(fn), 167728e71eeSPaolo Bonzini [arg]"D"(arg), 168728e71eeSPaolo Bonzini [kernel_ds]"i"(KERNEL_DS), 169728e71eeSPaolo Bonzini [kernel_entry_vector]"i"(0x20) 170728e71eeSPaolo Bonzini : "rcx", "rdx"); 171728e71eeSPaolo Bonzini return ret; 172728e71eeSPaolo Bonzini } 173728e71eeSPaolo Bonzini 174728e71eeSPaolo Bonzini int main() 175728e71eeSPaolo Bonzini { 176728e71eeSPaolo Bonzini extern unsigned char kernel_entry; 177728e71eeSPaolo Bonzini 178728e71eeSPaolo Bonzini setup_idt(); 179728e71eeSPaolo Bonzini set_idt_entry(0x20, &kernel_entry, 3); 180728e71eeSPaolo Bonzini handle_exception(13, gp_handler); 181728e71eeSPaolo Bonzini set_iopl(3); 182728e71eeSPaolo Bonzini 183728e71eeSPaolo Bonzini test_umip_nogp("UMIP=0, CPL=0\n"); 184728e71eeSPaolo Bonzini do_ring3(test_umip_nogp, "UMIP=0, CPL=3\n"); 185728e71eeSPaolo Bonzini 186728e71eeSPaolo Bonzini cpuid_7_ecx = cpuid(7).c; 187728e71eeSPaolo Bonzini if (!(cpuid_7_ecx & CPUID_7_ECX_UMIP)) { 188728e71eeSPaolo Bonzini printf("UMIP not available\n"); 189728e71eeSPaolo Bonzini return report_summary(); 190728e71eeSPaolo Bonzini } 191728e71eeSPaolo Bonzini write_cr4(read_cr4() | X86_CR4_UMIP); 192728e71eeSPaolo Bonzini 193728e71eeSPaolo Bonzini test_umip_nogp("UMIP=0, CPL=0\n"); 194728e71eeSPaolo Bonzini do_ring3(test_umip_gp, "UMIP=0, CPL=3\n"); 195728e71eeSPaolo Bonzini 196728e71eeSPaolo Bonzini return report_summary(); 197728e71eeSPaolo Bonzini } 198