X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=UefiCpuPkg%2FCpuMpPei%2FPeiMpServices.c;h=2f82130efd719d83f603ded1455a756a72c8ad28;hp=6b54396ca5c38a3aab7fcf7981219ca1794b86b6;hb=d11bbfff1f14239d7b78c6c522a4a6a9a00058cf;hpb=bf55f5b2749d575ea54a02d08cad4abf39abbbb8 diff --git a/UefiCpuPkg/CpuMpPei/PeiMpServices.c b/UefiCpuPkg/CpuMpPei/PeiMpServices.c index 6b54396ca5..2f82130efd 100644 --- a/UefiCpuPkg/CpuMpPei/PeiMpServices.c +++ b/UefiCpuPkg/CpuMpPei/PeiMpServices.c @@ -314,6 +314,310 @@ 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 service lets the caller get one enabled AP to execute a caller-provided + function. The caller can request the BSP to wait for the completion + of the AP. 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. + The execution is in blocking mode. The BSP waits until the AP finishes or + TimeoutInMicroSecondss expires. + + 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_PEI_MP_SERVICES_PPI.StartupAllAPs() and + EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). + + @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] 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_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @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, specified AP finished before the + timeout expires. + @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_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 +PeiStartupThisAP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL + ) +{ + PEI_CPU_MP_DATA *PeiCpuMpData; + UINTN CallerNumber; + 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; + } + + if (ProcessorNumber >= PeiCpuMpData->CpuCount) { + return EFI_NOT_FOUND; + } + + if (ProcessorNumber == PeiCpuMpData->BspNumber || Procedure == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether specified AP is disabled + // + if (PeiCpuMpData->CpuData[ProcessorNumber].State == CpuStateDisabled) { + return EFI_INVALID_PARAMETER; + } + + WaitCountNumber = TimeoutInMicroseconds / CPU_CHECK_AP_INTERVAL + 1; + WaitCountIndex = 0; + FinishedCount = &PeiCpuMpData->FinishedCount; + + WakeUpAP (PeiCpuMpData, FALSE, PeiCpuMpData->CpuData[ProcessorNumber].ApicId, Procedure, ProcedureArgument); + + // + // Wait to finish + // + if (TimeoutInMicroseconds == 0) { + while (*FinishedCount < 1) { + CpuPause() ; + } + Status = EFI_SUCCESS; + } else { + Status = EFI_TIMEOUT; + for (WaitCountIndex = 0; WaitCountIndex < WaitCountNumber; WaitCountIndex++) { + MicroSecondDelay (CPU_CHECK_AP_INTERVAL); + if (*FinishedCount >= 1) { + Status = EFI_SUCCESS; + break; + } + } + } + + return Status; +} + + /** This return the handle number for the calling processor. This service may be called from the BSP and APs.