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 #include "delay.h" 8 9 static void poll_remote_irr(unsigned line) 10 { 11 while (ioapic_read_redir(line).remote_irr) 12 cpu_relax(); 13 } 14 15 static void toggle_irq_line(unsigned line) 16 { 17 set_irq_line(line, 1); 18 set_irq_line(line, 0); 19 } 20 21 static void ioapic_reg_version(void) 22 { 23 u8 version_offset; 24 uint32_t data_read, data_write; 25 26 version_offset = 0x01; 27 data_read = ioapic_read_reg(version_offset); 28 data_write = data_read ^ 0xffffffff; 29 30 ioapic_write_reg(version_offset, data_write); 31 report(data_read == ioapic_read_reg(version_offset), 32 "version register read only test"); 33 } 34 35 static void ioapic_reg_id(void) 36 { 37 u8 id_offset; 38 uint32_t data_read, data_write, diff; 39 40 id_offset = 0x0; 41 data_read = ioapic_read_reg(id_offset); 42 data_write = data_read ^ 0xffffffff; 43 44 ioapic_write_reg(id_offset, data_write); 45 46 diff = data_read ^ ioapic_read_reg(id_offset); 47 report(diff == 0x0f000000, "id register only bits [24:27] writable"); 48 } 49 50 static void ioapic_arbitration_id(void) 51 { 52 u8 id_offset, arb_offset; 53 uint32_t write; 54 55 id_offset = 0x0; 56 arb_offset = 0x2; 57 write = 0x0f000000; 58 59 ioapic_write_reg(id_offset, write); 60 report(ioapic_read_reg(arb_offset) == write, 61 "arbitration register set by id"); 62 63 ioapic_write_reg(arb_offset, 0x0); 64 report(ioapic_read_reg(arb_offset) == write, 65 "arbtration register read only"); 66 } 67 68 static volatile int g_isr_76; 69 70 static void ioapic_isr_76(isr_regs_t *regs) 71 { 72 ++g_isr_76; 73 eoi(); 74 } 75 76 static void test_ioapic_edge_intr(void) 77 { 78 handle_irq(0x76, ioapic_isr_76); 79 ioapic_set_redir(0x0e, 0x76, TRIGGER_EDGE); 80 toggle_irq_line(0x0e); 81 asm volatile ("nop"); 82 report(g_isr_76 == 1, "edge triggered intr"); 83 } 84 85 static volatile int g_isr_77; 86 87 static void ioapic_isr_77(isr_regs_t *regs) 88 { 89 ++g_isr_77; 90 set_irq_line(0x0e, 0); 91 eoi(); 92 } 93 94 static void test_ioapic_level_intr(void) 95 { 96 handle_irq(0x77, ioapic_isr_77); 97 ioapic_set_redir(0x0e, 0x77, TRIGGER_LEVEL); 98 set_irq_line(0x0e, 1); 99 asm volatile ("nop"); 100 report(g_isr_77 == 1, "level triggered intr"); 101 } 102 103 static int g_78, g_66, g_66_after_78; 104 static ulong g_66_rip, g_78_rip; 105 106 static void ioapic_isr_78(isr_regs_t *regs) 107 { 108 ++g_78; 109 g_78_rip = regs->rip; 110 eoi(); 111 } 112 113 static void ioapic_isr_66(isr_regs_t *regs) 114 { 115 ++g_66; 116 if (g_78) 117 ++g_66_after_78; 118 g_66_rip = regs->rip; 119 eoi(); 120 } 121 122 static void test_ioapic_simultaneous(void) 123 { 124 handle_irq(0x78, ioapic_isr_78); 125 handle_irq(0x66, ioapic_isr_66); 126 ioapic_set_redir(0x0e, 0x78, TRIGGER_EDGE); 127 ioapic_set_redir(0x0f, 0x66, TRIGGER_EDGE); 128 cli(); 129 toggle_irq_line(0x0f); 130 toggle_irq_line(0x0e); 131 sti_nop(); 132 report(g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip, 133 "ioapic simultaneous edge interrupts"); 134 } 135 136 static volatile int g_tmr_79 = -1; 137 138 static void ioapic_isr_79(isr_regs_t *regs) 139 { 140 g_tmr_79 = apic_read_bit(APIC_TMR, 0x79); 141 set_irq_line(0x0e, 0); 142 eoi(); 143 } 144 145 static void test_ioapic_edge_tmr(bool expected_tmr_before) 146 { 147 int tmr_before; 148 149 handle_irq(0x79, ioapic_isr_79); 150 ioapic_set_redir(0x0e, 0x79, TRIGGER_EDGE); 151 tmr_before = apic_read_bit(APIC_TMR, 0x79); 152 toggle_irq_line(0x0e); 153 asm volatile ("nop"); 154 report(tmr_before == expected_tmr_before && !g_tmr_79, 155 "TMR for ioapic edge interrupts (expected %s)", 156 expected_tmr_before ? "true" : "false"); 157 } 158 159 static void test_ioapic_level_tmr(bool expected_tmr_before) 160 { 161 int tmr_before; 162 163 handle_irq(0x79, ioapic_isr_79); 164 ioapic_set_redir(0x0e, 0x79, TRIGGER_LEVEL); 165 tmr_before = apic_read_bit(APIC_TMR, 0x79); 166 set_irq_line(0x0e, 1); 167 asm volatile ("nop"); 168 report(tmr_before == expected_tmr_before && g_tmr_79, 169 "TMR for ioapic level interrupts (expected %s)", 170 expected_tmr_before ? "true" : "false"); 171 } 172 173 static void toggle_irq_line_0x0e(void *data) 174 { 175 cli(); 176 delay(IPI_DELAY); 177 toggle_irq_line(0x0e); 178 sti(); 179 } 180 181 static void test_ioapic_edge_tmr_smp(bool expected_tmr_before) 182 { 183 int tmr_before; 184 int i; 185 186 g_tmr_79 = -1; 187 handle_irq(0x79, ioapic_isr_79); 188 ioapic_set_redir(0x0e, 0x79, TRIGGER_EDGE); 189 tmr_before = apic_read_bit(APIC_TMR, 0x79); 190 on_cpu_async(1, toggle_irq_line_0x0e, 0); 191 i = 0; 192 while(g_tmr_79 == -1) i++; 193 printf("%d iterations before interrupt received\n", i); 194 report(tmr_before == expected_tmr_before && !g_tmr_79, 195 "TMR for ioapic edge interrupts (expected %s)", 196 expected_tmr_before ? "true" : "false"); 197 } 198 199 static void set_irq_line_0x0e(void *data) 200 { 201 cli(); 202 delay(IPI_DELAY); 203 set_irq_line(0x0e, 1); 204 sti(); 205 } 206 207 static void test_ioapic_level_tmr_smp(bool expected_tmr_before) 208 { 209 int i, tmr_before; 210 211 g_tmr_79 = -1; 212 handle_irq(0x79, ioapic_isr_79); 213 ioapic_set_redir(0x0e, 0x79, TRIGGER_LEVEL); 214 tmr_before = apic_read_bit(APIC_TMR, 0x79); 215 on_cpu_async(1, set_irq_line_0x0e, 0); 216 i = 0; 217 while(g_tmr_79 == -1) i++; 218 printf("%d iterations before interrupt received\n", i); 219 report(tmr_before == expected_tmr_before && g_tmr_79, 220 "TMR for ioapic level interrupts (expected %s)", 221 expected_tmr_before ? "true" : "false"); 222 poll_remote_irr(0xe); 223 } 224 225 static int g_isr_98; 226 227 static void ioapic_isr_98(isr_regs_t *regs) 228 { 229 ++g_isr_98; 230 if (g_isr_98 == 1) { 231 set_irq_line(0x0e, 0); 232 set_irq_line(0x0e, 1); 233 } 234 set_irq_line(0x0e, 0); 235 eoi(); 236 } 237 238 static void test_ioapic_level_coalesce(void) 239 { 240 handle_irq(0x98, ioapic_isr_98); 241 ioapic_set_redir(0x0e, 0x98, TRIGGER_LEVEL); 242 set_irq_line(0x0e, 1); 243 asm volatile ("nop"); 244 report(g_isr_98 == 1, "coalesce simultaneous level interrupts"); 245 } 246 247 static int g_isr_99; 248 249 static void ioapic_isr_99(isr_regs_t *regs) 250 { 251 ++g_isr_99; 252 set_irq_line(0x0e, 0); 253 eoi(); 254 } 255 256 static void test_ioapic_level_sequential(void) 257 { 258 handle_irq(0x99, ioapic_isr_99); 259 ioapic_set_redir(0x0e, 0x99, TRIGGER_LEVEL); 260 set_irq_line(0x0e, 1); 261 set_irq_line(0x0e, 1); 262 asm volatile ("nop"); 263 report(g_isr_99 == 2, "sequential level interrupts"); 264 } 265 266 static volatile int g_isr_9a; 267 268 static void ioapic_isr_9a(isr_regs_t *regs) 269 { 270 ++g_isr_9a; 271 if (g_isr_9a == 2) 272 set_irq_line(0x0e, 0); 273 eoi(); 274 } 275 276 static void test_ioapic_level_retrigger(void) 277 { 278 int i; 279 280 handle_irq(0x9a, ioapic_isr_9a); 281 ioapic_set_redir(0x0e, 0x9a, TRIGGER_LEVEL); 282 283 asm volatile ("cli"); 284 set_irq_line(0x0e, 1); 285 286 for (i = 0; i < 10; i++) { 287 if (g_isr_9a == 2) 288 break; 289 290 asm volatile ("sti; hlt; cli"); 291 } 292 293 asm volatile ("sti"); 294 295 report(g_isr_9a == 2, "retriggered level interrupts without masking"); 296 } 297 298 static volatile int g_isr_81; 299 300 static void ioapic_isr_81(isr_regs_t *regs) 301 { 302 ++g_isr_81; 303 set_irq_line(0x0e, 0); 304 eoi(); 305 } 306 307 static void test_ioapic_edge_mask(void) 308 { 309 handle_irq(0x81, ioapic_isr_81); 310 ioapic_set_redir(0x0e, 0x81, TRIGGER_EDGE); 311 312 set_mask(0x0e, true); 313 set_irq_line(0x0e, 1); 314 set_irq_line(0x0e, 0); 315 316 asm volatile ("nop"); 317 report(g_isr_81 == 0, "masked level interrupt"); 318 319 set_mask(0x0e, false); 320 set_irq_line(0x0e, 1); 321 322 asm volatile ("nop"); 323 report(g_isr_81 == 1, "unmasked level interrupt"); 324 } 325 326 static volatile int g_isr_82; 327 328 static void ioapic_isr_82(isr_regs_t *regs) 329 { 330 ++g_isr_82; 331 set_irq_line(0x0e, 0); 332 eoi(); 333 } 334 335 static void test_ioapic_level_mask(void) 336 { 337 handle_irq(0x82, ioapic_isr_82); 338 ioapic_set_redir(0x0e, 0x82, TRIGGER_LEVEL); 339 340 set_mask(0x0e, true); 341 set_irq_line(0x0e, 1); 342 343 asm volatile ("nop"); 344 report(g_isr_82 == 0, "masked level interrupt"); 345 346 set_mask(0x0e, false); 347 348 asm volatile ("nop"); 349 report(g_isr_82 == 1, "unmasked level interrupt"); 350 } 351 352 static volatile int g_isr_83; 353 354 static void ioapic_isr_83(isr_regs_t *regs) 355 { 356 ++g_isr_83; 357 set_mask(0x0e, true); 358 eoi(); 359 } 360 361 static void test_ioapic_level_retrigger_mask(void) 362 { 363 handle_irq(0x83, ioapic_isr_83); 364 ioapic_set_redir(0x0e, 0x83, TRIGGER_LEVEL); 365 366 set_irq_line(0x0e, 1); 367 asm volatile ("nop"); 368 set_mask(0x0e, false); 369 asm volatile ("nop"); 370 report(g_isr_83 == 2, "retriggered level interrupts with mask"); 371 372 set_irq_line(0x0e, 0); 373 set_mask(0x0e, false); 374 } 375 376 static volatile int g_isr_84; 377 378 static void ioapic_isr_84(isr_regs_t *regs) 379 { 380 int line = 0xe; 381 ioapic_redir_entry_t e; 382 383 ++g_isr_84; 384 set_irq_line(line, 0); 385 386 e = ioapic_read_redir(line); 387 e.dest_id = 1; 388 389 // Update only upper part of the register because we only change the 390 // destination, which resides in the upper part 391 ioapic_write_reg(0x10 + line * 2 + 1, ((u32 *)&e)[1]); 392 393 eoi(); 394 } 395 396 static void test_ioapic_self_reconfigure(void) 397 { 398 ioapic_redir_entry_t e = { 399 .vector = 0x84, 400 .delivery_mode = 0, 401 .dest_mode = 0, 402 .dest_id = 0, 403 .trig_mode = TRIGGER_LEVEL, 404 }; 405 406 handle_irq(0x84, ioapic_isr_84); 407 ioapic_write_redir(0xe, e); 408 set_irq_line(0x0e, 1); 409 e = ioapic_read_redir(0xe); 410 report(g_isr_84 == 1 && e.remote_irr == 0, "Reconfigure self"); 411 poll_remote_irr(0xe); 412 } 413 414 static volatile int g_isr_85; 415 416 static void ioapic_isr_85(isr_regs_t *regs) 417 { 418 ++g_isr_85; 419 set_irq_line(0x0e, 0); 420 eoi(); 421 } 422 423 static void test_ioapic_physical_destination_mode(void) 424 { 425 ioapic_redir_entry_t e = { 426 .vector = 0x85, 427 .delivery_mode = 0, 428 .dest_mode = 0, 429 .dest_id = 0x1, 430 .trig_mode = TRIGGER_LEVEL, 431 }; 432 handle_irq(0x85, ioapic_isr_85); 433 ioapic_write_redir(0xe, e); 434 set_irq_line(0x0e, 1); 435 do { 436 pause(); 437 } while(g_isr_85 != 1); 438 report(g_isr_85 == 1, "ioapic physical destination mode"); 439 poll_remote_irr(0xe); 440 } 441 442 static volatile int g_isr_86; 443 struct spinlock ioapic_lock; 444 445 static void ioapic_isr_86(isr_regs_t *regs) 446 { 447 spin_lock(&ioapic_lock); 448 ++g_isr_86; 449 spin_unlock(&ioapic_lock); 450 set_irq_line(0x0e, 0); 451 eoi(); 452 } 453 454 static void test_ioapic_logical_destination_mode(void) 455 { 456 /* Number of vcpus which are configured/set in dest_id */ 457 int nr_vcpus = 3; 458 ioapic_redir_entry_t e = { 459 .vector = 0x86, 460 .delivery_mode = 0, 461 .dest_mode = 1, 462 .dest_id = 0xd, 463 .trig_mode = TRIGGER_LEVEL, 464 }; 465 handle_irq(0x86, ioapic_isr_86); 466 ioapic_write_redir(0xe, e); 467 set_irq_line(0x0e, 1); 468 do { 469 pause(); 470 } while(g_isr_86 < nr_vcpus); 471 report(g_isr_86 == nr_vcpus, "ioapic logical destination mode"); 472 poll_remote_irr(0xe); 473 } 474 475 int main(void) 476 { 477 setup_vm(); 478 479 on_cpus(update_cr3, (void *)read_cr3()); 480 mask_pic_interrupts(); 481 482 if (enable_x2apic()) 483 printf("x2apic enabled\n"); 484 else 485 printf("x2apic not detected\n"); 486 487 sti(); 488 489 ioapic_reg_version(); 490 ioapic_reg_id(); 491 ioapic_arbitration_id(); 492 493 test_ioapic_edge_intr(); 494 test_ioapic_level_intr(); 495 test_ioapic_simultaneous(); 496 497 test_ioapic_level_coalesce(); 498 test_ioapic_level_sequential(); 499 test_ioapic_level_retrigger(); 500 501 test_ioapic_edge_mask(); 502 test_ioapic_level_mask(); 503 test_ioapic_level_retrigger_mask(); 504 505 test_ioapic_edge_tmr(false); 506 test_ioapic_level_tmr(false); 507 test_ioapic_level_tmr(true); 508 test_ioapic_edge_tmr(true); 509 510 if (cpu_count() > 1) 511 test_ioapic_physical_destination_mode(); 512 if (cpu_count() > 3) 513 test_ioapic_logical_destination_mode(); 514 515 if (cpu_count() > 1) { 516 test_ioapic_edge_tmr_smp(false); 517 test_ioapic_level_tmr_smp(false); 518 test_ioapic_level_tmr_smp(true); 519 test_ioapic_edge_tmr_smp(true); 520 521 test_ioapic_self_reconfigure(); 522 } 523 524 return report_summary(); 525 } 526