xref: /kvm-unit-tests/x86/umip.c (revision 92a6c9b95ab10eba66bff3ff44476ab0c015b276)
1 
2 #include "libcflat.h"
3 #include "desc.h"
4 #include "processor.h"
5 
6 
7 /* GP handler to skip over faulting instructions */
8 
9 static unsigned long expected_rip;
10 static int skip_count;
11 static volatile int gp_count;
12 
gp_handler(struct ex_regs * regs)13 static void gp_handler(struct ex_regs *regs)
14 {
15     if (regs->rip == expected_rip) {
16         gp_count++;
17         regs->rip += skip_count;
18     } else {
19         unhandled_exception(regs, false);
20     }
21 }
22 
23 #ifndef __x86_64__
24 #define GP_ASM_MOVE_TO_RIP                  \
25 	"mov" W " $1f, %[expected_rip]\n\t"
26 #else
27 #define GP_ASM_MOVE_TO_RIP                  \
28 	"pushq %%rax\n\t"                   \
29 	"lea 1f(%%rip), %%rax\n\t"          \
30 	"mov %%rax, %[expected_rip]\n\t"    \
31 	"popq %%rax\n\t"
32 #endif
33 
34 #define GP_ASM(stmt, in, clobber)                  \
35     asm volatile (                                 \
36 	  GP_ASM_MOVE_TO_RIP                       \
37           "movl $2f-1f, %[skip_count]\n\t"         \
38           "1: " stmt "\n\t"                        \
39           "2: "                                    \
40           : [expected_rip] "=m" (expected_rip),    \
41             [skip_count] "=m" (skip_count)         \
42           : in : clobber)
43 
do_smsw(void)44 static void do_smsw(void)
45 {
46     gp_count = 0;
47     GP_ASM("smsw %%ax", , "eax");
48 }
49 
do_sldt(void)50 static void do_sldt(void)
51 {
52     gp_count = 0;
53     GP_ASM("sldt %%ax", , "eax");
54 }
55 
do_str(void)56 static void do_str(void)
57 {
58     gp_count = 0;
59     GP_ASM("str %%ax", , "eax");
60 }
61 
do_sgdt(void)62 static void do_sgdt(void)
63 {
64     struct descriptor_table_ptr dt;
65     gp_count = 0;
66     GP_ASM("sgdt %[dt]", [dt]"m"(dt), );
67 }
68 
do_sidt(void)69 static void do_sidt(void)
70 {
71     struct descriptor_table_ptr dt;
72     gp_count = 0;
73     GP_ASM("sidt %[dt]", [dt]"m"(dt), );
74 }
75 
do_movcr(void)76 static void do_movcr(void)
77 {
78     gp_count = 0;
79     GP_ASM("mov %%cr0, %%" R "ax", , "eax");
80 }
81 
test_umip_nogp(const char * msg)82 static void test_umip_nogp(const char *msg)
83 {
84     puts(msg);
85 
86     do_smsw();
87     report(gp_count == 0, "no exception from smsw");
88     do_sgdt();
89     report(gp_count == 0, "no exception from sgdt");
90     do_sidt();
91     report(gp_count == 0, "no exception from sidt");
92     do_sldt();
93     report(gp_count == 0, "no exception from sldt");
94     do_str();
95     report(gp_count == 0, "no exception from str");
96     if (read_cs() & 3) {
97         do_movcr();
98         report(gp_count == 1, "exception from mov %%cr0, %%eax");
99     }
100 }
101 
test_umip_gp(const char * msg)102 static void test_umip_gp(const char *msg)
103 {
104     puts(msg);
105 
106 #if 0
107     /* Skip this, because it cannot be emulated correctly.  */
108     do_smsw();
109     report(gp_count == 1, "exception from smsw");
110 #endif
111     do_sgdt();
112     report(gp_count == 1, "exception from sgdt");
113     do_sidt();
114     report(gp_count == 1, "exception from sidt");
115     do_sldt();
116     report(gp_count == 1, "exception from sldt");
117     do_str();
118     report(gp_count == 1, "exception from str");
119     if (read_cs() & 3) {
120         do_movcr();
121         report(gp_count == 1, "exception from mov %%cr0, %%eax");
122     }
123 }
124 
125 /* The ugly mode switching code */
126 
do_ring3(void (* fn)(const char *),const char * arg)127 static noinline int do_ring3(void (*fn)(const char *), const char *arg)
128 {
129     static unsigned char user_stack[4096];
130     int ret;
131 
132     asm volatile ("mov %[user_ds], %%" R "dx\n\t"
133 		  "mov %%dx, %%ds\n\t"
134 		  "mov %%dx, %%es\n\t"
135 		  "mov %%dx, %%fs\n\t"
136 		  "mov %%dx, %%gs\n\t"
137 		  "mov %%" R "sp, %[sp0]\n\t" /* kernel sp for exception handlers */
138 		  "mov %[sp0], %%" R "bx\n\t" /* ebx/rbx is preserved before and after 'call' instruction */
139 		  "push" W " %%" R "dx \n\t"
140 		  "lea %[user_stack_top], %%" R "dx \n\t"
141 		  "push" W " %%" R "dx \n\t"
142 		  "pushf" W "\n\t"
143 		  "push" W " %[user_cs] \n\t"
144 #ifndef __x86_64__
145 		  "push" W " $1f \n\t"
146 #else
147 		  "lea 1f(%%rip), %%rdx \n\t"
148 		  "pushq %%rdx \n\t"
149 #endif
150 		  "iret" W "\n"
151 		  "1: \n\t"
152 #ifndef __x86_64__
153 		  "push %[arg]\n\t"
154 #endif
155 		  "call *%[fn]\n\t"
156 #ifndef __x86_64__
157 		  "pop %%ecx\n\t"
158 #endif
159 #ifndef __x86_64__
160 		  "mov $1f, %%" R "dx\n\t"
161 #else
162 		  "lea 1f(%%" R "ip), %%" R "dx\n\t"
163 #endif
164 		  "int %[kernel_entry_vector]\n\t"
165 		  ".section .text.entry \n\t"
166 		  "kernel_entry: \n\t"
167 #ifdef __x86_64__
168 		  "mov %%rbx, %%rsp\n\t"
169 #else
170 		  "add $(5 * " S "), %%esp\n\t"
171 #endif
172 		  "mov %[kernel_ds], %%cx\n\t"
173 		  "mov %%cx, %%ds\n\t"
174 		  "mov %%cx, %%es\n\t"
175 		  "mov %%cx, %%fs\n\t"
176 		  "mov %%cx, %%gs\n\t"
177 		  "jmp *%%" R "dx \n\t"
178 		  ".section .text\n\t"
179 		  "1:\n\t"
180 		  : [ret] "=&a" (ret),
181 #ifdef __x86_64__
182 		    [sp0] "=m" (tss[0].rsp0)
183 #else
184 		    [sp0] "=m" (tss[0].esp0)
185 #endif
186 		  : [user_ds] "i" (USER_DS),
187 		    [user_cs] "i" (USER_CS),
188 		    [user_stack_top]"m"(user_stack[sizeof(user_stack) -
189 						   sizeof(long)]),
190 		    [fn]"r"(fn),
191 		    [arg]"D"(arg),
192 		    [kernel_ds]"i"(KERNEL_DS),
193 		    [kernel_entry_vector]"i"(0x20)
194 		  : "rcx", "rdx", "rbx");
195     return ret;
196 }
197 
main(void)198 int main(void)
199 {
200     extern unsigned char kernel_entry;
201 
202     set_idt_entry(0x20, &kernel_entry, 3);
203     handle_exception(13, gp_handler);
204     set_iopl(3);
205 
206     test_umip_nogp("UMIP=0, CPL=0\n");
207     do_ring3(test_umip_nogp, "UMIP=0, CPL=3\n");
208 
209     if (!this_cpu_has(X86_FEATURE_UMIP)) {
210         printf("UMIP not available\n");
211         return report_summary();
212     }
213     write_cr4(read_cr4() | X86_CR4_UMIP);
214 
215     test_umip_nogp("UMIP=1, CPL=0\n");
216     do_ring3(test_umip_gp, "UMIP=1, CPL=3\n");
217 
218     return report_summary();
219 }
220