summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandrewfish <andrewfish@6f19259b-4bc3-4df7-8a09-765794883524>2011-05-13 00:03:26 +0000
committerandrewfish <andrewfish@6f19259b-4bc3-4df7-8a09-765794883524>2011-05-13 00:03:26 +0000
commitc4671a67d86ad8b6453fa36471cfeaf6b73cbcea (patch)
treec01f9ef31e53b8573adb9b9599f4aeb0aa498c1f
parente6a6082acfb0984ef56a05e56f8b3d7ca068cbae (diff)
downloadedk2-platforms-c4671a67d86ad8b6453fa36471cfeaf6b73cbcea.tar.xz
Add MP support. Based on PcdEmuApCount APs (Application Processors) are created in the CpuRuntimeDxe driver. If PcdEmuApCount > 0 then the MpServices protocol is created on top of pthreads and the APs are availible to use vis the MpService protocol.
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11644 6f19259b-4bc3-4df7-8a09-765794883524
-rw-r--r--InOsEmuPkg/CpuRuntimeDxe/Cpu.c2
-rw-r--r--InOsEmuPkg/CpuRuntimeDxe/Cpu.inf9
-rw-r--r--InOsEmuPkg/CpuRuntimeDxe/CpuDriver.h70
-rw-r--r--InOsEmuPkg/CpuRuntimeDxe/MpService.c1257
-rw-r--r--InOsEmuPkg/InOsEmuPkg.dec4
-rw-r--r--InOsEmuPkg/Include/Library/EmuThunkLib.h20
-rw-r--r--InOsEmuPkg/Library/DxeEmuLib/DxeEmuLib.c42
-rw-r--r--InOsEmuPkg/Library/DxeEmuLib/DxeEmuLib.inf1
8 files changed, 1398 insertions, 7 deletions
diff --git a/InOsEmuPkg/CpuRuntimeDxe/Cpu.c b/InOsEmuPkg/CpuRuntimeDxe/Cpu.c
index 2b1e1d1cda..bcb8b51046 100644
--- a/InOsEmuPkg/CpuRuntimeDxe/Cpu.c
+++ b/InOsEmuPkg/CpuRuntimeDxe/Cpu.c
@@ -326,6 +326,8 @@ InitializeCpu (
mTimerPeriod = DivU64x64Remainder (1000000000000000, Frequency, NULL);
CpuUpdateSmbios ();
+
+ CpuMpServicesInit ();
Status = gBS->InstallMultipleProtocolInterfaces (
&mCpuTemplate.Handle,
diff --git a/InOsEmuPkg/CpuRuntimeDxe/Cpu.inf b/InOsEmuPkg/CpuRuntimeDxe/Cpu.inf
index 44e651f2c8..baaf927852 100644
--- a/InOsEmuPkg/CpuRuntimeDxe/Cpu.inf
+++ b/InOsEmuPkg/CpuRuntimeDxe/Cpu.inf
@@ -34,7 +34,8 @@
Cpu.c
CpuDriver.h
Strings.uni
-
+ MpService.c
+
[Packages]
MdePkg/MdePkg.dec
@@ -53,6 +54,7 @@
DebugLib
BaseLib
EmuThunkLib
+ PcdLib
[Protocols]
gEmuIoThunkProtocolGuid # PROTOCOL_NOTIFY SOMETIMES_CONSUMED
@@ -60,6 +62,11 @@
gEfiHiiProtocolGuid # PROTOCOL SOMETIMES_CONSUMED
gEfiCpuIo2ProtocolGuid # PROTOCOL ALWAYS_PRODUCED
gEfiCpuArchProtocolGuid # PROTOCOL ALWAYS_PRODUCED
+ gEmuPthreadThunkProtocolGuid
+ gEfiMpServiceProtocolGuid
+
+[Pcd]
+ gInOsEmuPkgTokenSpaceGuid.PcdEmuMpServicesPollingInterval
[Depex]
gEfiSmbiosProtocolGuid
diff --git a/InOsEmuPkg/CpuRuntimeDxe/CpuDriver.h b/InOsEmuPkg/CpuRuntimeDxe/CpuDriver.h
index a836f631d1..2faf65be8f 100644
--- a/InOsEmuPkg/CpuRuntimeDxe/CpuDriver.h
+++ b/InOsEmuPkg/CpuRuntimeDxe/CpuDriver.h
@@ -18,11 +18,16 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include <FrameworkDxe.h>
#include <IndustryStandard/SmBios.h>
+
#include <Protocol/Cpu.h>
#include <Protocol/Smbios.h>
#include <Protocol/FrameworkHii.h>
-#include <Guid/DataHubRecords.h>
+#include <Protocol/MpService.h>
+#include <Protocol/EmuPthreadThunk.h>
#include <Protocol/CpuIo2.h>
+
+#include <Guid/DataHubRecords.h>
+
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/HiiLib.h>
@@ -31,6 +36,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/EmuThunkLib.h>
+#include <Library/UefiLib.h>
+#include <Library/PcdLib.h>
extern UINT8 CpuStrings[];
@@ -61,6 +68,52 @@ typedef struct {
CPU_ARCH_PROT_PRIVATE_SIGNATURE \
)
+
+
+typedef enum {
+ CPU_STATE_IDLE,
+ CPU_STATE_BLOCKED,
+ CPU_STATE_READY,
+ CPU_STATE_BUSY,
+ CPU_STATE_FINISHED
+} PROCESSOR_STATE;
+
+
+//
+// Define Individual Processor Data block.
+//
+typedef struct {
+ EFI_PROCESSOR_INFORMATION Info;
+ EFI_AP_PROCEDURE Procedure;
+ VOID *Parameter;
+ VOID *StateLock;
+ VOID *ProcedureLock;
+ PROCESSOR_STATE State;
+ EFI_EVENT CheckThisAPEvent;
+} PROCESSOR_DATA_BLOCK;
+
+
+//
+// Define MP data block which consumes individual processor block.
+//
+typedef struct {
+ UINTN NumberOfProcessors;
+ UINTN NumberOfEnabledProcessors;
+ EFI_EVENT CheckAllAPsEvent;
+ EFI_EVENT WaitEvent;
+ UINTN FinishCount;
+ UINTN StartCount;
+ EFI_AP_PROCEDURE Procedure;
+ VOID *ProcedureArgument;
+ BOOLEAN SingleThread;
+ UINTN StartedNumber;
+ PROCESSOR_DATA_BLOCK *ProcessorData;
+} MP_SYSTEM_DATA;
+
+
+
+
+
EFI_STATUS
EFIAPI
CpuMemoryServiceRead (
@@ -169,4 +222,19 @@ EmuSetMemoryAttributes (
IN UINT64 Attributes
);
+EFI_STATUS
+CpuMpServicesInit (
+ VOID
+ );
+
+EFI_STATUS
+EFIAPI
+CpuMpServicesWhoAmI (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ OUT UINTN *ProcessorNumber
+ );
+
+extern EFI_MP_SERVICES_PROTOCOL mMpSercicesTemplate;
+
+
#endif
diff --git a/InOsEmuPkg/CpuRuntimeDxe/MpService.c b/InOsEmuPkg/CpuRuntimeDxe/MpService.c
new file mode 100644
index 0000000000..8d7666b7b4
--- /dev/null
+++ b/InOsEmuPkg/CpuRuntimeDxe/MpService.c
@@ -0,0 +1,1257 @@
+/** @file
+ Construct MP Services Protocol on top of pthreads. This code makes APs show up
+ in the emulator. PcdEmuApCount is the number of APs the emulator should produce.
+
+ The MP Services Protocol provides a generalized way of performing following tasks:
+ - Retrieving information of multi-processor environment and MP-related status of
+ specific processors.
+ - Dispatching user-provided function to APs.
+ - Maintain MP-related processor status.
+
+ The MP Services Protocol must be produced on any system with more than one logical
+ processor.
+
+ The Protocol is available only during boot time.
+
+ MP Services Protocol is hardware-independent. Most of the logic of this protocol
+ is architecturally neutral. It abstracts the multi-processor environment and
+ status of processors, and provides interfaces to retrieve information, maintain,
+ and dispatch.
+
+ MP Services Protocol may be consumed by ACPI module. The ACPI module may use this
+ protocol to retrieve data that are needed for an MP platform and report them to OS.
+ MP Services Protocol may also be used to program and configure processors, such
+ as MTRR synchronization for memory space attributes setting in DXE Services.
+ MP Services Protocol may be used by non-CPU DXE drivers to speed up platform boot
+ by taking advantage of the processing capabilities of the APs, for example, using
+ APs to help test system memory in parallel with other device initialization.
+ Diagnostics applications may also use this protocol for multi-processor.
+
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+Portitions Copyright (c) 2011, Apple Inc. All rights reserved.
+This program and the accompanying materials are licensed and made available under
+the terms and conditions of the BSD License that accompanies this distribution.
+The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php.
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+**/
+
+#include "CpuDriver.h"
+
+
+MP_SYSTEM_DATA gMPSystem;
+EMU_PTREAD_THUNK_PROTOCOL *gPthread = NULL;
+EFI_EVENT gReadToBootEvent;
+BOOLEAN gReadToBoot = FALSE;
+UINTN gPollInterval;
+
+
+BOOLEAN
+IsBSP (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINTN ProcessorNumber;
+
+ Status = CpuMpServicesWhoAmI (&mMpSercicesTemplate, &ProcessorNumber);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ return (gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0;
+}
+
+
+VOID
+SetApProcedure (
+ IN PROCESSOR_DATA_BLOCK *Processor,
+ IN EFI_AP_PROCEDURE Procedure,
+ IN VOID *ProcedureArgument
+ )
+{
+ gPthread->MutextLock (Processor->ProcedureLock);
+ Processor->Parameter = ProcedureArgument;
+ Processor->Procedure = Procedure;
+ gPthread->MutexUnlock (Processor->ProcedureLock);
+}
+
+
+EFI_STATUS
+GetNextBlockedNumber (
+ OUT UINTN *NextNumber
+ )
+{
+ UINTN Number;
+ PROCESSOR_STATE ProcessorState;
+ PROCESSOR_DATA_BLOCK *Data;
+
+ for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {
+ Data = &gMPSystem.ProcessorData[Number];
+ if ((Data->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {
+ // Skip BSP
+ continue;
+ }
+
+ gPthread->MutextLock (Data->StateLock);
+ ProcessorState = Data->State;
+ gPthread->MutexUnlock (Data->StateLock);
+
+ if (ProcessorState == CPU_STATE_BLOCKED) {
+ *NextNumber = Number;
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+
+
+/**
+ This service retrieves the number of logical processor in the platform
+ and the number of those logical processors that are enabled on this boot.
+ This service may only be called from the BSP.
+
+ This function is used to retrieve the following information:
+ - The number of logical processors that are present in the system.
+ - The number of enabled logical processors in the system at the instant
+ this call is made.
+
+ Because MP Service Protocol provides services to enable and disable processors
+ dynamically, the number of enabled logical processors may vary during the
+ course of a boot session.
+
+ If this service is called from an AP, then EFI_DEVICE_ERROR is returned.
+ If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then
+ EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors
+ is returned in NumberOfProcessors, the number of currently enabled processor
+ is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ instance.
+ @param[out] NumberOfProcessors Pointer to the total number of logical
+ processors in the system, including the BSP
+ and disabled APs.
+ @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical
+ processors that exist in system, including
+ the BSP.
+
+ @retval EFI_SUCCESS The number of logical processors and enabled
+ logical processors was retrieved.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL.
+ @retval EFI_INVALID_PARAMETER NumberOfEnabledProcessors is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesGetNumberOfProcessors (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ OUT UINTN *NumberOfProcessors,
+ OUT UINTN *NumberOfEnabledProcessors
+ )
+{
+ if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IsBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ *NumberOfProcessors = gMPSystem.NumberOfProcessors;
+ *NumberOfEnabledProcessors = gMPSystem.NumberOfEnabledProcessors;
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Gets detailed MP-related information on the requested processor at the
+ instant this call is made. This service may only be called from the BSP.
+
+ This service retrieves detailed MP-related information about any processor
+ on the platform. Note the following:
+ - The processor information may change during the course of a boot session.
+ - The information presented here is entirely MP related.
+
+ Information regarding the number of caches and their sizes, frequency of operation,
+ slot numbers is all considered platform-related information and is not provided
+ by this service.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ instance.
+ @param[in] ProcessorNumber The handle number of processor.
+ @param[out] ProcessorInfoBuffer A pointer to the buffer where information for
+ the requested processor is deposited.
+
+ @retval EFI_SUCCESS Processor information was returned.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL.
+ @retval EFI_NOT_FOUND The processor with the handle specified by
+ ProcessorNumber does not exist in the platform.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesGetProcessorInfo (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN UINTN ProcessorNumber,
+ OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer
+ )
+{
+ if (ProcessorInfoBuffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IsBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {
+ return EFI_NOT_FOUND;
+ }
+
+ CopyMem (ProcessorInfoBuffer, &gMPSystem.ProcessorData[ProcessorNumber], sizeof (EFI_PROCESSOR_INFORMATION));
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This service executes a caller provided function on all enabled APs. APs can
+ run either simultaneously or one at a time in sequence. This service supports
+ both blocking and non-blocking requests. The non-blocking requests use EFI
+ events so the BSP can detect when the APs have finished. This service may only
+ be called from the BSP.
+
+ This function is used to dispatch all the enabled APs to the function specified
+ by Procedure. If any enabled AP is busy, then EFI_NOT_READY is returned
+ immediately and Procedure is not started on any AP.
+
+ If SingleThread is TRUE, all the enabled APs execute the function specified by
+ Procedure one by one, in ascending order of processor handle number. Otherwise,
+ all the enabled APs execute the function specified by Procedure simultaneously.
+
+ If WaitEvent is NULL, execution is in blocking mode. The BSP waits until all
+ APs finish or TimeoutInMicroseconds expires. Otherwise, execution is in non-blocking
+ mode, and the BSP returns from this service without waiting for APs. If a
+ non-blocking mode is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
+ is signaled, then EFI_UNSUPPORTED must be returned.
+
+ If the timeout specified by TimeoutInMicroseconds expires before all APs return
+ from Procedure, then Procedure on the failed APs is terminated. All enabled APs
+ are always available for further calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+ and EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). If FailedCpuList is not NULL, its
+ content points to the list of processor handle numbers in which Procedure was
+ terminated.
+
+ Note: It is the responsibility of the consumer of the EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+ to make sure that the nature of the code that is executed on the BSP and the
+ dispatched APs is well controlled. The MP Services Protocol does not guarantee
+ that the Procedure function is MP-safe. Hence, the tasks that can be run in
+ parallel are limited to certain independent tasks and well-controlled exclusive
+ code. EFI services and protocols may not be called by APs unless otherwise
+ specified.
+
+ In blocking execution mode, BSP waits until all APs finish or
+ TimeoutInMicroseconds expires.
+
+ In non-blocking execution mode, BSP is freed to return to the caller and then
+ proceed to the next task without having to wait for APs. The following
+ sequence needs to occur in a non-blocking execution mode:
+
+ -# The caller that intends to use this MP Services Protocol in non-blocking
+ mode creates WaitEvent by calling the EFI CreateEvent() service. The caller
+ invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the parameter WaitEvent
+ is not NULL, then StartupAllAPs() executes in non-blocking mode. It requests
+ the function specified by Procedure to be started on all the enabled APs,
+ and releases the BSP to continue with other tasks.
+ -# The caller can use the CheckEvent() and WaitForEvent() services to check
+ the state of the WaitEvent created in step 1.
+ -# When the APs complete their task or TimeoutInMicroSecondss expires, the MP
+ Service signals WaitEvent by calling the EFI SignalEvent() function. If
+ FailedCpuList is not NULL, its content is available when WaitEvent is
+ signaled. If all APs returned from Procedure prior to the timeout, then
+ FailedCpuList is set to NULL. If not all APs return from Procedure before
+ the timeout, then FailedCpuList is filled in with the list of the failed
+ APs. The buffer is allocated by MP Service Protocol using AllocatePool().
+ It is the caller's responsibility to free the buffer with FreePool() service.
+ -# This invocation of SignalEvent() function informs the caller that invoked
+ EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() that either all the APs completed
+ the specified task or a timeout occurred. The contents of FailedCpuList
+ can be examined to determine which APs did not complete the specified task
+ prior to the timeout.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ instance.
+ @param[in] Procedure A pointer to the function to be run on
+ enabled APs of the system. See type
+ EFI_AP_PROCEDURE.
+ @param[in] SingleThread If TRUE, then all the enabled APs execute
+ the function specified by Procedure one by
+ one, in ascending order of processor handle
+ number. If FALSE, then all the enabled APs
+ execute the function specified by Procedure
+ simultaneously.
+ @param[in] WaitEvent The event created by the caller with CreateEvent()
+ service. If it is NULL, then execute in
+ blocking mode. BSP waits until all APs finish
+ or TimeoutInMicroseconds expires. If it's
+ not NULL, then execute in non-blocking mode.
+ BSP requests the function specified by
+ Procedure to be started on all the enabled
+ APs, and go on executing immediately. If
+ all return from Procedure, or TimeoutInMicroseconds
+ expires, this event is signaled. The BSP
+ can use the CheckEvent() or WaitForEvent()
+ services to check the state of event. Type
+ EFI_EVENT is defined in CreateEvent() in
+ the Unified Extensible Firmware Interface
+ Specification.
+ @param[in] TimeoutInMicrosecsond Indicates the time limit in microseconds for
+ APs to return from Procedure, either for
+ blocking or non-blocking mode. Zero means
+ infinity. If the timeout expires before
+ all APs return from Procedure, then Procedure
+ on the failed APs is terminated. All enabled
+ APs are available for next function assigned
+ by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+ or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
+ If the timeout expires in blocking mode,
+ BSP returns EFI_TIMEOUT. If the timeout
+ expires in non-blocking mode, WaitEvent
+ is signaled with SignalEvent().
+ @param[in] ProcedureArgument The parameter passed into Procedure for
+ all APs.
+ @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise,
+ if all APs finish successfully, then its
+ content is set to NULL. If not all APs
+ finish before timeout expires, then its
+ content is set to address of the buffer
+ holding handle numbers of the failed APs.
+ The buffer is allocated by MP Service Protocol,
+ and it's the caller's responsibility to
+ free the buffer with FreePool() service.
+ In blocking mode, it is ready for consumption
+ when the call returns. In non-blocking mode,
+ it is ready when WaitEvent is signaled. The
+ list of failed CPU is terminated by
+ END_OF_CPU_LIST.
+
+ @retval EFI_SUCCESS In blocking mode, all APs have finished before
+ the timeout expired.
+ @retval EFI_SUCCESS In non-blocking mode, function has been dispatched
+ to all enabled APs.
+ @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
+ UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
+ signaled.
+ @retval EFI_DEVICE_ERROR Caller processor is AP.
+ @retval EFI_NOT_STARTED No enabled APs exist in the system.
+ @retval EFI_NOT_READY Any enabled APs are busy.
+ @retval EFI_TIMEOUT In blocking mode, the timeout expired before
+ all enabled APs have finished.
+ @retval EFI_INVALID_PARAMETER Procedure is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesStartupAllAps (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN EFI_AP_PROCEDURE Procedure,
+ IN BOOLEAN SingleThread,
+ IN EFI_EVENT WaitEvent OPTIONAL,
+ IN UINTN TimeoutInMicroseconds,
+ IN VOID *ProcedureArgument OPTIONAL,
+ OUT UINTN **FailedCpuList OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ PROCESSOR_DATA_BLOCK *ProcessorData;
+ UINTN *FailledList;
+ UINTN FailedListIndex;
+ UINTN ListIndex;
+ UINTN Number;
+ UINTN NextNumber;
+ PROCESSOR_STATE APInitialState;
+ PROCESSOR_STATE ProcessorState;
+ INTN Timeout;
+
+
+ if (!IsBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (gMPSystem.NumberOfProcessors == 1) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Procedure == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((WaitEvent != NULL) && gReadToBoot) {
+ return EFI_UNSUPPORTED;
+ }
+
+
+ if (FailedCpuList != NULL) {
+ FailledList = AllocatePool ((gMPSystem.NumberOfProcessors + 1) * sizeof (UINTN));
+ SetMemN (FailledList, (gMPSystem.NumberOfProcessors + 1) * sizeof (UINTN), END_OF_CPU_LIST);
+ FailedListIndex = 0;
+ *FailedCpuList = FailledList;
+ }
+
+ Timeout = TimeoutInMicroseconds;
+
+ ListIndex = 0;
+ ProcessorData = NULL;
+
+ gMPSystem.FinishCount = 0;
+ gMPSystem.StartCount = 0;
+ APInitialState = CPU_STATE_READY;
+
+ for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {
+ ProcessorData = &gMPSystem.ProcessorData[Number];
+
+ if ((ProcessorData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {
+ // Skip BSP
+ continue;
+ }
+
+ //
+ // Get APs prepared, and put failing APs into FailedCpuList
+ // if "SingleThread", only 1 AP will put to ready state, other AP will be put to ready
+ // state 1 by 1, until the previous 1 finished its task
+ // if not "SingleThread", all APs are put to ready state from the beginning
+ //
+ if (ProcessorData->State == CPU_STATE_IDLE) {
+ gMPSystem.StartCount++;
+
+ gPthread->MutextLock (&ProcessorData->StateLock);
+ ProcessorData->State = APInitialState;
+ gPthread->MutexUnlock (&ProcessorData->StateLock);
+
+ if (SingleThread) {
+ APInitialState = CPU_STATE_BLOCKED;
+ }
+
+ } else if (FailedCpuList != NULL) {
+ FailledList[FailedListIndex++] = Number;
+ ListIndex++;
+ }
+ }
+
+ if (FailedCpuList != NULL) {
+ if (FailedListIndex == 0) {
+ FreePool (*FailedCpuList);
+ *FailedCpuList = NULL;
+ }
+ }
+
+ while (TRUE) {
+ for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {
+ ProcessorData = &gMPSystem.ProcessorData[Number];
+ if ((ProcessorData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {
+ // Skip BSP
+ continue;
+ }
+
+ gPthread->MutextLock (ProcessorData->StateLock);
+ ProcessorState = ProcessorData->State;
+ gPthread->MutexUnlock (ProcessorData->StateLock);
+
+ switch (ProcessorState) {
+ case CPU_STATE_READY:
+ SetApProcedure (ProcessorData, Procedure, ProcedureArgument);
+ break;
+
+ case CPU_STATE_FINISHED:
+ gMPSystem.FinishCount++;
+ if (SingleThread) {
+ Status = GetNextBlockedNumber (&NextNumber);
+ if (!EFI_ERROR (Status)) {
+ gMPSystem.ProcessorData[NextNumber].State = CPU_STATE_READY;
+ }
+ }
+
+ ProcessorData->State = CPU_STATE_IDLE;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (gMPSystem.FinishCount == gMPSystem.StartCount) {
+ return EFI_SUCCESS;
+ }
+
+ if ((TimeoutInMicroseconds != 0) && (Timeout < 0)) {
+ //
+ // Save data into private data structure, and create timer to poll AP state before exiting
+ //
+ gMPSystem.Procedure = Procedure;
+ gMPSystem.ProcedureArgument = ProcedureArgument;
+ gMPSystem.WaitEvent = WaitEvent;
+
+ Status = gBS->SetTimer (
+ gMPSystem.CheckAllAPsEvent,
+ TimerPeriodic,
+ gPollInterval
+ );
+ return EFI_TIMEOUT;
+ }
+
+ gBS->Stall (gPollInterval);
+ Timeout -= gPollInterval;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This service lets the caller get one enabled AP to execute a caller-provided
+ function. The caller can request the BSP to either wait for the completion
+ of the AP or just proceed with the next task by using the EFI event mechanism.
+ See EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() for more details on non-blocking
+ execution support. This service may only be called from the BSP.
+
+ This function is used to dispatch one enabled AP to the function specified by
+ Procedure passing in the argument specified by ProcedureArgument. If WaitEvent
+ is NULL, execution is in blocking mode. The BSP waits until the AP finishes or
+ TimeoutInMicroSecondss expires. Otherwise, execution is in non-blocking mode.
+ BSP proceeds to the next task without waiting for the AP. If a non-blocking mode
+ is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled,
+ then EFI_UNSUPPORTED must be returned.
+
+ If the timeout specified by TimeoutInMicroseconds expires before the AP returns
+ from Procedure, then execution of Procedure by the AP is terminated. The AP is
+ available for subsequent calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and
+ EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ instance.
+ @param[in] Procedure A pointer to the function to be run on
+ enabled APs of the system. See type
+ EFI_AP_PROCEDURE.
+ @param[in] ProcessorNumber The handle number of the AP. The range is
+ from 0 to the total number of logical
+ processors minus 1. The total number of
+ logical processors can be retrieved by
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+ @param[in] WaitEvent The event created by the caller with CreateEvent()
+ service. If it is NULL, then execute in
+ blocking mode. BSP waits until all APs finish
+ or TimeoutInMicroseconds expires. If it's
+ not NULL, then execute in non-blocking mode.
+ BSP requests the function specified by
+ Procedure to be started on all the enabled
+ APs, and go on executing immediately. If
+ all return from Procedure or TimeoutInMicroseconds
+ expires, this event is signaled. The BSP
+ can use the CheckEvent() or WaitForEvent()
+ services to check the state of event. Type
+ EFI_EVENT is defined in CreateEvent() in
+ the Unified Extensible Firmware Interface
+ Specification.
+ @param[in] TimeoutInMicrosecsond Indicates the time limit in microseconds for
+ APs to return from Procedure, either for
+ blocking or non-blocking mode. Zero means
+ infinity. If the timeout expires before
+ all APs return from Procedure, then Procedure
+ on the failed APs is terminated. All enabled
+ APs are available for next function assigned
+ by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+ or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
+ If the timeout expires in blocking mode,
+ BSP returns EFI_TIMEOUT. If the timeout
+ expires in non-blocking mode, WaitEvent
+ is signaled with SignalEvent().
+ @param[in] ProcedureArgument The parameter passed into Procedure for
+ all APs.
+ @param[out] Finished If NULL, this parameter is ignored. In
+ blocking mode, this parameter is ignored.
+ In non-blocking mode, if AP returns from
+ Procedure before the timeout expires, its
+ content is set to TRUE. Otherwise, the
+ value is set to FALSE. The caller can
+ determine if the AP returned from Procedure
+ by evaluating this value.
+
+ @retval EFI_SUCCESS In blocking mode, specified AP finished before
+ the timeout expires.
+ @retval EFI_SUCCESS In non-blocking mode, the function has been
+ dispatched to specified AP.
+ @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
+ UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
+ signaled.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_TIMEOUT In blocking mode, the timeout expired before
+ the specified AP has finished.
+ @retval EFI_NOT_READY The specified AP is busy.
+ @retval EFI_NOT_FOUND The processor with the handle specified by
+ ProcessorNumber does not exist.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP.
+ @retval EFI_INVALID_PARAMETER Procedure is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesStartupThisAP (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN EFI_AP_PROCEDURE Procedure,
+ IN UINTN ProcessorNumber,
+ IN EFI_EVENT WaitEvent OPTIONAL,
+ IN UINTN TimeoutInMicroseconds,
+ IN VOID *ProcedureArgument OPTIONAL,
+ OUT BOOLEAN *Finished OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ INTN Timeout;
+
+ if (!IsBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (Procedure == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {
+ return EFI_NOT_FOUND;
+ }
+
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (gMPSystem.ProcessorData[ProcessorNumber].State != CPU_STATE_IDLE) {
+ return EFI_NOT_READY;
+ }
+
+ if ((WaitEvent != NULL) && gReadToBoot) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Timeout = TimeoutInMicroseconds;
+
+ gMPSystem.StartCount = 1;
+ gMPSystem.FinishCount = 0;
+
+ SetApProcedure (&gMPSystem.ProcessorData[ProcessorNumber], Procedure, ProcedureArgument);
+
+ while (TRUE) {
+ gPthread->MutextLock (&gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+ if (gMPSystem.ProcessorData[ProcessorNumber].State == CPU_STATE_FINISHED) {
+ gMPSystem.ProcessorData[ProcessorNumber].State = CPU_STATE_IDLE;
+ gPthread->MutexUnlock (&gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+ break;
+ }
+
+ gPthread->MutexUnlock (&gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+
+ if ((TimeoutInMicroseconds != 0) && (Timeout < 0)) {
+ gMPSystem.WaitEvent = WaitEvent;
+ Status = gBS->SetTimer (
+ gMPSystem.ProcessorData[ProcessorNumber].CheckThisAPEvent,
+ TimerPeriodic,
+ gPollInterval
+ );
+ return EFI_TIMEOUT;
+ }
+
+ gBS->Stall (gPollInterval);
+ Timeout -= gPollInterval;
+ }
+
+ return EFI_SUCCESS;
+
+}
+
+
+/**
+ This service switches the requested AP to be the BSP from that point onward.
+ This service changes the BSP for all purposes. This call can only be performed
+ by the current BSP.
+
+ This service switches the requested AP to be the BSP from that point onward.
+ This service changes the BSP for all purposes. The new BSP can take over the
+ execution of the old BSP and continue seamlessly from where the old one left
+ off. This service may not be supported after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
+ is signaled.
+
+ If the BSP cannot be switched prior to the return from this service, then
+ EFI_UNSUPPORTED must be returned.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+ @param[in] ProcessorNumber The handle number of AP that is to become the new
+ BSP. The range is from 0 to the total number of
+ logical processors minus 1. The total number of
+ logical processors can be retrieved by
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+ @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an
+ enabled AP. Otherwise, it will be disabled.
+
+ @retval EFI_SUCCESS BSP successfully switched.
+ @retval EFI_UNSUPPORTED Switching the BSP cannot be completed prior to
+ this service returning.
+ @retval EFI_UNSUPPORTED Switching the BSP is not supported.
+ @retval EFI_SUCCESS The calling processor is an AP.
+ @retval EFI_NOT_FOUND The processor with the handle specified by
+ ProcessorNumber does not exist.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or
+ a disabled AP.
+ @retval EFI_NOT_READY The specified AP is busy.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesSwitchBSP (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableOldBSP
+ )
+{
+ UINTN Index;
+
+ if (!IsBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {
+ return EFI_NOT_FOUND;
+ }
+
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index = 0; Index < gMPSystem.NumberOfProcessors; Index++) {
+ if ((gMPSystem.ProcessorData[Index].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {
+ break;
+ }
+ }
+ ASSERT (Index != gMPSystem.NumberOfProcessors);
+
+ if (gMPSystem.ProcessorData[ProcessorNumber].State != CPU_STATE_IDLE) {
+ return EFI_NOT_READY;
+ }
+
+ // Skip for now as we need switch a bunch of stack stuff around and it's complex
+ // May not be worth it?
+ return EFI_NOT_READY;
+}
+
+
+/**
+ This service lets the caller enable or disable an AP from this point onward.
+ This service may only be called from the BSP.
+
+ This service allows the caller enable or disable an AP from this point onward.
+ The caller can optionally specify the health status of the AP by Health. If
+ an AP is being disabled, then the state of the disabled AP is implementation
+ dependent. If an AP is enabled, then the implementation must guarantee that a
+ complete initialization sequence is performed on the AP, so the AP is in a state
+ that is compatible with an MP operating system. This service may not be supported
+ after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled.
+
+ If the enable or disable AP operation cannot be completed prior to the return
+ from this service, then EFI_UNSUPPORTED must be returned.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+ @param[in] ProcessorNumber The handle number of AP that is to become the new
+ BSP. The range is from 0 to the total number of
+ logical processors minus 1. The total number of
+ logical processors can be retrieved by
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+ @param[in] EnableAP Specifies the new state for the processor for
+ enabled, FALSE for disabled.
+ @param[in] HealthFlag If not NULL, a pointer to a value that specifies
+ the new health status of the AP. This flag
+ corresponds to StatusFlag defined in
+ EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only
+ the PROCESSOR_HEALTH_STATUS_BIT is used. All other
+ bits are ignored. If it is NULL, this parameter
+ is ignored.
+
+ @retval EFI_SUCCESS The specified AP was enabled or disabled successfully.
+ @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed
+ prior to this service returning.
+ @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber
+ does not exist.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesEnableDisableAP (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableAP,
+ IN UINT32 *HealthFlag OPTIONAL
+ )
+{
+ if (!IsBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {
+ return EFI_NOT_FOUND;
+ }
+
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (gMPSystem.ProcessorData[ProcessorNumber].State != CPU_STATE_IDLE) {
+ return EFI_UNSUPPORTED;
+ }
+
+ gPthread->MutextLock (&gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+
+ if (EnableAP) {
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0 ) {
+ gMPSystem.NumberOfEnabledProcessors++;
+ }
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag |= PROCESSOR_ENABLED_BIT;
+ } else {
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_ENABLED_BIT) == PROCESSOR_ENABLED_BIT ) {
+ gMPSystem.NumberOfEnabledProcessors--;
+ }
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag &= ~PROCESSOR_ENABLED_BIT;
+ }
+
+ if (HealthFlag != NULL) {
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag &= ~PROCESSOR_HEALTH_STATUS_BIT;
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag |= (*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT);
+ }
+
+ gPthread->MutexUnlock (&gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This return the handle number for the calling processor. This service may be
+ called from the BSP and APs.
+
+ This service returns the processor handle number for the calling processor.
+ The returned value is in the range from 0 to the total number of logical
+ processors minus 1. The total number of logical processors can be retrieved
+ with EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). This service may be
+ called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER
+ is returned. Otherwise, the current processors handle number is returned in
+ ProcessorNumber, and EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+ @param[in] ProcessorNumber The handle number of AP that is to become the new
+ BSP. The range is from 0 to the total number of
+ logical processors minus 1. The total number of
+ logical processors can be retrieved by
+ EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+
+ @retval EFI_SUCCESS The current processor handle number was returned
+ in ProcessorNumber.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesWhoAmI (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ OUT UINTN *ProcessorNumber
+ )
+{
+ UINTN Index;
+ UINT64 ProcessorId;
+
+ if (ProcessorNumber == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ProcessorId = gPthread->Self ();
+ for (Index = 0; Index < gMPSystem.NumberOfProcessors; Index++) {
+ if (gMPSystem.ProcessorData[Index].Info.ProcessorId == ProcessorId) {
+ break;
+ }
+ }
+
+ *ProcessorNumber = Index;
+ return EFI_SUCCESS;
+}
+
+
+
+EFI_MP_SERVICES_PROTOCOL mMpSercicesTemplate = {
+ CpuMpServicesGetNumberOfProcessors,
+ CpuMpServicesGetProcessorInfo,
+ CpuMpServicesStartupAllAps,
+ CpuMpServicesStartupThisAP,
+ CpuMpServicesSwitchBSP,
+ CpuMpServicesEnableDisableAP,
+ CpuMpServicesWhoAmI
+};
+
+
+
+/*++
+ If timeout occurs in StartupAllAps(), a timer is set, which invokes this
+ procedure periodically to check whether all APs have finished.
+
+
+--*/
+VOID
+EFIAPI
+CpuCheckAllAPsStatus (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ UINTN ProcessorNumber;
+ UINTN NextNumber;
+ PROCESSOR_DATA_BLOCK *ProcessorData;
+ PROCESSOR_DATA_BLOCK *NextData;
+ EFI_STATUS Status;
+ PROCESSOR_STATE ProcessorState;
+
+ for (ProcessorNumber = 0; ProcessorNumber < gMPSystem.NumberOfProcessors; ProcessorNumber++) {
+ if ((ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {
+ // Skip BSP
+ continue;
+ }
+
+ // This is an Interrupt Service routine.
+ // This can grab a lock that is held in a non-interrupt
+ // context. Meaning deadlock. Which is a bad thing.
+ // So, try lock it. If we can get it, cool, do our thing.
+ // otherwise, just dump out & try again on the next iteration.
+ Status = gPthread->MutexTryLock (gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+ if (EFI_ERROR(Status)) {
+ return;
+ }
+ ProcessorState = gMPSystem.ProcessorData[ProcessorNumber].State;
+ gPthread->MutexUnlock (gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+
+ switch (ProcessorState) {
+ case CPU_STATE_READY:
+ SetApProcedure (ProcessorData, gMPSystem.Procedure, gMPSystem.ProcedureArgument);
+ break;
+
+ case CPU_STATE_FINISHED:
+ if (gMPSystem.SingleThread) {
+ Status = GetNextBlockedNumber (&NextNumber);
+ if (!EFI_ERROR (Status)) {
+ NextData = &gMPSystem.ProcessorData[NextNumber];
+
+ gPthread->MutextLock (&NextData->ProcedureLock);
+ NextData->State = CPU_STATE_READY;
+ gPthread->MutexUnlock (&NextData->ProcedureLock);
+
+ SetApProcedure (NextData, gMPSystem.Procedure, gMPSystem.ProcedureArgument);
+ }
+ }
+
+ gMPSystem.ProcessorData[ProcessorNumber].State = CPU_STATE_IDLE;
+ gMPSystem.FinishCount++;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (gMPSystem.FinishCount == gMPSystem.StartCount) {
+ gBS->SetTimer (
+ gMPSystem.CheckAllAPsEvent,
+ TimerCancel,
+ 0
+ );
+ Status = gBS->SignalEvent (gMPSystem.WaitEvent);
+ }
+
+ return ;
+}
+
+VOID
+EFIAPI
+CpuCheckThisAPStatus (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ PROCESSOR_DATA_BLOCK *ProcessorData;
+ PROCESSOR_STATE ProcessorState;
+
+ ProcessorData = (PROCESSOR_DATA_BLOCK *) Context;
+
+ //
+ // rdar://6260979 - This is an Interrupt Service routine.
+ // this can grab a lock that is held in a non-interrupt
+ // context. Meaning deadlock. Which is a badddd thing.
+ // So, try lock it. If we can get it, cool, do our thing.
+ // otherwise, just dump out & try again on the next iteration.
+ //
+ Status = gPthread->MutexTryLock (ProcessorData->StateLock);
+ if (EFI_ERROR(Status)) {
+ return;
+ }
+ ProcessorState = ProcessorData->State;
+ gPthread->MutexUnlock (ProcessorData->StateLock);
+
+ if (ProcessorState == CPU_STATE_FINISHED) {
+ Status = gBS->SetTimer (ProcessorData->CheckThisAPEvent, TimerCancel, 0);
+ ASSERT_EFI_ERROR (Status);
+
+ Status = gBS->SignalEvent (gMPSystem.WaitEvent);
+ ASSERT_EFI_ERROR (Status);
+
+ gPthread->MutextLock (ProcessorData->StateLock);
+ ProcessorData->State = CPU_STATE_IDLE;
+ gPthread->MutexUnlock (ProcessorData->StateLock);
+ }
+
+ return ;
+}
+
+
+/*++
+ This function is called by all processors (both BSP and AP) once and collects MP related data
+
+ MPSystemData - Pointer to the data structure containing MP related data
+ BSP - TRUE if the CPU is BSP
+
+ EFI_SUCCESS - Data for the processor collected and filled in
+
+--*/
+EFI_STATUS
+FillInProcessorInformation (
+ IN BOOLEAN BSP,
+ IN UINTN ProcessorNumber
+ )
+{
+ PROCESSOR_DATA_BLOCK *ProcessorData;
+
+ ProcessorData = &gMPSystem.ProcessorData[ProcessorNumber];
+
+ gMPSystem.ProcessorData[ProcessorNumber].Info.ProcessorId = gPthread->Self ();
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag = PROCESSOR_ENABLED_BIT | PROCESSOR_HEALTH_STATUS_BIT;
+ if (BSP) {
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag |= PROCESSOR_AS_BSP_BIT;
+ }
+
+ gMPSystem.ProcessorData[ProcessorNumber].Info.Location.Package = ProcessorNumber;
+ gMPSystem.ProcessorData[ProcessorNumber].Info.Location.Core = 0;
+ gMPSystem.ProcessorData[ProcessorNumber].Info.Location.Thread = 0;
+ gMPSystem.ProcessorData[ProcessorNumber].State = BSP ? CPU_STATE_BUSY : CPU_STATE_IDLE;
+
+ gMPSystem.ProcessorData[ProcessorNumber].Procedure = NULL;
+ gMPSystem.ProcessorData[ProcessorNumber].Parameter = NULL;
+ gMPSystem.ProcessorData[ProcessorNumber].StateLock = gPthread->MutexInit ();
+ gMPSystem.ProcessorData[ProcessorNumber].ProcedureLock = gPthread->MutexInit ();
+
+ return EFI_SUCCESS;
+}
+
+VOID *
+EFIAPI
+CpuDriverApIdolLoop (
+ VOID *Context
+ )
+{
+ EFI_AP_PROCEDURE Procedure;
+ VOID *Parameter;
+ UINTN ProcessorNumber;
+ PROCESSOR_DATA_BLOCK *ProcessorData;
+
+ ProcessorNumber = (UINTN)Context;
+ ProcessorData = &gMPSystem.ProcessorData[ProcessorNumber];
+
+ ProcessorData->Info.ProcessorId = gPthread->Self ();
+
+ while (TRUE) {
+ //
+ // Make a local copy on the stack to be extra safe
+ //
+ gPthread->MutextLock (ProcessorData->ProcedureLock);
+ Procedure = ProcessorData->Procedure;
+ Parameter = ProcessorData->Parameter;
+ gPthread->MutexUnlock (ProcessorData->ProcedureLock);
+
+ if (Procedure != NULL) {
+ gPthread->MutextLock (ProcessorData->StateLock);
+ ProcessorData->State = CPU_STATE_BUSY;
+ gPthread->MutexUnlock (ProcessorData->StateLock);
+
+ Procedure (Parameter);
+
+ gPthread->MutextLock (ProcessorData->ProcedureLock);
+ ProcessorData->Procedure = NULL;
+ gPthread->MutexUnlock (ProcessorData->ProcedureLock);
+
+ gPthread->MutextLock (ProcessorData->StateLock);
+ ProcessorData->State = CPU_STATE_FINISHED;
+ gPthread->MutexUnlock (ProcessorData->StateLock);
+ }
+
+ // Poll 5 times a seconds, 200ms
+ // Don't want to burn too many system resources doing nothing.
+ gEmuThunk->Sleep (200);
+ }
+
+ return 0;
+}
+
+
+EFI_STATUS
+InitializeMpSystemData (
+ IN UINTN NumberOfProcessors
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+
+
+ //
+ // Clear the data structure area first.
+ //
+ ZeroMem (&gMPSystem, sizeof (MP_SYSTEM_DATA));
+
+ //
+ // First BSP fills and inits all known values, including it's own records.
+ //
+ gMPSystem.NumberOfProcessors = NumberOfProcessors;
+ gMPSystem.NumberOfEnabledProcessors = NumberOfProcessors;
+
+ gMPSystem.ProcessorData = AllocateZeroPool (gMPSystem.NumberOfProcessors * sizeof (PROCESSOR_DATA_BLOCK));
+ ASSERT (gMPSystem.ProcessorData != NULL);
+
+ FillInProcessorInformation (TRUE, 0);
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ CpuCheckAllAPsStatus,
+ NULL,
+ &gMPSystem.CheckAllAPsEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+
+
+ for (Index = 0; Index < gMPSystem.NumberOfProcessors; Index++) {
+ if ((gMPSystem.ProcessorData[Index].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {
+ // Skip BSP
+ continue;
+ }
+
+ FillInProcessorInformation (FALSE, Index);
+
+ Status = gPthread->CreateThread (
+ (VOID *)&gMPSystem.ProcessorData[Index].Info.ProcessorId,
+ NULL,
+ CpuDriverApIdolLoop,
+ (VOID *)Index
+ );
+
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ CpuCheckThisAPStatus,
+ (VOID *) &gMPSystem.ProcessorData[Index],
+ &gMPSystem.ProcessorData[Index].CheckThisAPEvent
+ );
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Invoke a notification event
+
+ @param Event Event whose notification function is being invoked.
+ @param Context The pointer to the notification function's context,
+ which is implementation-dependent.
+
+**/
+VOID
+EFIAPI
+CpuReadToBootFunction (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ gReadToBoot = TRUE;
+}
+
+
+
+EFI_STATUS
+CpuMpServicesInit (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+ EMU_IO_THUNK_PROTOCOL *IoThunk;
+ UINTN MaxCpus;
+ char *Str;
+
+ MaxCpus = 1; // BSP
+
+ IoThunk = GetIoThunkInstance (&gEmuPthreadThunkProtocolGuid, 0);
+ if (IoThunk != NULL) {
+ Status = IoThunk->Open (IoThunk);
+ if (!EFI_ERROR (Status)) {
+ if (IoThunk->ConfigString != NULL) {
+ MaxCpus += StrDecimalToUintn (IoThunk->ConfigString);
+ gPthread = IoThunk->Interface;
+ }
+ }
+ }
+
+ if (MaxCpus == 1) {
+ // We are not MP so nothing to do
+ return EFI_SUCCESS;
+ }
+
+ gPollInterval = PcdGet64 (PcdEmuMpServicesPollingInterval);
+
+ Status = InitializeMpSystemData (MaxCpus);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = EfiCreateEventReadyToBootEx (TPL_CALLBACK, CpuReadToBootFunction, NULL, &gReadToBootEvent);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Now install the MP services protocol.
+ //
+ Handle = NULL;
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Handle,
+ &gEfiMpServiceProtocolGuid, &mMpSercicesTemplate,
+ NULL
+ );
+ return Status;
+}
+
+
diff --git a/InOsEmuPkg/InOsEmuPkg.dec b/InOsEmuPkg/InOsEmuPkg.dec
index 14f1040a7c..6b4aca28c2 100644
--- a/InOsEmuPkg/InOsEmuPkg.dec
+++ b/InOsEmuPkg/InOsEmuPkg.dec
@@ -52,7 +52,7 @@
# gEmuNetworkGuid = {0x081603B4, 0x0F1D, 0x4022, {0xB6, 0xFD, 0x4C, 0xE3, 0x5E, 0x09, 0xA1, 0xA6}}
[PcdsFixedAtBuild]
- gInOsEmuPkgTokenSpaceGuid.PcdEmuFlashNvStorageVariableBase|0x0|UINT64|0x00001014
+ gInOsEmuPkgTokenSpaceGuid.PcdEmuFlashNvStorageVariableBase|0x0|UINT64|0x00001014
gInOsEmuPkgTokenSpaceGuid.PcdEmuFlashNvStorageFtwSpareBase|0x0|UINT64|0x00001015
gInOsEmuPkgTokenSpaceGuid.PcdEmuFlashNvStorageFtwWorkingBase|0x0|UINT64|0x00001016
gInOsEmuPkgTokenSpaceGuid.PcdEmuFdBaseAddress|0x0|UINT64|0x00001017
@@ -81,5 +81,5 @@
gInOsEmuPkgTokenSpaceGuid.PcdEmuCpuModel|L"Intel(R) Processor Model"|VOID*|0x00001007
gInOsEmuPkgTokenSpaceGuid.PcdEmuCpuSpeed|L"3000"|VOID*|0x00001008
-
+ gInOsEmuPkgTokenSpaceGuid.PcdEmuMpServicesPollingInterval|0x100|UINT64|0x0000101a
diff --git a/InOsEmuPkg/Include/Library/EmuThunkLib.h b/InOsEmuPkg/Include/Library/EmuThunkLib.h
index d8f55b862e..9d69d4f2eb 100644
--- a/InOsEmuPkg/Include/Library/EmuThunkLib.h
+++ b/InOsEmuPkg/Include/Library/EmuThunkLib.h
@@ -19,4 +19,24 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
extern EMU_THUNK_PROTOCOL *gEmuThunk;
+
+/**
+ Serach the EMU IO Thunk database for a matching EMU IO Thunk
+ Protocol instance.
+
+ @param Protocol Protocol to search for.
+ @param Instance Instance of protocol to search for.
+
+ @retval NULL Protocol and Instance not found.
+ @retval other EMU IO Thunk protocol that matched.
+
+**/
+EMU_IO_THUNK_PROTOCOL *
+EFIAPI
+GetIoThunkInstance (
+ IN EFI_GUID *Protocol,
+ IN UINTN Instance
+ );
+
+
#endif
diff --git a/InOsEmuPkg/Library/DxeEmuLib/DxeEmuLib.c b/InOsEmuPkg/Library/DxeEmuLib/DxeEmuLib.c
index 79e9fbc1ee..2e42ea6151 100644
--- a/InOsEmuPkg/Library/DxeEmuLib/DxeEmuLib.c
+++ b/InOsEmuPkg/Library/DxeEmuLib/DxeEmuLib.c
@@ -17,9 +17,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include <Library/DebugLib.h>
#include <Library/HobLib.h>
#include <Library/EmuThunkLib.h>
-
-#include <Protocol/EmuThunk.h>
-
+#include <Library/BaseMemoryLib.h>
EMU_THUNK_PROTOCOL *gEmuThunk = NULL;
@@ -50,3 +48,41 @@ DxeEmuLibConstructor (
return EFI_SUCCESS;
}
+
+
+/**
+ Serach the EMU IO Thunk database for a matching EMU IO Thunk
+ Protocol instance.
+
+ @param Protocol Protocol to search for.
+ @param Instance Instance of protocol to search for.
+
+ @retval NULL Protocol and Instance not found.
+ @retval other EMU IO Thunk protocol that matched.
+
+**/
+EMU_IO_THUNK_PROTOCOL *
+EFIAPI
+GetIoThunkInstance (
+ IN EFI_GUID *Protocol,
+ IN UINTN Instance
+ )
+{
+ EFI_STATUS Status;
+ EMU_IO_THUNK_PROTOCOL *EmuIoThunk;
+
+ for (Status = EFI_SUCCESS, EmuIoThunk = NULL; !EFI_ERROR (Status); ) {
+ Status = gEmuThunk->GetNextProtocol (FALSE, &EmuIoThunk);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ if (EmuIoThunk->Instance == Instance) {
+ if (CompareGuid (EmuIoThunk->Protocol, Protocol)) {
+ return EmuIoThunk;
+ }
+ }
+ }
+
+ return NULL;
+} \ No newline at end of file
diff --git a/InOsEmuPkg/Library/DxeEmuLib/DxeEmuLib.inf b/InOsEmuPkg/Library/DxeEmuLib/DxeEmuLib.inf
index de395d6272..0f7f0ddeca 100644
--- a/InOsEmuPkg/Library/DxeEmuLib/DxeEmuLib.inf
+++ b/InOsEmuPkg/Library/DxeEmuLib/DxeEmuLib.inf
@@ -38,6 +38,7 @@
[LibraryClasses]
HobLib
DebugLib
+ BaseMemoryLib
[Protocols]