Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Chen Fan <chen.fan.f...@cn.fujitsu.com> --- UefiCpuPkg/CpuDxe/CpuMp.c | 232 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 231 insertions(+), 1 deletion(-)
diff --git a/UefiCpuPkg/CpuDxe/CpuMp.c b/UefiCpuPkg/CpuDxe/CpuMp.c index f39e2db..7623a35 100644 --- a/UefiCpuPkg/CpuDxe/CpuMp.c +++ b/UefiCpuPkg/CpuDxe/CpuMp.c @@ -22,6 +22,15 @@ CPU_MP_SERVICE_DATA *mMpServiceData; UINTN mCpuIndex = 0; EFI_HANDLE mMpServiceHandle = NULL; +UINTN gPollInterval = 100; // 100us + +VOID +SendCallFuncIpi ( + IN UINT64 cpu + ) +{ + SendFixedIpi (cpu, CALL_FUNCTION_VECTOR); +} VOID SetApState ( @@ -226,6 +235,173 @@ MpServicesGetProcessorInfo ( } /** + 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 +MpServicesStartupThisAP ( + 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; + PROCESSOR_DATA_BLOCK *ProcessorData; + UINT64 ProcessorId; + EFI_STATUS Status; + + if (!IsBSP ()) { + return EFI_DEVICE_ERROR; + } + + if (Procedure == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (ProcessorNumber >= mMpServiceData->NumberOfProcessors) { + return EFI_NOT_FOUND; + } + + ProcessorData = mMpServiceData->ProcessorData[ProcessorNumber]; + if (TestProcessorStatusFlag (ProcessorData, PROCESSOR_AS_BSP_BIT)) { + return EFI_INVALID_PARAMETER; + } + + if (GetApState (ProcessorData) != CPU_STATE_IDLE) { + return EFI_NOT_READY; + } + + ProcessorId = ProcessorData->Info.ProcessorId; + + SetApProcedure (ProcessorData, Procedure, ProcedureArgument); + + SetApState (ProcessorData, CPU_STATE_READY); + SendCallFuncIpi (ProcessorId); + SetApState (ProcessorData, CPU_STATE_BUSY); + + ProcessorData->MpData.Timeout = TimeoutInMicroseconds; + ProcessorData->MpData.WaitEvent = WaitEvent; + ProcessorData->MpData.TimeoutActive = (BOOLEAN)(TimeoutInMicroseconds != 0); + + Timeout = TimeoutInMicroseconds; + + if (WaitEvent != NULL) { + // Non Blocking + Status = gBS->SetTimer ( + ProcessorData->CheckThisAPEvent, + TimerPeriodic, + EFI_TIMER_PERIOD_MICROSECONDS (1) + ); + return Status; + } + + // Blocking + while (TRUE) { + if (GetApState (ProcessorData) == CPU_STATE_FINISHED) { + SetApState (ProcessorData, CPU_STATE_IDLE); + break; + } + + if ((TimeoutInMicroseconds != 0) && (Timeout < 0)) { + SetApState (ProcessorData, 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. @@ -363,12 +539,53 @@ MpServicesWhoAmI ( return EFI_SUCCESS; } +VOID +EFIAPI +CpuCheckThisAPStatus ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + PROCESSOR_DATA_BLOCK *ProcessorData; + PROCESSOR_STATE ProcessorState; + + ProcessorData = (PROCESSOR_DATA_BLOCK *) Context; + if (ProcessorData->MpData.TimeoutActive) { + ProcessorData->MpData.Timeout -= gPollInterval; + } + + ProcessorState = GetApState (ProcessorData); + + if (ProcessorState == CPU_STATE_FINISHED) { + if (ProcessorData->Finished) { + *ProcessorData->Finished = TRUE; + } + goto out; + } + + if (ProcessorData->MpData.TimeoutActive && ProcessorData->MpData.Timeout < 0) { + if (ProcessorState != CPU_STATE_IDLE && + ProcessorData->Finished) { + *ProcessorData->Finished = FALSE; + } + goto out; + } + + return; + +out: + gBS->SetTimer (ProcessorData->CheckThisAPEvent, TimerCancel, 0); + gBS->SignalEvent (ProcessorData->MpData.WaitEvent); + SetApState (ProcessorData, CPU_STATE_IDLE); + + return; +} EFI_MP_SERVICES_PROTOCOL mMpServicesTemplate = { MpServicesGetNumberOfProcessors, MpServicesGetProcessorInfo, NULL, // MpServicesStartupAllAps, - NULL, // MpServicesStartupThisAP, + MpServicesStartupThisAP, NULL, // MpServicesSwitchBSP, MpServicesEnableDisableAP, MpServicesWhoAmI @@ -400,6 +617,15 @@ InitMpSystemData ( ProcessorData->MpData.Procedure = NULL; ProcessorData->MpData.Parameter = NULL; InitializeSpinLock (&ProcessorData->MpData.ProcedureLock); + + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + CpuCheckThisAPStatus, + (VOID *) ProcessorData, + &ProcessorData->CheckThisAPEvent + ); + ASSERT_EFI_ERROR (Status); } Status = gBS->InstallMultipleProtocolInterfaces ( @@ -456,6 +682,8 @@ CallFunctionInterrupt ( ProcessorId = GetApicId(); + DisableInterrupts (); + for (CpuIndex = 0; CpuIndex < mMpServiceData->NumberOfProcessors; CpuIndex++) { if (mMpServiceData->ProcessorData[CpuIndex]->Info.ProcessorId == ProcessorId) { ProcessorData = mMpServiceData->ProcessorData[CpuIndex]; @@ -470,6 +698,8 @@ CallFunctionInterrupt ( SetApProcedure (ProcessorData, NULL, NULL); SetApState (ProcessorData, CPU_STATE_FINISHED); } + + EnableInterrupts (); SendApicEoi (); } -- 1.9.3 ------------------------------------------------------------------------------ Infragistics Professional Build stunning WinForms apps today! Reboot your WinForms applications with our WinForms controls. Build a bridge from your legacy apps to the future. http://pubads.g.doubleclick.net/gampad/clk?id=153845071&iu=/4140/ostg.clktrk _______________________________________________ edk2-devel mailing list edk2-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/edk2-devel