This local APIC library instance supports x2APIC capable processors\r
which have xAPIC and x2APIC modes.\r
\r
- Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>\r
- This program and the accompanying materials\r
- are licensed and made available under the terms and conditions of the BSD License\r
- which accompanies this distribution. The full text of the license may be found at\r
- http://opensource.org/licenses/bsd-license.php\r
+ Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR>\r
+ Copyright (c) 2017, AMD Inc. All rights reserved.<BR>\r
\r
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
- WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
+#include <Register/Cpuid.h>\r
+#include <Register/Amd/Cpuid.h>\r
+#include <Register/Msr.h>\r
#include <Register/LocalApic.h>\r
\r
#include <Library/BaseLib.h>\r
// Library internal functions\r
//\r
\r
+/**\r
+ Determine if the standard CPU signature is "AuthenticAMD".\r
+\r
+ @retval TRUE The CPU signature matches.\r
+ @retval FALSE The CPU signature does not match.\r
+\r
+**/\r
+BOOLEAN\r
+StandardSignatureIsAuthenticAMD (\r
+ VOID\r
+ )\r
+{\r
+ UINT32 RegEbx;\r
+ UINT32 RegEcx;\r
+ UINT32 RegEdx;\r
+\r
+ AsmCpuid (CPUID_SIGNATURE, NULL, &RegEbx, &RegEcx, &RegEdx);\r
+ return (RegEbx == CPUID_SIGNATURE_AUTHENTIC_AMD_EBX &&\r
+ RegEcx == CPUID_SIGNATURE_AUTHENTIC_AMD_ECX &&\r
+ RegEdx == CPUID_SIGNATURE_AUTHENTIC_AMD_EDX);\r
+}\r
+\r
+/**\r
+ Determine if the CPU supports the Local APIC Base Address MSR.\r
+\r
+ @retval TRUE The CPU supports the Local APIC Base Address MSR.\r
+ @retval FALSE The CPU does not support the Local APIC Base Address MSR.\r
+\r
+**/\r
+BOOLEAN\r
+LocalApicBaseAddressMsrSupported (\r
+ VOID\r
+ )\r
+{\r
+ UINT32 RegEax;\r
+ UINTN FamilyId;\r
+\r
+ AsmCpuid (1, &RegEax, NULL, NULL, NULL);\r
+ FamilyId = BitFieldRead32 (RegEax, 8, 11);\r
+ if (FamilyId == 0x04 || FamilyId == 0x05) {\r
+ //\r
+ // CPUs with a FamilyId of 0x04 or 0x05 do not support the\r
+ // Local APIC Base Address MSR\r
+ //\r
+ return FALSE;\r
+ }\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Retrieve the base address of local APIC.\r
+\r
+ @return The base address of local APIC.\r
+\r
+**/\r
+UINTN\r
+EFIAPI\r
+GetLocalApicBaseAddress (\r
+ VOID\r
+ )\r
+{\r
+ MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr;\r
+\r
+ if (!LocalApicBaseAddressMsrSupported ()) {\r
+ //\r
+ // If CPU does not support Local APIC Base Address MSR, then retrieve\r
+ // Local APIC Base Address from PCD\r
+ //\r
+ return PcdGet32 (PcdCpuLocalApicBaseAddress);\r
+ }\r
+\r
+ ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);\r
+\r
+ return (UINTN)(LShiftU64 ((UINT64) ApicBaseMsr.Bits.ApicBaseHi, 32)) +\r
+ (((UINTN)ApicBaseMsr.Bits.ApicBase) << 12);\r
+}\r
+\r
+/**\r
+ Set the base address of local APIC.\r
+\r
+ If BaseAddress is not aligned on a 4KB boundary, then ASSERT().\r
+\r
+ @param[in] BaseAddress Local APIC base address to be set.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+SetLocalApicBaseAddress (\r
+ IN UINTN BaseAddress\r
+ )\r
+{\r
+ MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr;\r
+\r
+ ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);\r
+\r
+ if (!LocalApicBaseAddressMsrSupported ()) {\r
+ //\r
+ // Ignore set request of the CPU does not support APIC Base Address MSR\r
+ //\r
+ return;\r
+ }\r
+\r
+ ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);\r
+\r
+ ApicBaseMsr.Bits.ApicBase = (UINT32) (BaseAddress >> 12);\r
+ ApicBaseMsr.Bits.ApicBaseHi = (UINT32) (RShiftU64((UINT64) BaseAddress, 32));\r
+\r
+ AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64);\r
+}\r
+\r
/**\r
Read from a local APIC register.\r
\r
ASSERT ((MmioOffset & 0xf) == 0);\r
\r
if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) {\r
- return MmioRead32 (PcdGet32 (PcdCpuLocalApicBaseAddress) + MmioOffset);\r
+ return MmioRead32 (GetLocalApicBaseAddress() + MmioOffset);\r
} else {\r
//\r
// DFR is not supported in x2APIC mode.\r
ASSERT ((MmioOffset & 0xf) == 0);\r
\r
if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) {\r
- MmioWrite32 (PcdGet32 (PcdCpuLocalApicBaseAddress) + MmioOffset, Value);\r
+ MmioWrite32 (GetLocalApicBaseAddress() + MmioOffset, Value);\r
} else {\r
//\r
// DFR is not supported in x2APIC mode.\r
/**\r
Send an IPI by writing to ICR.\r
\r
- This function returns after the IPI has been accepted by the target processor. \r
+ This function returns after the IPI has been accepted by the target processor.\r
\r
@param IcrLow 32-bit value to be written to the low half of ICR.\r
@param ApicId APIC ID of the target processor if this IPI is targeted for a specific processor.\r
{\r
UINT64 MsrValue;\r
LOCAL_APIC_ICR_LOW IcrLowReg;\r
+ UINTN LocalApciBaseAddress;\r
+ UINT32 IcrHigh;\r
+ BOOLEAN InterruptState;\r
\r
+ //\r
+ // Legacy APIC or X2APIC?\r
+ //\r
if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) {\r
ASSERT (ApicId <= 0xff);\r
\r
+ InterruptState = SaveAndDisableInterrupts ();\r
+\r
+ //\r
+ // Get base address of this LAPIC\r
+ //\r
+ LocalApciBaseAddress = GetLocalApicBaseAddress();\r
+\r
+ //\r
+ // Save existing contents of ICR high 32 bits\r
+ //\r
+ IcrHigh = MmioRead32 (LocalApciBaseAddress + XAPIC_ICR_HIGH_OFFSET);\r
+\r
+ //\r
+ // Wait for DeliveryStatus clear in case a previous IPI\r
+ // is still being sent\r
+ //\r
+ do {\r
+ IcrLowReg.Uint32 = MmioRead32 (LocalApciBaseAddress + XAPIC_ICR_LOW_OFFSET);\r
+ } while (IcrLowReg.Bits.DeliveryStatus != 0);\r
+\r
//\r
// For xAPIC, the act of writing to the low doubleword of the ICR causes the IPI to be sent.\r
//\r
- MmioWrite32 (PcdGet32 (PcdCpuLocalApicBaseAddress) + XAPIC_ICR_HIGH_OFFSET, ApicId << 24);\r
- MmioWrite32 (PcdGet32 (PcdCpuLocalApicBaseAddress) + XAPIC_ICR_LOW_OFFSET, IcrLow);\r
+ MmioWrite32 (LocalApciBaseAddress + XAPIC_ICR_HIGH_OFFSET, ApicId << 24);\r
+ MmioWrite32 (LocalApciBaseAddress + XAPIC_ICR_LOW_OFFSET, IcrLow);\r
+\r
+ //\r
+ // Wait for DeliveryStatus clear again\r
+ //\r
do {\r
- IcrLowReg.Uint32 = MmioRead32 (PcdGet32 (PcdCpuLocalApicBaseAddress) + XAPIC_ICR_LOW_OFFSET);\r
+ IcrLowReg.Uint32 = MmioRead32 (LocalApciBaseAddress + XAPIC_ICR_LOW_OFFSET);\r
} while (IcrLowReg.Bits.DeliveryStatus != 0);\r
+\r
+ //\r
+ // And restore old contents of ICR high\r
+ //\r
+ MmioWrite32 (LocalApciBaseAddress + XAPIC_ICR_HIGH_OFFSET, IcrHigh);\r
+\r
+ SetInterruptState (InterruptState);\r
+\r
} else {\r
//\r
- // For x2APIC, A single MSR write to the Interrupt Command Register is required for dispatching an \r
+ // For x2APIC, A single MSR write to the Interrupt Command Register is required for dispatching an\r
// interrupt in x2APIC mode.\r
//\r
MsrValue = LShiftU64 ((UINT64) ApicId, 32) | IcrLow;\r
VOID\r
)\r
{\r
- MSR_IA32_APIC_BASE ApicBaseMsr;\r
+ MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr;\r
+\r
+ if (!LocalApicBaseAddressMsrSupported ()) {\r
+ //\r
+ // If CPU does not support APIC Base Address MSR, then return XAPIC mode\r
+ //\r
+ return LOCAL_APIC_MODE_XAPIC;\r
+ }\r
\r
- ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS);\r
+ ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);\r
//\r
// Local APIC should have been enabled\r
//\r
- ASSERT (ApicBaseMsr.Bits.En != 0);\r
- if (ApicBaseMsr.Bits.Extd != 0) {\r
+ ASSERT (ApicBaseMsr.Bits.EN != 0);\r
+ if (ApicBaseMsr.Bits.EXTD != 0) {\r
return LOCAL_APIC_MODE_X2APIC;\r
} else {\r
return LOCAL_APIC_MODE_XAPIC;\r
If the specified local APIC mode can't be set as current, then ASSERT.\r
\r
@param ApicMode APIC mode to be set.\r
+\r
+ @note This API must not be called from an interrupt handler or SMI handler.\r
+ It may result in unpredictable behavior.\r
**/\r
VOID\r
EFIAPI\r
IN UINTN ApicMode\r
)\r
{\r
- UINTN CurrentMode;\r
- MSR_IA32_APIC_BASE ApicBaseMsr;\r
+ UINTN CurrentMode;\r
+ MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr;\r
+\r
+ if (!LocalApicBaseAddressMsrSupported ()) {\r
+ //\r
+ // Ignore set request if the CPU does not support APIC Base Address MSR\r
+ //\r
+ return;\r
+ }\r
\r
CurrentMode = GetApicMode ();\r
if (CurrentMode == LOCAL_APIC_MODE_XAPIC) {\r
case LOCAL_APIC_MODE_XAPIC:\r
break;\r
case LOCAL_APIC_MODE_X2APIC:\r
- ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS);\r
- ApicBaseMsr.Bits.Extd = 1;\r
- AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64);\r
+ ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);\r
+ ApicBaseMsr.Bits.EXTD = 1;\r
+ AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64);\r
break;\r
default:\r
ASSERT (FALSE);\r
// Transition from x2APIC mode to xAPIC mode is a two-step process:\r
// x2APIC -> Local APIC disabled -> xAPIC\r
//\r
- ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS);\r
- ApicBaseMsr.Bits.Extd = 0;\r
- ApicBaseMsr.Bits.En = 0;\r
- AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64);\r
- ApicBaseMsr.Bits.En = 1;\r
- AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64);\r
+ ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);\r
+ ApicBaseMsr.Bits.EXTD = 0;\r
+ ApicBaseMsr.Bits.EN = 0;\r
+ AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64);\r
+ ApicBaseMsr.Bits.EN = 1;\r
+ AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64);\r
break;\r
case LOCAL_APIC_MODE_X2APIC:\r
break;\r
/**\r
Get the initial local APIC ID of the executing processor assigned by hardware upon power on or reset.\r
\r
- In xAPIC mode, the initial local APIC ID is 8-bit, and may be different from current APIC ID.\r
- In x2APIC mode, the local APIC ID can't be changed and there is no concept of initial APIC ID. In this case, \r
+ In xAPIC mode, the initial local APIC ID may be different from current APIC ID.\r
+ In x2APIC mode, the local APIC ID can't be changed and there is no concept of initial APIC ID. In this case,\r
the 32-bit local APIC ID is returned as initial APIC ID.\r
\r
@return 32-bit initial local APIC ID of the executing processor.\r
VOID\r
)\r
{\r
+ UINT32 ApicId;\r
+ UINT32 MaxCpuIdIndex;\r
UINT32 RegEbx;\r
\r
if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) {\r
+ //\r
+ // Get the max index of basic CPUID\r
+ //\r
+ AsmCpuid (CPUID_SIGNATURE, &MaxCpuIdIndex, NULL, NULL, NULL);\r
+ //\r
+ // If CPUID Leaf B is supported,\r
+ // And CPUID.0BH:EBX[15:0] reports a non-zero value,\r
+ // Then the initial 32-bit APIC ID = CPUID.0BH:EDX\r
+ // Else the initial 8-bit APIC ID = CPUID.1:EBX[31:24]\r
+ //\r
+ if (MaxCpuIdIndex >= CPUID_EXTENDED_TOPOLOGY) {\r
+ AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, 0, NULL, &RegEbx, NULL, &ApicId);\r
+ if ((RegEbx & (BIT16 - 1)) != 0) {\r
+ return ApicId;\r
+ }\r
+ }\r
AsmCpuid (CPUID_VERSION_INFO, NULL, &RegEbx, NULL, NULL);\r
return RegEbx >> 24;\r
} else {\r
)\r
{\r
UINT32 ApicId;\r
+ UINT32 InitApicId;\r
\r
ApicId = ReadLocalApicReg (XAPIC_ID_OFFSET);\r
if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) {\r
- ApicId >>= 24;\r
+ ApicId = ((InitApicId = GetInitialApicId ()) < 0x100) ? (ApicId >> 24) : InitApicId;\r
}\r
+\r
return ApicId;\r
}\r
\r
/**\r
Send a Fixed IPI to a specified target processor.\r
\r
- This function returns after the IPI has been accepted by the target processor. \r
+ This function returns after the IPI has been accepted by the target processor.\r
\r
@param ApicId The local APIC ID of the target processor.\r
@param Vector The vector number of the interrupt being sent.\r
/**\r
Send a Fixed IPI to all processors excluding self.\r
\r
- This function returns after the IPI has been accepted by the target processors. \r
+ This function returns after the IPI has been accepted by the target processors.\r
\r
@param Vector The vector number of the interrupt being sent.\r
**/\r
/**\r
Send a SMI IPI to a specified target processor.\r
\r
- This function returns after the IPI has been accepted by the target processor. \r
+ This function returns after the IPI has been accepted by the target processor.\r
\r
@param ApicId Specify the local APIC ID of the target processor.\r
**/\r
/**\r
Send a SMI IPI to all processors excluding self.\r
\r
- This function returns after the IPI has been accepted by the target processors. \r
+ This function returns after the IPI has been accepted by the target processors.\r
**/\r
VOID\r
EFIAPI\r
/**\r
Send an INIT IPI to a specified target processor.\r
\r
- This function returns after the IPI has been accepted by the target processor. \r
+ This function returns after the IPI has been accepted by the target processor.\r
\r
@param ApicId Specify the local APIC ID of the target processor.\r
**/\r
/**\r
Send an INIT IPI to all processors excluding self.\r
\r
- This function returns after the IPI has been accepted by the target processors. \r
+ This function returns after the IPI has been accepted by the target processors.\r
**/\r
VOID\r
EFIAPI\r
/**\r
Send an INIT-Start-up-Start-up IPI sequence to a specified target processor.\r
\r
- This function returns after the IPI has been accepted by the target processor. \r
+ This function returns after the IPI has been accepted by the target processor.\r
\r
if StartupRoutine >= 1M, then ASSERT.\r
if StartupRoutine is not multiple of 4K, then ASSERT.\r
ASSERT ((StartupRoutine & 0xfff) == 0);\r
\r
SendInitIpi (ApicId);\r
- MicroSecondDelay (10);\r
+ MicroSecondDelay (PcdGet32(PcdCpuInitIpiDelayInMicroSeconds));\r
IcrLow.Uint32 = 0;\r
IcrLow.Bits.Vector = (StartupRoutine >> 12);\r
IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_STARTUP;\r
IcrLow.Bits.Level = 1;\r
SendIpi (IcrLow.Uint32, ApicId);\r
- MicroSecondDelay (200);\r
- SendIpi (IcrLow.Uint32, ApicId);\r
+ if (!StandardSignatureIsAuthenticAMD ()) {\r
+ MicroSecondDelay (200);\r
+ SendIpi (IcrLow.Uint32, ApicId);\r
+ }\r
}\r
\r
/**\r
Send an INIT-Start-up-Start-up IPI sequence to all processors excluding self.\r
\r
- This function returns after the IPI has been accepted by the target processors. \r
+ This function returns after the IPI has been accepted by the target processors.\r
\r
if StartupRoutine >= 1M, then ASSERT.\r
if StartupRoutine is not multiple of 4K, then ASSERT.\r
ASSERT ((StartupRoutine & 0xfff) == 0);\r
\r
SendInitIpiAllExcludingSelf ();\r
- MicroSecondDelay (10);\r
+ MicroSecondDelay (PcdGet32(PcdCpuInitIpiDelayInMicroSeconds));\r
IcrLow.Uint32 = 0;\r
IcrLow.Bits.Vector = (StartupRoutine >> 12);\r
IcrLow.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_STARTUP;\r
IcrLow.Bits.Level = 1;\r
IcrLow.Bits.DestinationShorthand = LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF;\r
SendIpi (IcrLow.Uint32, 0);\r
- MicroSecondDelay (200);\r
- SendIpi (IcrLow.Uint32, 0);\r
+ if (!StandardSignatureIsAuthenticAMD ()) {\r
+ MicroSecondDelay (200);\r
+ SendIpi (IcrLow.Uint32, 0);\r
+ }\r
+}\r
+\r
+/**\r
+ Initialize the state of the SoftwareEnable bit in the Local APIC\r
+ Spurious Interrupt Vector register.\r
+\r
+ @param Enable If TRUE, then set SoftwareEnable to 1\r
+ If FALSE, then set SoftwareEnable to 0.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+InitializeLocalApicSoftwareEnable (\r
+ IN BOOLEAN Enable\r
+ )\r
+{\r
+ LOCAL_APIC_SVR Svr;\r
+\r
+ //\r
+ // Set local APIC software-enabled bit.\r
+ //\r
+ Svr.Uint32 = ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET);\r
+ if (Enable) {\r
+ if (Svr.Bits.SoftwareEnable == 0) {\r
+ Svr.Bits.SoftwareEnable = 1;\r
+ WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32);\r
+ }\r
+ } else {\r
+ if (Svr.Bits.SoftwareEnable == 1) {\r
+ Svr.Bits.SoftwareEnable = 0;\r
+ WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32);\r
+ }\r
+ }\r
}\r
\r
/**\r
IN UINT8 Vector\r
)\r
{\r
- LOCAL_APIC_SVR Svr;\r
LOCAL_APIC_DCR Dcr;\r
LOCAL_APIC_LVT_TIMER LvtTimer;\r
UINT32 Divisor;\r
//\r
// Ensure local APIC is in software-enabled state.\r
//\r
- Svr.Uint32 = ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET);\r
- Svr.Bits.SoftwareEnable = 1;\r
- WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32);\r
+ InitializeLocalApicSoftwareEnable (TRUE);\r
\r
//\r
// Program init-count register.\r
Dcr.Uint32 = ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET);\r
Dcr.Bits.DivideValue1 = (Divisor & 0x3);\r
Dcr.Bits.DivideValue2 = (Divisor >> 2);\r
- WriteLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET, Dcr.Uint32); \r
+ WriteLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET, Dcr.Uint32);\r
}\r
\r
//\r
/**\r
Get the state of the local APIC timer.\r
\r
+ This function will ASSERT if the local APIC is not software enabled.\r
+\r
@param DivideValue Return the divide value for the DCR. It is one of 1,2,4,8,16,32,64,128.\r
@param PeriodicMode Return the timer mode. If TRUE, timer mode is peridoic. Othewise, timer mode is one-shot.\r
@param Vector Return the timer interrupt vector number.\r
LOCAL_APIC_DCR Dcr;\r
LOCAL_APIC_LVT_TIMER LvtTimer;\r
\r
+ //\r
+ // Check the APIC Software Enable/Disable bit (bit 8) in Spurious-Interrupt\r
+ // Vector Register.\r
+ // This bit will be 1, if local APIC is software enabled.\r
+ //\r
+ ASSERT ((ReadLocalApicReg(XAPIC_SPURIOUS_VECTOR_OFFSET) & BIT8) != 0);\r
+\r
if (DivideValue != NULL) {\r
Dcr.Uint32 = ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET);\r
Divisor = Dcr.Bits.DivideValue1 | (Dcr.Bits.DivideValue2 << 2);\r
WriteLocalApicReg (XAPIC_EOI_OFFSET, 0);\r
}\r
\r
+/**\r
+ Get the 32-bit address that a device should use to send a Message Signaled\r
+ Interrupt (MSI) to the Local APIC of the currently executing processor.\r
+\r
+ @return 32-bit address used to send an MSI to the Local APIC.\r
+**/\r
+UINT32\r
+EFIAPI\r
+GetApicMsiAddress (\r
+ VOID\r
+ )\r
+{\r
+ LOCAL_APIC_MSI_ADDRESS MsiAddress;\r
+\r
+ //\r
+ // Return address for an MSI interrupt to be delivered only to the APIC ID\r
+ // of the currently executing processor.\r
+ //\r
+ MsiAddress.Uint32 = 0;\r
+ MsiAddress.Bits.BaseAddress = 0xFEE;\r
+ MsiAddress.Bits.DestinationId = GetApicId ();\r
+ return MsiAddress.Uint32;\r
+}\r
+\r
+/**\r
+ Get the 64-bit data value that a device should use to send a Message Signaled\r
+ Interrupt (MSI) to the Local APIC of the currently executing processor.\r
+\r
+ If Vector is not in range 0x10..0xFE, then ASSERT().\r
+ If DeliveryMode is not supported, then ASSERT().\r
+\r
+ @param Vector The 8-bit interrupt vector associated with the MSI.\r
+ Must be in the range 0x10..0xFE\r
+ @param DeliveryMode A 3-bit value that specifies how the recept of the MSI\r
+ is handled. The only supported values are:\r
+ 0: LOCAL_APIC_DELIVERY_MODE_FIXED\r
+ 1: LOCAL_APIC_DELIVERY_MODE_LOWEST_PRIORITY\r
+ 2: LOCAL_APIC_DELIVERY_MODE_SMI\r
+ 4: LOCAL_APIC_DELIVERY_MODE_NMI\r
+ 5: LOCAL_APIC_DELIVERY_MODE_INIT\r
+ 7: LOCAL_APIC_DELIVERY_MODE_EXTINT\r
+\r
+ @param LevelTriggered TRUE specifies a level triggered interrupt.\r
+ FALSE specifies an edge triggered interrupt.\r
+ @param AssertionLevel Ignored if LevelTriggered is FALSE.\r
+ TRUE specifies a level triggered interrupt that active\r
+ when the interrupt line is asserted.\r
+ FALSE specifies a level triggered interrupt that active\r
+ when the interrupt line is deasserted.\r
+\r
+ @return 64-bit data value used to send an MSI to the Local APIC.\r
+**/\r
+UINT64\r
+EFIAPI\r
+GetApicMsiValue (\r
+ IN UINT8 Vector,\r
+ IN UINTN DeliveryMode,\r
+ IN BOOLEAN LevelTriggered,\r
+ IN BOOLEAN AssertionLevel\r
+ )\r
+{\r
+ LOCAL_APIC_MSI_DATA MsiData;\r
+\r
+ ASSERT (Vector >= 0x10 && Vector <= 0xFE);\r
+ ASSERT (DeliveryMode < 8 && DeliveryMode != 6 && DeliveryMode != 3);\r
+\r
+ MsiData.Uint64 = 0;\r
+ MsiData.Bits.Vector = Vector;\r
+ MsiData.Bits.DeliveryMode = (UINT32)DeliveryMode;\r
+ if (LevelTriggered) {\r
+ MsiData.Bits.TriggerMode = 1;\r
+ if (AssertionLevel) {\r
+ MsiData.Bits.Level = 1;\r
+ }\r
+ }\r
+ return MsiData.Uint64;\r
+}\r
+\r
+/**\r
+ Get Package ID/Core ID/Thread ID of a processor.\r
+\r
+ The algorithm assumes the target system has symmetry across physical\r
+ package boundaries with respect to the number of logical processors\r
+ per package, number of cores per package.\r
+\r
+ @param[in] InitialApicId Initial APIC ID of the target logical processor.\r
+ @param[out] Package Returns the processor package ID.\r
+ @param[out] Core Returns the processor core ID.\r
+ @param[out] Thread Returns the processor thread ID.\r
+**/\r
+VOID\r
+EFIAPI\r
+GetProcessorLocationByApicId (\r
+ IN UINT32 InitialApicId,\r
+ OUT UINT32 *Package OPTIONAL,\r
+ OUT UINT32 *Core OPTIONAL,\r
+ OUT UINT32 *Thread OPTIONAL\r
+ )\r
+{\r
+ BOOLEAN TopologyLeafSupported;\r
+ CPUID_VERSION_INFO_EBX VersionInfoEbx;\r
+ CPUID_VERSION_INFO_EDX VersionInfoEdx;\r
+ CPUID_CACHE_PARAMS_EAX CacheParamsEax;\r
+ CPUID_EXTENDED_TOPOLOGY_EAX ExtendedTopologyEax;\r
+ CPUID_EXTENDED_TOPOLOGY_EBX ExtendedTopologyEbx;\r
+ CPUID_EXTENDED_TOPOLOGY_ECX ExtendedTopologyEcx;\r
+ CPUID_AMD_EXTENDED_CPU_SIG_ECX AmdExtendedCpuSigEcx;\r
+ CPUID_AMD_PROCESSOR_TOPOLOGY_EBX AmdProcessorTopologyEbx;\r
+ CPUID_AMD_VIR_PHY_ADDRESS_SIZE_ECX AmdVirPhyAddressSizeEcx;\r
+ UINT32 MaxStandardCpuIdIndex;\r
+ UINT32 MaxExtendedCpuIdIndex;\r
+ UINT32 SubIndex;\r
+ UINTN LevelType;\r
+ UINT32 MaxLogicProcessorsPerPackage;\r
+ UINT32 MaxCoresPerPackage;\r
+ UINTN ThreadBits;\r
+ UINTN CoreBits;\r
+\r
+ //\r
+ // Check if the processor is capable of supporting more than one logical processor.\r
+ //\r
+ AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &VersionInfoEdx.Uint32);\r
+ if (VersionInfoEdx.Bits.HTT == 0) {\r
+ if (Thread != NULL) {\r
+ *Thread = 0;\r
+ }\r
+ if (Core != NULL) {\r
+ *Core = 0;\r
+ }\r
+ if (Package != NULL) {\r
+ *Package = 0;\r
+ }\r
+ return;\r
+ }\r
+\r
+ //\r
+ // Assume three-level mapping of APIC ID: Package|Core|Thread.\r
+ //\r
+ ThreadBits = 0;\r
+ CoreBits = 0;\r
+\r
+ //\r
+ // Get max index of CPUID\r
+ //\r
+ AsmCpuid (CPUID_SIGNATURE, &MaxStandardCpuIdIndex, NULL, NULL, NULL);\r
+ AsmCpuid (CPUID_EXTENDED_FUNCTION, &MaxExtendedCpuIdIndex, NULL, NULL, NULL);\r
+\r
+ //\r
+ // If the extended topology enumeration leaf is available, it\r
+ // is the preferred mechanism for enumerating topology.\r
+ //\r
+ TopologyLeafSupported = FALSE;\r
+ if (MaxStandardCpuIdIndex >= CPUID_EXTENDED_TOPOLOGY) {\r
+ AsmCpuidEx(\r
+ CPUID_EXTENDED_TOPOLOGY,\r
+ 0,\r
+ &ExtendedTopologyEax.Uint32,\r
+ &ExtendedTopologyEbx.Uint32,\r
+ &ExtendedTopologyEcx.Uint32,\r
+ NULL\r
+ );\r
+ //\r
+ // If CPUID.(EAX=0BH, ECX=0H):EBX returns zero and maximum input value for\r
+ // basic CPUID information is greater than 0BH, then CPUID.0BH leaf is not\r
+ // supported on that processor.\r
+ //\r
+ if (ExtendedTopologyEbx.Uint32 != 0) {\r
+ TopologyLeafSupported = TRUE;\r
+\r
+ //\r
+ // Sub-leaf index 0 (ECX= 0 as input) provides enumeration parameters to extract\r
+ // the SMT sub-field of x2APIC ID.\r
+ //\r
+ LevelType = ExtendedTopologyEcx.Bits.LevelType;\r
+ ASSERT (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT);\r
+ ThreadBits = ExtendedTopologyEax.Bits.ApicIdShift;\r
+\r
+ //\r
+ // Software must not assume any "level type" encoding\r
+ // value to be related to any sub-leaf index, except sub-leaf 0.\r
+ //\r
+ SubIndex = 1;\r
+ do {\r
+ AsmCpuidEx (\r
+ CPUID_EXTENDED_TOPOLOGY,\r
+ SubIndex,\r
+ &ExtendedTopologyEax.Uint32,\r
+ NULL,\r
+ &ExtendedTopologyEcx.Uint32,\r
+ NULL\r
+ );\r
+ LevelType = ExtendedTopologyEcx.Bits.LevelType;\r
+ if (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_CORE) {\r
+ CoreBits = ExtendedTopologyEax.Bits.ApicIdShift - ThreadBits;\r
+ break;\r
+ }\r
+ SubIndex++;\r
+ } while (LevelType != CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_INVALID);\r
+ }\r
+ }\r
+\r
+ if (!TopologyLeafSupported) {\r
+ //\r
+ // Get logical processor count\r
+ //\r
+ AsmCpuid (CPUID_VERSION_INFO, NULL, &VersionInfoEbx.Uint32, NULL, NULL);\r
+ MaxLogicProcessorsPerPackage = VersionInfoEbx.Bits.MaximumAddressableIdsForLogicalProcessors;\r
+\r
+ //\r
+ // Assume single-core processor\r
+ //\r
+ MaxCoresPerPackage = 1;\r
+\r
+ //\r
+ // Check for topology extensions on AMD processor\r
+ //\r
+ if (StandardSignatureIsAuthenticAMD()) {\r
+ if (MaxExtendedCpuIdIndex >= CPUID_AMD_PROCESSOR_TOPOLOGY) {\r
+ AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, &AmdExtendedCpuSigEcx.Uint32, NULL);\r
+ if (AmdExtendedCpuSigEcx.Bits.TopologyExtensions != 0) {\r
+ //\r
+ // Account for max possible thread count to decode ApicId\r
+ //\r
+ AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, NULL, NULL, &AmdVirPhyAddressSizeEcx.Uint32, NULL);\r
+ MaxLogicProcessorsPerPackage = 1 << AmdVirPhyAddressSizeEcx.Bits.ApicIdCoreIdSize;\r
+\r
+ //\r
+ // Get cores per processor package\r
+ //\r
+ AsmCpuid (CPUID_AMD_PROCESSOR_TOPOLOGY, NULL, &AmdProcessorTopologyEbx.Uint32, NULL, NULL);\r
+ MaxCoresPerPackage = MaxLogicProcessorsPerPackage / (AmdProcessorTopologyEbx.Bits.ThreadsPerCore + 1);\r
+ }\r
+ }\r
+ }\r
+ else {\r
+ //\r
+ // Extract core count based on CACHE information\r
+ //\r
+ if (MaxStandardCpuIdIndex >= CPUID_CACHE_PARAMS) {\r
+ AsmCpuidEx (CPUID_CACHE_PARAMS, 0, &CacheParamsEax.Uint32, NULL, NULL, NULL);\r
+ if (CacheParamsEax.Uint32 != 0) {\r
+ MaxCoresPerPackage = CacheParamsEax.Bits.MaximumAddressableIdsForLogicalProcessors + 1;\r
+ }\r
+ }\r
+ }\r
+\r
+ ThreadBits = (UINTN)(HighBitSet32(MaxLogicProcessorsPerPackage / MaxCoresPerPackage - 1) + 1);\r
+ CoreBits = (UINTN)(HighBitSet32(MaxCoresPerPackage - 1) + 1);\r
+ }\r
+\r
+ if (Thread != NULL) {\r
+ *Thread = InitialApicId & ((1 << ThreadBits) - 1);\r
+ }\r
+ if (Core != NULL) {\r
+ *Core = (InitialApicId >> ThreadBits) & ((1 << CoreBits) - 1);\r
+ }\r
+ if (Package != NULL) {\r
+ *Package = (InitialApicId >> (ThreadBits + CoreBits));\r
+ }\r
+}\r
+\r
+/**\r
+ Get Package ID/Die ID/Tile ID/Module ID/Core ID/Thread ID of a processor.\r
+\r
+ The algorithm assumes the target system has symmetry across physical\r
+ package boundaries with respect to the number of threads per core, number of\r
+ cores per module, number of modules per tile, number of tiles per die, number\r
+ of dies per package.\r
+\r
+ @param[in] InitialApicId Initial APIC ID of the target logical processor.\r
+ @param[out] Package Returns the processor package ID.\r
+ @param[out] Die Returns the processor die ID.\r
+ @param[out] Tile Returns the processor tile ID.\r
+ @param[out] Module Returns the processor module ID.\r
+ @param[out] Core Returns the processor core ID.\r
+ @param[out] Thread Returns the processor thread ID.\r
+**/\r
+VOID\r
+EFIAPI\r
+GetProcessorLocation2ByApicId (\r
+ IN UINT32 InitialApicId,\r
+ OUT UINT32 *Package OPTIONAL,\r
+ OUT UINT32 *Die OPTIONAL,\r
+ OUT UINT32 *Tile OPTIONAL,\r
+ OUT UINT32 *Module OPTIONAL,\r
+ OUT UINT32 *Core OPTIONAL,\r
+ OUT UINT32 *Thread OPTIONAL\r
+ )\r
+{\r
+ CPUID_EXTENDED_TOPOLOGY_EAX ExtendedTopologyEax;\r
+ CPUID_EXTENDED_TOPOLOGY_ECX ExtendedTopologyEcx;\r
+ UINT32 MaxStandardCpuIdIndex;\r
+ UINT32 Index;\r
+ UINTN LevelType;\r
+ UINT32 Bits[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 2];\r
+ UINT32 *Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 2];\r
+\r
+ for (LevelType = 0; LevelType < ARRAY_SIZE (Bits); LevelType++) {\r
+ Bits[LevelType] = 0;\r
+ }\r
+\r
+ //\r
+ // Get max index of CPUID\r
+ //\r
+ AsmCpuid (CPUID_SIGNATURE, &MaxStandardCpuIdIndex, NULL, NULL, NULL);\r
+ if (MaxStandardCpuIdIndex < CPUID_V2_EXTENDED_TOPOLOGY) {\r
+ if (Die != NULL) {\r
+ *Die = 0;\r
+ }\r
+ if (Tile != NULL) {\r
+ *Tile = 0;\r
+ }\r
+ if (Module != NULL) {\r
+ *Module = 0;\r
+ }\r
+ GetProcessorLocationByApicId (InitialApicId, Package, Core, Thread);\r
+ return;\r
+ }\r
+\r
+ //\r
+ // If the V2 extended topology enumeration leaf is available, it\r
+ // is the preferred mechanism for enumerating topology.\r
+ //\r
+ for (Index = 0; ; Index++) {\r
+ AsmCpuidEx(\r
+ CPUID_V2_EXTENDED_TOPOLOGY,\r
+ Index,\r
+ &ExtendedTopologyEax.Uint32,\r
+ NULL,\r
+ &ExtendedTopologyEcx.Uint32,\r
+ NULL\r
+ );\r
+\r
+ LevelType = ExtendedTopologyEcx.Bits.LevelType;\r
+\r
+ //\r
+ // first level reported should be SMT.\r
+ //\r
+ ASSERT ((Index != 0) || (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT));\r
+ if (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_INVALID) {\r
+ break;\r
+ }\r
+ ASSERT (LevelType < ARRAY_SIZE (Bits));\r
+ Bits[LevelType] = ExtendedTopologyEax.Bits.ApicIdShift;\r
+ }\r
+\r
+ for (LevelType = CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_CORE; LevelType < ARRAY_SIZE (Bits); LevelType++) {\r
+ //\r
+ // If there are more levels between level-1 (low-level) and level-2 (high-level), the unknown levels will be ignored\r
+ // and treated as an extension of the last known level (i.e., level-1 in this case).\r
+ //\r
+ if (Bits[LevelType] == 0) {\r
+ Bits[LevelType] = Bits[LevelType - 1];\r
+ }\r
+ }\r
+\r
+ Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 1] = Package;\r
+ Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE ] = Die;\r
+ Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_TILE ] = Tile;\r
+ Location[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_MODULE ] = Module;\r
+ Location[CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_CORE ] = Core;\r
+ Location[CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT ] = Thread;\r
+\r
+ Bits[CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 1] = 32;\r
+\r
+ for ( LevelType = CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT\r
+ ; LevelType <= CPUID_V2_EXTENDED_TOPOLOGY_LEVEL_TYPE_DIE + 1\r
+ ; LevelType ++\r
+ ) {\r
+ if (Location[LevelType] != NULL) {\r
+ //\r
+ // Bits[i] holds the number of bits to shift right on x2APIC ID to get a unique\r
+ // topology ID of the next level type.\r
+ //\r
+ *Location[LevelType] = InitialApicId >> Bits[LevelType - 1];\r
+\r
+ //\r
+ // Bits[i] - Bits[i-1] holds the number of bits for the next ONE level type.\r
+ //\r
+ *Location[LevelType] &= (1 << (Bits[LevelType] - Bits[LevelType - 1])) - 1;\r
+ }\r
+ }\r
+}\r