--- /dev/null
+/** @file\r
+ Construct MP Services Protocol on top of pthreads. This code makes APs show up \r
+ in the emulator. PcdEmuApCount is the number of APs the emulator should produce.\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) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
+Portitions Copyright (c) 2011, Apple Inc. All rights reserved.\r
+This program and the accompanying materials are licensed and made available under \r
+the terms and conditions of the BSD License that accompanies this distribution. \r
+The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php. \r
+ \r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+ \r
+**/\r
+\r
+#include "CpuDriver.h"\r
+\r
+\r
+MP_SYSTEM_DATA gMPSystem;\r
+EMU_PTREAD_THUNK_PROTOCOL *gPthread = NULL; \r
+EFI_EVENT gReadToBootEvent;\r
+BOOLEAN gReadToBoot = FALSE;\r
+UINTN gPollInterval;\r
+\r
+\r
+BOOLEAN\r
+IsBSP (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN ProcessorNumber;\r
+ \r
+ Status = CpuMpServicesWhoAmI (&mMpSercicesTemplate, &ProcessorNumber);\r
+ if (EFI_ERROR (Status)) {\r
+ return FALSE;\r
+ }\r
+ \r
+ return (gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0;\r
+}\r
+\r
+\r
+VOID\r
+SetApProcedure (\r
+ IN PROCESSOR_DATA_BLOCK *Processor,\r
+ IN EFI_AP_PROCEDURE Procedure,\r
+ IN VOID *ProcedureArgument\r
+ )\r
+{\r
+ gPthread->MutextLock (Processor->ProcedureLock);\r
+ Processor->Parameter = ProcedureArgument;\r
+ Processor->Procedure = Procedure;\r
+ gPthread->MutexUnlock (Processor->ProcedureLock);\r
+}\r
+\r
+\r
+EFI_STATUS\r
+GetNextBlockedNumber (\r
+ OUT UINTN *NextNumber\r
+ )\r
+{\r
+ UINTN Number;\r
+ PROCESSOR_STATE ProcessorState;\r
+ PROCESSOR_DATA_BLOCK *Data;\r
+\r
+ for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {\r
+ Data = &gMPSystem.ProcessorData[Number];\r
+ if ((Data->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {\r
+ // Skip BSP\r
+ continue;\r
+ }\r
+\r
+ gPthread->MutextLock (Data->StateLock);\r
+ ProcessorState = Data->State;\r
+ gPthread->MutexUnlock (Data->StateLock);\r
+\r
+ if (ProcessorState == CPU_STATE_BLOCKED) {\r
+ *NextNumber = Number;\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+\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 EFI_MP_SERVICES_PROTOCOL\r
+ instance.\r
+ @param[out] NumberOfProcessors Pointer to the total number of logical\r
+ processors in the system, including the BSP\r
+ and disabled APs.\r
+ @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical\r
+ processors that exist in system, including\r
+ 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
+EFI_STATUS\r
+EFIAPI\r
+CpuMpServicesGetNumberOfProcessors (\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 (!IsBSP ()) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ \r
+ *NumberOfProcessors = gMPSystem.NumberOfProcessors;\r
+ *NumberOfEnabledProcessors = gMPSystem.NumberOfEnabledProcessors;\r
+ return EFI_SUCCESS;\r
+}\r
+\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 operation,\r
+ slot numbers is all considered platform-related information and is not provided \r
+ by this service.\r
+\r
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL\r
+ instance.\r
+ @param[in] ProcessorNumber The handle number of processor.\r
+ @param[out] ProcessorInfoBuffer A pointer to the buffer where information for\r
+ 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
+EFI_STATUS\r
+EFIAPI\r
+CpuMpServicesGetProcessorInfo (\r
+ IN EFI_MP_SERVICES_PROTOCOL *This,\r
+ IN UINTN ProcessorNumber,\r
+ OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer\r
+ )\r
+{\r
+ if (ProcessorInfoBuffer == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ \r
+ if (!IsBSP ()) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ \r
+ if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ \r
+ CopyMem (ProcessorInfoBuffer, &gMPSystem.ProcessorData[ProcessorNumber], sizeof (EFI_PROCESSOR_INFORMATION));\r
+ return EFI_SUCCESS;\r
+}\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 specified \r
+ by Procedure. If any enabled AP is busy, then EFI_NOT_READY is returned \r
+ 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. Otherwise, \r
+ all the enabled APs execute the function specified by Procedure 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 non-blocking \r
+ mode, and the BSP returns from this service without waiting for APs. If a \r
+ non-blocking mode is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT \r
+ is signaled, then EFI_UNSUPPORTED must be returned.\r
+\r
+ If the timeout specified by TimeoutInMicroseconds expires before all APs return \r
+ from Procedure, then Procedure on the failed APs is terminated. All enabled APs \r
+ are always available for further calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()\r
+ and 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 EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() \r
+ to make sure that the nature of the code that is executed on the BSP and the \r
+ dispatched APs is well controlled. The MP Services Protocol does not guarantee \r
+ that the Procedure function is MP-safe. Hence, the tasks that can be run in \r
+ parallel are limited to certain independent tasks and well-controlled exclusive \r
+ code. EFI services and protocols may not be called by APs unless otherwise \r
+ 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 caller \r
+ invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the parameter WaitEvent \r
+ is not NULL, then StartupAllAPs() executes in non-blocking mode. It requests \r
+ the function specified by Procedure to be started on all the enabled APs, \r
+ 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 MP \r
+ Service signals WaitEvent by calling the EFI SignalEvent() function. If \r
+ 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() service.\r
+ -# This invocation of SignalEvent() function informs the caller that invoked\r
+ EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() that either all the APs completed\r
+ the specified task or a timeout occurred. The contents of FailedCpuList \r
+ can be examined to determine which APs did not complete the specified task \r
+ 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 handle \r
+ number. If FALSE, then all the enabled APs \r
+ execute the function specified by Procedure\r
+ simultaneously.\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] TimeoutInMicrosecsond 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] FailedCpuList If NULL, this parameter is ignored. Otherwise, \r
+ if all APs finish successfully, then its \r
+ content is set to NULL. If not all APs \r
+ finish before timeout expires, then its \r
+ content is set to address of the buffer \r
+ holding handle numbers of the failed APs. \r
+ The buffer is allocated by MP Service Protocol, \r
+ and it's the caller's responsibility to \r
+ free the buffer with FreePool() service.\r
+ In blocking mode, it is ready for consumption \r
+ when the call returns. In non-blocking mode, \r
+ it is ready when WaitEvent is signaled. The \r
+ list of failed CPU is terminated by \r
+ 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 dispatched \r
+ 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
+EFI_STATUS\r
+EFIAPI\r
+CpuMpServicesStartupAllAps (\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
+ PROCESSOR_DATA_BLOCK *ProcessorData;\r
+ UINTN *FailledList;\r
+ UINTN FailedListIndex;\r
+ UINTN ListIndex;\r
+ UINTN Number;\r
+ UINTN NextNumber;\r
+ PROCESSOR_STATE APInitialState;\r
+ PROCESSOR_STATE ProcessorState;\r
+ INTN Timeout;\r
+\r
+\r
+ if (!IsBSP ()) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ \r
+ if (gMPSystem.NumberOfProcessors == 1) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (Procedure == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ \r
+ if ((WaitEvent != NULL) && gReadToBoot) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ \r
+ \r
+ if (FailedCpuList != NULL) {\r
+ FailledList = AllocatePool ((gMPSystem.NumberOfProcessors + 1) * sizeof (UINTN));\r
+ SetMemN (FailledList, (gMPSystem.NumberOfProcessors + 1) * sizeof (UINTN), END_OF_CPU_LIST);\r
+ FailedListIndex = 0;\r
+ *FailedCpuList = FailledList;\r
+ }\r
+\r
+ Timeout = TimeoutInMicroseconds;\r
+\r
+ ListIndex = 0;\r
+ ProcessorData = NULL;\r
+\r
+ gMPSystem.FinishCount = 0;\r
+ gMPSystem.StartCount = 0;\r
+ APInitialState = CPU_STATE_READY;\r
+\r
+ for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {\r
+ ProcessorData = &gMPSystem.ProcessorData[Number];\r
+\r
+ if ((ProcessorData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {\r
+ // Skip BSP\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Get APs prepared, and put failing APs into FailedCpuList\r
+ // if "SingleThread", only 1 AP will put to ready state, other AP will be put to ready\r
+ // state 1 by 1, until the previous 1 finished its task\r
+ // if not "SingleThread", all APs are put to ready state from the beginning\r
+ //\r
+ if (ProcessorData->State == CPU_STATE_IDLE) {\r
+ gMPSystem.StartCount++;\r
+\r
+ gPthread->MutextLock (&ProcessorData->StateLock);\r
+ ProcessorData->State = APInitialState;\r
+ gPthread->MutexUnlock (&ProcessorData->StateLock);\r
+\r
+ if (SingleThread) {\r
+ APInitialState = CPU_STATE_BLOCKED;\r
+ }\r
+\r
+ } else if (FailedCpuList != NULL) {\r
+ FailledList[FailedListIndex++] = Number;\r
+ ListIndex++;\r
+ }\r
+ }\r
+ \r
+ if (FailedCpuList != NULL) {\r
+ if (FailedListIndex == 0) {\r
+ FreePool (*FailedCpuList);\r
+ *FailedCpuList = NULL;\r
+ }\r
+ }\r
+\r
+ while (TRUE) {\r
+ for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {\r
+ ProcessorData = &gMPSystem.ProcessorData[Number]; \r
+ if ((ProcessorData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {\r
+ // Skip BSP\r
+ continue;\r
+ }\r
+\r
+ gPthread->MutextLock (ProcessorData->StateLock);\r
+ ProcessorState = ProcessorData->State;\r
+ gPthread->MutexUnlock (ProcessorData->StateLock);\r
+\r
+ switch (ProcessorState) {\r
+ case CPU_STATE_READY:\r
+ SetApProcedure (ProcessorData, Procedure, ProcedureArgument);\r
+ break;\r
+\r
+ case CPU_STATE_FINISHED:\r
+ gMPSystem.FinishCount++;\r
+ if (SingleThread) {\r
+ Status = GetNextBlockedNumber (&NextNumber);\r
+ if (!EFI_ERROR (Status)) {\r
+ gMPSystem.ProcessorData[NextNumber].State = CPU_STATE_READY;\r
+ }\r
+ }\r
+\r
+ ProcessorData->State = CPU_STATE_IDLE;\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (gMPSystem.FinishCount == gMPSystem.StartCount) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if ((TimeoutInMicroseconds != 0) && (Timeout < 0)) {\r
+ //\r
+ // Save data into private data structure, and create timer to poll AP state before exiting\r
+ //\r
+ gMPSystem.Procedure = Procedure;\r
+ gMPSystem.ProcedureArgument = ProcedureArgument;\r
+ gMPSystem.WaitEvent = WaitEvent;\r
+\r
+ Status = gBS->SetTimer (\r
+ gMPSystem.CheckAllAPsEvent,\r
+ TimerPeriodic,\r
+ gPollInterval\r
+ );\r
+ return EFI_TIMEOUT;\r
+ }\r
+\r
+ gBS->Stall (gPollInterval);\r
+ Timeout -= gPollInterval;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\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] TimeoutInMicrosecsond 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
+EFI_STATUS\r
+EFIAPI\r
+CpuMpServicesStartupThisAP (\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
+ INTN Timeout;\r
+ \r
+ if (!IsBSP ()) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ \r
+ if (Procedure == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ \r
+ if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ \r
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (gMPSystem.ProcessorData[ProcessorNumber].State != CPU_STATE_IDLE) {\r
+ return EFI_NOT_READY;\r
+ }\r
+\r
+ if ((WaitEvent != NULL) && gReadToBoot) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Timeout = TimeoutInMicroseconds;\r
+\r
+ gMPSystem.StartCount = 1;\r
+ gMPSystem.FinishCount = 0;\r
+\r
+ SetApProcedure (&gMPSystem.ProcessorData[ProcessorNumber], Procedure, ProcedureArgument);\r
+\r
+ while (TRUE) {\r
+ gPthread->MutextLock (&gMPSystem.ProcessorData[ProcessorNumber].StateLock);\r
+ if (gMPSystem.ProcessorData[ProcessorNumber].State == CPU_STATE_FINISHED) {\r
+ gMPSystem.ProcessorData[ProcessorNumber].State = CPU_STATE_IDLE;\r
+ gPthread->MutexUnlock (&gMPSystem.ProcessorData[ProcessorNumber].StateLock);\r
+ break;\r
+ }\r
+\r
+ gPthread->MutexUnlock (&gMPSystem.ProcessorData[ProcessorNumber].StateLock);\r
+\r
+ if ((TimeoutInMicroseconds != 0) && (Timeout < 0)) {\r
+ gMPSystem.WaitEvent = WaitEvent;\r
+ Status = gBS->SetTimer (\r
+ gMPSystem.ProcessorData[ProcessorNumber].CheckThisAPEvent,\r
+ TimerPeriodic,\r
+ gPollInterval\r
+ );\r
+ return EFI_TIMEOUT;\r
+ }\r
+\r
+ gBS->Stall (gPollInterval);\r
+ Timeout -= gPollInterval;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+}\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 performed \r
+ 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
+EFI_STATUS\r
+EFIAPI\r
+CpuMpServicesSwitchBSP (\r
+ IN EFI_MP_SERVICES_PROTOCOL *This,\r
+ IN UINTN ProcessorNumber,\r
+ IN BOOLEAN EnableOldBSP\r
+ )\r
+{\r
+ UINTN Index;\r
+ \r
+ if (!IsBSP ()) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ \r
+ if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ \r
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ \r
+ for (Index = 0; Index < gMPSystem.NumberOfProcessors; Index++) {\r
+ if ((gMPSystem.ProcessorData[Index].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {\r
+ break;\r
+ }\r
+ }\r
+ ASSERT (Index != gMPSystem.NumberOfProcessors);\r
+ \r
+ if (gMPSystem.ProcessorData[ProcessorNumber].State != CPU_STATE_IDLE) {\r
+ return EFI_NOT_READY;\r
+ }\r
+ \r
+ // Skip for now as we need switch a bunch of stack stuff around and it's complex\r
+ // May not be worth it?\r
+ return EFI_NOT_READY;\r
+}\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
+EFI_STATUS\r
+EFIAPI\r
+CpuMpServicesEnableDisableAP (\r
+ IN EFI_MP_SERVICES_PROTOCOL *This,\r
+ IN UINTN ProcessorNumber,\r
+ IN BOOLEAN EnableAP,\r
+ IN UINT32 *HealthFlag OPTIONAL\r
+ )\r
+{\r
+ if (!IsBSP ()) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ \r
+ if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ \r
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ } \r
+\r
+ if (gMPSystem.ProcessorData[ProcessorNumber].State != CPU_STATE_IDLE) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ gPthread->MutextLock (&gMPSystem.ProcessorData[ProcessorNumber].StateLock);\r
+ \r
+ if (EnableAP) {\r
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0 ) {\r
+ gMPSystem.NumberOfEnabledProcessors++;\r
+ }\r
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag |= PROCESSOR_ENABLED_BIT;\r
+ } else {\r
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_ENABLED_BIT) == PROCESSOR_ENABLED_BIT ) {\r
+ gMPSystem.NumberOfEnabledProcessors--;\r
+ }\r
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag &= ~PROCESSOR_ENABLED_BIT;\r
+ }\r
+ \r
+ if (HealthFlag != NULL) {\r
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag &= ~PROCESSOR_HEALTH_STATUS_BIT;\r
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag |= (*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT);\r
+ }\r
+ \r
+ gPthread->MutexUnlock (&gMPSystem.ProcessorData[ProcessorNumber].StateLock);\r
+ \r
+ return EFI_SUCCESS;\r
+}\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[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
+\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
+EFI_STATUS\r
+EFIAPI\r
+CpuMpServicesWhoAmI (\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 = gPthread->Self ();\r
+ for (Index = 0; Index < gMPSystem.NumberOfProcessors; Index++) {\r
+ if (gMPSystem.ProcessorData[Index].Info.ProcessorId == ProcessorId) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ *ProcessorNumber = Index;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+\r
+EFI_MP_SERVICES_PROTOCOL mMpSercicesTemplate = {\r
+ CpuMpServicesGetNumberOfProcessors,\r
+ CpuMpServicesGetProcessorInfo,\r
+ CpuMpServicesStartupAllAps,\r
+ CpuMpServicesStartupThisAP,\r
+ CpuMpServicesSwitchBSP,\r
+ CpuMpServicesEnableDisableAP,\r
+ CpuMpServicesWhoAmI\r
+};\r
+\r
+\r
+\r
+/*++\r
+ If timeout occurs in StartupAllAps(), a timer is set, which invokes this\r
+ procedure periodically to check whether all APs have finished.\r
+\r
+\r
+--*/\r
+VOID\r
+EFIAPI\r
+CpuCheckAllAPsStatus (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ UINTN ProcessorNumber;\r
+ UINTN NextNumber;\r
+ PROCESSOR_DATA_BLOCK *ProcessorData;\r
+ PROCESSOR_DATA_BLOCK *NextData;\r
+ EFI_STATUS Status;\r
+ PROCESSOR_STATE ProcessorState;\r
+\r
+ for (ProcessorNumber = 0; ProcessorNumber < gMPSystem.NumberOfProcessors; ProcessorNumber++) {\r
+ if ((ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {\r
+ // Skip BSP\r
+ continue;\r
+ }\r
+\r
+ // This is an Interrupt Service routine.\r
+ // This can grab a lock that is held in a non-interrupt\r
+ // context. Meaning deadlock. Which is a bad thing.\r
+ // So, try lock it. If we can get it, cool, do our thing.\r
+ // otherwise, just dump out & try again on the next iteration.\r
+ Status = gPthread->MutexTryLock (gMPSystem.ProcessorData[ProcessorNumber].StateLock);\r
+ if (EFI_ERROR(Status)) {\r
+ return;\r
+ }\r
+ ProcessorState = gMPSystem.ProcessorData[ProcessorNumber].State;\r
+ gPthread->MutexUnlock (gMPSystem.ProcessorData[ProcessorNumber].StateLock);\r
+\r
+ switch (ProcessorState) {\r
+ case CPU_STATE_READY:\r
+ SetApProcedure (ProcessorData, gMPSystem.Procedure, gMPSystem.ProcedureArgument);\r
+ break;\r
+\r
+ case CPU_STATE_FINISHED:\r
+ if (gMPSystem.SingleThread) {\r
+ Status = GetNextBlockedNumber (&NextNumber);\r
+ if (!EFI_ERROR (Status)) {\r
+ NextData = &gMPSystem.ProcessorData[NextNumber];\r
+\r
+ gPthread->MutextLock (&NextData->ProcedureLock);\r
+ NextData->State = CPU_STATE_READY;\r
+ gPthread->MutexUnlock (&NextData->ProcedureLock);\r
+\r
+ SetApProcedure (NextData, gMPSystem.Procedure, gMPSystem.ProcedureArgument);\r
+ }\r
+ }\r
+\r
+ gMPSystem.ProcessorData[ProcessorNumber].State = CPU_STATE_IDLE;\r
+ gMPSystem.FinishCount++;\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (gMPSystem.FinishCount == gMPSystem.StartCount) {\r
+ gBS->SetTimer (\r
+ gMPSystem.CheckAllAPsEvent,\r
+ TimerCancel,\r
+ 0\r
+ );\r
+ Status = gBS->SignalEvent (gMPSystem.WaitEvent);\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
+VOID\r
+EFIAPI\r
+CpuCheckThisAPStatus (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ PROCESSOR_DATA_BLOCK *ProcessorData;\r
+ PROCESSOR_STATE ProcessorState;\r
+\r
+ ProcessorData = (PROCESSOR_DATA_BLOCK *) Context;\r
+\r
+ //\r
+ // rdar://6260979 - This is an Interrupt Service routine.\r
+ // this can grab a lock that is held in a non-interrupt\r
+ // context. Meaning deadlock. Which is a badddd thing.\r
+ // So, try lock it. If we can get it, cool, do our thing.\r
+ // otherwise, just dump out & try again on the next iteration.\r
+ //\r
+ Status = gPthread->MutexTryLock (ProcessorData->StateLock);\r
+ if (EFI_ERROR(Status)) {\r
+ return;\r
+ }\r
+ ProcessorState = ProcessorData->State;\r
+ gPthread->MutexUnlock (ProcessorData->StateLock);\r
+\r
+ if (ProcessorState == CPU_STATE_FINISHED) {\r
+ Status = gBS->SetTimer (ProcessorData->CheckThisAPEvent, TimerCancel, 0);\r
+ ASSERT_EFI_ERROR (Status);\r
+ \r
+ Status = gBS->SignalEvent (gMPSystem.WaitEvent);\r
+ ASSERT_EFI_ERROR (Status);\r
+ \r
+ gPthread->MutextLock (ProcessorData->StateLock);\r
+ ProcessorData->State = CPU_STATE_IDLE;\r
+ gPthread->MutexUnlock (ProcessorData->StateLock);\r
+ }\r
+\r
+ return ;\r
+}\r
+\r
+\r
+/*++\r
+ This function is called by all processors (both BSP and AP) once and collects MP related data\r
+\r
+ MPSystemData - Pointer to the data structure containing MP related data\r
+ BSP - TRUE if the CPU is BSP\r
+\r
+ EFI_SUCCESS - Data for the processor collected and filled in\r
+\r
+--*/\r
+EFI_STATUS\r
+FillInProcessorInformation (\r
+ IN BOOLEAN BSP,\r
+ IN UINTN ProcessorNumber\r
+ )\r
+{\r
+ PROCESSOR_DATA_BLOCK *ProcessorData;\r
+\r
+ ProcessorData = &gMPSystem.ProcessorData[ProcessorNumber];\r
+ \r
+ gMPSystem.ProcessorData[ProcessorNumber].Info.ProcessorId = gPthread->Self ();\r
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag = PROCESSOR_ENABLED_BIT | PROCESSOR_HEALTH_STATUS_BIT;\r
+ if (BSP) {\r
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag |= PROCESSOR_AS_BSP_BIT;\r
+ }\r
+ \r
+ gMPSystem.ProcessorData[ProcessorNumber].Info.Location.Package = ProcessorNumber;\r
+ gMPSystem.ProcessorData[ProcessorNumber].Info.Location.Core = 0;\r
+ gMPSystem.ProcessorData[ProcessorNumber].Info.Location.Thread = 0;\r
+ gMPSystem.ProcessorData[ProcessorNumber].State = BSP ? CPU_STATE_BUSY : CPU_STATE_IDLE;\r
+ \r
+ gMPSystem.ProcessorData[ProcessorNumber].Procedure = NULL;\r
+ gMPSystem.ProcessorData[ProcessorNumber].Parameter = NULL;\r
+ gMPSystem.ProcessorData[ProcessorNumber].StateLock = gPthread->MutexInit ();\r
+ gMPSystem.ProcessorData[ProcessorNumber].ProcedureLock = gPthread->MutexInit ();\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+VOID *\r
+EFIAPI\r
+CpuDriverApIdolLoop (\r
+ VOID *Context\r
+ )\r
+{\r
+ EFI_AP_PROCEDURE Procedure;\r
+ VOID *Parameter;\r
+ UINTN ProcessorNumber;\r
+ PROCESSOR_DATA_BLOCK *ProcessorData;\r
+ \r
+ ProcessorNumber = (UINTN)Context;\r
+ ProcessorData = &gMPSystem.ProcessorData[ProcessorNumber];\r
+ \r
+ ProcessorData->Info.ProcessorId = gPthread->Self ();\r
+ \r
+ while (TRUE) {\r
+ //\r
+ // Make a local copy on the stack to be extra safe\r
+ //\r
+ gPthread->MutextLock (ProcessorData->ProcedureLock);\r
+ Procedure = ProcessorData->Procedure;\r
+ Parameter = ProcessorData->Parameter;\r
+ gPthread->MutexUnlock (ProcessorData->ProcedureLock);\r
+ \r
+ if (Procedure != NULL) {\r
+ gPthread->MutextLock (ProcessorData->StateLock);\r
+ ProcessorData->State = CPU_STATE_BUSY;\r
+ gPthread->MutexUnlock (ProcessorData->StateLock);\r
+ \r
+ Procedure (Parameter);\r
+ \r
+ gPthread->MutextLock (ProcessorData->ProcedureLock);\r
+ ProcessorData->Procedure = NULL;\r
+ gPthread->MutexUnlock (ProcessorData->ProcedureLock);\r
+ \r
+ gPthread->MutextLock (ProcessorData->StateLock);\r
+ ProcessorData->State = CPU_STATE_FINISHED;\r
+ gPthread->MutexUnlock (ProcessorData->StateLock); \r
+ }\r
+ \r
+ // Poll 5 times a seconds, 200ms\r
+ // Don't want to burn too many system resources doing nothing.\r
+ gEmuThunk->Sleep (200);\r
+ }\r
+ \r
+ return 0;\r
+}\r
+\r
+\r
+EFI_STATUS\r
+InitializeMpSystemData (\r
+ IN UINTN NumberOfProcessors\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Index;\r
+\r
+ \r
+ //\r
+ // Clear the data structure area first.\r
+ //\r
+ ZeroMem (&gMPSystem, sizeof (MP_SYSTEM_DATA));\r
+\r
+ //\r
+ // First BSP fills and inits all known values, including it's own records.\r
+ //\r
+ gMPSystem.NumberOfProcessors = NumberOfProcessors;\r
+ gMPSystem.NumberOfEnabledProcessors = NumberOfProcessors;\r
+ \r
+ gMPSystem.ProcessorData = AllocateZeroPool (gMPSystem.NumberOfProcessors * sizeof (PROCESSOR_DATA_BLOCK));\r
+ ASSERT (gMPSystem.ProcessorData != NULL);\r
+\r
+ FillInProcessorInformation (TRUE, 0);\r
+ \r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ CpuCheckAllAPsStatus,\r
+ NULL,\r
+ &gMPSystem.CheckAllAPsEvent\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ \r
+\r
+ for (Index = 0; Index < gMPSystem.NumberOfProcessors; Index++) {\r
+ if ((gMPSystem.ProcessorData[Index].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {\r
+ // Skip BSP\r
+ continue;\r
+ }\r
+ \r
+ FillInProcessorInformation (FALSE, Index);\r
+ \r
+ Status = gPthread->CreateThread (\r
+ (VOID *)&gMPSystem.ProcessorData[Index].Info.ProcessorId, \r
+ NULL,\r
+ CpuDriverApIdolLoop,\r
+ (VOID *)Index\r
+ );\r
+ \r
+ \r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ CpuCheckThisAPStatus,\r
+ (VOID *) &gMPSystem.ProcessorData[Index],\r
+ &gMPSystem.ProcessorData[Index].CheckThisAPEvent\r
+ );\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+\r
+/**\r
+ Invoke a notification event\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
+VOID\r
+EFIAPI\r
+CpuReadToBootFunction (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ gReadToBoot = TRUE;\r
+}\r
+\r
+\r
+\r
+EFI_STATUS\r
+CpuMpServicesInit (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE Handle;\r
+ EMU_IO_THUNK_PROTOCOL *IoThunk;\r
+ UINTN MaxCpus;\r
+ char *Str;\r
+\r
+ MaxCpus = 1; // BSP\r
+ \r
+ IoThunk = GetIoThunkInstance (&gEmuPthreadThunkProtocolGuid, 0);\r
+ if (IoThunk != NULL) {\r
+ Status = IoThunk->Open (IoThunk);\r
+ if (!EFI_ERROR (Status)) {\r
+ if (IoThunk->ConfigString != NULL) {\r
+ MaxCpus += StrDecimalToUintn (IoThunk->ConfigString);\r
+ gPthread = IoThunk->Interface;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (MaxCpus == 1) {\r
+ // We are not MP so nothing to do\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ gPollInterval = PcdGet64 (PcdEmuMpServicesPollingInterval);\r
+\r
+ Status = InitializeMpSystemData (MaxCpus);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = EfiCreateEventReadyToBootEx (TPL_CALLBACK, CpuReadToBootFunction, NULL, &gReadToBootEvent);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // Now install the MP services protocol.\r
+ //\r
+ Handle = NULL;\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &Handle,\r
+ &gEfiMpServiceProtocolGuid, &mMpSercicesTemplate,\r
+ NULL\r
+ );\r
+ return Status;\r
+}\r
+\r
+\r