Note: the Ap run loop will cause IoRead32() assert in MdePkg/Library/BaseIoLibIntrinsic/IoLibGcc.c: (163)
Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Chen Fan <[email protected]> --- UefiCpuPkg/CpuDxe/CpuMp.c | 294 ++++++++++++++++++++++++++++++++++++++++++++-- UefiCpuPkg/CpuDxe/CpuMp.h | 20 ++++ 2 files changed, 306 insertions(+), 8 deletions(-) diff --git a/UefiCpuPkg/CpuDxe/CpuMp.c b/UefiCpuPkg/CpuDxe/CpuMp.c index 9b57508..5c003f8 100644 --- a/UefiCpuPkg/CpuDxe/CpuMp.c +++ b/UefiCpuPkg/CpuDxe/CpuMp.c @@ -28,12 +28,13 @@ UINTN mNumberOfProcessors; BOOLEAN mAllApsInitFinished = FALSE; UINTN mApDoneCount = 0; +UINTN gPollInterval = 100; // 100 microseconds EFI_MP_SERVICES_PROTOCOL mMpServicesTemplate = { GetNumberOfProcessors, GetProcessorInfo, NULL, // StartupAllAps, - NULL, // StartupThisAP, + StartupThisAP, NULL, // SwitchBSP, EnableDisableAP, WhoAmI @@ -67,6 +68,30 @@ GetApState ( return State; } +VOID +SetApState ( + IN CPU_DATA_BLOCK *CpuData, + IN CPU_STATE State + ) +{ + AcquireSpinLock (&CpuData->CpuDataLock); + CpuData->State = State; + ReleaseSpinLock (&CpuData->CpuDataLock); +} + +VOID +SetApProcedure ( + IN CPU_DATA_BLOCK *CpuData, + IN EFI_AP_PROCEDURE Procedure, + IN VOID *ProcedureArgument + ) +{ + AcquireSpinLock (&CpuData->CpuDataLock); + CpuData->Parameter = ProcedureArgument; + CpuData->Procedure = Procedure; + ReleaseSpinLock (&CpuData->CpuDataLock); +} + BOOLEAN TestCpuStatusFlag ( IN CPU_DATA_BLOCK *CpuData, @@ -213,6 +238,169 @@ GetProcessorInfo ( } /** + 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 +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 + ) +{ + UINTN Timeout = 0; + CPU_DATA_BLOCK *CpuData; + EFI_STATUS Status; + + if (!IsBSP ()) { + return EFI_DEVICE_ERROR; + } + + if (Procedure == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (ProcessorNumber >= mMpSystemData.NumberOfProcessors) { + return EFI_NOT_FOUND; + } + + CpuData = &mMpSystemData.CpuDatas[ProcessorNumber]; + if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) { + return EFI_INVALID_PARAMETER; + } + + if (GetApState (CpuData) != CPU_STATE_IDLE) { + return EFI_NOT_READY; + } + + SetApState (CpuData, CPU_STATE_READY); + + SetApProcedure (CpuData, Procedure, ProcedureArgument); + + CpuData->Timeout = TimeoutInMicroseconds; + CpuData->WaitEvent = WaitEvent; + CpuData->TimeoutActive = !!(TimeoutInMicroseconds); + + Timeout = TimeoutInMicroseconds; + + if (WaitEvent != NULL) { + // Non Blocking + Status = gBS->SetTimer ( + CpuData->CheckThisAPEvent, + TimerPeriodic, + EFI_TIMER_PERIOD_MICROSECONDS (100) + ); + return Status; + } + + // Blocking + while (TRUE) { + if (GetApState (CpuData) == CPU_STATE_FINISHED) { + SetApState (CpuData, CPU_STATE_IDLE); + break; + } + + if ((TimeoutInMicroseconds != 0) && (Timeout < 0)) { + SetApState (CpuData, CPU_STATE_IDLE); + return EFI_TIMEOUT; + } + + gBS->Stall (gPollInterval); + Timeout -= gPollInterval; + } + + return EFI_SUCCESS; +} + + +/** This service lets the caller enable or disable an AP from this point onward. This service may only be called from the BSP. @@ -351,6 +539,82 @@ WhoAmI ( return EFI_SUCCESS; } +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 == CPU_STATE_FINISHED) { + if (CpuData->Finished) { + *CpuData->Finished = TRUE; + } + goto out; + } + + if (CpuData->TimeoutActive && CpuData->Timeout < 0) { + if (CpuState != CPU_STATE_IDLE && + CpuData->Finished) { + *CpuData->Finished = FALSE; + } + goto out; + } + + return; + +out: + gBS->SetTimer (CpuData->CheckThisAPEvent, TimerCancel, 0); + if (CpuData->WaitEvent) { + gBS->SignalEvent (CpuData->WaitEvent); + CpuData->WaitEvent = NULL; + } + SetApState (CpuData, CPU_STATE_IDLE); + + return; +} + +VOID +EFIAPI +ApDoLoop ( + IN VOID *Context + ) +{ + CPU_DATA_BLOCK *CpuData; + EFI_AP_PROCEDURE Procedure; + VOID *ProcedureArgument; + + CpuData = (CPU_DATA_BLOCK *)Context; + + while (TRUE) { + AcquireSpinLock (&CpuData->CpuDataLock); + ProcedureArgument = CpuData->Parameter; + Procedure = CpuData->Procedure; + ReleaseSpinLock (&CpuData->CpuDataLock); + + if (Procedure != NULL) { + SetApState (CpuData, CPU_STATE_BUSY); + + Procedure (ProcedureArgument); + + AcquireSpinLock (&CpuData->CpuDataLock); + CpuData->Procedure = NULL; + ReleaseSpinLock (&CpuData->CpuDataLock); + + SetApState (CpuData, CPU_STATE_FINISHED); + } + } +} VOID @@ -361,8 +625,8 @@ ProcessorToIdleState ( { CPU_DATA_BLOCK *CpuData; - DEBUG ((DEBUG_INFO, "detect Apic id: %d\n", - GetApicId())); + ASSERT (mIndexOfProcessors < mNumberOfProcessors); + CpuData = &mMpSystemData.CpuDatas[mIndexOfProcessors]; CpuData->Info.ProcessorId = GetApicId (); CpuData->Info.Location.Package = (UINT32) mIndexOfProcessors; @@ -373,6 +637,9 @@ ProcessorToIdleState ( mApDoneCount++; AsmApReleaseLock (); + ApDoLoop ((VOID *)CpuData); + + //ASSERT (FALSE); CpuDeadLoop (); } @@ -393,7 +660,7 @@ ApEntryPointInC ( (SWITCH_STACK_ENTRY_POINT)(UINTN) ProcessorToIdleState, NULL, NULL, - mApStackStart + (VOID *)(UINTN) mApStackStart ); /* never be here */ @@ -408,6 +675,7 @@ InitMpSystemData ( { UINTN CpuIndex; CPU_DATA_BLOCK *CpuData; + EFI_STATUS Status; for (CpuIndex = 0; CpuIndex < mMpSystemData.NumberOfProcessors; CpuIndex++) { @@ -418,6 +686,15 @@ InitMpSystemData ( CpuData->Info.ProcessorId = GetApicId (); } InitializeSpinLock (&CpuData->CpuDataLock); + + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + CheckThisAPStatus, + (VOID *) CpuData, + &CpuData->CheckThisAPEvent + ); + ASSERT_EFI_ERROR (Status); } return EFI_SUCCESS; @@ -457,10 +734,7 @@ InitializeMpSupport ( mApStackStart = AllocatePages (EFI_SIZE_TO_PAGES ((mNumberOfProcessors - 1) * AP_STACK_SIZE)); mApStackSize = AP_STACK_SIZE; - mAllApsInitFinished = TRUE; - - while (mApDoneCount != (mNumberOfProcessors - 1)); - + /* Initialize SpinLock should before AP do loop */ InitMpSystemData (); Status = gBS->InstallMultipleProtocolInterfaces ( @@ -470,6 +744,10 @@ InitializeMpSupport ( ); ASSERT_EFI_ERROR (Status); + mAllApsInitFinished = TRUE; + + while (mApDoneCount != (mNumberOfProcessors - 1)); + EXIT: mTopOfApCommonStack = NULL; FreePages (mCommonStack, EFI_SIZE_TO_PAGES (AP_STACK_SIZE)); diff --git a/UefiCpuPkg/CpuDxe/CpuMp.h b/UefiCpuPkg/CpuDxe/CpuMp.h index 4c6d935..b274312 100644 --- a/UefiCpuPkg/CpuDxe/CpuMp.h +++ b/UefiCpuPkg/CpuDxe/CpuMp.h @@ -87,6 +87,18 @@ GetProcessorInfo ( 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 + ); + +EFI_STATUS +EFIAPI EnableDisableAP ( IN EFI_MP_SERVICES_PROTOCOL *This, IN UINTN ProcessorNumber, @@ -116,6 +128,14 @@ typedef struct { EFI_PROCESSOR_INFORMATION Info; SPIN_LOCK CpuDataLock; CPU_STATE volatile State; + + EFI_AP_PROCEDURE Procedure; + VOID *Parameter; + BOOLEAN *Finished; + UINTN Timeout; + EFI_EVENT WaitEvent; + BOOLEAN TimeoutActive; + EFI_EVENT CheckThisAPEvent; } CPU_DATA_BLOCK; // -- 1.9.3 ------------------------------------------------------------------------------ Want excitement? Manually upgrade your production database. When you want reliability, choose Perforce Perforce version control. Predictably reliable. http://pubads.g.doubleclick.net/gampad/clk?id=157508191&iu=/4140/ostg.clktrk _______________________________________________ edk2-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/edk2-devel
