--- /dev/null
+/** @file\r
+ Construct MP Services Protocol.\r
+\r
+ The MP Services Protocol provides a generalized way of performing following tasks:\r
+ - Retrieving information of multi-processor environment and MP-related status of\r
+ specific processors.\r
+ - Dispatching user-provided function to APs.\r
+ - Maintain MP-related processor status.\r
+\r
+ The MP Services Protocol must be produced on any system with more than one logical\r
+ processor.\r
+\r
+ The Protocol is available only during boot time.\r
+\r
+ MP Services Protocol is hardware-independent. Most of the logic of this protocol\r
+ is architecturally neutral. It abstracts the multi-processor environment and\r
+ status of processors, and provides interfaces to retrieve information, maintain,\r
+ and dispatch.\r
+\r
+ MP Services Protocol may be consumed by ACPI module. The ACPI module may use this\r
+ protocol to retrieve data that are needed for an MP platform and report them to OS.\r
+ MP Services Protocol may also be used to program and configure processors, such\r
+ as MTRR synchronization for memory space attributes setting in DXE Services.\r
+ MP Services Protocol may be used by non-CPU DXE drivers to speed up platform boot\r
+ by taking advantage of the processing capabilities of the APs, for example, using\r
+ APs to help test system memory in parallel with other device initialization.\r
+ Diagnostics applications may also use this protocol for multi-processor.\r
+\r
+ Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include <PiDxe.h>\r
+\r
+#include <Library/ArmLib.h>\r
+#include <Library/ArmMmuLib.h>\r
+#include <Library/ArmPlatformLib.h>\r
+#include <Library/ArmSmcLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/CacheMaintenanceLib.h>\r
+#include <Library/CpuExceptionHandlerLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/HobLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <IndustryStandard/ArmStdSmc.h>\r
+#include <Ppi/ArmMpCoreInfo.h>\r
+#include <Protocol/LoadedImage.h>\r
+\r
+#include "MpServicesInternal.h"\r
+\r
+#define POLL_INTERVAL_US 50000\r
+\r
+STATIC CPU_MP_DATA mCpuMpData;\r
+STATIC BOOLEAN mNonBlockingModeAllowed;\r
+UINT64 *gApStacksBase;\r
+UINT64 *gProcessorIDs;\r
+CONST UINT64 gApStackSize = AP_STACK_SIZE;\r
+VOID *gTtbr0;\r
+UINTN gTcr;\r
+UINTN gMair;\r
+\r
+STATIC\r
+BOOLEAN\r
+IsCurrentProcessorBSP (\r
+ VOID\r
+ );\r
+\r
+/** Turns on the specified core using PSCI and executes the user-supplied\r
+ function that's been configured via a previous call to SetApProcedure.\r
+\r
+ @param ProcessorIndex The index of the core to turn on.\r
+\r
+ @retval EFI_SUCCESS Success.\r
+ @retval EFI_DEVICE_ERROR The processor could not be turned on.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+DispatchCpu (\r
+ IN UINTN ProcessorIndex\r
+ )\r
+{\r
+ ARM_SMC_ARGS Args;\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ mCpuMpData.CpuData[ProcessorIndex].State = CpuStateBusy;\r
+\r
+ /* Turn the AP on */\r
+ if (sizeof (Args.Arg0) == sizeof (UINT32)) {\r
+ Args.Arg0 = ARM_SMC_ID_PSCI_CPU_ON_AARCH32;\r
+ } else {\r
+ Args.Arg0 = ARM_SMC_ID_PSCI_CPU_ON_AARCH64;\r
+ }\r
+\r
+ Args.Arg1 = gProcessorIDs[ProcessorIndex];\r
+ Args.Arg2 = (UINTN)ApEntryPoint;\r
+\r
+ ArmCallSmc (&Args);\r
+\r
+ if (Args.Arg0 != ARM_SMC_PSCI_RET_SUCCESS) {\r
+ DEBUG ((DEBUG_ERROR, "PSCI_CPU_ON call failed: %d\n", Args.Arg0));\r
+ Status = EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/** Returns whether the specified processor is the BSP.\r
+\r
+ @param[in] ProcessorIndex The index the processor to check.\r
+\r
+ @return TRUE if the processor is the BSP, FALSE otherwise.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+IsProcessorBSP (\r
+ UINTN ProcessorIndex\r
+ )\r
+{\r
+ EFI_PROCESSOR_INFORMATION *CpuInfo;\r
+\r
+ CpuInfo = &mCpuMpData.CpuData[ProcessorIndex].Info;\r
+\r
+ return (CpuInfo->StatusFlag & PROCESSOR_AS_BSP_BIT) != 0;\r
+}\r
+\r
+/** Get the Application Processors state.\r
+\r
+ @param[in] CpuData The pointer to CPU_AP_DATA of specified AP.\r
+\r
+ @return The AP status.\r
+**/\r
+CPU_STATE\r
+GetApState (\r
+ IN CPU_AP_DATA *CpuData\r
+ )\r
+{\r
+ return CpuData->State;\r
+}\r
+\r
+/** Configures the processor context with the user-supplied procedure and\r
+ argument.\r
+\r
+ @param CpuData The processor context.\r
+ @param Procedure The user-supplied procedure.\r
+ @param ProcedureArgument The user-supplied procedure argument.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+SetApProcedure (\r
+ IN CPU_AP_DATA *CpuData,\r
+ IN EFI_AP_PROCEDURE Procedure,\r
+ IN VOID *ProcedureArgument\r
+ )\r
+{\r
+ ASSERT (CpuData != NULL);\r
+ ASSERT (Procedure != NULL);\r
+\r
+ CpuData->Parameter = ProcedureArgument;\r
+ CpuData->Procedure = Procedure;\r
+}\r
+\r
+/** Returns the index of the next processor that is blocked.\r
+\r
+ @param[out] NextNumber The index of the next blocked processor.\r
+\r
+ @retval EFI_SUCCESS Successfully found the next blocked processor.\r
+ @retval EFI_NOT_FOUND There are no blocked processors.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+GetNextBlockedNumber (\r
+ OUT UINTN *NextNumber\r
+ )\r
+{\r
+ UINTN Index;\r
+ CPU_STATE State;\r
+ CPU_AP_DATA *CpuData;\r
+\r
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {\r
+ CpuData = &mCpuMpData.CpuData[Index];\r
+ if (IsProcessorBSP (Index)) {\r
+ // Skip BSP\r
+ continue;\r
+ }\r
+\r
+ State = CpuData->State;\r
+\r
+ if (State == CpuStateBlocked) {\r
+ *NextNumber = Index;\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/** Stalls the BSP for the minimum of POLL_INTERVAL_US and Timeout.\r
+\r
+ @param[in] Timeout The time limit in microseconds remaining for\r
+ APs to return from Procedure.\r
+\r
+ @retval StallTime Time of execution stall.\r
+**/\r
+STATIC\r
+UINTN\r
+CalculateAndStallInterval (\r
+ IN UINTN Timeout\r
+ )\r
+{\r
+ UINTN StallTime;\r
+\r
+ if ((Timeout < POLL_INTERVAL_US) && (Timeout != 0)) {\r
+ StallTime = Timeout;\r
+ } else {\r
+ StallTime = POLL_INTERVAL_US;\r
+ }\r
+\r
+ gBS->Stall (StallTime);\r
+\r
+ return StallTime;\r
+}\r
+\r
+/**\r
+ This service retrieves the number of logical processor in the platform\r
+ and the number of those logical processors that are enabled on this boot.\r
+ This service may only be called from the BSP.\r
+\r
+ This function is used to retrieve the following information:\r
+ - The number of logical processors that are present in the system.\r
+ - The number of enabled logical processors in the system at the instant\r
+ this call is made.\r
+\r
+ Because MP Service Protocol provides services to enable and disable processors\r
+ dynamically, the number of enabled logical processors may vary during the\r
+ course of a boot session.\r
+\r
+ If this service is called from an AP, then EFI_DEVICE_ERROR is returned.\r
+ If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then\r
+ EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors\r
+ is returned in NumberOfProcessors, the number of currently enabled processor\r
+ is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned.\r
+\r
+ @param[in] This A pointer to the\r
+ EFI_MP_SERVICES_PROTOCOL instance.\r
+ @param[out] NumberOfProcessors Pointer to the total number of logical\r
+ processors in the system, including\r
+ the BSP and disabled APs.\r
+ @param[out] NumberOfEnabledProcessors Pointer to the number of enabled\r
+ logical processors that exist in the\r
+ system, including the BSP.\r
+\r
+ @retval EFI_SUCCESS The number of logical processors and enabled\r
+ logical processors was retrieved.\r
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.\r
+ @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL.\r
+ @retval EFI_INVALID_PARAMETER NumberOfEnabledProcessors is NULL.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+GetNumberOfProcessors (\r
+ IN EFI_MP_SERVICES_PROTOCOL *This,\r
+ OUT UINTN *NumberOfProcessors,\r
+ OUT UINTN *NumberOfEnabledProcessors\r
+ )\r
+{\r
+ if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (!IsCurrentProcessorBSP ()) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ *NumberOfProcessors = mCpuMpData.NumberOfProcessors;\r
+ *NumberOfEnabledProcessors = mCpuMpData.NumberOfEnabledProcessors;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Gets detailed MP-related information on the requested processor at the\r
+ instant this call is made. This service may only be called from the BSP.\r
+\r
+ This service retrieves detailed MP-related information about any processor\r
+ on the platform. Note the following:\r
+ - The processor information may change during the course of a boot session.\r
+ - The information presented here is entirely MP related.\r
+\r
+ Information regarding the number of caches and their sizes, frequency of\r
+ operation, slot numbers is all considered platform-related information and is\r
+ not provided by this service.\r
+\r
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL\r
+ instance.\r
+ @param[in] ProcessorIndex The index of the processor.\r
+ @param[out] ProcessorInfoBuffer A pointer to the buffer where information\r
+ for the requested processor is deposited.\r
+\r
+ @retval EFI_SUCCESS Processor information was returned.\r
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.\r
+ @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL.\r
+ @retval EFI_NOT_FOUND The processor with the handle specified by\r
+ ProcessorNumber does not exist in the platform.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+GetProcessorInfo (\r
+ IN EFI_MP_SERVICES_PROTOCOL *This,\r
+ IN UINTN ProcessorIndex,\r
+ OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer\r
+ )\r
+{\r
+ if (ProcessorInfoBuffer == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (!IsCurrentProcessorBSP ()) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ ProcessorIndex &= ~CPU_V2_EXTENDED_TOPOLOGY;\r
+\r
+ if (ProcessorIndex >= mCpuMpData.NumberOfProcessors) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ CopyMem (\r
+ ProcessorInfoBuffer,\r
+ &mCpuMpData.CpuData[ProcessorIndex],\r
+ sizeof (EFI_PROCESSOR_INFORMATION)\r
+ );\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ This service executes a caller provided function on all enabled APs. APs can\r
+ run either simultaneously or one at a time in sequence. This service supports\r
+ both blocking and non-blocking requests. The non-blocking requests use EFI\r
+ events so the BSP can detect when the APs have finished. This service may only\r
+ be called from the BSP.\r
+\r
+ This function is used to dispatch all the enabled APs to the function\r
+ specified by Procedure. If any enabled AP is busy, then EFI_NOT_READY is\r
+ returned immediately and Procedure is not started on any AP.\r
+\r
+ If SingleThread is TRUE, all the enabled APs execute the function specified by\r
+ Procedure one by one, in ascending order of processor handle number.\r
+ Otherwise, all the enabled APs execute the function specified by Procedure\r
+ simultaneously.\r
+\r
+ If WaitEvent is NULL, execution is in blocking mode. The BSP waits until all\r
+ APs finish or TimeoutInMicroseconds expires. Otherwise, execution is in\r
+ non-blocking mode, and the BSP returns from this service without waiting for\r
+ APs. If a non-blocking mode is requested after the UEFI Event\r
+ EFI_EVENT_GROUP_READY_TO_BOOT is signaled, then EFI_UNSUPPORTED must be\r
+ returned.\r
+\r
+ If the timeout specified by TimeoutInMicroseconds expires before all APs\r
+ return from Procedure, then Procedure on the failed APs is terminated.\r
+ All enabled APs are always available for further calls to\r
+ EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and\r
+ EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). If FailedCpuList is not NULL, its\r
+ content points to the list of processor handle numbers in which Procedure was\r
+ terminated.\r
+\r
+ Note: It is the responsibility of the consumer of the\r
+ EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() to make sure that the nature of the\r
+ code that is executed on the BSP and the dispatched APs is well controlled.\r
+ The MP Services Protocol does not guarantee that the Procedure function is\r
+ MP-safe. Hence, the tasks that can be run in parallel are limited to certain\r
+ independent tasks and well-controlled exclusive code. EFI services and\r
+ protocols may not be called by APs unless otherwise specified.\r
+\r
+ In blocking execution mode, BSP waits until all APs finish or\r
+ TimeoutInMicroseconds expires.\r
+\r
+ In non-blocking execution mode, BSP is freed to return to the caller and then\r
+ proceed to the next task without having to wait for APs. The following\r
+ sequence needs to occur in a non-blocking execution mode:\r
+\r
+ -# The caller that intends to use this MP Services Protocol in non-blocking\r
+ mode creates WaitEvent by calling the EFI CreateEvent() service. The\r
+ caller invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the parameter\r
+ WaitEvent is not NULL, then StartupAllAPs() executes in non-blocking\r
+ mode. It requests the function specified by Procedure to be started on\r
+ all the enabled APs, and releases the BSP to continue with other tasks.\r
+ -# The caller can use the CheckEvent() and WaitForEvent() services to check\r
+ the state of the WaitEvent created in step 1.\r
+ -# When the APs complete their task or TimeoutInMicroSecondss expires, the\r
+ MP Service signals WaitEvent by calling the EFI SignalEvent() function.\r
+ If FailedCpuList is not NULL, its content is available when WaitEvent is\r
+ signaled. If all APs returned from Procedure prior to the timeout, then\r
+ FailedCpuList is set to NULL. If not all APs return from Procedure before\r
+ the timeout, then FailedCpuList is filled in with the list of the failed\r
+ APs. The buffer is allocated by MP Service Protocol using AllocatePool().\r
+ It is the caller's responsibility to free the buffer with FreePool()\r
+ service.\r
+ -# This invocation of SignalEvent() function informs the caller that invoked\r
+ EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() that either all the APs\r
+ completed the specified task or a timeout occurred. The contents of\r
+ FailedCpuList can be examined to determine which APs did not complete the\r
+ specified task prior to the timeout.\r
+\r
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL\r
+ instance.\r
+ @param[in] Procedure A pointer to the function to be run on\r
+ enabled APs of the system. See type\r
+ EFI_AP_PROCEDURE.\r
+ @param[in] SingleThread If TRUE, then all the enabled APs execute\r
+ the function specified by Procedure one by\r
+ one, in ascending order of processor\r
+ handle number. If FALSE, then all the\r
+ enabled APs execute the function specified\r
+ by Procedure simultaneously.\r
+ @param[in] WaitEvent The event created by the caller with\r
+ CreateEvent() service. If it is NULL,\r
+ then execute in blocking mode. BSP waits\r
+ until all APs finish or\r
+ TimeoutInMicroseconds expires. If it's\r
+ not NULL, then execute in non-blocking\r
+ mode. BSP requests the function specified\r
+ by Procedure to be started on all the\r
+ enabled APs, and go on executing\r
+ immediately. If all return from Procedure,\r
+ or TimeoutInMicroseconds expires, this\r
+ event is signaled. The BSP can use the\r
+ CheckEvent() or WaitForEvent()\r
+ services to check the state of event. Type\r
+ EFI_EVENT is defined in CreateEvent() in\r
+ the Unified Extensible Firmware Interface\r
+ Specification.\r
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds\r
+ for APs to return from Procedure, either\r
+ for blocking or non-blocking mode. Zero\r
+ means infinity. If the timeout expires\r
+ before all APs return from Procedure, then\r
+ Procedure on the failed APs is terminated.\r
+ All enabled APs are available for next\r
+ function assigned by\r
+ EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()\r
+ or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().\r
+ If the timeout expires in blocking mode,\r
+ BSP returns EFI_TIMEOUT. If the timeout\r
+ expires in non-blocking mode, WaitEvent\r
+ is signaled with SignalEvent().\r
+ @param[in] ProcedureArgument The parameter passed into Procedure for\r
+ all APs.\r
+ @param[out] FailedCpuList If NULL, this parameter is ignored.\r
+ Otherwise, if all APs finish successfully,\r
+ then its content is set to NULL. If not\r
+ all APs finish before timeout expires,\r
+ then its content is set to address of the\r
+ buffer holding handle numbers of the\r
+ failed APs.\r
+ The buffer is allocated by MP Service\r
+ Protocol, and it's the caller's\r
+ responsibility to free the buffer with\r
+ FreePool() service.\r
+ In blocking mode, it is ready for\r
+ consumption when the call returns. In\r
+ non-blocking mode, it is ready when\r
+ WaitEvent is signaled. The list of failed\r
+ CPU is terminated by END_OF_CPU_LIST.\r
+\r
+ @retval EFI_SUCCESS In blocking mode, all APs have finished before\r
+ the timeout expired.\r
+ @retval EFI_SUCCESS In non-blocking mode, function has been\r
+ dispatched to all enabled APs.\r
+ @retval EFI_UNSUPPORTED A non-blocking mode request was made after the\r
+ UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was\r
+ signaled.\r
+ @retval EFI_DEVICE_ERROR Caller processor is AP.\r
+ @retval EFI_NOT_STARTED No enabled APs exist in the system.\r
+ @retval EFI_NOT_READY Any enabled APs are busy.\r
+ @retval EFI_TIMEOUT In blocking mode, the timeout expired before\r
+ all enabled APs have finished.\r
+ @retval EFI_INVALID_PARAMETER Procedure is NULL.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+StartupAllAPs (\r
+ IN EFI_MP_SERVICES_PROTOCOL *This,\r
+ IN EFI_AP_PROCEDURE Procedure,\r
+ IN BOOLEAN SingleThread,\r
+ IN EFI_EVENT WaitEvent OPTIONAL,\r
+ IN UINTN TimeoutInMicroseconds,\r
+ IN VOID *ProcedureArgument OPTIONAL,\r
+ OUT UINTN **FailedCpuList OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ if (!IsCurrentProcessorBSP ()) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if ((mCpuMpData.NumberOfProcessors == 1) || (mCpuMpData.NumberOfEnabledProcessors == 1)) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (Procedure == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ((WaitEvent != NULL) && !mNonBlockingModeAllowed) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ if (FailedCpuList != NULL) {\r
+ mCpuMpData.FailedList = AllocateZeroPool (\r
+ (mCpuMpData.NumberOfProcessors + 1) *\r
+ sizeof (UINTN)\r
+ );\r
+ if (mCpuMpData.FailedList == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ SetMemN (\r
+ mCpuMpData.FailedList,\r
+ (mCpuMpData.NumberOfProcessors + 1) *\r
+ sizeof (UINTN),\r
+ END_OF_CPU_LIST\r
+ );\r
+ mCpuMpData.FailedListIndex = 0;\r
+ *FailedCpuList = mCpuMpData.FailedList;\r
+ }\r
+\r
+ StartupAllAPsPrepareState (SingleThread);\r
+\r
+ // If any enabled APs are busy (ignoring the BSP), return EFI_NOT_READY\r
+ if (mCpuMpData.StartCount != (mCpuMpData.NumberOfEnabledProcessors - 1)) {\r
+ return EFI_NOT_READY;\r
+ }\r
+\r
+ if (WaitEvent != NULL) {\r
+ Status = StartupAllAPsWithWaitEvent (\r
+ Procedure,\r
+ ProcedureArgument,\r
+ WaitEvent,\r
+ TimeoutInMicroseconds,\r
+ SingleThread,\r
+ FailedCpuList\r
+ );\r
+\r
+ if (EFI_ERROR (Status) && (FailedCpuList != NULL)) {\r
+ if (mCpuMpData.FailedListIndex == 0) {\r
+ FreePool (*FailedCpuList);\r
+ *FailedCpuList = NULL;\r
+ }\r
+ }\r
+ } else {\r
+ Status = StartupAllAPsNoWaitEvent (\r
+ Procedure,\r
+ ProcedureArgument,\r
+ TimeoutInMicroseconds,\r
+ SingleThread,\r
+ FailedCpuList\r
+ );\r
+\r
+ if (FailedCpuList != NULL) {\r
+ if (mCpuMpData.FailedListIndex == 0) {\r
+ FreePool (*FailedCpuList);\r
+ *FailedCpuList = NULL;\r
+ }\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ This service lets the caller get one enabled AP to execute a caller-provided\r
+ function. The caller can request the BSP to either wait for the completion\r
+ of the AP or just proceed with the next task by using the EFI event mechanism.\r
+ See EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() for more details on non-blocking\r
+ execution support. This service may only be called from the BSP.\r
+\r
+ This function is used to dispatch one enabled AP to the function specified by\r
+ Procedure passing in the argument specified by ProcedureArgument. If WaitEvent\r
+ is NULL, execution is in blocking mode. The BSP waits until the AP finishes or\r
+ TimeoutInMicroSecondss expires. Otherwise, execution is in non-blocking mode.\r
+ BSP proceeds to the next task without waiting for the AP. If a non-blocking mode\r
+ is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled,\r
+ then EFI_UNSUPPORTED must be returned.\r
+\r
+ If the timeout specified by TimeoutInMicroseconds expires before the AP returns\r
+ from Procedure, then execution of Procedure by the AP is terminated. The AP is\r
+ available for subsequent calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and\r
+ EFI_MP_SERVICES_PROTOCOL.StartupThisAP().\r
+\r
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL\r
+ instance.\r
+ @param[in] Procedure A pointer to the function to be run on\r
+ enabled APs of the system. See type\r
+ EFI_AP_PROCEDURE.\r
+ @param[in] ProcessorNumber The handle number of the AP. The range is\r
+ from 0 to the total number of logical\r
+ processors minus 1. The total number of\r
+ logical processors can be retrieved by\r
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().\r
+ @param[in] WaitEvent The event created by the caller with CreateEvent()\r
+ service. If it is NULL, then execute in\r
+ blocking mode. BSP waits until all APs finish\r
+ or TimeoutInMicroseconds expires. If it's\r
+ not NULL, then execute in non-blocking mode.\r
+ BSP requests the function specified by\r
+ Procedure to be started on all the enabled\r
+ APs, and go on executing immediately. If\r
+ all return from Procedure or TimeoutInMicroseconds\r
+ expires, this event is signaled. The BSP\r
+ can use the CheckEvent() or WaitForEvent()\r
+ services to check the state of event. Type\r
+ EFI_EVENT is defined in CreateEvent() in\r
+ the Unified Extensible Firmware Interface\r
+ Specification.\r
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for\r
+ APs to return from Procedure, either for\r
+ blocking or non-blocking mode. Zero means\r
+ infinity. If the timeout expires before\r
+ all APs return from Procedure, then Procedure\r
+ on the failed APs is terminated. All enabled\r
+ APs are available for next function assigned\r
+ by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()\r
+ or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().\r
+ If the timeout expires in blocking mode,\r
+ BSP returns EFI_TIMEOUT. If the timeout\r
+ expires in non-blocking mode, WaitEvent\r
+ is signaled with SignalEvent().\r
+ @param[in] ProcedureArgument The parameter passed into Procedure for\r
+ all APs.\r
+ @param[out] Finished If NULL, this parameter is ignored. In\r
+ blocking mode, this parameter is ignored.\r
+ In non-blocking mode, if AP returns from\r
+ Procedure before the timeout expires, its\r
+ content is set to TRUE. Otherwise, the\r
+ value is set to FALSE. The caller can\r
+ determine if the AP returned from Procedure\r
+ by evaluating this value.\r
+\r
+ @retval EFI_SUCCESS In blocking mode, specified AP finished before\r
+ the timeout expires.\r
+ @retval EFI_SUCCESS In non-blocking mode, the function has been\r
+ dispatched to specified AP.\r
+ @retval EFI_UNSUPPORTED A non-blocking mode request was made after the\r
+ UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was\r
+ signaled.\r
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.\r
+ @retval EFI_TIMEOUT In blocking mode, the timeout expired before\r
+ the specified AP has finished.\r
+ @retval EFI_NOT_READY The specified AP is busy.\r
+ @retval EFI_NOT_FOUND The processor with the handle specified by\r
+ ProcessorNumber does not exist.\r
+ @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP.\r
+ @retval EFI_INVALID_PARAMETER Procedure is NULL.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+StartupThisAP (\r
+ IN EFI_MP_SERVICES_PROTOCOL *This,\r
+ IN EFI_AP_PROCEDURE Procedure,\r
+ IN UINTN ProcessorNumber,\r
+ IN EFI_EVENT WaitEvent OPTIONAL,\r
+ IN UINTN TimeoutInMicroseconds,\r
+ IN VOID *ProcedureArgument OPTIONAL,\r
+ OUT BOOLEAN *Finished OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Timeout;\r
+ CPU_AP_DATA *CpuData;\r
+\r
+ if (!IsCurrentProcessorBSP ()) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if (Procedure == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (ProcessorNumber >= mCpuMpData.NumberOfProcessors) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ CpuData = &mCpuMpData.CpuData[ProcessorNumber];\r
+\r
+ if (IsProcessorBSP (ProcessorNumber)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (!IsProcessorEnabled (ProcessorNumber)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ((GetApState (CpuData) != CpuStateIdle) &&\r
+ (GetApState (CpuData) != CpuStateFinished))\r
+ {\r
+ return EFI_NOT_READY;\r
+ }\r
+\r
+ if ((WaitEvent != NULL) && !mNonBlockingModeAllowed) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Timeout = TimeoutInMicroseconds;\r
+\r
+ CpuData->Timeout = TimeoutInMicroseconds;\r
+ CpuData->TimeTaken = 0;\r
+ CpuData->TimeoutActive = (BOOLEAN)(TimeoutInMicroseconds != 0);\r
+\r
+ SetApProcedure (\r
+ CpuData,\r
+ Procedure,\r
+ ProcedureArgument\r
+ );\r
+\r
+ Status = DispatchCpu (ProcessorNumber);\r
+ if (EFI_ERROR (Status)) {\r
+ CpuData->State = CpuStateIdle;\r
+ return EFI_NOT_READY;\r
+ }\r
+\r
+ if (WaitEvent != NULL) {\r
+ // Non Blocking\r
+ if (Finished != NULL) {\r
+ CpuData->SingleApFinished = Finished;\r
+ *Finished = FALSE;\r
+ }\r
+\r
+ CpuData->WaitEvent = WaitEvent;\r
+ Status = gBS->SetTimer (\r
+ CpuData->CheckThisAPEvent,\r
+ TimerPeriodic,\r
+ POLL_INTERVAL_US\r
+ );\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ // Blocking\r
+ while (TRUE) {\r
+ if (GetApState (CpuData) == CpuStateFinished) {\r
+ CpuData->State = CpuStateIdle;\r
+ break;\r
+ }\r
+\r
+ if ((TimeoutInMicroseconds != 0) && (Timeout == 0)) {\r
+ return EFI_TIMEOUT;\r
+ }\r
+\r
+ Timeout -= CalculateAndStallInterval (Timeout);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ This service switches the requested AP to be the BSP from that point onward.\r
+ This service changes the BSP for all purposes. This call can only be\r
+ performed by the current BSP.\r
+\r
+ This service switches the requested AP to be the BSP from that point onward.\r
+ This service changes the BSP for all purposes. The new BSP can take over the\r
+ execution of the old BSP and continue seamlessly from where the old one left\r
+ off. This service may not be supported after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT\r
+ is signaled.\r
+\r
+ If the BSP cannot be switched prior to the return from this service, then\r
+ EFI_UNSUPPORTED must be returned.\r
+\r
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.\r
+ @param[in] ProcessorNumber The handle number of AP that is to become the new\r
+ BSP. The range is from 0 to the total number of\r
+ logical processors minus 1. The total number of\r
+ logical processors can be retrieved by\r
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().\r
+ @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an\r
+ enabled AP. Otherwise, it will be disabled.\r
+\r
+ @retval EFI_SUCCESS BSP successfully switched.\r
+ @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to\r
+ this service returning.\r
+ @retval EFI_UNSUPPORTED Switching the BSP is not supported.\r
+ @retval EFI_SUCCESS The calling processor is an AP.\r
+ @retval EFI_NOT_FOUND The processor with the handle specified by\r
+ ProcessorNumber does not exist.\r
+ @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or\r
+ a disabled AP.\r
+ @retval EFI_NOT_READY The specified AP is busy.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+SwitchBSP (\r
+ IN EFI_MP_SERVICES_PROTOCOL *This,\r
+ IN UINTN ProcessorNumber,\r
+ IN BOOLEAN EnableOldBSP\r
+ )\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+ This service lets the caller enable or disable an AP from this point onward.\r
+ This service may only be called from the BSP.\r
+\r
+ This service allows the caller enable or disable an AP from this point onward.\r
+ The caller can optionally specify the health status of the AP by Health. If\r
+ an AP is being disabled, then the state of the disabled AP is implementation\r
+ dependent. If an AP is enabled, then the implementation must guarantee that a\r
+ complete initialization sequence is performed on the AP, so the AP is in a state\r
+ that is compatible with an MP operating system. This service may not be supported\r
+ after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled.\r
+\r
+ If the enable or disable AP operation cannot be completed prior to the return\r
+ from this service, then EFI_UNSUPPORTED must be returned.\r
+\r
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.\r
+ @param[in] ProcessorNumber The handle number of AP that is to become the new\r
+ BSP. The range is from 0 to the total number of\r
+ logical processors minus 1. The total number of\r
+ logical processors can be retrieved by\r
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().\r
+ @param[in] EnableAP Specifies the new state for the processor for\r
+ enabled, FALSE for disabled.\r
+ @param[in] HealthFlag If not NULL, a pointer to a value that specifies\r
+ the new health status of the AP. This flag\r
+ corresponds to StatusFlag defined in\r
+ EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only\r
+ the PROCESSOR_HEALTH_STATUS_BIT is used. All other\r
+ bits are ignored. If it is NULL, this parameter\r
+ is ignored.\r
+\r
+ @retval EFI_SUCCESS The specified AP was enabled or disabled successfully.\r
+ @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed\r
+ prior to this service returning.\r
+ @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported.\r
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.\r
+ @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber\r
+ does not exist.\r
+ @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+EnableDisableAP (\r
+ IN EFI_MP_SERVICES_PROTOCOL *This,\r
+ IN UINTN ProcessorNumber,\r
+ IN BOOLEAN EnableAP,\r
+ IN UINT32 *HealthFlag OPTIONAL\r
+ )\r
+{\r
+ UINTN StatusFlag;\r
+ CPU_AP_DATA *CpuData;\r
+\r
+ StatusFlag = mCpuMpData.CpuData[ProcessorNumber].Info.StatusFlag;\r
+ CpuData = &mCpuMpData.CpuData[ProcessorNumber];\r
+\r
+ if (!IsCurrentProcessorBSP ()) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if (ProcessorNumber >= mCpuMpData.NumberOfProcessors) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ if (IsProcessorBSP (ProcessorNumber)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (GetApState (CpuData) != CpuStateIdle) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ if (EnableAP) {\r
+ if (!IsProcessorEnabled (ProcessorNumber)) {\r
+ mCpuMpData.NumberOfEnabledProcessors++;\r
+ }\r
+\r
+ StatusFlag |= PROCESSOR_ENABLED_BIT;\r
+ } else {\r
+ if (IsProcessorEnabled (ProcessorNumber) && !IsProcessorBSP (ProcessorNumber)) {\r
+ mCpuMpData.NumberOfEnabledProcessors--;\r
+ }\r
+\r
+ StatusFlag &= ~PROCESSOR_ENABLED_BIT;\r
+ }\r
+\r
+ if ((HealthFlag != NULL) && !IsProcessorBSP (ProcessorNumber)) {\r
+ StatusFlag &= ~PROCESSOR_HEALTH_STATUS_BIT;\r
+ StatusFlag |= (*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT);\r
+ }\r
+\r
+ mCpuMpData.CpuData[ProcessorNumber].Info.StatusFlag = StatusFlag;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ This return the handle number for the calling processor. This service may be\r
+ called from the BSP and APs.\r
+\r
+ This service returns the processor handle number for the calling processor.\r
+ The returned value is in the range from 0 to the total number of logical\r
+ processors minus 1. The total number of logical processors can be retrieved\r
+ with EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). This service may be\r
+ called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER\r
+ is returned. Otherwise, the current processors handle number is returned in\r
+ ProcessorNumber, and EFI_SUCCESS is returned.\r
+\r
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.\r
+ @param[out] ProcessorNumber The handle number of AP that is to become the new\r
+ BSP. The range is from 0 to the total number of\r
+ logical processors minus 1. The total number of\r
+ logical processors can be retrieved by\r
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().\r
+\r
+ @retval EFI_SUCCESS The current processor handle number was returned\r
+ in ProcessorNumber.\r
+ @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+WhoAmI (\r
+ IN EFI_MP_SERVICES_PROTOCOL *This,\r
+ OUT UINTN *ProcessorNumber\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINT64 ProcessorId;\r
+\r
+ if (ProcessorNumber == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ProcessorId = GET_MPIDR_AFFINITY_BITS (ArmReadMpidr ());\r
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {\r
+ if (ProcessorId == gProcessorIDs[Index]) {\r
+ *ProcessorNumber = Index;\r
+ break;\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+STATIC EFI_MP_SERVICES_PROTOCOL mMpServicesProtocol = {\r
+ GetNumberOfProcessors,\r
+ GetProcessorInfo,\r
+ StartupAllAPs,\r
+ StartupThisAP,\r
+ SwitchBSP,\r
+ EnableDisableAP,\r
+ WhoAmI\r
+};\r
+\r
+/** Adds the specified processor the list of failed processors.\r
+\r
+ @param ProcessorIndex The processor index to add.\r
+ @param ApState Processor state.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+AddProcessorToFailedList (\r
+ UINTN ProcessorIndex,\r
+ CPU_STATE ApState\r
+ )\r
+{\r
+ UINTN Index;\r
+ BOOLEAN Found;\r
+\r
+ Found = FALSE;\r
+\r
+ if ((mCpuMpData.FailedList == NULL) ||\r
+ (ApState == CpuStateIdle) ||\r
+ (ApState == CpuStateFinished) ||\r
+ IsProcessorBSP (ProcessorIndex))\r
+ {\r
+ return;\r
+ }\r
+\r
+ // If we are retrying make sure we don't double count\r
+ for (Index = 0; Index < mCpuMpData.FailedListIndex; Index++) {\r
+ if (mCpuMpData.FailedList[Index] == ProcessorIndex) {\r
+ Found = TRUE;\r
+ break;\r
+ }\r
+ }\r
+\r
+ /* If the CPU isn't already in the FailedList, add it */\r
+ if (!Found) {\r
+ mCpuMpData.FailedList[mCpuMpData.FailedListIndex++] = ProcessorIndex;\r
+ }\r
+}\r
+\r
+/** Handles the StartupAllAPs case where the timeout has occurred.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+ProcessStartupAllAPsTimeout (\r
+ VOID\r
+ )\r
+{\r
+ CPU_AP_DATA *CpuData;\r
+ UINTN Index;\r
+\r
+ if (mCpuMpData.FailedList == NULL) {\r
+ return;\r
+ }\r
+\r
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {\r
+ CpuData = &mCpuMpData.CpuData[Index];\r
+ if (IsProcessorBSP (Index)) {\r
+ // Skip BSP\r
+ continue;\r
+ }\r
+\r
+ if (!IsProcessorEnabled (Index)) {\r
+ // Skip Disabled processors\r
+ continue;\r
+ }\r
+\r
+ CpuData = &mCpuMpData.CpuData[Index];\r
+ AddProcessorToFailedList (Index, GetApState (CpuData));\r
+ }\r
+}\r
+\r
+/** Updates the status of the APs.\r
+\r
+ @param[in] ProcessorIndex The index of the AP to update.\r
+**/\r
+STATIC\r
+VOID\r
+UpdateApStatus (\r
+ IN UINTN ProcessorIndex\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ CPU_AP_DATA *CpuData;\r
+ CPU_AP_DATA *NextCpuData;\r
+ CPU_STATE State;\r
+ UINTN NextNumber;\r
+\r
+ CpuData = &mCpuMpData.CpuData[ProcessorIndex];\r
+\r
+ if (IsProcessorBSP (ProcessorIndex)) {\r
+ // Skip BSP\r
+ return;\r
+ }\r
+\r
+ if (!IsProcessorEnabled (ProcessorIndex)) {\r
+ // Skip Disabled processors\r
+ return;\r
+ }\r
+\r
+ State = GetApState (CpuData);\r
+\r
+ switch (State) {\r
+ case CpuStateFinished:\r
+ if (mCpuMpData.SingleThread) {\r
+ Status = GetNextBlockedNumber (&NextNumber);\r
+ if (!EFI_ERROR (Status)) {\r
+ NextCpuData = &mCpuMpData.CpuData[NextNumber];\r
+\r
+ NextCpuData->State = CpuStateReady;\r
+\r
+ SetApProcedure (\r
+ NextCpuData,\r
+ mCpuMpData.Procedure,\r
+ mCpuMpData.ProcedureArgument\r
+ );\r
+\r
+ Status = DispatchCpu (NextNumber);\r
+ if (!EFI_ERROR (Status)) {\r
+ mCpuMpData.StartCount++;\r
+ } else {\r
+ AddProcessorToFailedList (NextNumber, NextCpuData->State);\r
+ }\r
+ }\r
+ }\r
+\r
+ CpuData->State = CpuStateIdle;\r
+ mCpuMpData.FinishCount++;\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+}\r
+\r
+/**\r
+ If a timeout is specified in StartupAllAps(), a timer is set, which invokes\r
+ this procedure periodically to check whether all APs have finished.\r
+\r
+ @param[in] Event The WaitEvent the user supplied.\r
+ @param[in] Context The event context.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+CheckAllAPsStatus (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Index;\r
+\r
+ mCpuMpData.AllTimeTaken += POLL_INTERVAL_US;\r
+\r
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {\r
+ UpdateApStatus (Index);\r
+ }\r
+\r
+ if (mCpuMpData.AllTimeoutActive && (mCpuMpData.AllTimeTaken > mCpuMpData.AllTimeout)) {\r
+ ProcessStartupAllAPsTimeout ();\r
+\r
+ // Force terminal exit\r
+ mCpuMpData.FinishCount = mCpuMpData.StartCount;\r
+ }\r
+\r
+ if (mCpuMpData.FinishCount != mCpuMpData.StartCount) {\r
+ return;\r
+ }\r
+\r
+ gBS->SetTimer (\r
+ mCpuMpData.CheckAllAPsEvent,\r
+ TimerCancel,\r
+ 0\r
+ );\r
+\r
+ if (mCpuMpData.FailedListIndex == 0) {\r
+ if (mCpuMpData.FailedList != NULL) {\r
+ // Since we don't have the original `FailedCpuList`\r
+ // pointer here to set to NULL, don't free the\r
+ // memory.\r
+ }\r
+ }\r
+\r
+ Status = gBS->SignalEvent (mCpuMpData.AllWaitEvent);\r
+ ASSERT_EFI_ERROR (Status);\r
+ mCpuMpData.AllWaitEvent = NULL;\r
+}\r
+\r
+/** Invoked periodically via a timer to check the state of the processor.\r
+\r
+ @param Event The event supplied by the timer expiration.\r
+ @param Context The processor context.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+CheckThisAPStatus (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ CPU_AP_DATA *CpuData;\r
+ CPU_STATE State;\r
+\r
+ CpuData = Context;\r
+\r
+ CpuData->TimeTaken += POLL_INTERVAL_US;\r
+\r
+ State = GetApState (CpuData);\r
+\r
+ if (State == CpuStateFinished) {\r
+ Status = gBS->SetTimer (CpuData->CheckThisAPEvent, TimerCancel, 0);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ if (CpuData->SingleApFinished != NULL) {\r
+ *(CpuData->SingleApFinished) = TRUE;\r
+ }\r
+\r
+ if (CpuData->WaitEvent != NULL) {\r
+ Status = gBS->SignalEvent (CpuData->WaitEvent);\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+\r
+ CpuData->State = CpuStateIdle;\r
+ }\r
+\r
+ if (CpuData->TimeoutActive && (CpuData->TimeTaken > CpuData->Timeout)) {\r
+ Status = gBS->SetTimer (CpuData->CheckThisAPEvent, TimerCancel, 0);\r
+ if (CpuData->WaitEvent != NULL) {\r
+ Status = gBS->SignalEvent (CpuData->WaitEvent);\r
+ ASSERT_EFI_ERROR (Status);\r
+ CpuData->WaitEvent = NULL;\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ This function is called by all processors (both BSP and AP) once and collects\r
+ MP related data.\r
+\r
+ @param BSP TRUE if the processor is the BSP.\r
+ @param Mpidr The MPIDR for the specified processor. This should be\r
+ the full MPIDR and not only the affinity bits.\r
+ @param ProcessorIndex The index of the processor.\r
+\r
+ @return EFI_SUCCESS if the data for the processor collected and filled in.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+FillInProcessorInformation (\r
+ IN BOOLEAN BSP,\r
+ IN UINTN Mpidr,\r
+ IN UINTN ProcessorIndex\r
+ )\r
+{\r
+ EFI_PROCESSOR_INFORMATION *CpuInfo;\r
+\r
+ CpuInfo = &mCpuMpData.CpuData[ProcessorIndex].Info;\r
+\r
+ CpuInfo->ProcessorId = GET_MPIDR_AFFINITY_BITS (Mpidr);\r
+ CpuInfo->StatusFlag = PROCESSOR_ENABLED_BIT | PROCESSOR_HEALTH_STATUS_BIT;\r
+\r
+ if (BSP) {\r
+ CpuInfo->StatusFlag |= PROCESSOR_AS_BSP_BIT;\r
+ }\r
+\r
+ if ((Mpidr & MPIDR_MT_BIT) > 0) {\r
+ CpuInfo->Location.Package = GET_MPIDR_AFF2 (Mpidr);\r
+ CpuInfo->Location.Core = GET_MPIDR_AFF1 (Mpidr);\r
+ CpuInfo->Location.Thread = GET_MPIDR_AFF0 (Mpidr);\r
+\r
+ CpuInfo->ExtendedInformation.Location2.Package = GET_MPIDR_AFF3 (Mpidr);\r
+ CpuInfo->ExtendedInformation.Location2.Die = GET_MPIDR_AFF2 (Mpidr);\r
+ CpuInfo->ExtendedInformation.Location2.Core = GET_MPIDR_AFF1 (Mpidr);\r
+ CpuInfo->ExtendedInformation.Location2.Thread = GET_MPIDR_AFF0 (Mpidr);\r
+ } else {\r
+ CpuInfo->Location.Package = GET_MPIDR_AFF1 (Mpidr);\r
+ CpuInfo->Location.Core = GET_MPIDR_AFF0 (Mpidr);\r
+ CpuInfo->Location.Thread = 0;\r
+\r
+ CpuInfo->ExtendedInformation.Location2.Package = GET_MPIDR_AFF2 (Mpidr);\r
+ CpuInfo->ExtendedInformation.Location2.Die = GET_MPIDR_AFF1 (Mpidr);\r
+ CpuInfo->ExtendedInformation.Location2.Core = GET_MPIDR_AFF0 (Mpidr);\r
+ CpuInfo->ExtendedInformation.Location2.Thread = 0;\r
+ }\r
+\r
+ mCpuMpData.CpuData[ProcessorIndex].State = BSP ? CpuStateBusy : CpuStateIdle;\r
+\r
+ mCpuMpData.CpuData[ProcessorIndex].Procedure = NULL;\r
+ mCpuMpData.CpuData[ProcessorIndex].Parameter = NULL;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Initializes the MP Services system data\r
+\r
+ @param NumberOfProcessors The number of processors, both BSP and AP.\r
+ @param CoreInfo CPU information gathered earlier during boot.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+MpServicesInitialize (\r
+ IN UINTN NumberOfProcessors,\r
+ IN CONST ARM_CORE_INFO *CoreInfo\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Index;\r
+ EFI_EVENT ReadyToBootEvent;\r
+ BOOLEAN IsBsp;\r
+\r
+ //\r
+ // Clear the data structure area first.\r
+ //\r
+ ZeroMem (&mCpuMpData, sizeof (CPU_MP_DATA));\r
+ //\r
+ // First BSP fills and inits all known values, including its own records.\r
+ //\r
+ mCpuMpData.NumberOfProcessors = NumberOfProcessors;\r
+ mCpuMpData.NumberOfEnabledProcessors = NumberOfProcessors;\r
+\r
+ mCpuMpData.CpuData = AllocateZeroPool (\r
+ mCpuMpData.NumberOfProcessors * sizeof (CPU_AP_DATA)\r
+ );\r
+\r
+ if (mCpuMpData.CpuData == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ /* Allocate one extra for the sentinel entry at the end */\r
+ gProcessorIDs = AllocateZeroPool ((mCpuMpData.NumberOfProcessors + 1) * sizeof (UINT64));\r
+ ASSERT (gProcessorIDs != NULL);\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ CheckAllAPsStatus,\r
+ NULL,\r
+ &mCpuMpData.CheckAllAPsEvent\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ gApStacksBase = AllocatePages (\r
+ EFI_SIZE_TO_PAGES (\r
+ mCpuMpData.NumberOfProcessors *\r
+ gApStackSize\r
+ )\r
+ );\r
+ ASSERT (gApStacksBase != NULL);\r
+\r
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {\r
+ if (GET_MPIDR_AFFINITY_BITS (ArmReadMpidr ()) == CoreInfo[Index].Mpidr) {\r
+ IsBsp = TRUE;\r
+ } else {\r
+ IsBsp = FALSE;\r
+ }\r
+\r
+ FillInProcessorInformation (IsBsp, CoreInfo[Index].Mpidr, Index);\r
+\r
+ gProcessorIDs[Index] = mCpuMpData.CpuData[Index].Info.ProcessorId;\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ CheckThisAPStatus,\r
+ (VOID *)&mCpuMpData.CpuData[Index],\r
+ &mCpuMpData.CpuData[Index].CheckThisAPEvent\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+\r
+ gProcessorIDs[Index] = MAX_UINT64;\r
+\r
+ gTcr = ArmGetTCR ();\r
+ gMair = ArmGetMAIR ();\r
+ gTtbr0 = ArmGetTTBR0BaseAddress ();\r
+\r
+ //\r
+ // The global pointer variables as well as the gProcessorIDs array contents\r
+ // are accessed by the other cores so we must clean them to the PoC\r
+ //\r
+ WriteBackDataCacheRange (&gProcessorIDs, sizeof (UINT64 *));\r
+ WriteBackDataCacheRange (&gApStacksBase, sizeof (UINT64 *));\r
+\r
+ WriteBackDataCacheRange (\r
+ gProcessorIDs,\r
+ (mCpuMpData.NumberOfProcessors + 1) * sizeof (UINT64)\r
+ );\r
+\r
+ mNonBlockingModeAllowed = TRUE;\r
+\r
+ Status = EfiCreateEventReadyToBootEx (\r
+ TPL_CALLBACK,\r
+ ReadyToBootSignaled,\r
+ NULL,\r
+ &ReadyToBootEvent\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Event notification function called when the EFI_EVENT_GROUP_READY_TO_BOOT is\r
+ signaled. After this point, non-blocking mode is no longer allowed.\r
+\r
+ @param Event Event whose notification function is being invoked.\r
+ @param Context The pointer to the notification function's context,\r
+ which is implementation-dependent.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+ReadyToBootSignaled (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ mNonBlockingModeAllowed = FALSE;\r
+}\r
+\r
+/** Initialize multi-processor support.\r
+\r
+ @param ImageHandle Image handle.\r
+ @param SystemTable System table.\r
+\r
+ @return EFI_SUCCESS on success, or an error code.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ArmPsciMpServicesDxeInitialize (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE Handle;\r
+ UINTN MaxCpus;\r
+ EFI_LOADED_IMAGE_PROTOCOL *Image;\r
+ EFI_HOB_GENERIC_HEADER *Hob;\r
+ VOID *HobData;\r
+ UINTN HobDataSize;\r
+ CONST ARM_CORE_INFO *CoreInfo;\r
+\r
+ MaxCpus = 1;\r
+\r
+ Status = gBS->HandleProtocol (\r
+ ImageHandle,\r
+ &gEfiLoadedImageProtocolGuid,\r
+ (VOID **)&Image\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // Parts of the code in this driver may be executed by other cores running\r
+ // with the MMU off so we need to ensure that everything is clean to the\r
+ // point of coherency (PoC)\r
+ //\r
+ WriteBackDataCacheRange (Image->ImageBase, Image->ImageSize);\r
+\r
+ Hob = GetFirstGuidHob (&gArmMpCoreInfoGuid);\r
+ if (Hob != NULL) {\r
+ HobData = GET_GUID_HOB_DATA (Hob);\r
+ HobDataSize = GET_GUID_HOB_DATA_SIZE (Hob);\r
+ CoreInfo = (ARM_CORE_INFO *)HobData;\r
+ MaxCpus = HobDataSize / sizeof (ARM_CORE_INFO);\r
+ }\r
+\r
+ if (MaxCpus == 1) {\r
+ DEBUG ((DEBUG_WARN, "Trying to use EFI_MP_SERVICES_PROTOCOL on a UP system"));\r
+ // We are not MP so nothing to do\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ Status = MpServicesInitialize (MaxCpus, CoreInfo);\r
+ if (Status != EFI_SUCCESS) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Now install the MP services protocol.\r
+ //\r
+ Handle = NULL;\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &Handle,\r
+ &gEfiMpServiceProtocolGuid,\r
+ &mMpServicesProtocol,\r
+ NULL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return Status;\r
+}\r
+\r
+/** AP exception handler.\r
+\r
+ @param InterruptType The AArch64 CPU exception type.\r
+ @param SystemContext System context.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+ApExceptionHandler (\r
+ IN CONST EFI_EXCEPTION_TYPE InterruptType,\r
+ IN CONST EFI_SYSTEM_CONTEXT SystemContext\r
+ )\r
+{\r
+ ARM_SMC_ARGS Args;\r
+ UINT64 Mpidr;\r
+ UINTN Index;\r
+ UINTN ProcessorIndex;\r
+\r
+ Mpidr = GET_MPIDR_AFFINITY_BITS (ArmReadMpidr ());\r
+\r
+ Index = 0;\r
+ ProcessorIndex = MAX_UINT64;\r
+\r
+ do {\r
+ if (gProcessorIDs[Index] == Mpidr) {\r
+ ProcessorIndex = Index;\r
+ break;\r
+ }\r
+\r
+ Index++;\r
+ } while (gProcessorIDs[Index] != MAX_UINT64);\r
+\r
+ if (ProcessorIndex != MAX_UINT64) {\r
+ mCpuMpData.CpuData[ProcessorIndex].State = CpuStateFinished;\r
+ ArmDataMemoryBarrier ();\r
+ }\r
+\r
+ Args.Arg0 = ARM_SMC_ID_PSCI_CPU_OFF;\r
+ ArmCallSmc (&Args);\r
+\r
+ /* Should never be reached */\r
+ ASSERT (FALSE);\r
+ CpuDeadLoop ();\r
+}\r
+\r
+/** C entry-point for the AP.\r
+ This function gets called from the assembly function ApEntryPoint.\r
+\r
+**/\r
+VOID\r
+ApProcedure (\r
+ VOID\r
+ )\r
+{\r
+ ARM_SMC_ARGS Args;\r
+ EFI_AP_PROCEDURE UserApProcedure;\r
+ VOID *UserApParameter;\r
+ UINTN ProcessorIndex;\r
+\r
+ ProcessorIndex = 0;\r
+\r
+ WhoAmI (&mMpServicesProtocol, &ProcessorIndex);\r
+\r
+ /* Fetch the user-supplied procedure and parameter to execute */\r
+ UserApProcedure = mCpuMpData.CpuData[ProcessorIndex].Procedure;\r
+ UserApParameter = mCpuMpData.CpuData[ProcessorIndex].Parameter;\r
+\r
+ InitializeCpuExceptionHandlers (NULL);\r
+ RegisterCpuInterruptHandler (EXCEPT_AARCH64_SYNCHRONOUS_EXCEPTIONS, ApExceptionHandler);\r
+ RegisterCpuInterruptHandler (EXCEPT_AARCH64_IRQ, ApExceptionHandler);\r
+ RegisterCpuInterruptHandler (EXCEPT_AARCH64_FIQ, ApExceptionHandler);\r
+ RegisterCpuInterruptHandler (EXCEPT_AARCH64_SERROR, ApExceptionHandler);\r
+\r
+ UserApProcedure (UserApParameter);\r
+\r
+ mCpuMpData.CpuData[ProcessorIndex].State = CpuStateFinished;\r
+\r
+ ArmDataMemoryBarrier ();\r
+\r
+ /* Since we're finished with this AP, turn it off */\r
+ Args.Arg0 = ARM_SMC_ID_PSCI_CPU_OFF;\r
+ ArmCallSmc (&Args);\r
+\r
+ /* Should never be reached */\r
+ ASSERT (FALSE);\r
+ CpuDeadLoop ();\r
+}\r
+\r
+/** Returns whether the processor executing this function is the BSP.\r
+\r
+ @return Whether the current processor is the BSP.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+IsCurrentProcessorBSP (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN ProcessorIndex;\r
+\r
+ Status = WhoAmI (&mMpServicesProtocol, &ProcessorIndex);\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ return FALSE;\r
+ }\r
+\r
+ return IsProcessorBSP (ProcessorIndex);\r
+}\r
+\r
+/** Returns whether the specified processor is enabled.\r
+\r
+ @param[in] ProcessorIndex The index of the processor to check.\r
+\r
+ @return TRUE if the processor is enabled, FALSE otherwise.\r
+**/\r
+STATIC\r
+BOOLEAN\r
+IsProcessorEnabled (\r
+ UINTN ProcessorIndex\r
+ )\r
+{\r
+ EFI_PROCESSOR_INFORMATION *CpuInfo;\r
+\r
+ CpuInfo = &mCpuMpData.CpuData[ProcessorIndex].Info;\r
+\r
+ return (CpuInfo->StatusFlag & PROCESSOR_ENABLED_BIT) != 0;\r
+}\r
+\r
+/** Sets up the state for the StartupAllAPs function.\r
+\r
+ @param SingleThread Whether the APs will execute sequentially.\r
+\r
+**/\r
+STATIC\r
+VOID\r
+StartupAllAPsPrepareState (\r
+ IN BOOLEAN SingleThread\r
+ )\r
+{\r
+ UINTN Index;\r
+ CPU_STATE APInitialState;\r
+ CPU_AP_DATA *CpuData;\r
+\r
+ mCpuMpData.FinishCount = 0;\r
+ mCpuMpData.StartCount = 0;\r
+ mCpuMpData.SingleThread = SingleThread;\r
+\r
+ APInitialState = CpuStateReady;\r
+\r
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {\r
+ CpuData = &mCpuMpData.CpuData[Index];\r
+\r
+ //\r
+ // Get APs prepared, and put failing APs into FailedCpuList.\r
+ // If "SingleThread", only 1 AP will put into ready state, other AP will be\r
+ // put into ready state 1 by 1, until the previous 1 finished its task.\r
+ // If not "SingleThread", all APs are put into ready state from the\r
+ // beginning\r
+ //\r
+\r
+ if (IsProcessorBSP (Index)) {\r
+ // Skip BSP\r
+ continue;\r
+ }\r
+\r
+ if (!IsProcessorEnabled (Index)) {\r
+ // Skip Disabled processors\r
+ if (mCpuMpData.FailedList != NULL) {\r
+ mCpuMpData.FailedList[mCpuMpData.FailedListIndex++] = Index;\r
+ }\r
+\r
+ continue;\r
+ }\r
+\r
+ // If any APs finished after timing out, reset state to Idle\r
+ if (GetApState (CpuData) == CpuStateFinished) {\r
+ CpuData->State = CpuStateIdle;\r
+ }\r
+\r
+ if (GetApState (CpuData) != CpuStateIdle) {\r
+ // Skip busy processors\r
+ if (mCpuMpData.FailedList != NULL) {\r
+ mCpuMpData.FailedList[mCpuMpData.FailedListIndex++] = Index;\r
+ }\r
+ }\r
+\r
+ CpuData->State = APInitialState;\r
+\r
+ mCpuMpData.StartCount++;\r
+ if (SingleThread) {\r
+ APInitialState = CpuStateBlocked;\r
+ }\r
+ }\r
+}\r
+\r
+/** Handles execution of StartupAllAPs when a WaitEvent has been specified.\r
+\r
+ @param Procedure The user-supplied procedure.\r
+ @param ProcedureArgument The user-supplied procedure argument.\r
+ @param WaitEvent The wait event to be signaled when the work is\r
+ complete or a timeout has occurred.\r
+ @param TimeoutInMicroseconds The timeout for the work to be completed. Zero\r
+ indicates an infinite timeout.\r
+ @param SingleThread Whether the APs will execute sequentially.\r
+ @param FailedCpuList User-supplied pointer for list of failed CPUs.\r
+\r
+ @return EFI_SUCCESS on success.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+StartupAllAPsWithWaitEvent (\r
+ IN EFI_AP_PROCEDURE Procedure,\r
+ IN VOID *ProcedureArgument,\r
+ IN EFI_EVENT WaitEvent,\r
+ IN UINTN TimeoutInMicroseconds,\r
+ IN BOOLEAN SingleThread,\r
+ IN UINTN **FailedCpuList\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Index;\r
+ CPU_AP_DATA *CpuData;\r
+\r
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {\r
+ CpuData = &mCpuMpData.CpuData[Index];\r
+ if (IsProcessorBSP (Index)) {\r
+ // Skip BSP\r
+ continue;\r
+ }\r
+\r
+ if (!IsProcessorEnabled (Index)) {\r
+ // Skip Disabled processors\r
+ continue;\r
+ }\r
+\r
+ if (GetApState (CpuData) == CpuStateReady) {\r
+ SetApProcedure (CpuData, Procedure, ProcedureArgument);\r
+ if ((mCpuMpData.StartCount == 0) || !SingleThread) {\r
+ Status = DispatchCpu (Index);\r
+ if (EFI_ERROR (Status)) {\r
+ AddProcessorToFailedList (Index, CpuData->State);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_NOT_READY;\r
+ }\r
+\r
+ //\r
+ // Save data into private data structure, and create timer to poll AP state\r
+ // before exiting\r
+ //\r
+ mCpuMpData.Procedure = Procedure;\r
+ mCpuMpData.ProcedureArgument = ProcedureArgument;\r
+ mCpuMpData.AllWaitEvent = WaitEvent;\r
+ mCpuMpData.AllTimeout = TimeoutInMicroseconds;\r
+ mCpuMpData.AllTimeTaken = 0;\r
+ mCpuMpData.AllTimeoutActive = (BOOLEAN)(TimeoutInMicroseconds != 0);\r
+ Status = gBS->SetTimer (\r
+ mCpuMpData.CheckAllAPsEvent,\r
+ TimerPeriodic,\r
+ POLL_INTERVAL_US\r
+ );\r
+\r
+ return Status;\r
+}\r
+\r
+/** Handles execution of StartupAllAPs when no wait event has been specified.\r
+\r
+ @param Procedure The user-supplied procedure.\r
+ @param ProcedureArgument The user-supplied procedure argument.\r
+ @param TimeoutInMicroseconds The timeout for the work to be completed. Zero\r
+ indicates an infinite timeout.\r
+ @param SingleThread Whether the APs will execute sequentially.\r
+ @param FailedCpuList User-supplied pointer for list of failed CPUs.\r
+\r
+ @return EFI_SUCCESS on success.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+StartupAllAPsNoWaitEvent (\r
+ IN EFI_AP_PROCEDURE Procedure,\r
+ IN VOID *ProcedureArgument,\r
+ IN UINTN TimeoutInMicroseconds,\r
+ IN BOOLEAN SingleThread,\r
+ IN UINTN **FailedCpuList\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Index;\r
+ UINTN NextIndex;\r
+ UINTN Timeout;\r
+ CPU_AP_DATA *CpuData;\r
+ BOOLEAN DispatchError;\r
+\r
+ Timeout = TimeoutInMicroseconds;\r
+ DispatchError = FALSE;\r
+\r
+ while (TRUE) {\r
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {\r
+ CpuData = &mCpuMpData.CpuData[Index];\r
+ if (IsProcessorBSP (Index)) {\r
+ // Skip BSP\r
+ continue;\r
+ }\r
+\r
+ if (!IsProcessorEnabled (Index)) {\r
+ // Skip Disabled processors\r
+ continue;\r
+ }\r
+\r
+ switch (GetApState (CpuData)) {\r
+ case CpuStateReady:\r
+ SetApProcedure (CpuData, Procedure, ProcedureArgument);\r
+ Status = DispatchCpu (Index);\r
+ if (EFI_ERROR (Status)) {\r
+ AddProcessorToFailedList (Index, CpuData->State);\r
+ CpuData->State = CpuStateIdle;\r
+ mCpuMpData.StartCount--;\r
+ DispatchError = TRUE;\r
+\r
+ if (SingleThread) {\r
+ // Dispatch the next available AP\r
+ Status = GetNextBlockedNumber (&NextIndex);\r
+ if (!EFI_ERROR (Status)) {\r
+ mCpuMpData.CpuData[NextIndex].State = CpuStateReady;\r
+ }\r
+ }\r
+ }\r
+\r
+ break;\r
+\r
+ case CpuStateFinished:\r
+ mCpuMpData.FinishCount++;\r
+ if (SingleThread) {\r
+ Status = GetNextBlockedNumber (&NextIndex);\r
+ if (!EFI_ERROR (Status)) {\r
+ mCpuMpData.CpuData[NextIndex].State = CpuStateReady;\r
+ }\r
+ }\r
+\r
+ CpuData->State = CpuStateIdle;\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (mCpuMpData.FinishCount == mCpuMpData.StartCount) {\r
+ Status = EFI_SUCCESS;\r
+ break;\r
+ }\r
+\r
+ if ((TimeoutInMicroseconds != 0) && (Timeout == 0)) {\r
+ Status = EFI_TIMEOUT;\r
+ break;\r
+ }\r
+\r
+ Timeout -= CalculateAndStallInterval (Timeout);\r
+ }\r
+\r
+ if (Status == EFI_TIMEOUT) {\r
+ // Add any remaining CPUs to the FailedCpuList\r
+ if (FailedCpuList != NULL) {\r
+ for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {\r
+ AddProcessorToFailedList (Index, mCpuMpData.CpuData[Index].State);\r
+ }\r
+ }\r
+ }\r
+\r
+ if (DispatchError) {\r
+ Status = EFI_NOT_READY;\r
+ }\r
+\r
+ return Status;\r
+}\r