1 // SPDX-License-Identifier: GPL-2.0-only
3 * entry_from_vm86.c - tests kernel entries from vm86 mode
4 * Copyright (c) 2014-2015 Andrew Lutomirski
6 * This exercises a few paths that need to special-case vm86 mode.
13 #include <sys/syscall.h>
14 #include <sys/signal.h>
15 #include <sys/ucontext.h>
27 static unsigned long load_addr
= 0x10000;
30 static void sethandler(int sig
, void (*handler
)(int, siginfo_t
*, void *),
34 memset(&sa
, 0, sizeof(sa
));
35 sa
.sa_sigaction
= handler
;
36 sa
.sa_flags
= SA_SIGINFO
| flags
;
37 sigemptyset(&sa
.sa_mask
);
38 if (sigaction(sig
, &sa
, 0))
42 static void clearhandler(int sig
)
45 memset(&sa
, 0, sizeof(sa
));
46 sa
.sa_handler
= SIG_DFL
;
47 sigemptyset(&sa
.sa_mask
);
48 if (sigaction(sig
, &sa
, 0))
52 static sig_atomic_t got_signal
;
54 static void sighandler(int sig
, siginfo_t
*info
, void *ctx_void
)
56 ucontext_t
*ctx
= (ucontext_t
*)ctx_void
;
58 if (ctx
->uc_mcontext
.gregs
[REG_EFL
] & X86_EFLAGS_VM
||
59 (ctx
->uc_mcontext
.gregs
[REG_CS
] & 3) != 3) {
60 printf("[FAIL]\tSignal frame should not reflect vm86 mode\n");
67 else if (sig
== SIGILL
)
70 signame
= "unexpected signal";
72 printf("[INFO]\t%s: FLAGS = 0x%lx, CS = 0x%hx\n", signame
,
73 (unsigned long)ctx
->uc_mcontext
.gregs
[REG_EFL
],
74 (unsigned short)ctx
->uc_mcontext
.gregs
[REG_CS
]);
80 ".pushsection .rodata\n\t"
81 ".type vmcode_bound, @object\n\t"
85 "bound %ax, (2048)\n\t"
87 "vmcode_sysenter:\n\t"
97 "vmcode_popf_hlt:\n\t"
102 /* addressing via displacements */
106 /* addressing via registers */
113 /* register operands, only for smsw */
115 "mov %ax, (2080)\n\t"
117 "vmcode_umip_str:\n\t"
119 "vmcode_umip_sldt:\n\t"
122 ".size vmcode, . - vmcode\n\t"
128 extern unsigned char vmcode
[], end_vmcode
[];
129 extern unsigned char vmcode_bound
[], vmcode_sysenter
[], vmcode_syscall
[],
130 vmcode_sti
[], vmcode_int3
[], vmcode_int80
[], vmcode_popf_hlt
[],
131 vmcode_umip
[], vmcode_umip_str
[], vmcode_umip_sldt
[];
133 /* Returns false if the test was skipped. */
134 static bool do_test(struct vm86plus_struct
*v86
, unsigned long eip
,
135 unsigned int rettype
, unsigned int retarg
,
140 printf("[RUN]\t%s from vm86 mode\n", text
);
142 ret
= vm86(VM86_ENTER
, v86
);
144 if (ret
== -1 && (errno
== ENOSYS
|| errno
== EPERM
)) {
145 printf("[SKIP]\tvm86 %s\n",
146 errno
== ENOSYS
? "not supported" : "not allowed");
150 if (VM86_TYPE(ret
) == VM86_INTx
) {
152 int trapno
= VM86_ARG(ret
);
154 strcpy(trapname
, "GP");
155 else if (trapno
== 5)
156 strcpy(trapname
, "BR");
157 else if (trapno
== 14)
158 strcpy(trapname
, "PF");
160 sprintf(trapname
, "%d", trapno
);
162 printf("[INFO]\tExited vm86 mode due to #%s\n", trapname
);
163 } else if (VM86_TYPE(ret
) == VM86_UNKNOWN
) {
164 printf("[INFO]\tExited vm86 mode due to unhandled GP fault\n");
165 } else if (VM86_TYPE(ret
) == VM86_TRAP
) {
166 printf("[INFO]\tExited vm86 mode due to a trap (arg=%ld)\n",
168 } else if (VM86_TYPE(ret
) == VM86_SIGNAL
) {
169 printf("[INFO]\tExited vm86 mode due to a signal\n");
170 } else if (VM86_TYPE(ret
) == VM86_STI
) {
171 printf("[INFO]\tExited vm86 mode due to STI\n");
173 printf("[INFO]\tExited vm86 mode due to type %ld, arg %ld\n",
174 VM86_TYPE(ret
), VM86_ARG(ret
));
178 (VM86_TYPE(ret
) == rettype
&& VM86_ARG(ret
) == retarg
)) {
179 printf("[OK]\tReturned correctly\n");
181 printf("[FAIL]\tIncorrect return reason (started at eip = 0x%lx, ended at eip = 0x%lx)\n", eip
, v86
->regs
.eip
);
188 void do_umip_tests(struct vm86plus_struct
*vm86
, unsigned char *test_mem
)
191 unsigned short limit
;
193 } __attribute__((packed
));
195 /* Initialize variables with arbitrary values */
196 struct table_desc gdt1
= { .base
= 0x3c3c3c3c, .limit
= 0x9999 };
197 struct table_desc gdt2
= { .base
= 0x1a1a1a1a, .limit
= 0xaeae };
198 struct table_desc idt1
= { .base
= 0x7b7b7b7b, .limit
= 0xf1f1 };
199 struct table_desc idt2
= { .base
= 0x89898989, .limit
= 0x1313 };
200 unsigned short msw1
= 0x1414, msw2
= 0x2525, msw3
= 3737;
202 /* UMIP -- exit with INT3 unless kernel emulation did not trap #GP */
203 do_test(vm86
, vmcode_umip
- vmcode
, VM86_TRAP
, 3, "UMIP tests");
205 /* Results from displacement-only addressing */
206 msw1
= *(unsigned short *)(test_mem
+ 2052);
207 memcpy(&idt1
, test_mem
+ 2054, sizeof(idt1
));
208 memcpy(&gdt1
, test_mem
+ 2060, sizeof(gdt1
));
210 /* Results from register-indirect addressing */
211 msw2
= *(unsigned short *)(test_mem
+ 2066);
212 memcpy(&idt2
, test_mem
+ 2068, sizeof(idt2
));
213 memcpy(&gdt2
, test_mem
+ 2074, sizeof(gdt2
));
215 /* Results when using register operands */
216 msw3
= *(unsigned short *)(test_mem
+ 2080);
218 printf("[INFO]\tResult from SMSW:[0x%04x]\n", msw1
);
219 printf("[INFO]\tResult from SIDT: limit[0x%04x]base[0x%08lx]\n",
220 idt1
.limit
, idt1
.base
);
221 printf("[INFO]\tResult from SGDT: limit[0x%04x]base[0x%08lx]\n",
222 gdt1
.limit
, gdt1
.base
);
224 if (msw1
!= msw2
|| msw1
!= msw3
)
225 printf("[FAIL]\tAll the results of SMSW should be the same.\n");
227 printf("[PASS]\tAll the results from SMSW are identical.\n");
229 if (memcmp(&gdt1
, &gdt2
, sizeof(gdt1
)))
230 printf("[FAIL]\tAll the results of SGDT should be the same.\n");
232 printf("[PASS]\tAll the results from SGDT are identical.\n");
234 if (memcmp(&idt1
, &idt2
, sizeof(idt1
)))
235 printf("[FAIL]\tAll the results of SIDT should be the same.\n");
237 printf("[PASS]\tAll the results from SIDT are identical.\n");
239 sethandler(SIGILL
, sighandler
, 0);
240 do_test(vm86
, vmcode_umip_str
- vmcode
, VM86_SIGNAL
, 0,
242 clearhandler(SIGILL
);
244 sethandler(SIGILL
, sighandler
, 0);
245 do_test(vm86
, vmcode_umip_sldt
- vmcode
, VM86_SIGNAL
, 0,
247 clearhandler(SIGILL
);
252 struct vm86plus_struct v86
;
253 unsigned char *addr
= mmap((void *)load_addr
, 4096,
254 PROT_READ
| PROT_WRITE
| PROT_EXEC
,
255 MAP_ANONYMOUS
| MAP_PRIVATE
, -1,0);
256 if (addr
!= (unsigned char *)load_addr
)
259 memcpy(addr
, vmcode
, end_vmcode
- vmcode
);
263 memset(&v86
, 0, sizeof(v86
));
265 v86
.regs
.cs
= load_addr
/ 16;
266 v86
.regs
.ss
= load_addr
/ 16;
267 v86
.regs
.ds
= load_addr
/ 16;
268 v86
.regs
.es
= load_addr
/ 16;
270 /* Use the end of the page as our stack. */
273 assert((v86
.regs
.cs
& 3) == 0); /* Looks like RPL = 0 */
275 /* #BR -- should deliver SIG??? */
276 do_test(&v86
, vmcode_bound
- vmcode
, VM86_INTx
, 5, "#BR");
279 * SYSENTER -- should cause #GP or #UD depending on CPU.
280 * Expected return type -1 means that we shouldn't validate
281 * the vm86 return value. This will avoid problems on non-SEP
284 sethandler(SIGILL
, sighandler
, 0);
285 do_test(&v86
, vmcode_sysenter
- vmcode
, -1, 0, "SYSENTER");
286 clearhandler(SIGILL
);
289 * SYSCALL would be a disaster in VM86 mode. Fortunately,
290 * there is no kernel that both enables SYSCALL and sets
291 * EFER.SCE, so it's #UD on all systems. But vm86 is
292 * buggy (or has a "feature"), so the SIGILL will actually
295 sethandler(SIGILL
, sighandler
, 0);
296 do_test(&v86
, vmcode_syscall
- vmcode
, VM86_SIGNAL
, 0, "SYSCALL");
297 clearhandler(SIGILL
);
299 /* STI with VIP set */
300 v86
.regs
.eflags
|= X86_EFLAGS_VIP
;
301 v86
.regs
.eflags
&= ~X86_EFLAGS_IF
;
302 do_test(&v86
, vmcode_sti
- vmcode
, VM86_STI
, 0, "STI with VIP set");
304 /* POPF with VIP set but IF clear: should not trap */
305 v86
.regs
.eflags
= X86_EFLAGS_VIP
;
307 do_test(&v86
, vmcode_popf_hlt
- vmcode
, VM86_UNKNOWN
, 0, "POPF with VIP set and IF clear");
309 /* POPF with VIP set and IF set: should trap */
310 v86
.regs
.eflags
= X86_EFLAGS_VIP
;
311 v86
.regs
.eax
= X86_EFLAGS_IF
;
312 do_test(&v86
, vmcode_popf_hlt
- vmcode
, VM86_STI
, 0, "POPF with VIP and IF set");
314 /* POPF with VIP clear and IF set: should not trap */
316 v86
.regs
.eax
= X86_EFLAGS_IF
;
317 do_test(&v86
, vmcode_popf_hlt
- vmcode
, VM86_UNKNOWN
, 0, "POPF with VIP clear and IF set");
321 /* INT3 -- should cause #BP */
322 do_test(&v86
, vmcode_int3
- vmcode
, VM86_TRAP
, 3, "INT3");
324 /* INT80 -- should exit with "INTx 0x80" */
325 v86
.regs
.eax
= (unsigned int)-1;
326 do_test(&v86
, vmcode_int80
- vmcode
, VM86_INTx
, 0x80, "int80");
328 /* UMIP -- should exit with INTx 0x80 unless UMIP was not disabled */
329 do_umip_tests(&v86
, addr
);
331 /* Execute a null pointer */
334 sethandler(SIGSEGV
, sighandler
, 0);
336 if (do_test(&v86
, 0, VM86_SIGNAL
, 0, "Execute null pointer") &&
338 printf("[FAIL]\tDid not receive SIGSEGV\n");
341 clearhandler(SIGSEGV
);
343 /* Make sure nothing explodes if we fork. */
347 return (nerrs
== 0 ? 0 : 1);