1 /* 2 * entry_from_vm86.c - tests kernel entries from vm86 mode 3 * Copyright (c) 2014-2015 Andrew Lutomirski 4 * 5 * This exercises a few paths that need to special-case vm86 mode. 6 * 7 * GPL v2. 8 */ 9 10 #define _GNU_SOURCE 11 12 #include <assert.h> 13 #include <stdlib.h> 14 #include <sys/syscall.h> 15 #include <sys/signal.h> 16 #include <sys/ucontext.h> 17 #include <unistd.h> 18 #include <stdio.h> 19 #include <string.h> 20 #include <inttypes.h> 21 #include <sys/mman.h> 22 #include <err.h> 23 #include <stddef.h> 24 #include <stdbool.h> 25 #include <errno.h> 26 #include <sys/vm86.h> 27 28 static unsigned long load_addr = 0x10000; 29 static int nerrs = 0; 30 31 asm ( 32 ".pushsection .rodata\n\t" 33 ".type vmcode_bound, @object\n\t" 34 "vmcode:\n\t" 35 "vmcode_bound:\n\t" 36 ".code16\n\t" 37 "bound %ax, (2048)\n\t" 38 "int3\n\t" 39 "vmcode_sysenter:\n\t" 40 "sysenter\n\t" 41 ".size vmcode, . - vmcode\n\t" 42 "end_vmcode:\n\t" 43 ".code32\n\t" 44 ".popsection" 45 ); 46 47 extern unsigned char vmcode[], end_vmcode[]; 48 extern unsigned char vmcode_bound[], vmcode_sysenter[]; 49 50 static void do_test(struct vm86plus_struct *v86, unsigned long eip, 51 const char *text) 52 { 53 long ret; 54 55 printf("[RUN]\t%s from vm86 mode\n", text); 56 v86->regs.eip = eip; 57 ret = vm86(VM86_ENTER, v86); 58 59 if (ret == -1 && errno == ENOSYS) { 60 printf("[SKIP]\tvm86 not supported\n"); 61 return; 62 } 63 64 if (VM86_TYPE(ret) == VM86_INTx) { 65 char trapname[32]; 66 int trapno = VM86_ARG(ret); 67 if (trapno == 13) 68 strcpy(trapname, "GP"); 69 else if (trapno == 5) 70 strcpy(trapname, "BR"); 71 else if (trapno == 14) 72 strcpy(trapname, "PF"); 73 else 74 sprintf(trapname, "%d", trapno); 75 76 printf("[OK]\tExited vm86 mode due to #%s\n", trapname); 77 } else if (VM86_TYPE(ret) == VM86_UNKNOWN) { 78 printf("[OK]\tExited vm86 mode due to unhandled GP fault\n"); 79 } else { 80 printf("[OK]\tExited vm86 mode due to type %ld, arg %ld\n", 81 VM86_TYPE(ret), VM86_ARG(ret)); 82 } 83 } 84 85 int main(void) 86 { 87 struct vm86plus_struct v86; 88 unsigned char *addr = mmap((void *)load_addr, 4096, 89 PROT_READ | PROT_WRITE | PROT_EXEC, 90 MAP_ANONYMOUS | MAP_PRIVATE, -1,0); 91 if (addr != (unsigned char *)load_addr) 92 err(1, "mmap"); 93 94 memcpy(addr, vmcode, end_vmcode - vmcode); 95 addr[2048] = 2; 96 addr[2050] = 3; 97 98 memset(&v86, 0, sizeof(v86)); 99 100 v86.regs.cs = load_addr / 16; 101 v86.regs.ss = load_addr / 16; 102 v86.regs.ds = load_addr / 16; 103 v86.regs.es = load_addr / 16; 104 105 assert((v86.regs.cs & 3) == 0); /* Looks like RPL = 0 */ 106 107 /* #BR -- should deliver SIG??? */ 108 do_test(&v86, vmcode_bound - vmcode, "#BR"); 109 110 /* SYSENTER -- should cause #GP or #UD depending on CPU */ 111 do_test(&v86, vmcode_sysenter - vmcode, "SYSENTER"); 112 113 return (nerrs == 0 ? 0 : 1); 114 } 115