Hi Ray and Laszlo, I would very much like to be merged into stable202302, the soft feature deadline is 2024-02-05, so could you please hlep to review this patch as soon as passable? Please... Thanks, Chao On 2024/1/31 11:32, Chao Li wrote: > > Hi Ray, > > Can you please help to review this patch again? > > On 2024/1/26 14:29, Chao Li wrote: >> Added LoongArch multiprocessor initialization instance into MpInitLib. >> >> BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=4584 >> >> Cc: Eric Dong >> Cc: Ray Ni >> Cc: Rahul Kumar >> Cc: Gerd Hoffmann >> Signed-off-by: Chao Li >> --- >> UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf | 27 +- >> .../Library/MpInitLib/LoongArch64/DxeMpLib.c | 480 +++++ >> .../Library/MpInitLib/LoongArch64/MpLib.c | 1621 +++++++++++++++++ >> .../Library/MpInitLib/LoongArch64/MpLib.h | 361 ++++ >> .../Library/MpInitLib/LoongArch64/PeiMpLib.c | 404 ++++ >> UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf | 27 +- >> 6 files changed, 2902 insertions(+), 18 deletions(-) >> create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/DxeMpLib.c >> create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.c >> create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.h >> create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/PeiMpLib.c >> >> diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf >> index 55e46d4a1f..6db26f5fec 100644 >> --- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf >> +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf >> @@ -2,6 +2,7 @@ >> # MP Initialize Library instance for DXE driver. >> # >> # Copyright (c) 2016 - 2023, Intel Corporation. All rights reserved.
>> +# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.
>> # SPDX-License-Identifier: BSD-2-Clause-Patent >> # >> ## >> @@ -18,7 +19,7 @@ >> # >> # The following information is for reference only and not required by the build tools. >> # >> -# VALID_ARCHITECTURES = IA32 X64 >> +# VALID_ARCHITECTURES = IA32 X64 LOONGARCH64 >> # >> >> [Sources.IA32] >> @@ -31,7 +32,7 @@ >> X64/MpFuncs.nasm >> X64/CreatePageTable.c >> >> -[Sources.common] >> +[Sources.IA32, Sources.X64] >> AmdSev.c >> MpEqu.inc >> DxeMpLib.c >> @@ -40,24 +41,32 @@ >> Microcode.c >> MpHandOff.h >> >> +[Sources.LoongArch64] >> + LoongArch64/DxeMpLib.c >> + LoongArch64/MpLib.c >> + LoongArch64/MpLib.h >> + >> [Packages] >> MdePkg/MdePkg.dec >> MdeModulePkg/MdeModulePkg.dec >> UefiCpuPkg/UefiCpuPkg.dec >> >> -[LibraryClasses] >> +[LibraryClasses.common] >> BaseLib >> - LocalApicLib >> - MemoryAllocationLib >> - HobLib >> - MtrrLib >> CpuLib >> - UefiBootServicesTableLib >> DebugAgentLib >> - SynchronizationLib >> + HobLib >> + MemoryAllocationLib >> PcdLib >> + UefiBootServicesTableLib >> + SynchronizationLib >> + >> +[LibraryClasses.IA32, LibraryClasses.X64] >> CcExitLib >> + LocalApicLib >> MicrocodeLib >> + MtrrLib >> + >> [LibraryClasses.X64] >> CpuPageTableLib >> >> diff --git a/UefiCpuPkg/Library/MpInitLib/LoongArch64/DxeMpLib.c b/UefiCpuPkg/Library/MpInitLib/LoongArch64/DxeMpLib.c >> new file mode 100644 >> index 0000000000..739da77e32 >> --- /dev/null >> +++ b/UefiCpuPkg/Library/MpInitLib/LoongArch64/DxeMpLib.c >> @@ -0,0 +1,480 @@ >> +/** @file >> + LoongArch64 MP initialize support functions for DXE phase. >> + >> + Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.
>> + SPDX-License-Identifier: BSD-2-Clause-Patent >> + >> +**/ >> + >> +#include "MpLib.h" >> + >> +#include >> +#include >> +#include >> + >> +#include >> + >> +CPU_MP_DATA *mCpuMpData = NULL; >> +EFI_EVENT mCheckAllApsEvent = NULL; >> +volatile BOOLEAN mStopCheckAllApsStatus = TRUE; >> + >> +/** >> + Enable Debug Agent to support source debugging on AP function. >> + >> +**/ >> +VOID >> +EnableDebugAgent ( >> + VOID >> + ) >> +{ >> + // >> + // Initialize Debug Agent to support source level debug in DXE phase >> + // >> + InitializeDebugAgent (DEBUG_AGENT_INIT_DXE_AP, NULL, NULL); >> +} >> + >> +/** >> + Get the pointer to CPU MP Data structure. >> + >> + @return The pointer to CPU MP Data structure. >> +**/ >> +CPU_MP_DATA * >> +GetCpuMpData ( >> + VOID >> + ) >> +{ >> + ASSERT (mCpuMpData != NULL); >> + return mCpuMpData; >> +} >> + >> +/** >> + Save the pointer to CPU MP Data structure. >> + >> + @param[in] CpuMpData The pointer to CPU MP Data structure will be saved. >> +**/ >> +VOID >> +SaveCpuMpData ( >> + IN CPU_MP_DATA *CpuMpData >> + ) >> +{ >> + mCpuMpData = CpuMpData; >> +} >> + >> +/** >> + Get available EfiBootServicesCode memory below 4GB by specified size. >> + >> + This buffer is required to safely transfer AP from real address mode to >> + protected mode or long mode, due to the fact that the buffer returned by >> + GetWakeupBuffer() may be marked as non-executable. >> + >> + @param[in] BufferSize Wakeup transition buffer size. >> + >> + @retval other Return wakeup transition buffer address below 4GB. >> + @retval 0 Cannot find free memory below 4GB. >> +**/ >> +UINTN >> +GetModeTransitionBuffer ( >> + IN UINTN BufferSize >> + ) >> +{ >> + return 0; >> +} >> + >> +/** >> + Checks APs status and updates APs status if needed. >> + >> +**/ >> +VOID >> +CheckAndUpdateApsStatus ( >> + VOID >> + ) >> +{ >> + UINTN ProcessorNumber; >> + EFI_STATUS Status; >> + CPU_MP_DATA *CpuMpData; >> + >> + CpuMpData = GetCpuMpData (); >> + >> + // >> + // First, check whether pending StartupAllAPs() exists. >> + // >> + if (CpuMpData->WaitEvent != NULL) { >> + Status = CheckAllAPs (); >> + // >> + // If all APs finish for StartupAllAPs(), signal the WaitEvent for it. >> + // >> + if (Status != EFI_NOT_READY) { >> + Status = gBS->SignalEvent (CpuMpData->WaitEvent); >> + CpuMpData->WaitEvent = NULL; >> + } >> + } >> + >> + // >> + // Second, check whether pending StartupThisAPs() callings exist. >> + // >> + for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) { >> + if (CpuMpData->CpuData[ProcessorNumber].WaitEvent == NULL) { >> + continue; >> + } >> + >> + Status = CheckThisAP (ProcessorNumber); >> + >> + if (Status != EFI_NOT_READY) { >> + gBS->SignalEvent (CpuMpData->CpuData[ProcessorNumber].WaitEvent); >> + CpuMpData->CpuData[ProcessorNumber].WaitEvent = NULL; >> + } >> + } >> +} >> + >> +/** >> + Checks APs' status periodically. >> + >> + This function is triggered by timer periodically to check the >> + state of APs for StartupAllAPs() and StartupThisAP() executed >> + in non-blocking mode. >> + >> + @param[in] Event Event triggered. >> + @param[in] Context Parameter passed with the event. >> + >> +**/ >> +VOID >> +EFIAPI >> +CheckApsStatus ( >> + IN EFI_EVENT Event, >> + IN VOID *Context >> + ) >> +{ >> + // >> + // If CheckApsStatus() is not stopped, otherwise return immediately. >> + // >> + if (!mStopCheckAllApsStatus) { >> + CheckAndUpdateApsStatus (); >> + } >> +} >> + >> +/** >> + Initialize global data for MP support. >> + >> + @param[in] CpuMpData The pointer to CPU MP Data structure. >> +**/ >> +VOID >> +InitMpGlobalData ( >> + IN CPU_MP_DATA *CpuMpData >> + ) >> +{ >> + EFI_STATUS Status; >> + >> + SaveCpuMpData (CpuMpData); >> + >> + Status = gBS->CreateEvent ( >> + EVT_TIMER | EVT_NOTIFY_SIGNAL, >> + TPL_NOTIFY, >> + CheckApsStatus, >> + NULL, >> + &mCheckAllApsEvent >> + ); >> + ASSERT_EFI_ERROR (Status); >> + >> + // >> + // Set timer to check all APs status. >> + // >> + Status = gBS->SetTimer ( >> + mCheckAllApsEvent, >> + TimerPeriodic, >> + EFI_TIMER_PERIOD_MICROSECONDS ( >> + PcdGet32 (PcdCpuApStatusCheckIntervalInMicroSeconds) >> + ) >> + ); >> + ASSERT_EFI_ERROR (Status); >> +} >> + >> +/** >> + This service executes a caller provided function on all enabled APs. >> + >> + @param[in] Procedure A pointer to the function to be run on >> + enabled APs of the system. See type >> + EFI_AP_PROCEDURE. >> + @param[in] SingleThread If TRUE, then all the enabled APs execute >> + the function specified by Procedure one by >> + one, in ascending order of processor handle >> + number. If FALSE, then all the enabled APs >> + execute the function specified by Procedure >> + simultaneously. >> + @param[in] WaitEvent The event created by the caller with CreateEvent() >> + service. If it is NULL, then execute in >> + blocking mode. BSP waits until all APs finish >> + or TimeoutInMicroSeconds expires. If it's >> + not NULL, then execute in non-blocking mode. >> + BSP requests the function specified by >> + Procedure to be started on all the enabled >> + APs, and go on executing immediately. If >> + all return from Procedure, or TimeoutInMicroSeconds >> + expires, this event is signaled. The BSP >> + can use the CheckEvent() or WaitForEvent() >> + services to check the state of event. Type >> + EFI_EVENT is defined in CreateEvent() in >> + the Unified Extensible Firmware Interface >> + Specification. >> + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for >> + APs to return from Procedure, either for >> + blocking or non-blocking mode. Zero means >> + infinity. If the timeout expires before >> + all APs return from Procedure, then Procedure >> + on the failed APs is terminated. All enabled >> + APs are available for next function assigned >> + by MpInitLibStartupAllAPs() or >> + MPInitLibStartupThisAP(). >> + If the timeout expires in blocking mode, >> + BSP returns EFI_TIMEOUT. If the timeout >> + expires in non-blocking mode, WaitEvent >> + is signaled with SignalEvent(). >> + @param[in] ProcedureArgument The parameter passed into Procedure for >> + all APs. >> + @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise, >> + if all APs finish successfully, then its >> + content is set to NULL. If not all APs >> + finish before timeout expires, then its >> + content is set to address of the buffer >> + holding handle numbers of the failed APs. >> + The buffer is allocated by MP Initialization >> + library, and it's the caller's responsibility to >> + free the buffer with FreePool() service. >> + In blocking mode, it is ready for consumption >> + when the call returns. In non-blocking mode, >> + it is ready when WaitEvent is signaled. The >> + list of failed CPU is terminated by >> + END_OF_CPU_LIST. >> + >> + @retval EFI_SUCCESS In blocking mode, all APs have finished before >> + the timeout expired. >> + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched >> + to all enabled APs. >> + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the >> + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was >> + signaled. >> + @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not >> + supported. >> + @retval EFI_DEVICE_ERROR Caller processor is AP. >> + @retval EFI_NOT_STARTED No enabled APs exist in the system. >> + @retval EFI_NOT_READY Any enabled APs are busy. >> + @retval EFI_NOT_READY MP Initialize Library is not initialized. >> + @retval EFI_TIMEOUT In blocking mode, the timeout expired before >> + all enabled APs have finished. >> + @retval EFI_INVALID_PARAMETER Procedure is NULL. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +MpInitLibStartupAllAPs ( >> + IN EFI_AP_PROCEDURE Procedure, >> + IN BOOLEAN SingleThread, >> + IN EFI_EVENT WaitEvent OPTIONAL, >> + IN UINTN TimeoutInMicroseconds, >> + IN VOID *ProcedureArgument OPTIONAL, >> + OUT UINTN **FailedCpuList OPTIONAL >> + ) >> +{ >> + EFI_STATUS Status; >> + >> + // >> + // Temporarily stop checkAllApsStatus for avoid resource dead-lock. >> + // >> + mStopCheckAllApsStatus = TRUE; >> + >> + Status = StartupAllCPUsWorker ( >> + Procedure, >> + SingleThread, >> + TRUE, >> + WaitEvent, >> + TimeoutInMicroseconds, >> + ProcedureArgument, >> + FailedCpuList >> + ); >> + >> + // >> + // Start checkAllApsStatus >> + // >> + mStopCheckAllApsStatus = FALSE; >> + >> + return Status; >> +} >> + >> +/** >> + This service lets the caller get one enabled AP to execute a caller-provided >> + function. >> + >> + @param[in] Procedure A pointer to the function to be run on the >> + designated AP of the system. See type >> + EFI_AP_PROCEDURE. >> + @param[in] ProcessorNumber The handle number of the AP. The range is >> + from 0 to the total number of logical >> + processors minus 1. The total number of >> + logical processors can be retrieved by >> + MpInitLibGetNumberOfProcessors(). >> + @param[in] WaitEvent The event created by the caller with CreateEvent() >> + service. If it is NULL, then execute in >> + blocking mode. BSP waits until this AP finish >> + or TimeoutInMicroSeconds expires. If it's >> + not NULL, then execute in non-blocking mode. >> + BSP requests the function specified by >> + Procedure to be started on this AP, >> + and go on executing immediately. If this AP >> + return from Procedure or TimeoutInMicroSeconds >> + expires, this event is signaled. The BSP >> + can use the CheckEvent() or WaitForEvent() >> + services to check the state of event. Type >> + EFI_EVENT is defined in CreateEvent() in >> + the Unified Extensible Firmware Interface >> + Specification. >> + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for >> + this AP to finish this Procedure, either for >> + blocking or non-blocking mode. Zero means >> + infinity. If the timeout expires before >> + this AP returns from Procedure, then Procedure >> + on the AP is terminated. The >> + AP is available for next function assigned >> + by MpInitLibStartupAllAPs() or >> + MpInitLibStartupThisAP(). >> + If the timeout expires in blocking mode, >> + BSP returns EFI_TIMEOUT. If the timeout >> + expires in non-blocking mode, WaitEvent >> + is signaled with SignalEvent(). >> + @param[in] ProcedureArgument The parameter passed into Procedure on the >> + specified AP. >> + @param[out] Finished If NULL, this parameter is ignored. In >> + blocking mode, this parameter is ignored. >> + In non-blocking mode, if AP returns from >> + Procedure before the timeout expires, its >> + content is set to TRUE. Otherwise, the >> + value is set to FALSE. The caller can >> + determine if the AP returned from Procedure >> + by evaluating this value. >> + >> + @retval EFI_SUCCESS In blocking mode, specified AP finished before >> + the timeout expires. >> + @retval EFI_SUCCESS In non-blocking mode, the function has been >> + dispatched to specified AP. >> + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the >> + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was >> + signaled. >> + @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not >> + supported. >> + @retval EFI_DEVICE_ERROR The calling processor is an AP. >> + @retval EFI_TIMEOUT In blocking mode, the timeout expired before >> + the specified AP has finished. >> + @retval EFI_NOT_READY The specified AP is busy. >> + @retval EFI_NOT_READY MP Initialize Library is not initialized. >> + @retval EFI_NOT_FOUND The processor with the handle specified by >> + ProcessorNumber does not exist. >> + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. >> + @retval EFI_INVALID_PARAMETER Procedure is NULL. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +MpInitLibStartupThisAP ( >> + IN EFI_AP_PROCEDURE Procedure, >> + IN UINTN ProcessorNumber, >> + IN EFI_EVENT WaitEvent OPTIONAL, >> + IN UINTN TimeoutInMicroseconds, >> + IN VOID *ProcedureArgument OPTIONAL, >> + OUT BOOLEAN *Finished OPTIONAL >> + ) >> +{ >> + EFI_STATUS Status; >> + >> + // >> + // temporarily stop checkAllApsStatus for avoid resource dead-lock. >> + // >> + mStopCheckAllApsStatus = TRUE; >> + >> + Status = StartupThisAPWorker ( >> + Procedure, >> + ProcessorNumber, >> + WaitEvent, >> + TimeoutInMicroseconds, >> + ProcedureArgument, >> + Finished >> + ); >> + >> + mStopCheckAllApsStatus = FALSE; >> + >> + return Status; >> +} >> + >> +/** >> + This service switches the requested AP to be the BSP from that point onward. >> + This service changes the BSP for all purposes. This call can only be performed >> + by the current BSP. >> + >> + @param[in] ProcessorNumber The handle number of AP that is to become the new >> + BSP. The range is from 0 to the total number of >> + logical processors minus 1. The total number of >> + logical processors can be retrieved by >> + MpInitLibGetNumberOfProcessors(). >> + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an >> + enabled AP. Otherwise, it will be disabled. >> + >> + @retval EFI_SUCCESS BSP successfully switched. >> + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to >> + this service returning. >> + @retval EFI_UNSUPPORTED Switching the BSP is not supported. >> + @retval EFI_DEVICE_ERROR The calling processor is an AP. >> + @retval EFI_NOT_FOUND The processor with the handle specified by >> + ProcessorNumber does not exist. >> + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or >> + a disabled AP. >> + @retval EFI_NOT_READY The specified AP is busy. >> + @retval EFI_NOT_READY MP Initialize Library is not initialized. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +MpInitLibSwitchBSP ( >> + IN UINTN ProcessorNumber, >> + IN BOOLEAN EnableOldBSP >> + ) >> +{ >> + return EFI_UNSUPPORTED; >> +} >> + >> +/** >> + This service lets the caller enable or disable an AP from this point onward. >> + This service may only be called from the BSP. >> + >> + @param[in] ProcessorNumber The handle number of AP. >> + The range is from 0 to the total number of >> + logical processors minus 1. The total number of >> + logical processors can be retrieved by >> + MpInitLibGetNumberOfProcessors(). >> + @param[in] EnableAP Specifies the new state for the processor for >> + enabled, FALSE for disabled. >> + @param[in] HealthFlag If not NULL, a pointer to a value that specifies >> + the new health status of the AP. This flag >> + corresponds to StatusFlag defined in >> + EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only >> + the PROCESSOR_HEALTH_STATUS_BIT is used. All other >> + bits are ignored. If it is NULL, this parameter >> + is ignored. >> + >> + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. >> + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed >> + prior to this service returning. >> + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. >> + @retval EFI_DEVICE_ERROR The calling processor is an AP. >> + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber >> + does not exist. >> + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. >> + @retval EFI_NOT_READY MP Initialize Library is not initialized. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +MpInitLibEnableDisableAP ( >> + IN UINTN ProcessorNumber, >> + IN BOOLEAN EnableAP, >> + IN UINT32 *HealthFlag OPTIONAL >> + ) >> +{ >> + return EFI_UNSUPPORTED; >> +} >> diff --git a/UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.c b/UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.c >> new file mode 100644 >> index 0000000000..930d34aa3d >> --- /dev/null >> +++ b/UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.c >> @@ -0,0 +1,1621 @@ >> +/** @file >> + LoongArch64 CPU MP Initialize Library common functions. >> + >> + Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.
>> + >> + SPDX-License-Identifier: BSD-2-Clause-Patent >> + >> +**/ >> + >> +#include "MpLib.h" >> + >> +#include >> +#include >> + >> +#define INVALID_APIC_ID 0xFFFFFFFF >> + >> +EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID; >> +EFI_GUID mProcessorResourceHobGuid = PROCESSOR_RESOURCE_HOB_GUID; >> + >> +/** >> + Get the Application Processors state. >> + >> + @param[in] CpuData The pointer to CPU_AP_DATA of specified AP >> + >> + @return The AP status >> +**/ >> +CPU_STATE >> +GetApState ( >> + IN CPU_AP_DATA *CpuData >> + ) >> +{ >> + return CpuData->State; >> +} >> + >> +/** >> + Set the Application Processors state. >> + >> + @param[in] CpuData The pointer to CPU_AP_DATA of specified AP >> + @param[in] State The AP status >> +**/ >> +VOID >> +SetApState ( >> + IN CPU_AP_DATA *CpuData, >> + IN CPU_STATE State >> + ) >> +{ >> + AcquireSpinLock (&CpuData->ApLock); >> + CpuData->State = State; >> + ReleaseSpinLock (&CpuData->ApLock); >> +} >> + >> +/** >> + Get APIC ID of the executing processor. >> + >> + @return 32-bit APIC ID of the executing processor. >> +**/ >> +UINT32 >> +GetApicId ( >> + VOID >> + ) >> +{ >> + UINTN CpuNum; >> + >> + CpuNum = CsrRead (LOONGARCH_CSR_CPUNUM); >> + >> + return CpuNum & 0x3ff; >> +} >> + >> +/** >> + Find the current Processor number by APIC ID. >> + >> + @param[in] CpuMpData Pointer to PEI CPU MP Data >> + @param[out] ProcessorNumber Return the pocessor number found >> + >> + @retval EFI_SUCCESS ProcessorNumber is found and returned. >> + @retval EFI_NOT_FOUND ProcessorNumber is not found. >> +**/ >> +EFI_STATUS >> +GetProcessorNumber ( >> + IN CPU_MP_DATA *CpuMpData, >> + OUT UINTN *ProcessorNumber >> + ) >> +{ >> + UINTN TotalProcessorNumber; >> + UINTN Index; >> + CPU_INFO_IN_HOB *CpuInfoInHob; >> + >> + CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob; >> + >> + TotalProcessorNumber = CpuMpData->CpuCount; >> + for (Index = 0; Index < TotalProcessorNumber; Index++) { >> + if (CpuInfoInHob[Index].ApicId == GetApicId ()) { >> + *ProcessorNumber = Index; >> + return EFI_SUCCESS; >> + } >> + } >> + >> + return EFI_NOT_FOUND; >> +} >> + >> +/** >> + Sort the APIC ID of all processors. >> + >> + This function sorts the APIC ID of all processors so that processor number is >> + assigned in the ascending order of APIC ID which eases MP debugging. >> + >> + @param[in] CpuMpData Pointer to PEI CPU MP Data >> +**/ >> +VOID >> +SortApicId ( >> + IN CPU_MP_DATA *CpuMpData >> + ) >> +{ >> + UINTN Index1; >> + UINTN Index2; >> + UINTN Index3; >> + UINT32 ApicId; >> + CPU_INFO_IN_HOB CpuInfo; >> + UINT32 ApCount; >> + CPU_INFO_IN_HOB *CpuInfoInHob; >> + volatile UINT32 *StartupApSignal; >> + >> + ApCount = CpuMpData->CpuCount - 1; >> + CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob; >> + if (ApCount != 0) { >> + Index2 = 0; >> + for (Index1 = (PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1); Index1 > 0; Index1--) { >> + if (CpuInfoInHob[Index1].ApicId != INVALID_APIC_ID) { >> + if (Index1 == ApCount) { >> + break; >> + } else { >> + for ( ; Index2 <= ApCount; Index2++) { >> + if (CpuInfoInHob[Index2].ApicId == INVALID_APIC_ID) { >> + CopyMem (&CpuInfoInHob[Index2], &CpuInfoInHob[Index1], sizeof (CPU_INFO_IN_HOB)); >> + CpuMpData->CpuData[Index2] = CpuMpData->CpuData[Index1]; >> + CpuInfoInHob[Index1].ApicId = INVALID_APIC_ID; >> + break; >> + } >> + } >> + } >> + } else { >> + continue; >> + } >> + } >> + >> + for (Index1 = 0; Index1 < ApCount; Index1++) { >> + Index3 = Index1; >> + // >> + // Sort key is the hardware default APIC ID >> + // >> + ApicId = CpuInfoInHob[Index1].ApicId; >> + for (Index2 = Index1 + 1; Index2 <= ApCount; Index2++) { >> + if (ApicId > CpuInfoInHob[Index2].ApicId) { >> + Index3 = Index2; >> + ApicId = CpuInfoInHob[Index2].ApicId; >> + } >> + } >> + >> + if (Index3 != Index1) { >> + CopyMem (&CpuInfo, &CpuInfoInHob[Index3], sizeof (CPU_INFO_IN_HOB)); >> + CopyMem ( >> + &CpuInfoInHob[Index3], >> + &CpuInfoInHob[Index1], >> + sizeof (CPU_INFO_IN_HOB) >> + ); >> + CopyMem (&CpuInfoInHob[Index1], &CpuInfo, sizeof (CPU_INFO_IN_HOB)); >> + >> + // >> + // Also exchange the StartupApSignal. >> + // >> + StartupApSignal = CpuMpData->CpuData[Index3].StartupApSignal; >> + CpuMpData->CpuData[Index3].StartupApSignal = >> + CpuMpData->CpuData[Index1].StartupApSignal; >> + CpuMpData->CpuData[Index1].StartupApSignal = StartupApSignal; >> + } >> + } >> + >> + // >> + // Get the processor number for the BSP >> + // >> + ApicId = GetApicId (); >> + for (Index1 = 0; Index1 < CpuMpData->CpuCount; Index1++) { >> + if (CpuInfoInHob[Index1].ApicId == ApicId) { >> + CpuMpData->BspNumber = (UINT32)Index1; >> + break; >> + } >> + } >> + } >> +} >> + >> +/** >> + Get pointer to Processor Resource Data structure from GUIDd HOB. >> + >> + @return The pointer to Processor Resource Data structure. >> +**/ >> +PROCESSOR_RESOURCE_DATA * >> +GetProcessorResourceDataFromGuidedHob ( >> + VOID >> + ) >> +{ >> + EFI_HOB_GUID_TYPE *GuidHob; >> + VOID *DataInHob; >> + PROCESSOR_RESOURCE_DATA *ResourceData; >> + >> + ResourceData = NULL; >> + GuidHob = GetFirstGuidHob (&mProcessorResourceHobGuid); >> + if (GuidHob != NULL) { >> + DataInHob = GET_GUID_HOB_DATA (GuidHob); >> + ResourceData = (PROCESSOR_RESOURCE_DATA *)(*(UINTN *)DataInHob); >> + } >> + >> + return ResourceData; >> +} >> + >> +/** >> + This function will get CPU count in the system. >> + >> + @param[in] CpuMpData Pointer to PEI CPU MP Data >> + >> + @return CPU count detected >> +**/ >> +UINTN >> +CollectProcessorCount ( >> + IN CPU_MP_DATA *CpuMpData >> + ) >> +{ >> + PROCESSOR_RESOURCE_DATA *ProcessorResourceData; >> + >> + ProcessorResourceData = NULL; >> + >> + // >> + // Set the default loop mode for APs. >> + // >> + CpuMpData->ApLoopMode = ApInRunLoop; >> + >> + // >> + // Beacuse LoongArch does not have SIPI now, the APIC ID must be obtained before >> + // calling IPI to wake up the APs. If NULL is obtained, NODE0 Core0 Mailbox0 is used >> + // as the first broadcast method to wake up all APs, and all of APs will read NODE0 >> + // Core0 Mailbox0 in an infinit loop. >> + // >> + ProcessorResourceData = GetProcessorResourceDataFromGuidedHob (); >> + >> + if (ProcessorResourceData != NULL) { >> + CpuMpData->ApLoopMode = ApInHltLoop; >> + CpuMpData->CpuCount = ProcessorResourceData->CpuCount; >> + CpuMpData->CpuInfoInHob = (UINTN)(ProcessorResourceData->CpuInfoInHob); >> + } >> + >> + // >> + // Send 1st broadcast IPI to APs to wakeup APs >> + // >> + CpuMpData->InitFlag = ApInitConfig; >> + WakeUpAP (CpuMpData, TRUE, 0, NULL, NULL, FALSE); >> + CpuMpData->InitFlag = ApInitDone; >> + >> + // >> + // When InitFlag == ApInitConfig, WakeUpAP () guarantees all APs are checked in. >> + // FinishedCount is the number of check-in APs. >> + // >> + CpuMpData->CpuCount = CpuMpData->FinishedCount + 1; >> + ASSERT (CpuMpData->CpuCount <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber)); >> + >> + // >> + // Wait for all APs finished the initialization >> + // >> + while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) { >> + CpuPause (); >> + } >> + >> + // >> + // Sort BSP/Aps by CPU APIC ID in ascending order >> + // >> + SortApicId (CpuMpData); >> + >> + DEBUG ((DEBUG_INFO, "MpInitLib: Find %d processors in system.\n", CpuMpData->CpuCount)); >> + >> + return CpuMpData->CpuCount; >> +} >> + >> +/** >> + Initialize CPU AP Data when AP is wakeup at the first time. >> + >> + @param[in, out] CpuMpData Pointer to PEI CPU MP Data >> + @param[in] ProcessorNumber The handle number of processor >> + @param[in] BistData Processor BIST data >> + >> +**/ >> +VOID >> +InitializeApData ( >> + IN OUT CPU_MP_DATA *CpuMpData, >> + IN UINTN ProcessorNumber, >> + IN UINT32 BistData >> + ) >> +{ >> + CPU_INFO_IN_HOB *CpuInfoInHob; >> + >> + CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)(CpuMpData->CpuInfoInHob); >> + >> + CpuInfoInHob[ProcessorNumber].ApicId = GetApicId (); >> + CpuInfoInHob[ProcessorNumber].Health = BistData; >> + >> + CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE; >> + CpuMpData->CpuData[ProcessorNumber].CpuHealthy = (BistData == 0) ? TRUE : FALSE; >> + >> + InitializeSpinLock (&CpuMpData->CpuData[ProcessorNumber].ApLock); >> + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle); >> +} >> + >> +/** >> + Ap wake up function. >> + >> + Ap will wait for scheduling here, and if the IPI or wake-up signal is enabled, >> + Ap will preform the corresponding functions. >> + >> + @param[in] ApIndex Number of current executing AP >> + @param[in] ExchangeInfo Pointer to the MP exchange info buffer >> +**/ >> +VOID >> +EFIAPI >> +ApWakeupFunction ( >> + IN UINTN ApIndex, >> + IN MP_CPU_EXCHANGE_INFO *ExchangeInfo >> + ) >> +{ >> + CPU_MP_DATA *CpuMpData; >> + UINTN ProcessorNumber; >> + volatile UINT32 *ApStartupSignalBuffer; >> + EFI_AP_PROCEDURE Procedure; >> + VOID *Parameter; >> + >> + CpuMpData = ExchangeInfo->CpuMpData; >> + >> + while (TRUE) { >> + if (CpuMpData->InitFlag == ApInitConfig) { >> + ProcessorNumber = ApIndex; >> + // >> + // If the AP can running to here, then the BIST must be zero. >> + // >> + InitializeApData (CpuMpData, ProcessorNumber, 0); >> + ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal; >> + } else { >> + // >> + // Execute AP function if AP is ready >> + // >> + GetProcessorNumber (CpuMpData, &ProcessorNumber); >> + >> + // >> + // Clear AP start-up signal when AP waken up >> + // >> + ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal; >> + InterlockedCompareExchange32 ( >> + (UINT32 *)ApStartupSignalBuffer, >> + WAKEUP_AP_SIGNAL, >> + 0 >> + ); >> + >> + // >> + // Invoke AP function here >> + // >> + if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateReady) { >> + Procedure = (EFI_AP_PROCEDURE)CpuMpData->CpuData[ProcessorNumber].ApFunction; >> + Parameter = (VOID *)CpuMpData->CpuData[ProcessorNumber].ApFunctionArgument; >> + if (Procedure != NULL) { >> + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateBusy); >> + Procedure (Parameter); >> + } >> + >> + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateFinished); >> + } >> + } >> + >> + // >> + // Updates the finished count >> + // >> + InterlockedIncrement ((UINT32 *)&CpuMpData->FinishedCount); >> + >> + while (TRUE) { >> + // >> + // Clean per-core mail box registers. >> + // >> + IoCsrWrite64 (LOONGARCH_IOCSR_MBUF0, 0x0); >> + IoCsrWrite64 (LOONGARCH_IOCSR_MBUF1, 0x0); >> + IoCsrWrite64 (LOONGARCH_IOCSR_MBUF2, 0x0); >> + IoCsrWrite64 (LOONGARCH_IOCSR_MBUF3, 0x0); >> + >> + // >> + // Enable IPI interrupt and global interrupt >> + // >> + EnableLocalInterrupts (BIT12); >> + IoCsrWrite32 (LOONGARCH_IOCSR_IPI_EN, 0xFFFFFFFFU); >> + EnableInterrupts (); >> + >> + // >> + // Ap entry HLT mode >> + // >> + CpuSleep (); >> + >> + // >> + // Disable global interrupts when wake up >> + // >> + DisableInterrupts (); >> + >> + // >> + // Update CpuMpData >> + // >> + if (CpuMpData != ExchangeInfo->CpuMpData) { >> + CpuMpData = ExchangeInfo->CpuMpData; >> + GetProcessorNumber (CpuMpData, &ProcessorNumber); >> + ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal; >> + } >> + >> + // >> + // Break out of the loop if wake up signal is not NULL. >> + // >> + if (*ApStartupSignalBuffer == WAKEUP_AP_SIGNAL) { >> + break; >> + } >> + } >> + } >> +} >> + >> +/** >> + Calculate timeout value and return the current performance counter value. >> + >> + Calculate the number of performance counter ticks required for a timeout. >> + If TimeoutInMicroseconds is 0, return value is also 0, which is recognized >> + as infinity. >> + >> + @param[in] TimeoutInMicroseconds Timeout value in microseconds. >> + @param[out] CurrentTime Returns the current value of the performance counter. >> + >> + @return Expected time stamp counter for timeout. >> + If TimeoutInMicroseconds is 0, return value is also 0, which is recognized >> + as infinity. >> + >> +**/ >> +UINT64 >> +CalculateTimeout ( >> + IN UINTN TimeoutInMicroseconds, >> + OUT UINT64 *CurrentTime >> + ) >> +{ >> + UINT64 TimeoutInSeconds; >> + UINT64 TimestampCounterFreq; >> + >> + // >> + // Read the current value of the performance counter >> + // >> + *CurrentTime = GetPerformanceCounter (); >> + >> + // >> + // If TimeoutInMicroseconds is 0, return value is also 0, which is recognized >> + // as infinity. >> + // >> + if (TimeoutInMicroseconds == 0) { >> + return 0; >> + } >> + >> + // >> + // GetPerformanceCounterProperties () returns the timestamp counter's frequency >> + // in Hz. >> + // >> + TimestampCounterFreq = GetPerformanceCounterProperties (NULL, NULL); >> + >> + // >> + // Check the potential overflow before calculate the number of ticks for the timeout value. >> + // >> + if (DivU64x64Remainder (MAX_UINT64, TimeoutInMicroseconds, NULL) < TimestampCounterFreq) { >> + // >> + // Convert microseconds into seconds if direct multiplication overflows >> + // >> + TimeoutInSeconds = DivU64x32 (TimeoutInMicroseconds, 1000000); >> + // >> + // Assertion if the final tick count exceeds MAX_UINT64 >> + // >> + ASSERT (DivU64x64Remainder (MAX_UINT64, TimeoutInSeconds, NULL) >= TimestampCounterFreq); >> + return MultU64x64 (TimestampCounterFreq, TimeoutInSeconds); >> + } else { >> + // >> + // No overflow case, multiply the return value with TimeoutInMicroseconds and then divide >> + // it by 1,000,000, to get the number of ticks for the timeout value. >> + // >> + return DivU64x32 ( >> + MultU64x64 ( >> + TimestampCounterFreq, >> + TimeoutInMicroseconds >> + ), >> + 1000000 >> + ); >> + } >> +} >> + >> +/** >> + Checks whether timeout expires. >> + >> + Check whether the number of elapsed performance counter ticks required for >> + a timeout condition has been reached. >> + If Timeout is zero, which means infinity, return value is always FALSE. >> + >> + @param[in, out] PreviousTime On input, the value of the performance counter >> + when it was last read. >> + On output, the current value of the performance >> + counter >> + @param[in] TotalTime The total amount of elapsed time in performance >> + counter ticks. >> + @param[in] Timeout The number of performance counter ticks required >> + to reach a timeout condition. >> + >> + @retval TRUE A timeout condition has been reached. >> + @retval FALSE A timeout condition has not been reached. >> + >> +**/ >> +BOOLEAN >> +CheckTimeout ( >> + IN OUT UINT64 *PreviousTime, >> + IN UINT64 *TotalTime, >> + IN UINT64 Timeout >> + ) >> +{ >> + UINT64 Start; >> + UINT64 End; >> + UINT64 CurrentTime; >> + INT64 Delta; >> + INT64 Cycle; >> + >> + if (Timeout == 0) { >> + return FALSE; >> + } >> + >> + GetPerformanceCounterProperties (&Start, &End); >> + Cycle = End - Start; >> + if (Cycle < 0) { >> + Cycle = -Cycle; >> + } >> + >> + Cycle++; >> + CurrentTime = GetPerformanceCounter (); >> + Delta = (INT64)(CurrentTime - *PreviousTime); >> + if (Start > End) { >> + Delta = -Delta; >> + } >> + >> + if (Delta < 0) { >> + Delta += Cycle; >> + } >> + >> + *TotalTime += Delta; >> + *PreviousTime = CurrentTime; >> + if (*TotalTime > Timeout) { >> + return TRUE; >> + } >> + >> + return FALSE; >> +} >> + >> +/** >> + Helper function that waits until the finished AP count reaches the specified >> + limit, or the specified timeout elapses (whichever comes first). >> + >> + @param[in] CpuMpData Pointer to CPU MP Data. >> + @param[in] FinishedApLimit The number of finished APs to wait for. >> + @param[in] TimeLimit The number of microseconds to wait for. >> +**/ >> +VOID >> +TimedWaitForApFinish ( >> + IN CPU_MP_DATA *CpuMpData, >> + IN UINT32 FinishedApLimit, >> + IN UINT32 TimeLimit >> + ) >> +{ >> + // >> + // CalculateTimeout() and CheckTimeout() consider a TimeLimit of 0 >> + // "infinity", so check for (TimeLimit == 0) explicitly. >> + // >> + if (TimeLimit == 0) { >> + return; >> + } >> + >> + CpuMpData->TotalTime = 0; >> + CpuMpData->ExpectedTime = CalculateTimeout ( >> + TimeLimit, >> + &CpuMpData->CurrentTime >> + ); >> + while (CpuMpData->FinishedCount < FinishedApLimit && >> + !CheckTimeout ( >> + &CpuMpData->CurrentTime, >> + &CpuMpData->TotalTime, >> + CpuMpData->ExpectedTime >> + )) >> + { >> + CpuPause (); >> + } >> + >> + if (CpuMpData->FinishedCount >= FinishedApLimit) { >> + DEBUG (( >> + DEBUG_VERBOSE, >> + "%a: reached FinishedApLimit=%u in %Lu microseconds\n", >> + __func__, >> + FinishedApLimit, >> + DivU64x64Remainder ( >> + MultU64x32 (CpuMpData->TotalTime, 1000000), >> + GetPerformanceCounterProperties (NULL, NULL), >> + NULL >> + ) >> + )); >> + } >> +} >> + >> +/** >> + Wait for AP wakeup and write AP start-up signal till AP is waken up. >> + >> + @param[in] ApStartupSignalBuffer Pointer to AP wakeup signal >> +**/ >> +VOID >> +WaitApWakeup ( >> + IN volatile UINT32 *ApStartupSignalBuffer >> + ) >> +{ >> + // >> + // If AP is waken up, StartupApSignal should be cleared. >> + // Otherwise, write StartupApSignal again till AP waken up. >> + // >> + while (InterlockedCompareExchange32 ( >> + (UINT32 *)ApStartupSignalBuffer, >> + WAKEUP_AP_SIGNAL, >> + WAKEUP_AP_SIGNAL >> + ) != 0) >> + { >> + CpuPause (); >> + } >> +} >> + >> +/** >> + This function will fill the exchange info structure. >> + >> + @param[in] CpuMpData Pointer to CPU MP Data >> + >> +**/ >> +VOID >> +FillExchangeInfoData ( >> + IN CPU_MP_DATA *CpuMpData >> + ) >> +{ >> + volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo; >> + >> + if (!CpuMpData->MpCpuExchangeInfo) { >> + CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *)AllocatePool (sizeof (MP_CPU_EXCHANGE_INFO)); >> + } >> + >> + ExchangeInfo = CpuMpData->MpCpuExchangeInfo; >> + ExchangeInfo->CpuMpData = CpuMpData; >> +} >> + >> +/** >> + This function will be called by BSP to wakeup AP. >> + >> + @param[in] CpuMpData Pointer to CPU MP Data >> + @param[in] Broadcast TRUE: Send broadcast IPI to all APs >> + FALSE: Send IPI to AP by ApicId >> + @param[in] ProcessorNumber The handle number of specified processor >> + @param[in] Procedure The function to be invoked by AP >> + @param[in] ProcedureArgument The argument to be passed into AP function >> + @param[in] WakeUpDisabledAps Whether need to wake up disabled APs in broadcast mode. Currently not used on LoongArch. >> +**/ >> +VOID >> +WakeUpAP ( >> + IN CPU_MP_DATA *CpuMpData, >> + IN BOOLEAN Broadcast, >> + IN UINTN ProcessorNumber, >> + IN EFI_AP_PROCEDURE Procedure OPTIONAL, >> + IN VOID *ProcedureArgument OPTIONAL, >> + IN BOOLEAN WakeUpDisabledAps >> + ) >> +{ >> + volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo; >> + UINTN Index; >> + CPU_AP_DATA *CpuData; >> + CPU_INFO_IN_HOB *CpuInfoInHob; >> + >> + CpuMpData->FinishedCount = 0; >> + >> + CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob; >> + >> + if (CpuMpData->InitFlag != ApInitDone) { >> + FillExchangeInfoData (CpuMpData); >> + } >> + >> + ExchangeInfo = CpuMpData->MpCpuExchangeInfo; >> + // >> + // If InitFlag is ApInitConfig, broadcasts all APs to initize themselves. >> + // >> + if (CpuMpData->InitFlag == ApInitConfig) { >> + DEBUG ((DEBUG_INFO, "%a: func 0x%llx, ExchangeInfo 0x%llx\n", __func__, ApWakeupFunction, (UINTN)ExchangeInfo)); >> + if (CpuMpData->ApLoopMode == ApInHltLoop) { >> + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { >> + if (Index != CpuMpData->BspNumber) { >> + IoCsrWrite64 ( >> + LOONGARCH_IOCSR_MBUF_SEND, >> + (IOCSR_MBUF_SEND_BLOCKING | >> + (IOCSR_MBUF_SEND_BOX_HI (0x3) << IOCSR_MBUF_SEND_BOX_SHIFT) | >> + (CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) | >> + ((UINTN)(ExchangeInfo) & IOCSR_MBUF_SEND_H32_MASK)) >> + ); >> + IoCsrWrite64 ( >> + LOONGARCH_IOCSR_MBUF_SEND, >> + (IOCSR_MBUF_SEND_BLOCKING | >> + (IOCSR_MBUF_SEND_BOX_LO (0x3) << IOCSR_MBUF_SEND_BOX_SHIFT) | >> + (CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) | >> + ((UINTN)ExchangeInfo) << IOCSR_MBUF_SEND_BUF_SHIFT) >> + ); >> + >> + IoCsrWrite64 ( >> + LOONGARCH_IOCSR_MBUF_SEND, >> + (IOCSR_MBUF_SEND_BLOCKING | >> + (IOCSR_MBUF_SEND_BOX_HI (0x0) << IOCSR_MBUF_SEND_BOX_SHIFT) | >> + (CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) | >> + ((UINTN)(ApWakeupFunction) & IOCSR_MBUF_SEND_H32_MASK)) >> + ); >> + IoCsrWrite64 ( >> + LOONGARCH_IOCSR_MBUF_SEND, >> + (IOCSR_MBUF_SEND_BLOCKING | >> + (IOCSR_MBUF_SEND_BOX_LO (0x0) << IOCSR_MBUF_SEND_BOX_SHIFT) | >> + (CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) | >> + ((UINTN)ApWakeupFunction) << IOCSR_MBUF_SEND_BUF_SHIFT) >> + ); >> + >> + // >> + // Send IPI 4 interrupt to wake up APs. >> + // >> + IoCsrWrite64 ( >> + LOONGARCH_IOCSR_IPI_SEND, >> + (IOCSR_MBUF_SEND_BLOCKING | >> + (CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) | >> + 0x2 // Bit 2 >> + ) >> + ); >> + } >> + } >> + } else { >> + IoCsrWrite64 (LOONGARCH_IOCSR_MBUF3, (UINTN)ExchangeInfo); >> + IoCsrWrite64 (LOONGARCH_IOCSR_MBUF0, (UINTN)ApWakeupFunction); >> + } >> + >> + TimedWaitForApFinish ( >> + CpuMpData, >> + PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1, >> + PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds) >> + ); >> + } else { >> + if (Broadcast) { >> + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { >> + if (Index != CpuMpData->BspNumber) { >> + CpuData = &CpuMpData->CpuData[Index]; >> + if ((GetApState (CpuData) == CpuStateDisabled) && !WakeUpDisabledAps) { >> + continue; >> + } >> + >> + CpuData->ApFunction = (UINTN)Procedure; >> + CpuData->ApFunctionArgument = (UINTN)ProcedureArgument; >> + SetApState (CpuData, CpuStateReady); >> + *(UINT32 *)CpuData->StartupApSignal = WAKEUP_AP_SIGNAL; >> + >> + // >> + // Send IPI 4 interrupt to wake up APs. >> + // >> + IoCsrWrite64 ( >> + LOONGARCH_IOCSR_IPI_SEND, >> + (IOCSR_MBUF_SEND_BLOCKING | >> + (CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) | >> + 0x2 // Bit 2 >> + ) >> + ); >> + } >> + } >> + >> + // >> + // Wait all APs waken up. >> + // >> + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { >> + CpuData = &CpuMpData->CpuData[Index]; >> + if (Index != CpuMpData->BspNumber) { >> + WaitApWakeup (CpuData->StartupApSignal); >> + } >> + } >> + } else { >> + CpuData = &CpuMpData->CpuData[ProcessorNumber]; >> + CpuData->ApFunction = (UINTN)Procedure; >> + CpuData->ApFunctionArgument = (UINTN)ProcedureArgument; >> + SetApState (CpuData, CpuStateReady); >> + // >> + // Wakeup specified AP >> + // >> + *(UINT32 *)CpuData->StartupApSignal = WAKEUP_AP_SIGNAL; >> + >> + // >> + // Send IPI 4 interrupt to wake up APs. >> + // >> + IoCsrWrite64 ( >> + LOONGARCH_IOCSR_IPI_SEND, >> + (IOCSR_MBUF_SEND_BLOCKING | >> + (CpuInfoInHob[ProcessorNumber].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) | >> + 0x2 // Bit 2 >> + ) >> + ); >> + >> + // >> + // Wait specified AP waken up >> + // >> + WaitApWakeup (CpuData->StartupApSignal); >> + } >> + } >> +} >> + >> +/** >> + Searches for the next waiting AP. >> + >> + Search for the next AP that is put in waiting state by single-threaded StartupAllAPs(). >> + >> + @param[out] NextProcessorNumber Pointer to the processor number of the next waiting AP. >> + >> + @retval EFI_SUCCESS The next waiting AP has been found. >> + @retval EFI_NOT_FOUND No waiting AP exists. >> + >> +**/ >> +EFI_STATUS >> +GetNextWaitingProcessorNumber ( >> + OUT UINTN *NextProcessorNumber >> + ) >> +{ >> + UINTN ProcessorNumber; >> + CPU_MP_DATA *CpuMpData; >> + >> + CpuMpData = GetCpuMpData (); >> + >> + for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) { >> + if (CpuMpData->CpuData[ProcessorNumber].Waiting) { >> + *NextProcessorNumber = ProcessorNumber; >> + return EFI_SUCCESS; >> + } >> + } >> + >> + return EFI_NOT_FOUND; >> +} >> + >> +/** Checks status of specified AP. >> + >> + This function checks whether the specified AP has finished the task assigned >> + by StartupThisAP(), and whether timeout expires. >> + >> + @param[in] ProcessorNumber The handle number of processor. >> + >> + @retval EFI_SUCCESS Specified AP has finished task assigned by StartupThisAPs(). >> + @retval EFI_TIMEOUT The timeout expires. >> + @retval EFI_NOT_READY Specified AP has not finished task and timeout has not expired. >> +**/ >> +EFI_STATUS >> +CheckThisAP ( >> + IN UINTN ProcessorNumber >> + ) >> +{ >> + CPU_MP_DATA *CpuMpData; >> + CPU_AP_DATA *CpuData; >> + >> + CpuMpData = GetCpuMpData (); >> + CpuData = &CpuMpData->CpuData[ProcessorNumber]; >> + >> + // >> + // If the AP finishes for StartupThisAP(), return EFI_SUCCESS. >> + // >> + if (GetApState (CpuData) == CpuStateFinished) { >> + if (CpuData->Finished != NULL) { >> + *(CpuData->Finished) = TRUE; >> + } >> + >> + SetApState (CpuData, CpuStateIdle); >> + return EFI_SUCCESS; >> + } else { >> + // >> + // If timeout expires for StartupThisAP(), report timeout. >> + // >> + if (CheckTimeout (&CpuData->CurrentTime, &CpuData->TotalTime, CpuData->ExpectedTime)) { >> + if (CpuData->Finished != NULL) { >> + *(CpuData->Finished) = FALSE; >> + } >> + >> + return EFI_TIMEOUT; >> + } >> + } >> + >> + return EFI_NOT_READY; >> +} >> + >> +/** >> + Checks status of all APs. >> + >> + This function checks whether all APs have finished task assigned by StartupAllAPs(), >> + and whether timeout expires. >> + >> + @retval EFI_SUCCESS All APs have finished task assigned by StartupAllAPs(). >> + @retval EFI_TIMEOUT The timeout expires. >> + @retval EFI_NOT_READY APs have not finished task and timeout has not expired. >> +**/ >> +EFI_STATUS >> +CheckAllAPs ( >> + VOID >> + ) >> +{ >> + UINTN ProcessorNumber; >> + UINTN NextProcessorNumber; >> + EFI_STATUS Status; >> + CPU_MP_DATA *CpuMpData; >> + CPU_AP_DATA *CpuData; >> + >> + CpuMpData = GetCpuMpData (); >> + >> + NextProcessorNumber = 0; >> + >> + // >> + // Go through all APs that are responsible for the StartupAllAPs(). >> + // >> + for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) { >> + if (!CpuMpData->CpuData[ProcessorNumber].Waiting) { >> + continue; >> + } >> + >> + CpuData = &CpuMpData->CpuData[ProcessorNumber]; >> + // >> + // Check the CPU state of AP. If it is CpuStateIdle, then the AP has finished its task. >> + // Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the >> + // value of state after setting the it to CpuStateIdle, so BSP can safely make use of its value. >> + // >> + if (GetApState (CpuData) == CpuStateFinished) { >> + CpuMpData->RunningCount--; >> + CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE; >> + SetApState (CpuData, CpuStateIdle); >> + >> + // >> + // If in Single Thread mode, then search for the next waiting AP for execution. >> + // >> + if (CpuMpData->SingleThread) { >> + Status = GetNextWaitingProcessorNumber (&NextProcessorNumber); >> + >> + if (!EFI_ERROR (Status)) { >> + WakeUpAP ( >> + CpuMpData, >> + FALSE, >> + (UINT32)NextProcessorNumber, >> + CpuMpData->Procedure, >> + CpuMpData->ProcArguments, >> + TRUE >> + ); >> + } >> + } >> + } >> + } >> + >> + // >> + // If all APs finish, return EFI_SUCCESS. >> + // >> + if (CpuMpData->RunningCount == 0) { >> + return EFI_SUCCESS; >> + } >> + >> + // >> + // If timeout expires, report timeout. >> + // >> + if (CheckTimeout ( >> + &CpuMpData->CurrentTime, >> + &CpuMpData->TotalTime, >> + CpuMpData->ExpectedTime >> + ) >> + ) >> + { >> + return EFI_TIMEOUT; >> + } >> + >> + return EFI_NOT_READY; >> +} >> + >> +/** >> + Worker function to execute a caller provided function on all enabled APs. >> + >> + @param[in] Procedure A pointer to the function to be run on >> + enabled APs of the system. >> + @param[in] SingleThread If TRUE, then all the enabled APs execute >> + the function specified by Procedure one by >> + one, in ascending order of processor handle >> + number. If FALSE, then all the enabled APs >> + execute the function specified by Procedure >> + simultaneously. >> + @param[in] ExcludeBsp Whether let BSP also trig this task. >> + @param[in] WaitEvent The event created by the caller with CreateEvent() >> + service. >> + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for >> + APs to return from Procedure, either for >> + blocking or non-blocking mode. >> + @param[in] ProcedureArgument The parameter passed into Procedure for >> + all APs. >> + @param[out] FailedCpuList If all APs finish successfully, then its >> + content is set to NULL. If not all APs >> + finish before timeout expires, then its >> + content is set to address of the buffer >> + holding handle numbers of the failed APs. >> + >> + @retval EFI_SUCCESS In blocking mode, all APs have finished before >> + the timeout expired. >> + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched >> + to all enabled APs. >> + @retval others Failed to Startup all APs. >> + >> +**/ >> +EFI_STATUS >> +StartupAllCPUsWorker ( >> + IN EFI_AP_PROCEDURE Procedure, >> + IN BOOLEAN SingleThread, >> + IN BOOLEAN ExcludeBsp, >> + IN EFI_EVENT WaitEvent OPTIONAL, >> + IN UINTN TimeoutInMicroseconds, >> + IN VOID *ProcedureArgument OPTIONAL, >> + OUT UINTN **FailedCpuList OPTIONAL >> + ) >> +{ >> + EFI_STATUS Status; >> + CPU_MP_DATA *CpuMpData; >> + UINTN ProcessorCount; >> + UINTN ProcessorNumber; >> + UINTN CallerNumber; >> + CPU_AP_DATA *CpuData; >> + BOOLEAN HasEnabledAp; >> + CPU_STATE ApState; >> + >> + CpuMpData = GetCpuMpData (); >> + >> + if (FailedCpuList != NULL) { >> + *FailedCpuList = NULL; >> + } >> + >> + if ((CpuMpData->CpuCount == 1) && ExcludeBsp) { >> + return EFI_NOT_STARTED; >> + } >> + >> + if (Procedure == NULL) { >> + return EFI_INVALID_PARAMETER; >> + } >> + >> + // >> + // Check whether caller processor is BSP >> + // >> + MpInitLibWhoAmI (&CallerNumber); >> + if (CallerNumber != CpuMpData->BspNumber) { >> + return EFI_DEVICE_ERROR; >> + } >> + >> + // >> + // Update AP state >> + // >> + CheckAndUpdateApsStatus (); >> + >> + ProcessorCount = CpuMpData->CpuCount; >> + HasEnabledAp = FALSE; >> + // >> + // Check whether all enabled APs are idle. >> + // If any enabled AP is not idle, return EFI_NOT_READY. >> + // >> + for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) { >> + CpuData = &CpuMpData->CpuData[ProcessorNumber]; >> + if (ProcessorNumber != CpuMpData->BspNumber) { >> + ApState = GetApState (CpuData); >> + if (ApState != CpuStateDisabled) { >> + HasEnabledAp = TRUE; >> + if (ApState != CpuStateIdle) { >> + // >> + // If any enabled APs are busy, return EFI_NOT_READY. >> + // >> + return EFI_NOT_READY; >> + } >> + } >> + } >> + } >> + >> + if (!HasEnabledAp && ExcludeBsp) { >> + // >> + // If no enabled AP exists and not include Bsp to do the procedure, return EFI_NOT_STARTED. >> + // >> + return EFI_NOT_STARTED; >> + } >> + >> + CpuMpData->RunningCount = 0; >> + for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) { >> + CpuData = &CpuMpData->CpuData[ProcessorNumber]; >> + CpuData->Waiting = FALSE; >> + if (ProcessorNumber != CpuMpData->BspNumber) { >> + if (CpuData->State == CpuStateIdle) { >> + // >> + // Mark this processor as responsible for current calling. >> + // >> + CpuData->Waiting = TRUE; >> + CpuMpData->RunningCount++; >> + } >> + } >> + } >> + >> + CpuMpData->Procedure = Procedure; >> + CpuMpData->ProcArguments = ProcedureArgument; >> + CpuMpData->SingleThread = SingleThread; >> + CpuMpData->FinishedCount = 0; >> + CpuMpData->ExpectedTime = CalculateTimeout ( >> + TimeoutInMicroseconds, >> + &CpuMpData->CurrentTime >> + ); >> + CpuMpData->TotalTime = 0; >> + CpuMpData->WaitEvent = WaitEvent; >> + >> + if (!SingleThread) { >> + WakeUpAP (CpuMpData, TRUE, 0, Procedure, ProcedureArgument, FALSE); >> + } else { >> + for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) { >> + if (ProcessorNumber == CallerNumber) { >> + continue; >> + } >> + >> + if (CpuMpData->CpuData[ProcessorNumber].Waiting) { >> + WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument, TRUE); >> + break; >> + } >> + } >> + } >> + >> + if (!ExcludeBsp) { >> + // >> + // Start BSP. >> + // >> + Procedure (ProcedureArgument); >> + } >> + >> + Status = EFI_SUCCESS; >> + if (WaitEvent == NULL) { >> + do { >> + Status = CheckAllAPs (); >> + } while (Status == EFI_NOT_READY); >> + } >> + >> + return Status; >> +} >> + >> +/** >> + Worker function to let the caller get one enabled AP to execute a caller-provided >> + function. >> + >> + @param[in] Procedure A pointer to the function to be run on >> + enabled APs of the system. >> + @param[in] ProcessorNumber The handle number of the AP. >> + @param[in] WaitEvent The event created by the caller with CreateEvent() >> + service. >> + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for >> + APs to return from Procedure, either for >> + blocking or non-blocking mode. >> + @param[in] ProcedureArgument The parameter passed into Procedure for >> + all APs. >> + @param[out] Finished If AP returns from Procedure before the >> + timeout expires, its content is set to TRUE. >> + Otherwise, the value is set to FALSE. >> + >> + @retval EFI_SUCCESS In blocking mode, specified AP finished before >> + the timeout expires. >> + @retval others Failed to Startup AP. >> + >> +**/ >> +EFI_STATUS >> +StartupThisAPWorker ( >> + IN EFI_AP_PROCEDURE Procedure, >> + IN UINTN ProcessorNumber, >> + IN EFI_EVENT WaitEvent OPTIONAL, >> + IN UINTN TimeoutInMicroseconds, >> + IN VOID *ProcedureArgument OPTIONAL, >> + OUT BOOLEAN *Finished OPTIONAL >> + ) >> +{ >> + EFI_STATUS Status; >> + CPU_MP_DATA *CpuMpData; >> + CPU_AP_DATA *CpuData; >> + UINTN CallerNumber; >> + >> + CpuMpData = GetCpuMpData (); >> + >> + if (Finished != NULL) { >> + *Finished = FALSE; >> + } >> + >> + // >> + // Check whether caller processor is BSP >> + // >> + MpInitLibWhoAmI (&CallerNumber); >> + if (CallerNumber != CpuMpData->BspNumber) { >> + return EFI_DEVICE_ERROR; >> + } >> + >> + // >> + // Check whether processor with the handle specified by ProcessorNumber exists >> + // >> + if (ProcessorNumber >= CpuMpData->CpuCount) { >> + return EFI_NOT_FOUND; >> + } >> + >> + // >> + // Check whether specified processor is BSP >> + // >> + if (ProcessorNumber == CpuMpData->BspNumber) { >> + return EFI_INVALID_PARAMETER; >> + } >> + >> + // >> + // Check parameter Procedure >> + // >> + if (Procedure == NULL) { >> + return EFI_INVALID_PARAMETER; >> + } >> + >> + // >> + // Update AP state >> + // >> + CheckAndUpdateApsStatus (); >> + >> + // >> + // Check whether specified AP is disabled >> + // >> + if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateDisabled) { >> + return EFI_INVALID_PARAMETER; >> + } >> + >> + CpuData = &CpuMpData->CpuData[ProcessorNumber]; >> + CpuData->WaitEvent = WaitEvent; >> + CpuData->Finished = Finished; >> + CpuData->ExpectedTime = CalculateTimeout (TimeoutInMicroseconds, &CpuData->CurrentTime); >> + CpuData->TotalTime = 0; >> + >> + WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument, FALSE); >> + >> + // >> + // If WaitEvent is NULL, execute in blocking mode. >> + // BSP checks AP's state until it finishes or TimeoutInMicrosecsond expires. >> + // >> + Status = EFI_SUCCESS; >> + if (WaitEvent == NULL) { >> + do { >> + Status = CheckThisAP (ProcessorNumber); >> + } while (Status == EFI_NOT_READY); >> + } >> + >> + return Status; >> +} >> + >> +/** >> + This service executes a caller provided function on all enabled CPUs. >> + >> + @param[in] Procedure A pointer to the function to be run on >> + enabled APs of the system. See type >> + EFI_AP_PROCEDURE. >> + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for >> + APs to return from Procedure, either for >> + blocking or non-blocking mode. Zero means >> + infinity. TimeoutInMicroseconds is ignored >> + for BSP. >> + @param[in] ProcedureArgument The parameter passed into Procedure for >> + all APs. >> + >> + @retval EFI_SUCCESS In blocking mode, all CPUs have finished before >> + the timeout expired. >> + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched >> + to all enabled CPUs. >> + @retval EFI_DEVICE_ERROR Caller processor is AP. >> + @retval EFI_NOT_READY Any enabled APs are busy. >> + @retval EFI_NOT_READY MP Initialize Library is not initialized. >> + @retval EFI_TIMEOUT In blocking mode, the timeout expired before >> + all enabled APs have finished. >> + @retval EFI_INVALID_PARAMETER Procedure is NULL. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +MpInitLibStartupAllCPUs ( >> + IN EFI_AP_PROCEDURE Procedure, >> + IN UINTN TimeoutInMicroseconds, >> + IN VOID *ProcedureArgument OPTIONAL >> + ) >> +{ >> + return StartupAllCPUsWorker ( >> + Procedure, >> + TRUE, >> + FALSE, >> + NULL, >> + TimeoutInMicroseconds, >> + ProcedureArgument, >> + NULL >> + ); >> +} >> + >> +/** >> + MP Initialize Library initialization. >> + >> + This service will allocate AP reset vector and wakeup all APs to do APs >> + initialization. >> + >> + This service must be invoked before all other MP Initialize Library >> + service are invoked. >> + >> + @retval EFI_SUCCESS MP initialization succeeds. >> + @retval Others MP initialization fails. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +MpInitLibInitialize ( >> + VOID >> + ) >> +{ >> + CPU_MP_DATA *OldCpuMpData; >> + CPU_INFO_IN_HOB *CpuInfoInHob; >> + UINT32 MaxLogicalProcessorNumber; >> + UINTN BufferSize; >> + UINTN MonitorBufferSize; >> + VOID *MpBuffer; >> + CPU_MP_DATA *CpuMpData; >> + UINTN Index; >> + >> + OldCpuMpData = GetCpuMpDataFromGuidedHob (); >> + if (OldCpuMpData == NULL) { >> + MaxLogicalProcessorNumber = PcdGet32 (PcdCpuMaxLogicalProcessorNumber); >> + } else { >> + MaxLogicalProcessorNumber = OldCpuMpData->CpuCount; >> + } >> + >> + ASSERT (MaxLogicalProcessorNumber != 0); >> + >> + MonitorBufferSize = sizeof (WAKEUP_AP_SIGNAL) * MaxLogicalProcessorNumber; >> + >> + BufferSize = 0; >> + BufferSize += MonitorBufferSize; >> + BufferSize += sizeof (CPU_MP_DATA); >> + BufferSize += (sizeof (CPU_AP_DATA) + sizeof (CPU_INFO_IN_HOB))* MaxLogicalProcessorNumber; >> + MpBuffer = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize)); >> + ASSERT (MpBuffer != NULL); >> + ZeroMem (MpBuffer, BufferSize); >> + >> + CpuMpData = (CPU_MP_DATA *)MpBuffer; >> + >> + CpuMpData->CpuCount = 1; >> + CpuMpData->BspNumber = 0; >> + CpuMpData->CpuData = (CPU_AP_DATA *)(CpuMpData + 1); >> + CpuMpData->CpuInfoInHob = (UINT64)(UINTN)(CpuMpData->CpuData + MaxLogicalProcessorNumber); >> + >> + InitializeSpinLock (&CpuMpData->MpLock); >> + >> + // >> + // Set BSP basic information >> + // >> + InitializeApData (CpuMpData, 0, 0); >> + >> + // >> + // Set up APs wakeup signal buffer and initialization APs ApicId status. >> + // >> + for (Index = 0; Index < MaxLogicalProcessorNumber; Index++) { >> + CpuMpData->CpuData[Index].StartupApSignal = >> + (UINT32 *)((MpBuffer + BufferSize - MonitorBufferSize) + (sizeof (WAKEUP_AP_SIGNAL) * Index)); >> + if ((OldCpuMpData == NULL) && (Index != CpuMpData->BspNumber)) { >> + ((CPU_INFO_IN_HOB *)CpuMpData->CpuInfoInHob)[Index].ApicId = INVALID_APIC_ID; >> + } >> + } >> + >> + if (OldCpuMpData == NULL) { >> + if (MaxLogicalProcessorNumber > 1) { >> + // >> + // Wakeup all APs and calculate the processor count in system >> + // >> + CollectProcessorCount (CpuMpData); >> + } >> + } else { >> + // >> + // APs have been wakeup before, just get the CPU Information >> + // from HOB >> + // >> + CpuMpData->CpuCount = OldCpuMpData->CpuCount; >> + CpuMpData->BspNumber = OldCpuMpData->BspNumber; >> + CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob; >> + CpuMpData->MpCpuExchangeInfo = OldCpuMpData->MpCpuExchangeInfo; >> + >> + CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob; >> + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { >> + InitializeSpinLock (&CpuMpData->CpuData[Index].ApLock); >> + CpuMpData->CpuData[Index].CpuHealthy = (CpuInfoInHob[Index].Health == 0) ? TRUE : FALSE; >> + } >> + >> + if (CpuMpData->CpuCount > 1) { >> + // >> + // Only needs to use this flag for DXE phase to update the wake up >> + // buffer. Wakeup buffer allocated in PEI phase is no longer valid >> + // in DXE. >> + // >> + CpuMpData->InitFlag = ApInitReconfig; >> + WakeUpAP (CpuMpData, TRUE, 0, NULL, NULL, TRUE); >> + >> + // >> + // Wait for all APs finished initialization >> + // >> + while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) { >> + CpuPause (); >> + } >> + >> + CpuMpData->InitFlag = ApInitDone; >> + } >> + >> + if (MaxLogicalProcessorNumber > 1) { >> + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { >> + SetApState (&CpuMpData->CpuData[Index], CpuStateIdle); >> + } >> + } >> + } >> + >> + // >> + // Initialize global data for MP support >> + // >> + InitMpGlobalData (CpuMpData); >> + >> + return EFI_SUCCESS; >> +} >> + >> +/** >> + Gets detailed MP-related information on the requested processor at the >> + instant this call is made. This service may only be called from the BSP. >> + >> + @param[in] ProcessorNumber The handle number of processor. >> + @param[out] ProcessorInfoBuffer A pointer to the buffer where information for >> + the requested processor is deposited. >> + @param[out] HealthData Return processor health data. >> + >> + @retval EFI_SUCCESS Processor information was returned. >> + @retval EFI_DEVICE_ERROR The calling processor is an AP. >> + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. >> + @retval EFI_NOT_FOUND The processor with the handle specified by >> + ProcessorNumber does not exist in the platform. >> + @retval EFI_NOT_READY MP Initialize Library is not initialized. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +MpInitLibGetProcessorInfo ( >> + IN UINTN ProcessorNumber, >> + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer, >> + OUT EFI_HEALTH_FLAGS *HealthData OPTIONAL >> + ) >> +{ >> + CPU_MP_DATA *CpuMpData; >> + UINTN CallerNumber; >> + CPU_INFO_IN_HOB *CpuInfoInHob; >> + >> + CpuMpData = GetCpuMpData (); >> + CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob; >> + >> + // >> + // Check whether caller processor is BSP >> + // >> + MpInitLibWhoAmI (&CallerNumber); >> + if (CallerNumber != CpuMpData->BspNumber) { >> + return EFI_DEVICE_ERROR; >> + } >> + >> + if (ProcessorInfoBuffer == NULL) { >> + return EFI_INVALID_PARAMETER; >> + } >> + >> + if (ProcessorNumber >= CpuMpData->CpuCount) { >> + return EFI_NOT_FOUND; >> + } >> + >> + ProcessorInfoBuffer->ProcessorId = (UINT64)CpuInfoInHob[ProcessorNumber].ApicId; >> + ProcessorInfoBuffer->StatusFlag = 0; >> + if (ProcessorNumber == CpuMpData->BspNumber) { >> + ProcessorInfoBuffer->StatusFlag |= PROCESSOR_AS_BSP_BIT; >> + } >> + >> + if (CpuMpData->CpuData[ProcessorNumber].CpuHealthy) { >> + ProcessorInfoBuffer->StatusFlag |= PROCESSOR_HEALTH_STATUS_BIT; >> + } >> + >> + if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateDisabled) { >> + ProcessorInfoBuffer->StatusFlag &= ~PROCESSOR_ENABLED_BIT; >> + } else { >> + ProcessorInfoBuffer->StatusFlag |= PROCESSOR_ENABLED_BIT; >> + } >> + >> + if (HealthData != NULL) { >> + HealthData->Uint32 = CpuInfoInHob[ProcessorNumber].Health; >> + } >> + >> + return EFI_SUCCESS; >> +} >> + >> +/** >> + This return the handle number for the calling processor. This service may be >> + called from the BSP and APs. >> + >> + @param[out] ProcessorNumber Pointer to the handle number of AP. >> + The range is from 0 to the total number of >> + logical processors minus 1. The total number of >> + logical processors can be retrieved by >> + MpInitLibGetNumberOfProcessors(). >> + >> + @retval EFI_SUCCESS The current processor handle number was returned >> + in ProcessorNumber. >> + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. >> + @retval EFI_NOT_READY MP Initialize Library is not initialized. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +MpInitLibWhoAmI ( >> + OUT UINTN *ProcessorNumber >> + ) >> +{ >> + CPU_MP_DATA *CpuMpData; >> + >> + if (ProcessorNumber == NULL) { >> + return EFI_INVALID_PARAMETER; >> + } >> + >> + CpuMpData = GetCpuMpData (); >> + >> + return GetProcessorNumber (CpuMpData, ProcessorNumber); >> +} >> + >> +/** >> + Retrieves the number of logical processor in the platform and the number of >> + those logical processors that are enabled on this boot. This service may only >> + be called from the BSP. >> + >> + @param[out] NumberOfProcessors Pointer to the total number of logical >> + processors in the system, including the BSP >> + and disabled APs. >> + @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical >> + processors that exist in system, including >> + the BSP. >> + >> + @retval EFI_SUCCESS The number of logical processors and enabled >> + logical processors was retrieved. >> + @retval EFI_DEVICE_ERROR The calling processor is an AP. >> + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL and NumberOfEnabledProcessors >> + is NULL. >> + @retval EFI_NOT_READY MP Initialize Library is not initialized. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +MpInitLibGetNumberOfProcessors ( >> + OUT UINTN *NumberOfProcessors OPTIONAL, >> + OUT UINTN *NumberOfEnabledProcessors OPTIONAL >> + ) >> +{ >> + CPU_MP_DATA *CpuMpData; >> + UINTN CallerNumber; >> + UINTN ProcessorNumber; >> + UINTN EnabledProcessorNumber; >> + UINTN Index; >> + >> + CpuMpData = GetCpuMpData (); >> + >> + if ((NumberOfProcessors == NULL) && (NumberOfEnabledProcessors == NULL)) { >> + return EFI_INVALID_PARAMETER; >> + } >> + >> + // >> + // Check whether caller processor is BSP >> + // >> + MpInitLibWhoAmI (&CallerNumber); >> + if (CallerNumber != CpuMpData->BspNumber) { >> + return EFI_DEVICE_ERROR; >> + } >> + >> + ProcessorNumber = CpuMpData->CpuCount; >> + EnabledProcessorNumber = 0; >> + for (Index = 0; Index < ProcessorNumber; Index++) { >> + if (GetApState (&CpuMpData->CpuData[Index]) != CpuStateDisabled) { >> + EnabledProcessorNumber++; >> + } >> + } >> + >> + if (NumberOfProcessors != NULL) { >> + *NumberOfProcessors = ProcessorNumber; >> + } >> + >> + if (NumberOfEnabledProcessors != NULL) { >> + *NumberOfEnabledProcessors = EnabledProcessorNumber; >> + } >> + >> + return EFI_SUCCESS; >> +} >> + >> +/** >> + Get pointer to CPU MP Data structure from GUIDed HOB. >> + >> + @return The pointer to CPU MP Data structure. >> +**/ >> +CPU_MP_DATA * >> +GetCpuMpDataFromGuidedHob ( >> + VOID >> + ) >> +{ >> + EFI_HOB_GUID_TYPE *GuidHob; >> + VOID *DataInHob; >> + CPU_MP_DATA *CpuMpData; >> + >> + CpuMpData = NULL; >> + GuidHob = GetFirstGuidHob (&mCpuInitMpLibHobGuid); >> + >> + if (GuidHob != NULL) { >> + DataInHob = GET_GUID_HOB_DATA (GuidHob); >> + CpuMpData = (CPU_MP_DATA *)(*(UINTN *)DataInHob); >> + } >> + >> + return CpuMpData; >> +} >> diff --git a/UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.h b/UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.h >> new file mode 100644 >> index 0000000000..b9c6c55b41 >> --- /dev/null >> +++ b/UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.h >> @@ -0,0 +1,361 @@ >> +/** @file >> + Common header file for LoongArch MP Initialize Library. >> + >> + Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.
>> + >> + SPDX-License-Identifier: BSD-2-Clause-Patent >> + >> +**/ >> + >> +#ifndef MP_LIB_H_ >> +#define MP_LIB_H_ >> + >> +#include >> +#include >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#define WAKEUP_AP_SIGNAL SIGNATURE_32 ('S', 'T', 'A', 'P') >> + >> +#define CPU_INIT_MP_LIB_HOB_GUID \ >> + { \ >> + 0x58eb6a19, 0x3699, 0x4c68, { 0xa8, 0x36, 0xda, 0xcd, 0x8e, 0xdc, 0xad, 0x4a } \ >> + } >> + >> +#define PROCESSOR_RESOURCE_HOB_GUID \ >> + { \ >> + 0xb855c7fe, 0xa758, 0x701f, { 0xa7, 0x30, 0x87, 0xf3, 0x9c, 0x03, 0x46, 0x7e } \ >> + } >> + >> +// >> +// AP loop state when APs are in idle state >> +// It's value is the same with PcdCpuApLoopMode >> +// >> +typedef enum { >> + ApInHltLoop = 1, >> + ApInRunLoop = 2 >> +} AP_LOOP_MODE; >> + >> +// >> +// AP initialization state during APs wakeup >> +// >> +typedef enum { >> + ApInitConfig = 1, >> + ApInitReconfig = 2, >> + ApInitDone = 3 >> +} AP_INIT_STATE; >> + >> +// >> +// AP state >> +// >> +typedef enum { >> + CpuStateIdle, >> + CpuStateReady, >> + CpuStateBusy, >> + CpuStateFinished, >> + CpuStateDisabled >> +} CPU_STATE; >> + >> +// >> +// AP related data >> +// >> +typedef struct { >> + SPIN_LOCK ApLock; >> + volatile UINT32 *StartupApSignal; >> + volatile UINTN ApFunction; >> + volatile UINTN ApFunctionArgument; >> + BOOLEAN CpuHealthy; >> + volatile CPU_STATE State; >> + BOOLEAN Waiting; >> + BOOLEAN *Finished; >> + UINT64 ExpectedTime; >> + UINT64 CurrentTime; >> + UINT64 TotalTime; >> + EFI_EVENT WaitEvent; >> +} CPU_AP_DATA; >> + >> +// >> +// Basic CPU information saved in Guided HOB. >> +// Because the contents will be shard between PEI and DXE, >> +// we need to make sure the each fields offset same in different >> +// architecture. >> +// >> +#pragma pack (1) >> +typedef struct { >> + UINT32 ApicId; >> + UINT32 Health; >> +} CPU_INFO_IN_HOB; >> +#pragma pack () >> + >> +typedef struct MP_CPU_DATA CPU_MP_DATA; >> + >> +#pragma pack(1) >> + >> +// >> +// MP CPU exchange information for AP reset code >> +// This structure is required to be packed because fixed field offsets >> +// into this structure are used in assembly code in this module >> +// >> +typedef struct { >> + CPU_MP_DATA *CpuMpData; >> +} MP_CPU_EXCHANGE_INFO; >> + >> +#pragma pack() >> + >> +typedef struct { >> + SPIN_LOCK Lock; >> + UINT32 CpuCount; >> + UINT64 CpuInfoInHob; >> +} PROCESSOR_RESOURCE_DATA; >> + >> +// >> +// CPU MP Data save in memory >> +// >> +struct MP_CPU_DATA { >> + UINT64 CpuInfoInHob; >> + UINT32 CpuCount; >> + UINT32 BspNumber; >> + // >> + // The above fields data will be passed from PEI to DXE >> + // Please make sure the fields offset same in the different >> + // architecture. >> + // >> + SPIN_LOCK MpLock; >> + >> + volatile UINT32 FinishedCount; >> + UINT32 RunningCount; >> + BOOLEAN SingleThread; >> + EFI_AP_PROCEDURE Procedure; >> + VOID *ProcArguments; >> + BOOLEAN *Finished; >> + UINT64 ExpectedTime; >> + UINT64 CurrentTime; >> + UINT64 TotalTime; >> + EFI_EVENT WaitEvent; >> + >> + AP_INIT_STATE InitFlag; >> + UINT8 ApLoopMode; >> + CPU_AP_DATA *CpuData; >> + volatile MP_CPU_EXCHANGE_INFO *MpCpuExchangeInfo; >> +}; >> + >> +extern EFI_GUID mCpuInitMpLibHobGuid; >> +extern EFI_GUID mProcessorResourceHobGuid; >> + >> +/** >> + Get the pointer to CPU MP Data structure. >> + >> + @return The pointer to CPU MP Data structure. >> +**/ >> +CPU_MP_DATA * >> +GetCpuMpData ( >> + VOID >> + ); >> + >> +/** >> + Save the pointer to CPU MP Data structure. >> + >> + @param[in] CpuMpData The pointer to CPU MP Data structure will be saved. >> +**/ >> +VOID >> +SaveCpuMpData ( >> + IN CPU_MP_DATA *CpuMpData >> + ); >> + >> +/** >> + This function will be called by BSP to wakeup AP. >> + >> + @param[in] CpuMpData Pointer to CPU MP Data >> + @param[in] Broadcast TRUE: Send broadcast IPI to all APs >> + FALSE: Send IPI to AP by ApicId >> + @param[in] ProcessorNumber The handle number of specified processor >> + @param[in] Procedure The function to be invoked by AP >> + @param[in] ProcedureArgument The argument to be passed into AP function >> + @param[in] WakeUpDisabledAps Whether need to wake up disabled APs in broadcast mode. >> +**/ >> +VOID >> +WakeUpAP ( >> + IN CPU_MP_DATA *CpuMpData, >> + IN BOOLEAN Broadcast, >> + IN UINTN ProcessorNumber, >> + IN EFI_AP_PROCEDURE Procedure OPTIONAL, >> + IN VOID *ProcedureArgument OPTIONAL, >> + IN BOOLEAN WakeUpDisabledAps >> + ); >> + >> +/** >> + Initialize global data for MP support. >> + >> + @param[in] CpuMpData The pointer to CPU MP Data structure. >> +**/ >> +VOID >> +InitMpGlobalData ( >> + IN CPU_MP_DATA *CpuMpData >> + ); >> + >> +/** >> + Worker function to execute a caller provided function on all enabled APs. >> + >> + @param[in] Procedure A pointer to the function to be run on >> + enabled APs of the system. >> + @param[in] SingleThread If TRUE, then all the enabled APs execute >> + the function specified by Procedure one by >> + one, in ascending order of processor handle >> + number. If FALSE, then all the enabled APs >> + execute the function specified by Procedure >> + simultaneously. >> + @param[in] WaitEvent The event created by the caller with CreateEvent() >> + service. >> + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for >> + APs to return from Procedure, either for >> + blocking or non-blocking mode. >> + @param[in] ProcedureArgument The parameter passed into Procedure for >> + all APs. >> + @param[out] FailedCpuList If all APs finish successfully, then its >> + content is set to NULL. If not all APs >> + finish before timeout expires, then its >> + content is set to address of the buffer >> + holding handle numbers of the failed APs. >> + >> + @retval EFI_SUCCESS In blocking mode, all APs have finished before >> + the timeout expired. >> + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched >> + to all enabled APs. >> + @retval others Failed to Startup all APs. >> + >> +**/ >> +EFI_STATUS >> +StartupAllCPUsWorker ( >> + IN EFI_AP_PROCEDURE Procedure, >> + IN BOOLEAN SingleThread, >> + IN BOOLEAN ExcludeBsp, >> + IN EFI_EVENT WaitEvent OPTIONAL, >> + IN UINTN TimeoutInMicroseconds, >> + IN VOID *ProcedureArgument OPTIONAL, >> + OUT UINTN **FailedCpuList OPTIONAL >> + ); >> + >> +/** >> + Worker function to let the caller get one enabled AP to execute a caller-provided >> + function. >> + >> + @param[in] Procedure A pointer to the function to be run on >> + enabled APs of the system. >> + @param[in] ProcessorNumber The handle number of the AP. >> + @param[in] WaitEvent The event created by the caller with CreateEvent() >> + service. >> + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for >> + APs to return from Procedure, either for >> + blocking or non-blocking mode. >> + @param[in] ProcedureArgument The parameter passed into Procedure for >> + all APs. >> + @param[out] Finished If AP returns from Procedure before the >> + timeout expires, its content is set to TRUE. >> + Otherwise, the value is set to FALSE. >> + >> + @retval EFI_SUCCESS In blocking mode, specified AP finished before >> + the timeout expires. >> + @retval others Failed to Startup AP. >> + >> +**/ >> +EFI_STATUS >> +StartupThisAPWorker ( >> + IN EFI_AP_PROCEDURE Procedure, >> + IN UINTN ProcessorNumber, >> + IN EFI_EVENT WaitEvent OPTIONAL, >> + IN UINTN TimeoutInMicroseconds, >> + IN VOID *ProcedureArgument OPTIONAL, >> + OUT BOOLEAN *Finished OPTIONAL >> + ); >> + >> +/** >> + Worker function to let the caller enable or disable an AP from this point onward. >> + This service may only be called from the BSP. >> + This instance will be added in the future. >> + >> + @param[in] ProcessorNumber The handle number of AP. >> + @param[in] EnableAP Specifies the new state for the processor for >> + enabled, FALSE for disabled. >> + @param[in] HealthFlag If not NULL, a pointer to a value that specifies >> + the new health status of the AP. >> + >> + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. >> + @retval others Failed to Enable/Disable AP. >> + >> +**/ >> +EFI_STATUS >> +EnableDisableApWorker ( >> + IN UINTN ProcessorNumber, >> + IN BOOLEAN EnableAP, >> + IN UINT32 *HealthFlag OPTIONAL >> + ); >> + >> +/** >> + Get pointer to CPU MP Data structure from GUIDed HOB. >> + >> + @return The pointer to CPU MP Data structure. >> +**/ >> +CPU_MP_DATA * >> +GetCpuMpDataFromGuidedHob ( >> + VOID >> + ); >> + >> +/** Checks status of specified AP. >> + >> + This function checks whether the specified AP has finished the task assigned >> + by StartupThisAP(), and whether timeout expires. >> + >> + @param[in] ProcessorNumber The handle number of processor. >> + >> + @retval EFI_SUCCESS Specified AP has finished task assigned by StartupThisAPs(). >> + @retval EFI_TIMEOUT The timeout expires. >> + @retval EFI_NOT_READY Specified AP has not finished task and timeout has not expired. >> +**/ >> +EFI_STATUS >> +CheckThisAP ( >> + IN UINTN ProcessorNumber >> + ); >> + >> +/** >> + Checks status of all APs. >> + >> + This function checks whether all APs have finished task assigned by StartupAllAPs(), >> + and whether timeout expires. >> + >> + @retval EFI_SUCCESS All APs have finished task assigned by StartupAllAPs(). >> + @retval EFI_TIMEOUT The timeout expires. >> + @retval EFI_NOT_READY APs have not finished task and timeout has not expired. >> +**/ >> +EFI_STATUS >> +CheckAllAPs ( >> + VOID >> + ); >> + >> +/** >> + Checks APs status and updates APs status if needed. >> + >> +**/ >> +VOID >> +CheckAndUpdateApsStatus ( >> + VOID >> + ); >> + >> +/** >> + Enable Debug Agent to support source debugging on AP function. >> + This instance will added in the future. >> + >> +**/ >> +VOID >> +EnableDebugAgent ( >> + VOID >> + ); >> + >> +#endif >> diff --git a/UefiCpuPkg/Library/MpInitLib/LoongArch64/PeiMpLib.c b/UefiCpuPkg/Library/MpInitLib/LoongArch64/PeiMpLib.c >> new file mode 100644 >> index 0000000000..d1c5e55b57 >> --- /dev/null >> +++ b/UefiCpuPkg/Library/MpInitLib/LoongArch64/PeiMpLib.c >> @@ -0,0 +1,404 @@ >> +/** @file >> + LoongArch64 MP initialize support functions for PEI phase. >> + >> + Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.
>> + SPDX-License-Identifier: BSD-2-Clause-Patent >> + >> +**/ >> + >> +#include "MpLib.h" >> + >> +/** >> + Enable Debug Agent to support source debugging on AP function. >> + >> +**/ >> +VOID >> +EnableDebugAgent ( >> + VOID >> + ) >> +{ >> +} >> + >> +/** >> + Get pointer to CPU MP Data structure. >> + >> + @return The pointer to CPU MP Data structure. >> +**/ >> +CPU_MP_DATA * >> +GetCpuMpData ( >> + VOID >> + ) >> +{ >> + CPU_MP_DATA *CpuMpData; >> + >> + CpuMpData = GetCpuMpDataFromGuidedHob (); >> + ASSERT (CpuMpData != NULL); >> + return CpuMpData; >> +} >> + >> +/** >> + Save the pointer to CPU MP Data structure. >> + >> + @param[in] CpuMpData The pointer to CPU MP Data structure will be saved. >> +**/ >> +VOID >> +SaveCpuMpData ( >> + IN CPU_MP_DATA *CpuMpData >> + ) >> +{ >> + UINT64 Data64; >> + >> + // >> + // Build location of CPU MP DATA buffer in HOB >> + // >> + Data64 = (UINT64)(UINTN)CpuMpData; >> + BuildGuidDataHob ( >> + &mCpuInitMpLibHobGuid, >> + (VOID *)&Data64, >> + sizeof (UINT64) >> + ); >> +} >> + >> +/** >> + Save the Processor Resource Data. >> + >> + @param[in] ResourceData The pointer to Processor Resource Data structure will be saved. >> +**/ >> +VOID >> +SaveProcessorResourceData ( >> + IN PROCESSOR_RESOURCE_DATA *ResourceData >> + ) >> +{ >> + UINT64 Data64; >> + >> + // >> + // Build location of Processor Resource Data buffer in HOB >> + // >> + Data64 = (UINT64)(UINTN)ResourceData; >> + BuildGuidDataHob ( >> + &mProcessorResourceHobGuid, >> + (VOID *)&Data64, >> + sizeof (UINT64) >> + ); >> +} >> + >> +/** >> + Get available EfiBootServicesCode memory below 4GB by specified size. >> + >> + This buffer is required to safely transfer AP from real address mode to >> + protected mode or long mode, due to the fact that the buffer returned by >> + GetWakeupBuffer() may be marked as non-executable. >> + >> + @param[in] BufferSize Wakeup transition buffer size. >> + >> + @retval other Return wakeup transition buffer address below 4GB. >> + @retval 0 Cannot find free memory below 4GB. >> +**/ >> +UINTN >> +GetModeTransitionBuffer ( >> + IN UINTN BufferSize >> + ) >> +{ >> + // >> + // PEI phase doesn't need to do such transition. So simply return 0. >> + // >> + return 0; >> +} >> + >> +/** >> + Checks APs status and updates APs status if needed. >> + >> +**/ >> +VOID >> +CheckAndUpdateApsStatus ( >> + VOID >> + ) >> +{ >> +} >> + >> +/** >> + Initialize global data for MP support. >> + >> + @param[in] CpuMpData The pointer to CPU MP Data structure. >> +**/ >> +VOID >> +InitMpGlobalData ( >> + IN CPU_MP_DATA *CpuMpData >> + ) >> +{ >> + SaveCpuMpData (CpuMpData); >> +} >> + >> +/** >> + This service executes a caller provided function on all enabled APs. >> + >> + @param[in] Procedure A pointer to the function to be run on >> + enabled APs of the system. See type >> + EFI_AP_PROCEDURE. >> + @param[in] SingleThread If TRUE, then all the enabled APs execute >> + the function specified by Procedure one by >> + one, in ascending order of processor handle >> + number. If FALSE, then all the enabled APs >> + execute the function specified by Procedure >> + simultaneously. >> + @param[in] WaitEvent The event created by the caller with CreateEvent() >> + service. If it is NULL, then execute in >> + blocking mode. BSP waits until all APs finish >> + or TimeoutInMicroSeconds expires. If it's >> + not NULL, then execute in non-blocking mode. >> + BSP requests the function specified by >> + Procedure to be started on all the enabled >> + APs, and go on executing immediately. If >> + all return from Procedure, or TimeoutInMicroSeconds >> + expires, this event is signaled. The BSP >> + can use the CheckEvent() or WaitForEvent() >> + services to check the state of event. Type >> + EFI_EVENT is defined in CreateEvent() in >> + the Unified Extensible Firmware Interface >> + Specification. >> + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for >> + APs to return from Procedure, either for >> + blocking or non-blocking mode. Zero means >> + infinity. If the timeout expires before >> + all APs return from Procedure, then Procedure >> + on the failed APs is terminated. All enabled >> + APs are available for next function assigned >> + by MpInitLibStartupAllAPs() or >> + MPInitLibStartupThisAP(). >> + If the timeout expires in blocking mode, >> + BSP returns EFI_TIMEOUT. If the timeout >> + expires in non-blocking mode, WaitEvent >> + is signaled with SignalEvent(). >> + @param[in] ProcedureArgument The parameter passed into Procedure for >> + all APs. >> + @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise, >> + if all APs finish successfully, then its >> + content is set to NULL. If not all APs >> + finish before timeout expires, then its >> + content is set to address of the buffer >> + holding handle numbers of the failed APs. >> + The buffer is allocated by MP Initialization >> + library, and it's the caller's responsibility to >> + free the buffer with FreePool() service. >> + In blocking mode, it is ready for consumption >> + when the call returns. In non-blocking mode, >> + it is ready when WaitEvent is signaled. The >> + list of failed CPU is terminated by >> + END_OF_CPU_LIST. >> + >> + @retval EFI_SUCCESS In blocking mode, all APs have finished before >> + the timeout expired. >> + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched >> + to all enabled APs. >> + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the >> + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was >> + signaled. >> + @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not >> + supported. >> + @retval EFI_DEVICE_ERROR Caller processor is AP. >> + @retval EFI_NOT_STARTED No enabled APs exist in the system. >> + @retval EFI_NOT_READY Any enabled APs are busy. >> + @retval EFI_NOT_READY MP Initialize Library is not initialized. >> + @retval EFI_TIMEOUT In blocking mode, the timeout expired before >> + all enabled APs have finished. >> + @retval EFI_INVALID_PARAMETER Procedure is NULL. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +MpInitLibStartupAllAPs ( >> + IN EFI_AP_PROCEDURE Procedure, >> + IN BOOLEAN SingleThread, >> + IN EFI_EVENT WaitEvent OPTIONAL, >> + IN UINTN TimeoutInMicroseconds, >> + IN VOID *ProcedureArgument OPTIONAL, >> + OUT UINTN **FailedCpuList OPTIONAL >> + ) >> +{ >> + if (WaitEvent != NULL) { >> + return EFI_UNSUPPORTED; >> + } >> + >> + return StartupAllCPUsWorker ( >> + Procedure, >> + SingleThread, >> + TRUE, >> + NULL, >> + TimeoutInMicroseconds, >> + ProcedureArgument, >> + FailedCpuList >> + ); >> +} >> + >> +/** >> + This service lets the caller get one enabled AP to execute a caller-provided >> + function. >> + >> + @param[in] Procedure A pointer to the function to be run on the >> + designated AP of the system. See type >> + EFI_AP_PROCEDURE. >> + @param[in] ProcessorNumber The handle number of the AP. The range is >> + from 0 to the total number of logical >> + processors minus 1. The total number of >> + logical processors can be retrieved by >> + MpInitLibGetNumberOfProcessors(). >> + @param[in] WaitEvent The event created by the caller with CreateEvent() >> + service. If it is NULL, then execute in >> + blocking mode. BSP waits until this AP finish >> + or TimeoutInMicroSeconds expires. If it's >> + not NULL, then execute in non-blocking mode. >> + BSP requests the function specified by >> + Procedure to be started on this AP, >> + and go on executing immediately. If this AP >> + return from Procedure or TimeoutInMicroSeconds >> + expires, this event is signaled. The BSP >> + can use the CheckEvent() or WaitForEvent() >> + services to check the state of event. Type >> + EFI_EVENT is defined in CreateEvent() in >> + the Unified Extensible Firmware Interface >> + Specification. >> + @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for >> + this AP to finish this Procedure, either for >> + blocking or non-blocking mode. Zero means >> + infinity. If the timeout expires before >> + this AP returns from Procedure, then Procedure >> + on the AP is terminated. The >> + AP is available for next function assigned >> + by MpInitLibStartupAllAPs() or >> + MpInitLibStartupThisAP(). >> + If the timeout expires in blocking mode, >> + BSP returns EFI_TIMEOUT. If the timeout >> + expires in non-blocking mode, WaitEvent >> + is signaled with SignalEvent(). >> + @param[in] ProcedureArgument The parameter passed into Procedure on the >> + specified AP. >> + @param[out] Finished If NULL, this parameter is ignored. In >> + blocking mode, this parameter is ignored. >> + In non-blocking mode, if AP returns from >> + Procedure before the timeout expires, its >> + content is set to TRUE. Otherwise, the >> + value is set to FALSE. The caller can >> + determine if the AP returned from Procedure >> + by evaluating this value. >> + >> + @retval EFI_SUCCESS In blocking mode, specified AP finished before >> + the timeout expires. >> + @retval EFI_SUCCESS In non-blocking mode, the function has been >> + dispatched to specified AP. >> + @retval EFI_UNSUPPORTED A non-blocking mode request was made after the >> + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was >> + signaled. >> + @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not >> + supported. >> + @retval EFI_DEVICE_ERROR The calling processor is an AP. >> + @retval EFI_TIMEOUT In blocking mode, the timeout expired before >> + the specified AP has finished. >> + @retval EFI_NOT_READY The specified AP is busy. >> + @retval EFI_NOT_READY MP Initialize Library is not initialized. >> + @retval EFI_NOT_FOUND The processor with the handle specified by >> + ProcessorNumber does not exist. >> + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. >> + @retval EFI_INVALID_PARAMETER Procedure is NULL. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +MpInitLibStartupThisAP ( >> + IN EFI_AP_PROCEDURE Procedure, >> + IN UINTN ProcessorNumber, >> + IN EFI_EVENT WaitEvent OPTIONAL, >> + IN UINTN TimeoutInMicroseconds, >> + IN VOID *ProcedureArgument OPTIONAL, >> + OUT BOOLEAN *Finished OPTIONAL >> + ) >> +{ >> + if (WaitEvent != NULL) { >> + return EFI_UNSUPPORTED; >> + } >> + >> + return StartupThisAPWorker ( >> + Procedure, >> + ProcessorNumber, >> + NULL, >> + TimeoutInMicroseconds, >> + ProcedureArgument, >> + Finished >> + ); >> +} >> + >> +/** >> + This service switches the requested AP to be the BSP from that point onward. >> + This service changes the BSP for all purposes. This call can only be performed >> + by the current BSP. >> + >> + @param[in] ProcessorNumber The handle number of AP that is to become the new >> + BSP. The range is from 0 to the total number of >> + logical processors minus 1. The total number of >> + logical processors can be retrieved by >> + MpInitLibGetNumberOfProcessors(). >> + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an >> + enabled AP. Otherwise, it will be disabled. >> + >> + @retval EFI_SUCCESS BSP successfully switched. >> + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to >> + this service returning. >> + @retval EFI_UNSUPPORTED Switching the BSP is not supported. >> + @retval EFI_DEVICE_ERROR The calling processor is an AP. >> + @retval EFI_NOT_FOUND The processor with the handle specified by >> + ProcessorNumber does not exist. >> + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or >> + a disabled AP. >> + @retval EFI_NOT_READY The specified AP is busy. >> + @retval EFI_NOT_READY MP Initialize Library is not initialized. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +MpInitLibSwitchBSP ( >> + IN UINTN ProcessorNumber, >> + IN BOOLEAN EnableOldBSP >> + ) >> +{ >> + return EFI_UNSUPPORTED; >> +} >> + >> +/** >> + This service lets the caller enable or disable an AP from this point onward. >> + This service may only be called from the BSP. >> + >> + @param[in] ProcessorNumber The handle number of AP. >> + The range is from 0 to the total number of >> + logical processors minus 1. The total number of >> + logical processors can be retrieved by >> + MpInitLibGetNumberOfProcessors(). >> + @param[in] EnableAP Specifies the new state for the processor for >> + enabled, FALSE for disabled. >> + @param[in] HealthFlag If not NULL, a pointer to a value that specifies >> + the new health status of the AP. This flag >> + corresponds to StatusFlag defined in >> + EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only >> + the PROCESSOR_HEALTH_STATUS_BIT is used. All other >> + bits are ignored. If it is NULL, this parameter >> + is ignored. >> + >> + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. >> + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed >> + prior to this service returning. >> + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. >> + @retval EFI_DEVICE_ERROR The calling processor is an AP. >> + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber >> + does not exist. >> + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. >> + @retval EFI_NOT_READY MP Initialize Library is not initialized. >> + >> +**/ >> +EFI_STATUS >> +EFIAPI >> +MpInitLibEnableDisableAP ( >> + IN UINTN ProcessorNumber, >> + IN BOOLEAN EnableAP, >> + IN UINT32 *HealthFlag OPTIONAL >> + ) >> +{ >> + return EFI_UNSUPPORTED; >> +} >> diff --git a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf >> index bc3d716aa9..36ee6b9c29 100644 >> --- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf >> +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf >> @@ -2,6 +2,7 @@ >> # MP Initialize Library instance for PEI driver. >> # >> # Copyright (c) 2016 - 2021, Intel Corporation. All rights reserved.
>> +# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.
>> # SPDX-License-Identifier: BSD-2-Clause-Patent >> # >> ## >> @@ -18,7 +19,7 @@ >> # >> # The following information is for reference only and not required by the build tools. >> # >> -# VALID_ARCHITECTURES = IA32 X64 >> +# VALID_ARCHITECTURES = IA32 X64 LOONGARCH64 >> # >> >> [Sources.IA32] >> @@ -29,7 +30,7 @@ >> X64/AmdSev.c >> X64/MpFuncs.nasm >> >> -[Sources.common] >> +[Sources.IA32, Sources.X64] >> AmdSev.c >> MpEqu.inc >> PeiMpLib.c >> @@ -37,23 +38,31 @@ >> MpLib.h >> Microcode.c >> MpHandOff.h >> + >> +[Sources.LoongArch64] >> + LoongArch64/PeiMpLib.c >> + LoongArch64/MpLib.c >> + LoongArch64/MpLib.h >> + >> [Packages] >> MdePkg/MdePkg.dec >> UefiCpuPkg/UefiCpuPkg.dec >> MdeModulePkg/MdeModulePkg.dec >> >> -[LibraryClasses] >> +[LibraryClasses.common] >> BaseLib >> - LocalApicLib >> - MemoryAllocationLib >> - HobLib >> - MtrrLib >> CpuLib >> - SynchronizationLib >> - PeiServicesLib >> + HobLib >> + MemoryAllocationLib >> PcdLib >> + PeiServicesLib >> + SynchronizationLib >> + >> +[LibraryClasses.IA32, LibraryClasses.X64] >> CcExitLib >> + LocalApicLib >> MicrocodeLib >> + MtrrLib >> >> [Pcd] >> gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber ## CONSUMES > -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#114862): https://edk2.groups.io/g/devel/message/114862 Mute This Topic: https://groups.io/mt/104070182/7686176 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=-