/** @file | |
Local APIC Library. | |
This local APIC library instance supports x2APIC capable processors | |
which have xAPIC and x2APIC modes. | |
Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include <Register/Cpuid.h> | |
#include <Register/LocalApic.h> | |
#include <Library/BaseLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/LocalApicLib.h> | |
#include <Library/IoLib.h> | |
#include <Library/TimerLib.h> | |
#include <Library/PcdLib.h> | |
// | |
// Library internal functions | |
// | |
/** | |
Determine if the CPU supports the Local APIC Base Address MSR. | |
@retval TRUE The CPU supports the Local APIC Base Address MSR. | |
@retval FALSE The CPU does not support the Local APIC Base Address MSR. | |
**/ | |
BOOLEAN | |
LocalApicBaseAddressMsrSupported ( | |
VOID | |
) | |
{ | |
UINT32 RegEax; | |
UINTN FamilyId; | |
AsmCpuid (1, &RegEax, NULL, NULL, NULL); | |
FamilyId = BitFieldRead32 (RegEax, 8, 11); | |
if (FamilyId == 0x04 || FamilyId == 0x05) { | |
// | |
// CPUs with a FamilyId of 0x04 or 0x05 do not support the | |
// Local APIC Base Address MSR | |
// | |
return FALSE; | |
} | |
return TRUE; | |
} | |
/** | |
Retrieve the base address of local APIC. | |
@return The base address of local APIC. | |
**/ | |
UINTN | |
EFIAPI | |
GetLocalApicBaseAddress ( | |
VOID | |
) | |
{ | |
MSR_IA32_APIC_BASE ApicBaseMsr; | |
if (!LocalApicBaseAddressMsrSupported ()) { | |
// | |
// If CPU does not support Local APIC Base Address MSR, then retrieve | |
// Local APIC Base Address from PCD | |
// | |
return PcdGet32 (PcdCpuLocalApicBaseAddress); | |
} | |
ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS); | |
return (UINTN)(LShiftU64 ((UINT64) ApicBaseMsr.Bits.ApicBaseHigh, 32)) + | |
(((UINTN)ApicBaseMsr.Bits.ApicBaseLow) << 12); | |
} | |
/** | |
Set the base address of local APIC. | |
If BaseAddress is not aligned on a 4KB boundary, then ASSERT(). | |
@param[in] BaseAddress Local APIC base address to be set. | |
**/ | |
VOID | |
EFIAPI | |
SetLocalApicBaseAddress ( | |
IN UINTN BaseAddress | |
) | |
{ | |
MSR_IA32_APIC_BASE ApicBaseMsr; | |
ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0); | |
if (!LocalApicBaseAddressMsrSupported ()) { | |
// | |
// Ignore set request of the CPU does not support APIC Base Address MSR | |
// | |
return; | |
} | |
ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS); | |
ApicBaseMsr.Bits.ApicBaseLow = (UINT32) (BaseAddress >> 12); | |
ApicBaseMsr.Bits.ApicBaseHigh = (UINT32) (RShiftU64((UINT64) BaseAddress, 32)); | |
AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64); | |
} | |
/** | |
Read from a local APIC register. | |
This function reads from a local APIC register either in xAPIC or x2APIC mode. | |
It is required that in xAPIC mode wider registers (64-bit or 256-bit) must be | |
accessed using multiple 32-bit loads or stores, so this function only performs | |
32-bit read. | |
@param MmioOffset The MMIO offset of the local APIC register in xAPIC mode. | |
It must be 16-byte aligned. | |
@return 32-bit Value read from the register. | |
**/ | |
UINT32 | |
EFIAPI | |
ReadLocalApicReg ( | |
IN UINTN MmioOffset | |
) | |
{ | |
UINT32 MsrIndex; | |
ASSERT ((MmioOffset & 0xf) == 0); | |
if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) { | |
return MmioRead32 (GetLocalApicBaseAddress() + MmioOffset); | |
} else { | |
// | |
// DFR is not supported in x2APIC mode. | |
// | |
ASSERT (MmioOffset != XAPIC_ICR_DFR_OFFSET); | |
// | |
// Note that in x2APIC mode, ICR is a 64-bit MSR that needs special treatment. It | |
// is not supported in this function for simplicity. | |
// | |
ASSERT (MmioOffset != XAPIC_ICR_HIGH_OFFSET); | |
MsrIndex = (UINT32)(MmioOffset >> 4) + X2APIC_MSR_BASE_ADDRESS; | |
return AsmReadMsr32 (MsrIndex); | |
} | |
} | |
/** | |
Write to a local APIC register. | |
This function writes to a local APIC register either in xAPIC or x2APIC mode. | |
It is required that in xAPIC mode wider registers (64-bit or 256-bit) must be | |
accessed using multiple 32-bit loads or stores, so this function only performs | |
32-bit write. | |
if the register index is invalid or unsupported in current APIC mode, then ASSERT. | |
@param MmioOffset The MMIO offset of the local APIC register in xAPIC mode. | |
It must be 16-byte aligned. | |
@param Value Value to be written to the register. | |
**/ | |
VOID | |
EFIAPI | |
WriteLocalApicReg ( | |
IN UINTN MmioOffset, | |
IN UINT32 Value | |
) | |
{ | |
UINT32 MsrIndex; | |
ASSERT ((MmioOffset & 0xf) == 0); | |
if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) { | |
MmioWrite32 (GetLocalApicBaseAddress() + MmioOffset, Value); | |
} else { | |
// | |
// DFR is not supported in x2APIC mode. | |
// | |
ASSERT (MmioOffset != XAPIC_ICR_DFR_OFFSET); | |
// | |
// Note that in x2APIC mode, ICR is a 64-bit MSR that needs special treatment. It | |
// is not supported in this function for simplicity. | |
// | |
ASSERT (MmioOffset != XAPIC_ICR_HIGH_OFFSET); | |
ASSERT (MmioOffset != XAPIC_ICR_LOW_OFFSET); | |
MsrIndex = (UINT32)(MmioOffset >> 4) + X2APIC_MSR_BASE_ADDRESS; | |
// | |
// The serializing semantics of WRMSR are relaxed when writing to the APIC registers. | |
// Use memory fence here to force the serializing semantics to be consisent with xAPIC mode. | |
// | |
MemoryFence (); | |
AsmWriteMsr32 (MsrIndex, Value); | |
} | |
} | |
/** | |
Send an IPI by writing to ICR. | |
This function returns after the IPI has been accepted by the target processor. | |
@param IcrLow 32-bit value to be written to the low half of ICR. | |
@param ApicId APIC ID of the target processor if this IPI is targeted for a specific processor. | |
**/ | |
VOID | |
SendIpi ( | |
IN UINT32 IcrLow, | |
IN UINT32 ApicId | |
) | |
{ | |
UINT64 MsrValue; | |
LOCAL_APIC_ICR_LOW IcrLowReg; | |
UINTN LocalApciBaseAddress; | |
UINT32 IcrHigh; | |
BOOLEAN InterruptState; | |
// | |
// Legacy APIC or X2APIC? | |
// | |
if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) { | |
ASSERT (ApicId <= 0xff); | |
InterruptState = SaveAndDisableInterrupts (); | |
// | |
// Get base address of this LAPIC | |
// | |
LocalApciBaseAddress = GetLocalApicBaseAddress(); | |
// | |
// Save existing contents of ICR high 32 bits | |
// | |
IcrHigh = MmioRead32 (LocalApciBaseAddress + XAPIC_ICR_HIGH_OFFSET); | |
// | |
// Wait for DeliveryStatus clear in case a previous IPI | |
// is still being sent | |
// | |
do { | |
IcrLowReg.Uint32 = MmioRead32 (LocalApciBaseAddress + XAPIC_ICR_LOW_OFFSET); | |
} while (IcrLowReg.Bits.DeliveryStatus != 0); | |
// | |
// For xAPIC, the act of writing to the low doubleword of the ICR causes the IPI to be sent. | |
// | |
MmioWrite32 (LocalApciBaseAddress + XAPIC_ICR_HIGH_OFFSET, ApicId << 24); | |
MmioWrite32 (LocalApciBaseAddress + XAPIC_ICR_LOW_OFFSET, IcrLow); | |
// | |
// Wait for DeliveryStatus clear again | |
// | |
do { | |
IcrLowReg.Uint32 = MmioRead32 (LocalApciBaseAddress + XAPIC_ICR_LOW_OFFSET); | |
} while (IcrLowReg.Bits.DeliveryStatus != 0); | |
// | |
// And restore old contents of ICR high | |
// | |
MmioWrite32 (LocalApciBaseAddress + XAPIC_ICR_HIGH_OFFSET, IcrHigh); | |
SetInterruptState (InterruptState); | |
} else { | |
// | |
// For x2APIC, A single MSR write to the Interrupt Command Register is required for dispatching an | |
// interrupt in x2APIC mode. | |
// | |
MsrValue = LShiftU64 ((UINT64) ApicId, 32) | IcrLow; | |
AsmWriteMsr64 (X2APIC_MSR_ICR_ADDRESS, MsrValue); | |
} | |
} | |
// | |
// Library API implementation functions | |
// | |
/** | |
Get the current local APIC mode. | |
If local APIC is disabled, then ASSERT. | |
@retval LOCAL_APIC_MODE_XAPIC current APIC mode is xAPIC. | |
@retval LOCAL_APIC_MODE_X2APIC current APIC mode is x2APIC. | |
**/ | |
UINTN | |
EFIAPI | |
GetApicMode ( | |
VOID | |
) | |
{ | |
MSR_IA32_APIC_BASE ApicBaseMsr; | |
if (!LocalApicBaseAddressMsrSupported ()) { | |
// | |
// If CPU does not support APIC Base Address MSR, then return XAPIC mode | |
// | |
return LOCAL_APIC_MODE_XAPIC; | |
} | |
ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS); | |
// | |
// Local APIC should have been enabled | |
// | |
ASSERT (ApicBaseMsr.Bits.En != 0); | |
if (ApicBaseMsr.Bits.Extd != 0) { | |
return LOCAL_APIC_MODE_X2APIC; | |
} else { | |
return LOCAL_APIC_MODE_XAPIC; | |
} | |
} | |
/** | |
Set the current local APIC mode. | |
If the specified local APIC mode is not valid, then ASSERT. | |
If the specified local APIC mode can't be set as current, then ASSERT. | |
@param ApicMode APIC mode to be set. | |
@note This API must not be called from an interrupt handler or SMI handler. | |
It may result in unpredictable behavior. | |
**/ | |
VOID | |
EFIAPI | |
SetApicMode ( | |
IN UINTN ApicMode | |
) | |
{ | |
UINTN CurrentMode; | |
MSR_IA32_APIC_BASE ApicBaseMsr; | |
if (!LocalApicBaseAddressMsrSupported ()) { | |
// | |
// Ignore set request if the CPU does not support APIC Base Address MSR | |
// | |
return; | |
} | |
CurrentMode = GetApicMode (); | |
if (CurrentMode == LOCAL_APIC_MODE_XAPIC) { | |
switch (ApicMode) { | |
case LOCAL_APIC_MODE_XAPIC: | |
break; | |
case LOCAL_APIC_MODE_X2APIC: | |
ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS); | |
ApicBaseMsr.Bits.Extd = 1; | |
AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64); | |
break; | |
default: | |
ASSERT (FALSE); | |
} | |
} else { | |
switch (ApicMode) { | |
case LOCAL_APIC_MODE_XAPIC: | |
// | |
// Transition from x2APIC mode to xAPIC mode is a two-step process: | |
// x2APIC -> Local APIC disabled -> xAPIC | |
// | |
ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS); | |
ApicBaseMsr.Bits.Extd = 0; | |
ApicBaseMsr.Bits.En = 0; | |
AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64); | |
ApicBaseMsr.Bits.En = 1; | |
AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64); | |
break; | |
case LOCAL_APIC_MODE_X2APIC: | |
break; | |
default: | |
ASSERT (FALSE); | |
} | |
} | |
} | |
/** | |
Get the initial local APIC ID of the executing processor assigned by hardware upon power on or reset. | |
In xAPIC mode, the initial local APIC ID may be different from current APIC ID. | |
In x2APIC mode, the local APIC ID can't be changed and there is no concept of initial APIC ID. In this case, | |
the 32-bit local APIC ID is returned as initial APIC ID. | |
@return 32-bit initial local APIC ID of the executing processor. | |
**/ | |
UINT32 | |
EFIAPI | |
GetInitialApicId ( | |
VOID | |
) | |
{ | |
UINT32 ApicId; | |
UINT32 MaxCpuIdIndex; | |
UINT32 RegEbx; | |
if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) { | |
// | |
// Get the max index of basic CPUID | |
// | |
AsmCpuid (CPUID_SIGNATURE, &MaxCpuIdIndex, NULL, NULL, NULL); | |
// | |
// If CPUID Leaf B is supported, | |
// Then the initial 32-bit APIC ID = CPUID.0BH:EDX | |
// Else the initial 8-bit APIC ID = CPUID.1:EBX[31:24] | |
// | |
if (MaxCpuIdIndex >= CPUID_EXTENDED_TOPOLOGY) { | |
AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, 0, NULL, NULL, NULL, &ApicId); | |
return ApicId; | |
} | |
AsmCpuid (CPUID_VERSION_INFO, NULL, &RegEbx, NULL, NULL); | |
return RegEbx >> 24; | |
} else { | |
return GetApicId (); | |
} | |
} | |
/** | |
Get the local APIC ID of the executing processor. | |
@return 32-bit local APIC ID of the executing processor. | |
**/ | |
UINT32 | |
EFIAPI | |
GetApicId ( | |
VOID | |
) | |
{ | |
UINT32 ApicId; | |
UINT32 InitApicId; | |
ApicId = ReadLocalApicReg (XAPIC_ID_OFFSET); | |
if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) { | |
ApicId = ((InitApicId = GetInitialApicId ()) < 0x100) ? (ApicId >> 24) : InitApicId; | |
} | |
return ApicId; | |
} | |
/** | |
Get the value of the local APIC version register. | |
@return the value of the local APIC version register. | |
**/ | |
UINT32 | |
EFIAPI | |
GetApicVersion ( | |
VOID | |
) | |
{ | |
return ReadLocalApicReg (XAPIC_VERSION_OFFSET); | |
} | |
/** | |
Send a Fixed IPI to a specified target processor. | |
This function returns after the IPI has been accepted by the target processor. | |
@param ApicId The local APIC ID of the target processor. | |
@param Vector The vector number of the interrupt being sent. | |
**/ | |
VOID | |
EFIAPI | |
SendFixedIpi ( | |
IN UINT32 ApicId, | |
IN UINT8 Vector | |
) | |
{ | |
LOCAL_APIC_ICR_LOW IcrLow; | |
IcrLow.Uint32 = 0; | |
IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_FIXED; | |
IcrLow.Bits.Level = 1; | |
IcrLow.Bits.Vector = Vector; | |
SendIpi (IcrLow.Uint32, ApicId); | |
} | |
/** | |
Send a Fixed IPI to all processors excluding self. | |
This function returns after the IPI has been accepted by the target processors. | |
@param Vector The vector number of the interrupt being sent. | |
**/ | |
VOID | |
EFIAPI | |
SendFixedIpiAllExcludingSelf ( | |
IN UINT8 Vector | |
) | |
{ | |
LOCAL_APIC_ICR_LOW IcrLow; | |
IcrLow.Uint32 = 0; | |
IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_FIXED; | |
IcrLow.Bits.Level = 1; | |
IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF; | |
IcrLow.Bits.Vector = Vector; | |
SendIpi (IcrLow.Uint32, 0); | |
} | |
/** | |
Send a SMI IPI to a specified target processor. | |
This function returns after the IPI has been accepted by the target processor. | |
@param ApicId Specify the local APIC ID of the target processor. | |
**/ | |
VOID | |
EFIAPI | |
SendSmiIpi ( | |
IN UINT32 ApicId | |
) | |
{ | |
LOCAL_APIC_ICR_LOW IcrLow; | |
IcrLow.Uint32 = 0; | |
IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_SMI; | |
IcrLow.Bits.Level = 1; | |
SendIpi (IcrLow.Uint32, ApicId); | |
} | |
/** | |
Send a SMI IPI to all processors excluding self. | |
This function returns after the IPI has been accepted by the target processors. | |
**/ | |
VOID | |
EFIAPI | |
SendSmiIpiAllExcludingSelf ( | |
VOID | |
) | |
{ | |
LOCAL_APIC_ICR_LOW IcrLow; | |
IcrLow.Uint32 = 0; | |
IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_SMI; | |
IcrLow.Bits.Level = 1; | |
IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF; | |
SendIpi (IcrLow.Uint32, 0); | |
} | |
/** | |
Send an INIT IPI to a specified target processor. | |
This function returns after the IPI has been accepted by the target processor. | |
@param ApicId Specify the local APIC ID of the target processor. | |
**/ | |
VOID | |
EFIAPI | |
SendInitIpi ( | |
IN UINT32 ApicId | |
) | |
{ | |
LOCAL_APIC_ICR_LOW IcrLow; | |
IcrLow.Uint32 = 0; | |
IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_INIT; | |
IcrLow.Bits.Level = 1; | |
SendIpi (IcrLow.Uint32, ApicId); | |
} | |
/** | |
Send an INIT IPI to all processors excluding self. | |
This function returns after the IPI has been accepted by the target processors. | |
**/ | |
VOID | |
EFIAPI | |
SendInitIpiAllExcludingSelf ( | |
VOID | |
) | |
{ | |
LOCAL_APIC_ICR_LOW IcrLow; | |
IcrLow.Uint32 = 0; | |
IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_INIT; | |
IcrLow.Bits.Level = 1; | |
IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF; | |
SendIpi (IcrLow.Uint32, 0); | |
} | |
/** | |
Send an INIT-Start-up-Start-up IPI sequence to a specified target processor. | |
This function returns after the IPI has been accepted by the target processor. | |
if StartupRoutine >= 1M, then ASSERT. | |
if StartupRoutine is not multiple of 4K, then ASSERT. | |
@param ApicId Specify the local APIC ID of the target processor. | |
@param StartupRoutine Points to a start-up routine which is below 1M physical | |
address and 4K aligned. | |
**/ | |
VOID | |
EFIAPI | |
SendInitSipiSipi ( | |
IN UINT32 ApicId, | |
IN UINT32 StartupRoutine | |
) | |
{ | |
LOCAL_APIC_ICR_LOW IcrLow; | |
ASSERT (StartupRoutine < 0x100000); | |
ASSERT ((StartupRoutine & 0xfff) == 0); | |
SendInitIpi (ApicId); | |
MicroSecondDelay (PcdGet32(PcdCpuInitIpiDelayInMicroSeconds)); | |
IcrLow.Uint32 = 0; | |
IcrLow.Bits.Vector = (StartupRoutine >> 12); | |
IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_STARTUP; | |
IcrLow.Bits.Level = 1; | |
SendIpi (IcrLow.Uint32, ApicId); | |
MicroSecondDelay (200); | |
SendIpi (IcrLow.Uint32, ApicId); | |
} | |
/** | |
Send an INIT-Start-up-Start-up IPI sequence to all processors excluding self. | |
This function returns after the IPI has been accepted by the target processors. | |
if StartupRoutine >= 1M, then ASSERT. | |
if StartupRoutine is not multiple of 4K, then ASSERT. | |
@param StartupRoutine Points to a start-up routine which is below 1M physical | |
address and 4K aligned. | |
**/ | |
VOID | |
EFIAPI | |
SendInitSipiSipiAllExcludingSelf ( | |
IN UINT32 StartupRoutine | |
) | |
{ | |
LOCAL_APIC_ICR_LOW IcrLow; | |
ASSERT (StartupRoutine < 0x100000); | |
ASSERT ((StartupRoutine & 0xfff) == 0); | |
SendInitIpiAllExcludingSelf (); | |
MicroSecondDelay (PcdGet32(PcdCpuInitIpiDelayInMicroSeconds)); | |
IcrLow.Uint32 = 0; | |
IcrLow.Bits.Vector = (StartupRoutine >> 12); | |
IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_STARTUP; | |
IcrLow.Bits.Level = 1; | |
IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF; | |
SendIpi (IcrLow.Uint32, 0); | |
MicroSecondDelay (200); | |
SendIpi (IcrLow.Uint32, 0); | |
} | |
/** | |
Initialize the state of the SoftwareEnable bit in the Local APIC | |
Spurious Interrupt Vector register. | |
@param Enable If TRUE, then set SoftwareEnable to 1 | |
If FALSE, then set SoftwareEnable to 0. | |
**/ | |
VOID | |
EFIAPI | |
InitializeLocalApicSoftwareEnable ( | |
IN BOOLEAN Enable | |
) | |
{ | |
LOCAL_APIC_SVR Svr; | |
// | |
// Set local APIC software-enabled bit. | |
// | |
Svr.Uint32 = ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET); | |
if (Enable) { | |
if (Svr.Bits.SoftwareEnable == 0) { | |
Svr.Bits.SoftwareEnable = 1; | |
WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32); | |
} | |
} else { | |
if (Svr.Bits.SoftwareEnable == 1) { | |
Svr.Bits.SoftwareEnable = 0; | |
WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32); | |
} | |
} | |
} | |
/** | |
Programming Virtual Wire Mode. | |
This function programs the local APIC for virtual wire mode following | |
the example described in chapter A.3 of the MP 1.4 spec. | |
IOxAPIC is not involved in this type of virtual wire mode. | |
**/ | |
VOID | |
EFIAPI | |
ProgramVirtualWireMode ( | |
VOID | |
) | |
{ | |
LOCAL_APIC_SVR Svr; | |
LOCAL_APIC_LVT_LINT Lint; | |
// | |
// Enable the APIC via SVR and set the spurious interrupt to use Int 00F. | |
// | |
Svr.Uint32 = ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET); | |
Svr.Bits.SpuriousVector = 0xf; | |
Svr.Bits.SoftwareEnable = 1; | |
WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32); | |
// | |
// Program the LINT0 vector entry as ExtInt. Not masked, edge, active high. | |
// | |
Lint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT0_OFFSET); | |
Lint.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_EXTINT; | |
Lint.Bits.InputPinPolarity = 0; | |
Lint.Bits.TriggerMode = 0; | |
Lint.Bits.Mask = 0; | |
WriteLocalApicReg (XAPIC_LVT_LINT0_OFFSET, Lint.Uint32); | |
// | |
// Program the LINT0 vector entry as NMI. Not masked, edge, active high. | |
// | |
Lint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT1_OFFSET); | |
Lint.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_NMI; | |
Lint.Bits.InputPinPolarity = 0; | |
Lint.Bits.TriggerMode = 0; | |
Lint.Bits.Mask = 0; | |
WriteLocalApicReg (XAPIC_LVT_LINT1_OFFSET, Lint.Uint32); | |
} | |
/** | |
Disable LINT0 & LINT1 interrupts. | |
This function sets the mask flag in the LVT LINT0 & LINT1 registers. | |
**/ | |
VOID | |
EFIAPI | |
DisableLvtInterrupts ( | |
VOID | |
) | |
{ | |
LOCAL_APIC_LVT_LINT LvtLint; | |
LvtLint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT0_OFFSET); | |
LvtLint.Bits.Mask = 1; | |
WriteLocalApicReg (XAPIC_LVT_LINT0_OFFSET, LvtLint.Uint32); | |
LvtLint.Uint32 = ReadLocalApicReg (XAPIC_LVT_LINT1_OFFSET); | |
LvtLint.Bits.Mask = 1; | |
WriteLocalApicReg (XAPIC_LVT_LINT1_OFFSET, LvtLint.Uint32); | |
} | |
/** | |
Read the initial count value from the init-count register. | |
@return The initial count value read from the init-count register. | |
**/ | |
UINT32 | |
EFIAPI | |
GetApicTimerInitCount ( | |
VOID | |
) | |
{ | |
return ReadLocalApicReg (XAPIC_TIMER_INIT_COUNT_OFFSET); | |
} | |
/** | |
Read the current count value from the current-count register. | |
@return The current count value read from the current-count register. | |
**/ | |
UINT32 | |
EFIAPI | |
GetApicTimerCurrentCount ( | |
VOID | |
) | |
{ | |
return ReadLocalApicReg (XAPIC_TIMER_CURRENT_COUNT_OFFSET); | |
} | |
/** | |
Initialize the local APIC timer. | |
The local APIC timer is initialized and enabled. | |
@param DivideValue The divide value for the DCR. It is one of 1,2,4,8,16,32,64,128. | |
If it is 0, then use the current divide value in the DCR. | |
@param InitCount The initial count value. | |
@param PeriodicMode If TRUE, timer mode is peridoic. Othewise, timer mode is one-shot. | |
@param Vector The timer interrupt vector number. | |
**/ | |
VOID | |
EFIAPI | |
InitializeApicTimer ( | |
IN UINTN DivideValue, | |
IN UINT32 InitCount, | |
IN BOOLEAN PeriodicMode, | |
IN UINT8 Vector | |
) | |
{ | |
LOCAL_APIC_DCR Dcr; | |
LOCAL_APIC_LVT_TIMER LvtTimer; | |
UINT32 Divisor; | |
// | |
// Ensure local APIC is in software-enabled state. | |
// | |
InitializeLocalApicSoftwareEnable (TRUE); | |
// | |
// Program init-count register. | |
// | |
WriteLocalApicReg (XAPIC_TIMER_INIT_COUNT_OFFSET, InitCount); | |
if (DivideValue != 0) { | |
ASSERT (DivideValue <= 128); | |
ASSERT (DivideValue == GetPowerOfTwo32((UINT32)DivideValue)); | |
Divisor = (UINT32)((HighBitSet32 ((UINT32)DivideValue) - 1) & 0x7); | |
Dcr.Uint32 = ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET); | |
Dcr.Bits.DivideValue1 = (Divisor & 0x3); | |
Dcr.Bits.DivideValue2 = (Divisor >> 2); | |
WriteLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET, Dcr.Uint32); | |
} | |
// | |
// Enable APIC timer interrupt with specified timer mode. | |
// | |
LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET); | |
if (PeriodicMode) { | |
LvtTimer.Bits.TimerMode = 1; | |
} else { | |
LvtTimer.Bits.TimerMode = 0; | |
} | |
LvtTimer.Bits.Mask = 0; | |
LvtTimer.Bits.Vector = Vector; | |
WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32); | |
} | |
/** | |
Get the state of the local APIC timer. | |
This function will ASSERT if the local APIC is not software enabled. | |
@param DivideValue Return the divide value for the DCR. It is one of 1,2,4,8,16,32,64,128. | |
@param PeriodicMode Return the timer mode. If TRUE, timer mode is peridoic. Othewise, timer mode is one-shot. | |
@param Vector Return the timer interrupt vector number. | |
**/ | |
VOID | |
EFIAPI | |
GetApicTimerState ( | |
OUT UINTN *DivideValue OPTIONAL, | |
OUT BOOLEAN *PeriodicMode OPTIONAL, | |
OUT UINT8 *Vector OPTIONAL | |
) | |
{ | |
UINT32 Divisor; | |
LOCAL_APIC_DCR Dcr; | |
LOCAL_APIC_LVT_TIMER LvtTimer; | |
// | |
// Check the APIC Software Enable/Disable bit (bit 8) in Spurious-Interrupt | |
// Vector Register. | |
// This bit will be 1, if local APIC is software enabled. | |
// | |
ASSERT ((ReadLocalApicReg(XAPIC_SPURIOUS_VECTOR_OFFSET) & BIT8) != 0); | |
if (DivideValue != NULL) { | |
Dcr.Uint32 = ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET); | |
Divisor = Dcr.Bits.DivideValue1 | (Dcr.Bits.DivideValue2 << 2); | |
Divisor = (Divisor + 1) & 0x7; | |
*DivideValue = ((UINTN)1) << Divisor; | |
} | |
if (PeriodicMode != NULL || Vector != NULL) { | |
LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET); | |
if (PeriodicMode != NULL) { | |
if (LvtTimer.Bits.TimerMode == 1) { | |
*PeriodicMode = TRUE; | |
} else { | |
*PeriodicMode = FALSE; | |
} | |
} | |
if (Vector != NULL) { | |
*Vector = (UINT8) LvtTimer.Bits.Vector; | |
} | |
} | |
} | |
/** | |
Enable the local APIC timer interrupt. | |
**/ | |
VOID | |
EFIAPI | |
EnableApicTimerInterrupt ( | |
VOID | |
) | |
{ | |
LOCAL_APIC_LVT_TIMER LvtTimer; | |
LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET); | |
LvtTimer.Bits.Mask = 0; | |
WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32); | |
} | |
/** | |
Disable the local APIC timer interrupt. | |
**/ | |
VOID | |
EFIAPI | |
DisableApicTimerInterrupt ( | |
VOID | |
) | |
{ | |
LOCAL_APIC_LVT_TIMER LvtTimer; | |
LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET); | |
LvtTimer.Bits.Mask = 1; | |
WriteLocalApicReg (XAPIC_LVT_TIMER_OFFSET, LvtTimer.Uint32); | |
} | |
/** | |
Get the local APIC timer interrupt state. | |
@retval TRUE The local APIC timer interrupt is enabled. | |
@retval FALSE The local APIC timer interrupt is disabled. | |
**/ | |
BOOLEAN | |
EFIAPI | |
GetApicTimerInterruptState ( | |
VOID | |
) | |
{ | |
LOCAL_APIC_LVT_TIMER LvtTimer; | |
LvtTimer.Uint32 = ReadLocalApicReg (XAPIC_LVT_TIMER_OFFSET); | |
return (BOOLEAN)(LvtTimer.Bits.Mask == 0); | |
} | |
/** | |
Send EOI to the local APIC. | |
**/ | |
VOID | |
EFIAPI | |
SendApicEoi ( | |
VOID | |
) | |
{ | |
WriteLocalApicReg (XAPIC_EOI_OFFSET, 0); | |
} | |
/** | |
Get the 32-bit address that a device should use to send a Message Signaled | |
Interrupt (MSI) to the Local APIC of the currently executing processor. | |
@return 32-bit address used to send an MSI to the Local APIC. | |
**/ | |
UINT32 | |
EFIAPI | |
GetApicMsiAddress ( | |
VOID | |
) | |
{ | |
LOCAL_APIC_MSI_ADDRESS MsiAddress; | |
// | |
// Return address for an MSI interrupt to be delivered only to the APIC ID | |
// of the currently executing processor. | |
// | |
MsiAddress.Uint32 = 0; | |
MsiAddress.Bits.BaseAddress = 0xFEE; | |
MsiAddress.Bits.DestinationId = GetApicId (); | |
return MsiAddress.Uint32; | |
} | |
/** | |
Get the 64-bit data value that a device should use to send a Message Signaled | |
Interrupt (MSI) to the Local APIC of the currently executing processor. | |
If Vector is not in range 0x10..0xFE, then ASSERT(). | |
If DeliveryMode is not supported, then ASSERT(). | |
@param Vector The 8-bit interrupt vector associated with the MSI. | |
Must be in the range 0x10..0xFE | |
@param DeliveryMode A 3-bit value that specifies how the recept of the MSI | |
is handled. The only supported values are: | |
0: LOCAL_APIC_DELIVERY_MODE_FIXED | |
1: LOCAL_APIC_DELIVERY_MODE_LOWEST_PRIORITY | |
2: LOCAL_APIC_DELIVERY_MODE_SMI | |
4: LOCAL_APIC_DELIVERY_MODE_NMI | |
5: LOCAL_APIC_DELIVERY_MODE_INIT | |
7: LOCAL_APIC_DELIVERY_MODE_EXTINT | |
@param LevelTriggered TRUE specifies a level triggered interrupt. | |
FALSE specifies an edge triggered interrupt. | |
@param AssertionLevel Ignored if LevelTriggered is FALSE. | |
TRUE specifies a level triggered interrupt that active | |
when the interrupt line is asserted. | |
FALSE specifies a level triggered interrupt that active | |
when the interrupt line is deasserted. | |
@return 64-bit data value used to send an MSI to the Local APIC. | |
**/ | |
UINT64 | |
EFIAPI | |
GetApicMsiValue ( | |
IN UINT8 Vector, | |
IN UINTN DeliveryMode, | |
IN BOOLEAN LevelTriggered, | |
IN BOOLEAN AssertionLevel | |
) | |
{ | |
LOCAL_APIC_MSI_DATA MsiData; | |
ASSERT (Vector >= 0x10 && Vector <= 0xFE); | |
ASSERT (DeliveryMode < 8 && DeliveryMode != 6 && DeliveryMode != 3); | |
MsiData.Uint64 = 0; | |
MsiData.Bits.Vector = Vector; | |
MsiData.Bits.DeliveryMode = (UINT32)DeliveryMode; | |
if (LevelTriggered) { | |
MsiData.Bits.TriggerMode = 1; | |
if (AssertionLevel) { | |
MsiData.Bits.Level = 1; | |
} | |
} | |
return MsiData.Uint64; | |
} |