diff --git a/UefiCpuPkg/Include/Library/LocalApicLib.h b/UefiCpuPkg/Include/Library/LocalApicLib.h new file mode 100644 index 0000000000..58e3474e88 --- /dev/null +++ b/UefiCpuPkg/Include/Library/LocalApicLib.h @@ -0,0 +1,270 @@ +/** @file + Public include file for Local APIC library. + + Local APIC library assumes local APIC is enabled. It does not + handles cases where local APIC is disabled. + + Copyright (c) 2010, Intel Corporation. All rights reserved.
+ 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. + +**/ + +#ifndef __LOCAL_APIC_LIB_H__ +#define __LOCAL_APIC_LIB_H__ + +#define LOCAL_APIC_MODE_XAPIC 0x1 ///< xAPIC mode. +#define LOCAL_APIC_MODE_X2APIC 0x2 ///< x2APIC mode. + +/** + 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 + ); + +/** + 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. +**/ +VOID +EFIAPI +SetApicMode ( + IN UINTN ApicMode + ); + +/** + 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 is 8-bit, and 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 + ); + +/** + Get the local APIC ID of the executing processor. + + @return 32-bit local APIC ID of the executing processor. +**/ +UINT32 +EFIAPI +GetApicId ( + VOID + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + Get the divide value from the DCR (Divide Configuration Register) by which + the processor's bus clock is divided to form the time base for the APIC timer. + + @return The divide value is one of 1,2,4,8,16,32,64,128. +**/ +UINTN +EFIAPI +GetApicTimerDivisor ( + VOID + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + Enable the local APIC timer interrupt. +**/ +VOID +EFIAPI +EnableApicTimerInterrupt ( + VOID + ); + +/** + Disable the local APIC timer interrupt. +**/ +VOID +EFIAPI +DisableApicTimerInterrupt ( + VOID + ); + +/** + 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 + ); + +/** + Send EOI to the local APIC. +**/ +VOID +EFIAPI +SendApicEoi ( + VOID + ); + +#endif + diff --git a/UefiCpuPkg/Include/Register/LocalApic.h b/UefiCpuPkg/Include/Register/LocalApic.h new file mode 100644 index 0000000000..7535ef50e9 --- /dev/null +++ b/UefiCpuPkg/Include/Register/LocalApic.h @@ -0,0 +1,167 @@ +/** @file + IA32 Local APIC Definitions. + + Copyright (c) 2010, Intel Corporation. All rights reserved.
+ 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. + +**/ + +#ifndef __LOCAL_APIC_H__ +#define __LOCAL_APIC_H__ + +// +// Definitions for IA32 architectural MSRs +// +#define MSR_IA32_APIC_BASE_ADDRESS 0x1B + +// +// Definitions for CPUID instruction +// +#define CPUID_VERSION_INFO 0x1 +#define CPUID_EXTENDED_FUNCTION 0x80000000 +#define CPUID_VIR_PHY_ADDRESS_SIZE 0x80000008 + +// +// Definition for Local APIC registers and related values +// +#define XAPIC_ID_OFFSET 0x0 +#define XAPIC_EOI_OFFSET 0x0b0 +#define XAPIC_ICR_DFR_OFFSET 0x0e0 +#define XAPIC_SPURIOUS_VECTOR_OFFSET 0x0f0 +#define XAPIC_ICR_LOW_OFFSET 0x300 +#define XAPIC_ICR_HIGH_OFFSET 0x310 +#define XAPIC_LVT_TIMER_OFFSET 0x320 +#define XAPIC_LINT0_VECTOR_OFFSET 0x350 +#define XAPIC_LINT1_VECTOR_OFFSET 0x360 +#define XAPIC_TIMER_INIT_COUNT_OFFSET 0x380 +#define XAPIC_TIMER_CURRENT_COUNT_OFFSET 0x390 +#define XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET 0x3E0 + +#define X2APIC_MSR_BASE_ADDRESS 0x800 +#define X2APIC_MSR_ICR_ADDRESS 0x830 + +#define LOCAL_APIC_DELIVERY_MODE_FIXED 0 +#define LOCAL_APIC_DELIVERY_MODE_LOWEST_PRIORITY 1 +#define LOCAL_APIC_DELIVERY_MODE_SMI 2 +#define LOCAL_APIC_DELIVERY_MODE_NMI 4 +#define LOCAL_APIC_DELIVERY_MODE_INIT 5 +#define LOCAL_APIC_DELIVERY_MODE_STARTUP 6 +#define LOCAL_APIC_DELIVERY_MODE_EXTINT 7 + +#define LOCAL_APIC_DESTINATION_SHORTHAND_NO_SHORTHAND 0 +#define LOCAL_APIC_DESTINATION_SHORTHAND_SELF 1 +#define LOCAL_APIC_DESTINATION_SHORTHAND_ALL_INCLUDING_SELF 2 +#define LOCAL_APIC_DESTINATION_SHORTHAND_ALL_EXCLUDING_SELF 3 + +typedef union { + struct { + UINT64 Reserved0:8; ///< Reserved. + UINT64 Bsp:1; ///< Processor is BSP. + UINT64 Reserved1:1; ///< Reserved. + UINT64 Extd:1; ///< Enable x2APIC mode. + UINT64 En:1; ///< xAPIC global enable/disable. + UINT64 ApicBase:52; ///< APIC Base physical address. The actual field width depends on physical address width. + } Bits; + UINT64 Uint64; +} MSR_IA32_APIC_BASE; + +// +// Low half of Interrupt Command Register (ICR). +// +typedef union { + struct { + UINT32 Vector:8; ///< The vector number of the interrupt being sent. + UINT32 DeliveryMode:3; ///< Specifies the type of IPI to be sent. + UINT32 DestinationMode:1; ///< 0: physical destination mode, 1: logical destination mode. + UINT32 DeliveryStatus:1; ///< Indicates the IPI delivery status. This field is reserved in x2APIC mode. + UINT32 Reserved0:1; ///< Reserved. + UINT32 Level:1; ///< 0 for the INIT level de-assert delivery mode. Otherwise 1. + UINT32 TriggerMode:1; ///< 0: edge, 1: level when using the INIT level de-assert delivery mode. + UINT32 Reserved1:2; ///< Reserved. + UINT32 DestinationShorthand:2; ///< A shorthand notation to specify the destination of the interrupt. + UINT32 Reserved2:12; ///< Reserved. + } Bits; + UINT32 Uint32; +} LOCAL_APIC_ICR_LOW; + +// +// High half of Interrupt Command Register (ICR) +// +typedef union { + struct { + UINT32 Reserved0:24; ///< Reserved. + UINT32 Destination:8; ///< Specifies the target processor or processors in xAPIC mode. + } Bits; + UINT32 Uint32; ///< Destination field expanded to 32-bit in x2APIC mode. +} LOCAL_APIC_ICR_HIGH; + +// +// Spurious-Interrupt Vector Register (SVR) +// +typedef union { + struct { + UINT32 SpuriousVector:8; ///< Spurious Vector. + UINT32 SoftwareEnable:1; ///< APIC Software Enable/Disable. + UINT32 FocusProcessorChecking:1; ///< Focus Processor Checking. + UINT32 Reserved0:2; ///< Reserved. + UINT32 EoiBroadcastSuppression:1; ///< EOI-Broadcast Suppression. + UINT32 Reserved1:19; ///< Reserved. + } Bits; + UINT32 Uint32; +} LOCAL_APIC_SVR; + +// +// Divide Configuration Register (DCR) +// +typedef union { + struct { + UINT32 DivideValue1:2; ///< Low 2 bits of the divide value. + UINT32 Reserved0:1; ///< Always 0. + UINT32 DivideValue2:1; ///< Highest 1 bit of the divide value. + UINT32 Reserved1:28; ///< Reserved. + } Bits; + UINT32 Uint32; +} LOCAL_APIC_DCR; + +// +// LVT Timer Register +// +typedef union { + struct { + UINT32 Vector:8; ///< The vector number of the interrupt being sent. + UINT32 Reserved0:4; ///< Reserved. + UINT32 DeliveryStatus:1; ///< 0: Idle, 1: send pending. + UINT32 Reserved1:3; ///< Reserved. + UINT32 Mask:1; ///< 0: Not masked, 1: Masked. + UINT32 TimerMode:1; ///< 0: One-shot, 1: Periodic. + UINT32 Reserved2:14; ///< Reserved. + } Bits; + UINT32 Uint32; +} LOCAL_APIC_LVT_TIMER; + +// +// LVT LINT0/LINT1 Register +// +typedef union { + struct { + UINT32 Vector:8; ///< The vector number of the interrupt being sent. + UINT32 DeliveryMode:3; ///< Specifies the type of interrupt to be sent. + UINT32 Reserved0:1; ///< Reserved. + UINT32 DeliveryStatus:1; ///< 0: Idle, 1: send pending. + UINT32 InputPinPolarity:1; ///< Interrupt Input Pin Polarity. + UINT32 RemoteIrr:1; ///< RO. Set when the local APIC accepts the interrupt and reset when an EOI is received. + UINT32 TriggerMode:1; ///< 0:edge, 1:level. + UINT32 Mask:1; ///< 0: Not masked, 1: Masked. + UINT32 Reserved1:15; ///< Reserved. + } Bits; + UINT32 Uint32; +} LOCAL_APIC_LVT_LINT; + +#endif + diff --git a/UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.c b/UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.c new file mode 100644 index 0000000000..16dbd4be6e --- /dev/null +++ b/UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.c @@ -0,0 +1,574 @@ +/** @file + Local APIC Library. + + This local APIC library instance supports xAPIC mode only. + + Copyright (c) 2010, Intel Corporation. All rights reserved.
+ 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 + +#include +#include +#include +#include +#include +#include + +// +// Library internal functions +// + +/** + 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 + ) +{ + ASSERT ((MmioOffset & 0xf) == 0); + ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC); + + return MmioRead32 (PcdGet32 (PcdCpuLocalApicBaseAddress) + MmioOffset); +} + +/** + 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 + ) +{ + ASSERT ((MmioOffset & 0xf) == 0); + ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC); + + MmioWrite32 (PcdGet32 (PcdCpuLocalApicBaseAddress) + MmioOffset, 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 + ) +{ + LOCAL_APIC_ICR_LOW IcrLowReg; + + ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC); + ASSERT (ApicId <= 0xff); + + // + // For xAPIC, the act of writing to the low doubleword of the ICR causes the IPI to be sent. + // + WriteLocalApicReg (XAPIC_ICR_HIGH_OFFSET, ApicId << 24); + WriteLocalApicReg (XAPIC_ICR_LOW_OFFSET, IcrLow); + do { + IcrLowReg.Uint32 = ReadLocalApicReg (XAPIC_ICR_LOW_OFFSET); + } while (IcrLowReg.Bits.DeliveryStatus != 0); +} + +// +// 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 + ) +{ + DEBUG_CODE ( + { + MSR_IA32_APIC_BASE ApicBaseMsr; + + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS); + // + // Local APIC should have been enabled + // + ASSERT (ApicBaseMsr.Bits.En != 0); + ASSERT (ApicBaseMsr.Bits.Extd == 0); + } + ); + 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. +**/ +VOID +EFIAPI +SetApicMode ( + IN UINTN ApicMode + ) +{ + ASSERT (ApicMode == LOCAL_APIC_MODE_XAPIC); + ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC); +} + +/** + 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 is 8-bit, and 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 RegEbx; + + ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC); + + AsmCpuid (CPUID_VERSION_INFO, NULL, &RegEbx, NULL, NULL); + return RegEbx >> 24; +} + +/** + 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; + + ASSERT (GetApicMode () == LOCAL_APIC_MODE_XAPIC); + + ApicId = ReadLocalApicReg (XAPIC_ID_OFFSET); + ApicId >>= 24; + return ApicId; +} + +/** + 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 (10); + 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 (10); + 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); +} + +/** + 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_LINT0_VECTOR_OFFSET); + Lint.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_EXTINT; + Lint.Bits.InputPinPolarity = 0; + Lint.Bits.TriggerMode = 0; + Lint.Bits.Mask = 0; + WriteLocalApicReg (XAPIC_LINT0_VECTOR_OFFSET, Lint.Uint32); + + // + // Program the LINT0 vector entry as NMI. Not masked, edge, active high. + // + Lint.Uint32 = ReadLocalApicReg (XAPIC_LINT1_VECTOR_OFFSET); + Lint.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_NMI; + Lint.Bits.InputPinPolarity = 0; + Lint.Bits.TriggerMode = 0; + Lint.Bits.Mask = 0; + WriteLocalApicReg (XAPIC_LINT1_VECTOR_OFFSET, Lint.Uint32); +} + +/** + Get the divide value from the DCR (Divide Configuration Register) by which + the processor's bus clock is divided to form the time base for the APIC timer. + + @return The divide value is one of 1,2,4,8,16,32,64,128. +**/ +UINTN +EFIAPI +GetApicTimerDivisor ( + VOID + ) +{ + UINT32 DivideValue; + LOCAL_APIC_DCR Dcr; + + Dcr.Uint32 = ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET); + DivideValue = Dcr.Bits.DivideValue1 | (Dcr.Bits.DivideValue2 << 2); + DivideValue = (DivideValue + 1) & 0x7; + return ((UINTN)1) << DivideValue; +} + +/** + 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_SVR Svr; + LOCAL_APIC_DCR Dcr; + LOCAL_APIC_LVT_TIMER LvtTimer; + UINT32 Divisor; + + // + // Ensure local APIC is in software-enabled state. + // + Svr.Uint32 = ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET); + Svr.Bits.SoftwareEnable = 1; + WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32); + + // + // 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); +} + +/** + 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); +} + diff --git a/UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.inf b/UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.inf new file mode 100644 index 0000000000..470e02971d --- /dev/null +++ b/UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.inf @@ -0,0 +1,46 @@ +## @file +# Component description file for CPU Local APIC Library. +# +# This library instance supports xAPIC mode only. +# +# Copyright (c) 2010, Intel Corporation. All rights reserved.
+# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BaseXApicLib + FILE_GUID = D87CA0A8-1AC2-439b-90F8-EF4A2AC88DAF + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = LocalApicLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + BaseXApicLib.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + TimerLib + IoLib + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress + diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c new file mode 100644 index 0000000000..bff66eeb1b --- /dev/null +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.c @@ -0,0 +1,657 @@ +/** @file + Local APIC Library. + + This local APIC library instance supports x2APIC capable processors + which have xAPIC and x2APIC modes. + + Copyright (c) 2010, Intel Corporation. All rights reserved.
+ 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 + +#include +#include +#include +#include +#include +#include + +// +// Library internal functions +// + +/** + 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 (PcdGet32 (PcdCpuLocalApicBaseAddress) + 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 (PcdGet32 (PcdCpuLocalApicBaseAddress) + 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; + + if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) { + ASSERT (ApicId <= 0xff); + + // + // For xAPIC, the act of writing to the low doubleword of the ICR causes the IPI to be sent. + // + MmioWrite32 (PcdGet32 (PcdCpuLocalApicBaseAddress) + XAPIC_ICR_HIGH_OFFSET, ApicId << 24); + MmioWrite32 (PcdGet32 (PcdCpuLocalApicBaseAddress) + XAPIC_ICR_LOW_OFFSET, IcrLow); + do { + IcrLowReg.Uint32 = MmioRead32 (PcdGet32 (PcdCpuLocalApicBaseAddress) + XAPIC_ICR_LOW_OFFSET); + } while (IcrLowReg.Bits.DeliveryStatus != 0); + } else { + // + // For x2APIC, A single MSR write to the Interrupt Command Register is required for dispatching an + // interrupt in x2APIC mode. + // + MsrValue = (((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; + + 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. +**/ +VOID +EFIAPI +SetApicMode ( + IN UINTN ApicMode + ) +{ + UINTN CurrentMode; + MSR_IA32_APIC_BASE ApicBaseMsr; + + 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 is 8-bit, and 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 RegEbx; + + if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) { + 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; + + ApicId = ReadLocalApicReg (XAPIC_ID_OFFSET); + if (GetApicMode () == LOCAL_APIC_MODE_XAPIC) { + ApicId >>= 24; + } + return ApicId; +} + +/** + 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 (10); + 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 (10); + 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); +} + +/** + 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_LINT0_VECTOR_OFFSET); + Lint.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_EXTINT; + Lint.Bits.InputPinPolarity = 0; + Lint.Bits.TriggerMode = 0; + Lint.Bits.Mask = 0; + WriteLocalApicReg (XAPIC_LINT0_VECTOR_OFFSET, Lint.Uint32); + + // + // Program the LINT0 vector entry as NMI. Not masked, edge, active high. + // + Lint.Uint32 = ReadLocalApicReg (XAPIC_LINT1_VECTOR_OFFSET); + Lint.Bits.DeliveryMode = LOCAL_APIC_DELIVERY_MODE_NMI; + Lint.Bits.InputPinPolarity = 0; + Lint.Bits.TriggerMode = 0; + Lint.Bits.Mask = 0; + WriteLocalApicReg (XAPIC_LINT1_VECTOR_OFFSET, Lint.Uint32); +} + +/** + Get the divide value from the DCR (Divide Configuration Register) by which + the processor's bus clock is divided to form the time base for the APIC timer. + + @return The divide value is one of 1,2,4,8,16,32,64,128. +**/ +UINTN +EFIAPI +GetApicTimerDivisor ( + VOID + ) +{ + UINT32 DivideValue; + LOCAL_APIC_DCR Dcr; + + Dcr.Uint32 = ReadLocalApicReg (XAPIC_TIMER_DIVIDE_CONFIGURATION_OFFSET); + DivideValue = Dcr.Bits.DivideValue1 | (Dcr.Bits.DivideValue2 << 2); + DivideValue = (DivideValue + 1) & 0x7; + return ((UINTN)1) << DivideValue; +} + +/** + 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_SVR Svr; + LOCAL_APIC_DCR Dcr; + LOCAL_APIC_LVT_TIMER LvtTimer; + UINT32 Divisor; + + // + // Ensure local APIC is in software-enabled state. + // + Svr.Uint32 = ReadLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET); + Svr.Bits.SoftwareEnable = 1; + WriteLocalApicReg (XAPIC_SPURIOUS_VECTOR_OFFSET, Svr.Uint32); + + // + // 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); +} + +/** + 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); +} + diff --git a/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf new file mode 100644 index 0000000000..df2769c9ff --- /dev/null +++ b/UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf @@ -0,0 +1,47 @@ +## @file +# Component description file for CPU Local APIC Library. +# +# This library instance supports x2APIC capable processors +# which have xAPIC and x2APIC modes. +# +# Copyright (c) 2010, Intel Corporation. All rights reserved.
+# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = BaseXApicX2ApicLib + FILE_GUID = 967B6E05-F10D-4c10-8BF7-365291CA143F + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = LocalApicLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + BaseXApicX2ApicLib.c + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + TimerLib + IoLib + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress + diff --git a/UefiCpuPkg/UefiCpuPkg.dec b/UefiCpuPkg/UefiCpuPkg.dec index 8547c54e13..98fba36920 100644 --- a/UefiCpuPkg/UefiCpuPkg.dec +++ b/UefiCpuPkg/UefiCpuPkg.dec @@ -28,3 +28,10 @@ ## to be UEFI specification compliant. ## UefiCpuLib|Include/Library/UefiCpuLib.h + +[Guids] + gUefiCpuPkgTokenSpaceGuid = { 0xac05bf33, 0x995a, 0x4ed4, { 0xaa, 0xb8, 0xef, 0x7a, 0xe8, 0xf, 0x5c, 0xb0 }} + +[PcdsFixedAtBuild, PcdsPatchableInModule] + gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress|0xfee00000|UINT32|0x00000001 + diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc index a9e45bc9ae..c414ae8b29 100644 --- a/UefiCpuPkg/UefiCpuPkg.dsc +++ b/UefiCpuPkg/UefiCpuPkg.dsc @@ -73,4 +73,7 @@ UefiCpuPkg/CpuDxe/CpuDxe.inf UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.inf UefiCpuPkg/Library/BaseUefiCpuLib/BaseUefiCpuLib.inf - UefiCpuPkg/Library/MtrrLib/MtrrLib.inf \ No newline at end of file + UefiCpuPkg/Library/MtrrLib/MtrrLib.inf + UefiCpuPkg/Library/BaseXApicX2ApicLib/BaseXApicX2ApicLib.inf + UefiCpuPkg/Library/BaseXApicLib/BaseXApicLib.inf +