/** @file | |
CPU DXE Module. | |
Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include "CpuDxe.h" | |
#include "CpuMp.h" | |
UINTN gMaxLogicalProcessorNumber; | |
UINTN gApStackSize; | |
UINTN gPollInterval = 100; // 100 microseconds | |
MP_SYSTEM_DATA mMpSystemData; | |
EFI_HANDLE mMpServiceHandle = NULL; | |
EFI_EVENT mExitBootServicesEvent = (EFI_EVENT)NULL; | |
VOID *mCommonStack = 0; | |
VOID *mTopOfApCommonStack = 0; | |
VOID *mApStackStart = 0; | |
volatile BOOLEAN mAPsAlreadyInitFinished = FALSE; | |
volatile BOOLEAN mStopCheckAllAPsStatus = TRUE; | |
EFI_MP_SERVICES_PROTOCOL mMpServicesTemplate = { | |
GetNumberOfProcessors, | |
GetProcessorInfo, | |
StartupAllAPs, | |
StartupThisAP, | |
SwitchBSP, | |
EnableDisableAP, | |
WhoAmI | |
}; | |
/** | |
Get Mp Service Lock. | |
@param CpuData the pointer to CPU_DATA_BLOCK of specified processor | |
**/ | |
VOID | |
GetMpSpinLock ( | |
IN CPU_DATA_BLOCK *CpuData | |
) | |
{ | |
while (!AcquireSpinLockOrFail (&CpuData->CpuDataLock)) { | |
CpuPause (); | |
} | |
CpuData->LockSelf = GetApicId (); | |
} | |
/** | |
Release Mp Service Lock. | |
@param CpuData the pointer to CPU_DATA_BLOCK of specified processor | |
**/ | |
VOID | |
ReleaseMpSpinLock ( | |
IN CPU_DATA_BLOCK *CpuData | |
) | |
{ | |
ReleaseSpinLock (&CpuData->CpuDataLock); | |
} | |
/** | |
Check whether caller processor is BSP. | |
@retval TRUE the caller is BSP | |
@retval FALSE the caller is AP | |
**/ | |
BOOLEAN | |
IsBSP ( | |
VOID | |
) | |
{ | |
UINTN CpuIndex; | |
CPU_DATA_BLOCK *CpuData; | |
CpuData = NULL; | |
WhoAmI (&mMpServicesTemplate, &CpuIndex); | |
CpuData = &mMpSystemData.CpuDatas[CpuIndex]; | |
return CpuData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT ? TRUE : FALSE; | |
} | |
/** | |
Get the Application Processors state. | |
@param CpuData the pointer to CPU_DATA_BLOCK of specified AP | |
@retval CPU_STATE the AP status | |
**/ | |
CPU_STATE | |
GetApState ( | |
IN CPU_DATA_BLOCK *CpuData | |
) | |
{ | |
CPU_STATE State; | |
GetMpSpinLock (CpuData); | |
State = CpuData->State; | |
ReleaseMpSpinLock (CpuData); | |
return State; | |
} | |
/** | |
Set the Application Processors state. | |
@param CpuData The pointer to CPU_DATA_BLOCK of specified AP | |
@param State The AP status | |
**/ | |
VOID | |
SetApState ( | |
IN CPU_DATA_BLOCK *CpuData, | |
IN CPU_STATE State | |
) | |
{ | |
GetMpSpinLock (CpuData); | |
CpuData->State = State; | |
ReleaseMpSpinLock (CpuData); | |
} | |
/** | |
Set the Application Processor prepare to run a function specified | |
by Params. | |
@param CpuData the pointer to CPU_DATA_BLOCK of specified AP | |
@param Procedure A pointer to the function to be run on enabled APs of the system | |
@param ProcedureArgument Pointer to the optional parameter of the assigned function | |
**/ | |
VOID | |
SetApProcedure ( | |
IN CPU_DATA_BLOCK *CpuData, | |
IN EFI_AP_PROCEDURE Procedure, | |
IN VOID *ProcedureArgument | |
) | |
{ | |
GetMpSpinLock (CpuData); | |
CpuData->Parameter = ProcedureArgument; | |
CpuData->Procedure = Procedure; | |
ReleaseMpSpinLock (CpuData); | |
} | |
/** | |
Check the Application Processors Status whether contains the Flags. | |
@param CpuData the pointer to CPU_DATA_BLOCK of specified AP | |
@param Flags the StatusFlag describing in EFI_PROCESSOR_INFORMATION | |
@retval TRUE the AP status includes the StatusFlag | |
@retval FALSE the AP status excludes the StatusFlag | |
**/ | |
BOOLEAN | |
TestCpuStatusFlag ( | |
IN CPU_DATA_BLOCK *CpuData, | |
IN UINT32 Flags | |
) | |
{ | |
UINT32 Ret; | |
GetMpSpinLock (CpuData); | |
Ret = CpuData->Info.StatusFlag & Flags; | |
ReleaseMpSpinLock (CpuData); | |
return (BOOLEAN) (Ret != 0); | |
} | |
/** | |
Bitwise-Or of the Application Processors Status with the Flags. | |
@param CpuData the pointer to CPU_DATA_BLOCK of specified AP | |
@param Flags the StatusFlag describing in EFI_PROCESSOR_INFORMATION | |
**/ | |
VOID | |
CpuStatusFlagOr ( | |
IN CPU_DATA_BLOCK *CpuData, | |
IN UINT32 Flags | |
) | |
{ | |
GetMpSpinLock (CpuData); | |
CpuData->Info.StatusFlag |= Flags; | |
ReleaseMpSpinLock (CpuData); | |
} | |
/** | |
Bitwise-AndNot of the Application Processors Status with the Flags. | |
@param CpuData the pointer to CPU_DATA_BLOCK of specified AP | |
@param Flags the StatusFlag describing in EFI_PROCESSOR_INFORMATION | |
**/ | |
VOID | |
CpuStatusFlagAndNot ( | |
IN CPU_DATA_BLOCK *CpuData, | |
IN UINT32 Flags | |
) | |
{ | |
GetMpSpinLock (CpuData); | |
CpuData->Info.StatusFlag &= ~Flags; | |
ReleaseMpSpinLock (CpuData); | |
} | |
/** | |
Searches for the next blocking AP. | |
Search for the next AP that is put in blocking state by single-threaded StartupAllAPs(). | |
@param NextNumber Pointer to the processor number of the next blocking AP. | |
@retval EFI_SUCCESS The next blocking AP has been found. | |
@retval EFI_NOT_FOUND No blocking AP exists. | |
**/ | |
EFI_STATUS | |
GetNextBlockedNumber ( | |
OUT UINTN *NextNumber | |
) | |
{ | |
UINTN Number; | |
CPU_STATE CpuState; | |
CPU_DATA_BLOCK *CpuData; | |
for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) { | |
CpuData = &mMpSystemData.CpuDatas[Number]; | |
if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) { | |
// | |
// Skip BSP | |
// | |
continue; | |
} | |
CpuState = GetApState (CpuData); | |
if (CpuState == CpuStateBlocked) { | |
*NextNumber = Number; | |
return EFI_SUCCESS; | |
} | |
} | |
return EFI_NOT_FOUND; | |
} | |
/** | |
Check if the APs state are finished, and update them to idle state | |
by StartupAllAPs(). | |
**/ | |
VOID | |
CheckAndUpdateAllAPsToIdleState ( | |
VOID | |
) | |
{ | |
UINTN ProcessorNumber; | |
UINTN NextNumber; | |
CPU_DATA_BLOCK *CpuData; | |
EFI_STATUS Status; | |
CPU_STATE CpuState; | |
for (ProcessorNumber = 0; ProcessorNumber < mMpSystemData.NumberOfProcessors; ProcessorNumber++) { | |
CpuData = &mMpSystemData.CpuDatas[ProcessorNumber]; | |
if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) { | |
// | |
// Skip BSP | |
// | |
continue; | |
} | |
if (!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) { | |
// | |
// Skip Disabled processors | |
// | |
continue; | |
} | |
CpuState = GetApState (CpuData); | |
if (CpuState == CpuStateFinished) { | |
mMpSystemData.FinishCount++; | |
if (mMpSystemData.SingleThread) { | |
Status = GetNextBlockedNumber (&NextNumber); | |
if (!EFI_ERROR (Status)) { | |
SetApState (&mMpSystemData.CpuDatas[NextNumber], CpuStateReady); | |
SetApProcedure (&mMpSystemData.CpuDatas[NextNumber], | |
mMpSystemData.Procedure, | |
mMpSystemData.ProcedureArgument); | |
} | |
} | |
SetApState (CpuData, CpuStateIdle); | |
} | |
} | |
} | |
/** | |
If the timeout expires before all APs returns from Procedure, | |
we should forcibly terminate the executing AP and fill FailedList back | |
by StartupAllAPs(). | |
**/ | |
VOID | |
ResetAllFailedAPs ( | |
VOID | |
) | |
{ | |
CPU_DATA_BLOCK *CpuData; | |
UINTN Number; | |
CPU_STATE CpuState; | |
if (mMpSystemData.FailedList != NULL) { | |
*mMpSystemData.FailedList = AllocatePool ((mMpSystemData.StartCount - mMpSystemData.FinishCount + 1) * sizeof(UINTN)); | |
ASSERT (*mMpSystemData.FailedList != NULL); | |
} | |
for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) { | |
CpuData = &mMpSystemData.CpuDatas[Number]; | |
if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) { | |
// | |
// Skip BSP | |
// | |
continue; | |
} | |
if (!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) { | |
// | |
// Skip Disabled processors | |
// | |
continue; | |
} | |
CpuState = GetApState (CpuData); | |
if (CpuState != CpuStateIdle) { | |
if (mMpSystemData.FailedList != NULL) { | |
(*mMpSystemData.FailedList)[mMpSystemData.FailedListIndex++] = Number; | |
} | |
ResetProcessorToIdleState (CpuData); | |
} | |
} | |
if (mMpSystemData.FailedList != NULL) { | |
(*mMpSystemData.FailedList)[mMpSystemData.FailedListIndex] = END_OF_CPU_LIST; | |
} | |
} | |
/** | |
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 | |
GetNumberOfProcessors ( | |
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 = mMpSystemData.NumberOfProcessors; | |
*NumberOfEnabledProcessors = mMpSystemData.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 | |
GetProcessorInfo ( | |
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 >= mMpSystemData.NumberOfProcessors) { | |
return EFI_NOT_FOUND; | |
} | |
CopyMem (ProcessorInfoBuffer, &mMpSystemData.CpuDatas[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] TimeoutInMicroseconds Indicates the time limit in microseconds for | |
APs to return from Procedure, either for | |
blocking or non-blocking mode. Zero means | |
infinity. If the timeout expires before | |
all APs return from Procedure, then Procedure | |
on the failed APs is terminated. All enabled | |
APs are available for next function assigned | |
by 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 | |
StartupAllAPs ( | |
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; | |
CPU_DATA_BLOCK *CpuData; | |
UINTN Number; | |
CPU_STATE APInitialState; | |
CpuData = NULL; | |
if (FailedCpuList != NULL) { | |
*FailedCpuList = NULL; | |
} | |
if (!IsBSP ()) { | |
return EFI_DEVICE_ERROR; | |
} | |
if (mMpSystemData.NumberOfProcessors == 1) { | |
return EFI_NOT_STARTED; | |
} | |
if (Procedure == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// temporarily stop checkAllAPsStatus for avoid resource dead-lock. | |
// | |
mStopCheckAllAPsStatus = TRUE; | |
for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) { | |
CpuData = &mMpSystemData.CpuDatas[Number]; | |
if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) { | |
// | |
// Skip BSP | |
// | |
continue; | |
} | |
if (!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) { | |
// | |
// Skip Disabled processors | |
// | |
continue; | |
} | |
if (GetApState (CpuData) != CpuStateIdle) { | |
return EFI_NOT_READY; | |
} | |
} | |
mMpSystemData.Procedure = Procedure; | |
mMpSystemData.ProcedureArgument = ProcedureArgument; | |
mMpSystemData.WaitEvent = WaitEvent; | |
mMpSystemData.Timeout = TimeoutInMicroseconds; | |
mMpSystemData.TimeoutActive = (BOOLEAN) (TimeoutInMicroseconds != 0); | |
mMpSystemData.FinishCount = 0; | |
mMpSystemData.StartCount = 0; | |
mMpSystemData.SingleThread = SingleThread; | |
mMpSystemData.FailedList = FailedCpuList; | |
mMpSystemData.FailedListIndex = 0; | |
APInitialState = CpuStateReady; | |
for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) { | |
CpuData = &mMpSystemData.CpuDatas[Number]; | |
if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) { | |
// | |
// Skip BSP | |
// | |
continue; | |
} | |
if (!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) { | |
// | |
// Skip Disabled processors | |
// | |
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 (GetApState (CpuData) == CpuStateIdle) { | |
mMpSystemData.StartCount++; | |
SetApState (CpuData, APInitialState); | |
if (APInitialState == CpuStateReady) { | |
SetApProcedure (CpuData, Procedure, ProcedureArgument); | |
} | |
if (SingleThread) { | |
APInitialState = CpuStateBlocked; | |
} | |
} | |
} | |
mStopCheckAllAPsStatus = FALSE; | |
if (WaitEvent != NULL) { | |
// | |
// non blocking | |
// | |
return EFI_SUCCESS; | |
} | |
// | |
// Blocking temporarily stop CheckAllAPsStatus() | |
// | |
mStopCheckAllAPsStatus = TRUE; | |
while (TRUE) { | |
CheckAndUpdateAllAPsToIdleState (); | |
if (mMpSystemData.FinishCount == mMpSystemData.StartCount) { | |
Status = EFI_SUCCESS; | |
goto Done; | |
} | |
// | |
// task timeout | |
// | |
if (mMpSystemData.TimeoutActive && mMpSystemData.Timeout < 0) { | |
ResetAllFailedAPs(); | |
Status = EFI_TIMEOUT; | |
goto Done; | |
} | |
gBS->Stall (gPollInterval); | |
mMpSystemData.Timeout -= gPollInterval; | |
} | |
Done: | |
return Status; | |
} | |
/** | |
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] TimeoutInMicroseconds Indicates the time limit in microseconds for | |
APs to return from Procedure, either for | |
blocking or non-blocking mode. Zero means | |
infinity. If the timeout expires before | |
all APs return from Procedure, then Procedure | |
on the failed APs is terminated. All enabled | |
APs are available for next function assigned | |
by 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 | |
StartupThisAP ( | |
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 | |
) | |
{ | |
CPU_DATA_BLOCK *CpuData; | |
CpuData = NULL; | |
if (Finished != NULL) { | |
*Finished = FALSE; | |
} | |
if (!IsBSP ()) { | |
return EFI_DEVICE_ERROR; | |
} | |
if (Procedure == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (ProcessorNumber >= mMpSystemData.NumberOfProcessors) { | |
return EFI_NOT_FOUND; | |
} | |
// | |
// temporarily stop checkAllAPsStatus for avoid resource dead-lock. | |
// | |
mStopCheckAllAPsStatus = TRUE; | |
CpuData = &mMpSystemData.CpuDatas[ProcessorNumber]; | |
if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT) || | |
!TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (GetApState (CpuData) != CpuStateIdle) { | |
return EFI_NOT_READY; | |
} | |
SetApState (CpuData, CpuStateReady); | |
SetApProcedure (CpuData, Procedure, ProcedureArgument); | |
CpuData->Timeout = TimeoutInMicroseconds; | |
CpuData->WaitEvent = WaitEvent; | |
CpuData->TimeoutActive = (BOOLEAN) (TimeoutInMicroseconds != 0); | |
CpuData->Finished = Finished; | |
mStopCheckAllAPsStatus = FALSE; | |
if (WaitEvent != NULL) { | |
// | |
// Non Blocking | |
// | |
return EFI_SUCCESS; | |
} | |
// | |
// Blocking | |
// | |
while (TRUE) { | |
if (GetApState (CpuData) == CpuStateFinished) { | |
SetApState (CpuData, CpuStateIdle); | |
break; | |
} | |
if (CpuData->TimeoutActive && CpuData->Timeout < 0) { | |
ResetProcessorToIdleState (CpuData); | |
return EFI_TIMEOUT; | |
} | |
gBS->Stall (gPollInterval); | |
CpuData->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 | |
SwitchBSP ( | |
IN EFI_MP_SERVICES_PROTOCOL *This, | |
IN UINTN ProcessorNumber, | |
IN BOOLEAN EnableOldBSP | |
) | |
{ | |
// | |
// Current always return unsupported. | |
// | |
return EFI_UNSUPPORTED; | |
} | |
/** | |
This service lets the caller enable or disable an AP from this point onward. | |
This service may only be called from the BSP. | |
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 | |
EnableDisableAP ( | |
IN EFI_MP_SERVICES_PROTOCOL *This, | |
IN UINTN ProcessorNumber, | |
IN BOOLEAN EnableAP, | |
IN UINT32 *HealthFlag OPTIONAL | |
) | |
{ | |
CPU_DATA_BLOCK *CpuData; | |
BOOLEAN TempStopCheckState; | |
CpuData = NULL; | |
TempStopCheckState = FALSE; | |
if (!IsBSP ()) { | |
return EFI_DEVICE_ERROR; | |
} | |
if (ProcessorNumber >= mMpSystemData.NumberOfProcessors) { | |
return EFI_NOT_FOUND; | |
} | |
// | |
// temporarily stop checkAllAPsStatus for initialize parameters. | |
// | |
if (!mStopCheckAllAPsStatus) { | |
mStopCheckAllAPsStatus = TRUE; | |
TempStopCheckState = TRUE; | |
} | |
CpuData = &mMpSystemData.CpuDatas[ProcessorNumber]; | |
if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (GetApState (CpuData) != CpuStateIdle) { | |
return EFI_UNSUPPORTED; | |
} | |
if (EnableAP) { | |
if (!(TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT))) { | |
mMpSystemData.NumberOfEnabledProcessors++; | |
} | |
CpuStatusFlagOr (CpuData, PROCESSOR_ENABLED_BIT); | |
} else { | |
if (TestCpuStatusFlag (CpuData, PROCESSOR_ENABLED_BIT)) { | |
mMpSystemData.NumberOfEnabledProcessors--; | |
} | |
CpuStatusFlagAndNot (CpuData, PROCESSOR_ENABLED_BIT); | |
} | |
if (HealthFlag != NULL) { | |
CpuStatusFlagAndNot (CpuData, (UINT32)~PROCESSOR_HEALTH_STATUS_BIT); | |
CpuStatusFlagOr (CpuData, (*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT)); | |
} | |
if (TempStopCheckState) { | |
mStopCheckAllAPsStatus = FALSE; | |
} | |
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[out] 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 | |
WhoAmI ( | |
IN EFI_MP_SERVICES_PROTOCOL *This, | |
OUT UINTN *ProcessorNumber | |
) | |
{ | |
UINTN Index; | |
UINT32 ProcessorId; | |
if (ProcessorNumber == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
ProcessorId = GetApicId (); | |
for (Index = 0; Index < mMpSystemData.NumberOfProcessors; Index++) { | |
if (mMpSystemData.CpuDatas[Index].Info.ProcessorId == ProcessorId) { | |
break; | |
} | |
} | |
*ProcessorNumber = Index; | |
return EFI_SUCCESS; | |
} | |
/** | |
Terminate AP's task and set it to idle state. | |
This function terminates AP's task due to timeout by sending INIT-SIPI, | |
and sends it to idle state. | |
@param CpuData the pointer to CPU_DATA_BLOCK of specified AP | |
**/ | |
VOID | |
ResetProcessorToIdleState ( | |
IN CPU_DATA_BLOCK *CpuData | |
) | |
{ | |
ResetApStackless ((UINT32)CpuData->Info.ProcessorId); | |
} | |
/** | |
Application Processors do loop routine | |
after switch to its own stack. | |
@param Context1 A pointer to the context to pass into the function. | |
@param Context2 A pointer to the context to pass into the function. | |
**/ | |
VOID | |
ProcessorToIdleState ( | |
IN VOID *Context1, OPTIONAL | |
IN VOID *Context2 OPTIONAL | |
) | |
{ | |
UINTN ProcessorNumber; | |
CPU_DATA_BLOCK *CpuData; | |
EFI_AP_PROCEDURE Procedure; | |
VOID *ProcedureArgument; | |
AsmApDoneWithCommonStack (); | |
while (!mAPsAlreadyInitFinished) { | |
CpuPause (); | |
} | |
WhoAmI (&mMpServicesTemplate, &ProcessorNumber); | |
CpuData = &mMpSystemData.CpuDatas[ProcessorNumber]; | |
// | |
// Avoid forcibly reset AP caused the AP got lock not release. | |
// | |
if (CpuData->LockSelf == (INTN) GetApicId ()) { | |
ReleaseSpinLock (&CpuData->CpuDataLock); | |
} | |
// | |
// Avoid forcibly reset AP caused the AP State is not updated. | |
// | |
GetMpSpinLock (CpuData); | |
CpuData->State = CpuStateIdle; | |
CpuData->Procedure = NULL; | |
ReleaseMpSpinLock (CpuData); | |
while (TRUE) { | |
GetMpSpinLock (CpuData); | |
ProcedureArgument = CpuData->Parameter; | |
Procedure = CpuData->Procedure; | |
ReleaseMpSpinLock (CpuData); | |
if (Procedure != NULL) { | |
Procedure (ProcedureArgument); | |
GetMpSpinLock (CpuData); | |
CpuData->Procedure = NULL; | |
CpuData->State = CpuStateFinished; | |
ReleaseMpSpinLock (CpuData); | |
} | |
CpuPause (); | |
} | |
CpuSleep (); | |
CpuDeadLoop (); | |
} | |
/** | |
Checks AP' status periodically. | |
This function is triggerred by timer perodically to check the | |
state of AP forStartupThisAP() executed in non-blocking mode. | |
@param Event Event triggered. | |
@param Context Parameter passed with the event. | |
**/ | |
VOID | |
EFIAPI | |
CheckThisAPStatus ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
CPU_DATA_BLOCK *CpuData; | |
CPU_STATE CpuState; | |
CpuData = (CPU_DATA_BLOCK *) Context; | |
if (CpuData->TimeoutActive) { | |
CpuData->Timeout -= gPollInterval; | |
} | |
CpuState = GetApState (CpuData); | |
if (CpuState == CpuStateFinished) { | |
if (CpuData->Finished) { | |
*CpuData->Finished = TRUE; | |
} | |
SetApState (CpuData, CpuStateIdle); | |
goto out; | |
} | |
if (CpuData->TimeoutActive && CpuData->Timeout < 0) { | |
if (CpuState != CpuStateIdle && | |
CpuData->Finished) { | |
*CpuData->Finished = FALSE; | |
} | |
ResetProcessorToIdleState (CpuData); | |
goto out; | |
} | |
return; | |
out: | |
CpuData->TimeoutActive = FALSE; | |
gBS->SignalEvent (CpuData->WaitEvent); | |
CpuData->WaitEvent = NULL; | |
} | |
/** | |
Checks APs' status periodically. | |
This function is triggerred by timer perodically to check the | |
state of APs for StartupAllAPs() executed in non-blocking mode. | |
@param Event Event triggered. | |
@param Context Parameter passed with the event. | |
**/ | |
VOID | |
EFIAPI | |
CheckAllAPsStatus ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
CPU_DATA_BLOCK *CpuData; | |
UINTN Number; | |
EFI_STATUS Status; | |
if (mMpSystemData.TimeoutActive) { | |
mMpSystemData.Timeout -= gPollInterval; | |
} | |
if (mStopCheckAllAPsStatus) { | |
return; | |
} | |
// | |
// avoid next timer enter. | |
// | |
Status = gBS->SetTimer ( | |
mMpSystemData.CheckAllAPsEvent, | |
TimerCancel, | |
0 | |
); | |
ASSERT_EFI_ERROR (Status); | |
if (mMpSystemData.WaitEvent != NULL) { | |
CheckAndUpdateAllAPsToIdleState (); | |
// | |
// task timeout | |
// | |
if (mMpSystemData.TimeoutActive && mMpSystemData.Timeout < 0) { | |
ResetAllFailedAPs(); | |
// | |
// force exit | |
// | |
mMpSystemData.FinishCount = mMpSystemData.StartCount; | |
} | |
if (mMpSystemData.FinishCount != mMpSystemData.StartCount) { | |
goto EXIT; | |
} | |
mMpSystemData.TimeoutActive = FALSE; | |
gBS->SignalEvent (mMpSystemData.WaitEvent); | |
mMpSystemData.WaitEvent = NULL; | |
mStopCheckAllAPsStatus = TRUE; | |
goto EXIT; | |
} | |
// | |
// check each AP status for StartupThisAP | |
// | |
for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) { | |
CpuData = &mMpSystemData.CpuDatas[Number]; | |
if (CpuData->WaitEvent) { | |
CheckThisAPStatus (NULL, (VOID *)CpuData); | |
} | |
} | |
EXIT: | |
Status = gBS->SetTimer ( | |
mMpSystemData.CheckAllAPsEvent, | |
TimerPeriodic, | |
EFI_TIMER_PERIOD_MICROSECONDS (100) | |
); | |
ASSERT_EFI_ERROR (Status); | |
} | |
/** | |
Application Processor C code entry point. | |
**/ | |
VOID | |
EFIAPI | |
ApEntryPointInC ( | |
VOID | |
) | |
{ | |
VOID* TopOfApStack; | |
UINTN ProcessorNumber; | |
if (!mAPsAlreadyInitFinished) { | |
FillInProcessorInformation (FALSE, mMpSystemData.NumberOfProcessors); | |
TopOfApStack = (UINT8*)mApStackStart + gApStackSize; | |
mApStackStart = TopOfApStack; | |
// | |
// Store the Stack address, when reset the AP, We can found the original address. | |
// | |
mMpSystemData.CpuDatas[mMpSystemData.NumberOfProcessors].TopOfStack = TopOfApStack; | |
mMpSystemData.NumberOfProcessors++; | |
mMpSystemData.NumberOfEnabledProcessors++; | |
} else { | |
WhoAmI (&mMpServicesTemplate, &ProcessorNumber); | |
// | |
// Get the original stack address. | |
// | |
TopOfApStack = mMpSystemData.CpuDatas[ProcessorNumber].TopOfStack; | |
} | |
SwitchStack ( | |
(SWITCH_STACK_ENTRY_POINT)(UINTN)ProcessorToIdleState, | |
NULL, | |
NULL, | |
TopOfApStack); | |
} | |
/** | |
This function is called by all processors (both BSP and AP) once and collects MP related data. | |
@param Bsp TRUE if the CPU is BSP | |
@param ProcessorNumber The specific processor number | |
@retval EFI_SUCCESS Data for the processor collected and filled in | |
**/ | |
EFI_STATUS | |
FillInProcessorInformation ( | |
IN BOOLEAN Bsp, | |
IN UINTN ProcessorNumber | |
) | |
{ | |
CPU_DATA_BLOCK *CpuData; | |
UINT32 ProcessorId; | |
CpuData = &mMpSystemData.CpuDatas[ProcessorNumber]; | |
ProcessorId = GetApicId (); | |
CpuData->Info.ProcessorId = ProcessorId; | |
CpuData->Info.StatusFlag = PROCESSOR_ENABLED_BIT | PROCESSOR_HEALTH_STATUS_BIT; | |
if (Bsp) { | |
CpuData->Info.StatusFlag |= PROCESSOR_AS_BSP_BIT; | |
} | |
CpuData->Info.Location.Package = ProcessorId; | |
CpuData->Info.Location.Core = 0; | |
CpuData->Info.Location.Thread = 0; | |
CpuData->State = Bsp ? CpuStateBuzy : CpuStateIdle; | |
CpuData->Procedure = NULL; | |
CpuData->Parameter = NULL; | |
InitializeSpinLock (&CpuData->CpuDataLock); | |
CpuData->LockSelf = -1; | |
return EFI_SUCCESS; | |
} | |
/** | |
Prepare the System Data. | |
@retval EFI_SUCCESS the System Data finished initilization. | |
**/ | |
EFI_STATUS | |
InitMpSystemData ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
ZeroMem (&mMpSystemData, sizeof (MP_SYSTEM_DATA)); | |
mMpSystemData.NumberOfProcessors = 1; | |
mMpSystemData.NumberOfEnabledProcessors = 1; | |
mMpSystemData.CpuDatas = AllocateZeroPool (sizeof (CPU_DATA_BLOCK) * gMaxLogicalProcessorNumber); | |
ASSERT(mMpSystemData.CpuDatas != NULL); | |
Status = gBS->CreateEvent ( | |
EVT_TIMER | EVT_NOTIFY_SIGNAL, | |
TPL_CALLBACK, | |
CheckAllAPsStatus, | |
NULL, | |
&mMpSystemData.CheckAllAPsEvent | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Set timer to check all APs status. | |
// | |
Status = gBS->SetTimer ( | |
mMpSystemData.CheckAllAPsEvent, | |
TimerPeriodic, | |
EFI_TIMER_PERIOD_MICROSECONDS (100) | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// BSP | |
// | |
FillInProcessorInformation (TRUE, 0); | |
return EFI_SUCCESS; | |
} | |
/** | |
Callback function for ExitBootServices. | |
@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 | |
ExitBootServicesCallback ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
// | |
// Avoid APs access invalid buff datas which allocated by BootServices, | |
// so we send INIT IPI to APs to let them wait for SIPI state. | |
// | |
SendInitIpiAllExcludingSelf (); | |
} | |
/** | |
Initialize Multi-processor support. | |
**/ | |
VOID | |
InitializeMpSupport ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
gMaxLogicalProcessorNumber = (UINTN) PcdGet32 (PcdCpuMaxLogicalProcessorNumber); | |
if (gMaxLogicalProcessorNumber < 1) { | |
DEBUG ((DEBUG_ERROR, "Setting PcdCpuMaxLogicalProcessorNumber should be more than zero.\n")); | |
return; | |
} | |
if (gMaxLogicalProcessorNumber == 1) { | |
return; | |
} | |
gApStackSize = (UINTN) PcdGet32 (PcdCpuApStackSize); | |
ASSERT ((gApStackSize & (SIZE_4KB - 1)) == 0); | |
mApStackStart = AllocatePages (EFI_SIZE_TO_PAGES (gMaxLogicalProcessorNumber * gApStackSize)); | |
ASSERT (mApStackStart != NULL); | |
// | |
// the first buffer of stack size used for common stack, when the amount of AP | |
// more than 1, we should never free the common stack which maybe used for AP reset. | |
// | |
mCommonStack = mApStackStart; | |
mTopOfApCommonStack = (UINT8*) mApStackStart + gApStackSize; | |
mApStackStart = mTopOfApCommonStack; | |
InitMpSystemData (); | |
PrepareAPStartupCode (); | |
StartApsStackless (); | |
DEBUG ((DEBUG_INFO, "Detect CPU count: %d\n", mMpSystemData.NumberOfProcessors)); | |
if (mMpSystemData.NumberOfProcessors == 1) { | |
FreeApStartupCode (); | |
FreePages (mCommonStack, EFI_SIZE_TO_PAGES (gMaxLogicalProcessorNumber * gApStackSize)); | |
return; | |
} | |
mMpSystemData.CpuDatas = ReallocatePool ( | |
sizeof (CPU_DATA_BLOCK) * gMaxLogicalProcessorNumber, | |
sizeof (CPU_DATA_BLOCK) * mMpSystemData.NumberOfProcessors, | |
mMpSystemData.CpuDatas); | |
mAPsAlreadyInitFinished = TRUE; | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&mMpServiceHandle, | |
&gEfiMpServiceProtocolGuid, &mMpServicesTemplate, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
if (mMpSystemData.NumberOfProcessors < gMaxLogicalProcessorNumber) { | |
FreePages (mApStackStart, EFI_SIZE_TO_PAGES ( | |
(gMaxLogicalProcessorNumber - mMpSystemData.NumberOfProcessors) * | |
gApStackSize)); | |
} | |
Status = gBS->CreateEvent ( | |
EVT_SIGNAL_EXIT_BOOT_SERVICES, | |
TPL_CALLBACK, | |
ExitBootServicesCallback, | |
NULL, | |
&mExitBootServicesEvent | |
); | |
ASSERT_EFI_ERROR (Status); | |
} |