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 *******************************************************************/
21 /* GLOBALS ********************************************************************/
24 UCHAR HalpVectorToIndex
[256];
30 0x00, /* 0 PASSIVE_LEVEL */
31 0x3d, /* 1 APC_LEVEL */
32 0x41, /* 2 DISPATCH_LEVEL */
45 0xb1, /* 15 DEVICE IRQL */
57 0xc1, /* 27 PROFILE_LEVEL */
58 0xd1, /* 28 CLOCK2_LEVEL */
59 0xe1, /* 29 IPI_LEVEL */
60 0xef, /* 30 POWER_LEVEL */
61 0xff, /* 31 HIGH_LEVEL */
67 0, /* 00 PASSIVE_LEVEL */
71 2, /* 41 DISPATCH_LEVEL */
75 7, /* 80 DEVICE IRQL */
79 27, /* C1 PROFILE_LEVEL */
80 28, /* D1 CLOCK2_LEVEL */
81 29, /* E1 IPI_LEVEL / EF POWER_LEVEL */
82 31, /* FF HIGH_LEVEL */
86 /* PRIVATE FUNCTIONS **********************************************************/
90 IOApicRead(UCHAR Register
)
92 /* Select the register, then do the read */
93 *(volatile UCHAR
*)(IOAPIC_BASE
+ IOAPIC_IOREGSEL
) = Register
;
94 return *(volatile ULONG
*)(IOAPIC_BASE
+ IOAPIC_IOWIN
);
99 IOApicWrite(UCHAR Register
, ULONG Value
)
101 /* Select the register, then do the write */
102 *(volatile UCHAR
*)(IOAPIC_BASE
+ IOAPIC_IOREGSEL
) = Register
;
103 *(volatile ULONG
*)(IOAPIC_BASE
+ IOAPIC_IOWIN
) = Value
;
110 //ApicWrite(APIC_EOI, 0);
116 ApicGetProcessorIrql(VOID
)
118 /* Read the TPR and convert it to an IRQL */
119 return TprToIrql(ApicRead(APIC_PPR
));
124 ApicGetCurrentIrql(VOID
)
127 return (KIRQL
)__readcr8();
129 // HACK: some magic to Sync VBox's APIC registers
132 /* Read the TPR and convert it to an IRQL */
133 return TprToIrql(ApicRead(APIC_TPR
));
139 ApicSetCurrentIrql(KIRQL Irql
)
144 /* Convert IRQL and write the TPR */
145 ApicWrite(APIC_TPR
, IrqlToTpr(Irql
));
151 HalpIrqToVector(UCHAR Irq
)
153 IOAPIC_REDIRECTION_REGISTER ReDirReg
;
155 /* Read low dword of the redirection entry */
156 ReDirReg
.Long0
= IOApicRead(IOAPIC_REDTBL
+ 2 * Irq
);
158 /* Return the vector */
159 return (UCHAR
)ReDirReg
.Vector
;
164 HalpVectorToIrql(UCHAR Vector
)
166 return TprToIrql(Vector
);
171 HalpVectorToIrq(UCHAR Vector
)
173 return HalpVectorToIndex
[Vector
];
178 HalpInitializeLegacyPIC(VOID
)
185 /* Initialize ICW1 for master, interval 8, edge-triggered mode with ICW4 */
186 Icw1
.NeedIcw4
= TRUE
;
187 Icw1
.OperatingMode
= Cascade
;
188 Icw1
.Interval
= Interval8
;
189 Icw1
.InterruptMode
= EdgeTriggered
;
191 Icw1
.InterruptVectorAddress
= 0;
192 __outbyte(PIC1_CONTROL_PORT
, Icw1
.Bits
);
194 /* ICW2 - interrupt vector offset */
195 Icw2
.Bits
= PRIMARY_VECTOR_BASE
;
196 __outbyte(PIC1_DATA_PORT
, Icw2
.Bits
);
198 /* Connect slave to IRQ 2 */
200 Icw3
.SlaveIrq2
= TRUE
;
201 __outbyte(PIC1_DATA_PORT
, Icw3
.Bits
);
203 /* Enable 8086 mode, non-automatic EOI, non-buffered mode, non special fully nested mode */
204 Icw4
.SystemMode
= New8086Mode
;
205 Icw4
.EoiMode
= NormalEoi
;
206 Icw4
.BufferedMode
= NonBuffered
;
207 Icw4
.SpecialFullyNestedMode
= FALSE
;
209 __outbyte(PIC1_DATA_PORT
, Icw4
.Bits
);
211 /* Mask all interrupts */
212 __outbyte(PIC1_DATA_PORT
, 0xFF);
214 /* Initialize ICW1 for slave, interval 8, edge-triggered mode with ICW4 */
215 Icw1
.NeedIcw4
= TRUE
;
216 Icw1
.InterruptMode
= EdgeTriggered
;
217 Icw1
.OperatingMode
= Cascade
;
218 Icw1
.Interval
= Interval8
;
220 Icw1
.InterruptVectorAddress
= 0; /* This is only used in MCS80/85 mode */
221 __outbyte(PIC2_CONTROL_PORT
, Icw1
.Bits
);
223 /* Set interrupt vector base */
224 Icw2
.Bits
= PRIMARY_VECTOR_BASE
+ 8;
225 __outbyte(PIC2_DATA_PORT
, Icw2
.Bits
);
230 __outbyte(PIC2_DATA_PORT
, Icw3
.Bits
);
232 /* Enable 8086 mode, non-automatic EOI, non-buffered mode, non special fully nested mode */
233 Icw4
.SystemMode
= New8086Mode
;
234 Icw4
.EoiMode
= NormalEoi
;
235 Icw4
.BufferedMode
= NonBuffered
;
236 Icw4
.SpecialFullyNestedMode
= FALSE
;
238 __outbyte(PIC2_DATA_PORT
, Icw4
.Bits
);
240 /* Mask all interrupts */
241 __outbyte(PIC2_DATA_PORT
, 0xFF);
246 ApicInitializeLocalApic(ULONG Cpu
)
248 APIC_BASE_ADRESS_REGISTER BaseRegister
;
249 APIC_SPURIOUS_INERRUPT_REGISTER SpIntRegister
;
250 LVT_REGISTER LvtEntry
;
252 /* Enable the APIC if it wasn't yet */
253 BaseRegister
.Long
= __readmsr(MSR_APIC_BASE
);
254 BaseRegister
.Enable
= 1;
255 BaseRegister
.BootStrapCPUCore
= (Cpu
== 0);
256 __writemsr(MSR_APIC_BASE
, BaseRegister
.Long
);
258 /* Set spurious vector and SoftwareEnable to 1 */
259 SpIntRegister
.Long
= ApicRead(APIC_SIVR
);
260 SpIntRegister
.Vector
= APIC_SPURIOUS_VECTOR
;
261 SpIntRegister
.SoftwareEnable
= 1;
262 SpIntRegister
.FocusCPUCoreChecking
= 0;
263 ApicWrite(APIC_SIVR
, SpIntRegister
.Long
);
265 /* Read the version and save it globally */
266 if (Cpu
== 0) ApicVersion
= ApicRead(APIC_VER
);
268 /* Set the mode to flat (max 8 CPUs supported!) */
269 ApicWrite(APIC_DFR
, APIC_DF_Flat
);
271 /* Set logical apic ID */
272 ApicWrite(APIC_LDR
, ApicLogicalId(Cpu
) << 24);
274 /* Set the spurious ISR */
275 KeRegisterInterruptHandler(APIC_SPURIOUS_VECTOR
, ApicSpuriousService
);
277 /* Create a template LVT */
279 LvtEntry
.Vector
= 0xFF;
280 LvtEntry
.MessageType
= APIC_MT_Fixed
;
281 LvtEntry
.DeliveryStatus
= 0;
282 LvtEntry
.RemoteIRR
= 0;
283 LvtEntry
.TriggerMode
= APIC_TGM_Edge
;
285 LvtEntry
.TimerMode
= 0;
287 /* Initalize and mask LVTs */
288 ApicWrite(APIC_TMRLVTR
, LvtEntry
.Long
);
289 ApicWrite(APIC_THRMLVTR
, LvtEntry
.Long
);
290 ApicWrite(APIC_PCLVTR
, LvtEntry
.Long
);
291 ApicWrite(APIC_EXT0LVTR
, LvtEntry
.Long
);
292 ApicWrite(APIC_EXT1LVTR
, LvtEntry
.Long
);
293 ApicWrite(APIC_EXT2LVTR
, LvtEntry
.Long
);
294 ApicWrite(APIC_EXT3LVTR
, LvtEntry
.Long
);
297 LvtEntry
.Vector
= APIC_SPURIOUS_VECTOR
;
298 LvtEntry
.MessageType
= APIC_MT_ExtInt
;
299 ApicWrite(APIC_LINT0
, LvtEntry
.Long
);
301 /* Enable LINT1 (NMI) */
303 LvtEntry
.Vector
= APIC_NMI_VECTOR
;
304 LvtEntry
.MessageType
= APIC_MT_NMI
;
305 LvtEntry
.TriggerMode
= APIC_TGM_Level
;
306 ApicWrite(APIC_LINT1
, LvtEntry
.Long
);
308 /* Enable error LVTR */
309 LvtEntry
.Vector
= APIC_ERROR_VECTOR
;
310 LvtEntry
.MessageType
= APIC_MT_Fixed
;
311 ApicWrite(APIC_ERRLVTR
, LvtEntry
.Long
);
313 /* Set the IRQL from the PCR */
314 ApicSetCurrentIrql(KeGetPcr()->Irql
);
320 ApicWriteIORedirectionEntry(
322 IOAPIC_REDIRECTION_REGISTER ReDirReg
)
324 IOApicWrite(IOAPIC_REDTBL
+ 2 * Index
, ReDirReg
.Long0
);
325 IOApicWrite(IOAPIC_REDTBL
+ 2 * Index
+ 1, ReDirReg
.Long1
);
328 IOAPIC_REDIRECTION_REGISTER
330 ApicReadIORedirectionEntry(
333 IOAPIC_REDIRECTION_REGISTER ReDirReg
;
335 ReDirReg
.Long0
= IOApicRead(IOAPIC_REDTBL
+ 2 * Index
);
336 ReDirReg
.Long1
= IOApicRead(IOAPIC_REDTBL
+ 2 * Index
+ 1);
343 HalpAllocateSystemInterrupt(
347 IOAPIC_REDIRECTION_REGISTER ReDirReg
;
350 /* Start with low vector */
351 Vector
= IrqlToTpr(Irql
);
353 /* Find an empty vector */
354 while (HalpVectorToIndex
[Vector
] != 0xFF)
358 /* Check if we went over the edge */
359 if (TprToIrql(Vector
) > Irql
)
361 /* Nothing free, return failure */
366 /* Save irq in the table */
367 HalpVectorToIndex
[Vector
] = Irq
;
369 /* Setup a redirection entry */
370 ReDirReg
.Vector
= Vector
;
371 ReDirReg
.DeliveryMode
= APIC_MT_LowestPriority
;
372 ReDirReg
.DestinationMode
= APIC_DM_Logical
;
373 ReDirReg
.DeliveryStatus
= 0;
374 ReDirReg
.Polarity
= 0;
375 ReDirReg
.RemoteIRR
= 0;
376 ReDirReg
.TriggerMode
= APIC_TGM_Edge
;
378 ReDirReg
.Reserved
= 0;
379 ReDirReg
.Destination
= 0;
381 /* Initialize entry */
382 IOApicWrite(IOAPIC_REDTBL
+ 2 * Irq
, ReDirReg
.Long0
);
383 IOApicWrite(IOAPIC_REDTBL
+ 2 * Irq
+ 1, ReDirReg
.Long1
);
390 ApicInitializeIOApic(VOID
)
393 IOAPIC_REDIRECTION_REGISTER ReDirReg
;
397 /* Map the I/O Apic page */
398 Pte
= HalAddressToPte(IOAPIC_BASE
);
399 Pte
->PageFrameNumber
= IOAPIC_PHYS_BASE
/ PAGE_SIZE
;
403 Pte
->CacheDisable
= 1;
407 /* Setup a redirection entry */
408 ReDirReg
.Vector
= 0xFF;
409 ReDirReg
.DeliveryMode
= APIC_MT_Fixed
;
410 ReDirReg
.DestinationMode
= APIC_DM_Physical
;
411 ReDirReg
.DeliveryStatus
= 0;
412 ReDirReg
.Polarity
= 0;
413 ReDirReg
.RemoteIRR
= 0;
414 ReDirReg
.TriggerMode
= APIC_TGM_Edge
;
416 ReDirReg
.Reserved
= 0;
417 ReDirReg
.Destination
= 0;
419 /* Loop all table entries */
420 for (Index
= 0; Index
< 24; Index
++)
422 /* Initialize entry */
423 IOApicWrite(IOAPIC_REDTBL
+ 2 * Index
, ReDirReg
.Long0
);
424 IOApicWrite(IOAPIC_REDTBL
+ 2 * Index
+ 1, ReDirReg
.Long1
);
427 /* Init the vactor to index table */
428 for (Vector
= 0; Vector
<= 255; Vector
++)
430 HalpVectorToIndex
[Vector
] = 0xFF;
433 // HACK: Allocate all IRQs, should rather do that on demand
434 for (Index
= 0; Index
<= 15; Index
++)
436 /* Map the IRQs to IRQLs like with the PIC */
437 HalpAllocateSystemInterrupt(Index
, 27 - Index
);
440 /* Enable the timer interrupt */
441 ReDirReg
.Vector
= APIC_CLOCK_VECTOR
;
442 ReDirReg
.DeliveryMode
= APIC_MT_Fixed
;
443 ReDirReg
.DestinationMode
= APIC_DM_Physical
;
444 ReDirReg
.TriggerMode
= APIC_TGM_Edge
;
446 ReDirReg
.Destination
= ApicRead(APIC_ID
);
447 IOApicWrite(IOAPIC_REDTBL
+ 2 * APIC_CLOCK_INDEX
, ReDirReg
.Long0
);
454 HalpInitializePICs(IN BOOLEAN EnableInterrupts
)
458 /* Save EFlags and disable interrupts */
459 EFlags
= __readeflags();
462 /* Initialize and mask the PIC */
463 HalpInitializeLegacyPIC();
465 /* Initialize the I/O APIC */
466 ApicInitializeIOApic();
468 /* Manually reserve some vectors */
469 HalpVectorToIndex
[APIC_CLOCK_VECTOR
] = 8;
470 HalpVectorToIndex
[APC_VECTOR
] = 99;
471 HalpVectorToIndex
[DISPATCH_VECTOR
] = 99;
473 /* Set interrupt handlers in the IDT */
474 KeRegisterInterruptHandler(APIC_CLOCK_VECTOR
, HalpClockInterrupt
);
476 KeRegisterInterruptHandler(APC_VECTOR
, HalpApcInterrupt
);
477 KeRegisterInterruptHandler(DISPATCH_VECTOR
, HalpDispatchInterrupt
);
480 /* Register the vectors for APC and dispatch interrupts */
481 HalpRegisterVector(IDT_INTERNAL
, 0, APC_VECTOR
, APC_LEVEL
);
482 HalpRegisterVector(IDT_INTERNAL
, 0, DISPATCH_VECTOR
, DISPATCH_LEVEL
);
484 /* Restore interrupt state */
485 if (EnableInterrupts
) EFlags
|= EFLAGS_INTERRUPT_MASK
;
486 __writeeflags(EFlags
);
492 HalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame
)
494 KPROCESSOR_MODE ProcessorMode
;
496 ASSERT(ApicGetCurrentIrql() < APC_LEVEL
);
497 ASSERT(ApicGetProcessorIrql() == APC_LEVEL
);
500 KiEnterInterruptTrap(TrapFrame
);
502 /* Save the old IRQL */
503 OldIrql
= ApicGetCurrentIrql();
505 /* Raise to APC_LEVEL */
506 ApicSetCurrentIrql(APC_LEVEL
);
508 /* End the interrupt */
511 /* Kernel or user APC? */
512 if (KiUserTrap(TrapFrame
)) ProcessorMode
= UserMode
;
513 else if (TrapFrame
->EFlags
& EFLAGS_V86_MASK
) ProcessorMode
= UserMode
;
514 else ProcessorMode
= KernelMode
;
516 /* Enable interrupts and call the kernel's APC interrupt handler */
518 KiDeliverApc(ProcessorMode
, NULL
, TrapFrame
);
520 /* Disable interrupts */
523 /* Restore the old IRQL */
524 ApicSetCurrentIrql(OldIrql
);
526 /* Exit the interrupt */
527 KiEoiHelper(TrapFrame
);
534 HalpDispatchInterruptHandler(IN PKTRAP_FRAME TrapFrame
)
537 ASSERT(ApicGetCurrentIrql() < DISPATCH_LEVEL
);
538 ASSERT(ApicGetProcessorIrql() == DISPATCH_LEVEL
);
541 KiEnterInterruptTrap(TrapFrame
);
543 /* Save the old IRQL */
544 OldIrql
= ApicGetCurrentIrql();
546 /* Raise to DISPATCH_LEVEL */
547 ApicSetCurrentIrql(DISPATCH_LEVEL
);
549 /* End the interrupt */
552 /* Enable interrupts and call the kernel's DPC interrupt handler */
554 KiDispatchInterrupt();
557 /* Restore the old IRQL */
558 ApicSetCurrentIrql(OldIrql
);
560 /* Exit the interrupt */
561 KiEoiHelper(TrapFrame
);
572 /* PUBLIC FUNCTIONS ***********************************************************/
576 HalRequestSoftwareInterrupt(IN KIRQL Irql
)
578 APIC_COMMAND_REGISTER CommandRegister
;
580 /* Setup the command register */
581 CommandRegister
.Long0
= 0;
582 CommandRegister
.Vector
= IrqlToTpr(Irql
);
583 CommandRegister
.MessageType
= APIC_MT_Fixed
;
584 CommandRegister
.TriggerMode
= APIC_TGM_Edge
;
585 CommandRegister
.DestinationShortHand
= APIC_DSH_Self
;
587 /* Write the low dword to send the interrupt */
588 ApicWrite(APIC_ICR0
, CommandRegister
.Long0
);
593 HalClearSoftwareInterrupt(
601 HalEnableSystemInterrupt(
604 IN KINTERRUPT_MODE InterruptMode
)
606 IOAPIC_REDIRECTION_REGISTER ReDirReg
;
607 PKPRCB Prcb
= KeGetCurrentPrcb();
609 ASSERT(Irql
<= HIGH_LEVEL
);
610 ASSERT((IrqlToTpr(Irql
) & 0xF0) == (Vector
& 0xF0));
612 /* Get the irq for this vector */
613 Index
= HalpVectorToIndex
[Vector
];
615 /* Check if its valid */
618 /* Interrupt is not in use */
622 /* Read the redirection entry */
623 ReDirReg
= ApicReadIORedirectionEntry(Index
);
625 /* Check if the interrupt was unused */
626 if (ReDirReg
.Vector
!= Vector
)
628 ReDirReg
.Vector
= Vector
;
629 ReDirReg
.DeliveryMode
= APIC_MT_LowestPriority
;
630 ReDirReg
.DestinationMode
= APIC_DM_Logical
;
631 ReDirReg
.Destination
= 0;
632 ReDirReg
.TriggerMode
= 1 - InterruptMode
;
635 /* Check if the destination is logical */
636 if (ReDirReg
.DestinationMode
= APIC_DM_Logical
)
638 /* Set the bit for this cpu */
639 ReDirReg
.Destination
|= ApicLogicalId(Prcb
->Number
);
643 ReDirReg
.Mask
= FALSE
;
645 /* Write back the entry */
646 ApicWriteIORedirectionEntry(Index
, ReDirReg
);
653 HalDisableSystemInterrupt(
657 IOAPIC_REDIRECTION_REGISTER ReDirReg
;
659 ASSERT(Irql
<= HIGH_LEVEL
);
661 Index
= HalpVectorToIndex
[Vector
];
663 /* Read lower dword of redirection entry */
664 ReDirReg
.Long0
= IOApicRead(IOAPIC_REDTBL
+ 2 * Index
);
669 /* Write back lower dword */
670 IOApicWrite(IOAPIC_REDTBL
+ 2 * Irql
, ReDirReg
.Long0
);
676 HalBeginSystemInterrupt(
681 /* Get the current IRQL */
682 *OldIrql
= ApicGetCurrentIrql();
684 /* Set the new IRQL */
685 ApicSetCurrentIrql(Irql
);
687 /* Turn on interrupts */
696 HalEndSystemInterrupt(
698 IN PKTRAP_FRAME TrapFrame
)
703 /* Restore the old IRQL */
704 ApicSetCurrentIrql(OldIrql
);
710 KeGetCurrentIrql(VOID
)
712 /* Read the current TPR and convert it to an IRQL */
713 return ApicGetCurrentIrql();
722 /* Validate correct lower */
723 if (OldIrql
> ApicGetCurrentIrql())
726 KeBugCheck(IRQL_NOT_LESS_OR_EQUAL
);
729 /* Convert the new IRQL to a TPR value and write the register */
730 ApicSetCurrentIrql(OldIrql
);
740 /* Read the current IRQL */
741 OldIrql
= ApicGetCurrentIrql();
743 /* Validate correct raise */
744 if (OldIrql
> NewIrql
)
747 KeBugCheck(IRQL_NOT_GREATER_OR_EQUAL
);
750 /* Convert the new IRQL to a TPR value and write the register */
751 ApicSetCurrentIrql(NewIrql
);
753 /* Return old IRQL */
759 KeRaiseIrqlToDpcLevel(VOID
)
761 return KfRaiseIrql(DISPATCH_LEVEL
);
766 KeRaiseIrqlToSynchLevel(VOID
)
768 return KfRaiseIrql(SYNCH_LEVEL
);
771 #endif /* !_M_AMD64 */