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

Reply via email to