ioapic.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. #include "libcflat.h"
  2. #include "apic.h"
  3. #include "vm.h"
  4. #include "smp.h"
  5. #include "desc.h"
  6. #include "isr.h"
  7. static void set_ioapic_redir(unsigned line, unsigned vec,
  8. trigger_mode_t trig_mode)
  9. {
  10. ioapic_redir_entry_t e = {
  11. .vector = vec,
  12. .delivery_mode = 0,
  13. .trig_mode = trig_mode,
  14. };
  15. ioapic_write_redir(line, e);
  16. }
  17. static void set_irq_line(unsigned line, int val)
  18. {
  19. asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line)));
  20. }
  21. static void toggle_irq_line(unsigned line)
  22. {
  23. set_irq_line(line, 1);
  24. set_irq_line(line, 0);
  25. }
  26. static void ioapic_reg_version(void)
  27. {
  28. u8 version_offset;
  29. uint32_t data_read, data_write;
  30. version_offset = 0x01;
  31. data_read = ioapic_read_reg(version_offset);
  32. data_write = data_read ^ 0xffffffff;
  33. ioapic_write_reg(version_offset, data_write);
  34. report("version register read only test",
  35. data_read == ioapic_read_reg(version_offset));
  36. }
  37. static void ioapic_reg_id(void)
  38. {
  39. u8 id_offset;
  40. uint32_t data_read, data_write, diff;
  41. id_offset = 0x0;
  42. data_read = ioapic_read_reg(id_offset);
  43. data_write = data_read ^ 0xffffffff;
  44. ioapic_write_reg(id_offset, data_write);
  45. diff = data_read ^ ioapic_read_reg(id_offset);
  46. report("id register only bits [24:27] writable",
  47. diff == 0x0f000000);
  48. }
  49. static void ioapic_arbitration_id(void)
  50. {
  51. u8 id_offset, arb_offset;
  52. uint32_t write;
  53. id_offset = 0x0;
  54. arb_offset = 0x2;
  55. write = 0x0f000000;
  56. ioapic_write_reg(id_offset, write);
  57. report("arbitration register set by id",
  58. ioapic_read_reg(arb_offset) == write);
  59. ioapic_write_reg(arb_offset, 0x0);
  60. report("arbtration register read only",
  61. ioapic_read_reg(arb_offset) == write);
  62. }
  63. static volatile int g_isr_76;
  64. static void ioapic_isr_76(isr_regs_t *regs)
  65. {
  66. ++g_isr_76;
  67. eoi();
  68. }
  69. static void test_ioapic_edge_intr(void)
  70. {
  71. handle_irq(0x76, ioapic_isr_76);
  72. set_ioapic_redir(0x0e, 0x76, TRIGGER_EDGE);
  73. toggle_irq_line(0x0e);
  74. asm volatile ("nop");
  75. report("edge triggered intr", g_isr_76 == 1);
  76. }
  77. static volatile int g_isr_77;
  78. static void ioapic_isr_77(isr_regs_t *regs)
  79. {
  80. ++g_isr_77;
  81. set_irq_line(0x0e, 0);
  82. eoi();
  83. }
  84. static void test_ioapic_level_intr(void)
  85. {
  86. handle_irq(0x77, ioapic_isr_77);
  87. set_ioapic_redir(0x0e, 0x77, TRIGGER_LEVEL);
  88. set_irq_line(0x0e, 1);
  89. asm volatile ("nop");
  90. report("level triggered intr", g_isr_77 == 1);
  91. }
  92. static int g_78, g_66, g_66_after_78;
  93. static ulong g_66_rip, g_78_rip;
  94. static void ioapic_isr_78(isr_regs_t *regs)
  95. {
  96. ++g_78;
  97. g_78_rip = regs->rip;
  98. eoi();
  99. }
  100. static void ioapic_isr_66(isr_regs_t *regs)
  101. {
  102. ++g_66;
  103. if (g_78)
  104. ++g_66_after_78;
  105. g_66_rip = regs->rip;
  106. eoi();
  107. }
  108. static void test_ioapic_simultaneous(void)
  109. {
  110. handle_irq(0x78, ioapic_isr_78);
  111. handle_irq(0x66, ioapic_isr_66);
  112. set_ioapic_redir(0x0e, 0x78, TRIGGER_EDGE);
  113. set_ioapic_redir(0x0f, 0x66, TRIGGER_EDGE);
  114. irq_disable();
  115. toggle_irq_line(0x0f);
  116. toggle_irq_line(0x0e);
  117. irq_enable();
  118. asm volatile ("nop");
  119. report("ioapic simultaneous edge interrupts",
  120. g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip);
  121. }
  122. static volatile int g_tmr_79 = -1;
  123. static void ioapic_isr_79(isr_regs_t *regs)
  124. {
  125. g_tmr_79 = apic_read_bit(APIC_TMR, 0x79);
  126. set_irq_line(0x0e, 0);
  127. eoi();
  128. }
  129. static void test_ioapic_edge_tmr(bool expected_tmr_before)
  130. {
  131. int tmr_before;
  132. handle_irq(0x79, ioapic_isr_79);
  133. set_ioapic_redir(0x0e, 0x79, TRIGGER_EDGE);
  134. tmr_before = apic_read_bit(APIC_TMR, 0x79);
  135. toggle_irq_line(0x0e);
  136. asm volatile ("nop");
  137. report("TMR for ioapic edge interrupts (expected %s)",
  138. tmr_before == expected_tmr_before && !g_tmr_79,
  139. expected_tmr_before ? "true" : "false");
  140. }
  141. static void test_ioapic_level_tmr(bool expected_tmr_before)
  142. {
  143. int tmr_before;
  144. handle_irq(0x79, ioapic_isr_79);
  145. set_ioapic_redir(0x0e, 0x79, TRIGGER_LEVEL);
  146. tmr_before = apic_read_bit(APIC_TMR, 0x79);
  147. set_irq_line(0x0e, 1);
  148. asm volatile ("nop");
  149. report("TMR for ioapic level interrupts (expected %s)",
  150. tmr_before == expected_tmr_before && g_tmr_79,
  151. expected_tmr_before ? "true" : "false");
  152. }
  153. #define IPI_DELAY 1000000
  154. static void delay(int count)
  155. {
  156. while(count--) asm("");
  157. }
  158. static void toggle_irq_line_0x0e(void *data)
  159. {
  160. irq_disable();
  161. delay(IPI_DELAY);
  162. toggle_irq_line(0x0e);
  163. irq_enable();
  164. }
  165. static void test_ioapic_edge_tmr_smp(bool expected_tmr_before)
  166. {
  167. int tmr_before;
  168. int i;
  169. g_tmr_79 = -1;
  170. handle_irq(0x79, ioapic_isr_79);
  171. set_ioapic_redir(0x0e, 0x79, TRIGGER_EDGE);
  172. tmr_before = apic_read_bit(APIC_TMR, 0x79);
  173. on_cpu_async(1, toggle_irq_line_0x0e, 0);
  174. i = 0;
  175. while(g_tmr_79 == -1) i++;
  176. printf("%d iterations before interrupt received\n", i);
  177. report("TMR for ioapic edge interrupts (expected %s)",
  178. tmr_before == expected_tmr_before && !g_tmr_79,
  179. expected_tmr_before ? "true" : "false");
  180. }
  181. static void set_irq_line_0x0e(void *data)
  182. {
  183. irq_disable();
  184. delay(IPI_DELAY);
  185. set_irq_line(0x0e, 1);
  186. irq_enable();
  187. }
  188. static void test_ioapic_level_tmr_smp(bool expected_tmr_before)
  189. {
  190. int i, tmr_before;
  191. g_tmr_79 = -1;
  192. handle_irq(0x79, ioapic_isr_79);
  193. set_ioapic_redir(0x0e, 0x79, TRIGGER_LEVEL);
  194. tmr_before = apic_read_bit(APIC_TMR, 0x79);
  195. on_cpu_async(1, set_irq_line_0x0e, 0);
  196. i = 0;
  197. while(g_tmr_79 == -1) i++;
  198. printf("%d iterations before interrupt received\n", i);
  199. report("TMR for ioapic level interrupts (expected %s)",
  200. tmr_before == expected_tmr_before && g_tmr_79,
  201. expected_tmr_before ? "true" : "false");
  202. }
  203. static int g_isr_98;
  204. static void ioapic_isr_98(isr_regs_t *regs)
  205. {
  206. ++g_isr_98;
  207. if (g_isr_98 == 1) {
  208. set_irq_line(0x0e, 0);
  209. set_irq_line(0x0e, 1);
  210. }
  211. set_irq_line(0x0e, 0);
  212. eoi();
  213. }
  214. static void test_ioapic_level_coalesce(void)
  215. {
  216. handle_irq(0x98, ioapic_isr_98);
  217. set_ioapic_redir(0x0e, 0x98, TRIGGER_LEVEL);
  218. set_irq_line(0x0e, 1);
  219. asm volatile ("nop");
  220. report("coalesce simultaneous level interrupts", g_isr_98 == 1);
  221. }
  222. static int g_isr_99;
  223. static void ioapic_isr_99(isr_regs_t *regs)
  224. {
  225. ++g_isr_99;
  226. set_irq_line(0x0e, 0);
  227. eoi();
  228. }
  229. static void test_ioapic_level_sequential(void)
  230. {
  231. handle_irq(0x99, ioapic_isr_99);
  232. set_ioapic_redir(0x0e, 0x99, TRIGGER_LEVEL);
  233. set_irq_line(0x0e, 1);
  234. set_irq_line(0x0e, 1);
  235. asm volatile ("nop");
  236. report("sequential level interrupts", g_isr_99 == 2);
  237. }
  238. static volatile int g_isr_9a;
  239. static void ioapic_isr_9a(isr_regs_t *regs)
  240. {
  241. ++g_isr_9a;
  242. if (g_isr_9a == 2)
  243. set_irq_line(0x0e, 0);
  244. eoi();
  245. }
  246. static void test_ioapic_level_retrigger(void)
  247. {
  248. int i;
  249. handle_irq(0x9a, ioapic_isr_9a);
  250. set_ioapic_redir(0x0e, 0x9a, TRIGGER_LEVEL);
  251. asm volatile ("cli");
  252. set_irq_line(0x0e, 1);
  253. for (i = 0; i < 10; i++) {
  254. if (g_isr_9a == 2)
  255. break;
  256. asm volatile ("sti; hlt; cli");
  257. }
  258. asm volatile ("sti");
  259. report("retriggered level interrupts without masking", g_isr_9a == 2);
  260. }
  261. static volatile int g_isr_81;
  262. static void ioapic_isr_81(isr_regs_t *regs)
  263. {
  264. ++g_isr_81;
  265. set_irq_line(0x0e, 0);
  266. eoi();
  267. }
  268. static void test_ioapic_edge_mask(void)
  269. {
  270. handle_irq(0x81, ioapic_isr_81);
  271. set_ioapic_redir(0x0e, 0x81, TRIGGER_EDGE);
  272. set_mask(0x0e, true);
  273. set_irq_line(0x0e, 1);
  274. set_irq_line(0x0e, 0);
  275. asm volatile ("nop");
  276. report("masked level interrupt", g_isr_81 == 0);
  277. set_mask(0x0e, false);
  278. set_irq_line(0x0e, 1);
  279. asm volatile ("nop");
  280. report("unmasked level interrupt", g_isr_81 == 1);
  281. }
  282. static volatile int g_isr_82;
  283. static void ioapic_isr_82(isr_regs_t *regs)
  284. {
  285. ++g_isr_82;
  286. set_irq_line(0x0e, 0);
  287. eoi();
  288. }
  289. static void test_ioapic_level_mask(void)
  290. {
  291. handle_irq(0x82, ioapic_isr_82);
  292. set_ioapic_redir(0x0e, 0x82, TRIGGER_LEVEL);
  293. set_mask(0x0e, true);
  294. set_irq_line(0x0e, 1);
  295. asm volatile ("nop");
  296. report("masked level interrupt", g_isr_82 == 0);
  297. set_mask(0x0e, false);
  298. asm volatile ("nop");
  299. report("unmasked level interrupt", g_isr_82 == 1);
  300. }
  301. static volatile int g_isr_83;
  302. static void ioapic_isr_83(isr_regs_t *regs)
  303. {
  304. ++g_isr_83;
  305. set_mask(0x0e, true);
  306. eoi();
  307. }
  308. static void test_ioapic_level_retrigger_mask(void)
  309. {
  310. handle_irq(0x83, ioapic_isr_83);
  311. set_ioapic_redir(0x0e, 0x83, TRIGGER_LEVEL);
  312. set_irq_line(0x0e, 1);
  313. asm volatile ("nop");
  314. set_mask(0x0e, false);
  315. asm volatile ("nop");
  316. report("retriggered level interrupts with mask", g_isr_83 == 2);
  317. set_irq_line(0x0e, 0);
  318. set_mask(0x0e, false);
  319. }
  320. int main(void)
  321. {
  322. setup_vm();
  323. smp_init();
  324. mask_pic_interrupts();
  325. if (enable_x2apic())
  326. printf("x2apic enabled\n");
  327. else
  328. printf("x2apic not detected\n");
  329. irq_enable();
  330. ioapic_reg_version();
  331. ioapic_reg_id();
  332. ioapic_arbitration_id();
  333. test_ioapic_edge_intr();
  334. test_ioapic_level_intr();
  335. test_ioapic_simultaneous();
  336. test_ioapic_level_coalesce();
  337. test_ioapic_level_sequential();
  338. test_ioapic_level_retrigger();
  339. test_ioapic_edge_mask();
  340. test_ioapic_level_mask();
  341. test_ioapic_level_retrigger_mask();
  342. test_ioapic_edge_tmr(false);
  343. test_ioapic_level_tmr(false);
  344. test_ioapic_level_tmr(true);
  345. test_ioapic_edge_tmr(true);
  346. if (cpu_count() > 1) {
  347. test_ioapic_edge_tmr_smp(false);
  348. test_ioapic_level_tmr_smp(false);
  349. test_ioapic_level_tmr_smp(true);
  350. test_ioapic_edge_tmr_smp(true);
  351. }
  352. return report_summary();
  353. }