3 * LICENSE: GNU GPL - See COPYING in the top level directory
4 * FILE: hal/halx86/generic/apic.c
5 * PURPOSE: HAL APIC Management and Control Code
6 * PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
7 * REFERENCES: http://www.joseflores.com/docs/ExploringIrql.html
8 * http://www.codeproject.com/KB/system/soviet_kernel_hack.aspx
9 * http://bbs.unixmap.net/thread-2022-1-1.html
12 /* INCLUDES *******************************************************************/
20 /* GLOBALS ********************************************************************/
22 UCHAR HalpVectorToIndex
[256];
28 0x00, /* 0 PASSIVE_LEVEL */
29 0x3d, /* 1 APC_LEVEL */
30 0x41, /* 2 DISPATCH_LEVEL */
43 0xb1, /* 15 DEVICE IRQL */
55 0xc1, /* 27 PROFILE_LEVEL */
56 0xd1, /* 28 CLOCK2_LEVEL */
57 0xe1, /* 29 IPI_LEVEL */
58 0xef, /* 30 POWER_LEVEL */
59 0xff, /* 31 HIGH_LEVEL */
65 0, /* 00 PASSIVE_LEVEL */
69 2, /* 41 DISPATCH_LEVEL */
73 7, /* 80 DEVICE IRQL */
77 27, /* C1 PROFILE_LEVEL */
78 28, /* D1 CLOCK2_LEVEL */
79 29, /* E1 IPI_LEVEL / EF POWER_LEVEL */
80 31, /* FF HIGH_LEVEL */
84 /* PRIVATE FUNCTIONS **********************************************************/
88 IOApicRead(UCHAR Register
)
90 /* Select the register, then do the read */
91 *(volatile ULONG
*)(IOAPIC_BASE
+ IOAPIC_IOREGSEL
) = Register
;
92 return *(volatile ULONG
*)(IOAPIC_BASE
+ IOAPIC_IOWIN
);
97 IOApicWrite(UCHAR Register
, ULONG Value
)
99 /* Select the register, then do the write */
100 *(volatile ULONG
*)(IOAPIC_BASE
+ IOAPIC_IOREGSEL
) = Register
;
101 *(volatile ULONG
*)(IOAPIC_BASE
+ IOAPIC_IOWIN
) = Value
;
106 ApicGetProcessorIrql(VOID
)
108 /* Read the TPR and convert it to an IRQL */
109 return TprToIrql(ApicRead(APIC_PPR
));
114 ApicGetCurrentIrql(VOID
)
116 /* Read the TPR and convert it to an IRQL */
117 return TprToIrql(ApicRead(APIC_TPR
));
122 ApicSetCurrentIrql(KIRQL Irql
)
124 /* Convert IRQL and write the TPR */
125 ApicWrite(APIC_TPR
, IrqlToTpr(Irql
));
130 HalpIrqToVector(UCHAR Irq
)
132 IOAPIC_REDIRECTION_REGISTER ReDirReg
;
134 /* Read low dword of the redirection entry */
135 ReDirReg
.Long0
= IOApicRead(IOAPIC_REDTBL
+ 2 * Irq
);
137 /* Return the vector */
138 return (UCHAR
)ReDirReg
.Vector
;
143 HalpVectorToIrql(UCHAR Vector
)
145 return TprToIrql(Vector
>> 2);
150 HalpVectorToIrq(UCHAR Vector
)
152 return HalpVectorToIndex
[Vector
];
157 HalpInitializeLegacyPIC(VOID
)
164 /* Initialize ICW1 for master, interval 8, edge-triggered mode with ICW4 */
165 Icw1
.NeedIcw4
= TRUE
;
166 Icw1
.OperatingMode
= Cascade
;
167 Icw1
.Interval
= Interval8
;
168 Icw1
.InterruptMode
= EdgeTriggered
;
170 Icw1
.InterruptVectorAddress
= 0;
171 __outbyte(PIC1_CONTROL_PORT
, Icw1
.Bits
);
173 /* ICW2 - interrupt vector offset */
174 Icw2
.Bits
= PRIMARY_VECTOR_BASE
;
175 __outbyte(PIC1_DATA_PORT
, Icw2
.Bits
);
177 /* Connect slave to IRQ 2 */
179 Icw3
.SlaveIrq2
= TRUE
;
180 __outbyte(PIC1_DATA_PORT
, Icw3
.Bits
);
182 /* Enable 8086 mode, non-automatic EOI, non-buffered mode, non special fully nested mode */
183 Icw4
.SystemMode
= New8086Mode
;
184 Icw4
.EoiMode
= NormalEoi
;
185 Icw4
.BufferedMode
= NonBuffered
;
186 Icw4
.SpecialFullyNestedMode
= FALSE
;
188 __outbyte(PIC1_DATA_PORT
, Icw4
.Bits
);
190 /* Mask all interrupts */
191 __outbyte(PIC1_DATA_PORT
, 0xFF);
193 /* Initialize ICW1 for slave, interval 8, edge-triggered mode with ICW4 */
194 Icw1
.NeedIcw4
= TRUE
;
195 Icw1
.InterruptMode
= EdgeTriggered
;
196 Icw1
.OperatingMode
= Cascade
;
197 Icw1
.Interval
= Interval8
;
199 Icw1
.InterruptVectorAddress
= 0; /* This is only used in MCS80/85 mode */
200 __outbyte(PIC2_CONTROL_PORT
, Icw1
.Bits
);
202 /* Set interrupt vector base */
203 Icw2
.Bits
= PRIMARY_VECTOR_BASE
+ 8;
204 __outbyte(PIC2_DATA_PORT
, Icw2
.Bits
);
209 __outbyte(PIC2_DATA_PORT
, Icw3
.Bits
);
211 /* Enable 8086 mode, non-automatic EOI, non-buffered mode, non special fully nested mode */
212 Icw4
.SystemMode
= New8086Mode
;
213 Icw4
.EoiMode
= NormalEoi
;
214 Icw4
.BufferedMode
= NonBuffered
;
215 Icw4
.SpecialFullyNestedMode
= FALSE
;
217 __outbyte(PIC2_DATA_PORT
, Icw4
.Bits
);
219 /* Mask all interrupts */
220 __outbyte(PIC2_DATA_PORT
, 0xFF);
225 ApicInitializeLocalApic(ULONG Cpu
)
227 APIC_BASE_ADRESS_REGISTER BaseRegister
;
228 APIC_SPURIOUS_INERRUPT_REGISTER SpIntRegister
;
229 LVT_REGISTER LvtEntry
;
231 /* Enable the APIC if it wasn't yet */
232 BaseRegister
.Long
= __readmsr(MSR_APIC_BASE
);
233 BaseRegister
.Enable
= 1;
234 BaseRegister
.BootStrapCPUCore
= (Cpu
== 0);
235 __writemsr(MSR_APIC_BASE
, BaseRegister
.Long
);
237 /* Set spurious vector and SoftwareEnable to 1 */
238 SpIntRegister
.Long
= ApicRead(APIC_SIVR
);
239 SpIntRegister
.Vector
= APIC_SPURIOUS_VECTOR
;
240 SpIntRegister
.SoftwareEnable
= 1;
241 SpIntRegister
.FocusCPUCoreChecking
= 0;
242 ApicWrite(APIC_SIVR
, SpIntRegister
.Long
);
244 /* Set the spurious ISR */
245 KeRegisterInterruptHandler(APIC_SPURIOUS_VECTOR
, ApicSpuriousService
);
247 /* Create a template LVT */
249 LvtEntry
.Vector
= 0xFF;
250 LvtEntry
.MessageType
= APIC_MT_Fixed
;
251 LvtEntry
.DeliveryStatus
= 0;
252 LvtEntry
.RemoteIRR
= 0;
253 LvtEntry
.TriggerMode
= APIC_TGM_Edge
;
255 LvtEntry
.TimerMode
= 0;
257 /* Initalize and mask LVTs */
258 ApicWrite(APIC_TMRLVTR
, LvtEntry
.Long
);
259 ApicWrite(APIC_THRMLVTR
, LvtEntry
.Long
);
260 ApicWrite(APIC_PCLVTR
, LvtEntry
.Long
);
261 ApicWrite(APIC_EXT0LVTR
, LvtEntry
.Long
);
262 ApicWrite(APIC_EXT1LVTR
, LvtEntry
.Long
);
263 ApicWrite(APIC_EXT2LVTR
, LvtEntry
.Long
);
264 ApicWrite(APIC_EXT3LVTR
, LvtEntry
.Long
);
267 LvtEntry
.Vector
= APIC_SPURIOUS_VECTOR
;
268 LvtEntry
.MessageType
= APIC_MT_ExtInt
;
269 ApicWrite(APIC_LINT0
, LvtEntry
.Long
);
271 /* Enable LINT1 (NMI) */
273 LvtEntry
.Vector
= APIC_NMI_VECTOR
;
274 LvtEntry
.MessageType
= APIC_MT_NMI
;
275 LvtEntry
.TriggerMode
= APIC_TGM_Level
;
276 ApicWrite(APIC_LINT1
, LvtEntry
.Long
);
278 /* Enable error LVTR */
279 LvtEntry
.Vector
= APIC_ERROR_VECTOR
;
280 LvtEntry
.MessageType
= APIC_MT_Fixed
;
281 ApicWrite(APIC_ERRLVTR
, LvtEntry
.Long
);
283 /* Set the IRQL from the PCR */
284 ApicSetCurrentIrql(KeGetPcr()->Irql
);
290 ApicWriteIORedirectionEntry(
292 IOAPIC_REDIRECTION_REGISTER ReDirReg
)
294 IOApicWrite(IOAPIC_REDTBL
+ 2 * Index
, ReDirReg
.Long0
);
295 IOApicWrite(IOAPIC_REDTBL
+ 2 * Index
+ 1, ReDirReg
.Long1
);
298 IOAPIC_REDIRECTION_REGISTER
300 ApicReadIORedirectionEntry(
303 IOAPIC_REDIRECTION_REGISTER ReDirReg
;
305 ReDirReg
.Long0
= IOApicRead(IOAPIC_REDTBL
+ 2 * Index
);
306 ReDirReg
.Long1
= IOApicRead(IOAPIC_REDTBL
+ 2 * Index
+ 1);
313 HalpAllocateSystemInterrupt(
317 IOAPIC_REDIRECTION_REGISTER ReDirReg
;
320 /* Start with low vector */
321 Vector
= IrqlToTpr(Irql
);
323 /* Find an empty vector */
324 while (HalpVectorToIndex
[Vector
] != 0xFF)
328 /* Check if we went over the edge */
329 if (TprToIrql(Vector
) > Irql
)
331 /* Nothing free, return failure */
336 /* Save irq in the table */
337 HalpVectorToIndex
[Vector
] = Irq
;
339 /* Setup a redirection entry */
340 ReDirReg
.Vector
= Vector
;
341 ReDirReg
.DeliveryMode
= APIC_MT_LowestPriority
;
342 ReDirReg
.DestinationMode
= APIC_DM_Logical
;
343 ReDirReg
.DeliveryStatus
= 0;
344 ReDirReg
.Polarity
= 0;
345 ReDirReg
.RemoteIRR
= 0;
346 ReDirReg
.TriggerMode
= APIC_TGM_Edge
;
348 ReDirReg
.Reserved
= 0;
349 ReDirReg
.Destination
= 0;
351 /* Initialize entry */
352 IOApicWrite(IOAPIC_REDTBL
+ 2 * Irq
, ReDirReg
.Long0
);
353 IOApicWrite(IOAPIC_REDTBL
+ 2 * Irq
+ 1, ReDirReg
.Long1
);
360 ApicInitializeIOApic(VOID
)
363 IOAPIC_REDIRECTION_REGISTER ReDirReg
;
367 /* Map the I/O Apic page */
368 Pte
= HalAddressToPte(IOAPIC_BASE
);
369 Pte
->PageFrameNumber
= IOAPIC_PHYS_BASE
/ PAGE_SIZE
;
373 Pte
->CacheDisable
= 1;
377 /* Setup a redirection entry */
378 ReDirReg
.Vector
= 0xFF;
379 ReDirReg
.DeliveryMode
= APIC_MT_Fixed
;
380 ReDirReg
.DestinationMode
= APIC_DM_Physical
;
381 ReDirReg
.DeliveryStatus
= 0;
382 ReDirReg
.Polarity
= 0;
383 ReDirReg
.RemoteIRR
= 0;
384 ReDirReg
.TriggerMode
= APIC_TGM_Edge
;
386 ReDirReg
.Reserved
= 0;
387 ReDirReg
.Destination
= 0;
389 /* Loop all table entries */
390 for (Index
= 0; Index
< 24; Index
++)
392 /* Initialize entry */
393 IOApicWrite(IOAPIC_REDTBL
+ 2 * Index
, ReDirReg
.Long0
);
394 IOApicWrite(IOAPIC_REDTBL
+ 2 * Index
+ 1, ReDirReg
.Long1
);
397 /* Init the vactor to index table */
398 for (Vector
= 0; Vector
<= 255; Vector
++)
400 HalpVectorToIndex
[Vector
] = 0xFF;
403 // HACK: Allocate all IRQs, should rather do that on demand
404 for (Index
= 0; Index
<= 15; Index
++)
406 /* Map the IRQs to IRQLs like with the PIC */
407 HalpAllocateSystemInterrupt(Index
, 27 - Index
);
410 /* Enable the timer interrupt */
411 ReDirReg
.Vector
= APIC_CLOCK_VECTOR
;
412 ReDirReg
.DestinationMode
= APIC_DM_Logical
;
413 ReDirReg
.TriggerMode
= APIC_TGM_Edge
;
415 IOApicWrite(IOAPIC_REDTBL
+ 2 * APIC_CLOCK_INDEX
, ReDirReg
.Long0
);
421 HalpInitializePICs(IN BOOLEAN EnableInterrupts
)
425 /* Save EFlags and disable interrupts */
426 EFlags
= __readeflags();
429 /* Initialize and mask the PIC */
430 HalpInitializeLegacyPIC();
432 /* Initialize the I/O APIC */
433 ApicInitializeIOApic();
434 ApicWrite(APIC_EOI
, 0);
436 /* Register interrupt handlers */
437 KeRegisterInterruptHandler(APIC_CLOCK_VECTOR
, HalpClockInterrupt
);
439 KeRegisterInterruptHandler(APC_VECTOR
, HalpApcInterrupt
);
440 KeRegisterInterruptHandler(DPC_VECTOR
, HalpDispatchInterrupt
);
443 /* Restore interrupt state */
444 if (EnableInterrupts
) EFlags
|= EFLAGS_INTERRUPT_MASK
;
445 __writeeflags(EFlags
);
451 HalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame
)
453 ASSERT(ApicGetCurrentIrql() < APC_LEVEL
);
454 ASSERT(ApicGetProcessorIrql() == APC_LEVEL
);
464 HalpDispatchInterruptHandler(IN PKTRAP_FRAME TrapFrame
)
466 KIRQL OldIrql
= ApicGetCurrentIrql();
468 ASSERT(OldIrql
< DISPATCH_LEVEL
);
469 ASSERT(ApicGetProcessorIrql() == DISPATCH_LEVEL
);
471 ApicSetCurrentIrql(DISPATCH_LEVEL
);
473 /* Enable interrupts and call the kernel's DPC interrupt handler */
475 KiDispatchInterrupt();
478 ApicSetCurrentIrql(OldIrql
);
480 ApicWrite(APIC_EOI
, 0);
482 /* Exit the interrupt */
483 KiEoiHelper(TrapFrame
);
487 /* PUBLIC FUNCTIONS ***********************************************************/
491 HalRequestSoftwareInterrupt(IN KIRQL Irql
)
493 APIC_COMMAND_REGISTER CommandRegister
;
495 /* Setup the command register */
496 CommandRegister
.Long0
= 0;
497 CommandRegister
.Vector
= IrqlToTpr(Irql
);
498 CommandRegister
.MessageType
= APIC_MT_Fixed
;
499 CommandRegister
.TriggerMode
= APIC_TGM_Edge
;
500 CommandRegister
.DestinationShortHand
= APIC_DSH_Self
;
502 /* Write the low dword to send the interrupt */
503 ApicWrite(APIC_ICR0
, CommandRegister
.Long0
);
508 HalClearSoftwareInterrupt(
516 HalEnableSystemInterrupt(
519 IN KINTERRUPT_MODE InterruptMode
)
521 IOAPIC_REDIRECTION_REGISTER ReDirReg
;
523 ASSERT(Irql
<= HIGH_LEVEL
);
524 ASSERT((IrqlToTpr(Irql
) & 0xF0) == (Vector
& 0xF0));
526 Index
= HalpVectorToIndex
[Vector
];
528 /* Read lower dword of redirection entry */
529 ReDirReg
.Long0
= IOApicRead(IOAPIC_REDTBL
+ 2 * Index
);
531 ReDirReg
.Vector
= Vector
;
532 ReDirReg
.DeliveryMode
= APIC_MT_LowestPriority
;
533 ReDirReg
.DestinationMode
= APIC_DM_Logical
;
534 ReDirReg
.TriggerMode
= 1 - InterruptMode
;
535 ReDirReg
.Mask
= FALSE
;
537 /* Write back lower dword */
538 IOApicWrite(IOAPIC_REDTBL
+ 2 * Irql
, ReDirReg
.Long0
);
545 HalDisableSystemInterrupt(
549 IOAPIC_REDIRECTION_REGISTER ReDirReg
;
551 ASSERT(Irql
<= HIGH_LEVEL
);
553 Index
= HalpVectorToIndex
[Vector
];
555 /* Read lower dword of redirection entry */
556 ReDirReg
.Long0
= IOApicRead(IOAPIC_REDTBL
+ 2 * Index
);
561 /* Write back lower dword */
562 IOApicWrite(IOAPIC_REDTBL
+ 2 * Irql
, ReDirReg
.Long0
);
568 HalBeginSystemInterrupt(
573 /* Get the current IRQL */
574 *OldIrql
= ApicGetCurrentIrql();
576 /* Set the new IRQL */
577 ApicSetCurrentIrql(Irql
);
579 /* Turn on interrupts */
588 HalEndSystemInterrupt(
590 IN PKTRAP_FRAME TrapFrame
)
592 /* Restore the old IRQL */
593 ApicSetCurrentIrql(OldIrql
);
595 /* Write 0 to the EndOfInterruptRegister for level triggered ints */
596 ApicWrite(APIC_EOI
, 0);
602 KeGetCurrentIrql(VOID
)
604 /* Read the current TPR and convert it to an IRQL */
605 return ApicGetCurrentIrql();
614 /* Validate correct lower */
615 if (OldIrql
> ApicGetCurrentIrql())
618 KeBugCheck(IRQL_NOT_LESS_OR_EQUAL
);
621 /* Convert the new IRQL to a TPR value and write the register */
622 ApicSetCurrentIrql(OldIrql
);
632 /* Read the current IRQL */
633 OldIrql
= ApicGetCurrentIrql();
635 /* Validate correct raise */
636 if (OldIrql
> NewIrql
)
639 KeBugCheck(IRQL_NOT_GREATER_OR_EQUAL
);
642 /* Convert the new IRQL to a TPR value and write the register */
643 ApicSetCurrentIrql(NewIrql
);
645 /* Return old IRQL */
651 KeRaiseIrqlToDpcLevel(VOID
)
653 return KfRaiseIrql(DISPATCH_LEVEL
);
658 KeRaiseIrqlToSynchLevel(VOID
)
660 return KfRaiseIrql(SYNCH_LEVEL
);
663 #endif /* !_M_AMD64 */