3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: hal/halx86/generic/ioapic.c
10 /* INCLUDES *****************************************************************/
16 /* GLOBALS *****************************************************************/
18 MP_CONFIGURATION_INTSRC IRQMap
[MAX_IRQ_SOURCE
]; /* Map of all IRQs */
19 ULONG IRQCount
= 0; /* Number of IRQs */
20 ULONG IrqApicMap
[MAX_IRQ_SOURCE
];
22 UCHAR BUSMap
[MAX_BUS
]; /* Map of all buses in the system */
23 UCHAR PCIBUSMap
[MAX_BUS
]; /* Map of all PCI buses in the system */
25 IOAPIC_INFO IOAPICMap
[MAX_IOAPIC
]; /* Map of all I/O APICs in the system */
26 ULONG IOAPICCount
; /* Number of I/O APICs in the system */
28 ULONG IRQVectorMap
[MAX_IRQ_SOURCE
]; /* IRQ to vector map */
30 /* EISA interrupts are always polarity zero and can be edge or level
31 * trigger depending on the ELCR value. If an interrupt is listed as
32 * EISA conforming in the MP table, that means its trigger type must
33 * be read in from the ELCR */
35 #define default_EISA_trigger(idx) (EISA_ELCR(IRQMap[idx].SrcBusIrq))
36 #define default_EISA_polarity(idx) (0)
38 /* ISA interrupts are always polarity zero edge triggered,
39 * when listed as conforming in the MP table. */
41 #define default_ISA_trigger(idx) (0)
42 #define default_ISA_polarity(idx) (0)
44 /* PCI interrupts are always polarity one level triggered,
45 * when listed as conforming in the MP table. */
47 #define default_PCI_trigger(idx) (1)
48 #define default_PCI_polarity(idx) (1)
50 /* MCA interrupts are always polarity zero level triggered,
51 * when listed as conforming in the MP table. */
53 #define default_MCA_trigger(idx) (1)
54 #define default_MCA_polarity(idx) (0)
56 /***************************************************************************/
58 extern VOID
Disable8259AIrq(ULONG irq
);
59 ULONG
IOAPICRead(ULONG Apic
, ULONG Offset
);
60 VOID
IOAPICWrite(ULONG Apic
, ULONG Offset
, ULONG Value
);
62 /* FUNCTIONS ***************************************************************/
65 * EISA Edge/Level control register, ELCR
67 static ULONG
EISA_ELCR(ULONG irq
)
71 PUCHAR port
= (PUCHAR
)(0x4d0 + (irq
>> 3));
72 return (READ_PORT_UCHAR(port
) >> (irq
& 7)) & 1;
74 DPRINT("Broken MPtable reports ISA irq %d\n", irq
);
79 IRQPolarity(ULONG idx
)
81 ULONG bus
= IRQMap
[idx
].SrcBusId
;
85 * Determine IRQ line polarity (high active or low active):
87 switch (IRQMap
[idx
].IrqFlag
& 3)
89 case 0: /* conforms, ie. bus-type dependent polarity */
93 case MP_BUS_ISA
: /* ISA pin */
94 polarity
= default_ISA_polarity(idx
);
97 case MP_BUS_EISA
: /* EISA pin */
98 polarity
= default_EISA_polarity(idx
);
101 case MP_BUS_PCI
: /* PCI pin */
102 polarity
= default_PCI_polarity(idx
);
105 case MP_BUS_MCA
: /* MCA pin */
106 polarity
= default_MCA_polarity(idx
);
110 DPRINT("Broken BIOS!!\n");
116 case 1: /* high active */
120 case 2: /* reserved */
121 DPRINT("Broken BIOS!!\n");
125 case 3: /* low active */
129 default: /* invalid */
130 DPRINT("Broken BIOS!!\n");
137 IRQTrigger(ULONG idx
)
139 ULONG bus
= IRQMap
[idx
].SrcBusId
;
143 * Determine IRQ trigger mode (edge or level sensitive):
145 switch ((IRQMap
[idx
].IrqFlag
>> 2) & 3)
147 case 0: /* conforms, ie. bus-type dependent */
151 case MP_BUS_ISA
: /* ISA pin */
152 trigger
= default_ISA_trigger(idx
);
155 case MP_BUS_EISA
: /* EISA pin */
156 trigger
= default_EISA_trigger(idx
);
159 case MP_BUS_PCI
: /* PCI pin */
160 trigger
= default_PCI_trigger(idx
);
163 case MP_BUS_MCA
: /* MCA pin */
164 trigger
= default_MCA_trigger(idx
);
168 DPRINT("Broken BIOS!!\n");
178 case 2: /* reserved */
179 DPRINT("Broken BIOS!!\n");
187 default: /* invalid */
188 DPRINT("Broken BIOS!!\n");
200 ULONG bus
= IRQMap
[idx
].SrcBusId
;
203 * Debugging check, we are in big trouble if this message pops up!
205 if (IRQMap
[idx
].DstApicInt
!= pin
)
207 DPRINT("broken BIOS or MPTABLE parser, ayiee!!\n");
212 case MP_BUS_ISA
: /* ISA pin */
215 irq
= IRQMap
[idx
].SrcBusIrq
;
218 case MP_BUS_PCI
: /* PCI pin */
220 * PCI IRQs are mapped in order
225 irq
+= IOAPICMap
[i
++].EntryCount
;
231 DPRINT("Unknown bus type %d.\n",bus
);
238 AssignIrqVector(ULONG irq
)
241 static ULONG current_vector
= FIRST_DEVICE_VECTOR
, vector_offset
= 0;
244 /* There may already have been assigned a vector for this IRQ */
245 vector
= IRQVectorMap
[irq
];
251 if (current_vector
> FIRST_SYSTEM_VECTOR
)
254 current_vector
= FIRST_DEVICE_VECTOR
+ vector_offset
;
256 else if (current_vector
== FIRST_SYSTEM_VECTOR
)
258 DPRINT1("Ran out of interrupt sources!");
262 vector
= current_vector
;
263 IRQVectorMap
[irq
] = vector
;
267 vector
= IRQ2VECTOR(irq
);
268 IRQVectorMap
[irq
] = vector
;
274 * Find the IRQ entry number of a certain pin.
277 IOAPICGetIrqEntry(ULONG apic
,
283 for (i
= 0; i
< IRQCount
; i
++)
285 if (IRQMap
[i
].IrqType
== type
&&
286 (IRQMap
[i
].DstApicId
== IOAPICMap
[apic
].ApicId
|| IRQMap
[i
].DstApicId
== MP_APIC_ALL
) &&
287 IRQMap
[i
].DstApicInt
== pin
)
297 IOAPICSetupIrqs(VOID
)
299 IOAPIC_ROUTE_ENTRY entry
;
300 ULONG apic
, pin
, idx
, irq
, first_notcon
= 1, vector
, trigger
;
302 DPRINT("Init IO_APIC IRQs\n");
304 /* Setup IRQ to vector translation map */
305 memset(&IRQVectorMap
, 0, sizeof(IRQVectorMap
));
307 for (apic
= 0; apic
< IOAPICCount
; apic
++)
309 for (pin
= 0; pin
< IOAPICMap
[apic
].EntryCount
; pin
++)
312 * add it to the IO-APIC irq-routing table
314 memset(&entry
,0,sizeof(entry
));
316 entry
.delivery_mode
= (APIC_DM_LOWEST
>> 8);
317 entry
.dest_mode
= 1; /* logical delivery */
318 entry
.mask
= 1; /* disable IRQ */
319 entry
.dest
.logical
.logical_dest
= 0;
321 idx
= IOAPICGetIrqEntry(apic
,pin
,INT_VECTORED
);
322 if (idx
== (ULONG
)-1)
326 DPRINT(" IO-APIC (apicid-pin) %d-%d\n", IOAPICMap
[apic
].ApicId
, pin
);
331 DPRINT(", %d-%d\n", IOAPICMap
[apic
].ApicId
, pin
);
336 trigger
= IRQTrigger(idx
);
337 entry
.polarity
= IRQPolarity(idx
);
344 irq
= Pin2Irq(idx
, apic
, pin
);
346 vector
= AssignIrqVector(irq
);
347 entry
.vector
= vector
;
349 DPRINT("vector 0x%.08x assigned to irq 0x%.02x\n", vector
, irq
);
357 if ((apic
== 0) && (irq
< 16))
359 Disable8259AIrq(irq
);
361 IOAPICWrite(apic
, IOAPIC_REDTBL
+2*pin
+1, *(((PULONG
)&entry
)+1));
362 IOAPICWrite(apic
, IOAPIC_REDTBL
+2*pin
, *(((PULONG
)&entry
)+0));
364 IrqApicMap
[irq
] = apic
;
366 DPRINT("Vector %x, Pin %x, Irq %x\n", vector
, pin
, irq
);
372 IOAPICClearPin(ULONG Apic
, ULONG Pin
)
374 IOAPIC_ROUTE_ENTRY Entry
;
376 DPRINT("IOAPICClearPin(Apic %d, Pin %d\n", Apic
, Pin
);
378 * Disable it in the IO-APIC irq-routing table
380 memset(&Entry
, 0, sizeof(Entry
));
383 IOAPICWrite(Apic
, IOAPIC_REDTBL
+ 2 * Pin
, *(((PULONG
)&Entry
) + 0));
384 IOAPICWrite(Apic
, IOAPIC_REDTBL
+ 1 + 2 * Pin
, *(((PULONG
)&Entry
) + 1));
388 IOAPICClear(ULONG Apic
)
392 for (Pin
= 0; Pin
< /*IOAPICMap[Apic].EntryCount*/24; Pin
++)
394 IOAPICClearPin(Apic
, Pin
);
403 for (Apic
= 0; Apic
< IOAPICCount
; Apic
++)
414 /* Setup IRQ to vector translation map */
415 memset(&IRQVectorMap
, 0, sizeof(IRQVectorMap
));
418 * The number of IO-APIC IRQ registers (== #pins):
420 for (i
= 0; i
< IOAPICCount
; i
++)
422 tmp
= IOAPICRead(i
, IOAPIC_VER
);
423 IOAPICMap
[i
].EntryCount
= GET_IOAPIC_MRE(tmp
) + 1;
427 * Do not trust the IO-APIC being empty at bootup
439 * Set the IOAPIC ID to the value stored in the MPC table.
441 for (apic
= 0; apic
< IOAPICCount
; apic
++)
444 /* Read the register 0 value */
445 tmp
= IOAPICRead(apic
, IOAPIC_ID
);
447 old_id
= IOAPICMap
[apic
].ApicId
;
449 if (IOAPICMap
[apic
].ApicId
>= 0xf)
451 DPRINT1("BIOS bug, IO-APIC#%d ID is %d in the MPC table!...\n",
452 apic
, IOAPICMap
[apic
].ApicId
);
453 DPRINT1("... fixing up to %d. (tell your hw vendor)\n",
455 IOAPICMap
[apic
].ApicId
= GET_IOAPIC_ID(tmp
);
459 * We need to adjust the IRQ routing table
462 if (old_id
!= IOAPICMap
[apic
].ApicId
)
464 for (i
= 0; i
< IRQCount
; i
++)
466 if (IRQMap
[i
].DstApicId
== old_id
)
468 IRQMap
[i
].DstApicId
= IOAPICMap
[apic
].ApicId
;
474 * Read the right value from the MPC table and
475 * write it into the ID register.
477 DPRINT("Changing IO-APIC physical APIC ID to %d\n",
478 IOAPICMap
[apic
].ApicId
);
480 tmp
&= ~IOAPIC_ID_MASK
;
481 tmp
|= SET_IOAPIC_ID(IOAPICMap
[apic
].ApicId
);
483 IOAPICWrite(apic
, IOAPIC_ID
, tmp
);
488 tmp
= IOAPICRead(apic
, 0);
489 if (GET_IOAPIC_ID(tmp
) != IOAPICMap
[apic
].ApicId
)
491 DPRINT1("Could not set I/O APIC ID!\n");
497 /* This is performance critical and should probably be done in assembler */
498 VOID
IOAPICMaskIrq(ULONG Irq
)
500 IOAPIC_ROUTE_ENTRY Entry
;
501 ULONG Apic
= IrqApicMap
[Irq
];
503 *(((PULONG
)&Entry
)+0) = IOAPICRead(Apic
, IOAPIC_REDTBL
+2*Irq
);
504 *(((PULONG
)&Entry
)+1) = IOAPICRead(Apic
, IOAPIC_REDTBL
+2*Irq
+1);
505 Entry
.dest
.logical
.logical_dest
&= ~(1 << KeGetCurrentProcessorNumber());
506 if (Entry
.dest
.logical
.logical_dest
== 0)
510 IOAPICWrite(Apic
, IOAPIC_REDTBL
+2*Irq
+1, *(((PULONG
)&Entry
)+1));
511 IOAPICWrite(Apic
, IOAPIC_REDTBL
+2*Irq
, *(((PULONG
)&Entry
)+0));
514 /* This is performance critical and should probably be done in assembler */
515 VOID
IOAPICUnmaskIrq(ULONG Irq
)
517 IOAPIC_ROUTE_ENTRY Entry
;
518 ULONG Apic
= IrqApicMap
[Irq
];
520 *(((PULONG
)&Entry
)+0) = IOAPICRead(Apic
, IOAPIC_REDTBL
+2*Irq
);
521 *(((PULONG
)&Entry
)+1) = IOAPICRead(Apic
, IOAPIC_REDTBL
+2*Irq
+1);
522 Entry
.dest
.logical
.logical_dest
|= 1 << KeGetCurrentProcessorNumber();
524 IOAPICWrite(Apic
, IOAPIC_REDTBL
+2*Irq
+1, *(((PULONG
)&Entry
)+1));
525 IOAPICWrite(Apic
, IOAPIC_REDTBL
+2*Irq
, *(((PULONG
)&Entry
)+0));
528 VOID
IOAPICDump(VOID
)
531 ULONG reg0
, reg1
, reg2
=0;
533 DbgPrint("Number of MP IRQ sources: %d.\n", IRQCount
);
534 for (i
= 0; i
< IOAPICCount
; i
++)
536 DbgPrint("Number of IO-APIC #%d registers: %d.\n",
538 IOAPICMap
[i
].EntryCount
);
542 * We are a bit conservative about what we expect. We have to
543 * know about every hardware change ASAP.
545 DbgPrint("Testing the IO APIC.......................\n");
547 for (apic
= 0; apic
< IOAPICCount
; apic
++)
549 reg0
= IOAPICRead(apic
, IOAPIC_ID
);
550 reg1
= IOAPICRead(apic
, IOAPIC_VER
);
551 if (GET_IOAPIC_VERSION(reg1
) >= 0x10)
553 reg2
= IOAPICRead(apic
, IOAPIC_ARB
);
557 DbgPrint("IO APIC #%d......\n", IOAPICMap
[apic
].ApicId
);
558 DbgPrint(".... register #00: %08X\n", reg0
);
559 DbgPrint("....... : physical APIC id: %02X\n", GET_IOAPIC_ID(reg0
));
560 if (reg0
& 0xF0FFFFFF)
562 DbgPrint(" WARNING: Unexpected IO-APIC\n");
565 DbgPrint(".... register #01: %08X\n", reg1
);
566 i
= GET_IOAPIC_MRE(reg1
);
568 DbgPrint("....... : max redirection entries: %04X\n", i
);
569 if ((i
!= 0x0f) && /* older (Neptune) boards */
570 (i
!= 0x17) && /* typical ISA+PCI boards */
571 (i
!= 0x1b) && /* Compaq Proliant boards */
572 (i
!= 0x1f) && /* dual Xeon boards */
573 (i
!= 0x22) && /* bigger Xeon boards */
577 DbgPrint(" WARNING: Unexpected IO-APIC\n");
580 i
= GET_IOAPIC_VERSION(reg1
);
581 DbgPrint("....... : IO APIC version: %04X\n", i
);
582 if ((i
!= 0x01) && /* 82489DX IO-APICs */
583 (i
!= 0x10) && /* oldest IO-APICs */
584 (i
!= 0x11) && /* Pentium/Pro IO-APICs */
585 (i
!= 0x13)) /* Xeon IO-APICs */
587 DbgPrint(" WARNING: Unexpected IO-APIC\n");
590 if (reg1
& 0xFF00FF00)
592 DbgPrint(" WARNING: Unexpected IO-APIC\n");
595 if (GET_IOAPIC_VERSION(reg1
) >= 0x10)
597 DbgPrint(".... register #02: %08X\n", reg2
);
598 DbgPrint("....... : arbitration: %02X\n",
599 GET_IOAPIC_ARB(reg2
));
600 if (reg2
& 0xF0FFFFFF)
602 DbgPrint(" WARNING: Unexpected IO-APIC\n");
606 DbgPrint(".... IRQ redirection table:\n");
607 DbgPrint(" NR Log Phy Mask Trig IRR Pol"
608 " Stat Dest Deli Vect: \n");
610 for (i
= 0; i
<= GET_IOAPIC_MRE(reg1
); i
++)
612 IOAPIC_ROUTE_ENTRY entry
;
614 *(((PULONG
)&entry
)+0) = IOAPICRead(apic
, 0x10+i
*2);
615 *(((PULONG
)&entry
)+1) = IOAPICRead(apic
, 0x11+i
*2);
617 DbgPrint(" %02x %03X %02X ",
619 entry
.dest
.logical
.logical_dest
,
620 entry
.dest
.physical
.physical_dest
);
622 DbgPrint("%C %C %1d %C %C %C %03X %02X\n",
623 (entry
.mask
== 0) ? 'U' : 'M', // Unmasked/masked
624 (entry
.trigger
== 0) ? 'E' : 'L', // Edge/level sensitive
626 (entry
.polarity
== 0) ? 'H' : 'L', // Active high/active low
627 (entry
.delivery_status
== 0) ? 'I' : 'S', // Idle / send pending
628 (entry
.dest_mode
== 0) ? 'P' : 'L', // Physical logical
634 DbgPrint(".................................... done.\n");
638 HaliReconfigurePciInterrupts(VOID
)
642 for (i
= 0; i
< IRQCount
; i
++)
644 if (BUSMap
[IRQMap
[i
].SrcBusId
] == MP_BUS_PCI
)
646 DPRINT("%02x: IrqType %02x, IrqFlag %02x, SrcBusId %02x, SrcBusIrq %02x"
647 ", DstApicId %02x, DstApicInt %02x\n",
648 i
, IRQMap
[i
].IrqType
, IRQMap
[i
].IrqFlag
, IRQMap
[i
].SrcBusId
,
649 IRQMap
[i
].SrcBusIrq
, IRQMap
[i
].DstApicId
, IRQMap
[i
].DstApicInt
);
651 if(1 != HalSetBusDataByOffset(PCIConfiguration
,
653 (IRQMap
[i
].SrcBusIrq
>> 2) & 0x1f,
654 &IRQMap
[i
].DstApicInt
,
655 0x3c /*PCI_INTERRUPT_LINE*/,
664 VOID
Disable8259AIrq(ULONG irq
)
670 tmp
= READ_PORT_UCHAR((PUCHAR
)0xA1);
671 tmp
|= (1 << (irq
- 8));
672 WRITE_PORT_UCHAR((PUCHAR
)0xA1, tmp
);
676 tmp
= READ_PORT_UCHAR((PUCHAR
)0x21);
678 WRITE_PORT_UCHAR((PUCHAR
)0x21, tmp
);
682 ULONG
IOAPICRead(ULONG Apic
, ULONG Offset
)
686 Base
= (PULONG
)IOAPICMap
[Apic
].ApicAddress
;
688 return *((PULONG
)((ULONG
)Base
+ IOAPIC_IOWIN
));
691 VOID
IOAPICWrite(ULONG Apic
, ULONG Offset
, ULONG Value
)
695 Base
= (PULONG
)IOAPICMap
[Apic
].ApicAddress
;
697 *((PULONG
)((ULONG
)Base
+ IOAPIC_IOWIN
)) = Value
;