1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS kernel 4 * FILE: hal/halx86/mp/ioapic.c 5 * PURPOSE: 6 * PROGRAMMER: 7 */ 8 9 /* INCLUDES *****************************************************************/ 10 11 #include <hal.h> 12 #define NDEBUG 13 #include <debug.h> 14 15 /* GLOBALS *****************************************************************/ 16 17 MP_CONFIGURATION_INTSRC IRQMap[MAX_IRQ_SOURCE]; /* Map of all IRQs */ 18 ULONG IRQCount = 0; /* Number of IRQs */ 19 ULONG IrqApicMap[MAX_IRQ_SOURCE]; 20 21 UCHAR BUSMap[MAX_BUS]; /* Map of all buses in the system */ 22 UCHAR PCIBUSMap[MAX_BUS]; /* Map of all PCI buses in the system */ 23 24 IOAPIC_INFO IOAPICMap[MAX_IOAPIC]; /* Map of all I/O APICs in the system */ 25 ULONG IOAPICCount; /* Number of I/O APICs in the system */ 26 27 ULONG IRQVectorMap[MAX_IRQ_SOURCE]; /* IRQ to vector map */ 28 29 /* EISA interrupts are always polarity zero and can be edge or level 30 * trigger depending on the ELCR value. If an interrupt is listed as 31 * EISA conforming in the MP table, that means its trigger type must 32 * be read in from the ELCR */ 33 34 #define default_EISA_trigger(idx) (EISA_ELCR_Read(IRQMap[idx].SrcBusIrq)) 35 #define default_EISA_polarity(idx) (0) 36 37 /* ISA interrupts are always polarity zero edge triggered, 38 * when listed as conforming in the MP table. */ 39 40 #define default_ISA_trigger(idx) (0) 41 #define default_ISA_polarity(idx) (0) 42 43 /* PCI interrupts are always polarity one level triggered, 44 * when listed as conforming in the MP table. */ 45 46 #define default_PCI_trigger(idx) (1) 47 #define default_PCI_polarity(idx) (1) 48 49 /* MCA interrupts are always polarity zero level triggered, 50 * when listed as conforming in the MP table. */ 51 52 #define default_MCA_trigger(idx) (1) 53 #define default_MCA_polarity(idx) (0) 54 55 /***************************************************************************/ 56 57 extern VOID Disable8259AIrq(ULONG irq); 58 ULONG IOAPICRead(ULONG Apic, ULONG Offset); 59 VOID IOAPICWrite(ULONG Apic, ULONG Offset, ULONG Value); 60 61 /* FUNCTIONS ***************************************************************/ 62 63 /* 64 * EISA Edge/Level control register, ELCR 65 */ 66 static ULONG EISA_ELCR_Read(ULONG irq) 67 { 68 if (irq < 16) 69 { 70 PUCHAR port = (PUCHAR)(0x4d0 + (irq >> 3)); 71 return (READ_PORT_UCHAR(port) >> (irq & 7)) & 1; 72 } 73 DPRINT1("Broken MPtable reports ISA irq %lu\n", irq); 74 return 0; 75 } 76 77 static ULONG 78 IRQPolarity(ULONG idx) 79 { 80 ULONG bus = IRQMap[idx].SrcBusId; 81 ULONG polarity; 82 83 /* 84 * Determine IRQ line polarity (high active or low active): 85 */ 86 switch (IRQMap[idx].IrqFlag & 3) 87 { 88 case 0: /* conforms, ie. bus-type dependent polarity */ 89 { 90 switch (BUSMap[bus]) 91 { 92 case MP_BUS_ISA: /* ISA pin */ 93 polarity = default_ISA_polarity(idx); 94 break; 95 96 case MP_BUS_EISA: /* EISA pin */ 97 polarity = default_EISA_polarity(idx); 98 break; 99 100 case MP_BUS_PCI: /* PCI pin */ 101 polarity = default_PCI_polarity(idx); 102 break; 103 104 case MP_BUS_MCA: /* MCA pin */ 105 polarity = default_MCA_polarity(idx); 106 break; 107 108 default: 109 DPRINT1("Broken BIOS\n"); 110 polarity = 1; 111 } 112 } 113 break; 114 115 case 1: /* high active */ 116 polarity = 0; 117 break; 118 119 case 2: /* reserved */ 120 DPRINT1("Broken BIOS\n"); 121 polarity = 1; 122 break; 123 124 case 3: /* low active */ 125 polarity = 1; 126 break; 127 128 default: /* invalid */ 129 DPRINT1("Broken BIOS\n"); 130 polarity = 1; 131 } 132 return polarity; 133 } 134 135 static ULONG 136 IRQTrigger(ULONG idx) 137 { 138 ULONG bus = IRQMap[idx].SrcBusId; 139 ULONG trigger; 140 141 /* 142 * Determine IRQ trigger mode (edge or level sensitive): 143 */ 144 switch ((IRQMap[idx].IrqFlag >> 2) & 3) 145 { 146 case 0: /* conforms, ie. bus-type dependent */ 147 { 148 switch (BUSMap[bus]) 149 { 150 case MP_BUS_ISA: /* ISA pin */ 151 trigger = default_ISA_trigger(idx); 152 break; 153 154 case MP_BUS_EISA: /* EISA pin */ 155 trigger = default_EISA_trigger(idx); 156 break; 157 158 case MP_BUS_PCI: /* PCI pin */ 159 trigger = default_PCI_trigger(idx); 160 break; 161 162 case MP_BUS_MCA: /* MCA pin */ 163 trigger = default_MCA_trigger(idx); 164 break; 165 166 default: 167 DPRINT1("Broken BIOS\n"); 168 trigger = 1; 169 } 170 } 171 break; 172 173 case 1: /* edge */ 174 trigger = 0; 175 break; 176 177 case 2: /* reserved */ 178 DPRINT1("Broken BIOS\n"); 179 trigger = 1; 180 break; 181 182 case 3: /* level */ 183 trigger = 1; 184 break; 185 186 default: /* invalid */ 187 DPRINT1("Broken BIOS\n"); 188 trigger = 0; 189 } 190 return trigger; 191 } 192 193 static ULONG 194 Pin2Irq(ULONG idx, 195 ULONG apic, 196 ULONG pin) 197 { 198 ULONG irq, i; 199 ULONG bus = IRQMap[idx].SrcBusId; 200 201 /* 202 * Debugging check, we are in big trouble if this message pops up! 203 */ 204 if (IRQMap[idx].DstApicInt != pin) 205 { 206 DPRINT1("Broken BIOS or MPTABLE parser\n"); 207 } 208 209 switch (BUSMap[bus]) 210 { 211 case MP_BUS_ISA: /* ISA pin */ 212 case MP_BUS_EISA: 213 case MP_BUS_MCA: 214 irq = IRQMap[idx].SrcBusIrq; 215 break; 216 217 case MP_BUS_PCI: /* PCI pin */ 218 /* 219 * PCI IRQs are mapped in order 220 */ 221 i = irq = 0; 222 while (i < apic) 223 { 224 irq += IOAPICMap[i++].EntryCount; 225 } 226 irq += pin; 227 break; 228 229 default: 230 DPRINT1("Unknown bus type %lu\n", bus); 231 irq = 0; 232 } 233 return irq; 234 } 235 236 static ULONG 237 AssignIrqVector(ULONG irq) 238 { 239 #if 0 240 static ULONG current_vector = FIRST_DEVICE_VECTOR, vector_offset = 0; 241 #endif 242 ULONG vector; 243 /* There may already have been assigned a vector for this IRQ */ 244 vector = IRQVectorMap[irq]; 245 if (vector > 0) 246 { 247 return vector; 248 } 249 #if 0 250 if (current_vector > FIRST_SYSTEM_VECTOR) 251 { 252 vector_offset++; 253 current_vector = FIRST_DEVICE_VECTOR + vector_offset; 254 } 255 else if (current_vector == FIRST_SYSTEM_VECTOR) 256 { 257 DPRINT1("Ran out of interrupt sources\n"); 258 ASSERT(FALSE); 259 } 260 261 vector = current_vector; 262 IRQVectorMap[irq] = vector; 263 current_vector += 8; 264 return vector; 265 #else 266 vector = IRQ2VECTOR(irq); 267 IRQVectorMap[irq] = vector; 268 return vector; 269 #endif 270 } 271 272 /* 273 * Find the IRQ entry number of a certain pin. 274 */ 275 static ULONG 276 IOAPICGetIrqEntry(ULONG apic, 277 ULONG pin, 278 ULONG type) 279 { 280 ULONG i; 281 282 for (i = 0; i < IRQCount; i++) 283 { 284 if (IRQMap[i].IrqType == type && 285 (IRQMap[i].DstApicId == IOAPICMap[apic].ApicId || IRQMap[i].DstApicId == MP_APIC_ALL) && 286 IRQMap[i].DstApicInt == pin) 287 { 288 return i; 289 } 290 } 291 return -1; 292 } 293 294 295 VOID 296 IOAPICSetupIrqs(VOID) 297 { 298 IOAPIC_ROUTE_ENTRY entry; 299 ULONG apic, pin, idx, irq, first_notcon = 1, vector, trigger; 300 301 DPRINT("Init IO_APIC IRQs\n"); 302 303 /* Setup IRQ to vector translation map */ 304 memset(&IRQVectorMap, 0, sizeof(IRQVectorMap)); 305 306 for (apic = 0; apic < IOAPICCount; apic++) 307 { 308 for (pin = 0; pin < IOAPICMap[apic].EntryCount; pin++) 309 { 310 /* 311 * add it to the IO-APIC irq-routing table 312 */ 313 memset(&entry,0,sizeof(entry)); 314 315 entry.delivery_mode = (APIC_DM_LOWEST >> 8); 316 entry.dest_mode = 1; /* logical delivery */ 317 entry.mask = 1; /* disable IRQ */ 318 entry.dest.logical.logical_dest = 0; 319 320 idx = IOAPICGetIrqEntry(apic,pin,INT_VECTORED); 321 if (idx == (ULONG)-1) 322 { 323 if (first_notcon) 324 { 325 DPRINT(" IO-APIC (apicid-pin) %u-%lu\n", IOAPICMap[apic].ApicId, pin); 326 first_notcon = 0; 327 } 328 else 329 { 330 DPRINT(", %u-%lu\n", IOAPICMap[apic].ApicId, pin); 331 } 332 continue; 333 } 334 335 trigger = IRQTrigger(idx); 336 entry.polarity = IRQPolarity(idx); 337 338 if (trigger) 339 { 340 entry.trigger = 1; 341 } 342 343 irq = Pin2Irq(idx, apic, pin); 344 345 vector = AssignIrqVector(irq); 346 entry.vector = vector; 347 348 DPRINT("Vector 0x%.08lx assigned to irq 0x%.02lx\n", vector, irq); 349 350 if (irq == 0) 351 { 352 /* Mask timer IRQ */ 353 entry.mask = 1; 354 } 355 356 if ((apic == 0) && (irq < 16)) 357 { 358 Disable8259AIrq(irq); 359 } 360 IOAPICWrite(apic, IOAPIC_REDTBL+2*pin+1, *(((PULONG)&entry)+1)); 361 IOAPICWrite(apic, IOAPIC_REDTBL+2*pin, *(((PULONG)&entry)+0)); 362 363 IrqApicMap[irq] = apic; 364 365 DPRINT("Vector %lx, Pin %lx, Irq %lx\n", vector, pin, irq); 366 } 367 } 368 } 369 370 static VOID 371 IOAPICClearPin(ULONG Apic, ULONG Pin) 372 { 373 IOAPIC_ROUTE_ENTRY Entry; 374 375 DPRINT("IOAPICClearPin(Apic %lu, Pin %lu\n", Apic, Pin); 376 /* 377 * Disable it in the IO-APIC irq-routing table 378 */ 379 memset(&Entry, 0, sizeof(Entry)); 380 Entry.mask = 1; 381 382 IOAPICWrite(Apic, IOAPIC_REDTBL + 2 * Pin, *(((PULONG)&Entry) + 0)); 383 IOAPICWrite(Apic, IOAPIC_REDTBL + 1 + 2 * Pin, *(((PULONG)&Entry) + 1)); 384 } 385 386 static VOID 387 IOAPICClear(ULONG Apic) 388 { 389 ULONG Pin; 390 391 for (Pin = 0; Pin < /*IOAPICMap[Apic].EntryCount*/24; Pin++) 392 { 393 IOAPICClearPin(Apic, Pin); 394 } 395 } 396 397 static VOID 398 IOAPICClearAll(VOID) 399 { 400 ULONG Apic; 401 402 for (Apic = 0; Apic < IOAPICCount; Apic++) 403 { 404 IOAPICClear(Apic); 405 } 406 } 407 408 VOID 409 IOAPICEnable(VOID) 410 { 411 ULONG i, tmp; 412 413 /* Setup IRQ to vector translation map */ 414 memset(&IRQVectorMap, 0, sizeof(IRQVectorMap)); 415 416 /* 417 * The number of IO-APIC IRQ registers (== #pins): 418 */ 419 for (i = 0; i < IOAPICCount; i++) 420 { 421 tmp = IOAPICRead(i, IOAPIC_VER); 422 IOAPICMap[i].EntryCount = GET_IOAPIC_MRE(tmp) + 1; 423 } 424 425 /* 426 * Do not trust the IO-APIC being empty at bootup 427 */ 428 IOAPICClearAll(); 429 } 430 431 VOID 432 IOAPICSetupIds(VOID) 433 { 434 ULONG tmp, apic, i; 435 UCHAR old_id; 436 437 /* 438 * Set the IOAPIC ID to the value stored in the MPC table. 439 */ 440 for (apic = 0; apic < IOAPICCount; apic++) 441 { 442 443 /* Read the register 0 value */ 444 tmp = IOAPICRead(apic, IOAPIC_ID); 445 446 old_id = IOAPICMap[apic].ApicId; 447 448 if (IOAPICMap[apic].ApicId >= 0xf) 449 { 450 DPRINT1("BIOS bug, IO-APIC#%lu ID is %u in the MPC table\n", apic, IOAPICMap[apic].ApicId); 451 IOAPICMap[apic].ApicId = GET_IOAPIC_ID(tmp); 452 DPRINT1(" Fixed up to %u. (Tell your hardware vendor)\n", IOAPICMap[apic].ApicId); 453 } 454 455 /* 456 * We need to adjust the IRQ routing table 457 * if the ID changed. 458 */ 459 if (old_id != IOAPICMap[apic].ApicId) 460 { 461 for (i = 0; i < IRQCount; i++) 462 { 463 if (IRQMap[i].DstApicId == old_id) 464 { 465 IRQMap[i].DstApicId = IOAPICMap[apic].ApicId; 466 } 467 } 468 } 469 470 /* 471 * Read the right value from the MPC table and 472 * write it into the ID register. 473 */ 474 DPRINT("Changing IO-APIC physical APIC ID to %u\n", IOAPICMap[apic].ApicId); 475 476 tmp &= ~IOAPIC_ID_MASK; 477 tmp |= SET_IOAPIC_ID(IOAPICMap[apic].ApicId); 478 479 IOAPICWrite(apic, IOAPIC_ID, tmp); 480 481 /* 482 * Sanity check 483 */ 484 tmp = IOAPICRead(apic, 0); 485 if (GET_IOAPIC_ID(tmp) != IOAPICMap[apic].ApicId) 486 { 487 DPRINT1("Could not set I/O APIC ID!\n"); 488 ASSERT(FALSE); 489 } 490 } 491 } 492 493 /* This is performance critical and should probably be done in assembler */ 494 VOID IOAPICMaskIrq(ULONG Irq) 495 { 496 IOAPIC_ROUTE_ENTRY Entry; 497 ULONG Apic = IrqApicMap[Irq]; 498 499 *(((PULONG)&Entry)+0) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq); 500 *(((PULONG)&Entry)+1) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq+1); 501 Entry.dest.logical.logical_dest &= ~(1 << KeGetCurrentProcessorNumber()); 502 if (Entry.dest.logical.logical_dest == 0) 503 { 504 Entry.mask = 1; 505 } 506 IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq+1, *(((PULONG)&Entry)+1)); 507 IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq, *(((PULONG)&Entry)+0)); 508 } 509 510 /* This is performance critical and should probably be done in assembler */ 511 VOID IOAPICUnmaskIrq(ULONG Irq) 512 { 513 IOAPIC_ROUTE_ENTRY Entry; 514 ULONG Apic = IrqApicMap[Irq]; 515 516 *(((PULONG)&Entry)+0) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq); 517 *(((PULONG)&Entry)+1) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq+1); 518 Entry.dest.logical.logical_dest |= 1 << KeGetCurrentProcessorNumber(); 519 Entry.mask = 0; 520 IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq+1, *(((PULONG)&Entry)+1)); 521 IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq, *(((PULONG)&Entry)+0)); 522 } 523 524 VOID IOAPICDump(VOID) 525 { 526 ULONG apic, i; 527 ULONG reg0, reg1, reg2=0; 528 529 DbgPrint("Number of MP IRQ sources: %d.\n", IRQCount); 530 for (i = 0; i < IOAPICCount; i++) 531 { 532 DbgPrint("Number of IO-APIC #%d registers: %d.\n", 533 IOAPICMap[i].ApicId, 534 IOAPICMap[i].EntryCount); 535 } 536 537 /* 538 * We are a bit conservative about what we expect. We have to 539 * know about every hardware change ASAP. 540 */ 541 DbgPrint("Testing the IO APIC.......................\n"); 542 543 for (apic = 0; apic < IOAPICCount; apic++) 544 { 545 reg0 = IOAPICRead(apic, IOAPIC_ID); 546 reg1 = IOAPICRead(apic, IOAPIC_VER); 547 if (GET_IOAPIC_VERSION(reg1) >= 0x10) 548 { 549 reg2 = IOAPICRead(apic, IOAPIC_ARB); 550 } 551 552 DbgPrint("\n"); 553 DbgPrint("IO APIC #%d......\n", IOAPICMap[apic].ApicId); 554 DbgPrint(".... register #00: %08X\n", reg0); 555 DbgPrint("....... : physical APIC id: %02X\n", GET_IOAPIC_ID(reg0)); 556 if (reg0 & 0xF0FFFFFF) 557 { 558 DbgPrint(" WARNING: Unexpected IO-APIC\n"); 559 } 560 561 DbgPrint(".... register #01: %08X\n", reg1); 562 i = GET_IOAPIC_MRE(reg1); 563 564 DbgPrint("....... : max redirection entries: %04X\n", i); 565 if ((i != 0x0f) && /* older (Neptune) boards */ 566 (i != 0x17) && /* typical ISA+PCI boards */ 567 (i != 0x1b) && /* Compaq Proliant boards */ 568 (i != 0x1f) && /* dual Xeon boards */ 569 (i != 0x22) && /* bigger Xeon boards */ 570 (i != 0x2E) && 571 (i != 0x3F)) 572 { 573 DbgPrint(" WARNING: Unexpected IO-APIC\n"); 574 } 575 576 i = GET_IOAPIC_VERSION(reg1); 577 DbgPrint("....... : IO APIC version: %04X\n", i); 578 if ((i != 0x01) && /* 82489DX IO-APICs */ 579 (i != 0x10) && /* oldest IO-APICs */ 580 (i != 0x11) && /* Pentium/Pro IO-APICs */ 581 (i != 0x13)) /* Xeon IO-APICs */ 582 { 583 DbgPrint(" WARNING: Unexpected IO-APIC\n"); 584 } 585 586 if (reg1 & 0xFF00FF00) 587 { 588 DbgPrint(" WARNING: Unexpected IO-APIC\n"); 589 } 590 591 if (GET_IOAPIC_VERSION(reg1) >= 0x10) 592 { 593 DbgPrint(".... register #02: %08X\n", reg2); 594 DbgPrint("....... : arbitration: %02X\n", 595 GET_IOAPIC_ARB(reg2)); 596 if (reg2 & 0xF0FFFFFF) 597 { 598 DbgPrint(" WARNING: Unexpected IO-APIC\n"); 599 } 600 } 601 602 DbgPrint(".... IRQ redirection table:\n"); 603 DbgPrint(" NR Log Phy Mask Trig IRR Pol" 604 " Stat Dest Deli Vect: \n"); 605 606 for (i = 0; i <= GET_IOAPIC_MRE(reg1); i++) 607 { 608 IOAPIC_ROUTE_ENTRY entry; 609 610 *(((PULONG)&entry)+0) = IOAPICRead(apic, 0x10+i*2); 611 *(((PULONG)&entry)+1) = IOAPICRead(apic, 0x11+i*2); 612 613 DbgPrint(" %02x %03X %02X ", 614 i, 615 entry.dest.logical.logical_dest, 616 entry.dest.physical.physical_dest); 617 618 DbgPrint("%C %C %1d %C %C %C %03X %02X\n", 619 (entry.mask == 0) ? 'U' : 'M', // Unmasked/masked 620 (entry.trigger == 0) ? 'E' : 'L', // Edge/level sensitive 621 entry.irr, 622 (entry.polarity == 0) ? 'H' : 'L', // Active high/active low 623 (entry.delivery_status == 0) ? 'I' : 'S', // Idle / send pending 624 (entry.dest_mode == 0) ? 'P' : 'L', // Physical logical 625 entry.delivery_mode, 626 entry.vector); 627 } 628 } 629 630 DbgPrint(".................................... done.\n"); 631 } 632 633 VOID 634 HaliReconfigurePciInterrupts(VOID) 635 { 636 ULONG i; 637 638 for (i = 0; i < IRQCount; i++) 639 { 640 if (BUSMap[IRQMap[i].SrcBusId] == MP_BUS_PCI) 641 { 642 DPRINT("%02lx: IrqType %02x, IrqFlag %04x, SrcBusId %02x" 643 ", SrcBusIrq %02x, DstApicId %02x, DstApicInt %02x\n", 644 i, IRQMap[i].IrqType, IRQMap[i].IrqFlag, IRQMap[i].SrcBusId, 645 IRQMap[i].SrcBusIrq, IRQMap[i].DstApicId, IRQMap[i].DstApicInt); 646 647 HalSetBusDataByOffset(PCIConfiguration, 648 IRQMap[i].SrcBusId, 649 (IRQMap[i].SrcBusIrq >> 2) & 0x1f, 650 &IRQMap[i].DstApicInt, 651 0x3c /*PCI_INTERRUPT_LINE*/, 652 1); 653 654 } 655 } 656 } 657 658 VOID Disable8259AIrq(ULONG irq) 659 { 660 UCHAR tmp; 661 662 if (irq >= 8) 663 { 664 tmp = READ_PORT_UCHAR((PUCHAR)0xA1); 665 tmp |= (1 << (irq - 8)); 666 WRITE_PORT_UCHAR((PUCHAR)0xA1, tmp); 667 } 668 else 669 { 670 tmp = READ_PORT_UCHAR((PUCHAR)0x21); 671 tmp |= (1 << irq); 672 WRITE_PORT_UCHAR((PUCHAR)0x21, tmp); 673 } 674 } 675 676 ULONG IOAPICRead(ULONG Apic, ULONG Offset) 677 { 678 PULONG Base; 679 680 Base = (PULONG)IOAPICMap[Apic].ApicAddress; 681 *Base = Offset; 682 return *((PULONG)((ULONG)Base + IOAPIC_IOWIN)); 683 } 684 685 VOID IOAPICWrite(ULONG Apic, ULONG Offset, ULONG Value) 686 { 687 PULONG Base; 688 689 Base = (PULONG)IOAPICMap[Apic].ApicAddress; 690 *Base = Offset; 691 *((PULONG)((ULONG)Base + IOAPIC_IOWIN)) = Value; 692 } 693 694 /* EOF */ 695