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