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