Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Chen Fan <chen.fan.f...@cn.fujitsu.com>
---
 UefiCpuPkg/CpuDxe/ApStartup.c |    9 +-
 UefiCpuPkg/CpuDxe/CpuDxe.inf  |    4 +
 UefiCpuPkg/CpuDxe/CpuMp.c     | 1126 ++++++++++++++++++++++++++++++++++++++++-
 UefiCpuPkg/CpuDxe/CpuMp.h     |   62 ++-
 UefiCpuPkg/UefiCpuPkg.dec     |    2 +-
 5 files changed, 1195 insertions(+), 8 deletions(-)

diff --git a/UefiCpuPkg/CpuDxe/ApStartup.c b/UefiCpuPkg/CpuDxe/ApStartup.c
index 27abe7a..de08dd9 100644
--- a/UefiCpuPkg/CpuDxe/ApStartup.c
+++ b/UefiCpuPkg/CpuDxe/ApStartup.c
@@ -149,7 +149,8 @@ STARTUP_CODE mStartupCodeTemplate = {
 
 EFI_STATUS
 StartApsStackless (
-  IN STACKLESS_AP_ENTRY_POINT ApEntryPoint
+  IN STACKLESS_AP_ENTRY_POINT ApEntryPoint,
+  OUT UINTN                   *CountCPUs
   )
 {
   EFI_STATUS            Status;
@@ -202,14 +203,12 @@ StartApsStackless (
     Status = EFI_TIMEOUT;
   }
 
+  *CountCPUs = CurrentCPUs;
+
   DEBUG ((EFI_D_INFO, "Found CPU Count: %d\n", CurrentCPUs));
 
   gBS->FreePages (StartAddress, EFI_SIZE_TO_PAGES (sizeof (*StartupCode)));
 
-  if (CurrentCPUs == 1) {
-    return EFI_SUCCESS;
-  }
-
   return Status;
 }
 
diff --git a/UefiCpuPkg/CpuDxe/CpuDxe.inf b/UefiCpuPkg/CpuDxe/CpuDxe.inf
index cb20425..143531b 100644
--- a/UefiCpuPkg/CpuDxe/CpuDxe.inf
+++ b/UefiCpuPkg/CpuDxe/CpuDxe.inf
@@ -65,11 +65,15 @@
 
 [Protocols]
   gEfiCpuArchProtocolGuid
+  gEfiMpServiceProtocolGuid
 
 [Guids]
   gIdleLoopEventGuid                            ## CONSUMES ## GUID
   gEfiVectorHandoffTableGuid                    ## CONSUMES ## Configuration 
Table
 
+[Pcd]
+  gUefiCpuPkgTokenSpaceGuid.PcdCpuMpServicesPollingInterval
+
 [Depex]
   TRUE
 
diff --git a/UefiCpuPkg/CpuDxe/CpuMp.c b/UefiCpuPkg/CpuDxe/CpuMp.c
index e46de49..a5b0742 100644
--- a/UefiCpuPkg/CpuDxe/CpuMp.c
+++ b/UefiCpuPkg/CpuDxe/CpuMp.c
@@ -24,6 +24,10 @@ IA32_DESCRIPTOR       gIdtr;
 extern UINT32 ProcessorIdx;
 extern UINT32 ProcessorIds[];
 
+MP_SYSTEM_DATA                gMPSystem;
+EFI_HANDLE                    mpServiceHandle = NULL;
+UINTN                         gPollInterval;
+
 VOID
 EFIAPI
 ApEntryPointInit (
@@ -54,12 +58,1117 @@ ApEntryPointInit (
   ProcessorIdx++;
 }
 
+VOID
+SendCallFuncIpi(
+  IN UINT64  cpu
+  )
+{
+   SendFixedIpi(cpu, CALL_FUNCTION_VECTOR);
+}
+
+PROCESSOR_STATE
+GetApState (
+  IN  PROCESSOR_DATA_BLOCK  *Processor
+  )
+{
+  PROCESSOR_STATE       State;
+
+  while (!AcquireSpinLockOrFail (&Processor->ProcedureLock)) {
+    CpuPause();
+  }
+  State = Processor->State;
+  ReleaseSpinLock (&Processor->ProcedureLock);
+
+  return State;
+}
+
+VOID
+SetApState (
+  IN  PROCESSOR_DATA_BLOCK   *Processor,
+  IN  PROCESSOR_STATE        State
+  )
+{
+  while (!AcquireSpinLockOrFail (&Processor->ProcedureLock)) {
+    CpuPause();
+  }
+  Processor->State = State;
+  ReleaseSpinLock (&Processor->ProcedureLock);
+}
+
+VOID
+SetApProcedure (
+  IN   PROCESSOR_DATA_BLOCK  *Processor,
+  IN   EFI_AP_PROCEDURE      Procedure,
+  IN   VOID                  *ProcedureArgument
+  )
+{
+  while (!AcquireSpinLockOrFail (&Processor->ProcedureLock)) {
+    CpuPause();
+  }
+  Processor->Parameter  = ProcedureArgument;
+  Processor->Procedure  = Procedure;
+  ReleaseSpinLock (&Processor->ProcedureLock);
+}
+
+
+BOOLEAN
+IsBSP (
+  VOID
+  )
+{
+  UINTN ApicId;
+
+  ApicId = GetApicId();
+  if (ApicId == 0) {
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+/**
+  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
+CpuMpServicesStartupThisAP (
+  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
+  )
+{
+  INTN            Timeout;
+  PROCESSOR_DATA_BLOCK  *ProcessorData;
+  UINT64                ProcessorId;
+
+  if (!IsBSP ()) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  if (Procedure == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (ProcessorNumber >=  gMPSystem.NumberOfProcessors) {
+    return EFI_NOT_FOUND;
+  }
+
+  if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & 
PROCESSOR_AS_BSP_BIT) != 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (gMPSystem.ProcessorData[ProcessorNumber].State != CPU_STATE_IDLE) {
+    return EFI_NOT_READY;
+  }
+
+  ProcessorData = &gMPSystem.ProcessorData[ProcessorNumber];
+
+  ProcessorId = ProcessorData->Info.ProcessorId;
+
+  SetApProcedure(ProcessorData, Procedure, ProcedureArgument);
+
+  SetApState(ProcessorData, CPU_STATE_READY);
+  SendCallFuncIpi(ProcessorId);
+  SetApState(ProcessorData, CPU_STATE_BUSY);
+
+  ProcessorData->Timeout = TimeoutInMicroseconds;
+  ProcessorData->WaitEvent = WaitEvent;
+  ProcessorData->TimeoutActive = (BOOLEAN)(TimeoutInMicroseconds != 0);
+
+  if (WaitEvent != NULL) {
+    // Non Blocking
+    gBS->SetTimer (
+           gMPSystem.ProcessorData[ProcessorNumber].CheckThisAPEvent,
+           TimerPeriodic,
+           gPollInterval
+           );
+    return EFI_SUCCESS;
+  }
+
+  // Blocking
+  while (TRUE) {
+    if (GetApState(&gMPSystem.ProcessorData[ProcessorNumber]) == 
CPU_STATE_FINISHED) {
+      SetApState(&gMPSystem.ProcessorData[ProcessorNumber], CPU_STATE_IDLE);
+      break;
+    }
+
+    if ((TimeoutInMicroseconds != 0) && (ProcessorData->Timeout < 0)) {
+      return EFI_TIMEOUT;
+    }
+
+    gBS->Stall (gPollInterval);
+    Timeout -= gPollInterval;
+  }
+
+
+  return EFI_SUCCESS;
+}
+
+/**
+  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.
+
+  This function is used to retrieve the following information:
+    - The number of logical processors that are present in the system.
+    - The number of enabled logical processors in the system at the instant
+      this call is made.
+
+  Because MP Service Protocol provides services to enable and disable 
processors
+  dynamically, the number of enabled logical processors may vary during the
+  course of a boot session.
+
+  If this service is called from an AP, then EFI_DEVICE_ERROR is returned.
+  If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then
+  EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors
+  is returned in NumberOfProcessors, the number of currently enabled processor
+  is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned.
+
+  @param[in]  This                        A pointer to the 
EFI_MP_SERVICES_PROTOCOL
+                                          instance.
+  @param[out] NumberOfProcessors          Pointer to the total number of 
logical
+                                          processors in the system, including 
the BSP
+                                          and disabled APs.
+  @param[out] NumberOfEnabledProcessors   Pointer to the number of enabled 
logical
+                                          processors that exist in system, 
including
+                                          the BSP.
+
+  @retval EFI_SUCCESS             The number of logical processors and enabled
+                                  logical processors was retrieved.
+  @retval EFI_DEVICE_ERROR        The calling processor is an AP.
+  @retval EFI_INVALID_PARAMETER   NumberOfProcessors is NULL.
+  @retval EFI_INVALID_PARAMETER   NumberOfEnabledProcessors is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesGetNumberOfProcessors (
+  IN  EFI_MP_SERVICES_PROTOCOL  *This,
+  OUT UINTN                     *NumberOfProcessors,
+  OUT UINTN                     *NumberOfEnabledProcessors
+  )
+{
+  if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (!IsBSP ()) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  *NumberOfProcessors        = gMPSystem.NumberOfProcessors;
+  *NumberOfEnabledProcessors = gMPSystem.NumberOfEnabledProcessors;
+  return EFI_SUCCESS;
+}
+
+/**
+  Gets detailed MP-related information on the requested processor at the
+  instant this call is made. This service may only be called from the BSP.
+
+  This service retrieves detailed MP-related information about any processor
+  on the platform. Note the following:
+    - The processor information may change during the course of a boot session.
+    - The information presented here is entirely MP related.
+
+  Information regarding the number of caches and their sizes, frequency of 
operation,
+  slot numbers is all considered platform-related information and is not 
provided
+  by this service.
+
+  @param[in]  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL
+                                    instance.
+  @param[in]  ProcessorNumber       The handle number of processor.
+  @param[out] ProcessorInfoBuffer   A pointer to the buffer where information 
for
+                                    the requested processor is deposited.
+
+  @retval EFI_SUCCESS             Processor information was returned.
+  @retval EFI_DEVICE_ERROR        The calling processor is an AP.
+  @retval EFI_INVALID_PARAMETER   ProcessorInfoBuffer is NULL.
+  @retval EFI_NOT_FOUND           The processor with the handle specified by
+                                  ProcessorNumber does not exist in the 
platform.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesGetProcessorInfo (
+  IN  EFI_MP_SERVICES_PROTOCOL   *This,
+  IN  UINTN                      ProcessorNumber,
+  OUT EFI_PROCESSOR_INFORMATION  *ProcessorInfoBuffer
+  )
+{
+  if (ProcessorInfoBuffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (!IsBSP ()) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {
+    return EFI_NOT_FOUND;
+  }
+
+  CopyMem (ProcessorInfoBuffer, &gMPSystem.ProcessorData[ProcessorNumber], 
sizeof (EFI_PROCESSOR_INFORMATION));
+  return EFI_SUCCESS;
+}
+
+/**
+  This return the handle number for the calling processor.  This service may be
+  called from the BSP and APs.
+
+  This service returns the processor handle number for the calling processor.
+  The returned value is in the range from 0 to the total number of logical
+  processors minus 1. The total number of logical processors can be retrieved
+  with EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). This service may be
+  called from the BSP and APs. If ProcessorNumber is NULL, then 
EFI_INVALID_PARAMETER
+  is returned. Otherwise, the current processors handle number is returned in
+  ProcessorNumber, and EFI_SUCCESS is returned.
+
+  @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL 
instance.
+  @param[in] ProcessorNumber   The handle number of AP that is to become the 
new
+                               BSP. 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().
+
+  @retval EFI_SUCCESS             The current processor handle number was 
returned
+                                  in ProcessorNumber.
+  @retval EFI_INVALID_PARAMETER   ProcessorNumber is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesWhoAmI (
+  IN EFI_MP_SERVICES_PROTOCOL  *This,
+  OUT UINTN                    *ProcessorNumber
+  )
+{
+  UINTN   Index;
+  UINT32  ProcessorId;
+
+  if (ProcessorNumber == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ProcessorId = GetApicId();
+  for (Index = 0; Index < gMPSystem.NumberOfProcessors; Index++) {
+    if (gMPSystem.ProcessorData[Index].Info.ProcessorId == ProcessorId) {
+       break;
+    }
+  }
+
+  *ProcessorNumber = Index;
+  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.
+
+  This service allows the caller enable or disable an AP from this point 
onward.
+  The caller can optionally specify the health status of the AP by Health. If
+  an AP is being disabled, then the state of the disabled AP is implementation
+  dependent. If an AP is enabled, then the implementation must guarantee that a
+  complete initialization sequence is performed on the AP, so the AP is in a 
state
+  that is compatible with an MP operating system. This service may not be 
supported
+  after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled.
+
+  If the enable or disable AP operation cannot be completed prior to the return
+  from this service, then EFI_UNSUPPORTED must be returned.
+
+  @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL 
instance.
+  @param[in] ProcessorNumber   The handle number of AP that is to become the 
new
+                               BSP. 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] 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. This flag
+                               corresponds to StatusFlag defined in
+                               EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). 
Only
+                               the PROCESSOR_HEALTH_STATUS_BIT is used. All 
other
+                               bits are ignored.  If it is NULL, this parameter
+                               is ignored.
+
+  @retval EFI_SUCCESS             The specified AP was enabled or disabled 
successfully.
+  @retval EFI_UNSUPPORTED         Enabling or disabling an AP cannot be 
completed
+                                  prior to this service returning.
+  @retval EFI_UNSUPPORTED         Enabling or disabling an AP is not supported.
+  @retval EFI_DEVICE_ERROR        The calling processor is an AP.
+  @retval EFI_NOT_FOUND           Processor with the handle specified by 
ProcessorNumber
+                                  does not exist.
+  @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the BSP.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesEnableDisableAP (
+  IN  EFI_MP_SERVICES_PROTOCOL  *This,
+  IN  UINTN                     ProcessorNumber,
+  IN  BOOLEAN                   EnableAP,
+  IN  UINT32                    *HealthFlag OPTIONAL
+  )
+{
+  if (!IsBSP ()) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {
+    return EFI_NOT_FOUND;
+  }
+
+  if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & 
PROCESSOR_AS_BSP_BIT) != 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (gMPSystem.ProcessorData[ProcessorNumber].State != CPU_STATE_IDLE) {
+    return EFI_UNSUPPORTED;
+  }
+
+  while (! AcquireSpinLockOrFail 
(&gMPSystem.ProcessorData[ProcessorNumber].StateLock)) {
+    CpuPause();
+  }
+
+  if (EnableAP) {
+    if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & 
PROCESSOR_ENABLED_BIT) == 0 ) {
+      gMPSystem.NumberOfEnabledProcessors++;
+    }
+    gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag |= 
PROCESSOR_ENABLED_BIT;
+  } else {
+    if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & 
PROCESSOR_ENABLED_BIT) == PROCESSOR_ENABLED_BIT ) {
+      gMPSystem.NumberOfEnabledProcessors--;
+    }
+    gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag &= 
~PROCESSOR_ENABLED_BIT;
+  }
+
+  if (HealthFlag != NULL) {
+    gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag &= 
~PROCESSOR_HEALTH_STATUS_BIT;
+    gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag |= (*HealthFlag & 
PROCESSOR_HEALTH_STATUS_BIT);
+  }
+
+  ReleaseSpinLock (&gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+GetNextBlockedNumber (
+  OUT UINTN                               *NextNumber
+  )
+{
+  UINTN                 Number;
+  PROCESSOR_STATE       ProcessorState;
+  PROCESSOR_DATA_BLOCK  *Data;
+
+  for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {
+    Data = &gMPSystem.ProcessorData[Number];
+    if ((Data->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {
+      // Skip BSP
+      continue;
+    }
+
+    ProcessorState = GetApState(Data);
+
+    if (ProcessorState == CPU_STATE_BLOCKED) {
+      *NextNumber = Number;
+      return EFI_SUCCESS;
+    }
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  This service executes a caller provided function on all enabled APs. APs can
+  run either simultaneously or one at a time in sequence. This service supports
+  both blocking and non-blocking requests. The non-blocking requests use EFI
+  events so the BSP can detect when the APs have finished. This service may 
only
+  be called from the BSP.
+
+  This function is used to dispatch all the enabled APs to the function 
specified
+  by Procedure.  If any enabled AP is busy, then EFI_NOT_READY is returned
+  immediately and Procedure is not started on any AP.
+
+  If SingleThread is TRUE, all the enabled APs execute the function specified 
by
+  Procedure one by one, in ascending order of processor handle number. 
Otherwise,
+  all the enabled APs execute the function specified by Procedure 
simultaneously.
+
+  If WaitEvent is NULL, execution is in blocking mode. The BSP waits until all
+  APs finish or TimeoutInMicroseconds expires. Otherwise, execution is in 
non-blocking
+  mode, and the BSP returns from this service without waiting for APs. 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 all APs 
return
+  from Procedure, then Procedure on the failed APs is terminated. All enabled 
APs
+  are always available for further calls to 
EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+  and EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). If FailedCpuList is not NULL, 
its
+  content points to the list of processor handle numbers in which Procedure was
+  terminated.
+
+  Note: It is the responsibility of the consumer of the 
EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+  to make sure that the nature of the code that is executed on the BSP and the
+  dispatched APs is well controlled. The MP Services Protocol does not 
guarantee
+  that the Procedure function is MP-safe. Hence, the tasks that can be run in
+  parallel are limited to certain independent tasks and well-controlled 
exclusive
+  code. EFI services and protocols may not be called by APs unless otherwise
+  specified.
+
+  In blocking execution mode, BSP waits until all APs finish or
+  TimeoutInMicroseconds expires.
+
+  In non-blocking execution mode, BSP is freed to return to the caller and then
+  proceed to the next task without having to wait for APs. The following
+  sequence needs to occur in a non-blocking execution mode:
+
+    -# The caller that intends to use this MP Services Protocol in non-blocking
+       mode creates WaitEvent by calling the EFI CreateEvent() service.  The 
caller
+       invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the parameter 
WaitEvent
+       is not NULL, then StartupAllAPs() executes in non-blocking mode. It 
requests
+       the function specified by Procedure to be started on all the enabled 
APs,
+       and releases the BSP to continue with other tasks.
+    -# The caller can use the CheckEvent() and WaitForEvent() services to check
+       the state of the WaitEvent created in step 1.
+    -# When the APs complete their task or TimeoutInMicroSecondss expires, the 
MP
+       Service signals WaitEvent by calling the EFI SignalEvent() function. If
+       FailedCpuList is not NULL, its content is available when WaitEvent is
+       signaled. If all APs returned from Procedure prior to the timeout, then
+       FailedCpuList is set to NULL. If not all APs return from Procedure 
before
+       the timeout, then FailedCpuList is filled in with the list of the failed
+       APs. The buffer is allocated by MP Service Protocol using 
AllocatePool().
+       It is the caller's responsibility to free the buffer with FreePool() 
service.
+    -# This invocation of SignalEvent() function informs the caller that 
invoked
+       EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() that either all the APs 
completed
+       the specified task or a timeout occurred. The contents of FailedCpuList
+       can be examined to determine which APs did not complete the specified 
task
+       prior to the timeout.
+
+  @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]  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.  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] FailedCpuList           If NULL, this parameter is ignored. 
Otherwise,
+                                      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.
+                                      The buffer is allocated by MP Service 
Protocol,
+                                      and it's the caller's responsibility to
+                                      free the buffer with FreePool() service.
+                                      In blocking mode, it is ready for 
consumption
+                                      when the call returns. In non-blocking 
mode,
+                                      it is ready when WaitEvent is signaled.  
The
+                                      list of failed CPU is terminated by
+                                      END_OF_CPU_LIST.
+
+  @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 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        Caller processor is AP.
+  @retval EFI_NOT_STARTED         No enabled APs exist in the system.
+  @retval EFI_NOT_READY           Any enabled APs are busy.
+  @retval EFI_TIMEOUT             In blocking mode, the timeout expired before
+                                  all enabled APs have finished.
+  @retval EFI_INVALID_PARAMETER   Procedure is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesStartupAllAps (
+  IN  EFI_MP_SERVICES_PROTOCOL  *This,
+  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;
+  PROCESSOR_DATA_BLOCK  *ProcessorData = NULL;
+  UINTN                 Number;
+  UINTN                 NextNumber;
+  PROCESSOR_STATE       APInitialState;
+  PROCESSOR_STATE       ProcessorState;
+  INTN                  Timeout;
+
+
+  if (!IsBSP ()) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  if (gMPSystem.NumberOfProcessors == 1) {
+    return EFI_NOT_STARTED;
+  }
+
+  if (Procedure == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (FailedCpuList != NULL) {
+    gMPSystem.FailedList = AllocatePool ((gMPSystem.NumberOfProcessors + 1) * 
sizeof (UINTN));
+    if (gMPSystem.FailedList == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    SetMemN (gMPSystem.FailedList, (gMPSystem.NumberOfProcessors + 1) * sizeof 
(UINTN), END_OF_CPU_LIST);
+    gMPSystem.FailedListIndex = 0;
+    *FailedCpuList = gMPSystem.FailedList;
+  }
+
+  Timeout = TimeoutInMicroseconds;
+
+  gMPSystem.FinishCount   = 0;
+  gMPSystem.StartCount    = 0;
+  gMPSystem.SingleThread  = SingleThread;
+  APInitialState          = CPU_STATE_READY;
+
+  for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {
+    ProcessorData = &gMPSystem.ProcessorData[Number];
+
+    if ((ProcessorData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == 
PROCESSOR_AS_BSP_BIT) {
+      // Skip BSP
+      continue;
+    }
+
+    if ((ProcessorData->Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
+      // Skip Disabled processors
+      gMPSystem.FailedList[gMPSystem.FailedListIndex++] = Number;
+      continue;
+    }
+
+    //
+    // Get APs prepared, and put failing APs into FailedCpuList
+    // if "SingleThread", only 1 AP will put to ready state, other AP will be 
put to ready
+    // state 1 by 1, until the previous 1 finished its task
+    // if not "SingleThread", all APs are put to ready state from the beginning
+    //
+    if (ProcessorData->State == CPU_STATE_IDLE) {
+      gMPSystem.StartCount++;
+
+      SetApState(ProcessorData, APInitialState);
+      SetApProcedure (ProcessorData, Procedure, ProcedureArgument);
+
+      if (SingleThread) {
+        APInitialState = CPU_STATE_BLOCKED;
+      }
+    } else {
+      return EFI_NOT_READY;
+    }
+  }
+
+  if (WaitEvent != NULL) {
+    //
+    // Save data into private data structure, and create timer to poll AP 
state before exiting
+    //
+    gMPSystem.Procedure         = Procedure;
+    gMPSystem.ProcedureArgument = ProcedureArgument;
+    gMPSystem.WaitEvent         = WaitEvent;
+    gMPSystem.Timeout           = TimeoutInMicroseconds;
+    gMPSystem.TimeoutActive     = (BOOLEAN)(TimeoutInMicroseconds != 0);
+    Status = gBS->SetTimer (
+                    gMPSystem.CheckAllAPsEvent,
+                    TimerPeriodic,
+                    gPollInterval
+                    );
+    return Status;
+  }
+
+  while (TRUE) {
+    for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {
+      ProcessorData = &gMPSystem.ProcessorData[Number];
+      if ((ProcessorData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == 
PROCESSOR_AS_BSP_BIT) {
+       // Skip BSP
+        continue;
+      }
+
+      if ((ProcessorData->Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
+        // Skip Disabled processors
+        continue;
+      }
+
+      ProcessorState = GetApState(ProcessorData);
+
+      switch (ProcessorState) {
+      case CPU_STATE_READY:
+        SendCallFuncIpi(ProcessorData->Info.ProcessorId);
+        SetApState(ProcessorData, CPU_STATE_BUSY);
+        break;
+
+      case CPU_STATE_FINISHED:
+        gMPSystem.FinishCount++;
+        if (SingleThread) {
+          Status = GetNextBlockedNumber (&NextNumber);
+          if (!EFI_ERROR (Status)) {
+            SetApState(&gMPSystem.ProcessorData[NextNumber], CPU_STATE_READY);
+          }
+        }
+
+        SetApState(ProcessorData, CPU_STATE_IDLE);
+        break;
+
+      default:
+        break;
+      }
+    }
+
+    if (gMPSystem.FinishCount == gMPSystem.StartCount) {
+      Status = EFI_SUCCESS;
+      goto Done;
+    }
+
+    if ((TimeoutInMicroseconds != 0) && (Timeout < 0)) {
+      Status = EFI_TIMEOUT;
+      goto Done;
+    }
+
+    gBS->Stall (gPollInterval);
+    Timeout -= gPollInterval;
+  }
+
+Done:
+  if (FailedCpuList != NULL) {
+    if (gMPSystem.FailedListIndex == 0) {
+      FreePool (*FailedCpuList);
+      *FailedCpuList = NULL;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+EFI_MP_SERVICES_PROTOCOL  mMpServicesTemplate = {
+  CpuMpServicesGetNumberOfProcessors,
+  CpuMpServicesGetProcessorInfo,
+  CpuMpServicesStartupAllAps,
+  CpuMpServicesStartupThisAP,
+  NULL, //CpuMpServicesSwitchBSP,
+  CpuMpServicesEnableDisableAP,
+  CpuMpServicesWhoAmI
+};
+
+
+VOID
+EFIAPI
+CallFunctionInterrupt (
+  IN EFI_EXCEPTION_TYPE       InterruptType,
+  IN EFI_SYSTEM_CONTEXT       SystemContext
+  )
+{
+  UINT64                      ProcessorId;
+  PROCESSOR_DATA_BLOCK        *ProcessorData = NULL;
+  UINTN  i;
+
+  ProcessorId = GetApicId();
+
+  for (i = 0; i < ProcessorIdx; i++) {
+    if (gMPSystem.ProcessorData[i].Info.ProcessorId == ProcessorId) {
+      ProcessorData = &gMPSystem.ProcessorData[i];
+      break;
+    }
+  }
+
+  if (ProcessorData) {
+    if (ProcessorData->Procedure) {
+      ProcessorData->Procedure (ProcessorData->Parameter);
+    }
+    SetApProcedure(ProcessorData, NULL, NULL);
+    SetApState(ProcessorData, CPU_STATE_FINISHED);
+  }
+  DEBUG ((EFI_D_INFO, "Ap: %d excute end\n", ProcessorId));
+  SendApicEoi();
+}
+
+EFI_STATUS
+FillInProcessorInformation (
+  IN     BOOLEAN              BSP,
+  IN     UINTN                ProcessorNumber
+  )
+{
+  if (BSP) {
+    gMPSystem.ProcessorData[ProcessorNumber].Info.ProcessorId  = 0;
+  } else {
+    gMPSystem.ProcessorData[ProcessorNumber].Info.ProcessorId  = 
ProcessorIds[ProcessorNumber];
+  }
+  gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag   = 
PROCESSOR_ENABLED_BIT | PROCESSOR_HEALTH_STATUS_BIT;
+  if (BSP) {
+    gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag |= 
PROCESSOR_AS_BSP_BIT;
+  }
+
+  gMPSystem.ProcessorData[ProcessorNumber].Info.Location.Package = (UINT32) 
ProcessorNumber;
+  gMPSystem.ProcessorData[ProcessorNumber].Info.Location.Core    = 0;
+  gMPSystem.ProcessorData[ProcessorNumber].Info.Location.Thread  = 0;
+  gMPSystem.ProcessorData[ProcessorNumber].State = BSP ? CPU_STATE_BUSY : 
CPU_STATE_IDLE;
+
+  gMPSystem.ProcessorData[ProcessorNumber].Procedure        = NULL;
+  gMPSystem.ProcessorData[ProcessorNumber].Parameter        = NULL;
+  InitializeSpinLock(&gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+  InitializeSpinLock(&gMPSystem.ProcessorData[ProcessorNumber].ProcedureLock);
+
+  return EFI_SUCCESS;
+}
+
+VOID
+EFIAPI
+CpuCheckAllAPsStatus (
+  IN  EFI_EVENT        Event,
+  IN  VOID             *Context
+  )
+{
+  UINTN                 ProcessorNumber;
+  UINTN                 NextNumber;
+  PROCESSOR_DATA_BLOCK  *ProcessorData;
+  PROCESSOR_DATA_BLOCK  *NextData;
+  EFI_STATUS            Status;
+  PROCESSOR_STATE       ProcessorState;
+  UINTN                 Cpu;
+  BOOLEAN               Found;
+
+  if (gMPSystem.TimeoutActive) {
+    gMPSystem.Timeout -= gPollInterval;
+  }
+
+  for (ProcessorNumber = 0; ProcessorNumber < gMPSystem.NumberOfProcessors; 
ProcessorNumber++) {
+    ProcessorData = &gMPSystem.ProcessorData[ProcessorNumber];
+    if ((ProcessorData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == 
PROCESSOR_AS_BSP_BIT) {
+     // Skip BSP
+      continue;
+    }
+
+    if ((ProcessorData->Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
+      // Skip Disabled processors
+      continue;
+    }
+
+    ProcessorState = GetApState(ProcessorData);
+
+    switch (ProcessorState) {
+    case CPU_STATE_READY:
+      SendCallFuncIpi(ProcessorData->Info.ProcessorId);
+      SetApState(ProcessorData, CPU_STATE_BUSY);
+      break;
+
+    case CPU_STATE_FINISHED:
+      if (gMPSystem.SingleThread) {
+        Status = GetNextBlockedNumber (&NextNumber);
+        if (!EFI_ERROR (Status)) {
+          NextData = &gMPSystem.ProcessorData[NextNumber];
+
+          SetApState(NextData, CPU_STATE_READY);
+        }
+      }
+
+      SetApState(ProcessorData, CPU_STATE_IDLE);
+      gMPSystem.FinishCount++;
+      break;
+
+    default:
+      break;
+    }
+  }
+
+  if (gMPSystem.TimeoutActive && gMPSystem.Timeout < 0) {
+    //
+    // Timeout
+    //
+    if (gMPSystem.FailedList != NULL) {
+      for (ProcessorNumber = 0; ProcessorNumber < 
gMPSystem.NumberOfProcessors; ProcessorNumber++) {
+        ProcessorData = &gMPSystem.ProcessorData[ProcessorNumber];
+        if ((ProcessorData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == 
PROCESSOR_AS_BSP_BIT) {
+         // Skip BSP
+          continue;
+        }
+
+        if ((ProcessorData->Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
+          // Skip Disabled processors
+          continue;
+        }
+
+        ProcessorState = GetApState(ProcessorData);
+
+        if (ProcessorState != CPU_STATE_IDLE) {
+          // If we are retrying make sure we don't double count
+          for (Cpu = 0, Found = FALSE; Cpu < gMPSystem.NumberOfProcessors; 
Cpu++) {
+            if (gMPSystem.FailedList[Cpu] == END_OF_CPU_LIST) {
+              break;
+            }
+            if (gMPSystem.FailedList[ProcessorNumber] == Cpu) {
+              Found = TRUE;
+              break;
+            }
+          }
+          if (!Found) {
+            gMPSystem.FailedList[gMPSystem.FailedListIndex++] = Cpu;
+          }
+        }
+      }
+    }
+    // Force terminal exit
+    gMPSystem.FinishCount = gMPSystem.StartCount;
+  }
+
+  if (gMPSystem.FinishCount != gMPSystem.StartCount) {
+    return;
+  }
+
+  gBS->SetTimer (
+         gMPSystem.CheckAllAPsEvent,
+         TimerCancel,
+         0
+         );
+
+  if (gMPSystem.FailedListIndex == 0) {
+    if (gMPSystem.FailedList != NULL) {
+      FreePool (gMPSystem.FailedList);
+      gMPSystem.FailedList = NULL;
+    }
+  }
+
+  Status = gBS->SignalEvent (gMPSystem.WaitEvent);
+
+  return;
+}
+
+
+VOID
+EFIAPI
+CpuCheckThisAPStatus (
+  IN  EFI_EVENT        Event,
+  IN  VOID             *Context
+  )
+{
+   PROCESSOR_DATA_BLOCK  *ProcessorData;
+   PROCESSOR_STATE       ProcessorState;
+   BOOLEAN               TimerExit;
+
+   ProcessorData = (PROCESSOR_DATA_BLOCK *) Context;
+   if (ProcessorData->TimeoutActive) {
+     ProcessorData->Timeout -= gPollInterval;
+   }
+
+   ProcessorState = GetApState(ProcessorData);
+
+   if (ProcessorState == CPU_STATE_FINISHED) {
+     SetApState (ProcessorData, CPU_STATE_IDLE);
+     if (ProcessorData->Finished) {
+       ProcessorData->Finished = TRUE;
+     }
+     TimerExit = TRUE;
+   }
+
+   if (gMPSystem.TimeoutActive && gMPSystem.Timeout < 0) {
+     ProcessorState = GetApState(ProcessorData);
+     if (ProcessorState != CPU_STATE_IDLE &&
+         ProcessorData->Finished) {
+       ProcessorData->Finished = FALSE;
+     }
+     TimerExit = TRUE;
+   }
+
+   if (!TimerExit) {
+     return;
+   }
+
+   gBS->SetTimer (ProcessorData->CheckThisAPEvent, TimerCancel, 0);
+
+   gBS->SignalEvent (ProcessorData->WaitEvent);
+
+   return;
+}
+
+EFI_STATUS
+InitMpSystemData(
+  IN   INTN       CountCPUs
+  )
+{
+  EFI_STATUS    Status;
+  UINTN         Index;
+
+  ZeroMem (&gMPSystem, sizeof (MP_SYSTEM_DATA));
+
+  gMPSystem.NumberOfProcessors         = CountCPUs;
+  gMPSystem.NumberOfEnabledProcessors  = CountCPUs;
+
+  gMPSystem.ProcessorData = AllocateZeroPool (gMPSystem.NumberOfProcessors * 
sizeof (PROCESSOR_DATA_BLOCK));
+  ASSERT (gMPSystem.ProcessorData != NULL);
+
+  gPollInterval = (UINTN) PcdGet64 (PcdCpuMpServicesPollingInterval);
+
+  Status = gBS->CreateEvent (
+                  EVT_TIMER | EVT_NOTIFY_SIGNAL,
+                  TPL_CALLBACK,
+                  CpuCheckAllAPsStatus,
+                  NULL,
+                  &gMPSystem.CheckAllAPsEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  for (Index = 0; Index < ProcessorIdx; Index++) {
+    if (Index == 0) {
+      FillInProcessorInformation (TRUE, 0);
+    } else {
+      FillInProcessorInformation (FALSE, Index);
+    }
+
+  Status = gBS->CreateEvent (
+                      EVT_TIMER | EVT_NOTIFY_SIGNAL,
+                      TPL_CALLBACK,
+                      CpuCheckThisAPStatus,
+                      (VOID *)  &gMPSystem.ProcessorData[Index],
+                      &gMPSystem.ProcessorData[Index].CheckThisAPEvent
+                      );
+    ASSERT_EFI_ERROR (Status);
+  }
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                   &mpServiceHandle,
+                   &gEfiMpServiceProtocolGuid,  &mMpServicesTemplate,
+                   NULL
+                   );
+
+  return Status;
+}
+
 
 VOID
 InitializeMpSupport (
   VOID
   )
 {
+  EFI_CPU_ARCH_PROTOCOL   *Cpu;
+  EFI_STATUS              Status;
+  UINTN                   CountCPUs;
+
   mCommonStack = AllocatePages (EFI_SIZE_TO_PAGES (SIZE_64KB));
   mTopOfApCommonStack = (VOID*) ((UINTN)mCommonStack + SIZE_64KB);
   if (mCommonStack == NULL) {
@@ -69,9 +1178,24 @@ InitializeMpSupport (
 
   DEBUG ((EFI_D_INFO, "mTopOfApCommonStack: %p\n", mTopOfApCommonStack));
 
+  Status = gBS->LocateProtocol(&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Cpu);
+  ASSERT_EFI_ERROR(Status);
+
+  //Register interrupt handler
+  Status = Cpu->RegisterInterruptHandler(Cpu, CALL_FUNCTION_VECTOR, 
CallFunctionInterrupt);
+  ASSERT_EFI_ERROR(Status);
+
   AsmReadIdtr (&gIdtr);
 
-  StartApsStackless (AsmApEntryPoint);
+  StartApsStackless (AsmApEntryPoint, &CountCPUs);
+
+  if (CountCPUs == 1) {
+    return;
+  }
+
+  //init system data
+  Status = InitMpSystemData(CountCPUs);
+  ASSERT_EFI_ERROR(Status);
 
   gBS->FreePages ((EFI_PHYSICAL_ADDRESS)mCommonStack, 
EFI_SIZE_TO_PAGES(SIZE_64KB));
 }
diff --git a/UefiCpuPkg/CpuDxe/CpuMp.h b/UefiCpuPkg/CpuDxe/CpuMp.h
index 884d87b..48d8254 100644
--- a/UefiCpuPkg/CpuDxe/CpuMp.h
+++ b/UefiCpuPkg/CpuDxe/CpuMp.h
@@ -15,6 +15,12 @@
 #ifndef _CPU_MP_H_
 #define _CPU_MP_H_
 
+#include <Protocol/MpService.h>
+#include <Library/SynchronizationLib.h>
+#include <Library/PcdLib.h>
+
+#define CALL_FUNCTION_VECTOR            0x30
+
 VOID InitializeMpSupport (
   VOID
   );
@@ -27,7 +33,8 @@ VOID
 
 EFI_STATUS
 StartApsStackless (
-  IN STACKLESS_AP_ENTRY_POINT ApEntryPoint
+  IN STACKLESS_AP_ENTRY_POINT ApEntryPoint,
+  OUT UINTN                   *CountCPUs
   );
 
 VOID
@@ -36,5 +43,58 @@ AsmApEntryPoint (
   VOID
   );
 
+
+VOID
+EFIAPI
+CallFunctionInterrupt (
+  IN EFI_EXCEPTION_TYPE       InterruptType,
+  IN EFI_SYSTEM_CONTEXT       SystemContext
+  );
+
+typedef enum {
+  CPU_STATE_IDLE,
+  CPU_STATE_BLOCKED,
+  CPU_STATE_READY,
+  CPU_STATE_BUSY,
+  CPU_STATE_FINISHED
+} PROCESSOR_STATE;
+
+// Define Individual Processor Data block.
+//
+typedef struct {
+  EFI_PROCESSOR_INFORMATION   Info;
+  EFI_AP_PROCEDURE            Procedure;
+  VOID                        *Parameter;
+  EFI_EVENT                   WaitEvent;
+  UINTN                       Timeout;
+  BOOLEAN                     TimeoutActive;
+  SPIN_LOCK                   StateLock;
+  SPIN_LOCK                   ProcedureLock;
+  PROCESSOR_STATE             State;
+  BOOLEAN                     Finished;
+  EFI_EVENT                   CheckThisAPEvent;
+} PROCESSOR_DATA_BLOCK;
+
+//
+// Define MP data block which consumes individual processor block.
+//
+typedef struct {
+  UINTN                       NumberOfProcessors;
+  UINTN                       NumberOfEnabledProcessors;
+  PROCESSOR_DATA_BLOCK        *ProcessorData;
+  EFI_AP_PROCEDURE            Procedure;
+  VOID                        *ProcedureArgument;
+  EFI_EVENT                   WaitEvent;
+  UINTN                       Timeout;
+  BOOLEAN                     TimeoutActive;
+  UINTN                       StartCount;
+  UINTN                       FinishCount;
+  BOOLEAN                     SingleThread;
+  UINTN                       StartedNumber;
+  UINTN                       *FailedList;
+  UINTN                       FailedListIndex;
+  EFI_EVENT                   CheckAllAPsEvent;
+} MP_SYSTEM_DATA;
+
 #endif // _CPU_MP_H_
 
diff --git a/UefiCpuPkg/UefiCpuPkg.dec b/UefiCpuPkg/UefiCpuPkg.dec
index 4a28335..5f958ae 100644
--- a/UefiCpuPkg/UefiCpuPkg.dec
+++ b/UefiCpuPkg/UefiCpuPkg.dec
@@ -43,4 +43,4 @@
 
 [PcdsFixedAtBuild, PcdsPatchableInModule]
   
gUefiCpuPkgTokenSpaceGuid.PcdCpuLocalApicBaseAddress|0xfee00000|UINT32|0x00000001
-
+  
gUefiCpuPkgTokenSpaceGuid.PcdCpuMpServicesPollingInterval|0x100|UINT64|0x0000000a
-- 
1.9.3


------------------------------------------------------------------------------
Want fast and easy access to all the code in your enterprise? Index and
search up to 200,000 lines of code with a free copy of Black Duck
Code Sight - the same software that powers the world's largest code
search on Ohloh, the Black Duck Open Hub! Try it now.
http://p.sf.net/sfu/bds
_______________________________________________
edk2-devel mailing list
edk2-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/edk2-devel

Reply via email to