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 8 static void toggle_irq_line(unsigned line) 9 { 10 set_irq_line(line, 1); 11 set_irq_line(line, 0); 12 } 13 14 static void ioapic_reg_version(void) 15 { 16 u8 version_offset; 17 uint32_t data_read, data_write; 18 19 version_offset = 0x01; 20 data_read = ioapic_read_reg(version_offset); 21 data_write = data_read ^ 0xffffffff; 22 23 ioapic_write_reg(version_offset, data_write); 24 report("version register read only test", 25 data_read == ioapic_read_reg(version_offset)); 26 } 27 28 static void ioapic_reg_id(void) 29 { 30 u8 id_offset; 31 uint32_t data_read, data_write, diff; 32 33 id_offset = 0x0; 34 data_read = ioapic_read_reg(id_offset); 35 data_write = data_read ^ 0xffffffff; 36 37 ioapic_write_reg(id_offset, data_write); 38 39 diff = data_read ^ ioapic_read_reg(id_offset); 40 report("id register only bits [24:27] writable", 41 diff == 0x0f000000); 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("arbitration register set by id", 55 ioapic_read_reg(arb_offset) == write); 56 57 ioapic_write_reg(arb_offset, 0x0); 58 report("arbtration register read only", 59 ioapic_read_reg(arb_offset) == write); 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("edge triggered intr", g_isr_76 == 1); 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("level triggered intr", g_isr_77 == 1); 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("ioapic simultaneous edge interrupts", 128 g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip); 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 for ioapic edge interrupts (expected %s)", 150 tmr_before == expected_tmr_before && !g_tmr_79, 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 for ioapic level interrupts (expected %s)", 164 tmr_before == expected_tmr_before && g_tmr_79, 165 expected_tmr_before ? "true" : "false"); 166 } 167 168 #define IPI_DELAY 1000000 169 170 static void delay(int count) 171 { 172 while(count--) asm(""); 173 } 174 175 static void toggle_irq_line_0x0e(void *data) 176 { 177 irq_disable(); 178 delay(IPI_DELAY); 179 toggle_irq_line(0x0e); 180 irq_enable(); 181 } 182 183 static void test_ioapic_edge_tmr_smp(bool expected_tmr_before) 184 { 185 int tmr_before; 186 int i; 187 188 g_tmr_79 = -1; 189 handle_irq(0x79, ioapic_isr_79); 190 ioapic_set_redir(0x0e, 0x79, TRIGGER_EDGE); 191 tmr_before = apic_read_bit(APIC_TMR, 0x79); 192 on_cpu_async(1, toggle_irq_line_0x0e, 0); 193 i = 0; 194 while(g_tmr_79 == -1) i++; 195 printf("%d iterations before interrupt received\n", i); 196 report("TMR for ioapic edge interrupts (expected %s)", 197 tmr_before == expected_tmr_before && !g_tmr_79, 198 expected_tmr_before ? "true" : "false"); 199 } 200 201 static void set_irq_line_0x0e(void *data) 202 { 203 irq_disable(); 204 delay(IPI_DELAY); 205 set_irq_line(0x0e, 1); 206 irq_enable(); 207 } 208 209 static void test_ioapic_level_tmr_smp(bool expected_tmr_before) 210 { 211 int i, tmr_before; 212 213 g_tmr_79 = -1; 214 handle_irq(0x79, ioapic_isr_79); 215 ioapic_set_redir(0x0e, 0x79, TRIGGER_LEVEL); 216 tmr_before = apic_read_bit(APIC_TMR, 0x79); 217 on_cpu_async(1, set_irq_line_0x0e, 0); 218 i = 0; 219 while(g_tmr_79 == -1) i++; 220 printf("%d iterations before interrupt received\n", i); 221 report("TMR for ioapic level interrupts (expected %s)", 222 tmr_before == expected_tmr_before && g_tmr_79, 223 expected_tmr_before ? "true" : "false"); 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("coalesce simultaneous level interrupts", g_isr_98 == 1); 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("sequential level interrupts", g_isr_99 == 2); 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("retriggered level interrupts without masking", g_isr_9a == 2); 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("masked level interrupt", g_isr_81 == 0); 319 320 set_mask(0x0e, false); 321 set_irq_line(0x0e, 1); 322 323 asm volatile ("nop"); 324 report("unmasked level interrupt", g_isr_81 == 1); 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("masked level interrupt", g_isr_82 == 0); 346 347 set_mask(0x0e, false); 348 349 asm volatile ("nop"); 350 report("unmasked level interrupt", g_isr_82 == 1); 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("retriggered level interrupts with mask", g_isr_83 == 2); 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("Reconfigure self", g_isr_84 == 1 && e.remote_irr == 0); 412 } 413 414 415 int main(void) 416 { 417 setup_vm(); 418 smp_init(); 419 420 mask_pic_interrupts(); 421 422 if (enable_x2apic()) 423 printf("x2apic enabled\n"); 424 else 425 printf("x2apic not detected\n"); 426 427 irq_enable(); 428 429 ioapic_reg_version(); 430 ioapic_reg_id(); 431 ioapic_arbitration_id(); 432 433 test_ioapic_edge_intr(); 434 test_ioapic_level_intr(); 435 test_ioapic_simultaneous(); 436 437 test_ioapic_level_coalesce(); 438 test_ioapic_level_sequential(); 439 test_ioapic_level_retrigger(); 440 441 test_ioapic_edge_mask(); 442 test_ioapic_level_mask(); 443 test_ioapic_level_retrigger_mask(); 444 445 test_ioapic_edge_tmr(false); 446 test_ioapic_level_tmr(false); 447 test_ioapic_level_tmr(true); 448 test_ioapic_edge_tmr(true); 449 450 if (cpu_count() > 1) { 451 test_ioapic_edge_tmr_smp(false); 452 test_ioapic_level_tmr_smp(false); 453 test_ioapic_level_tmr_smp(true); 454 test_ioapic_edge_tmr_smp(true); 455 456 test_ioapic_self_reconfigure(); 457 } 458 459 return report_summary(); 460 } 461