xref: /kvm-unit-tests/x86/umip.c (revision 24a4a7c96a3b1c60d0785118a7ba61c442d621df)
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 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, %%" R "cx\n\t"
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 		  "push %%" R "cx\n\t"   /* save kernel SP */
137 
138 #ifndef __x86_64__
139 		  "push %[arg]\n\t"
140 #endif
141 		  "call *%[fn]\n\t"
142 #ifndef __x86_64__
143 		  "pop %%ecx\n\t"
144 #endif
145 
146 		  "pop %%" R "cx\n\t"
147 		  "mov $1f, %%" R "dx\n\t"
148 		  "int %[kernel_entry_vector]\n\t"
149 		  ".section .text.entry \n\t"
150 		  "kernel_entry: \n\t"
151 		  "mov %%" R "cx, %%" R "sp \n\t"
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 		  : [user_ds] "i" (USER_DS),
162 		    [user_cs] "i" (USER_CS),
163 		    [user_stack_top]"m"(user_stack[sizeof(user_stack) -
164 						   sizeof(long)]),
165 		    [fn]"r"(fn),
166 		    [arg]"D"(arg),
167 		    [kernel_ds]"i"(KERNEL_DS),
168 		    [kernel_entry_vector]"i"(0x20)
169 		  : "rcx", "rdx");
170     return ret;
171 }
172 
173 int main(void)
174 {
175     extern unsigned char kernel_entry;
176 
177     set_idt_entry(0x20, &kernel_entry, 3);
178     handle_exception(13, gp_handler);
179     set_iopl(3);
180 
181     test_umip_nogp("UMIP=0, CPL=0\n");
182     do_ring3(test_umip_nogp, "UMIP=0, CPL=3\n");
183 
184     if (!this_cpu_has(X86_FEATURE_UMIP)) {
185         printf("UMIP not available\n");
186         return report_summary();
187     }
188     write_cr4(read_cr4() | X86_CR4_UMIP);
189 
190     test_umip_nogp("UMIP=1, CPL=0\n");
191     do_ring3(test_umip_gp, "UMIP=1, CPL=3\n");
192 
193     return report_summary();
194 }
195