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 DPRINT("Broken MPtable reports ISA irq %d\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 DPRINT("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 DPRINT("Broken BIOS!!\n"); 121 polarity = 1; 122 break; 123 124 case 3: /* low active */ 125 polarity = 1; 126 break; 127 128 default: /* invalid */ 129 DPRINT("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 DPRINT("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 DPRINT("Broken BIOS!!\n"); 179 trigger = 1; 180 break; 181 182 case 3: /* level */ 183 trigger = 1; 184 break; 185 186 default: /* invalid */ 187 DPRINT("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 DPRINT("broken BIOS or MPTABLE parser, ayiee!!\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 DPRINT("Unknown bus type %d.\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!"); 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) %d-%d\n", IOAPICMap[apic].ApicId, pin); 326 first_notcon = 0; 327 } 328 else 329 { 330 DPRINT(", %d-%d\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%.08x assigned to irq 0x%.02x\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 %x, Pin %x, Irq %x\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 %d, Pin %d\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#%d ID is %d in the MPC table!...\n", 451 apic, IOAPICMap[apic].ApicId); 452 DPRINT1("... fixing up to %d. (tell your hw vendor)\n", 453 GET_IOAPIC_ID(tmp)); 454 IOAPICMap[apic].ApicId = GET_IOAPIC_ID(tmp); 455 } 456 457 /* 458 * We need to adjust the IRQ routing table 459 * if the ID changed. 460 */ 461 if (old_id != IOAPICMap[apic].ApicId) 462 { 463 for (i = 0; i < IRQCount; i++) 464 { 465 if (IRQMap[i].DstApicId == old_id) 466 { 467 IRQMap[i].DstApicId = IOAPICMap[apic].ApicId; 468 } 469 } 470 } 471 472 /* 473 * Read the right value from the MPC table and 474 * write it into the ID register. 475 */ 476 DPRINT("Changing IO-APIC physical APIC ID to %d\n", 477 IOAPICMap[apic].ApicId); 478 479 tmp &= ~IOAPIC_ID_MASK; 480 tmp |= SET_IOAPIC_ID(IOAPICMap[apic].ApicId); 481 482 IOAPICWrite(apic, IOAPIC_ID, tmp); 483 484 /* 485 * Sanity check 486 */ 487 tmp = IOAPICRead(apic, 0); 488 if (GET_IOAPIC_ID(tmp) != IOAPICMap[apic].ApicId) 489 { 490 DPRINT1("Could not set I/O APIC ID!\n"); 491 ASSERT(FALSE); 492 } 493 } 494 } 495 496 /* This is performance critical and should probably be done in assembler */ 497 VOID IOAPICMaskIrq(ULONG Irq) 498 { 499 IOAPIC_ROUTE_ENTRY Entry; 500 ULONG Apic = IrqApicMap[Irq]; 501 502 *(((PULONG)&Entry)+0) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq); 503 *(((PULONG)&Entry)+1) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq+1); 504 Entry.dest.logical.logical_dest &= ~(1 << KeGetCurrentProcessorNumber()); 505 if (Entry.dest.logical.logical_dest == 0) 506 { 507 Entry.mask = 1; 508 } 509 IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq+1, *(((PULONG)&Entry)+1)); 510 IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq, *(((PULONG)&Entry)+0)); 511 } 512 513 /* This is performance critical and should probably be done in assembler */ 514 VOID IOAPICUnmaskIrq(ULONG Irq) 515 { 516 IOAPIC_ROUTE_ENTRY Entry; 517 ULONG Apic = IrqApicMap[Irq]; 518 519 *(((PULONG)&Entry)+0) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq); 520 *(((PULONG)&Entry)+1) = IOAPICRead(Apic, IOAPIC_REDTBL+2*Irq+1); 521 Entry.dest.logical.logical_dest |= 1 << KeGetCurrentProcessorNumber(); 522 Entry.mask = 0; 523 IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq+1, *(((PULONG)&Entry)+1)); 524 IOAPICWrite(Apic, IOAPIC_REDTBL+2*Irq, *(((PULONG)&Entry)+0)); 525 } 526 527 VOID IOAPICDump(VOID) 528 { 529 ULONG apic, i; 530 ULONG reg0, reg1, reg2=0; 531 532 DbgPrint("Number of MP IRQ sources: %d.\n", IRQCount); 533 for (i = 0; i < IOAPICCount; i++) 534 { 535 DbgPrint("Number of IO-APIC #%d registers: %d.\n", 536 IOAPICMap[i].ApicId, 537 IOAPICMap[i].EntryCount); 538 } 539 540 /* 541 * We are a bit conservative about what we expect. We have to 542 * know about every hardware change ASAP. 543 */ 544 DbgPrint("Testing the IO APIC.......................\n"); 545 546 for (apic = 0; apic < IOAPICCount; apic++) 547 { 548 reg0 = IOAPICRead(apic, IOAPIC_ID); 549 reg1 = IOAPICRead(apic, IOAPIC_VER); 550 if (GET_IOAPIC_VERSION(reg1) >= 0x10) 551 { 552 reg2 = IOAPICRead(apic, IOAPIC_ARB); 553 } 554 555 DbgPrint("\n"); 556 DbgPrint("IO APIC #%d......\n", IOAPICMap[apic].ApicId); 557 DbgPrint(".... register #00: %08X\n", reg0); 558 DbgPrint("....... : physical APIC id: %02X\n", GET_IOAPIC_ID(reg0)); 559 if (reg0 & 0xF0FFFFFF) 560 { 561 DbgPrint(" WARNING: Unexpected IO-APIC\n"); 562 } 563 564 DbgPrint(".... register #01: %08X\n", reg1); 565 i = GET_IOAPIC_MRE(reg1); 566 567 DbgPrint("....... : max redirection entries: %04X\n", i); 568 if ((i != 0x0f) && /* older (Neptune) boards */ 569 (i != 0x17) && /* typical ISA+PCI boards */ 570 (i != 0x1b) && /* Compaq Proliant boards */ 571 (i != 0x1f) && /* dual Xeon boards */ 572 (i != 0x22) && /* bigger Xeon boards */ 573 (i != 0x2E) && 574 (i != 0x3F)) 575 { 576 DbgPrint(" WARNING: Unexpected IO-APIC\n"); 577 } 578 579 i = GET_IOAPIC_VERSION(reg1); 580 DbgPrint("....... : IO APIC version: %04X\n", i); 581 if ((i != 0x01) && /* 82489DX IO-APICs */ 582 (i != 0x10) && /* oldest IO-APICs */ 583 (i != 0x11) && /* Pentium/Pro IO-APICs */ 584 (i != 0x13)) /* Xeon IO-APICs */ 585 { 586 DbgPrint(" WARNING: Unexpected IO-APIC\n"); 587 } 588 589 if (reg1 & 0xFF00FF00) 590 { 591 DbgPrint(" WARNING: Unexpected IO-APIC\n"); 592 } 593 594 if (GET_IOAPIC_VERSION(reg1) >= 0x10) 595 { 596 DbgPrint(".... register #02: %08X\n", reg2); 597 DbgPrint("....... : arbitration: %02X\n", 598 GET_IOAPIC_ARB(reg2)); 599 if (reg2 & 0xF0FFFFFF) 600 { 601 DbgPrint(" WARNING: Unexpected IO-APIC\n"); 602 } 603 } 604 605 DbgPrint(".... IRQ redirection table:\n"); 606 DbgPrint(" NR Log Phy Mask Trig IRR Pol" 607 " Stat Dest Deli Vect: \n"); 608 609 for (i = 0; i <= GET_IOAPIC_MRE(reg1); i++) 610 { 611 IOAPIC_ROUTE_ENTRY entry; 612 613 *(((PULONG)&entry)+0) = IOAPICRead(apic, 0x10+i*2); 614 *(((PULONG)&entry)+1) = IOAPICRead(apic, 0x11+i*2); 615 616 DbgPrint(" %02x %03X %02X ", 617 i, 618 entry.dest.logical.logical_dest, 619 entry.dest.physical.physical_dest); 620 621 DbgPrint("%C %C %1d %C %C %C %03X %02X\n", 622 (entry.mask == 0) ? 'U' : 'M', // Unmasked/masked 623 (entry.trigger == 0) ? 'E' : 'L', // Edge/level sensitive 624 entry.irr, 625 (entry.polarity == 0) ? 'H' : 'L', // Active high/active low 626 (entry.delivery_status == 0) ? 'I' : 'S', // Idle / send pending 627 (entry.dest_mode == 0) ? 'P' : 'L', // Physical logical 628 entry.delivery_mode, 629 entry.vector); 630 } 631 } 632 633 DbgPrint(".................................... done.\n"); 634 } 635 636 VOID 637 HaliReconfigurePciInterrupts(VOID) 638 { 639 ULONG i; 640 641 for (i = 0; i < IRQCount; i++) 642 { 643 if (BUSMap[IRQMap[i].SrcBusId] == MP_BUS_PCI) 644 { 645 DPRINT("%02x: IrqType %02x, IrqFlag %02x, SrcBusId %02x, SrcBusIrq %02x" 646 ", DstApicId %02x, DstApicInt %02x\n", 647 i, IRQMap[i].IrqType, IRQMap[i].IrqFlag, IRQMap[i].SrcBusId, 648 IRQMap[i].SrcBusIrq, IRQMap[i].DstApicId, IRQMap[i].DstApicInt); 649 650 HalSetBusDataByOffset(PCIConfiguration, 651 IRQMap[i].SrcBusId, 652 (IRQMap[i].SrcBusIrq >> 2) & 0x1f, 653 &IRQMap[i].DstApicInt, 654 0x3c /*PCI_INTERRUPT_LINE*/, 655 1); 656 657 } 658 } 659 } 660 661 VOID Disable8259AIrq(ULONG irq) 662 { 663 UCHAR tmp; 664 665 if (irq >= 8) 666 { 667 tmp = READ_PORT_UCHAR((PUCHAR)0xA1); 668 tmp |= (1 << (irq - 8)); 669 WRITE_PORT_UCHAR((PUCHAR)0xA1, tmp); 670 } 671 else 672 { 673 tmp = READ_PORT_UCHAR((PUCHAR)0x21); 674 tmp |= (1 << irq); 675 WRITE_PORT_UCHAR((PUCHAR)0x21, tmp); 676 } 677 } 678 679 ULONG IOAPICRead(ULONG Apic, ULONG Offset) 680 { 681 PULONG Base; 682 683 Base = (PULONG)IOAPICMap[Apic].ApicAddress; 684 *Base = Offset; 685 return *((PULONG)((ULONG)Base + IOAPIC_IOWIN)); 686 } 687 688 VOID IOAPICWrite(ULONG Apic, ULONG Offset, ULONG Value) 689 { 690 PULONG Base; 691 692 Base = (PULONG)IOAPICMap[Apic].ApicAddress; 693 *Base = Offset; 694 *((PULONG)((ULONG)Base + IOAPIC_IOWIN)) = Value; 695 } 696 697 /* EOF */ 698