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