[edk2] [RFC PATCH v6 14/27] UefiCpuPkg/CpuDxe: implement Mp Protocol:StartupThisAP()

Subject: [edk2] [RFC PATCH v6 14/27] UefiCpuPkg/CpuDxe: implement Mp Protocol:StartupThisAP()

From: Chen Fan <chen.fan.fnst@cn.fujitsu.com>

To: <edk2-devel@lists.sourceforge.net>

Date: 2014-10-28 01:30:07

  • 2014-10-28 01:30:07  Chen Fan   [edk2] [RFC PATCH v6 14/27] UefiCpuPkg/CpuDxe: implement Mp Protocol:StartupThisAP()
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Chen Fan 
---
 UefiCpuPkg/CpuDxe/CpuMp.c | 344 +++++++++++++++++++++++++++++++++++++++++++++-
 UefiCpuPkg/CpuDxe/CpuMp.h | 117 ++++++++++++++++
 2 files changed, 460 insertions(+), 1 deletion(-)

diff --git a/UefiCpuPkg/CpuDxe/CpuMp.c b/UefiCpuPkg/CpuDxe/CpuMp.c
index c25157a..a6d70d5 100644
--- a/UefiCpuPkg/CpuDxe/CpuMp.c
+++ b/UefiCpuPkg/CpuDxe/CpuMp.c
@@ -17,6 +17,7 @@
 
 UINTN gMaxLogicalProcessorNumber;
 UINTN gApStackSize;
+UINTN gPollInterval = 100; // 100 microseconds
 
 MP_SYSTEM_DATA mMpSystemData;
 
@@ -28,7 +29,7 @@ EFI_MP_SERVICES_PROTOCOL  mMpServicesTemplate = {
   GetNumberOfProcessors,
   GetProcessorInfo,
   NULL, // StartupAllAPs,
-  NULL, // StartupThisAP,
+  StartupThisAP,
   NULL, // SwitchBSP,
   EnableDisableAP,
   WhoAmI
@@ -83,6 +84,52 @@ GetApState (
 }
 
 /**
+  Set the Application Processors state.
+
+  @param   CpuData    The pointer to CPU_DATA_BLOCK of specified AP
+  @param   State      The AP status
+
+**/
+VOID
+SetApState (
+  IN  CPU_DATA_BLOCK   *CpuData,
+  IN  CPU_STATE        State
+  )
+{
+  while (!AcquireSpinLockOrFail (&CpuData->CpuDataLock)) {
+    CpuPause ();
+  }
+
+  CpuData->State = State;
+  ReleaseSpinLock (&CpuData->CpuDataLock);
+}
+
+/**
+  Set the Application Processor prepare to run a function specified
+  by Params.
+
+  @param CpuData           the pointer to CPU_DATA_BLOCK of specified AP
+  @param Procedure         A pointer to the function to be run on enabled APs of the system
+  @param ProcedureArgument Pointer to the optional parameter of the assigned function
+
+**/
+VOID
+SetApProcedure (
+  IN   CPU_DATA_BLOCK        *CpuData,
+  IN   EFI_AP_PROCEDURE      Procedure,
+  IN   VOID                  *ProcedureArgument
+  )
+{
+  while (!AcquireSpinLockOrFail (&CpuData->CpuDataLock)) {
+    CpuPause ();
+  }
+
+  CpuData->Parameter  = ProcedureArgument;
+  CpuData->Procedure  = Procedure;
+  ReleaseSpinLock (&CpuData->CpuDataLock);
+}
+
+/**
   Check the Application Processors Status whether contains the Flags.
 
   @param     CpuData  the pointer to CPU_DATA_BLOCK of specified AP
@@ -153,6 +200,45 @@ CpuStatusFlagAndNot (
 }
 
 /**
+  Searches for the next blocking AP.
+
+  Search for the next AP that is put in blocking state by single-threaded StartupAllAPs().
+
+  @param  NextNumber           Pointer to the processor number of the next blocking AP.
+
+  @retval EFI_SUCCESS          The next blocking AP has been found.
+  @retval EFI_NOT_FOUND        No blocking AP exists.
+
+**/
+EFI_STATUS
+GetNextBlockedNumber (
+  OUT UINTN  *NextNumber
+  )
+{
+  UINTN                 Number;
+  CPU_STATE             CpuState;
+  CPU_DATA_BLOCK        *CpuData;
+
+  for (Number = 0; Number < mMpSystemData.NumberOfProcessors; Number++) {
+    CpuData = &mMpSystemData.CpuDatas[Number];
+    if (TestCpuStatusFlag (CpuData, PROCESSOR_AS_BSP_BIT)) {
+      //
+      // Skip BSP
+      //
+      continue;
+    }
+
+    CpuState = GetApState (CpuData);
+    if (CpuState == CpuStateBlocked) {
+      *NextNumber = Number;
+      return EFI_SUCCESS;
+    }
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
   This service 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 be called from the BSP.
@@ -260,6 +346,176 @@ 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]  TimeoutInMicroseconds   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
+  )
+{
+  CPU_DATA_BLOCK        *CpuData;
+  EFI_STATUS            Status;
+
+  CpuData = NULL;
+
+  if (Finished != NULL) {
+    *Finished = FALSE;
+  }
+
+  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) != CpuStateIdle) {
+    return EFI_NOT_READY;
+  }
+
+  SetApState (CpuData, CpuStateReady);
+
+  SetApProcedure (CpuData, Procedure, ProcedureArgument);
+
+  CpuData->Timeout = TimeoutInMicroseconds;
+  CpuData->WaitEvent = WaitEvent;
+  CpuData->TimeoutActive = !!(TimeoutInMicroseconds);
+  CpuData->Finished = Finished;
+
+  if (WaitEvent != NULL) {
+    //
+    // Non Blocking
+    //
+    Status = gBS->SetTimer (
+                    CpuData->CheckThisAPEvent,
+                    TimerPeriodic,
+                    EFI_TIMER_PERIOD_MICROSECONDS (100)
+                    );
+    return Status;
+  }
+
+  //
+  // Blocking
+  //
+  while (TRUE) {
+    if (GetApState (CpuData) == CpuStateFinished) {
+      SetApState (CpuData, CpuStateIdle);
+      break;
+    }
+
+    if (CpuData->TimeoutActive && CpuData->Timeout < 0) {
+      ResetProcessorToIdleState (CpuData);
+      return EFI_TIMEOUT;
+    }
+
+    gBS->Stall (gPollInterval);
+    CpuData->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.
 
@@ -398,6 +654,22 @@ WhoAmI (
 }
 
 /**
+  Terminate AP's task and set it to idle state.
+
+  This function terminates AP's task due to timeout by sending INIT-SIPI,
+  and sends it to idle state.
+
+  @param CpuData           the pointer to CPU_DATA_BLOCK of specified AP
+
+**/
+VOID
+ResetProcessorToIdleState (
+  IN CPU_DATA_BLOCK  *CpuData
+  )
+{
+}
+
+/**
   Application Processors do loop routine
   after switch to its own stack.
 
@@ -417,6 +689,60 @@ ProcessorToIdleState (
 }
 
 /**
+  Checks AP' status periodically.
+
+  This function is triggerred by timer perodically to check the
+  state of AP forStartupThisAP() executed in non-blocking mode.
+
+  @param  Event    Event triggered.
+  @param  Context  Parameter passed with the event.
+
+**/
+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 == CpuStateFinished) {
+    if (CpuData->Finished) {
+      *CpuData->Finished = TRUE;
+    }
+    SetApState (CpuData, CpuStateIdle);
+    goto out;
+  }
+
+  if (CpuData->TimeoutActive && CpuData->Timeout < 0) {
+    if (CpuState != CpuStateIdle &&
+        CpuData->Finished) {
+      *CpuData->Finished = FALSE;
+    }
+    ResetProcessorToIdleState (CpuData);
+    goto out;
+  }
+
+  return;
+
+out:
+  gBS->SetTimer (CpuData->CheckThisAPEvent, TimerCancel, 0);
+  if (CpuData->WaitEvent) {
+    gBS->SignalEvent (CpuData->WaitEvent);
+    CpuData->WaitEvent = NULL;
+  }
+}
+
+/**
   Application Processor C code entry point.
 
 **/
@@ -487,6 +813,10 @@ InitMpSystemData (
   VOID
   )
 {
+  UINTN          ProcessorNumber;
+  CPU_DATA_BLOCK *CpuData;
+  EFI_STATUS     Status;
+
   ZeroMem (&mMpSystemData, sizeof (MP_SYSTEM_DATA));
 
   mMpSystemData.NumberOfProcessors = 1;
@@ -495,6 +825,18 @@ InitMpSystemData (
   mMpSystemData.CpuDatas = AllocateZeroPool (sizeof (CPU_DATA_BLOCK) * gMaxLogicalProcessorNumber);
   ASSERT(mMpSystemData.CpuDatas != NULL);
 
+  for (ProcessorNumber = 0; ProcessorNumber < gMaxLogicalProcessorNumber; ProcessorNumber++) {
+    CpuData = &mMpSystemData.CpuDatas[ProcessorNumber];
+    Status = gBS->CreateEvent (
+                    EVT_TIMER | EVT_NOTIFY_SIGNAL,
+                    TPL_CALLBACK,
+                    CheckThisAPStatus,
+                    (VOID *) CpuData,
+                    &CpuData->CheckThisAPEvent
+                    );
+    ASSERT_EFI_ERROR (Status);
+  }
+
   //
   // BSP
   //
diff --git a/UefiCpuPkg/CpuDxe/CpuMp.h b/UefiCpuPkg/CpuDxe/CpuMp.h
index 72489be..8c41a5a 100644
--- a/UefiCpuPkg/CpuDxe/CpuMp.h
+++ b/UefiCpuPkg/CpuDxe/CpuMp.h
@@ -96,6 +96,11 @@ typedef struct {
 
   EFI_AP_PROCEDURE               Procedure;
   VOID                           *Parameter;
+  BOOLEAN                        *Finished;
+  INTN                           Timeout;
+  EFI_EVENT                      WaitEvent;
+  BOOLEAN                        TimeoutActive;
+  EFI_EVENT                      CheckThisAPEvent;
 } CPU_DATA_BLOCK;
 
 /**
@@ -202,6 +207,104 @@ 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]  TimeoutInMicroseconds   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
+  );
+
+/**
   This service lets the caller enable or disable an AP from this point onward.
   This service may only be called from the BSP.
 
@@ -282,5 +385,19 @@ WhoAmI (
   OUT UINTN                    *ProcessorNumber
   );
 
+/**
+  Terminate AP's task and set it to idle state.
+
+  This function terminates AP's task due to timeout by sending INIT-SIPI,
+  and sends it to idle state.
+
+  @param CpuData           the pointer to CPU_DATA_BLOCK of specified AP
+
+**/
+VOID
+ResetProcessorToIdleState (
+  IN CPU_DATA_BLOCK  *CpuData
+  );
+
 #endif // _CPU_MP_H_
 
-- 
1.9.3


------------------------------------------------------------------------------
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/edk2-devel