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