taskswitch2.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. #include "libcflat.h"
  2. #include "desc.h"
  3. #include "apic-defs.h"
  4. #include "apic.h"
  5. #include "processor.h"
  6. #include "vm.h"
  7. #define MAIN_TSS_SEL (FIRST_SPARE_SEL + 0)
  8. #define VM86_TSS_SEL (FIRST_SPARE_SEL + 8)
  9. #define CONFORM_CS_SEL (FIRST_SPARE_SEL + 16)
  10. static volatile int test_count;
  11. static volatile unsigned int test_divider;
  12. static char *fault_addr;
  13. static ulong fault_phys;
  14. static inline void io_delay(void)
  15. {
  16. }
  17. static void nmi_tss(void)
  18. {
  19. start:
  20. printf("NMI task is running\n");
  21. print_current_tss_info();
  22. test_count++;
  23. asm volatile ("iret");
  24. goto start;
  25. }
  26. static void de_tss(void)
  27. {
  28. start:
  29. printf("DE task is running\n");
  30. print_current_tss_info();
  31. test_divider = 10;
  32. test_count++;
  33. asm volatile ("iret");
  34. goto start;
  35. }
  36. static void of_tss(void)
  37. {
  38. start:
  39. printf("OF task is running\n");
  40. print_current_tss_info();
  41. test_count++;
  42. asm volatile ("iret");
  43. goto start;
  44. }
  45. static void bp_tss(void)
  46. {
  47. start:
  48. printf("BP task is running\n");
  49. print_current_tss_info();
  50. test_count++;
  51. asm volatile ("iret");
  52. goto start;
  53. }
  54. void do_pf_tss(ulong *error_code)
  55. {
  56. printf("PF task is running %p %lx\n", error_code, *error_code);
  57. print_current_tss_info();
  58. if (*error_code == 0x2) /* write access, not present */
  59. test_count++;
  60. install_pte(phys_to_virt(read_cr3()), 1, fault_addr,
  61. fault_phys | PT_PRESENT_MASK | PT_WRITABLE_MASK, 0);
  62. }
  63. extern void pf_tss(void);
  64. asm (
  65. "pf_tss: \n\t"
  66. "push %esp \n\t"
  67. "call do_pf_tss \n\t"
  68. "add $4, %esp \n\t"
  69. "iret\n\t"
  70. "jmp pf_tss\n\t"
  71. );
  72. static void jmp_tss(void)
  73. {
  74. start:
  75. printf("JMP to task succeeded\n");
  76. print_current_tss_info();
  77. test_count++;
  78. asm volatile ("ljmp $" xstr(TSS_MAIN) ", $0");
  79. goto start;
  80. }
  81. static void irq_tss(void)
  82. {
  83. start:
  84. printf("IRQ task is running\n");
  85. print_current_tss_info();
  86. test_count++;
  87. asm volatile ("iret");
  88. test_count++;
  89. printf("IRQ task restarts after iret.\n");
  90. goto start;
  91. }
  92. static void user_tss(void)
  93. {
  94. start:
  95. printf("Conforming task is running\n");
  96. print_current_tss_info();
  97. test_count++;
  98. asm volatile ("iret");
  99. goto start;
  100. }
  101. void test_kernel_mode_int()
  102. {
  103. unsigned int res;
  104. /* test that int $2 triggers task gate */
  105. test_count = 0;
  106. set_intr_task_gate(2, nmi_tss);
  107. printf("Triggering nmi 2\n");
  108. asm volatile ("int $2");
  109. printf("Return from nmi %d\n", test_count);
  110. report("NMI int $2", test_count == 1);
  111. /* test that external NMI triggers task gate */
  112. test_count = 0;
  113. set_intr_task_gate(2, nmi_tss);
  114. printf("Triggering nmi through APIC\n");
  115. apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
  116. io_delay();
  117. printf("Return from APIC nmi\n");
  118. report("NMI external", test_count == 1);
  119. /* test that external interrupt triggesr task gate */
  120. test_count = 0;
  121. printf("Trigger IRQ from APIC\n");
  122. set_intr_task_gate(0xf0, irq_tss);
  123. irq_enable();
  124. apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT | 0xf0, 0);
  125. io_delay();
  126. irq_disable();
  127. printf("Return from APIC IRQ\n");
  128. report("IRQ external", test_count == 1);
  129. /* test that HW exception triggesr task gate */
  130. set_intr_task_gate(0, de_tss);
  131. printf("Try to devide by 0\n");
  132. asm volatile ("divl %3": "=a"(res)
  133. : "d"(0), "a"(1500), "m"(test_divider));
  134. printf("Result is %d\n", res);
  135. report("DE exeption", res == 150);
  136. /* test if call HW exeption DE by int $0 triggers task gate */
  137. test_count = 0;
  138. set_intr_task_gate(0, de_tss);
  139. printf("Call int 0\n");
  140. asm volatile ("int $0");
  141. printf("Return from int 0\n");
  142. report("int $0", test_count == 1);
  143. /* test if HW exception OF triggers task gate */
  144. test_count = 0;
  145. set_intr_task_gate(4, of_tss);
  146. printf("Call into\n");
  147. asm volatile ("addb $127, %b0\ninto"::"a"(127));
  148. printf("Return from into\n");
  149. report("OF exeption", test_count);
  150. /* test if HW exception BP triggers task gate */
  151. test_count = 0;
  152. set_intr_task_gate(3, bp_tss);
  153. printf("Call int 3\n");
  154. asm volatile ("int $3");
  155. printf("Return from int 3\n");
  156. report("BP exeption", test_count == 1);
  157. /*
  158. * test that PF triggers task gate and error code is placed on
  159. * exception task's stack
  160. */
  161. fault_addr = alloc_vpage();
  162. fault_phys = (ulong)virt_to_phys(alloc_page());
  163. test_count = 0;
  164. set_intr_task_gate(14, pf_tss);
  165. printf("Access unmapped page\n");
  166. *fault_addr = 0;
  167. printf("Return from pf tss\n");
  168. report("PF exeption", test_count == 1);
  169. }
  170. void test_gdt_task_gate(void)
  171. {
  172. /* test that calling a task by lcall works */
  173. test_count = 0;
  174. tss_intr.eip = (u32)irq_tss;
  175. printf("Calling task by lcall\n");
  176. /* hlt opcode is 0xf4 I use destination IP 0xf4f4f4f4 to catch
  177. incorrect instruction length calculation */
  178. asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4");
  179. printf("Return from call\n");
  180. report("lcall", test_count == 1);
  181. /* call the same task again and check that it restarted after iret */
  182. test_count = 0;
  183. asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4");
  184. report("lcall2", test_count == 2);
  185. /* test that calling a task by ljmp works */
  186. test_count = 0;
  187. tss_intr.eip = (u32)jmp_tss;
  188. printf("Jumping to a task by ljmp\n");
  189. asm volatile ("ljmp $" xstr(TSS_INTR) ", $0xf4f4f4f4");
  190. printf("Jump back succeeded\n");
  191. report("ljmp", test_count == 1);
  192. }
  193. void test_vm86_switch(void)
  194. {
  195. static tss32_t main_tss;
  196. static tss32_t vm86_tss;
  197. u8 *vm86_start;
  198. /* Write a 'ud2' instruction somewhere below 1 MB */
  199. vm86_start = (void*) 0x42000;
  200. vm86_start[0] = 0x0f;
  201. vm86_start[1] = 0x0b;
  202. /* Main TSS */
  203. set_gdt_entry(MAIN_TSS_SEL, (u32)&main_tss, sizeof(tss32_t) - 1, 0x89, 0);
  204. ltr(MAIN_TSS_SEL);
  205. main_tss = (tss32_t) {
  206. .prev = VM86_TSS_SEL,
  207. .cr3 = read_cr3(),
  208. };
  209. /* VM86 TSS (marked as busy, so we can iret to it) */
  210. set_gdt_entry(VM86_TSS_SEL, (u32)&vm86_tss, sizeof(tss32_t) - 1, 0x8b, 0);
  211. vm86_tss = (tss32_t) {
  212. .eflags = 0x20002,
  213. .cr3 = read_cr3(),
  214. .eip = (u32) vm86_start & 0x0f,
  215. .cs = (u32) vm86_start >> 4,
  216. .ds = 0x1234,
  217. .es = 0x2345,
  218. };
  219. /* Setup task gate to main TSS for #UD */
  220. set_idt_task_gate(6, MAIN_TSS_SEL);
  221. /* Jump into VM86 task with iret, #UD lets it come back immediately */
  222. printf("Switch to VM86 task and back\n");
  223. asm volatile(
  224. "pushf\n"
  225. "orw $0x4000, (%esp)\n"
  226. "popf\n"
  227. "iret\n"
  228. );
  229. report("VM86", 1);
  230. }
  231. #define IOPL_SHIFT 12
  232. void test_conforming_switch(void)
  233. {
  234. /* test lcall with conforming segment, cs.dpl != cs.rpl */
  235. test_count = 0;
  236. tss_intr.cs = CONFORM_CS_SEL | 3;
  237. tss_intr.eip = (u32)user_tss;
  238. tss_intr.ss = USER_DS;
  239. tss_intr.ds = tss_intr.gs = tss_intr.es = tss_intr.fs = tss_intr.ss;
  240. tss_intr.eflags |= 3 << IOPL_SHIFT;
  241. set_gdt_entry(CONFORM_CS_SEL, 0, 0xffffffff, 0x9f, 0xc0);
  242. asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4");
  243. report("lcall with cs.rpl != cs.dpl", test_count == 1);
  244. }
  245. int main()
  246. {
  247. setup_vm();
  248. setup_idt();
  249. setup_tss32();
  250. test_gdt_task_gate();
  251. test_kernel_mode_int();
  252. test_vm86_switch();
  253. test_conforming_switch();
  254. return report_summary();
  255. }