X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=UefiCpuPkg%2FLibrary%2FMpInitLib%2FMpLib.c;h=f205b6bad4ccff9018a593f9d4c78e03d69530c8;hp=7ae65598dc281d7cd75382bd515077ddf532cbea;hb=73152f19c0be7f31ee05f32878b515a296c487fa;hpb=6a2ee2bb63758c9e171b6c791a21bc1a68aa8429 diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.c b/UefiCpuPkg/Library/MpInitLib/MpLib.c index 7ae65598dc..f205b6bad4 100644 --- a/UefiCpuPkg/Library/MpInitLib/MpLib.c +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c @@ -57,6 +57,26 @@ IsBspExecuteDisableEnabled ( return Enabled; } +/** + Worker function for SwitchBSP(). + + Worker function for SwitchBSP(), assigned to the AP which is intended + to become BSP. + + @param[in] Buffer Pointer to CPU MP Data +**/ +VOID +EFIAPI +FutureBSPProc ( + IN VOID *Buffer + ) +{ + CPU_MP_DATA *DataInHob; + + DataInHob = (CPU_MP_DATA *) Buffer; + AsmExchangeRole (&DataInHob->APInfo, &DataInHob->BSPInfo); +} + /** Get the Application Processors state. @@ -367,10 +387,6 @@ CollectProcessorCount ( CpuMpData->InitFlag = ApInitConfig; CpuMpData->X2ApicEnable = FALSE; WakeUpAP (CpuMpData, TRUE, 0, NULL, NULL); - // - // Wait for AP task to complete and then exit. - // - MicroSecondDelay (PcdGet32(PcdCpuApInitTimeOutInMicroSeconds)); CpuMpData->InitFlag = ApInitDone; ASSERT (CpuMpData->CpuCount <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber)); // @@ -520,11 +536,20 @@ ApWakeupFunction ( // Invoke AP function here // Procedure (Parameter); - // - // Re-get the CPU APICID and Initial APICID - // - CpuMpData->CpuData[ProcessorNumber].ApicId = GetApicId (); - CpuMpData->CpuData[ProcessorNumber].InitialApicId = GetInitialApicId (); + if (CpuMpData->SwitchBspFlag) { + // + // Re-get the processor number due to BSP/AP maybe exchange in AP function + // + GetProcessorNumber (CpuMpData, &ProcessorNumber); + CpuMpData->CpuData[ProcessorNumber].ApFunction = 0; + CpuMpData->CpuData[ProcessorNumber].ApFunctionArgument = 0; + } else { + // + // Re-get the CPU APICID and Initial APICID + // + CpuMpData->CpuData[ProcessorNumber].ApicId = GetApicId (); + CpuMpData->CpuData[ProcessorNumber].InitialApicId = GetInitialApicId (); + } } SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateFinished); } @@ -708,7 +733,12 @@ WakeUpAP ( // SendInitSipiSipiAllExcludingSelf ((UINT32) ExchangeInfo->BufferStart); } - if (CpuMpData->InitFlag != ApInitConfig) { + if (CpuMpData->InitFlag == ApInitConfig) { + // + // Wait for all potential APs waken up in one specified period + // + MicroSecondDelay (PcdGet32(PcdCpuApInitTimeOutInMicroSeconds)); + } else { // // Wait all APs waken up if this is not the 1st broadcast of SIPI // @@ -746,6 +776,332 @@ WakeUpAP ( } } +/** + Calculate timeout value and return the current performance counter value. + + Calculate the number of performance counter ticks required for a timeout. + If TimeoutInMicroseconds is 0, return value is also 0, which is recognized + as infinity. + + @param[in] TimeoutInMicroseconds Timeout value in microseconds. + @param[out] CurrentTime Returns the current value of the performance counter. + + @return Expected time stamp counter for timeout. + If TimeoutInMicroseconds is 0, return value is also 0, which is recognized + as infinity. + +**/ +UINT64 +CalculateTimeout ( + IN UINTN TimeoutInMicroseconds, + OUT UINT64 *CurrentTime + ) +{ + // + // Read the current value of the performance counter + // + *CurrentTime = GetPerformanceCounter (); + + // + // If TimeoutInMicroseconds is 0, return value is also 0, which is recognized + // as infinity. + // + if (TimeoutInMicroseconds == 0) { + return 0; + } + + // + // GetPerformanceCounterProperties () returns the timestamp counter's frequency + // in Hz. So multiply the return value with TimeoutInMicroseconds and then divide + // it by 1,000,000, to get the number of ticks for the timeout value. + // + return DivU64x32 ( + MultU64x64 ( + GetPerformanceCounterProperties (NULL, NULL), + TimeoutInMicroseconds + ), + 1000000 + ); +} + +/** + Checks whether timeout expires. + + Check whether the number of elapsed performance counter ticks required for + a timeout condition has been reached. + If Timeout is zero, which means infinity, return value is always FALSE. + + @param[in, out] PreviousTime On input, the value of the performance counter + when it was last read. + On output, the current value of the performance + counter + @param[in] TotalTime The total amount of elapsed time in performance + counter ticks. + @param[in] Timeout The number of performance counter ticks required + to reach a timeout condition. + + @retval TRUE A timeout condition has been reached. + @retval FALSE A timeout condition has not been reached. + +**/ +BOOLEAN +CheckTimeout ( + IN OUT UINT64 *PreviousTime, + IN UINT64 *TotalTime, + IN UINT64 Timeout + ) +{ + UINT64 Start; + UINT64 End; + UINT64 CurrentTime; + INT64 Delta; + INT64 Cycle; + + if (Timeout == 0) { + return FALSE; + } + GetPerformanceCounterProperties (&Start, &End); + Cycle = End - Start; + if (Cycle < 0) { + Cycle = -Cycle; + } + Cycle++; + CurrentTime = GetPerformanceCounter(); + Delta = (INT64) (CurrentTime - *PreviousTime); + if (Start > End) { + Delta = -Delta; + } + if (Delta < 0) { + Delta += Cycle; + } + *TotalTime += Delta; + *PreviousTime = CurrentTime; + if (*TotalTime > Timeout) { + return TRUE; + } + return FALSE; +} + +/** + Reset an AP to Idle state. + + Any task being executed by the AP will be aborted and the AP + will be waiting for a new task in Wait-For-SIPI state. + + @param[in] ProcessorNumber The handle number of processor. +**/ +VOID +ResetProcessorToIdleState ( + IN UINTN ProcessorNumber + ) +{ + CPU_MP_DATA *CpuMpData; + + CpuMpData = GetCpuMpData (); + + WakeUpAP (CpuMpData, FALSE, ProcessorNumber, NULL, NULL); + + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle); +} + +/** + Searches for the next waiting AP. + + Search for the next AP that is put in waiting state by single-threaded StartupAllAPs(). + + @param[out] NextProcessorNumber Pointer to the processor number of the next waiting AP. + + @retval EFI_SUCCESS The next waiting AP has been found. + @retval EFI_NOT_FOUND No waiting AP exists. + +**/ +EFI_STATUS +GetNextWaitingProcessorNumber ( + OUT UINTN *NextProcessorNumber + ) +{ + UINTN ProcessorNumber; + CPU_MP_DATA *CpuMpData; + + CpuMpData = GetCpuMpData (); + + for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) { + if (CpuMpData->CpuData[ProcessorNumber].Waiting) { + *NextProcessorNumber = ProcessorNumber; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** Checks status of specified AP. + + This function checks whether the specified AP has finished the task assigned + by StartupThisAP(), and whether timeout expires. + + @param[in] ProcessorNumber The handle number of processor. + + @retval EFI_SUCCESS Specified AP has finished task assigned by StartupThisAPs(). + @retval EFI_TIMEOUT The timeout expires. + @retval EFI_NOT_READY Specified AP has not finished task and timeout has not expired. +**/ +EFI_STATUS +CheckThisAP ( + IN UINTN ProcessorNumber + ) +{ + CPU_MP_DATA *CpuMpData; + CPU_AP_DATA *CpuData; + + CpuMpData = GetCpuMpData (); + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + + // + // Check the CPU state of AP. If it is CpuStateFinished, then the AP has finished its task. + // Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the + // value of state after setting the it to CpuStateFinished, so BSP can safely make use of its value. + // + // + // If the AP finishes for StartupThisAP(), return EFI_SUCCESS. + // + if (GetApState(CpuData) == CpuStateFinished) { + if (CpuData->Finished != NULL) { + *(CpuData->Finished) = TRUE; + } + SetApState (CpuData, CpuStateIdle); + return EFI_SUCCESS; + } else { + // + // If timeout expires for StartupThisAP(), report timeout. + // + if (CheckTimeout (&CpuData->CurrentTime, &CpuData->TotalTime, CpuData->ExpectedTime)) { + if (CpuData->Finished != NULL) { + *(CpuData->Finished) = FALSE; + } + // + // Reset failed AP to idle state + // + ResetProcessorToIdleState (ProcessorNumber); + + return EFI_TIMEOUT; + } + } + return EFI_NOT_READY; +} + +/** + Checks status of all APs. + + This function checks whether all APs have finished task assigned by StartupAllAPs(), + and whether timeout expires. + + @retval EFI_SUCCESS All APs have finished task assigned by StartupAllAPs(). + @retval EFI_TIMEOUT The timeout expires. + @retval EFI_NOT_READY APs have not finished task and timeout has not expired. +**/ +EFI_STATUS +CheckAllAPs ( + VOID + ) +{ + UINTN ProcessorNumber; + UINTN NextProcessorNumber; + UINTN ListIndex; + EFI_STATUS Status; + CPU_MP_DATA *CpuMpData; + CPU_AP_DATA *CpuData; + + CpuMpData = GetCpuMpData (); + + NextProcessorNumber = 0; + + // + // Go through all APs that are responsible for the StartupAllAPs(). + // + for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) { + if (!CpuMpData->CpuData[ProcessorNumber].Waiting) { + continue; + } + + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + // + // Check the CPU state of AP. If it is CpuStateFinished, then the AP has finished its task. + // Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the + // value of state after setting the it to CpuStateFinished, so BSP can safely make use of its value. + // + if (GetApState(CpuData) == CpuStateFinished) { + CpuMpData->RunningCount ++; + CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE; + SetApState(CpuData, CpuStateIdle); + + // + // If in Single Thread mode, then search for the next waiting AP for execution. + // + if (CpuMpData->SingleThread) { + Status = GetNextWaitingProcessorNumber (&NextProcessorNumber); + + if (!EFI_ERROR (Status)) { + WakeUpAP ( + CpuMpData, + FALSE, + (UINT32) NextProcessorNumber, + CpuMpData->Procedure, + CpuMpData->ProcArguments + ); + } + } + } + } + + // + // If all APs finish, return EFI_SUCCESS. + // + if (CpuMpData->RunningCount == CpuMpData->StartCount) { + return EFI_SUCCESS; + } + + // + // If timeout expires, report timeout. + // + if (CheckTimeout ( + &CpuMpData->CurrentTime, + &CpuMpData->TotalTime, + CpuMpData->ExpectedTime) + ) { + // + // If FailedCpuList is not NULL, record all failed APs in it. + // + if (CpuMpData->FailedCpuList != NULL) { + *CpuMpData->FailedCpuList = + AllocatePool ((CpuMpData->StartCount - CpuMpData->FinishedCount + 1) * sizeof (UINTN)); + ASSERT (*CpuMpData->FailedCpuList != NULL); + } + ListIndex = 0; + + for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) { + // + // Check whether this processor is responsible for StartupAllAPs(). + // + if (CpuMpData->CpuData[ProcessorNumber].Waiting) { + // + // Reset failed APs to idle state + // + ResetProcessorToIdleState (ProcessorNumber); + CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE; + if (CpuMpData->FailedCpuList != NULL) { + (*CpuMpData->FailedCpuList)[ListIndex++] = ProcessorNumber; + } + } + } + if (CpuMpData->FailedCpuList != NULL) { + (*CpuMpData->FailedCpuList)[ListIndex] = END_OF_CPU_LIST; + } + return EFI_TIMEOUT; + } + return EFI_NOT_READY; +} + /** MP Initialize Library initialization. @@ -810,11 +1166,12 @@ MpInitLibInitialize ( CpuMpData->CpuApStackSize = ApStackSize; CpuMpData->BackupBuffer = BackupBufferAddr; CpuMpData->BackupBufferSize = ApResetVectorSize; - CpuMpData->EndOfPeiFlag = FALSE; + CpuMpData->SaveRestoreFlag = FALSE; CpuMpData->WakeupBuffer = (UINTN) -1; CpuMpData->CpuCount = 1; CpuMpData->BspNumber = 0; CpuMpData->WaitEvent = NULL; + CpuMpData->SwitchBspFlag = FALSE; CpuMpData->CpuData = (CPU_AP_DATA *) (CpuMpData + 1); CpuMpData->CpuInfoInHob = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber); InitializeSpinLock(&CpuMpData->MpLock); @@ -930,8 +1287,219 @@ MpInitLibGetProcessorInfo ( OUT EFI_HEALTH_FLAGS *HealthData OPTIONAL ) { - return EFI_UNSUPPORTED; + CPU_MP_DATA *CpuMpData; + UINTN CallerNumber; + + CpuMpData = GetCpuMpData (); + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + if (ProcessorInfoBuffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (ProcessorNumber >= CpuMpData->CpuCount) { + return EFI_NOT_FOUND; + } + + ProcessorInfoBuffer->ProcessorId = (UINT64) CpuMpData->CpuData[ProcessorNumber].ApicId; + ProcessorInfoBuffer->StatusFlag = 0; + if (ProcessorNumber == CpuMpData->BspNumber) { + ProcessorInfoBuffer->StatusFlag |= PROCESSOR_AS_BSP_BIT; + } + if (CpuMpData->CpuData[ProcessorNumber].CpuHealthy) { + ProcessorInfoBuffer->StatusFlag |= PROCESSOR_HEALTH_STATUS_BIT; + } + if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateDisabled) { + ProcessorInfoBuffer->StatusFlag &= ~PROCESSOR_ENABLED_BIT; + } else { + ProcessorInfoBuffer->StatusFlag |= PROCESSOR_ENABLED_BIT; + } + + // + // Get processor location information + // + GetProcessorLocation ( + CpuMpData->CpuData[ProcessorNumber].ApicId, + &ProcessorInfoBuffer->Location.Package, + &ProcessorInfoBuffer->Location.Core, + &ProcessorInfoBuffer->Location.Thread + ); + + if (HealthData != NULL) { + HealthData->Uint32 = CpuMpData->CpuData[ProcessorNumber].Health; + } + + return EFI_SUCCESS; +} + +/** + Worker function to switch the requested AP to be the BSP from that point onward. + + @param[in] ProcessorNumber The handle number of AP that is to become the new BSP. + @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 others Failed to switch BSP. + +**/ +EFI_STATUS +SwitchBSPWorker ( + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ) +{ + CPU_MP_DATA *CpuMpData; + UINTN CallerNumber; + CPU_STATE State; + MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr; + + CpuMpData = GetCpuMpData (); + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_SUCCESS; + } + + if (ProcessorNumber >= CpuMpData->CpuCount) { + return EFI_NOT_FOUND; + } + + // + // Check whether specified AP is disabled + // + State = GetApState (&CpuMpData->CpuData[ProcessorNumber]); + if (State == CpuStateDisabled) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether ProcessorNumber specifies the current BSP + // + if (ProcessorNumber == CpuMpData->BspNumber) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether specified AP is busy + // + if (State == CpuStateBusy) { + return EFI_NOT_READY; + } + + CpuMpData->BSPInfo.State = CPU_SWITCH_STATE_IDLE; + CpuMpData->APInfo.State = CPU_SWITCH_STATE_IDLE; + CpuMpData->SwitchBspFlag = TRUE; + + // + // Clear the BSP bit of MSR_IA32_APIC_BASE + // + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + ApicBaseMsr.Bits.BSP = 0; + AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); + + // + // Need to wakeUp AP (future BSP). + // + WakeUpAP (CpuMpData, FALSE, ProcessorNumber, FutureBSPProc, CpuMpData); + + AsmExchangeRole (&CpuMpData->BSPInfo, &CpuMpData->APInfo); + + // + // Set the BSP bit of MSR_IA32_APIC_BASE on new BSP + // + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + ApicBaseMsr.Bits.BSP = 1; + AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); + + // + // Wait for old BSP finished AP task + // + while (GetApState (&CpuMpData->CpuData[CallerNumber]) != CpuStateFinished) { + CpuPause (); + } + + CpuMpData->SwitchBspFlag = FALSE; + // + // Set old BSP enable state + // + if (!EnableOldBSP) { + SetApState (&CpuMpData->CpuData[CallerNumber], CpuStateDisabled); + } + // + // Save new BSP number + // + CpuMpData->BspNumber = (UINT32) ProcessorNumber; + + return EFI_SUCCESS; +} + +/** + Worker function to let the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + @param[in] ProcessorNumber The handle number of AP. + @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. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval others Failed to Enable/Disable AP. + +**/ +EFI_STATUS +EnableDisableApWorker ( + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ) +{ + CPU_MP_DATA *CpuMpData; + UINTN CallerNumber; + + CpuMpData = GetCpuMpData (); + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + if (ProcessorNumber == CpuMpData->BspNumber) { + return EFI_INVALID_PARAMETER; + } + + if (ProcessorNumber >= CpuMpData->CpuCount) { + return EFI_NOT_FOUND; + } + + if (!EnableAP) { + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateDisabled); + } else { + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle); + } + + if (HealthFlag != NULL) { + CpuMpData->CpuData[ProcessorNumber].CpuHealthy = + (BOOLEAN) ((*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT) != 0); + } + + return EFI_SUCCESS; } + /** This return the handle number for the calling processor. This service may be called from the BSP and APs. @@ -954,8 +1522,17 @@ MpInitLibWhoAmI ( OUT UINTN *ProcessorNumber ) { - return EFI_UNSUPPORTED; + CPU_MP_DATA *CpuMpData; + + if (ProcessorNumber == NULL) { + return EFI_INVALID_PARAMETER; + } + + CpuMpData = GetCpuMpData (); + + return GetProcessorNumber (CpuMpData, ProcessorNumber); } + /** 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 @@ -983,7 +1560,313 @@ MpInitLibGetNumberOfProcessors ( OUT UINTN *NumberOfEnabledProcessors OPTIONAL ) { - return EFI_UNSUPPORTED; + CPU_MP_DATA *CpuMpData; + UINTN CallerNumber; + UINTN ProcessorNumber; + UINTN EnabledProcessorNumber; + UINTN Index; + + CpuMpData = GetCpuMpData (); + + if ((NumberOfProcessors == NULL) && (NumberOfEnabledProcessors == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + ProcessorNumber = CpuMpData->CpuCount; + EnabledProcessorNumber = 0; + for (Index = 0; Index < ProcessorNumber; Index++) { + if (GetApState (&CpuMpData->CpuData[Index]) != CpuStateDisabled) { + EnabledProcessorNumber ++; + } + } + + if (NumberOfProcessors != NULL) { + *NumberOfProcessors = ProcessorNumber; + } + if (NumberOfEnabledProcessors != NULL) { + *NumberOfEnabledProcessors = EnabledProcessorNumber; + } + + return EFI_SUCCESS; +} + + +/** + Worker function to execute a caller provided function on all enabled APs. + + @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] WaitEvent The event created by the caller with CreateEvent() + service. + @param[in] TimeoutInMicrosecsond Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + @param[out] FailedCpuList 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. + + @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 others Failed to Startup all APs. + +**/ +EFI_STATUS +StartupAllAPsWorker ( + 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_MP_DATA *CpuMpData; + UINTN ProcessorCount; + UINTN ProcessorNumber; + UINTN CallerNumber; + CPU_AP_DATA *CpuData; + BOOLEAN HasEnabledAp; + CPU_STATE ApState; + + CpuMpData = GetCpuMpData (); + + if (FailedCpuList != NULL) { + *FailedCpuList = NULL; + } + + if (CpuMpData->CpuCount == 1) { + return EFI_NOT_STARTED; + } + + if (Procedure == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + // + // Update AP state + // + CheckAndUpdateApsStatus (); + + ProcessorCount = CpuMpData->CpuCount; + HasEnabledAp = FALSE; + // + // Check whether all enabled APs are idle. + // If any enabled AP is not idle, return EFI_NOT_READY. + // + for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) { + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + if (ProcessorNumber != CpuMpData->BspNumber) { + ApState = GetApState (CpuData); + if (ApState != CpuStateDisabled) { + HasEnabledAp = TRUE; + if (ApState != CpuStateIdle) { + // + // If any enabled APs are busy, return EFI_NOT_READY. + // + return EFI_NOT_READY; + } + } + } + } + + if (!HasEnabledAp) { + // + // If no enabled AP exists, return EFI_NOT_STARTED. + // + return EFI_NOT_STARTED; + } + + CpuMpData->StartCount = 0; + for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) { + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + CpuData->Waiting = FALSE; + if (ProcessorNumber != CpuMpData->BspNumber) { + if (CpuData->State == CpuStateIdle) { + // + // Mark this processor as responsible for current calling. + // + CpuData->Waiting = TRUE; + CpuMpData->StartCount++; + } + } + } + + CpuMpData->Procedure = Procedure; + CpuMpData->ProcArguments = ProcedureArgument; + CpuMpData->SingleThread = SingleThread; + CpuMpData->FinishedCount = 0; + CpuMpData->RunningCount = 0; + CpuMpData->FailedCpuList = FailedCpuList; + CpuMpData->ExpectedTime = CalculateTimeout ( + TimeoutInMicroseconds, + &CpuMpData->CurrentTime + ); + CpuMpData->TotalTime = 0; + CpuMpData->WaitEvent = WaitEvent; + + if (!SingleThread) { + WakeUpAP (CpuMpData, TRUE, 0, Procedure, ProcedureArgument); + } else { + for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) { + if (ProcessorNumber == CallerNumber) { + continue; + } + if (CpuMpData->CpuData[ProcessorNumber].Waiting) { + WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument); + break; + } + } + } + + Status = EFI_SUCCESS; + if (WaitEvent == NULL) { + do { + Status = CheckAllAPs (); + } while (Status == EFI_NOT_READY); + } + + return Status; +} + +/** + Worker function to let the caller get one enabled AP to execute a caller-provided + function. + + @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. + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. + @param[in] TimeoutInMicrosecsond Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + @param[out] Finished If AP returns from Procedure before the + timeout expires, its content is set to TRUE. + Otherwise, the value is set to FALSE. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before + the timeout expires. + @retval others Failed to Startup AP. + +**/ +EFI_STATUS +StartupThisAPWorker ( + 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; + CPU_MP_DATA *CpuMpData; + CPU_AP_DATA *CpuData; + UINTN CallerNumber; + + CpuMpData = GetCpuMpData (); + + if (Finished != NULL) { + *Finished = FALSE; + } + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + // + // Check whether processor with the handle specified by ProcessorNumber exists + // + if (ProcessorNumber >= CpuMpData->CpuCount) { + return EFI_NOT_FOUND; + } + + // + // Check whether specified processor is BSP + // + if (ProcessorNumber == CpuMpData->BspNumber) { + return EFI_INVALID_PARAMETER; + } + + // + // Check parameter Procedure + // + if (Procedure == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Update AP state + // + CheckAndUpdateApsStatus (); + + // + // Check whether specified AP is disabled + // + if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateDisabled) { + return EFI_INVALID_PARAMETER; + } + + // + // If WaitEvent is not NULL, execute in non-blocking mode. + // BSP saves data for CheckAPsStatus(), and returns EFI_SUCCESS. + // CheckAPsStatus() will check completion and timeout periodically. + // + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + CpuData->WaitEvent = WaitEvent; + CpuData->Finished = Finished; + CpuData->ExpectedTime = CalculateTimeout (TimeoutInMicroseconds, &CpuData->CurrentTime); + CpuData->TotalTime = 0; + + WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument); + + // + // If WaitEvent is NULL, execute in blocking mode. + // BSP checks AP's state until it finishes or TimeoutInMicrosecsond expires. + // + Status = EFI_SUCCESS; + if (WaitEvent == NULL) { + do { + Status = CheckThisAP (ProcessorNumber); + } while (Status == EFI_NOT_READY); + } + + return Status; } /** @@ -1008,3 +1891,42 @@ GetCpuMpDataFromGuidedHob ( } return CpuMpData; } + +/** + Get available system memory below 1MB by specified size. + + @param[in] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +BackupAndPrepareWakeupBuffer( + IN CPU_MP_DATA *CpuMpData + ) +{ + CopyMem ( + (VOID *) CpuMpData->BackupBuffer, + (VOID *) CpuMpData->WakeupBuffer, + CpuMpData->BackupBufferSize + ); + CopyMem ( + (VOID *) CpuMpData->WakeupBuffer, + (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress, + CpuMpData->AddressMap.RendezvousFunnelSize + ); +} + +/** + Restore wakeup buffer data. + + @param[in] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +RestoreWakeupBuffer( + IN CPU_MP_DATA *CpuMpData + ) +{ + CopyMem ( + (VOID *) CpuMpData->WakeupBuffer, + (VOID *) CpuMpData->BackupBuffer, + CpuMpData->BackupBufferSize + ); +}