4 This local APIC library instance supports x2APIC capable processors
5 which have xAPIC and x2APIC modes.
7 Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
8 This program and the accompanying materials
9 are licensed and made available under the terms and conditions of the BSD License
10 which accompanies this distribution. The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 #include <Register/LocalApic.h>
20 #include <Library/BaseLib.h>
21 #include <Library/DebugLib.h>
22 #include <Library/LocalApicLib.h>
23 #include <Library/IoLib.h>
24 #include <Library/TimerLib.h>
25 #include <Library/PcdLib.h>
28 // Library internal functions
32 Read from a local APIC register.
34 This function reads from a local APIC register either in xAPIC or x2APIC mode.
35 It is required that in xAPIC mode wider registers (64-bit or 256-bit) must be
36 accessed using multiple 32-bit loads or stores, so this function only performs
39 @param MmioOffset The MMIO offset of the local APIC register in xAPIC mode.
40 It must be 16-byte aligned.
42 @return 32-bit Value read from the register.
52 ASSERT ((MmioOffset
& 0xf) == 0);
54 if (GetApicMode () == LOCAL_APIC_MODE_XAPIC
) {
55 return MmioRead32 (PcdGet32 (PcdCpuLocalApicBaseAddress
) + MmioOffset
);
58 // DFR is not supported in x2APIC mode.
60 ASSERT (MmioOffset
!= XAPIC_ICR_DFR_OFFSET
);
62 // Note that in x2APIC mode, ICR is a 64-bit MSR that needs special treatment. It
63 // is not supported in this function for simplicity.
65 ASSERT (MmioOffset
!= XAPIC_ICR_HIGH_OFFSET
);
67 MsrIndex
= (UINT32
)(MmioOffset
>> 4) + X2APIC_MSR_BASE_ADDRESS
;
68 return AsmReadMsr32 (MsrIndex
);
73 Write to a local APIC register.
75 This function writes to a local APIC register either in xAPIC or x2APIC mode.
76 It is required that in xAPIC mode wider registers (64-bit or 256-bit) must be
77 accessed using multiple 32-bit loads or stores, so this function only performs
80 if the register index is invalid or unsupported in current APIC mode, then ASSERT.
82 @param MmioOffset The MMIO offset of the local APIC register in xAPIC mode.
83 It must be 16-byte aligned.
84 @param Value Value to be written to the register.
95 ASSERT ((MmioOffset
& 0xf) == 0);
97 if (GetApicMode () == LOCAL_APIC_MODE_XAPIC
) {
98 MmioWrite32 (PcdGet32 (PcdCpuLocalApicBaseAddress
) + MmioOffset
, Value
);
101 // DFR is not supported in x2APIC mode.
103 ASSERT (MmioOffset
!= XAPIC_ICR_DFR_OFFSET
);
105 // Note that in x2APIC mode, ICR is a 64-bit MSR that needs special treatment. It
106 // is not supported in this function for simplicity.
108 ASSERT (MmioOffset
!= XAPIC_ICR_HIGH_OFFSET
);
109 ASSERT (MmioOffset
!= XAPIC_ICR_LOW_OFFSET
);
111 MsrIndex
= (UINT32
)(MmioOffset
>> 4) + X2APIC_MSR_BASE_ADDRESS
;
113 // The serializing semantics of WRMSR are relaxed when writing to the APIC registers.
114 // Use memory fence here to force the serializing semantics to be consisent with xAPIC mode.
117 AsmWriteMsr32 (MsrIndex
, Value
);
122 Send an IPI by writing to ICR.
124 This function returns after the IPI has been accepted by the target processor.
126 @param IcrLow 32-bit value to be written to the low half of ICR.
127 @param ApicId APIC ID of the target processor if this IPI is targeted for a specific processor.
136 LOCAL_APIC_ICR_LOW IcrLowReg
;
138 if (GetApicMode () == LOCAL_APIC_MODE_XAPIC
) {
139 ASSERT (ApicId
<= 0xff);
142 // For xAPIC, the act of writing to the low doubleword of the ICR causes the IPI to be sent.
144 MmioWrite32 (PcdGet32 (PcdCpuLocalApicBaseAddress
) + XAPIC_ICR_HIGH_OFFSET
, ApicId
<< 24);
145 MmioWrite32 (PcdGet32 (PcdCpuLocalApicBaseAddress
) + XAPIC_ICR_LOW_OFFSET
, IcrLow
);
147 IcrLowReg
.Uint32
= MmioRead32 (PcdGet32 (PcdCpuLocalApicBaseAddress
) + XAPIC_ICR_LOW_OFFSET
);
148 } while (IcrLowReg
.Bits
.DeliveryStatus
!= 0);
151 // For x2APIC, A single MSR write to the Interrupt Command Register is required for dispatching an
152 // interrupt in x2APIC mode.
154 MsrValue
= (((UINT64
)ApicId
) << 32) | IcrLow
;
155 AsmWriteMsr64 (X2APIC_MSR_ICR_ADDRESS
, MsrValue
);
160 // Library API implementation functions
164 Get the current local APIC mode.
166 If local APIC is disabled, then ASSERT.
168 @retval LOCAL_APIC_MODE_XAPIC current APIC mode is xAPIC.
169 @retval LOCAL_APIC_MODE_X2APIC current APIC mode is x2APIC.
177 MSR_IA32_APIC_BASE ApicBaseMsr
;
179 ApicBaseMsr
.Uint64
= AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS
);
181 // Local APIC should have been enabled
183 ASSERT (ApicBaseMsr
.Bits
.En
!= 0);
184 if (ApicBaseMsr
.Bits
.Extd
!= 0) {
185 return LOCAL_APIC_MODE_X2APIC
;
187 return LOCAL_APIC_MODE_XAPIC
;
192 Set the current local APIC mode.
194 If the specified local APIC mode is not valid, then ASSERT.
195 If the specified local APIC mode can't be set as current, then ASSERT.
197 @param ApicMode APIC mode to be set.
206 MSR_IA32_APIC_BASE ApicBaseMsr
;
208 CurrentMode
= GetApicMode ();
209 if (CurrentMode
== LOCAL_APIC_MODE_XAPIC
) {
211 case LOCAL_APIC_MODE_XAPIC
:
213 case LOCAL_APIC_MODE_X2APIC
:
214 ApicBaseMsr
.Uint64
= AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS
);
215 ApicBaseMsr
.Bits
.Extd
= 1;
216 AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS
, ApicBaseMsr
.Uint64
);
223 case LOCAL_APIC_MODE_XAPIC
:
225 // Transition from x2APIC mode to xAPIC mode is a two-step process:
226 // x2APIC -> Local APIC disabled -> xAPIC
228 ApicBaseMsr
.Uint64
= AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS
);
229 ApicBaseMsr
.Bits
.Extd
= 0;
230 ApicBaseMsr
.Bits
.En
= 0;
231 AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS
, ApicBaseMsr
.Uint64
);
232 ApicBaseMsr
.Bits
.En
= 1;
233 AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS
, ApicBaseMsr
.Uint64
);
235 case LOCAL_APIC_MODE_X2APIC
:
244 Get the initial local APIC ID of the executing processor assigned by hardware upon power on or reset.
246 In xAPIC mode, the initial local APIC ID is 8-bit, and may be different from current APIC ID.
247 In x2APIC mode, the local APIC ID can't be changed and there is no concept of initial APIC ID. In this case,
248 the 32-bit local APIC ID is returned as initial APIC ID.
250 @return 32-bit initial local APIC ID of the executing processor.
260 if (GetApicMode () == LOCAL_APIC_MODE_XAPIC
) {
261 AsmCpuid (CPUID_VERSION_INFO
, NULL
, &RegEbx
, NULL
, NULL
);
269 Get the local APIC ID of the executing processor.
271 @return 32-bit local APIC ID of the executing processor.
281 ApicId
= ReadLocalApicReg (XAPIC_ID_OFFSET
);
282 if (GetApicMode () == LOCAL_APIC_MODE_XAPIC
) {
289 Send a SMI IPI to a specified target processor.
291 This function returns after the IPI has been accepted by the target processor.
293 @param ApicId Specify the local APIC ID of the target processor.
301 LOCAL_APIC_ICR_LOW IcrLow
;
304 IcrLow
.Bits
.DeliveryMode
= LOCAL_APIC_DELIVERY_MODE_SMI
;
305 IcrLow
.Bits
.Level
= 1;
306 SendIpi (IcrLow
.Uint32
, ApicId
);
310 Send a SMI IPI to all processors excluding self.
312 This function returns after the IPI has been accepted by the target processors.
316 SendSmiIpiAllExcludingSelf (
320 LOCAL_APIC_ICR_LOW IcrLow
;
323 IcrLow
.Bits
.DeliveryMode
= LOCAL_APIC_DELIVERY_MODE_SMI
;
324 IcrLow
.Bits
.Level
= 1;
325 IcrLow
.Bits
.DestinationShorthand
= LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF
;
326 SendIpi (IcrLow
.Uint32
, 0);
330 Send an INIT IPI to a specified target processor.
332 This function returns after the IPI has been accepted by the target processor.
334 @param ApicId Specify the local APIC ID of the target processor.
342 LOCAL_APIC_ICR_LOW IcrLow
;
345 IcrLow
.Bits
.DeliveryMode
= LOCAL_APIC_DELIVERY_MODE_INIT
;
346 IcrLow
.Bits
.Level
= 1;
347 SendIpi (IcrLow
.Uint32
, ApicId
);
351 Send an INIT IPI to all processors excluding self.
353 This function returns after the IPI has been accepted by the target processors.
357 SendInitIpiAllExcludingSelf (
361 LOCAL_APIC_ICR_LOW IcrLow
;
364 IcrLow
.Bits
.DeliveryMode
= LOCAL_APIC_DELIVERY_MODE_INIT
;
365 IcrLow
.Bits
.Level
= 1;
366 IcrLow
.Bits
.DestinationShorthand
= LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF
;
367 SendIpi (IcrLow
.Uint32
, 0);
371 Send an INIT-Start-up-Start-up IPI sequence to a specified target processor.
373 This function returns after the IPI has been accepted by the target processor.
375 if StartupRoutine >= 1M, then ASSERT.
376 if StartupRoutine is not multiple of 4K, then ASSERT.
378 @param ApicId Specify the local APIC ID of the target processor.
379 @param StartupRoutine Points to a start-up routine which is below 1M physical
380 address and 4K aligned.
386 IN UINT32 StartupRoutine
389 LOCAL_APIC_ICR_LOW IcrLow
;
391 ASSERT (StartupRoutine
< 0x100000);
392 ASSERT ((StartupRoutine
& 0xfff) == 0);
394 SendInitIpi (ApicId
);
395 MicroSecondDelay (10);
397 IcrLow
.Bits
.Vector
= (StartupRoutine
>> 12);
398 IcrLow
.Bits
.DeliveryMode
= LOCAL_APIC_DELIVERY_MODE_STARTUP
;
399 IcrLow
.Bits
.Level
= 1;
400 SendIpi (IcrLow
.Uint32
, ApicId
);
401 MicroSecondDelay (200);
402 SendIpi (IcrLow
.Uint32
, ApicId
);
406 Send an INIT-Start-up-Start-up IPI sequence to all processors excluding self.
408 This function returns after the IPI has been accepted by the target processors.
410 if StartupRoutine >= 1M, then ASSERT.
411 if StartupRoutine is not multiple of 4K, then ASSERT.
413 @param StartupRoutine Points to a start-up routine which is below 1M physical
414 address and 4K aligned.
418 SendInitSipiSipiAllExcludingSelf (
419 IN UINT32 StartupRoutine
422 LOCAL_APIC_ICR_LOW IcrLow
;
424 ASSERT (StartupRoutine
< 0x100000);
425 ASSERT ((StartupRoutine
& 0xfff) == 0);
427 SendInitIpiAllExcludingSelf ();
428 MicroSecondDelay (10);
430 IcrLow
.Bits
.Vector
= (StartupRoutine
>> 12);
431 IcrLow
.Bits
.DeliveryMode
= LOCAL_APIC_DELIVERY_MODE_STARTUP
;
432 IcrLow
.Bits
.Level
= 1;
433 IcrLow
.Bits
.DestinationShorthand
= LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF
;
434 SendIpi (IcrLow
.Uint32
, 0);
435 MicroSecondDelay (200);
436 SendIpi (IcrLow
.Uint32
, 0);
440 Programming Virtual Wire Mode.
442 This function programs the local APIC for virtual wire mode following
443 the example described in chapter A.3 of the MP 1.4 spec.
445 IOxAPIC is not involved in this type of virtual wire mode.
449 ProgramVirtualWireMode (
454 LOCAL_APIC_LVT_LINT Lint
;
457 // Enable the APIC via SVR and set the spurious interrupt to use Int 00F.
459 Svr
.Uint32
= ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET
);
460 Svr
.Bits
.SpuriousVector
= 0xf;
461 Svr
.Bits
.SoftwareEnable
= 1;
462 WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET
, Svr
.Uint32
);
465 // Program the LINT0 vector entry as ExtInt. Not masked, edge, active high.
467 Lint
.Uint32
= ReadLocalApicReg (XAPIC_LINT0_VECTOR_OFFSET
);
468 Lint
.Bits
.DeliveryMode
= LOCAL_APIC_DELIVERY_MODE_EXTINT
;
469 Lint
.Bits
.InputPinPolarity
= 0;
470 Lint
.Bits
.TriggerMode
= 0;
472 WriteLocalApicReg (XAPIC_LINT0_VECTOR_OFFSET
, Lint
.Uint32
);
475 // Program the LINT0 vector entry as NMI. Not masked, edge, active high.
477 Lint
.Uint32
= ReadLocalApicReg (XAPIC_LINT1_VECTOR_OFFSET
);
478 Lint
.Bits
.DeliveryMode
= LOCAL_APIC_DELIVERY_MODE_NMI
;
479 Lint
.Bits
.InputPinPolarity
= 0;
480 Lint
.Bits
.TriggerMode
= 0;
482 WriteLocalApicReg (XAPIC_LINT1_VECTOR_OFFSET
, Lint
.Uint32
);
486 Get the divide value from the DCR (Divide Configuration Register) by which
487 the processor's bus clock is divided to form the time base for the APIC timer.
489 @return The divide value is one of 1,2,4,8,16,32,64,128.
493 GetApicTimerDivisor (
500 Dcr
.Uint32
= ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET
);
501 DivideValue
= Dcr
.Bits
.DivideValue1
| (Dcr
.Bits
.DivideValue2
<< 2);
502 DivideValue
= (DivideValue
+ 1) & 0x7;
503 return ((UINTN
)1) << DivideValue
;
507 Read the initial count value from the init-count register.
509 @return The initial count value read from the init-count register.
513 GetApicTimerInitCount (
517 return ReadLocalApicReg (XAPIC_TIMER_INIT_COUNT_OFFSET
);
521 Read the current count value from the current-count register.
523 @return The current count value read from the current-count register.
527 GetApicTimerCurrentCount (
531 return ReadLocalApicReg (XAPIC_TIMER_CURRENT_COUNT_OFFSET
);
535 Initialize the local APIC timer.
537 The local APIC timer is initialized and enabled.
539 @param DivideValue The divide value for the DCR. It is one of 1,2,4,8,16,32,64,128.
540 If it is 0, then use the current divide value in the DCR.
541 @param InitCount The initial count value.
542 @param PeriodicMode If TRUE, timer mode is peridoic. Othewise, timer mode is one-shot.
543 @param Vector The timer interrupt vector number.
547 InitializeApicTimer (
548 IN UINTN DivideValue
,
550 IN BOOLEAN PeriodicMode
,
556 LOCAL_APIC_LVT_TIMER LvtTimer
;
560 // Ensure local APIC is in software-enabled state.
562 Svr
.Uint32
= ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET
);
563 Svr
.Bits
.SoftwareEnable
= 1;
564 WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET
, Svr
.Uint32
);
567 // Program init-count register.
569 WriteLocalApicReg (XAPIC_TIMER_INIT_COUNT_OFFSET
, InitCount
);
571 if (DivideValue
!= 0) {
572 ASSERT (DivideValue
<= 128);
573 ASSERT (DivideValue
== GetPowerOfTwo32((UINT32
)DivideValue
));
574 Divisor
= (UINT32
)((HighBitSet32 ((UINT32
)DivideValue
) - 1) & 0x7);
576 Dcr
.Uint32
= ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET
);
577 Dcr
.Bits
.DivideValue1
= (Divisor
& 0x3);
578 Dcr
.Bits
.DivideValue2
= (Divisor
>> 2);
579 WriteLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET
, Dcr
.Uint32
);
583 // Enable APIC timer interrupt with specified timer mode.
585 LvtTimer
.Uint32
= ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET
);
587 LvtTimer
.Bits
.TimerMode
= 1;
589 LvtTimer
.Bits
.TimerMode
= 0;
591 LvtTimer
.Bits
.Mask
= 0;
592 LvtTimer
.Bits
.Vector
= Vector
;
593 WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET
, LvtTimer
.Uint32
);
597 Enable the local APIC timer interrupt.
601 EnableApicTimerInterrupt (
605 LOCAL_APIC_LVT_TIMER LvtTimer
;
607 LvtTimer
.Uint32
= ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET
);
608 LvtTimer
.Bits
.Mask
= 0;
609 WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET
, LvtTimer
.Uint32
);
613 Disable the local APIC timer interrupt.
617 DisableApicTimerInterrupt (
621 LOCAL_APIC_LVT_TIMER LvtTimer
;
623 LvtTimer
.Uint32
= ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET
);
624 LvtTimer
.Bits
.Mask
= 1;
625 WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET
, LvtTimer
.Uint32
);
629 Get the local APIC timer interrupt state.
631 @retval TRUE The local APIC timer interrupt is enabled.
632 @retval FALSE The local APIC timer interrupt is disabled.
636 GetApicTimerInterruptState (
640 LOCAL_APIC_LVT_TIMER LvtTimer
;
642 LvtTimer
.Uint32
= ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET
);
643 return (BOOLEAN
)(LvtTimer
.Bits
.Mask
== 0);
647 Send EOI to the local APIC.
655 WriteLocalApicReg (XAPIC_EOI_OFFSET
, 0);