UefiCpuPkg/CpuMpPei: Implementation of PeiStartupAllAPs ()

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Jeff Fan <jeff.fan@intel.com>
Reviewed-by: Feng Tian <feng.tian@intel.com>
Reviewed-by: Jiewen Yao <jiewen.yao@intel.com>

git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@18008 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
Jeff Fan
2015-07-15 03:43:12 +00:00
committed by vanjeff
parent bf55f5b274
commit 60ca9e8c18
4 changed files with 308 additions and 0 deletions

View File

@@ -314,6 +314,189 @@ PeiGetProcessorInfo (
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 requests only. 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 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_PEI_MP_SERVICES_PPI.StartupAllAPs()
and EFI_PEI_MP_SERVICES_PPI.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_PEI_MP_SERVICES_PPI.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 Ppi 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. PEI services and Ppis may not be called by APs unless otherwise
specified.
In blocking execution mode, BSP waits until all APs finish or
TimeoutInMicroSeconds expires.
@param[in] PeiServices An indirect pointer to the PEI Services Table
published by the PEI Foundation.
@param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance.
@param[in] Procedure A pointer to the function to be run on enabled APs of
the system.
@param[in] SingleThread If TRUE, then all the enabled APs execute the function
specified by Procedure one by one, in ascending order
of processor handle number. If FALSE, then all the
enabled APs execute the function specified by Procedure
simultaneously.
@param[in] TimeoutInMicroSeconds
Indicates the time limit in microseconds for APs to
return from Procedure, for blocking mode only. 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_PEI_MP_SERVICES_PPI.StartupAllAPs()
or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the
timeout expires in blocking mode, BSP returns
EFI_TIMEOUT.
@param[in] ProcedureArgument The parameter passed into Procedure for all APs.
@retval EFI_SUCCESS In blocking mode, all APs have finished before the
timeout expired.
@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
PeiStartupAllAPs (
IN CONST EFI_PEI_SERVICES **PeiServices,
IN EFI_PEI_MP_SERVICES_PPI *This,
IN EFI_AP_PROCEDURE Procedure,
IN BOOLEAN SingleThread,
IN UINTN TimeoutInMicroSeconds,
IN VOID *ProcedureArgument OPTIONAL
)
{
PEI_CPU_MP_DATA *PeiCpuMpData;
UINTN ProcessorNumber;
UINTN Index;
UINTN CallerNumber;
BOOLEAN HasEnabledAp;
BOOLEAN HasEnabledIdleAp;
volatile UINT32 *FinishedCount;
EFI_STATUS Status;
UINTN WaitCountIndex;
UINTN WaitCountNumber;
PeiCpuMpData = GetMpHobData ();
if (PeiCpuMpData == NULL) {
return EFI_NOT_FOUND;
}
//
// Check whether caller processor is BSP
//
PeiWhoAmI (PeiServices, This, &CallerNumber);
if (CallerNumber != PeiCpuMpData->BspNumber) {
return EFI_DEVICE_ERROR;
}
ProcessorNumber = PeiCpuMpData->CpuCount;
HasEnabledAp = FALSE;
HasEnabledIdleAp = FALSE;
for (Index = 0; Index < ProcessorNumber; Index ++) {
if (Index == CallerNumber) {
//
// Skip BSP
//
continue;
}
if (PeiCpuMpData->CpuData[Index].State != CpuStateDisabled) {
HasEnabledAp = TRUE;
if (PeiCpuMpData->CpuData[Index].State != CpuStateBusy) {
HasEnabledIdleAp = TRUE;
}
}
}
if (!HasEnabledAp) {
//
// If no enabled AP exists, return EFI_NOT_STARTED.
//
return EFI_NOT_STARTED;
}
if (!HasEnabledIdleAp) {
//
// If any enabled APs are busy, return EFI_NOT_READY.
//
return EFI_NOT_READY;
}
WaitCountNumber = TimeoutInMicroSeconds / CPU_CHECK_AP_INTERVAL + 1;
WaitCountIndex = 0;
FinishedCount = &PeiCpuMpData->FinishedCount;
if (!SingleThread) {
WakeUpAP (PeiCpuMpData, TRUE, 0, Procedure, ProcedureArgument);
//
// Wait to finish
//
if (TimeoutInMicroSeconds == 0) {
while (*FinishedCount < ProcessorNumber - 1) {
CpuPause ();
}
Status = EFI_SUCCESS;
} else {
Status = EFI_TIMEOUT;
for (WaitCountIndex = 0; WaitCountIndex < WaitCountNumber; WaitCountIndex++) {
MicroSecondDelay (CPU_CHECK_AP_INTERVAL);
if (*FinishedCount >= ProcessorNumber - 1) {
Status = EFI_SUCCESS;
break;
}
}
}
} else {
Status = EFI_SUCCESS;
for (Index = 0; Index < ProcessorNumber; Index++) {
if (Index == CallerNumber) {
continue;
}
WakeUpAP (PeiCpuMpData, FALSE, PeiCpuMpData->CpuData[Index].ApicId, Procedure, ProcedureArgument);
//
// Wait to finish
//
if (TimeoutInMicroSeconds == 0) {
while (*FinishedCount < 1) {
CpuPause ();
}
} else {
for (WaitCountIndex = 0; WaitCountIndex < WaitCountNumber; WaitCountIndex++) {
MicroSecondDelay (CPU_CHECK_AP_INTERVAL);
if (*FinishedCount >= 1) {
break;
}
}
if (WaitCountIndex == WaitCountNumber) {
Status = EFI_TIMEOUT;
}
}
}
}
return Status;
}
/**
This return the handle number for the calling processor. This service may be
called from the BSP and APs.