eventinj.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. #include "libcflat.h"
  2. #include "processor.h"
  3. #include "vm.h"
  4. #include "desc.h"
  5. #include "isr.h"
  6. #include "apic.h"
  7. #include "apic-defs.h"
  8. #ifdef __x86_64__
  9. # define R "r"
  10. #else
  11. # define R "e"
  12. #endif
  13. static inline void io_delay(void)
  14. {
  15. }
  16. void apic_self_ipi(u8 v)
  17. {
  18. apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED |
  19. APIC_INT_ASSERT | v, 0);
  20. }
  21. void apic_self_nmi(void)
  22. {
  23. apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
  24. }
  25. #define flush_phys_addr(__s) outl(__s, 0xe4)
  26. #define flush_stack() do { \
  27. int __l; \
  28. flush_phys_addr(virt_to_phys(&__l)); \
  29. } while (0)
  30. extern char isr_iret_ip[];
  31. static void flush_idt_page()
  32. {
  33. struct descriptor_table_ptr ptr;
  34. sidt(&ptr);
  35. flush_phys_addr(virt_to_phys((void*)ptr.base));
  36. }
  37. static volatile unsigned int test_divider;
  38. static volatile int test_count;
  39. ulong stack_phys;
  40. void *stack_va;
  41. void do_pf_tss(void)
  42. {
  43. printf("PF running\n");
  44. install_pte(phys_to_virt(read_cr3()), 1, stack_va,
  45. stack_phys | PT_PRESENT_MASK | PT_WRITABLE_MASK, 0);
  46. invlpg(stack_va);
  47. }
  48. extern void pf_tss(void);
  49. asm ("pf_tss: \n\t"
  50. #ifdef __x86_64__
  51. // no task on x86_64, save/restore caller-save regs
  52. "push %rax; push %rcx; push %rdx; push %rsi; push %rdi\n"
  53. "push %r8; push %r9; push %r10; push %r11\n"
  54. #endif
  55. "call do_pf_tss \n\t"
  56. #ifdef __x86_64__
  57. "pop %r11; pop %r10; pop %r9; pop %r8\n"
  58. "pop %rdi; pop %rsi; pop %rdx; pop %rcx; pop %rax\n"
  59. #endif
  60. "add $"S", %"R "sp\n\t" // discard error code
  61. "iret"W" \n\t"
  62. "jmp pf_tss\n\t"
  63. );
  64. #ifndef __x86_64__
  65. static void of_isr(struct ex_regs *r)
  66. {
  67. printf("OF isr running\n");
  68. test_count++;
  69. }
  70. #endif
  71. static void np_isr(struct ex_regs *r)
  72. {
  73. printf("NP isr running %lx err=%lx\n", r->rip, r->error_code);
  74. set_idt_sel(33, read_cs());
  75. test_count++;
  76. }
  77. static void de_isr(struct ex_regs *r)
  78. {
  79. printf("DE isr running divider is %d\n", test_divider);
  80. test_divider = 10;
  81. }
  82. static void bp_isr(struct ex_regs *r)
  83. {
  84. printf("BP isr running\n");
  85. test_count++;
  86. }
  87. static void nested_nmi_isr(struct ex_regs *r)
  88. {
  89. printf("Nested NMI isr running rip=%lx\n", r->rip);
  90. if (r->rip != (ulong)&isr_iret_ip)
  91. test_count++;
  92. }
  93. static void nmi_isr(struct ex_regs *r)
  94. {
  95. printf("NMI isr running %p\n", &isr_iret_ip);
  96. test_count++;
  97. handle_exception(2, nested_nmi_isr);
  98. printf("Sending nested NMI to self\n");
  99. apic_self_nmi();
  100. io_delay();
  101. printf("After nested NMI to self\n");
  102. }
  103. unsigned long *iret_stack;
  104. static void nested_nmi_iret_isr(struct ex_regs *r)
  105. {
  106. printf("Nested NMI isr running rip=%lx\n", r->rip);
  107. if (r->rip == iret_stack[-3])
  108. test_count++;
  109. }
  110. extern void do_iret(ulong phys_stack, void *virt_stack);
  111. // Return to same privilege level won't pop SS or SP, so
  112. // save it in RDX while we run on the nested stack
  113. asm("do_iret:"
  114. #ifdef __x86_64__
  115. "mov %rdi, %rax \n\t" // phys_stack
  116. "mov %rsi, %rdx \n\t" // virt_stack
  117. #else
  118. "mov 4(%esp), %eax \n\t" // phys_stack
  119. "mov 8(%esp), %edx \n\t" // virt_stack
  120. #endif
  121. "xchg %"R "dx, %"R "sp \n\t" // point to new stack
  122. "pushf"W" \n\t"
  123. "mov %cs, %ecx \n\t"
  124. "push"W" %"R "cx \n\t"
  125. "push"W" $1f \n\t"
  126. "outl %eax, $0xe4 \n\t" // flush page
  127. "iret"W" \n\t"
  128. "1: xchg %"R "dx, %"R "sp \n\t" // point to old stack
  129. "ret\n\t"
  130. );
  131. static void nmi_iret_isr(struct ex_regs *r)
  132. {
  133. unsigned long *s = alloc_page();
  134. test_count++;
  135. printf("NMI isr running stack %p\n", s);
  136. handle_exception(2, nested_nmi_iret_isr);
  137. printf("Sending nested NMI to self\n");
  138. apic_self_nmi();
  139. printf("After nested NMI to self\n");
  140. iret_stack = &s[128];
  141. do_iret(virt_to_phys(s), iret_stack);
  142. printf("After iret\n");
  143. }
  144. static void tirq0(isr_regs_t *r)
  145. {
  146. printf("irq0 running\n");
  147. if (test_count == 1)
  148. test_count++;
  149. eoi();
  150. }
  151. static void tirq1(isr_regs_t *r)
  152. {
  153. printf("irq1 running\n");
  154. test_count++;
  155. eoi();
  156. }
  157. ulong saved_stack;
  158. #define switch_stack(S) do { \
  159. asm volatile ("mov %%" R "sp, %0":"=r"(saved_stack)); \
  160. asm volatile ("mov %0, %%" R "sp"::"r"(S)); \
  161. } while(0)
  162. #define restore_stack() do { \
  163. asm volatile ("mov %0, %%" R "sp"::"r"(saved_stack)); \
  164. } while(0)
  165. int main()
  166. {
  167. unsigned int res;
  168. ulong *pt, *cr3, i;
  169. setup_vm();
  170. setup_idt();
  171. setup_alt_stack();
  172. handle_irq(32, tirq0);
  173. handle_irq(33, tirq1);
  174. /* generate HW exception that will fault on IDT and stack */
  175. handle_exception(0, de_isr);
  176. printf("Try to divide by 0\n");
  177. flush_idt_page();
  178. flush_stack();
  179. asm volatile ("divl %3": "=a"(res)
  180. : "d"(0), "a"(1500), "m"(test_divider));
  181. printf("Result is %d\n", res);
  182. report("DE exception", res == 150);
  183. /* generate soft exception (BP) that will fault on IDT and stack */
  184. test_count = 0;
  185. handle_exception(3, bp_isr);
  186. printf("Try int 3\n");
  187. flush_idt_page();
  188. flush_stack();
  189. asm volatile ("int $3");
  190. printf("After int 3\n");
  191. report("BP exception", test_count == 1);
  192. #ifndef __x86_64__
  193. /* generate soft exception (OF) that will fault on IDT */
  194. test_count = 0;
  195. handle_exception(4, of_isr);
  196. flush_idt_page();
  197. printf("Try into\n");
  198. asm volatile ("addb $127, %b0\ninto"::"a"(127));
  199. printf("After into\n");
  200. report("OF exception", test_count == 1);
  201. /* generate soft exception (OF) using two bit instruction that will
  202. fault on IDT */
  203. test_count = 0;
  204. handle_exception(4, of_isr);
  205. flush_idt_page();
  206. printf("Try into\n");
  207. asm volatile ("addb $127, %b0\naddr16 into"::"a"(127));
  208. printf("After into\n");
  209. report("2 byte OF exception", test_count == 1);
  210. #endif
  211. /* generate HW interrupt that will fault on IDT */
  212. test_count = 0;
  213. flush_idt_page();
  214. printf("Sending vec 33 to self\n");
  215. irq_enable();
  216. apic_self_ipi(33);
  217. io_delay();
  218. irq_disable();
  219. printf("After vec 33 to self\n");
  220. report("vec 33", test_count == 1);
  221. /* generate soft interrupt that will fault on IDT and stack */
  222. test_count = 0;
  223. flush_idt_page();
  224. printf("Try int $33\n");
  225. flush_stack();
  226. asm volatile ("int $33");
  227. printf("After int $33\n");
  228. report("int $33", test_count == 1);
  229. /* Inject two HW interrupt than open iterrupt windows. Both interrupt
  230. will fault on IDT access */
  231. test_count = 0;
  232. flush_idt_page();
  233. printf("Sending vec 32 and 33 to self\n");
  234. apic_self_ipi(32);
  235. apic_self_ipi(33);
  236. io_delay();
  237. irq_enable();
  238. asm volatile("nop");
  239. irq_disable();
  240. printf("After vec 32 and 33 to self\n");
  241. report("vec 32/33", test_count == 2);
  242. /* Inject HW interrupt, do sti and than (while in irq shadow) inject
  243. soft interrupt. Fault during soft interrupt. Soft interrup shoud be
  244. handled before HW interrupt */
  245. test_count = 0;
  246. flush_idt_page();
  247. printf("Sending vec 32 and int $33\n");
  248. apic_self_ipi(32);
  249. flush_stack();
  250. io_delay();
  251. asm volatile ("sti; int $33");
  252. irq_disable();
  253. printf("After vec 32 and int $33\n");
  254. report("vec 32/int $33", test_count == 2);
  255. /* test that TPR is honored */
  256. test_count = 0;
  257. handle_irq(62, tirq1);
  258. flush_idt_page();
  259. printf("Sending vec 33 and 62 and mask one with TPR\n");
  260. apic_write(APIC_TASKPRI, 0xf << 4);
  261. irq_enable();
  262. apic_self_ipi(32);
  263. apic_self_ipi(62);
  264. io_delay();
  265. apic_write(APIC_TASKPRI, 0x2 << 4);
  266. printf("After 33/62 TPR test\n");
  267. report("TPR", test_count == 1);
  268. apic_write(APIC_TASKPRI, 0x0);
  269. while(test_count != 2); /* wait for second irq */
  270. irq_disable();
  271. /* test fault durint NP delivery */
  272. printf("Before NP test\n");
  273. test_count = 0;
  274. handle_exception(11, np_isr);
  275. set_idt_sel(33, NP_SEL);
  276. flush_idt_page();
  277. flush_stack();
  278. asm volatile ("int $33");
  279. printf("After int33\n");
  280. report("NP exception", test_count == 2);
  281. /* generate NMI that will fault on IDT */
  282. test_count = 0;
  283. handle_exception(2, nmi_isr);
  284. flush_idt_page();
  285. printf("Sending NMI to self\n");
  286. apic_self_nmi();
  287. printf("After NMI to self\n");
  288. /* this is needed on VMX without NMI window notification.
  289. Interrupt windows is used instead, so let pending NMI
  290. to be injected */
  291. irq_enable();
  292. asm volatile ("nop");
  293. irq_disable();
  294. report("NMI", test_count == 2);
  295. /* generate NMI that will fault on IRET */
  296. printf("Before NMI IRET test\n");
  297. test_count = 0;
  298. handle_exception(2, nmi_iret_isr);
  299. printf("Sending NMI to self\n");
  300. apic_self_nmi();
  301. /* this is needed on VMX without NMI window notification.
  302. Interrupt windows is used instead, so let pending NMI
  303. to be injected */
  304. irq_enable();
  305. asm volatile ("nop");
  306. irq_disable();
  307. printf("After NMI to self\n");
  308. report("NMI", test_count == 2);
  309. stack_phys = (ulong)virt_to_phys(alloc_page());
  310. stack_va = alloc_vpage();
  311. /* Generate DE and PF exceptions serially */
  312. test_divider = 0;
  313. set_intr_alt_stack(14, pf_tss);
  314. handle_exception(0, de_isr);
  315. printf("Try to divide by 0\n");
  316. /* install read only pte */
  317. install_pte(phys_to_virt(read_cr3()), 1, stack_va,
  318. stack_phys | PT_PRESENT_MASK, 0);
  319. invlpg(stack_va);
  320. flush_phys_addr(stack_phys);
  321. switch_stack(stack_va + 4095);
  322. flush_idt_page();
  323. asm volatile ("divl %3": "=a"(res)
  324. : "d"(0), "a"(1500), "m"(test_divider));
  325. restore_stack();
  326. printf("Result is %d\n", res);
  327. report("DE PF exceptions", res == 150);
  328. /* Generate NP and PF exceptions serially */
  329. printf("Before NP test\n");
  330. test_count = 0;
  331. set_intr_alt_stack(14, pf_tss);
  332. handle_exception(11, np_isr);
  333. set_idt_sel(33, NP_SEL);
  334. /* install read only pte */
  335. install_pte(phys_to_virt(read_cr3()), 1, stack_va,
  336. stack_phys | PT_PRESENT_MASK, 0);
  337. invlpg(stack_va);
  338. flush_idt_page();
  339. flush_phys_addr(stack_phys);
  340. switch_stack(stack_va + 4095);
  341. asm volatile ("int $33");
  342. restore_stack();
  343. printf("After int33\n");
  344. report("NP PF exceptions", test_count == 2);
  345. pt = alloc_page();
  346. cr3 = (void*)read_cr3();
  347. memset(pt, 0, 4096);
  348. /* use shadowed stack during interrupt delivery */
  349. for (i = 0; i < 4096/sizeof(ulong); i++) {
  350. if (!cr3[i]) {
  351. cr3[i] = virt_to_phys(pt) | PT_PRESENT_MASK | PT_WRITABLE_MASK;
  352. pt[0] = virt_to_phys(pt) | PT_PRESENT_MASK | PT_WRITABLE_MASK;
  353. #ifndef __x86_64__
  354. ((ulong*)(i<<22))[1] = 0;
  355. #else
  356. ((ulong*)(i<<39))[1] = 0;
  357. #endif
  358. write_cr3(virt_to_phys(cr3));
  359. break;
  360. }
  361. }
  362. test_count = 0;
  363. printf("Try int 33 with shadowed stack\n");
  364. switch_stack(((char*)pt) + 4095);
  365. asm volatile("int $33");
  366. restore_stack();
  367. printf("After int 33 with shadowed stack\n");
  368. report("int 33 with shadowed stack", test_count == 1);
  369. return report_summary();
  370. }