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