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 irq_disable(); 129 toggle_irq_line(0x0f); 130 toggle_irq_line(0x0e); 131 irq_enable(); 132 asm volatile ("nop"); 133 report(g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip, 134 "ioapic simultaneous edge interrupts"); 135 } 136 137 static volatile int g_tmr_79 = -1; 138 139 static void ioapic_isr_79(isr_regs_t *regs) 140 { 141 g_tmr_79 = apic_read_bit(APIC_TMR, 0x79); 142 set_irq_line(0x0e, 0); 143 eoi(); 144 } 145 146 static void test_ioapic_edge_tmr(bool expected_tmr_before) 147 { 148 int tmr_before; 149 150 handle_irq(0x79, ioapic_isr_79); 151 ioapic_set_redir(0x0e, 0x79, TRIGGER_EDGE); 152 tmr_before = apic_read_bit(APIC_TMR, 0x79); 153 toggle_irq_line(0x0e); 154 asm volatile ("nop"); 155 report(tmr_before == expected_tmr_before && !g_tmr_79, 156 "TMR for ioapic edge interrupts (expected %s)", 157 expected_tmr_before ? "true" : "false"); 158 } 159 160 static void test_ioapic_level_tmr(bool expected_tmr_before) 161 { 162 int tmr_before; 163 164 handle_irq(0x79, ioapic_isr_79); 165 ioapic_set_redir(0x0e, 0x79, TRIGGER_LEVEL); 166 tmr_before = apic_read_bit(APIC_TMR, 0x79); 167 set_irq_line(0x0e, 1); 168 asm volatile ("nop"); 169 report(tmr_before == expected_tmr_before && g_tmr_79, 170 "TMR for ioapic level interrupts (expected %s)", 171 expected_tmr_before ? "true" : "false"); 172 } 173 174 static void toggle_irq_line_0x0e(void *data) 175 { 176 irq_disable(); 177 delay(IPI_DELAY); 178 toggle_irq_line(0x0e); 179 irq_enable(); 180 } 181 182 static void test_ioapic_edge_tmr_smp(bool expected_tmr_before) 183 { 184 int tmr_before; 185 int i; 186 187 g_tmr_79 = -1; 188 handle_irq(0x79, ioapic_isr_79); 189 ioapic_set_redir(0x0e, 0x79, TRIGGER_EDGE); 190 tmr_before = apic_read_bit(APIC_TMR, 0x79); 191 on_cpu_async(1, toggle_irq_line_0x0e, 0); 192 i = 0; 193 while(g_tmr_79 == -1) i++; 194 printf("%d iterations before interrupt received\n", i); 195 report(tmr_before == expected_tmr_before && !g_tmr_79, 196 "TMR for ioapic edge interrupts (expected %s)", 197 expected_tmr_before ? "true" : "false"); 198 } 199 200 static void set_irq_line_0x0e(void *data) 201 { 202 irq_disable(); 203 delay(IPI_DELAY); 204 set_irq_line(0x0e, 1); 205 irq_enable(); 206 } 207 208 static void test_ioapic_level_tmr_smp(bool expected_tmr_before) 209 { 210 int i, tmr_before; 211 212 g_tmr_79 = -1; 213 handle_irq(0x79, ioapic_isr_79); 214 ioapic_set_redir(0x0e, 0x79, TRIGGER_LEVEL); 215 tmr_before = apic_read_bit(APIC_TMR, 0x79); 216 on_cpu_async(1, set_irq_line_0x0e, 0); 217 i = 0; 218 while(g_tmr_79 == -1) i++; 219 printf("%d iterations before interrupt received\n", i); 220 report(tmr_before == expected_tmr_before && g_tmr_79, 221 "TMR for ioapic level interrupts (expected %s)", 222 expected_tmr_before ? "true" : "false"); 223 poll_remote_irr(0xe); 224 } 225 226 static int g_isr_98; 227 228 static void ioapic_isr_98(isr_regs_t *regs) 229 { 230 ++g_isr_98; 231 if (g_isr_98 == 1) { 232 set_irq_line(0x0e, 0); 233 set_irq_line(0x0e, 1); 234 } 235 set_irq_line(0x0e, 0); 236 eoi(); 237 } 238 239 static void test_ioapic_level_coalesce(void) 240 { 241 handle_irq(0x98, ioapic_isr_98); 242 ioapic_set_redir(0x0e, 0x98, TRIGGER_LEVEL); 243 set_irq_line(0x0e, 1); 244 asm volatile ("nop"); 245 report(g_isr_98 == 1, "coalesce simultaneous level interrupts"); 246 } 247 248 static int g_isr_99; 249 250 static void ioapic_isr_99(isr_regs_t *regs) 251 { 252 ++g_isr_99; 253 set_irq_line(0x0e, 0); 254 eoi(); 255 } 256 257 static void test_ioapic_level_sequential(void) 258 { 259 handle_irq(0x99, ioapic_isr_99); 260 ioapic_set_redir(0x0e, 0x99, TRIGGER_LEVEL); 261 set_irq_line(0x0e, 1); 262 set_irq_line(0x0e, 1); 263 asm volatile ("nop"); 264 report(g_isr_99 == 2, "sequential level interrupts"); 265 } 266 267 static volatile int g_isr_9a; 268 269 static void ioapic_isr_9a(isr_regs_t *regs) 270 { 271 ++g_isr_9a; 272 if (g_isr_9a == 2) 273 set_irq_line(0x0e, 0); 274 eoi(); 275 } 276 277 static void test_ioapic_level_retrigger(void) 278 { 279 int i; 280 281 handle_irq(0x9a, ioapic_isr_9a); 282 ioapic_set_redir(0x0e, 0x9a, TRIGGER_LEVEL); 283 284 asm volatile ("cli"); 285 set_irq_line(0x0e, 1); 286 287 for (i = 0; i < 10; i++) { 288 if (g_isr_9a == 2) 289 break; 290 291 asm volatile ("sti; hlt; cli"); 292 } 293 294 asm volatile ("sti"); 295 296 report(g_isr_9a == 2, "retriggered level interrupts without masking"); 297 } 298 299 static volatile int g_isr_81; 300 301 static void ioapic_isr_81(isr_regs_t *regs) 302 { 303 ++g_isr_81; 304 set_irq_line(0x0e, 0); 305 eoi(); 306 } 307 308 static void test_ioapic_edge_mask(void) 309 { 310 handle_irq(0x81, ioapic_isr_81); 311 ioapic_set_redir(0x0e, 0x81, TRIGGER_EDGE); 312 313 set_mask(0x0e, true); 314 set_irq_line(0x0e, 1); 315 set_irq_line(0x0e, 0); 316 317 asm volatile ("nop"); 318 report(g_isr_81 == 0, "masked level interrupt"); 319 320 set_mask(0x0e, false); 321 set_irq_line(0x0e, 1); 322 323 asm volatile ("nop"); 324 report(g_isr_81 == 1, "unmasked level interrupt"); 325 } 326 327 static volatile int g_isr_82; 328 329 static void ioapic_isr_82(isr_regs_t *regs) 330 { 331 ++g_isr_82; 332 set_irq_line(0x0e, 0); 333 eoi(); 334 } 335 336 static void test_ioapic_level_mask(void) 337 { 338 handle_irq(0x82, ioapic_isr_82); 339 ioapic_set_redir(0x0e, 0x82, TRIGGER_LEVEL); 340 341 set_mask(0x0e, true); 342 set_irq_line(0x0e, 1); 343 344 asm volatile ("nop"); 345 report(g_isr_82 == 0, "masked level interrupt"); 346 347 set_mask(0x0e, false); 348 349 asm volatile ("nop"); 350 report(g_isr_82 == 1, "unmasked level interrupt"); 351 } 352 353 static volatile int g_isr_83; 354 355 static void ioapic_isr_83(isr_regs_t *regs) 356 { 357 ++g_isr_83; 358 set_mask(0x0e, true); 359 eoi(); 360 } 361 362 static void test_ioapic_level_retrigger_mask(void) 363 { 364 handle_irq(0x83, ioapic_isr_83); 365 ioapic_set_redir(0x0e, 0x83, TRIGGER_LEVEL); 366 367 set_irq_line(0x0e, 1); 368 asm volatile ("nop"); 369 set_mask(0x0e, false); 370 asm volatile ("nop"); 371 report(g_isr_83 == 2, "retriggered level interrupts with mask"); 372 373 set_irq_line(0x0e, 0); 374 set_mask(0x0e, false); 375 } 376 377 static volatile int g_isr_84; 378 379 static void ioapic_isr_84(isr_regs_t *regs) 380 { 381 int line = 0xe; 382 ioapic_redir_entry_t e; 383 384 ++g_isr_84; 385 set_irq_line(line, 0); 386 387 e = ioapic_read_redir(line); 388 e.dest_id = 1; 389 390 // Update only upper part of the register because we only change the 391 // destination, which resides in the upper part 392 ioapic_write_reg(0x10 + line * 2 + 1, ((u32 *)&e)[1]); 393 394 eoi(); 395 } 396 397 static void test_ioapic_self_reconfigure(void) 398 { 399 ioapic_redir_entry_t e = { 400 .vector = 0x84, 401 .delivery_mode = 0, 402 .dest_mode = 0, 403 .dest_id = 0, 404 .trig_mode = TRIGGER_LEVEL, 405 }; 406 407 handle_irq(0x84, ioapic_isr_84); 408 ioapic_write_redir(0xe, e); 409 set_irq_line(0x0e, 1); 410 e = ioapic_read_redir(0xe); 411 report(g_isr_84 == 1 && e.remote_irr == 0, "Reconfigure self"); 412 poll_remote_irr(0xe); 413 } 414 415 static volatile int g_isr_85; 416 417 static void ioapic_isr_85(isr_regs_t *regs) 418 { 419 ++g_isr_85; 420 set_irq_line(0x0e, 0); 421 eoi(); 422 } 423 424 static void test_ioapic_physical_destination_mode(void) 425 { 426 ioapic_redir_entry_t e = { 427 .vector = 0x85, 428 .delivery_mode = 0, 429 .dest_mode = 0, 430 .dest_id = 0x1, 431 .trig_mode = TRIGGER_LEVEL, 432 }; 433 handle_irq(0x85, ioapic_isr_85); 434 ioapic_write_redir(0xe, e); 435 set_irq_line(0x0e, 1); 436 do { 437 pause(); 438 } while(g_isr_85 != 1); 439 report(g_isr_85 == 1, "ioapic physical destination mode"); 440 poll_remote_irr(0xe); 441 } 442 443 static volatile int g_isr_86; 444 struct spinlock ioapic_lock; 445 446 static void ioapic_isr_86(isr_regs_t *regs) 447 { 448 spin_lock(&ioapic_lock); 449 ++g_isr_86; 450 spin_unlock(&ioapic_lock); 451 set_irq_line(0x0e, 0); 452 eoi(); 453 } 454 455 static void test_ioapic_logical_destination_mode(void) 456 { 457 /* Number of vcpus which are configured/set in dest_id */ 458 int nr_vcpus = 3; 459 ioapic_redir_entry_t e = { 460 .vector = 0x86, 461 .delivery_mode = 0, 462 .dest_mode = 1, 463 .dest_id = 0xd, 464 .trig_mode = TRIGGER_LEVEL, 465 }; 466 handle_irq(0x86, ioapic_isr_86); 467 ioapic_write_redir(0xe, e); 468 set_irq_line(0x0e, 1); 469 do { 470 pause(); 471 } while(g_isr_86 < nr_vcpus); 472 report(g_isr_86 == nr_vcpus, "ioapic logical destination mode"); 473 poll_remote_irr(0xe); 474 } 475 476 int main(void) 477 { 478 setup_vm(); 479 480 on_cpus(update_cr3, (void *)read_cr3()); 481 mask_pic_interrupts(); 482 483 if (enable_x2apic()) 484 printf("x2apic enabled\n"); 485 else 486 printf("x2apic not detected\n"); 487 488 irq_enable(); 489 490 ioapic_reg_version(); 491 ioapic_reg_id(); 492 ioapic_arbitration_id(); 493 494 test_ioapic_edge_intr(); 495 test_ioapic_level_intr(); 496 test_ioapic_simultaneous(); 497 498 test_ioapic_level_coalesce(); 499 test_ioapic_level_sequential(); 500 test_ioapic_level_retrigger(); 501 502 test_ioapic_edge_mask(); 503 test_ioapic_level_mask(); 504 test_ioapic_level_retrigger_mask(); 505 506 test_ioapic_edge_tmr(false); 507 test_ioapic_level_tmr(false); 508 test_ioapic_level_tmr(true); 509 test_ioapic_edge_tmr(true); 510 511 if (cpu_count() > 1) 512 test_ioapic_physical_destination_mode(); 513 if (cpu_count() > 3) 514 test_ioapic_logical_destination_mode(); 515 516 if (cpu_count() > 1) { 517 test_ioapic_edge_tmr_smp(false); 518 test_ioapic_level_tmr_smp(false); 519 test_ioapic_level_tmr_smp(true); 520 test_ioapic_edge_tmr_smp(true); 521 522 test_ioapic_self_reconfigure(); 523 } 524 525 return report_summary(); 526 } 527