entry_from_vm86.c 2.6 KB

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