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