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