public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [PATCH v2 0/2] ArmPkg,ArmVirtPkg: Add support EFI_MP_SERVICES_PROTOCOL on AARCH64
@ 2021-12-15 17:46 Rebecca Cran
  2021-12-15 17:46 ` [PATCH v2 1/2] ArmPkg: Replace CoreId and ClusterId with Mpidr in ARM_CORE_INFO struct Rebecca Cran
  2021-12-15 17:46 ` [PATCH v2 2/2] ArmPkg: Add Library/MpInitLib to support EFI_MP_SERVICES_PROTOCOL Rebecca Cran
  0 siblings, 2 replies; 9+ messages in thread
From: Rebecca Cran @ 2021-12-15 17:46 UTC (permalink / raw)
  To: devel, Ard Biesheuvel, Gerd Hoffmann, Samer El-Haj-Mahmoud,
	Leif Lindholm, Sami Mujawar
  Cc: Rebecca Cran


ArmPkg: Add Library/MpInitLib to support EFI_MP_SERVICES_PROTOCOL


Changes from v1 to v2:

Mask off the non-affinity bits of the MPIDR, and since cluster 0/core 0
is 0x0 set the end of list value to MAX_UINT32.

Other changes based on Sami's feedback.

Rebecca Cran (2):
  ArmPkg: Replace CoreId and ClusterId with Mpidr in ARM_CORE_INFO
    struct
  ArmPkg: Add Library/MpInitLib to support EFI_MP_SERVICES_PROTOCOL

 ArmPkg/ArmPkg.dec                                              |    4 +
 ArmPkg/ArmPkg.dsc                                              |    4 +
 ArmPkg/Drivers/CpuDxe/AArch64/Arch.c                           |   21 +
 ArmPkg/Drivers/CpuDxe/Arm/Arch.c                               |   21 +
 ArmPkg/Drivers/CpuDxe/CpuDxe.c                                 |    2 +
 ArmPkg/Drivers/CpuDxe/CpuDxe.h                                 |   10 +
 ArmPkg/Drivers/CpuDxe/CpuDxe.inf                               |    6 +
 ArmPkg/Drivers/CpuDxe/CpuMpInit.c                              |  608 ++++++++
 ArmPkg/Include/Guid/ArmMpCoreInfo.h                            |    3 +-
 ArmPkg/Include/Library/ArmLib.h                                |   10 +-
 ArmPkg/Include/Library/MpInitLib.h                             |  366 +++++
 ArmPkg/Library/MpInitLib/AArch64/MpFuncs.S                     |   65 +
 ArmPkg/Library/MpInitLib/DxeMpInitLib.inf                      |   53 +
 ArmPkg/Library/MpInitLib/DxeMpLib.c                            | 1477 ++++++++++++++++++++
 ArmPkg/Library/MpInitLib/InternalMpInitLib.h                   |  359 +++++
 ArmPlatformPkg/Library/ArmPlatformLibNull/ArmPlatformLibNull.c |    8 +-
 ArmPlatformPkg/PrePeiCore/MainMPCore.c                         |    2 +-
 ArmPlatformPkg/PrePi/MainMPCore.c                              |    2 +-
 ArmVirtPkg/ArmVirt.dsc.inc                                     |    3 +
 19 files changed, 3013 insertions(+), 11 deletions(-)
 create mode 100644 ArmPkg/Drivers/CpuDxe/AArch64/Arch.c
 create mode 100644 ArmPkg/Drivers/CpuDxe/Arm/Arch.c
 create mode 100644 ArmPkg/Drivers/CpuDxe/CpuMpInit.c
 create mode 100644 ArmPkg/Include/Library/MpInitLib.h
 create mode 100644 ArmPkg/Library/MpInitLib/AArch64/MpFuncs.S
 create mode 100644 ArmPkg/Library/MpInitLib/DxeMpInitLib.inf
 create mode 100644 ArmPkg/Library/MpInitLib/DxeMpLib.c
 create mode 100644 ArmPkg/Library/MpInitLib/InternalMpInitLib.h

-- 
2.31.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v2 1/2] ArmPkg: Replace CoreId and ClusterId with Mpidr in ARM_CORE_INFO struct
  2021-12-15 17:46 [PATCH v2 0/2] ArmPkg,ArmVirtPkg: Add support EFI_MP_SERVICES_PROTOCOL on AARCH64 Rebecca Cran
@ 2021-12-15 17:46 ` Rebecca Cran
  2021-12-15 17:53   ` Ard Biesheuvel
  2021-12-15 17:46 ` [PATCH v2 2/2] ArmPkg: Add Library/MpInitLib to support EFI_MP_SERVICES_PROTOCOL Rebecca Cran
  1 sibling, 1 reply; 9+ messages in thread
From: Rebecca Cran @ 2021-12-15 17:46 UTC (permalink / raw)
  To: devel, Ard Biesheuvel, Gerd Hoffmann, Samer El-Haj-Mahmoud,
	Leif Lindholm, Sami Mujawar
  Cc: Rebecca Cran

Remove the ClusterId and CoreId fields in the ARM_CORE_INFO structure in
favor of a new Mpidr field. Update code in
ArmPlatformPkg/PrePeiCore/MainMPCore and ArmPlatformPkg/PrePi/MainMPCore.c
to use the new field and call new macros GET_MPIDR_AFF0 and GET_MPIDR_AFF1
instead.

Signed-off-by: Rebecca Cran <rebecca@nuviainc.com>
---
 ArmPkg/Include/Guid/ArmMpCoreInfo.h                            |  3 +--
 ArmPkg/Include/Library/ArmLib.h                                | 10 +++++++---
 ArmPlatformPkg/Library/ArmPlatformLibNull/ArmPlatformLibNull.c |  8 ++++----
 ArmPlatformPkg/PrePeiCore/MainMPCore.c                         |  2 +-
 ArmPlatformPkg/PrePi/MainMPCore.c                              |  2 +-
 5 files changed, 14 insertions(+), 11 deletions(-)

diff --git a/ArmPkg/Include/Guid/ArmMpCoreInfo.h b/ArmPkg/Include/Guid/ArmMpCoreInfo.h
index 06f9326ca021..43f0848e78b8 100644
--- a/ArmPkg/Include/Guid/ArmMpCoreInfo.h
+++ b/ArmPkg/Include/Guid/ArmMpCoreInfo.h
@@ -14,8 +14,7 @@
 #define MPIDR_U_BIT_MASK            0x40000000
 
 typedef struct {
-  UINT32                  ClusterId;
-  UINT32                  CoreId;
+  UINT64                  Mpidr;
 
   // MP Core Mailbox
   EFI_PHYSICAL_ADDRESS    MailboxSetAddress;
diff --git a/ArmPkg/Include/Library/ArmLib.h b/ArmPkg/Include/Library/ArmLib.h
index e4d0476090c7..5287bdfc8684 100644
--- a/ArmPkg/Include/Library/ArmLib.h
+++ b/ArmPkg/Include/Library/ArmLib.h
@@ -108,10 +108,14 @@ typedef enum {
 
 #define ARM_CORE_MASK     ARM_CORE_AFF0
 #define ARM_CLUSTER_MASK  ARM_CORE_AFF1
-#define GET_CORE_ID(MpId)            ((MpId) & ARM_CORE_MASK)
-#define GET_CLUSTER_ID(MpId)         (((MpId) & ARM_CLUSTER_MASK) >> 8)
+#define GET_CORE_ID(MpId)           ((MpId) & ARM_CORE_MASK)
+#define GET_CLUSTER_ID(MpId)        (((MpId) & ARM_CLUSTER_MASK) >> 8)
 #define GET_MPID(ClusterId, CoreId)  (((ClusterId) << 8) | (CoreId))
-#define PRIMARY_CORE_ID  (PcdGet32(PcdArmPrimaryCore) & ARM_CORE_MASK)
+#define GET_MPIDR_AFF0(MpId)  ((MpId) & ARM_CORE_AFF0)
+#define GET_MPIDR_AFF1(MpId)  (((MpId) & ARM_CORE_AFF1) >> 8)
+#define GET_MPIDR_AFF2(MpId)  (((MpId) & ARM_CORE_AFF2) >> 16)
+#define GET_MPIDR_AFF3(MpId)  (((MpId) & ARM_CORE_AFF3) >> 32)
+#define PRIMARY_CORE_ID       (PcdGet32(PcdArmPrimaryCore) & ARM_CORE_MASK)
 
 /** Reads the CCSIDR register for the specified cache.
 
diff --git a/ArmPlatformPkg/Library/ArmPlatformLibNull/ArmPlatformLibNull.c b/ArmPlatformPkg/Library/ArmPlatformLibNull/ArmPlatformLibNull.c
index eeab58805e67..852275f44fc3 100644
--- a/ArmPlatformPkg/Library/ArmPlatformLibNull/ArmPlatformLibNull.c
+++ b/ArmPlatformPkg/Library/ArmPlatformLibNull/ArmPlatformLibNull.c
@@ -14,7 +14,7 @@
 ARM_CORE_INFO  mArmPlatformNullMpCoreInfoTable[] = {
   {
     // Cluster 0, Core 0
-    0x0, 0x0,
+    0x0,
 
     // MP Core MailBox Set/Get/Clear Addresses and Clear Value
     (EFI_PHYSICAL_ADDRESS)0,
@@ -24,7 +24,7 @@ ARM_CORE_INFO  mArmPlatformNullMpCoreInfoTable[] = {
   },
   {
     // Cluster 0, Core 1
-    0x0, 0x1,
+    0x1,
 
     // MP Core MailBox Set/Get/Clear Addresses and Clear Value
     (EFI_PHYSICAL_ADDRESS)0,
@@ -34,7 +34,7 @@ ARM_CORE_INFO  mArmPlatformNullMpCoreInfoTable[] = {
   },
   {
     // Cluster 0, Core 2
-    0x0, 0x2,
+    0x2,
 
     // MP Core MailBox Set/Get/Clear Addresses and Clear Value
     (EFI_PHYSICAL_ADDRESS)0,
@@ -44,7 +44,7 @@ ARM_CORE_INFO  mArmPlatformNullMpCoreInfoTable[] = {
   },
   {
     // Cluster 0, Core 3
-    0x0, 0x3,
+    0x3,
 
     // MP Core MailBox Set/Get/Clear Addresses and Clear Value
     (EFI_PHYSICAL_ADDRESS)0,
diff --git a/ArmPlatformPkg/PrePeiCore/MainMPCore.c b/ArmPlatformPkg/PrePeiCore/MainMPCore.c
index 0b8e5dfb3f30..e6cb75157053 100644
--- a/ArmPlatformPkg/PrePeiCore/MainMPCore.c
+++ b/ArmPlatformPkg/PrePeiCore/MainMPCore.c
@@ -68,7 +68,7 @@ SecondaryMain (
 
   // Find the core in the ArmCoreTable
   for (Index = 0; Index < ArmCoreCount; Index++) {
-    if ((ArmCoreInfoTable[Index].ClusterId == ClusterId) && (ArmCoreInfoTable[Index].CoreId == CoreId)) {
+    if ((GET_MPIDR_AFF1 (ArmCoreInfoTable[Index].Mpidr) == ClusterId) && (GET_MPIDR_AFF0 (ArmCoreInfoTable[Index].Mpidr) == CoreId)) {
       break;
     }
   }
diff --git a/ArmPlatformPkg/PrePi/MainMPCore.c b/ArmPlatformPkg/PrePi/MainMPCore.c
index ce53cea6367c..0433fd0f2e75 100644
--- a/ArmPlatformPkg/PrePi/MainMPCore.c
+++ b/ArmPlatformPkg/PrePi/MainMPCore.c
@@ -67,7 +67,7 @@ SecondaryMain (
 
   // Find the core in the ArmCoreTable
   for (Index = 0; Index < ArmCoreCount; Index++) {
-    if ((ArmCoreInfoTable[Index].ClusterId == ClusterId) && (ArmCoreInfoTable[Index].CoreId == CoreId)) {
+    if ((GET_MPIDR_AFF1 (ArmCoreInfoTable[Index].Mpidr) == ClusterId) && (GET_MPIDR_AFF0 (ArmCoreInfoTable[Index].Mpidr) == CoreId)) {
       break;
     }
   }
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v2 2/2] ArmPkg: Add Library/MpInitLib to support EFI_MP_SERVICES_PROTOCOL
  2021-12-15 17:46 [PATCH v2 0/2] ArmPkg,ArmVirtPkg: Add support EFI_MP_SERVICES_PROTOCOL on AARCH64 Rebecca Cran
  2021-12-15 17:46 ` [PATCH v2 1/2] ArmPkg: Replace CoreId and ClusterId with Mpidr in ARM_CORE_INFO struct Rebecca Cran
@ 2021-12-15 17:46 ` Rebecca Cran
  2021-12-15 17:55   ` Ard Biesheuvel
                     ` (2 more replies)
  1 sibling, 3 replies; 9+ messages in thread
From: Rebecca Cran @ 2021-12-15 17:46 UTC (permalink / raw)
  To: devel, Ard Biesheuvel, Gerd Hoffmann, Samer El-Haj-Mahmoud,
	Leif Lindholm, Sami Mujawar
  Cc: Rebecca Cran

Add support for EFI_MP_SERVICES_PROTOCOL during the DXE phase under
AArch64.

PSCI_CPU_ON is called to power on the core, the supplied procedure is
executed and PSCI_CPU_OFF is called to power off the core.

Minimal setup is done before calling the supplied procedure: for example
the MMU and caches are not enabled.

Signed-off-by: Rebecca Cran <rebecca@nuviainc.com>
---
 ArmPkg/ArmPkg.dec                            |    4 +
 ArmPkg/ArmPkg.dsc                            |    4 +
 ArmPkg/Drivers/CpuDxe/AArch64/Arch.c         |   21 +
 ArmPkg/Drivers/CpuDxe/Arm/Arch.c             |   21 +
 ArmPkg/Drivers/CpuDxe/CpuDxe.c               |    2 +
 ArmPkg/Drivers/CpuDxe/CpuDxe.h               |   10 +
 ArmPkg/Drivers/CpuDxe/CpuDxe.inf             |    6 +
 ArmPkg/Drivers/CpuDxe/CpuMpInit.c            |  608 ++++++++
 ArmPkg/Include/Library/MpInitLib.h           |  366 +++++
 ArmPkg/Library/MpInitLib/AArch64/MpFuncs.S   |   65 +
 ArmPkg/Library/MpInitLib/DxeMpInitLib.inf    |   53 +
 ArmPkg/Library/MpInitLib/DxeMpLib.c          | 1477 ++++++++++++++++++++
 ArmPkg/Library/MpInitLib/InternalMpInitLib.h |  359 +++++
 ArmVirtPkg/ArmVirt.dsc.inc                   |    3 +
 14 files changed, 2999 insertions(+)

diff --git a/ArmPkg/ArmPkg.dec b/ArmPkg/ArmPkg.dec
index 9da1bbc9f216..363eddc57393 100644
--- a/ArmPkg/ArmPkg.dec
+++ b/ArmPkg/ArmPkg.dec
@@ -75,6 +75,10 @@
   #
   DefaultExceptionHandlerLib|Include/Library/DefaultExceptionHandlerLib.h
 
+  ##  @libraryclass  Provides a MP Services interface.
+  #
+  MpInitLib|Include/Library/MpInitLib.h
+
   ##  @libraryclass  Provides an interface to query miscellaneous OEM
   #   information.
   #
diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc
index 59fd8f295d4f..6e053d6ee31d 100644
--- a/ArmPkg/ArmPkg.dsc
+++ b/ArmPkg/ArmPkg.dsc
@@ -100,6 +100,9 @@
   PeiServicesLib|MdePkg/Library/PeiServicesLib/PeiServicesLib.inf
   PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf
 
+[LibraryClasses.AARCH64]
+  MpInitLib|ArmPkg/Library/MpInitLib/DxeMpInitLib.inf
+
 [LibraryClasses.ARM, LibraryClasses.AARCH64]
   NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf
 
@@ -163,6 +166,7 @@
 [Components.AARCH64]
   ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.inf
   ArmPkg/Library/ArmMmuLib/ArmMmuPeiLib.inf
+  ArmPkg/Library/MpInitLib/DxeMpInitLib.inf
 
 [Components.AARCH64, Components.ARM]
   ArmPkg/Library/StandaloneMmMmuLib/ArmMmuStandaloneMmLib.inf
diff --git a/ArmPkg/Drivers/CpuDxe/AArch64/Arch.c b/ArmPkg/Drivers/CpuDxe/AArch64/Arch.c
new file mode 100644
index 000000000000..cb7cb747bc15
--- /dev/null
+++ b/ArmPkg/Drivers/CpuDxe/AArch64/Arch.c
@@ -0,0 +1,21 @@
+/** @file
+  Architecture specific functions.
+
+  Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <CpuDxe.h>
+
+/** Initializes multi-processor support.
+ *
+**/
+VOID
+ArchInitializeMpSupport (
+  VOID
+  )
+{
+  InitializeMpSupport ();
+}
diff --git a/ArmPkg/Drivers/CpuDxe/Arm/Arch.c b/ArmPkg/Drivers/CpuDxe/Arm/Arch.c
new file mode 100644
index 000000000000..f8d57b41225a
--- /dev/null
+++ b/ArmPkg/Drivers/CpuDxe/Arm/Arch.c
@@ -0,0 +1,21 @@
+/** @file
+  Architecture specific functions.
+
+  Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <CpuDxe.h>
+
+/** Initializes multi-processor support.
+ *
+**/
+VOID
+ArchInitializeMpSupport (
+  VOID
+  )
+{
+  /* Nothing to do - ARM doesn't support EFI_MP_SERVICES_PROTOCOL */
+}
diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.c b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
index 62a6e2d620a6..6c076982a1bd 100644
--- a/ArmPkg/Drivers/CpuDxe/CpuDxe.c
+++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
@@ -275,5 +275,7 @@ CpuDxeInitialize (
                   );
   ASSERT_EFI_ERROR (Status);
 
+  ArchInitializeMpSupport ();
+
   return Status;
 }
diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.h b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
index 58ee1444c1b3..3f04b89d7ad0 100644
--- a/ArmPkg/Drivers/CpuDxe/CpuDxe.h
+++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
@@ -141,4 +141,14 @@ SetGcdMemorySpaceAttributes (
   IN UINT64                           Attributes
   );
 
+VOID
+InitializeMpSupport (
+  VOID
+  );
+
+VOID
+ArchInitializeMpSupport (
+  VOID
+  );
+
 #endif // CPU_DXE_H_
diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
index e5549fc71df7..f4cdb8ab5613 100644
--- a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
+++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
@@ -26,10 +26,13 @@
   Exception.c
 
 [Sources.ARM]
+  Arm/Arch.c
   Arm/Mmu.c
 
 [Sources.AARCH64]
+  AArch64/Arch.c
   AArch64/Mmu.c
+  CpuMpInit.c
 
 [Packages]
   ArmPkg/ArmPkg.dec
@@ -37,6 +40,9 @@
   MdePkg/MdePkg.dec
   MdeModulePkg/MdeModulePkg.dec
 
+[LibraryClasses.AARCH64]
+  MpInitLib
+
 [LibraryClasses]
   ArmLib
   ArmMmuLib
diff --git a/ArmPkg/Drivers/CpuDxe/CpuMpInit.c b/ArmPkg/Drivers/CpuDxe/CpuMpInit.c
new file mode 100644
index 000000000000..876a29e09b1b
--- /dev/null
+++ b/ArmPkg/Drivers/CpuDxe/CpuMpInit.c
@@ -0,0 +1,608 @@
+/** @file
+  Construct MP Services Protocol.
+
+  The MP Services Protocol provides a generalized way of performing following tasks:
+    - Retrieving information of multi-processor environment and MP-related status of
+      specific processors.
+    - Dispatching user-provided function to APs.
+    - Maintain MP-related processor status.
+
+  The MP Services Protocol must be produced on any system with more than one logical
+  processor.
+
+  The Protocol is available only during boot time.
+
+  MP Services Protocol is hardware-independent. Most of the logic of this protocol
+  is architecturally neutral. It abstracts the multi-processor environment and
+  status of processors, and provides interfaces to retrieve information, maintain,
+  and dispatch.
+
+  MP Services Protocol may be consumed by ACPI module. The ACPI module may use this
+  protocol to retrieve data that are needed for an MP platform and report them to OS.
+  MP Services Protocol may also be used to program and configure processors, such
+  as MTRR synchronization for memory space attributes setting in DXE Services.
+  MP Services Protocol may be used by non-CPU DXE drivers to speed up platform boot
+  by taking advantage of the processing capabilities of the APs, for example, using
+  APs to help test system memory in parallel with other device initialization.
+  Diagnostics applications may also use this protocol for multi-processor.
+
+  Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/MpInitLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+/**
+  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 the
+                                          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.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GetNumberOfProcessors (
+  IN  EFI_MP_SERVICES_PROTOCOL  *This,
+  OUT UINTN                     *NumberOfProcessors,
+  OUT UINTN                     *NumberOfEnabledProcessors
+  )
+{
+  return MpInitLibGetNumberOfProcessors (
+           This,
+           NumberOfProcessors,
+           NumberOfEnabledProcessors
+           );
+}
+
+/**
+  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 index of the 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.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GetProcessorInfo (
+  IN  EFI_MP_SERVICES_PROTOCOL   *This,
+  IN  UINTN                      ProcessorNumber,
+  OUT EFI_PROCESSOR_INFORMATION  *ProcessorInfoBuffer
+  )
+{
+  return MpInitLibGetProcessorInfo (
+           This,
+           ProcessorNumber,
+           ProcessorInfoBuffer
+           );
+}
+
+/**
+  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]  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] 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.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+StartupAllAPs (
+  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
+  )
+{
+  return MpInitLibStartupAllAPs (
+           This,
+           Procedure,
+           SingleThread,
+           WaitEvent,
+           TimeoutInMicroseconds,
+           ProcedureArgument,
+           FailedCpuList
+           );
+}
+
+/**
+  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.
+
+**/
+STATIC
+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
+  )
+{
+  return MpInitLibStartupThisAP (
+           This,
+           Procedure,
+           ProcessorNumber,
+           WaitEvent,
+           TimeoutInMicroseconds,
+           ProcedureArgument,
+           Finished
+           );
+}
+
+/**
+  This service switches the requested AP to be the BSP from that point onward.
+  This service changes the BSP for all purposes.   This call can only be
+  performed by the current BSP.
+
+  This service switches the requested AP to be the BSP from that point onward.
+  This service changes the BSP for all purposes. The new BSP can take over the
+  execution of the old BSP and continue seamlessly from where the old one left
+  off. This service may not be supported after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
+  is signaled.
+
+  If the BSP cannot be switched 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] EnableOldBSP      If TRUE, then the old BSP will be listed as an
+                               enabled AP. Otherwise, it will be disabled.
+
+  @retval EFI_SUCCESS             BSP successfully switched.
+  @retval EFI_UNSUPPORTED         Switching the BSP cannot be completed prior to
+                                  this service returning.
+  @retval EFI_UNSUPPORTED         Switching the BSP is not supported.
+  @retval EFI_SUCCESS             The calling processor is an AP.
+  @retval EFI_NOT_FOUND           The processor with the handle specified by
+                                  ProcessorNumber does not exist.
+  @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the current BSP or
+                                  a disabled AP.
+  @retval EFI_NOT_READY           The specified AP is busy.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+SwitchBSP (
+  IN EFI_MP_SERVICES_PROTOCOL  *This,
+  IN  UINTN                    ProcessorNumber,
+  IN  BOOLEAN                  EnableOldBSP
+  )
+{
+  return MpInitLibSwitchBSP (This, ProcessorNumber, EnableOldBSP);
+}
+
+/**
+  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.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+EnableDisableAP (
+  IN  EFI_MP_SERVICES_PROTOCOL  *This,
+  IN  UINTN                     ProcessorNumber,
+  IN  BOOLEAN                   EnableAP,
+  IN  UINT32                    *HealthFlag OPTIONAL
+  )
+{
+  return MpInitLibEnableDisableAP (This, ProcessorNumber, EnableAP, HealthFlag);
+}
+
+/**
+  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[out] 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.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+WhoAmI (
+  IN EFI_MP_SERVICES_PROTOCOL  *This,
+  OUT UINTN                    *ProcessorNumber
+  )
+{
+  return MpInitLibWhoAmI (This, ProcessorNumber);
+}
+
+EFI_MP_SERVICES_PROTOCOL  mMpServicesTemplate = {
+  GetNumberOfProcessors,
+  GetProcessorInfo,
+  StartupAllAPs,
+  StartupThisAP,
+  SwitchBSP,
+  EnableDisableAP,
+  WhoAmI
+};
+
+/** Initialize multi-processor support.
+
+**/
+VOID
+InitializeMpSupport (
+  VOID
+  )
+{
+  EFI_STATUS              Status;
+  EFI_HANDLE              Handle;
+  UINTN                   MaxCpus;
+  EFI_HOB_GENERIC_HEADER  *Hob;
+  VOID                    *HobData;
+  UINTN                   HobDataSize;
+  ARM_PROCESSOR_TABLE     CpuInfo;
+
+  MaxCpus = 1;
+  ZeroMem (&CpuInfo, sizeof (ARM_PROCESSOR_TABLE));
+
+  DEBUG ((DEBUG_INFO, "Starting MP services"));
+
+  Hob = GetFirstGuidHob (&gArmMpCoreInfoGuid);
+  if (Hob != NULL) {
+    HobData                 = GET_GUID_HOB_DATA (Hob);
+    HobDataSize             = GET_GUID_HOB_DATA_SIZE (Hob);
+    CpuInfo.ArmCpus         = (ARM_CORE_INFO *)HobData;
+    CpuInfo.NumberOfEntries = HobDataSize / sizeof (ARM_CORE_INFO);
+    MaxCpus                 = CpuInfo.NumberOfEntries;
+  }
+
+  if (MaxCpus == 1) {
+    DEBUG ((DEBUG_WARN, "Trying to use EFI_MP_SERVICES_PROTOCOL on a UP system"));
+    // We are not MP so nothing to do
+    return;
+  }
+
+  MpInitLibInitialize (MaxCpus, &CpuInfo);
+
+  //
+  // Now install the MP services protocol.
+  //
+  Handle = NULL;
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &Handle,
+                  &gEfiMpServiceProtocolGuid,
+                  &mMpServicesTemplate,
+                  NULL
+                  );
+  ASSERT_EFI_ERROR (Status);
+}
diff --git a/ArmPkg/Include/Library/MpInitLib.h b/ArmPkg/Include/Library/MpInitLib.h
new file mode 100644
index 000000000000..a4b80c18a9e8
--- /dev/null
+++ b/ArmPkg/Include/Library/MpInitLib.h
@@ -0,0 +1,366 @@
+/** @file
+
+Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
+Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
+Portions copyright (c) 2011, Apple Inc. All rights reserved.
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef MP_INITLIB_H_
+#define MP_INITLIB_H_
+
+#include <Protocol/Cpu.h>
+#include <Protocol/MpService.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Guid/ArmMpCoreInfo.h>
+
+
+/**
+  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.
+
+  @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 the
+                                          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
+MpInitLibGetNumberOfProcessors (
+  IN  EFI_MP_SERVICES_PROTOCOL  *This,
+  OUT UINTN                     *NumberOfProcessors,
+  OUT UINTN                     *NumberOfEnabledProcessors
+  );
+
+/**
+  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.
+
+  @param[in]  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL
+                                    instance.
+  @param[in]  ProcessorIndex        The index of the 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
+MpInitLibGetProcessorInfo (
+  IN  EFI_MP_SERVICES_PROTOCOL   *This,
+  IN  UINTN                      ProcessorIndex,
+  OUT EFI_PROCESSOR_INFORMATION  *ProcessorInfoBuffer
+  );
+
+
+/**
+  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.
+
+  @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]  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] 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
+MpInitLibStartupAllAPs (
+  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
+  );
+
+/**
+  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.
+
+  @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
+MpInitLibStartupThisAP (
+  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 switches the requested AP to be the BSP from that point onward.
+  This service changes the BSP for all purposes.   This call can only be
+  performed by the current BSP.
+
+  @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] EnableOldBSP      If TRUE, then the old BSP will be listed as an
+                               enabled AP. Otherwise, it will be disabled.
+
+  @retval EFI_SUCCESS             BSP successfully switched.
+  @retval EFI_UNSUPPORTED         Switching the BSP cannot be completed prior to
+                                  this service returning.
+  @retval EFI_UNSUPPORTED         Switching the BSP is not supported.
+  @retval EFI_SUCCESS             The calling processor is an AP.
+  @retval EFI_NOT_FOUND           The processor with the handle specified by
+                                  ProcessorNumber does not exist.
+  @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the current BSP or
+                                  a disabled AP.
+  @retval EFI_NOT_READY           The specified AP is busy.
+
+**/
+EFI_STATUS
+EFIAPI
+MpInitLibSwitchBSP (
+  IN EFI_MP_SERVICES_PROTOCOL  *This,
+  IN  UINTN                    ProcessorNumber,
+  IN  BOOLEAN                  EnableOldBSP
+  );
+
+/**
+  This service lets the caller enable or disable an AP from this point onward.
+  This service may only be called from the BSP.
+
+  @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
+MpInitLibEnableDisableAP (
+  IN  EFI_MP_SERVICES_PROTOCOL  *This,
+  IN  UINTN                     ProcessorNumber,
+  IN  BOOLEAN                   EnableAP,
+  IN  UINT32                    *HealthFlag OPTIONAL
+  );
+
+/**
+  This return the handle number for the calling processor.  This service may be
+  called from the BSP and APs.
+
+  @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+  @param[out] 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
+MpInitLibWhoAmI (
+  IN EFI_MP_SERVICES_PROTOCOL  *This,
+  OUT UINTN                    *ProcessorNumber
+  );
+
+/** Initializes the MP Services system data
+
+   @param NumberOfProcessors The number of processors, both BSP and AP.
+   @param CpuInfo            CPU information gathered earlier during boot.
+
+**/
+VOID
+MpInitLibInitialize (
+  IN   UINTN     NumberOfProcessors,
+  IN   ARM_PROCESSOR_TABLE *CpuInfo
+  );
+
+
+
+#endif /* MP_INITLIB_H_ */
diff --git a/ArmPkg/Library/MpInitLib/AArch64/MpFuncs.S b/ArmPkg/Library/MpInitLib/AArch64/MpFuncs.S
new file mode 100644
index 000000000000..8f7019a1c62c
--- /dev/null
+++ b/ArmPkg/Library/MpInitLib/AArch64/MpFuncs.S
@@ -0,0 +1,65 @@
+#===============================================================================
+#  Copyright (c) 2021 NUVIA Inc. All rights reserved.
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#===============================================================================
+
+.text
+.align 3
+
+#include <AsmMacroIoLibV8.h>
+#include <IndustryStandard/ArmStdSmc.h>
+
+#include "InternalMpInitLib.h"
+
+GCC_ASM_IMPORT (gApStacksBase)
+GCC_ASM_IMPORT (gProcessorIDs)
+GCC_ASM_IMPORT (ApProcedure)
+GCC_ASM_IMPORT (gApStackSize)
+
+GCC_ASM_EXPORT (ApEntryPoint)
+
+StartupAddr:        .8byte ASM_PFX(ApProcedure)
+
+// Entry-point for the AP
+// VOID
+// ApEntryPoint (
+//   VOID
+//   );
+ASM_PFX(ApEntryPoint):
+  mrs x0, mpidr_el1
+  // Mask the non-affinity bits
+  ldr x1, =0xff00ffffff
+  and x0, x0, x1
+  ldr x1, gProcessorIDs
+  mov x2, 0                   // x2 = processor index
+  mov x3, 0                   // x3 = address offset
+
+// Find index in gProcessorIDs for current processor
+1:
+  ldr x4, [x1, x3]            // x4 = gProcessorIDs + x3
+  ldr x5, =0xffffffffff
+  cmp x4, x5                  // check if we've reached the end of gProcessorIDs
+  beq ProcessorNotFound
+  add x3, x3, 8               // x3 += sizeof (*gProcessorIDs)
+  add x2, x2, 1               // x2++
+  cmp x0, x4                  // if mpidr_el1 != *(gProcessorIDs + x3) then loop
+  bne 1b
+  sub x2, x2, 1
+
+// Calculate stack address
+  // x2 contains the index for the current processor
+  ldr x0, gApStacksBase
+  ldr x1, gApStackSize
+  mul x3, x2, x1              // x3 = ProcessorIndex * gApStackSize
+  add x4, x0, x3              // x4 = gApStacksBase + x3
+  add sp, x4, x1              // sp = x4 + gApStackSize
+
+  ldr x0, StartupAddr         // ASM_PFX(ApProcedure)
+  blr x0                      // doesn't return
+
+ProcessorNotFound:
+// Turn off the processor
+  MOV32 (w0, ARM_SMC_ID_PSCI_CPU_OFF)
+  smc #0
+  b .
diff --git a/ArmPkg/Library/MpInitLib/DxeMpInitLib.inf b/ArmPkg/Library/MpInitLib/DxeMpInitLib.inf
new file mode 100644
index 000000000000..2275b6cca33a
--- /dev/null
+++ b/ArmPkg/Library/MpInitLib/DxeMpInitLib.inf
@@ -0,0 +1,53 @@
+#/** @file
+#
+# Component description file for the DxeMpInitLib module.
+#
+# Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#**/
+
+[Defines]
+  INF_VERSION                    = 1.29
+  BASE_NAME                      = DxeMpInitLib
+  FILE_GUID                      = c9ca773c-8ae4-4b74-82fd-f7345503294e
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = MpInitLib|DXE_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = AARCH64
+#
+
+[Sources.AARCH64]
+  AArch64/MpFuncs.S
+
+[Sources]
+  DxeMpLib.c
+  InternalMpInitLib.h
+
+[Packages]
+  ArmPkg/ArmPkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  ArmLib
+  ArmSmcLib
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  HobLib
+  MemoryAllocationLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiLib
+
+[Protocols]
+  gEfiMpServiceProtocolGuid
+
+[Guids]
+  gArmMpCoreInfoGuid
diff --git a/ArmPkg/Library/MpInitLib/DxeMpLib.c b/ArmPkg/Library/MpInitLib/DxeMpLib.c
new file mode 100644
index 000000000000..e8d8808a3225
--- /dev/null
+++ b/ArmPkg/Library/MpInitLib/DxeMpLib.c
@@ -0,0 +1,1477 @@
+/** @file
+  Construct MP Services Protocol.
+
+  Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
+  Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
+  Portions Copyright (c) 2011, Apple Inc. All rights reserved.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Ppi/ArmMpCoreInfo.h>
+#include <Library/ArmLib.h>
+#include <Library/ArmSmcLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/MpInitLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <IndustryStandard/ArmStdSmc.h>
+
+#include "InternalMpInitLib.h"
+
+#define POLL_INTERVAL_US  50000
+
+#define GET_MPIDR_AFFINITY_BITS(x)  ((x) & 0xFF00FFFFFF)
+
+#define MPIDR_MT_BIT  BIT24
+
+STATIC CPU_MP_DATA  mCpuMpData;
+STATIC BOOLEAN      mNonBlockingModeAllowed;
+UINT64              *gApStacksBase;
+UINT64              *gProcessorIDs;
+CONST UINT64        gApStackSize = AP_STACK_SIZE;
+
+/** C entry-point for the AP.
+    This function gets called from the assembly function ApEntryPoint.
+
+**/
+VOID
+ApProcedure (
+  VOID
+  )
+{
+  ARM_SMC_ARGS      Args;
+  EFI_AP_PROCEDURE  UserApProcedure;
+  VOID              *UserApParameter;
+  UINTN             ProcessorIndex;
+
+  MpInitLibWhoAmI (&mMpServicesTemplate, &ProcessorIndex);
+
+  /* Fetch the user-supplied procedure and parameter to execute */
+  UserApProcedure = mCpuMpData.CpuData[ProcessorIndex].Procedure;
+  UserApParameter = mCpuMpData.CpuData[ProcessorIndex].Parameter;
+
+  UserApProcedure (UserApParameter);
+
+  mCpuMpData.CpuData[ProcessorIndex].State = CpuStateFinished;
+
+  /* Since we're finished with this AP, turn it off */
+  Args.Arg0 = ARM_SMC_ID_PSCI_CPU_OFF;
+  ArmCallSmc (&Args);
+
+  /* Should never be reached */
+  ASSERT (FALSE);
+  CpuDeadLoop ();
+}
+
+/** Turns on the specified core using PSCI and executes the user-supplied
+    function that's been configured via a previous call to SetApProcedure.
+
+    @param ProcessorIndex The index of the core to turn on.
+
+    @retval EFI_SUCCESS      Success.
+    @retval EFI_DEVICE_ERROR The processor could not be turned on.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+DispatchCpu (
+  IN UINTN  ProcessorIndex
+  )
+{
+  ARM_SMC_ARGS  Args;
+  EFI_STATUS    Status;
+
+  Status = EFI_SUCCESS;
+
+  mCpuMpData.CpuData[ProcessorIndex].State = CpuStateBusy;
+
+  /* Turn the AP on */
+  if (sizeof (Args.Arg0) == sizeof (UINT32)) {
+    Args.Arg0 = ARM_SMC_ID_PSCI_CPU_ON_AARCH32;
+  } else {
+    Args.Arg0 = ARM_SMC_ID_PSCI_CPU_ON_AARCH64;
+  }
+
+  Args.Arg1 = gProcessorIDs[ProcessorIndex];
+  Args.Arg2 = (UINTN)ApEntryPoint;
+
+  ArmCallSmc (&Args);
+
+  if (Args.Arg0 != ARM_SMC_PSCI_RET_SUCCESS) {
+    DEBUG ((DEBUG_ERROR, "PSCI_CPU_ON call failed: %d", Args.Arg0));
+    Status = EFI_DEVICE_ERROR;
+  }
+
+  return Status;
+}
+
+/** Returns whether the specified processor is the BSP.
+
+  @param[in] ProcessorIndex The index the processor to check.
+
+  @return TRUE if the processor is the BSP, FALSE otherwise.
+**/
+STATIC
+BOOLEAN
+IsProcessorBSP (
+  UINTN  ProcessorIndex
+  )
+{
+  EFI_PROCESSOR_INFORMATION  *CpuInfo;
+
+  CpuInfo = &mCpuMpData.CpuData[ProcessorIndex].Info;
+
+  return (CpuInfo->StatusFlag & PROCESSOR_AS_BSP_BIT) != 0;
+}
+
+/** Returns whether the processor executing this function is the BSP.
+
+    @return Whether the current processor is the BSP.
+**/
+STATIC
+BOOLEAN
+IsCurrentProcessorBSP (
+  VOID
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       ProcessorIndex;
+
+  Status = MpInitLibWhoAmI (&mMpServicesTemplate, &ProcessorIndex);
+  if (EFI_ERROR (Status)) {
+    ASSERT (0);
+    return FALSE;
+  }
+
+  return IsProcessorBSP (ProcessorIndex);
+}
+
+/** Get the Application Processors state.
+
+  @param[in]  CpuData    The pointer to CPU_AP_DATA of specified AP.
+
+  @return The AP status.
+**/
+CPU_STATE
+GetApState (
+  IN  CPU_AP_DATA  *CpuData
+  )
+{
+  return CpuData->State;
+}
+
+/** Configures the processor context with the user-supplied procedure and
+    argument.
+
+   @param CpuData           The processor context.
+   @param Procedure         The user-supplied procedure.
+   @param ProcedureArgument The user-supplied procedure argument.
+
+**/
+STATIC
+VOID
+SetApProcedure (
+  IN   CPU_AP_DATA       *CpuData,
+  IN   EFI_AP_PROCEDURE  Procedure,
+  IN   VOID              *ProcedureArgument
+  )
+{
+  ASSERT (CpuData != NULL);
+  ASSERT (Procedure != NULL);
+
+  CpuData->Parameter = ProcedureArgument;
+  CpuData->Procedure = Procedure;
+}
+
+/** Returns the index of the next processor that is blocked.
+
+   @param[out] NextNumber The index of the next blocked processor.
+
+   @retval EFI_SUCCESS   Successfully found the next blocked processor.
+   @retval EFI_NOT_FOUND There are no blocked processors.
+
+**/
+STATIC
+EFI_STATUS
+GetNextBlockedNumber (
+  OUT UINTN  *NextNumber
+  )
+{
+  UINTN        Index;
+  CPU_STATE    State;
+  CPU_AP_DATA  *CpuData;
+
+  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+    CpuData = &mCpuMpData.CpuData[Index];
+    if (IsProcessorBSP (Index)) {
+      // Skip BSP
+      continue;
+    }
+
+    State = CpuData->State;
+
+    if (State == CpuStateBlocked) {
+      *NextNumber = Index;
+      return EFI_SUCCESS;
+    }
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/** Stalls the BSP for the minimum of POLL_INTERVAL_US and Timeout.
+
+   @param[in]  Timeout    The time limit in microseconds remaining for
+                          APs to return from Procedure.
+
+   @retval     StallTime  Time of execution stall.
+**/
+STATIC
+UINTN
+CalculateAndStallInterval (
+  IN UINTN  Timeout
+  )
+{
+  UINTN  StallTime;
+
+  if ((Timeout < POLL_INTERVAL_US) && (Timeout != 0)) {
+    StallTime = Timeout;
+  } else {
+    StallTime = POLL_INTERVAL_US;
+  }
+
+  gBS->Stall (StallTime);
+
+  return StallTime;
+}
+
+/**
+  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.
+
+  @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 the
+                                          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
+MpInitLibGetNumberOfProcessors (
+  IN  EFI_MP_SERVICES_PROTOCOL  *This,
+  OUT UINTN                     *NumberOfProcessors,
+  OUT UINTN                     *NumberOfEnabledProcessors
+  )
+{
+  if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (!IsCurrentProcessorBSP ()) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  *NumberOfProcessors        = mCpuMpData.NumberOfProcessors;
+  *NumberOfEnabledProcessors = mCpuMpData.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.
+
+  @param[in]  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL
+                                    instance.
+  @param[in]  ProcessorIndex        The index of the 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
+MpInitLibGetProcessorInfo (
+  IN  EFI_MP_SERVICES_PROTOCOL   *This,
+  IN  UINTN                      ProcessorIndex,
+  OUT EFI_PROCESSOR_INFORMATION  *ProcessorInfoBuffer
+  )
+{
+  if (ProcessorInfoBuffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (!IsCurrentProcessorBSP ()) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  ProcessorIndex &= ~CPU_V2_EXTENDED_TOPOLOGY;
+
+  if (ProcessorIndex >= mCpuMpData.NumberOfProcessors) {
+    return EFI_NOT_FOUND;
+  }
+
+  CopyMem (
+    ProcessorInfoBuffer,
+    &mCpuMpData.CpuData[ProcessorIndex],
+    sizeof (EFI_PROCESSOR_INFORMATION)
+    );
+  return EFI_SUCCESS;
+}
+
+/** Returns whether the specified processor is enabled.
+
+   @param[in] ProcessorIndex The index of the processor to check.
+
+   @return TRUE if the processor is enabled, FALSE otherwise.
+**/
+STATIC
+BOOLEAN
+IsProcessorEnabled (
+  UINTN  ProcessorIndex
+  )
+{
+  EFI_PROCESSOR_INFORMATION  *CpuInfo;
+
+  CpuInfo = &mCpuMpData.CpuData[ProcessorIndex].Info;
+
+  return (CpuInfo->StatusFlag & PROCESSOR_ENABLED_BIT) != 0;
+}
+
+/** Returns whether all processors are in the idle state.
+
+   @return Whether all the processors are idle.
+
+**/
+STATIC
+BOOLEAN
+CheckAllCpusReady (
+  VOID
+  )
+{
+  UINTN        Index;
+  CPU_AP_DATA  *CpuData;
+
+  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+    CpuData = &mCpuMpData.CpuData[Index];
+    if (IsProcessorBSP (Index)) {
+      // Skip BSP
+      continue;
+    }
+
+    if (!IsProcessorEnabled (Index)) {
+      // Skip Disabled processors
+      continue;
+    }
+
+    if (GetApState (CpuData) != CpuStateIdle) {
+      return FALSE;
+    }
+  }
+
+  return TRUE;
+}
+
+/** Sets up the state for the StartupAllAPs function.
+
+   @param SingleThread Whether the APs will execute sequentially.
+
+**/
+STATIC
+VOID
+StartupAllAPsPrepareState (
+  IN BOOLEAN  SingleThread
+  )
+{
+  UINTN        Index;
+  CPU_STATE    APInitialState;
+  CPU_AP_DATA  *CpuData;
+
+  mCpuMpData.FinishCount  = 0;
+  mCpuMpData.StartCount   = 0;
+  mCpuMpData.SingleThread = SingleThread;
+
+  APInitialState = CpuStateReady;
+
+  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+    CpuData = &mCpuMpData.CpuData[Index];
+
+    //
+    // Get APs prepared, and put failing APs into FailedCpuList.
+    // If "SingleThread", only 1 AP will put into ready state, other AP will be
+    // put into ready state 1 by 1, until the previous 1 finished its task.
+    // If not "SingleThread", all APs are put into ready state from the
+    // beginning
+    //
+
+    if (IsProcessorBSP (Index)) {
+      // Skip BSP
+      continue;
+    }
+
+    if (!IsProcessorEnabled (Index)) {
+      // Skip Disabled processors
+      if (mCpuMpData.FailedList != NULL) {
+        mCpuMpData.FailedList[mCpuMpData.FailedListIndex++] = Index;
+      }
+
+      continue;
+    }
+
+    ASSERT (GetApState (CpuData) == CpuStateIdle);
+    CpuData->State = APInitialState;
+
+    mCpuMpData.StartCount++;
+    if (SingleThread) {
+      APInitialState = CpuStateBlocked;
+    }
+  }
+}
+
+/** Handles execution of StartupAllAPs when a WaitEvent has been specified.
+
+   @param Procedure         The user-supplied procedure.
+   @param ProcedureArgument The user-supplied procedure argument.
+   @param WaitEvent         The wait event to be signaled when the work is
+                            complete or a timeout has occurred.
+   @param TimeoutInMicroseconds The timeout for the work to be completed. Zero
+                                indicates an infinite timeout.
+
+   @return EFI_SUCCESS on success.
+**/
+STATIC
+EFI_STATUS
+StartupAllAPsWithWaitEvent (
+  IN EFI_AP_PROCEDURE  Procedure,
+  IN VOID              *ProcedureArgument,
+  IN EFI_EVENT         WaitEvent,
+  IN UINTN             TimeoutInMicroseconds
+  )
+{
+  EFI_STATUS   Status;
+  UINTN        Index;
+  CPU_AP_DATA  *CpuData;
+
+  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+    CpuData = &mCpuMpData.CpuData[Index];
+    if (IsProcessorBSP (Index)) {
+      // Skip BSP
+      continue;
+    }
+
+    if (!IsProcessorEnabled (Index)) {
+      // Skip Disabled processors
+      continue;
+    }
+
+    if (GetApState (CpuData) == CpuStateReady) {
+      SetApProcedure (CpuData, Procedure, ProcedureArgument);
+    }
+  }
+
+  //
+  // Save data into private data structure, and create timer to poll AP state
+  // before exiting
+  //
+  mCpuMpData.Procedure         = Procedure;
+  mCpuMpData.ProcedureArgument = ProcedureArgument;
+  mCpuMpData.WaitEvent         = WaitEvent;
+  mCpuMpData.Timeout           = TimeoutInMicroseconds;
+  mCpuMpData.TimeoutActive     = (BOOLEAN)(TimeoutInMicroseconds != 0);
+  Status                       = gBS->SetTimer (
+                                        mCpuMpData.CheckAllAPsEvent,
+                                        TimerPeriodic,
+                                        POLL_INTERVAL_US
+                                        );
+  return Status;
+}
+
+/** Handles execution of StartupAllAPs when no wait event has been specified.
+
+   @param Procedure             The user-supplied procedure.
+   @param ProcedureArgument     The user-supplied procedure argument.
+   @param TimeoutInMicroseconds The timeout for the work to be completed. Zero
+                                indicates an infinite timeout.
+   @param SingleThread          Whether the APs will execute sequentially.
+   @param FailedCpuList         User-supplied pointer for list of failed CPUs.
+
+   @return EFI_SUCCESS on success.
+**/
+STATIC
+EFI_STATUS
+StartupAllAPsNoWaitEvent (
+  IN EFI_AP_PROCEDURE  Procedure,
+  IN VOID              *ProcedureArgument,
+  IN UINTN             TimeoutInMicroseconds,
+  IN BOOLEAN           SingleThread,
+  IN UINTN             **FailedCpuList
+  )
+{
+  EFI_STATUS   Status;
+  UINTN        Index;
+  UINTN        NextIndex;
+  UINTN        Timeout;
+  CPU_AP_DATA  *CpuData;
+
+  Timeout = TimeoutInMicroseconds;
+
+  while (TRUE) {
+    for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+      CpuData = &mCpuMpData.CpuData[Index];
+      if (IsProcessorBSP (Index)) {
+        // Skip BSP
+        continue;
+      }
+
+      if (!IsProcessorEnabled (Index)) {
+        // Skip Disabled processors
+        continue;
+      }
+
+      switch (GetApState (CpuData)) {
+        case CpuStateReady:
+          SetApProcedure (CpuData, Procedure, ProcedureArgument);
+          Status = DispatchCpu (Index);
+          if (EFI_ERROR (Status)) {
+            CpuData->State = CpuStateIdle;
+            Status         = EFI_NOT_READY;
+            goto Done;
+          }
+
+          break;
+
+        case CpuStateFinished:
+          mCpuMpData.FinishCount++;
+          if (SingleThread) {
+            Status = GetNextBlockedNumber (&NextIndex);
+            if (!EFI_ERROR (Status)) {
+              mCpuMpData.CpuData[NextIndex].State = CpuStateReady;
+            }
+          }
+
+          CpuData->State = CpuStateIdle;
+          break;
+
+        default:
+          break;
+      }
+    }
+
+    if (mCpuMpData.FinishCount == mCpuMpData.StartCount) {
+      Status = EFI_SUCCESS;
+      goto Done;
+    }
+
+    if ((TimeoutInMicroseconds != 0) && (Timeout == 0)) {
+      Status = EFI_TIMEOUT;
+      goto Done;
+    }
+
+    Timeout -= CalculateAndStallInterval (Timeout);
+  }
+
+Done:
+  if (FailedCpuList != NULL) {
+    if (mCpuMpData.FailedListIndex == 0) {
+      FreePool (*FailedCpuList);
+      *FailedCpuList = NULL;
+    }
+  }
+
+  return Status;
+}
+
+/**
+  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.
+
+  @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]  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] 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
+MpInitLibStartupAllAPs (
+  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;
+
+  if (!IsCurrentProcessorBSP ()) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  if (mCpuMpData.NumberOfProcessors == 1) {
+    return EFI_NOT_STARTED;
+  }
+
+  if (Procedure == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((WaitEvent != NULL) && !mNonBlockingModeAllowed) {
+    return EFI_UNSUPPORTED;
+  }
+
+  if (!CheckAllCpusReady ()) {
+    return EFI_NOT_READY;
+  }
+
+  if (FailedCpuList != NULL) {
+    mCpuMpData.FailedList = AllocatePool (
+                              (mCpuMpData.NumberOfProcessors + 1) *
+                              sizeof (UINTN)
+                              );
+    if (mCpuMpData.FailedList == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    SetMemN (
+      mCpuMpData.FailedList,
+      (mCpuMpData.NumberOfProcessors + 1) *
+      sizeof (UINTN),
+      END_OF_CPU_LIST
+      );
+    mCpuMpData.FailedListIndex = 0;
+    *FailedCpuList             = mCpuMpData.FailedList;
+  }
+
+  StartupAllAPsPrepareState (SingleThread);
+
+  if (WaitEvent != NULL) {
+    Status = StartupAllAPsWithWaitEvent (
+               Procedure,
+               ProcedureArgument,
+               WaitEvent,
+               TimeoutInMicroseconds
+               );
+  } else {
+    Status = StartupAllAPsNoWaitEvent (
+               Procedure,
+               ProcedureArgument,
+               TimeoutInMicroseconds,
+               SingleThread,
+               FailedCpuList
+               );
+  }
+
+  return Status;
+}
+
+/**
+  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.
+
+  @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
+MpInitLibStartupThisAP (
+  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   Status;
+  UINTN        Timeout;
+  CPU_AP_DATA  *CpuData;
+
+  if (!IsCurrentProcessorBSP ()) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  if (Procedure == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (ProcessorNumber >= mCpuMpData.NumberOfProcessors) {
+    return EFI_NOT_FOUND;
+  }
+
+  CpuData = &mCpuMpData.CpuData[ProcessorNumber];
+
+  if (IsProcessorBSP (ProcessorNumber)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (!IsProcessorEnabled (ProcessorNumber)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (GetApState (CpuData) != CpuStateIdle) {
+    return EFI_NOT_READY;
+  }
+
+  if ((WaitEvent != NULL) && !mNonBlockingModeAllowed) {
+    return EFI_UNSUPPORTED;
+  }
+
+  Timeout = TimeoutInMicroseconds;
+
+  mCpuMpData.StartCount  = 1;
+  mCpuMpData.FinishCount = 0;
+
+  SetApProcedure (
+    CpuData,
+    Procedure,
+    ProcedureArgument
+    );
+
+  Status = DispatchCpu (ProcessorNumber);
+  if (EFI_ERROR (Status)) {
+    CpuData->State = CpuStateIdle;
+    return EFI_NOT_READY;
+  }
+
+  if (WaitEvent != NULL) {
+    // Non Blocking
+    mCpuMpData.WaitEvent = WaitEvent;
+    gBS->SetTimer (
+           CpuData->CheckThisAPEvent,
+           TimerPeriodic,
+           POLL_INTERVAL_US
+           );
+    return EFI_SUCCESS;
+  }
+
+  // Blocking
+  while (TRUE) {
+    if (GetApState (CpuData) == CpuStateFinished) {
+      CpuData->State = CpuStateIdle;
+      break;
+    }
+
+    if ((TimeoutInMicroseconds != 0) && (Timeout == 0)) {
+      return EFI_TIMEOUT;
+    }
+
+    Timeout -= CalculateAndStallInterval (Timeout);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This service switches the requested AP to be the BSP from that point onward.
+  This service changes the BSP for all purposes.   This call can only be
+  performed by the current BSP.
+
+  @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] EnableOldBSP      If TRUE, then the old BSP will be listed as an
+                               enabled AP. Otherwise, it will be disabled.
+
+  @retval EFI_SUCCESS             BSP successfully switched.
+  @retval EFI_UNSUPPORTED         Switching the BSP cannot be completed prior to
+                                  this service returning.
+  @retval EFI_UNSUPPORTED         Switching the BSP is not supported.
+  @retval EFI_SUCCESS             The calling processor is an AP.
+  @retval EFI_NOT_FOUND           The processor with the handle specified by
+                                  ProcessorNumber does not exist.
+  @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the current BSP or
+                                  a disabled AP.
+  @retval EFI_NOT_READY           The specified AP is busy.
+
+**/
+EFI_STATUS
+EFIAPI
+MpInitLibSwitchBSP (
+  IN EFI_MP_SERVICES_PROTOCOL  *This,
+  IN  UINTN                    ProcessorNumber,
+  IN  BOOLEAN                  EnableOldBSP
+  )
+{
+  // Skip for now as we need switch a bunch of stack stuff around and it's
+  // complex. May not be worth it?
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  This service lets the caller enable or disable an AP from this point onward.
+  This service may only be called from the BSP.
+
+  @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
+MpInitLibEnableDisableAP (
+  IN  EFI_MP_SERVICES_PROTOCOL  *This,
+  IN  UINTN                     ProcessorNumber,
+  IN  BOOLEAN                   EnableAP,
+  IN  UINT32                    *HealthFlag OPTIONAL
+  )
+{
+  UINTN        StatusFlag;
+  CPU_AP_DATA  *CpuData;
+
+  StatusFlag = mCpuMpData.CpuData[ProcessorNumber].Info.StatusFlag;
+  CpuData    = &mCpuMpData.CpuData[ProcessorNumber];
+
+  if (!IsCurrentProcessorBSP ()) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  if (ProcessorNumber >= mCpuMpData.NumberOfProcessors) {
+    return EFI_NOT_FOUND;
+  }
+
+  if (IsProcessorBSP (ProcessorNumber)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (GetApState (CpuData) != CpuStateIdle) {
+    return EFI_UNSUPPORTED;
+  }
+
+  if (EnableAP) {
+    if (!IsProcessorEnabled (ProcessorNumber)) {
+      mCpuMpData.NumberOfEnabledProcessors++;
+    }
+
+    StatusFlag |= PROCESSOR_ENABLED_BIT;
+  } else {
+    if (IsProcessorEnabled (ProcessorNumber)) {
+      mCpuMpData.NumberOfEnabledProcessors--;
+    }
+
+    StatusFlag &= ~PROCESSOR_ENABLED_BIT;
+  }
+
+  if (HealthFlag != NULL) {
+    StatusFlag &= ~PROCESSOR_HEALTH_STATUS_BIT;
+    StatusFlag |= (*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This return the handle number for the calling processor.  This service may be
+  called from the BSP and APs.
+
+  @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+  @param[out] 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
+MpInitLibWhoAmI (
+  IN EFI_MP_SERVICES_PROTOCOL  *This,
+  OUT UINTN                    *ProcessorNumber
+  )
+{
+  UINTN        Index;
+  UINT64       ProcessorId;
+  CPU_AP_DATA  *CpuData;
+
+  if (ProcessorNumber == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ProcessorId = GET_MPIDR_AFFINITY_BITS (ArmReadMpidr ());
+  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+    CpuData = &mCpuMpData.CpuData[Index];
+    if (CpuData->Info.ProcessorId == ProcessorId) {
+      break;
+    }
+  }
+
+  *ProcessorNumber = Index;
+  return EFI_SUCCESS;
+}
+
+/** Adds the specified processor the list of failed processors.
+
+   @param ProcessorIndex The processor index to add.
+   @param ApState        Processor state.
+
+**/
+STATIC
+VOID
+AddProcessorToFailedList (
+  UINTN      ProcessorIndex,
+  CPU_STATE  ApState
+  )
+{
+  UINTN    Index;
+  BOOLEAN  Found;
+
+  Found = FALSE;
+
+  if (ApState == CpuStateIdle) {
+    return;
+  }
+
+  // If we are retrying make sure we don't double count
+  for (Index = 0; Index < mCpuMpData.FailedListIndex; Index++) {
+    if (mCpuMpData.FailedList[Index] == ProcessorIndex) {
+      Found = TRUE;
+      break;
+    }
+  }
+
+  /* If the CPU isn't already in the FailedList, add it */
+  if (!Found) {
+    mCpuMpData.FailedList[mCpuMpData.FailedListIndex++] = Index;
+  }
+}
+
+/** Handles the StartupAllAPs case where the timeout has occurred.
+
+**/
+STATIC
+VOID
+ProcessStartupAllAPsTimeout (
+  VOID
+  )
+{
+  CPU_AP_DATA  *CpuData;
+  UINTN        Index;
+
+  if (mCpuMpData.FailedList == NULL) {
+    return;
+  }
+
+  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+    CpuData = &mCpuMpData.CpuData[Index];
+    if (IsProcessorBSP (Index)) {
+      // Skip BSP
+      continue;
+    }
+
+    if (!IsProcessorEnabled (Index)) {
+      // Skip Disabled processors
+      continue;
+    }
+
+    CpuData = &mCpuMpData.CpuData[Index];
+    AddProcessorToFailedList (Index, GetApState (CpuData));
+  }
+}
+
+/** Updates the status of the APs.
+
+   @param[in] ProcessorIndex The index of the AP to update.
+**/
+STATIC
+VOID
+UpdateApStatus (
+  IN UINTN  ProcessorIndex
+  )
+{
+  EFI_STATUS   Status;
+  CPU_AP_DATA  *CpuData;
+  CPU_AP_DATA  *NextCpuData;
+  CPU_STATE    State;
+  UINTN        NextNumber;
+
+  CpuData = &mCpuMpData.CpuData[ProcessorIndex];
+
+  if (IsProcessorBSP (ProcessorIndex)) {
+    // Skip BSP
+    return;
+  }
+
+  if (!IsProcessorEnabled (ProcessorIndex)) {
+    // Skip Disabled processors
+    return;
+  }
+
+  State = GetApState (CpuData);
+
+  switch (State) {
+    case CpuStateFinished:
+      if (mCpuMpData.SingleThread) {
+        Status = GetNextBlockedNumber (&NextNumber);
+        if (!EFI_ERROR (Status)) {
+          NextCpuData = &mCpuMpData.CpuData[NextNumber];
+
+          NextCpuData->State = CpuStateReady;
+
+          SetApProcedure (
+            NextCpuData,
+            mCpuMpData.Procedure,
+            mCpuMpData.ProcedureArgument
+            );
+        }
+      }
+
+      CpuData->State = CpuStateIdle;
+      mCpuMpData.FinishCount++;
+      break;
+
+    default:
+      break;
+  }
+}
+
+/**
+  If a timeout is specified in StartupAllAps(), a timer is set, which invokes
+  this procedure periodically to check whether all APs have finished.
+
+  @param[in] Event   The WaitEvent the user supplied.
+  @param[in] Context The event context.
+**/
+STATIC
+VOID
+EFIAPI
+CheckAllAPsStatus (
+  IN  EFI_EVENT  Event,
+  IN  VOID       *Context
+  )
+{
+  UINTN  Index;
+
+  if (mCpuMpData.TimeoutActive) {
+    mCpuMpData.Timeout -= CalculateAndStallInterval (mCpuMpData.Timeout);
+  }
+
+  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+    UpdateApStatus (Index);
+  }
+
+  if (mCpuMpData.TimeoutActive && (mCpuMpData.Timeout == 0)) {
+    ProcessStartupAllAPsTimeout ();
+
+    // Force terminal exit
+    mCpuMpData.FinishCount = mCpuMpData.StartCount;
+  }
+
+  if (mCpuMpData.FinishCount != mCpuMpData.StartCount) {
+    return;
+  }
+
+  gBS->SetTimer (
+         mCpuMpData.CheckAllAPsEvent,
+         TimerCancel,
+         0
+         );
+
+  if (mCpuMpData.FailedListIndex == 0) {
+    if (mCpuMpData.FailedList != NULL) {
+      FreePool (mCpuMpData.FailedList);
+      mCpuMpData.FailedList = NULL;
+    }
+  }
+
+  gBS->SignalEvent (mCpuMpData.WaitEvent);
+}
+
+/** Invoked periodically via a timer to check the state of the processor.
+
+   @param Event   The event supplied by the timer expiration.
+   @param Context The processor context.
+
+**/
+STATIC
+VOID
+EFIAPI
+CheckThisAPStatus (
+  IN  EFI_EVENT  Event,
+  IN  VOID       *Context
+  )
+{
+  EFI_STATUS   Status;
+  CPU_AP_DATA  *CpuData;
+  CPU_STATE    State;
+
+  CpuData             = Context;
+  CpuData->TimeTaken += POLL_INTERVAL_US;
+
+  State = GetApState (CpuData);
+
+  if (State == CpuStateFinished) {
+    Status = gBS->SetTimer (CpuData->CheckThisAPEvent, TimerCancel, 0);
+    ASSERT_EFI_ERROR (Status);
+
+    if (mCpuMpData.WaitEvent != NULL) {
+      Status = gBS->SignalEvent (mCpuMpData.WaitEvent);
+      ASSERT_EFI_ERROR (Status);
+    }
+
+    CpuData->State = CpuStateIdle;
+  }
+
+  if (CpuData->TimeTaken > CpuData->Timeout) {
+    if (mCpuMpData.WaitEvent != NULL) {
+      Status = gBS->SignalEvent (mCpuMpData.WaitEvent);
+      ASSERT_EFI_ERROR (Status);
+    }
+  }
+}
+
+/**
+  This function is called by all processors (both BSP and AP) once and collects
+  MP related data.
+
+  @param BSP            TRUE if the processor is the BSP.
+  @param Mpidr          The MPIDR for the specified processor. This should be
+                        the full MPIDR and not only the affinity bits.
+  @param ProcessorIndex The index of the processor.
+
+  @return EFI_SUCCESS if the data for the processor collected and filled in.
+
+**/
+STATIC
+EFI_STATUS
+FillInProcessorInformation (
+  IN BOOLEAN  BSP,
+  IN UINTN    Mpidr,
+  IN UINTN    ProcessorIndex
+  )
+{
+  EFI_PROCESSOR_INFORMATION  *CpuInfo;
+
+  CpuInfo = &mCpuMpData.CpuData[ProcessorIndex].Info;
+
+  CpuInfo->ProcessorId = GET_MPIDR_AFFINITY_BITS (Mpidr);
+  CpuInfo->StatusFlag  = PROCESSOR_ENABLED_BIT | PROCESSOR_HEALTH_STATUS_BIT;
+
+  if (BSP) {
+    CpuInfo->StatusFlag |= PROCESSOR_AS_BSP_BIT;
+  }
+
+  if (Mpidr & MPIDR_MT_BIT) {
+    CpuInfo->Location.Package = GET_MPIDR_AFF2 (Mpidr);
+    CpuInfo->Location.Core    = GET_MPIDR_AFF1 (Mpidr);
+    CpuInfo->Location.Thread  = GET_MPIDR_AFF0 (Mpidr);
+
+    CpuInfo->ExtendedInformation.Location2.Package = GET_MPIDR_AFF3 (Mpidr);
+    CpuInfo->ExtendedInformation.Location2.Die     = GET_MPIDR_AFF2 (Mpidr);
+    CpuInfo->ExtendedInformation.Location2.Core    = GET_MPIDR_AFF1 (Mpidr);
+    CpuInfo->ExtendedInformation.Location2.Thread  = GET_MPIDR_AFF0 (Mpidr);
+  } else {
+    CpuInfo->Location.Package = GET_MPIDR_AFF1 (Mpidr);
+    CpuInfo->Location.Core    = GET_MPIDR_AFF0 (Mpidr);
+    CpuInfo->Location.Thread  = 0;
+
+    CpuInfo->ExtendedInformation.Location2.Package = GET_MPIDR_AFF2 (Mpidr);
+    CpuInfo->ExtendedInformation.Location2.Die     = GET_MPIDR_AFF1 (Mpidr);
+    CpuInfo->ExtendedInformation.Location2.Core    = GET_MPIDR_AFF0 (Mpidr);
+    CpuInfo->ExtendedInformation.Location2.Thread  = 0;
+  }
+
+  CpuInfo->ExtendedInformation.Location2.Package = 0;
+  CpuInfo->ExtendedInformation.Location2.Module  = 0;
+  CpuInfo->ExtendedInformation.Location2.Tile    = 0;
+
+  mCpuMpData.CpuData[ProcessorIndex].State = BSP ? CpuStateBusy : CpuStateIdle;
+
+  mCpuMpData.CpuData[ProcessorIndex].Procedure = NULL;
+  mCpuMpData.CpuData[ProcessorIndex].Parameter = NULL;
+
+  return EFI_SUCCESS;
+}
+
+/** Initializes the MP Services system data
+
+   @param NumberOfProcessors The number of processors, both BSP and AP.
+   @param CpuInfo            CPU information gathered earlier during boot.
+
+**/
+VOID
+MpInitLibInitialize (
+  IN   UINTN                NumberOfProcessors,
+  IN   ARM_PROCESSOR_TABLE  *CpuInfo
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Index;
+  EFI_EVENT   ReadyToBootEvent;
+
+  //
+  // Clear the data structure area first.
+  //
+  ZeroMem (&mCpuMpData, sizeof (CPU_MP_DATA));
+  //
+  // First BSP fills and inits all known values, including its own records.
+  //
+  mCpuMpData.NumberOfProcessors        = NumberOfProcessors;
+  mCpuMpData.NumberOfEnabledProcessors = NumberOfProcessors;
+
+  mCpuMpData.CpuData = AllocateZeroPool (
+                         mCpuMpData.NumberOfProcessors *
+                         sizeof (CPU_AP_DATA)
+                         );
+  ASSERT (mCpuMpData.CpuData != NULL);
+
+  /* Allocate one extra for the NULL entry at the end */
+  gProcessorIDs = AllocatePool ((mCpuMpData.NumberOfProcessors + 1) * sizeof (UINT64));
+  ASSERT (gProcessorIDs != NULL);
+
+  FillInProcessorInformation (TRUE, CpuInfo->ArmCpus[0].Mpidr, 0);
+  gProcessorIDs[0] = mCpuMpData.CpuData[0].Info.ProcessorId;
+
+  Status = gBS->CreateEvent (
+                  EVT_TIMER | EVT_NOTIFY_SIGNAL,
+                  TPL_CALLBACK,
+                  CheckAllAPsStatus,
+                  NULL,
+                  &mCpuMpData.CheckAllAPsEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  gApStacksBase = AllocatePool (
+                    mCpuMpData.NumberOfProcessors *
+                    gApStackSize
+                    );
+  ASSERT (gApStacksBase != NULL);
+
+  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
+    if (IsProcessorBSP (Index)) {
+      /* Skip BSP */
+      continue;
+    }
+
+    FillInProcessorInformation (FALSE, CpuInfo->ArmCpus[Index].Mpidr, Index);
+
+    gProcessorIDs[Index] = mCpuMpData.CpuData[Index].Info.ProcessorId;
+
+    Status = gBS->CreateEvent (
+                    EVT_TIMER | EVT_NOTIFY_SIGNAL,
+                    TPL_CALLBACK,
+                    CheckThisAPStatus,
+                    (VOID *)&mCpuMpData.CpuData[Index],
+                    &mCpuMpData.CpuData[Index].CheckThisAPEvent
+                    );
+    ASSERT (Status == EFI_SUCCESS);
+  }
+
+  Status = EfiCreateEventReadyToBootEx (
+             TPL_CALLBACK,
+             ReadyToBootSignaled,
+             NULL,
+             &ReadyToBootEvent
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  gProcessorIDs[Index] = MAX_UINT32;
+}
+
+/**
+  Event notification function called when the EFI_EVENT_GROUP_READY_TO_BOOT is
+  signaled. After this point, non-blocking mode is no longer allowed.
+
+  @param  Event     Event whose notification function is being invoked.
+  @param  Context   The pointer to the notification function's context,
+                    which is implementation-dependent.
+
+**/
+STATIC
+VOID
+EFIAPI
+ReadyToBootSignaled (
+  IN  EFI_EVENT  Event,
+  IN  VOID       *Context
+  )
+{
+  mNonBlockingModeAllowed = FALSE;
+}
diff --git a/ArmPkg/Library/MpInitLib/InternalMpInitLib.h b/ArmPkg/Library/MpInitLib/InternalMpInitLib.h
new file mode 100644
index 000000000000..461d85ffd28f
--- /dev/null
+++ b/ArmPkg/Library/MpInitLib/InternalMpInitLib.h
@@ -0,0 +1,359 @@
+/** @file
+
+Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
+Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
+Portions copyright (c) 2011, Apple Inc. All rights reserved.
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef MP_INTERNAL_INIT_LIB_H_
+#define MP_INTERNAL_INIT_LIB_H_
+
+#include <Protocol/Cpu.h>
+#include <Protocol/MpService.h>
+
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+
+
+#define AP_STACK_SIZE 0x1000
+
+//
+// Internal Data Structures
+//
+
+//
+// AP state
+//
+// The state transitions for an AP when it process a procedure are:
+//  Idle ----> Ready ----> Busy ----> Idle
+//       [BSP]       [AP]       [AP]
+//
+typedef enum {
+  CpuStateIdle,
+  CpuStateReady,
+  CpuStateBlocked,
+  CpuStateBusy,
+  CpuStateFinished,
+  CpuStateDisabled
+} CPU_STATE;
+
+//
+// Define Individual Processor Data block.
+//
+typedef struct {
+  EFI_PROCESSOR_INFORMATION    Info;
+  EFI_AP_PROCEDURE             Procedure;
+  VOID                         *Parameter;
+  CPU_STATE                    State;
+  EFI_EVENT                    CheckThisAPEvent;
+  UINTN                        Timeout;
+  UINTN                        TimeTaken;
+} CPU_AP_DATA;
+
+//
+// Define MP data block which consumes individual processor block.
+//
+typedef struct {
+  UINTN                   NumberOfProcessors;
+  UINTN                   NumberOfEnabledProcessors;
+  EFI_EVENT               CheckAllAPsEvent;
+  EFI_EVENT               WaitEvent;
+  UINTN                   FinishCount;
+  UINTN                   StartCount;
+  EFI_AP_PROCEDURE        Procedure;
+  VOID                    *ProcedureArgument;
+  BOOLEAN                 SingleThread;
+  UINTN                   StartedNumber;
+  CPU_AP_DATA             *CpuData;
+  UINTN                   Timeout;
+  UINTN                   *FailedList;
+  UINTN                   FailedListIndex;
+  BOOLEAN                 TimeoutActive;
+} CPU_MP_DATA;
+
+EFI_STATUS
+EFIAPI
+CpuMpServicesInit (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  );
+
+extern EFI_MP_SERVICES_PROTOCOL  mMpServicesTemplate;
+
+/** Secondary core entry point.
+
+**/
+VOID  ApEntryPoint (
+  VOID
+  );
+
+/** C entry-point for the AP.
+    This function gets called from the assembly function ApEntryPoint.
+**/
+VOID
+ApProcedure (
+  VOID
+  );
+
+/** Turns on the specified core using PSCI and executes the user-supplied
+    function that's been configured via a previous call to SetApProcedure.
+
+   @param ProcessorIndex The index of the core to turn on.
+
+   @retval EFI_SUCCESS       The processor was successfully turned on.
+   @retval EFI_DEVICE_ERROR  An error occurred turning the processor on.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+DispatchCpu (
+  IN UINTN ProcessorIndex
+  );
+
+/** Returns whether the specified processor is the BSP.
+
+   @param[in] ProcessorIndex The index the processor to check.
+
+   @return TRUE if the processor is the BSP, FALSE otherwise.
+**/
+STATIC
+BOOLEAN
+IsProcessorBSP (
+  UINTN ProcessorIndex
+  );
+
+/** Returns whether the processor executing this function is the BSP.
+
+   @return Whether the current processor is the BSP.
+**/
+STATIC
+BOOLEAN
+IsCurrentProcessorBSP (
+  VOID
+  );
+
+/** Returns whether the specified processor is enabled.
+
+   @param[in] ProcessorIndex The index of the processor to check.
+
+   @return TRUE if the processor is enabled, FALSE otherwise.
+**/
+STATIC
+BOOLEAN
+IsProcessorEnabled (
+  UINTN ProcessorIndex
+  );
+
+/** Configures the processor context with the user-supplied procedure and
+    argument.
+
+   @param CpuData           The processor context.
+   @param Procedure         The user-supplied procedure.
+   @param ProcedureArgument The user-supplied procedure argument.
+
+**/
+STATIC
+VOID
+SetApProcedure (
+  IN   CPU_AP_DATA         *CpuData,
+  IN   EFI_AP_PROCEDURE    Procedure,
+  IN   VOID                *ProcedureArgument
+  );
+
+/**
+  Get the Application Processors state.
+
+  @param[in]  CpuData    The pointer to CPU_AP_DATA of specified AP
+
+  @return  The AP status
+**/
+CPU_STATE
+GetApState (
+  IN  CPU_AP_DATA     *CpuData
+  );
+
+/** Returns the index of the next processor that is blocked.
+
+   @param[out] NextNumber The index of the next blocked processor.
+
+   @retval EFI_SUCCESS   Successfully found the next blocked processor.
+   @retval EFI_NOT_FOUND There are no blocked processors.
+
+**/
+STATIC
+EFI_STATUS
+GetNextBlockedNumber (
+  OUT UINTN *NextNumber
+  );
+
+/** Stalls the BSP for the minimum of gPollInterval and Timeout.
+
+   @param[in]  Timeout    The time limit in microseconds remaining for
+                          APs to return from Procedure.
+
+   @retval     StallTime  Time of execution stall.
+**/
+STATIC
+UINTN
+CalculateAndStallInterval (
+  IN UINTN Timeout
+  );
+
+/** Returns whether all processors are in the idle state.
+
+   @return Whether all the processors are idle.
+
+**/
+STATIC
+BOOLEAN
+CheckAllCpusReady (
+  VOID
+  );
+
+/** Sets up the state for the StartupAllAPs function.
+
+   @param SingleThread Whether the APs will execute sequentially.
+
+**/
+STATIC
+VOID
+StartupAllAPsPrepareState (
+  IN BOOLEAN SingleThread
+  );
+
+/** Handles execution of StartupAllAPs when a WaitEvent has been specified.
+
+   @param Procedure         The user-supplied procedure.
+   @param ProcedureArgument The user-supplied procedure argument.
+   @param WaitEvent         The wait event to be signaled when the work is
+                            complete or a timeout has occurred.
+   @param TimeoutInMicroseconds The timeout for the work to be completed. Zero
+                                indicates an infinite timeout.
+
+   @return EFI_SUCCESS on success.
+**/
+STATIC
+EFI_STATUS
+StartupAllAPsWithWaitEvent (
+  IN EFI_AP_PROCEDURE Procedure,
+  IN VOID             *ProcedureArgument,
+  IN EFI_EVENT        WaitEvent,
+  IN UINTN            TimeoutInMicroseconds
+  );
+
+/** Handles execution of StartupAllAPs when no wait event has been specified.
+
+   @param Procedure             The user-supplied procedure.
+   @param ProcedureArgument     The user-supplied procedure argument.
+   @param TimeoutInMicroseconds The timeout for the work to be completed. Zero
+                                indicates an infinite timeout.
+   @param SingleThread          Whether the APs will execute sequentially.
+   @param FailedCpuList         User-supplied pointer for list of failed CPUs.
+
+   @return EFI_SUCCESS on success.
+**/
+STATIC
+EFI_STATUS
+StartupAllAPsNoWaitEvent (
+  IN EFI_AP_PROCEDURE Procedure,
+  IN VOID             *ProcedureArgument,
+  IN UINTN            TimeoutInMicroseconds,
+  IN BOOLEAN          SingleThread,
+  IN UINTN            **FailedCpuList
+  );
+
+
+/** Adds the specified processor the list of failed processors.
+
+   @param ProcessorIndex The processor index to add.
+   @param ApState         Processor state.
+
+**/
+STATIC
+VOID
+AddProcessorToFailedList (
+  UINTN           ProcessorIndex,
+  CPU_STATE       ApState
+  );
+
+/** Handles the StartupAllAPs case where the timeout has occurred.
+
+**/
+STATIC
+VOID
+ProcessStartupAllAPsTimeout (
+  VOID
+  );
+
+/**
+  If a timeout is specified in StartupAllAps(), a timer is set, which invokes
+  this procedure periodically to check whether all APs have finished.
+
+  @param[in] Event   The WaitEvent the user supplied.
+  @param[in] Context The event context.
+**/
+STATIC
+VOID
+EFIAPI
+CheckAllAPsStatus (
+  IN  EFI_EVENT   Event,
+  IN  VOID        *Context
+  );
+
+/** Invoked periodically via a timer to check the state of the processor.
+
+   @param Event   The event supplied by the timer expiration.
+   @param Context The processor context.
+
+**/
+STATIC
+VOID
+EFIAPI
+CheckThisAPStatus (
+  IN  EFI_EVENT   Event,
+  IN  VOID        *Context
+  );
+
+/**
+  This function is called by all processors (both BSP and AP) once and collects
+  MP related data.
+
+  @param BSP            TRUE if the processor is the BSP.
+  @param Mpidr          The MPIDR for the specified processor. This should be
+                        the full MPIDR and not only the affinity bits.
+  @param ProcessorIndex The index of the processor.
+
+  @return EFI_SUCCESS if the data for the processor collected and filled in.
+
+**/
+STATIC
+EFI_STATUS
+FillInProcessorInformation (
+  IN BOOLEAN      BSP,
+  IN UINTN        Mpidr,
+  IN UINTN        ProcessorIndex
+  );
+
+/**
+  Event notification function called when the EFI_EVENT_GROUP_READY_TO_BOOT is
+  signaled. After this point, non-blocking mode is no longer allowed.
+
+  @param  Event     Event whose notification function is being invoked.
+  @param  Context   The pointer to the notification function's context,
+                    which is implementation-dependent.
+
+**/
+STATIC
+VOID
+EFIAPI
+ReadyToBootSignaled (
+  IN  EFI_EVENT                Event,
+  IN  VOID                     *Context
+  );
+
+
+#endif /* MP_INTERNAL_INIT_LIB_H_ */
diff --git a/ArmVirtPkg/ArmVirt.dsc.inc b/ArmVirtPkg/ArmVirt.dsc.inc
index 5a1598d90ca7..af6e48fd6cfc 100644
--- a/ArmVirtPkg/ArmVirt.dsc.inc
+++ b/ArmVirtPkg/ArmVirt.dsc.inc
@@ -261,6 +261,9 @@
 [LibraryClasses.ARM]
   ArmSoftFloatLib|ArmPkg/Library/ArmSoftFloatLib/ArmSoftFloatLib.inf
 
+[LibraryClasses.AARCH64]
+  MpInitLib|ArmPkg/Library/MpInitLib/DxeMpInitLib.inf
+
 [BuildOptions]
   RVCT:RELEASE_*_*_CC_FLAGS  = -DMDEPKG_NDEBUG
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 1/2] ArmPkg: Replace CoreId and ClusterId with Mpidr in ARM_CORE_INFO struct
  2021-12-15 17:46 ` [PATCH v2 1/2] ArmPkg: Replace CoreId and ClusterId with Mpidr in ARM_CORE_INFO struct Rebecca Cran
@ 2021-12-15 17:53   ` Ard Biesheuvel
  2021-12-15 20:33     ` Rebecca Cran
  0 siblings, 1 reply; 9+ messages in thread
From: Ard Biesheuvel @ 2021-12-15 17:53 UTC (permalink / raw)
  To: Rebecca Cran
  Cc: edk2-devel-groups-io, Ard Biesheuvel, Gerd Hoffmann,
	Samer El-Haj-Mahmoud, Leif Lindholm, Sami Mujawar

Hi Rebecca,

Some nits below.

On Wed, 15 Dec 2021 at 18:46, Rebecca Cran <rebecca@nuviainc.com> wrote:
>
> Remove the ClusterId and CoreId fields in the ARM_CORE_INFO structure in
> favor of a new Mpidr field. Update code in
> ArmPlatformPkg/PrePeiCore/MainMPCore and ArmPlatformPkg/PrePi/MainMPCore.c
> to use the new field and call new macros GET_MPIDR_AFF0 and GET_MPIDR_AFF1
> instead.
>
> Signed-off-by: Rebecca Cran <rebecca@nuviainc.com>
> ---
>  ArmPkg/Include/Guid/ArmMpCoreInfo.h                            |  3 +--
>  ArmPkg/Include/Library/ArmLib.h                                | 10 +++++++---
>  ArmPlatformPkg/Library/ArmPlatformLibNull/ArmPlatformLibNull.c |  8 ++++----
>  ArmPlatformPkg/PrePeiCore/MainMPCore.c                         |  2 +-
>  ArmPlatformPkg/PrePi/MainMPCore.c                              |  2 +-
>  5 files changed, 14 insertions(+), 11 deletions(-)
>
> diff --git a/ArmPkg/Include/Guid/ArmMpCoreInfo.h b/ArmPkg/Include/Guid/ArmMpCoreInfo.h
> index 06f9326ca021..43f0848e78b8 100644
> --- a/ArmPkg/Include/Guid/ArmMpCoreInfo.h
> +++ b/ArmPkg/Include/Guid/ArmMpCoreInfo.h
> @@ -14,8 +14,7 @@
>  #define MPIDR_U_BIT_MASK            0x40000000
>
>  typedef struct {
> -  UINT32                  ClusterId;
> -  UINT32                  CoreId;
> +  UINT64                  Mpidr;
>
>    // MP Core Mailbox
>    EFI_PHYSICAL_ADDRESS    MailboxSetAddress;
> diff --git a/ArmPkg/Include/Library/ArmLib.h b/ArmPkg/Include/Library/ArmLib.h
> index e4d0476090c7..5287bdfc8684 100644
> --- a/ArmPkg/Include/Library/ArmLib.h
> +++ b/ArmPkg/Include/Library/ArmLib.h
> @@ -108,10 +108,14 @@ typedef enum {
>
>  #define ARM_CORE_MASK     ARM_CORE_AFF0
>  #define ARM_CLUSTER_MASK  ARM_CORE_AFF1
> -#define GET_CORE_ID(MpId)            ((MpId) & ARM_CORE_MASK)
> -#define GET_CLUSTER_ID(MpId)         (((MpId) & ARM_CLUSTER_MASK) >> 8)
> +#define GET_CORE_ID(MpId)           ((MpId) & ARM_CORE_MASK)
> +#define GET_CLUSTER_ID(MpId)        (((MpId) & ARM_CLUSTER_MASK) >> 8)
>  #define GET_MPID(ClusterId, CoreId)  (((ClusterId) << 8) | (CoreId))
> -#define PRIMARY_CORE_ID  (PcdGet32(PcdArmPrimaryCore) & ARM_CORE_MASK)
> +#define GET_MPIDR_AFF0(MpId)  ((MpId) & ARM_CORE_AFF0)
> +#define GET_MPIDR_AFF1(MpId)  (((MpId) & ARM_CORE_AFF1) >> 8)
> +#define GET_MPIDR_AFF2(MpId)  (((MpId) & ARM_CORE_AFF2) >> 16)
> +#define GET_MPIDR_AFF3(MpId)  (((MpId) & ARM_CORE_AFF3) >> 32)
> +#define PRIMARY_CORE_ID       (PcdGet32(PcdArmPrimaryCore) & ARM_CORE_MASK)
>

Any reason in particular for these whitespace changes? If not, please
omit them - reviewing changes in unfamiliar code is challenging enough
without having to figure out which hunks actually matter and which
ones don't.

>  /** Reads the CCSIDR register for the specified cache.
>
> diff --git a/ArmPlatformPkg/Library/ArmPlatformLibNull/ArmPlatformLibNull.c b/ArmPlatformPkg/Library/ArmPlatformLibNull/ArmPlatformLibNull.c
> index eeab58805e67..852275f44fc3 100644
> --- a/ArmPlatformPkg/Library/ArmPlatformLibNull/ArmPlatformLibNull.c
> +++ b/ArmPlatformPkg/Library/ArmPlatformLibNull/ArmPlatformLibNull.c
> @@ -14,7 +14,7 @@
>  ARM_CORE_INFO  mArmPlatformNullMpCoreInfoTable[] = {
>    {
>      // Cluster 0, Core 0
> -    0x0, 0x0,
> +    0x0,
>
>      // MP Core MailBox Set/Get/Clear Addresses and Clear Value
>      (EFI_PHYSICAL_ADDRESS)0,
> @@ -24,7 +24,7 @@ ARM_CORE_INFO  mArmPlatformNullMpCoreInfoTable[] = {
>    },
>    {
>      // Cluster 0, Core 1
> -    0x0, 0x1,
> +    0x1,
>
>      // MP Core MailBox Set/Get/Clear Addresses and Clear Value
>      (EFI_PHYSICAL_ADDRESS)0,
> @@ -34,7 +34,7 @@ ARM_CORE_INFO  mArmPlatformNullMpCoreInfoTable[] = {
>    },
>    {
>      // Cluster 0, Core 2
> -    0x0, 0x2,
> +    0x2,
>
>      // MP Core MailBox Set/Get/Clear Addresses and Clear Value
>      (EFI_PHYSICAL_ADDRESS)0,
> @@ -44,7 +44,7 @@ ARM_CORE_INFO  mArmPlatformNullMpCoreInfoTable[] = {
>    },
>    {
>      // Cluster 0, Core 3
> -    0x0, 0x3,
> +    0x3,
>
>      // MP Core MailBox Set/Get/Clear Addresses and Clear Value
>      (EFI_PHYSICAL_ADDRESS)0,
> diff --git a/ArmPlatformPkg/PrePeiCore/MainMPCore.c b/ArmPlatformPkg/PrePeiCore/MainMPCore.c
> index 0b8e5dfb3f30..e6cb75157053 100644
> --- a/ArmPlatformPkg/PrePeiCore/MainMPCore.c
> +++ b/ArmPlatformPkg/PrePeiCore/MainMPCore.c
> @@ -68,7 +68,7 @@ SecondaryMain (
>
>    // Find the core in the ArmCoreTable
>    for (Index = 0; Index < ArmCoreCount; Index++) {
> -    if ((ArmCoreInfoTable[Index].ClusterId == ClusterId) && (ArmCoreInfoTable[Index].CoreId == CoreId)) {
> +    if ((GET_MPIDR_AFF1 (ArmCoreInfoTable[Index].Mpidr) == ClusterId) && (GET_MPIDR_AFF0 (ArmCoreInfoTable[Index].Mpidr) == CoreId)) {

Please rewrap overly long lines.

>        break;
>      }
>    }
> diff --git a/ArmPlatformPkg/PrePi/MainMPCore.c b/ArmPlatformPkg/PrePi/MainMPCore.c
> index ce53cea6367c..0433fd0f2e75 100644
> --- a/ArmPlatformPkg/PrePi/MainMPCore.c
> +++ b/ArmPlatformPkg/PrePi/MainMPCore.c
> @@ -67,7 +67,7 @@ SecondaryMain (
>
>    // Find the core in the ArmCoreTable
>    for (Index = 0; Index < ArmCoreCount; Index++) {
> -    if ((ArmCoreInfoTable[Index].ClusterId == ClusterId) && (ArmCoreInfoTable[Index].CoreId == CoreId)) {
> +    if ((GET_MPIDR_AFF1 (ArmCoreInfoTable[Index].Mpidr) == ClusterId) && (GET_MPIDR_AFF0 (ArmCoreInfoTable[Index].Mpidr) == CoreId)) {
>        break;
>      }
>    }
> --
> 2.31.1
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 2/2] ArmPkg: Add Library/MpInitLib to support EFI_MP_SERVICES_PROTOCOL
  2021-12-15 17:46 ` [PATCH v2 2/2] ArmPkg: Add Library/MpInitLib to support EFI_MP_SERVICES_PROTOCOL Rebecca Cran
@ 2021-12-15 17:55   ` Ard Biesheuvel
  2021-12-15 18:15     ` Rebecca Cran
  2021-12-15 21:26   ` Rebecca Cran
       [not found]   ` <16C10ACB9CD70BF0.6360@groups.io>
  2 siblings, 1 reply; 9+ messages in thread
From: Ard Biesheuvel @ 2021-12-15 17:55 UTC (permalink / raw)
  To: Rebecca Cran
  Cc: edk2-devel-groups-io, Ard Biesheuvel, Gerd Hoffmann,
	Samer El-Haj-Mahmoud, Leif Lindholm, Sami Mujawar

Hi Rebecca,

On Wed, 15 Dec 2021 at 18:47, Rebecca Cran <rebecca@nuviainc.com> wrote:
>
> Add support for EFI_MP_SERVICES_PROTOCOL during the DXE phase under
> AArch64.
>
> PSCI_CPU_ON is called to power on the core, the supplied procedure is
> executed and PSCI_CPU_OFF is called to power off the core.
>
> Minimal setup is done before calling the supplied procedure: for example
> the MMU and caches are not enabled.
>
> Signed-off-by: Rebecca Cran <rebecca@nuviainc.com>
> ---
>  ArmPkg/ArmPkg.dec                            |    4 +
>  ArmPkg/ArmPkg.dsc                            |    4 +
>  ArmPkg/Drivers/CpuDxe/AArch64/Arch.c         |   21 +
>  ArmPkg/Drivers/CpuDxe/Arm/Arch.c             |   21 +
>  ArmPkg/Drivers/CpuDxe/CpuDxe.c               |    2 +
>  ArmPkg/Drivers/CpuDxe/CpuDxe.h               |   10 +
>  ArmPkg/Drivers/CpuDxe/CpuDxe.inf             |    6 +
>  ArmPkg/Drivers/CpuDxe/CpuMpInit.c            |  608 ++++++++
>  ArmPkg/Include/Library/MpInitLib.h           |  366 +++++
>  ArmPkg/Library/MpInitLib/AArch64/MpFuncs.S   |   65 +
>  ArmPkg/Library/MpInitLib/DxeMpInitLib.inf    |   53 +
>  ArmPkg/Library/MpInitLib/DxeMpLib.c          | 1477 ++++++++++++++++++++
>  ArmPkg/Library/MpInitLib/InternalMpInitLib.h |  359 +++++

Perhaps I misunderstood your question about splitting up this patch,
as surely, adding a completely new library can be broken out into a
separate one?


>  ArmVirtPkg/ArmVirt.dsc.inc                   |    3 +
>  14 files changed, 2999 insertions(+)
>
> diff --git a/ArmPkg/ArmPkg.dec b/ArmPkg/ArmPkg.dec
> index 9da1bbc9f216..363eddc57393 100644
> --- a/ArmPkg/ArmPkg.dec
> +++ b/ArmPkg/ArmPkg.dec
> @@ -75,6 +75,10 @@
>    #
>    DefaultExceptionHandlerLib|Include/Library/DefaultExceptionHandlerLib.h
>
> +  ##  @libraryclass  Provides a MP Services interface.
> +  #
> +  MpInitLib|Include/Library/MpInitLib.h
> +
>    ##  @libraryclass  Provides an interface to query miscellaneous OEM
>    #   information.
>    #
> diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc
> index 59fd8f295d4f..6e053d6ee31d 100644
> --- a/ArmPkg/ArmPkg.dsc
> +++ b/ArmPkg/ArmPkg.dsc
> @@ -100,6 +100,9 @@
>    PeiServicesLib|MdePkg/Library/PeiServicesLib/PeiServicesLib.inf
>    PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf
>
> +[LibraryClasses.AARCH64]
> +  MpInitLib|ArmPkg/Library/MpInitLib/DxeMpInitLib.inf
> +
>  [LibraryClasses.ARM, LibraryClasses.AARCH64]
>    NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf
>
> @@ -163,6 +166,7 @@
>  [Components.AARCH64]
>    ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.inf
>    ArmPkg/Library/ArmMmuLib/ArmMmuPeiLib.inf
> +  ArmPkg/Library/MpInitLib/DxeMpInitLib.inf
>
>  [Components.AARCH64, Components.ARM]
>    ArmPkg/Library/StandaloneMmMmuLib/ArmMmuStandaloneMmLib.inf
> diff --git a/ArmPkg/Drivers/CpuDxe/AArch64/Arch.c b/ArmPkg/Drivers/CpuDxe/AArch64/Arch.c
> new file mode 100644
> index 000000000000..cb7cb747bc15
> --- /dev/null
> +++ b/ArmPkg/Drivers/CpuDxe/AArch64/Arch.c
> @@ -0,0 +1,21 @@
> +/** @file
> +  Architecture specific functions.
> +
> +  Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <CpuDxe.h>
> +
> +/** Initializes multi-processor support.
> + *
> +**/
> +VOID
> +ArchInitializeMpSupport (
> +  VOID
> +  )
> +{
> +  InitializeMpSupport ();
> +}
> diff --git a/ArmPkg/Drivers/CpuDxe/Arm/Arch.c b/ArmPkg/Drivers/CpuDxe/Arm/Arch.c
> new file mode 100644
> index 000000000000..f8d57b41225a
> --- /dev/null
> +++ b/ArmPkg/Drivers/CpuDxe/Arm/Arch.c
> @@ -0,0 +1,21 @@
> +/** @file
> +  Architecture specific functions.
> +
> +  Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <CpuDxe.h>
> +
> +/** Initializes multi-processor support.
> + *
> +**/
> +VOID
> +ArchInitializeMpSupport (
> +  VOID
> +  )
> +{
> +  /* Nothing to do - ARM doesn't support EFI_MP_SERVICES_PROTOCOL */
> +}
> diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.c b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> index 62a6e2d620a6..6c076982a1bd 100644
> --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
> @@ -275,5 +275,7 @@ CpuDxeInitialize (
>                    );
>    ASSERT_EFI_ERROR (Status);
>
> +  ArchInitializeMpSupport ();
> +
>    return Status;
>  }
> diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.h b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> index 58ee1444c1b3..3f04b89d7ad0 100644
> --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
> @@ -141,4 +141,14 @@ SetGcdMemorySpaceAttributes (
>    IN UINT64                           Attributes
>    );
>
> +VOID
> +InitializeMpSupport (
> +  VOID
> +  );
> +
> +VOID
> +ArchInitializeMpSupport (
> +  VOID
> +  );
> +
>  #endif // CPU_DXE_H_
> diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> index e5549fc71df7..f4cdb8ab5613 100644
> --- a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
> @@ -26,10 +26,13 @@
>    Exception.c
>
>  [Sources.ARM]
> +  Arm/Arch.c
>    Arm/Mmu.c
>
>  [Sources.AARCH64]
> +  AArch64/Arch.c
>    AArch64/Mmu.c
> +  CpuMpInit.c
>
>  [Packages]
>    ArmPkg/ArmPkg.dec
> @@ -37,6 +40,9 @@
>    MdePkg/MdePkg.dec
>    MdeModulePkg/MdeModulePkg.dec
>
> +[LibraryClasses.AARCH64]
> +  MpInitLib
> +
>  [LibraryClasses]
>    ArmLib
>    ArmMmuLib
> diff --git a/ArmPkg/Drivers/CpuDxe/CpuMpInit.c b/ArmPkg/Drivers/CpuDxe/CpuMpInit.c
> new file mode 100644
> index 000000000000..876a29e09b1b
> --- /dev/null
> +++ b/ArmPkg/Drivers/CpuDxe/CpuMpInit.c
> @@ -0,0 +1,608 @@
> +/** @file
> +  Construct MP Services Protocol.
> +
> +  The MP Services Protocol provides a generalized way of performing following tasks:
> +    - Retrieving information of multi-processor environment and MP-related status of
> +      specific processors.
> +    - Dispatching user-provided function to APs.
> +    - Maintain MP-related processor status.
> +
> +  The MP Services Protocol must be produced on any system with more than one logical
> +  processor.
> +
> +  The Protocol is available only during boot time.
> +
> +  MP Services Protocol is hardware-independent. Most of the logic of this protocol
> +  is architecturally neutral. It abstracts the multi-processor environment and
> +  status of processors, and provides interfaces to retrieve information, maintain,
> +  and dispatch.
> +
> +  MP Services Protocol may be consumed by ACPI module. The ACPI module may use this
> +  protocol to retrieve data that are needed for an MP platform and report them to OS.
> +  MP Services Protocol may also be used to program and configure processors, such
> +  as MTRR synchronization for memory space attributes setting in DXE Services.
> +  MP Services Protocol may be used by non-CPU DXE drivers to speed up platform boot
> +  by taking advantage of the processing capabilities of the APs, for example, using
> +  APs to help test system memory in parallel with other device initialization.
> +  Diagnostics applications may also use this protocol for multi-processor.
> +
> +  Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/HobLib.h>
> +#include <Library/MpInitLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +
> +/**
> +  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 the
> +                                          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.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +GetNumberOfProcessors (
> +  IN  EFI_MP_SERVICES_PROTOCOL  *This,
> +  OUT UINTN                     *NumberOfProcessors,
> +  OUT UINTN                     *NumberOfEnabledProcessors
> +  )
> +{
> +  return MpInitLibGetNumberOfProcessors (
> +           This,
> +           NumberOfProcessors,
> +           NumberOfEnabledProcessors
> +           );
> +}
> +
> +/**
> +  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 index of the 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.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +GetProcessorInfo (
> +  IN  EFI_MP_SERVICES_PROTOCOL   *This,
> +  IN  UINTN                      ProcessorNumber,
> +  OUT EFI_PROCESSOR_INFORMATION  *ProcessorInfoBuffer
> +  )
> +{
> +  return MpInitLibGetProcessorInfo (
> +           This,
> +           ProcessorNumber,
> +           ProcessorInfoBuffer
> +           );
> +}
> +
> +/**
> +  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]  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] 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.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +StartupAllAPs (
> +  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
> +  )
> +{
> +  return MpInitLibStartupAllAPs (
> +           This,
> +           Procedure,
> +           SingleThread,
> +           WaitEvent,
> +           TimeoutInMicroseconds,
> +           ProcedureArgument,
> +           FailedCpuList
> +           );
> +}
> +
> +/**
> +  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.
> +
> +**/
> +STATIC
> +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
> +  )
> +{
> +  return MpInitLibStartupThisAP (
> +           This,
> +           Procedure,
> +           ProcessorNumber,
> +           WaitEvent,
> +           TimeoutInMicroseconds,
> +           ProcedureArgument,
> +           Finished
> +           );
> +}
> +
> +/**
> +  This service switches the requested AP to be the BSP from that point onward.
> +  This service changes the BSP for all purposes.   This call can only be
> +  performed by the current BSP.
> +
> +  This service switches the requested AP to be the BSP from that point onward.
> +  This service changes the BSP for all purposes. The new BSP can take over the
> +  execution of the old BSP and continue seamlessly from where the old one left
> +  off. This service may not be supported after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
> +  is signaled.
> +
> +  If the BSP cannot be switched 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] EnableOldBSP      If TRUE, then the old BSP will be listed as an
> +                               enabled AP. Otherwise, it will be disabled.
> +
> +  @retval EFI_SUCCESS             BSP successfully switched.
> +  @retval EFI_UNSUPPORTED         Switching the BSP cannot be completed prior to
> +                                  this service returning.
> +  @retval EFI_UNSUPPORTED         Switching the BSP is not supported.
> +  @retval EFI_SUCCESS             The calling processor is an AP.
> +  @retval EFI_NOT_FOUND           The processor with the handle specified by
> +                                  ProcessorNumber does not exist.
> +  @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the current BSP or
> +                                  a disabled AP.
> +  @retval EFI_NOT_READY           The specified AP is busy.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +SwitchBSP (
> +  IN EFI_MP_SERVICES_PROTOCOL  *This,
> +  IN  UINTN                    ProcessorNumber,
> +  IN  BOOLEAN                  EnableOldBSP
> +  )
> +{
> +  return MpInitLibSwitchBSP (This, ProcessorNumber, EnableOldBSP);
> +}
> +
> +/**
> +  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.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +EnableDisableAP (
> +  IN  EFI_MP_SERVICES_PROTOCOL  *This,
> +  IN  UINTN                     ProcessorNumber,
> +  IN  BOOLEAN                   EnableAP,
> +  IN  UINT32                    *HealthFlag OPTIONAL
> +  )
> +{
> +  return MpInitLibEnableDisableAP (This, ProcessorNumber, EnableAP, HealthFlag);
> +}
> +
> +/**
> +  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[out] 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.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +WhoAmI (
> +  IN EFI_MP_SERVICES_PROTOCOL  *This,
> +  OUT UINTN                    *ProcessorNumber
> +  )
> +{
> +  return MpInitLibWhoAmI (This, ProcessorNumber);
> +}
> +
> +EFI_MP_SERVICES_PROTOCOL  mMpServicesTemplate = {
> +  GetNumberOfProcessors,
> +  GetProcessorInfo,
> +  StartupAllAPs,
> +  StartupThisAP,
> +  SwitchBSP,
> +  EnableDisableAP,
> +  WhoAmI
> +};
> +
> +/** Initialize multi-processor support.
> +
> +**/
> +VOID
> +InitializeMpSupport (
> +  VOID
> +  )
> +{
> +  EFI_STATUS              Status;
> +  EFI_HANDLE              Handle;
> +  UINTN                   MaxCpus;
> +  EFI_HOB_GENERIC_HEADER  *Hob;
> +  VOID                    *HobData;
> +  UINTN                   HobDataSize;
> +  ARM_PROCESSOR_TABLE     CpuInfo;
> +
> +  MaxCpus = 1;
> +  ZeroMem (&CpuInfo, sizeof (ARM_PROCESSOR_TABLE));
> +
> +  DEBUG ((DEBUG_INFO, "Starting MP services"));
> +
> +  Hob = GetFirstGuidHob (&gArmMpCoreInfoGuid);
> +  if (Hob != NULL) {
> +    HobData                 = GET_GUID_HOB_DATA (Hob);
> +    HobDataSize             = GET_GUID_HOB_DATA_SIZE (Hob);
> +    CpuInfo.ArmCpus         = (ARM_CORE_INFO *)HobData;
> +    CpuInfo.NumberOfEntries = HobDataSize / sizeof (ARM_CORE_INFO);
> +    MaxCpus                 = CpuInfo.NumberOfEntries;
> +  }
> +
> +  if (MaxCpus == 1) {
> +    DEBUG ((DEBUG_WARN, "Trying to use EFI_MP_SERVICES_PROTOCOL on a UP system"));
> +    // We are not MP so nothing to do
> +    return;
> +  }
> +
> +  MpInitLibInitialize (MaxCpus, &CpuInfo);
> +
> +  //
> +  // Now install the MP services protocol.
> +  //
> +  Handle = NULL;
> +  Status = gBS->InstallMultipleProtocolInterfaces (
> +                  &Handle,
> +                  &gEfiMpServiceProtocolGuid,
> +                  &mMpServicesTemplate,
> +                  NULL
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +}
> diff --git a/ArmPkg/Include/Library/MpInitLib.h b/ArmPkg/Include/Library/MpInitLib.h
> new file mode 100644
> index 000000000000..a4b80c18a9e8
> --- /dev/null
> +++ b/ArmPkg/Include/Library/MpInitLib.h
> @@ -0,0 +1,366 @@
> +/** @file
> +
> +Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
> +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
> +Portions copyright (c) 2011, Apple Inc. All rights reserved.
> +
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef MP_INITLIB_H_
> +#define MP_INITLIB_H_
> +
> +#include <Protocol/Cpu.h>
> +#include <Protocol/MpService.h>
> +#include <Library/BaseLib.h>
> +#include <Library/UefiLib.h>
> +#include <Guid/ArmMpCoreInfo.h>
> +
> +
> +/**
> +  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.
> +
> +  @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 the
> +                                          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
> +MpInitLibGetNumberOfProcessors (
> +  IN  EFI_MP_SERVICES_PROTOCOL  *This,
> +  OUT UINTN                     *NumberOfProcessors,
> +  OUT UINTN                     *NumberOfEnabledProcessors
> +  );
> +
> +/**
> +  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.
> +
> +  @param[in]  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL
> +                                    instance.
> +  @param[in]  ProcessorIndex        The index of the 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
> +MpInitLibGetProcessorInfo (
> +  IN  EFI_MP_SERVICES_PROTOCOL   *This,
> +  IN  UINTN                      ProcessorIndex,
> +  OUT EFI_PROCESSOR_INFORMATION  *ProcessorInfoBuffer
> +  );
> +
> +
> +/**
> +  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.
> +
> +  @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]  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] 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
> +MpInitLibStartupAllAPs (
> +  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
> +  );
> +
> +/**
> +  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.
> +
> +  @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
> +MpInitLibStartupThisAP (
> +  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 switches the requested AP to be the BSP from that point onward.
> +  This service changes the BSP for all purposes.   This call can only be
> +  performed by the current BSP.
> +
> +  @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] EnableOldBSP      If TRUE, then the old BSP will be listed as an
> +                               enabled AP. Otherwise, it will be disabled.
> +
> +  @retval EFI_SUCCESS             BSP successfully switched.
> +  @retval EFI_UNSUPPORTED         Switching the BSP cannot be completed prior to
> +                                  this service returning.
> +  @retval EFI_UNSUPPORTED         Switching the BSP is not supported.
> +  @retval EFI_SUCCESS             The calling processor is an AP.
> +  @retval EFI_NOT_FOUND           The processor with the handle specified by
> +                                  ProcessorNumber does not exist.
> +  @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the current BSP or
> +                                  a disabled AP.
> +  @retval EFI_NOT_READY           The specified AP is busy.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +MpInitLibSwitchBSP (
> +  IN EFI_MP_SERVICES_PROTOCOL  *This,
> +  IN  UINTN                    ProcessorNumber,
> +  IN  BOOLEAN                  EnableOldBSP
> +  );
> +
> +/**
> +  This service lets the caller enable or disable an AP from this point onward.
> +  This service may only be called from the BSP.
> +
> +  @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
> +MpInitLibEnableDisableAP (
> +  IN  EFI_MP_SERVICES_PROTOCOL  *This,
> +  IN  UINTN                     ProcessorNumber,
> +  IN  BOOLEAN                   EnableAP,
> +  IN  UINT32                    *HealthFlag OPTIONAL
> +  );
> +
> +/**
> +  This return the handle number for the calling processor.  This service may be
> +  called from the BSP and APs.
> +
> +  @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
> +  @param[out] 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
> +MpInitLibWhoAmI (
> +  IN EFI_MP_SERVICES_PROTOCOL  *This,
> +  OUT UINTN                    *ProcessorNumber
> +  );
> +
> +/** Initializes the MP Services system data
> +
> +   @param NumberOfProcessors The number of processors, both BSP and AP.
> +   @param CpuInfo            CPU information gathered earlier during boot.
> +
> +**/
> +VOID
> +MpInitLibInitialize (
> +  IN   UINTN     NumberOfProcessors,
> +  IN   ARM_PROCESSOR_TABLE *CpuInfo
> +  );
> +
> +
> +
> +#endif /* MP_INITLIB_H_ */
> diff --git a/ArmPkg/Library/MpInitLib/AArch64/MpFuncs.S b/ArmPkg/Library/MpInitLib/AArch64/MpFuncs.S
> new file mode 100644
> index 000000000000..8f7019a1c62c
> --- /dev/null
> +++ b/ArmPkg/Library/MpInitLib/AArch64/MpFuncs.S
> @@ -0,0 +1,65 @@
> +#===============================================================================
> +#  Copyright (c) 2021 NUVIA Inc. All rights reserved.
> +#
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#===============================================================================
> +
> +.text
> +.align 3
> +
> +#include <AsmMacroIoLibV8.h>
> +#include <IndustryStandard/ArmStdSmc.h>
> +
> +#include "InternalMpInitLib.h"
> +
> +GCC_ASM_IMPORT (gApStacksBase)
> +GCC_ASM_IMPORT (gProcessorIDs)
> +GCC_ASM_IMPORT (ApProcedure)
> +GCC_ASM_IMPORT (gApStackSize)
> +
> +GCC_ASM_EXPORT (ApEntryPoint)
> +
> +StartupAddr:        .8byte ASM_PFX(ApProcedure)
> +
> +// Entry-point for the AP
> +// VOID
> +// ApEntryPoint (
> +//   VOID
> +//   );
> +ASM_PFX(ApEntryPoint):
> +  mrs x0, mpidr_el1
> +  // Mask the non-affinity bits
> +  ldr x1, =0xff00ffffff
> +  and x0, x0, x1
> +  ldr x1, gProcessorIDs
> +  mov x2, 0                   // x2 = processor index
> +  mov x3, 0                   // x3 = address offset
> +
> +// Find index in gProcessorIDs for current processor
> +1:
> +  ldr x4, [x1, x3]            // x4 = gProcessorIDs + x3
> +  ldr x5, =0xffffffffff
> +  cmp x4, x5                  // check if we've reached the end of gProcessorIDs
> +  beq ProcessorNotFound
> +  add x3, x3, 8               // x3 += sizeof (*gProcessorIDs)
> +  add x2, x2, 1               // x2++
> +  cmp x0, x4                  // if mpidr_el1 != *(gProcessorIDs + x3) then loop
> +  bne 1b
> +  sub x2, x2, 1
> +
> +// Calculate stack address
> +  // x2 contains the index for the current processor
> +  ldr x0, gApStacksBase
> +  ldr x1, gApStackSize
> +  mul x3, x2, x1              // x3 = ProcessorIndex * gApStackSize
> +  add x4, x0, x3              // x4 = gApStacksBase + x3
> +  add sp, x4, x1              // sp = x4 + gApStackSize
> +
> +  ldr x0, StartupAddr         // ASM_PFX(ApProcedure)
> +  blr x0                      // doesn't return
> +
> +ProcessorNotFound:
> +// Turn off the processor
> +  MOV32 (w0, ARM_SMC_ID_PSCI_CPU_OFF)
> +  smc #0
> +  b .
> diff --git a/ArmPkg/Library/MpInitLib/DxeMpInitLib.inf b/ArmPkg/Library/MpInitLib/DxeMpInitLib.inf
> new file mode 100644
> index 000000000000..2275b6cca33a
> --- /dev/null
> +++ b/ArmPkg/Library/MpInitLib/DxeMpInitLib.inf
> @@ -0,0 +1,53 @@
> +#/** @file
> +#
> +# Component description file for the DxeMpInitLib module.
> +#
> +# Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +#**/
> +
> +[Defines]
> +  INF_VERSION                    = 1.29
> +  BASE_NAME                      = DxeMpInitLib
> +  FILE_GUID                      = c9ca773c-8ae4-4b74-82fd-f7345503294e
> +  MODULE_TYPE                    = DXE_DRIVER
> +  VERSION_STRING                 = 1.0
> +  LIBRARY_CLASS                  = MpInitLib|DXE_DRIVER
> +
> +#
> +# The following information is for reference only and not required by the build tools.
> +#
> +#  VALID_ARCHITECTURES           = AARCH64
> +#
> +
> +[Sources.AARCH64]
> +  AArch64/MpFuncs.S
> +
> +[Sources]
> +  DxeMpLib.c
> +  InternalMpInitLib.h
> +
> +[Packages]
> +  ArmPkg/ArmPkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  MdePkg/MdePkg.dec
> +
> +[LibraryClasses]
> +  ArmLib
> +  ArmSmcLib
> +  BaseLib
> +  BaseMemoryLib
> +  DebugLib
> +  HobLib
> +  MemoryAllocationLib
> +  UefiBootServicesTableLib
> +  UefiDriverEntryPoint
> +  UefiLib
> +
> +[Protocols]
> +  gEfiMpServiceProtocolGuid
> +
> +[Guids]
> +  gArmMpCoreInfoGuid
> diff --git a/ArmPkg/Library/MpInitLib/DxeMpLib.c b/ArmPkg/Library/MpInitLib/DxeMpLib.c
> new file mode 100644
> index 000000000000..e8d8808a3225
> --- /dev/null
> +++ b/ArmPkg/Library/MpInitLib/DxeMpLib.c
> @@ -0,0 +1,1477 @@
> +/** @file
> +  Construct MP Services Protocol.
> +
> +  Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
> +  Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
> +  Portions Copyright (c) 2011, Apple Inc. All rights reserved.
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Ppi/ArmMpCoreInfo.h>
> +#include <Library/ArmLib.h>
> +#include <Library/ArmSmcLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/MpInitLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/UefiDriverEntryPoint.h>
> +#include <IndustryStandard/ArmStdSmc.h>
> +
> +#include "InternalMpInitLib.h"
> +
> +#define POLL_INTERVAL_US  50000
> +
> +#define GET_MPIDR_AFFINITY_BITS(x)  ((x) & 0xFF00FFFFFF)
> +
> +#define MPIDR_MT_BIT  BIT24
> +
> +STATIC CPU_MP_DATA  mCpuMpData;
> +STATIC BOOLEAN      mNonBlockingModeAllowed;
> +UINT64              *gApStacksBase;
> +UINT64              *gProcessorIDs;
> +CONST UINT64        gApStackSize = AP_STACK_SIZE;
> +
> +/** C entry-point for the AP.
> +    This function gets called from the assembly function ApEntryPoint.
> +
> +**/
> +VOID
> +ApProcedure (
> +  VOID
> +  )
> +{
> +  ARM_SMC_ARGS      Args;
> +  EFI_AP_PROCEDURE  UserApProcedure;
> +  VOID              *UserApParameter;
> +  UINTN             ProcessorIndex;
> +
> +  MpInitLibWhoAmI (&mMpServicesTemplate, &ProcessorIndex);
> +
> +  /* Fetch the user-supplied procedure and parameter to execute */
> +  UserApProcedure = mCpuMpData.CpuData[ProcessorIndex].Procedure;
> +  UserApParameter = mCpuMpData.CpuData[ProcessorIndex].Parameter;
> +
> +  UserApProcedure (UserApParameter);
> +
> +  mCpuMpData.CpuData[ProcessorIndex].State = CpuStateFinished;
> +
> +  /* Since we're finished with this AP, turn it off */
> +  Args.Arg0 = ARM_SMC_ID_PSCI_CPU_OFF;
> +  ArmCallSmc (&Args);
> +
> +  /* Should never be reached */
> +  ASSERT (FALSE);
> +  CpuDeadLoop ();
> +}
> +
> +/** Turns on the specified core using PSCI and executes the user-supplied
> +    function that's been configured via a previous call to SetApProcedure.
> +
> +    @param ProcessorIndex The index of the core to turn on.
> +
> +    @retval EFI_SUCCESS      Success.
> +    @retval EFI_DEVICE_ERROR The processor could not be turned on.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +DispatchCpu (
> +  IN UINTN  ProcessorIndex
> +  )
> +{
> +  ARM_SMC_ARGS  Args;
> +  EFI_STATUS    Status;
> +
> +  Status = EFI_SUCCESS;
> +
> +  mCpuMpData.CpuData[ProcessorIndex].State = CpuStateBusy;
> +
> +  /* Turn the AP on */
> +  if (sizeof (Args.Arg0) == sizeof (UINT32)) {
> +    Args.Arg0 = ARM_SMC_ID_PSCI_CPU_ON_AARCH32;
> +  } else {
> +    Args.Arg0 = ARM_SMC_ID_PSCI_CPU_ON_AARCH64;
> +  }
> +
> +  Args.Arg1 = gProcessorIDs[ProcessorIndex];
> +  Args.Arg2 = (UINTN)ApEntryPoint;
> +
> +  ArmCallSmc (&Args);
> +
> +  if (Args.Arg0 != ARM_SMC_PSCI_RET_SUCCESS) {
> +    DEBUG ((DEBUG_ERROR, "PSCI_CPU_ON call failed: %d", Args.Arg0));
> +    Status = EFI_DEVICE_ERROR;
> +  }
> +
> +  return Status;
> +}
> +
> +/** Returns whether the specified processor is the BSP.
> +
> +  @param[in] ProcessorIndex The index the processor to check.
> +
> +  @return TRUE if the processor is the BSP, FALSE otherwise.
> +**/
> +STATIC
> +BOOLEAN
> +IsProcessorBSP (
> +  UINTN  ProcessorIndex
> +  )
> +{
> +  EFI_PROCESSOR_INFORMATION  *CpuInfo;
> +
> +  CpuInfo = &mCpuMpData.CpuData[ProcessorIndex].Info;
> +
> +  return (CpuInfo->StatusFlag & PROCESSOR_AS_BSP_BIT) != 0;
> +}
> +
> +/** Returns whether the processor executing this function is the BSP.
> +
> +    @return Whether the current processor is the BSP.
> +**/
> +STATIC
> +BOOLEAN
> +IsCurrentProcessorBSP (
> +  VOID
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINTN       ProcessorIndex;
> +
> +  Status = MpInitLibWhoAmI (&mMpServicesTemplate, &ProcessorIndex);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT (0);
> +    return FALSE;
> +  }
> +
> +  return IsProcessorBSP (ProcessorIndex);
> +}
> +
> +/** Get the Application Processors state.
> +
> +  @param[in]  CpuData    The pointer to CPU_AP_DATA of specified AP.
> +
> +  @return The AP status.
> +**/
> +CPU_STATE
> +GetApState (
> +  IN  CPU_AP_DATA  *CpuData
> +  )
> +{
> +  return CpuData->State;
> +}
> +
> +/** Configures the processor context with the user-supplied procedure and
> +    argument.
> +
> +   @param CpuData           The processor context.
> +   @param Procedure         The user-supplied procedure.
> +   @param ProcedureArgument The user-supplied procedure argument.
> +
> +**/
> +STATIC
> +VOID
> +SetApProcedure (
> +  IN   CPU_AP_DATA       *CpuData,
> +  IN   EFI_AP_PROCEDURE  Procedure,
> +  IN   VOID              *ProcedureArgument
> +  )
> +{
> +  ASSERT (CpuData != NULL);
> +  ASSERT (Procedure != NULL);
> +
> +  CpuData->Parameter = ProcedureArgument;
> +  CpuData->Procedure = Procedure;
> +}
> +
> +/** Returns the index of the next processor that is blocked.
> +
> +   @param[out] NextNumber The index of the next blocked processor.
> +
> +   @retval EFI_SUCCESS   Successfully found the next blocked processor.
> +   @retval EFI_NOT_FOUND There are no blocked processors.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +GetNextBlockedNumber (
> +  OUT UINTN  *NextNumber
> +  )
> +{
> +  UINTN        Index;
> +  CPU_STATE    State;
> +  CPU_AP_DATA  *CpuData;
> +
> +  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
> +    CpuData = &mCpuMpData.CpuData[Index];
> +    if (IsProcessorBSP (Index)) {
> +      // Skip BSP
> +      continue;
> +    }
> +
> +    State = CpuData->State;
> +
> +    if (State == CpuStateBlocked) {
> +      *NextNumber = Index;
> +      return EFI_SUCCESS;
> +    }
> +  }
> +
> +  return EFI_NOT_FOUND;
> +}
> +
> +/** Stalls the BSP for the minimum of POLL_INTERVAL_US and Timeout.
> +
> +   @param[in]  Timeout    The time limit in microseconds remaining for
> +                          APs to return from Procedure.
> +
> +   @retval     StallTime  Time of execution stall.
> +**/
> +STATIC
> +UINTN
> +CalculateAndStallInterval (
> +  IN UINTN  Timeout
> +  )
> +{
> +  UINTN  StallTime;
> +
> +  if ((Timeout < POLL_INTERVAL_US) && (Timeout != 0)) {
> +    StallTime = Timeout;
> +  } else {
> +    StallTime = POLL_INTERVAL_US;
> +  }
> +
> +  gBS->Stall (StallTime);
> +
> +  return StallTime;
> +}
> +
> +/**
> +  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.
> +
> +  @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 the
> +                                          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
> +MpInitLibGetNumberOfProcessors (
> +  IN  EFI_MP_SERVICES_PROTOCOL  *This,
> +  OUT UINTN                     *NumberOfProcessors,
> +  OUT UINTN                     *NumberOfEnabledProcessors
> +  )
> +{
> +  if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (!IsCurrentProcessorBSP ()) {
> +    return EFI_DEVICE_ERROR;
> +  }
> +
> +  *NumberOfProcessors        = mCpuMpData.NumberOfProcessors;
> +  *NumberOfEnabledProcessors = mCpuMpData.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.
> +
> +  @param[in]  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL
> +                                    instance.
> +  @param[in]  ProcessorIndex        The index of the 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
> +MpInitLibGetProcessorInfo (
> +  IN  EFI_MP_SERVICES_PROTOCOL   *This,
> +  IN  UINTN                      ProcessorIndex,
> +  OUT EFI_PROCESSOR_INFORMATION  *ProcessorInfoBuffer
> +  )
> +{
> +  if (ProcessorInfoBuffer == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (!IsCurrentProcessorBSP ()) {
> +    return EFI_DEVICE_ERROR;
> +  }
> +
> +  ProcessorIndex &= ~CPU_V2_EXTENDED_TOPOLOGY;
> +
> +  if (ProcessorIndex >= mCpuMpData.NumberOfProcessors) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  CopyMem (
> +    ProcessorInfoBuffer,
> +    &mCpuMpData.CpuData[ProcessorIndex],
> +    sizeof (EFI_PROCESSOR_INFORMATION)
> +    );
> +  return EFI_SUCCESS;
> +}
> +
> +/** Returns whether the specified processor is enabled.
> +
> +   @param[in] ProcessorIndex The index of the processor to check.
> +
> +   @return TRUE if the processor is enabled, FALSE otherwise.
> +**/
> +STATIC
> +BOOLEAN
> +IsProcessorEnabled (
> +  UINTN  ProcessorIndex
> +  )
> +{
> +  EFI_PROCESSOR_INFORMATION  *CpuInfo;
> +
> +  CpuInfo = &mCpuMpData.CpuData[ProcessorIndex].Info;
> +
> +  return (CpuInfo->StatusFlag & PROCESSOR_ENABLED_BIT) != 0;
> +}
> +
> +/** Returns whether all processors are in the idle state.
> +
> +   @return Whether all the processors are idle.
> +
> +**/
> +STATIC
> +BOOLEAN
> +CheckAllCpusReady (
> +  VOID
> +  )
> +{
> +  UINTN        Index;
> +  CPU_AP_DATA  *CpuData;
> +
> +  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
> +    CpuData = &mCpuMpData.CpuData[Index];
> +    if (IsProcessorBSP (Index)) {
> +      // Skip BSP
> +      continue;
> +    }
> +
> +    if (!IsProcessorEnabled (Index)) {
> +      // Skip Disabled processors
> +      continue;
> +    }
> +
> +    if (GetApState (CpuData) != CpuStateIdle) {
> +      return FALSE;
> +    }
> +  }
> +
> +  return TRUE;
> +}
> +
> +/** Sets up the state for the StartupAllAPs function.
> +
> +   @param SingleThread Whether the APs will execute sequentially.
> +
> +**/
> +STATIC
> +VOID
> +StartupAllAPsPrepareState (
> +  IN BOOLEAN  SingleThread
> +  )
> +{
> +  UINTN        Index;
> +  CPU_STATE    APInitialState;
> +  CPU_AP_DATA  *CpuData;
> +
> +  mCpuMpData.FinishCount  = 0;
> +  mCpuMpData.StartCount   = 0;
> +  mCpuMpData.SingleThread = SingleThread;
> +
> +  APInitialState = CpuStateReady;
> +
> +  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
> +    CpuData = &mCpuMpData.CpuData[Index];
> +
> +    //
> +    // Get APs prepared, and put failing APs into FailedCpuList.
> +    // If "SingleThread", only 1 AP will put into ready state, other AP will be
> +    // put into ready state 1 by 1, until the previous 1 finished its task.
> +    // If not "SingleThread", all APs are put into ready state from the
> +    // beginning
> +    //
> +
> +    if (IsProcessorBSP (Index)) {
> +      // Skip BSP
> +      continue;
> +    }
> +
> +    if (!IsProcessorEnabled (Index)) {
> +      // Skip Disabled processors
> +      if (mCpuMpData.FailedList != NULL) {
> +        mCpuMpData.FailedList[mCpuMpData.FailedListIndex++] = Index;
> +      }
> +
> +      continue;
> +    }
> +
> +    ASSERT (GetApState (CpuData) == CpuStateIdle);
> +    CpuData->State = APInitialState;
> +
> +    mCpuMpData.StartCount++;
> +    if (SingleThread) {
> +      APInitialState = CpuStateBlocked;
> +    }
> +  }
> +}
> +
> +/** Handles execution of StartupAllAPs when a WaitEvent has been specified.
> +
> +   @param Procedure         The user-supplied procedure.
> +   @param ProcedureArgument The user-supplied procedure argument.
> +   @param WaitEvent         The wait event to be signaled when the work is
> +                            complete or a timeout has occurred.
> +   @param TimeoutInMicroseconds The timeout for the work to be completed. Zero
> +                                indicates an infinite timeout.
> +
> +   @return EFI_SUCCESS on success.
> +**/
> +STATIC
> +EFI_STATUS
> +StartupAllAPsWithWaitEvent (
> +  IN EFI_AP_PROCEDURE  Procedure,
> +  IN VOID              *ProcedureArgument,
> +  IN EFI_EVENT         WaitEvent,
> +  IN UINTN             TimeoutInMicroseconds
> +  )
> +{
> +  EFI_STATUS   Status;
> +  UINTN        Index;
> +  CPU_AP_DATA  *CpuData;
> +
> +  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
> +    CpuData = &mCpuMpData.CpuData[Index];
> +    if (IsProcessorBSP (Index)) {
> +      // Skip BSP
> +      continue;
> +    }
> +
> +    if (!IsProcessorEnabled (Index)) {
> +      // Skip Disabled processors
> +      continue;
> +    }
> +
> +    if (GetApState (CpuData) == CpuStateReady) {
> +      SetApProcedure (CpuData, Procedure, ProcedureArgument);
> +    }
> +  }
> +
> +  //
> +  // Save data into private data structure, and create timer to poll AP state
> +  // before exiting
> +  //
> +  mCpuMpData.Procedure         = Procedure;
> +  mCpuMpData.ProcedureArgument = ProcedureArgument;
> +  mCpuMpData.WaitEvent         = WaitEvent;
> +  mCpuMpData.Timeout           = TimeoutInMicroseconds;
> +  mCpuMpData.TimeoutActive     = (BOOLEAN)(TimeoutInMicroseconds != 0);
> +  Status                       = gBS->SetTimer (
> +                                        mCpuMpData.CheckAllAPsEvent,
> +                                        TimerPeriodic,
> +                                        POLL_INTERVAL_US
> +                                        );
> +  return Status;
> +}
> +
> +/** Handles execution of StartupAllAPs when no wait event has been specified.
> +
> +   @param Procedure             The user-supplied procedure.
> +   @param ProcedureArgument     The user-supplied procedure argument.
> +   @param TimeoutInMicroseconds The timeout for the work to be completed. Zero
> +                                indicates an infinite timeout.
> +   @param SingleThread          Whether the APs will execute sequentially.
> +   @param FailedCpuList         User-supplied pointer for list of failed CPUs.
> +
> +   @return EFI_SUCCESS on success.
> +**/
> +STATIC
> +EFI_STATUS
> +StartupAllAPsNoWaitEvent (
> +  IN EFI_AP_PROCEDURE  Procedure,
> +  IN VOID              *ProcedureArgument,
> +  IN UINTN             TimeoutInMicroseconds,
> +  IN BOOLEAN           SingleThread,
> +  IN UINTN             **FailedCpuList
> +  )
> +{
> +  EFI_STATUS   Status;
> +  UINTN        Index;
> +  UINTN        NextIndex;
> +  UINTN        Timeout;
> +  CPU_AP_DATA  *CpuData;
> +
> +  Timeout = TimeoutInMicroseconds;
> +
> +  while (TRUE) {
> +    for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
> +      CpuData = &mCpuMpData.CpuData[Index];
> +      if (IsProcessorBSP (Index)) {
> +        // Skip BSP
> +        continue;
> +      }
> +
> +      if (!IsProcessorEnabled (Index)) {
> +        // Skip Disabled processors
> +        continue;
> +      }
> +
> +      switch (GetApState (CpuData)) {
> +        case CpuStateReady:
> +          SetApProcedure (CpuData, Procedure, ProcedureArgument);
> +          Status = DispatchCpu (Index);
> +          if (EFI_ERROR (Status)) {
> +            CpuData->State = CpuStateIdle;
> +            Status         = EFI_NOT_READY;
> +            goto Done;
> +          }
> +
> +          break;
> +
> +        case CpuStateFinished:
> +          mCpuMpData.FinishCount++;
> +          if (SingleThread) {
> +            Status = GetNextBlockedNumber (&NextIndex);
> +            if (!EFI_ERROR (Status)) {
> +              mCpuMpData.CpuData[NextIndex].State = CpuStateReady;
> +            }
> +          }
> +
> +          CpuData->State = CpuStateIdle;
> +          break;
> +
> +        default:
> +          break;
> +      }
> +    }
> +
> +    if (mCpuMpData.FinishCount == mCpuMpData.StartCount) {
> +      Status = EFI_SUCCESS;
> +      goto Done;
> +    }
> +
> +    if ((TimeoutInMicroseconds != 0) && (Timeout == 0)) {
> +      Status = EFI_TIMEOUT;
> +      goto Done;
> +    }
> +
> +    Timeout -= CalculateAndStallInterval (Timeout);
> +  }
> +
> +Done:
> +  if (FailedCpuList != NULL) {
> +    if (mCpuMpData.FailedListIndex == 0) {
> +      FreePool (*FailedCpuList);
> +      *FailedCpuList = NULL;
> +    }
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  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.
> +
> +  @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]  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] 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
> +MpInitLibStartupAllAPs (
> +  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;
> +
> +  if (!IsCurrentProcessorBSP ()) {
> +    return EFI_DEVICE_ERROR;
> +  }
> +
> +  if (mCpuMpData.NumberOfProcessors == 1) {
> +    return EFI_NOT_STARTED;
> +  }
> +
> +  if (Procedure == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if ((WaitEvent != NULL) && !mNonBlockingModeAllowed) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  if (!CheckAllCpusReady ()) {
> +    return EFI_NOT_READY;
> +  }
> +
> +  if (FailedCpuList != NULL) {
> +    mCpuMpData.FailedList = AllocatePool (
> +                              (mCpuMpData.NumberOfProcessors + 1) *
> +                              sizeof (UINTN)
> +                              );
> +    if (mCpuMpData.FailedList == NULL) {
> +      return EFI_OUT_OF_RESOURCES;
> +    }
> +
> +    SetMemN (
> +      mCpuMpData.FailedList,
> +      (mCpuMpData.NumberOfProcessors + 1) *
> +      sizeof (UINTN),
> +      END_OF_CPU_LIST
> +      );
> +    mCpuMpData.FailedListIndex = 0;
> +    *FailedCpuList             = mCpuMpData.FailedList;
> +  }
> +
> +  StartupAllAPsPrepareState (SingleThread);
> +
> +  if (WaitEvent != NULL) {
> +    Status = StartupAllAPsWithWaitEvent (
> +               Procedure,
> +               ProcedureArgument,
> +               WaitEvent,
> +               TimeoutInMicroseconds
> +               );
> +  } else {
> +    Status = StartupAllAPsNoWaitEvent (
> +               Procedure,
> +               ProcedureArgument,
> +               TimeoutInMicroseconds,
> +               SingleThread,
> +               FailedCpuList
> +               );
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  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.
> +
> +  @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
> +MpInitLibStartupThisAP (
> +  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   Status;
> +  UINTN        Timeout;
> +  CPU_AP_DATA  *CpuData;
> +
> +  if (!IsCurrentProcessorBSP ()) {
> +    return EFI_DEVICE_ERROR;
> +  }
> +
> +  if (Procedure == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (ProcessorNumber >= mCpuMpData.NumberOfProcessors) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  CpuData = &mCpuMpData.CpuData[ProcessorNumber];
> +
> +  if (IsProcessorBSP (ProcessorNumber)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (!IsProcessorEnabled (ProcessorNumber)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (GetApState (CpuData) != CpuStateIdle) {
> +    return EFI_NOT_READY;
> +  }
> +
> +  if ((WaitEvent != NULL) && !mNonBlockingModeAllowed) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  Timeout = TimeoutInMicroseconds;
> +
> +  mCpuMpData.StartCount  = 1;
> +  mCpuMpData.FinishCount = 0;
> +
> +  SetApProcedure (
> +    CpuData,
> +    Procedure,
> +    ProcedureArgument
> +    );
> +
> +  Status = DispatchCpu (ProcessorNumber);
> +  if (EFI_ERROR (Status)) {
> +    CpuData->State = CpuStateIdle;
> +    return EFI_NOT_READY;
> +  }
> +
> +  if (WaitEvent != NULL) {
> +    // Non Blocking
> +    mCpuMpData.WaitEvent = WaitEvent;
> +    gBS->SetTimer (
> +           CpuData->CheckThisAPEvent,
> +           TimerPeriodic,
> +           POLL_INTERVAL_US
> +           );
> +    return EFI_SUCCESS;
> +  }
> +
> +  // Blocking
> +  while (TRUE) {
> +    if (GetApState (CpuData) == CpuStateFinished) {
> +      CpuData->State = CpuStateIdle;
> +      break;
> +    }
> +
> +    if ((TimeoutInMicroseconds != 0) && (Timeout == 0)) {
> +      return EFI_TIMEOUT;
> +    }
> +
> +    Timeout -= CalculateAndStallInterval (Timeout);
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This service switches the requested AP to be the BSP from that point onward.
> +  This service changes the BSP for all purposes.   This call can only be
> +  performed by the current BSP.
> +
> +  @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] EnableOldBSP      If TRUE, then the old BSP will be listed as an
> +                               enabled AP. Otherwise, it will be disabled.
> +
> +  @retval EFI_SUCCESS             BSP successfully switched.
> +  @retval EFI_UNSUPPORTED         Switching the BSP cannot be completed prior to
> +                                  this service returning.
> +  @retval EFI_UNSUPPORTED         Switching the BSP is not supported.
> +  @retval EFI_SUCCESS             The calling processor is an AP.
> +  @retval EFI_NOT_FOUND           The processor with the handle specified by
> +                                  ProcessorNumber does not exist.
> +  @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the current BSP or
> +                                  a disabled AP.
> +  @retval EFI_NOT_READY           The specified AP is busy.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +MpInitLibSwitchBSP (
> +  IN EFI_MP_SERVICES_PROTOCOL  *This,
> +  IN  UINTN                    ProcessorNumber,
> +  IN  BOOLEAN                  EnableOldBSP
> +  )
> +{
> +  // Skip for now as we need switch a bunch of stack stuff around and it's
> +  // complex. May not be worth it?
> +  return EFI_UNSUPPORTED;
> +}
> +
> +/**
> +  This service lets the caller enable or disable an AP from this point onward.
> +  This service may only be called from the BSP.
> +
> +  @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
> +MpInitLibEnableDisableAP (
> +  IN  EFI_MP_SERVICES_PROTOCOL  *This,
> +  IN  UINTN                     ProcessorNumber,
> +  IN  BOOLEAN                   EnableAP,
> +  IN  UINT32                    *HealthFlag OPTIONAL
> +  )
> +{
> +  UINTN        StatusFlag;
> +  CPU_AP_DATA  *CpuData;
> +
> +  StatusFlag = mCpuMpData.CpuData[ProcessorNumber].Info.StatusFlag;
> +  CpuData    = &mCpuMpData.CpuData[ProcessorNumber];
> +
> +  if (!IsCurrentProcessorBSP ()) {
> +    return EFI_DEVICE_ERROR;
> +  }
> +
> +  if (ProcessorNumber >= mCpuMpData.NumberOfProcessors) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  if (IsProcessorBSP (ProcessorNumber)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (GetApState (CpuData) != CpuStateIdle) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  if (EnableAP) {
> +    if (!IsProcessorEnabled (ProcessorNumber)) {
> +      mCpuMpData.NumberOfEnabledProcessors++;
> +    }
> +
> +    StatusFlag |= PROCESSOR_ENABLED_BIT;
> +  } else {
> +    if (IsProcessorEnabled (ProcessorNumber)) {
> +      mCpuMpData.NumberOfEnabledProcessors--;
> +    }
> +
> +    StatusFlag &= ~PROCESSOR_ENABLED_BIT;
> +  }
> +
> +  if (HealthFlag != NULL) {
> +    StatusFlag &= ~PROCESSOR_HEALTH_STATUS_BIT;
> +    StatusFlag |= (*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT);
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This return the handle number for the calling processor.  This service may be
> +  called from the BSP and APs.
> +
> +  @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
> +  @param[out] 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
> +MpInitLibWhoAmI (
> +  IN EFI_MP_SERVICES_PROTOCOL  *This,
> +  OUT UINTN                    *ProcessorNumber
> +  )
> +{
> +  UINTN        Index;
> +  UINT64       ProcessorId;
> +  CPU_AP_DATA  *CpuData;
> +
> +  if (ProcessorNumber == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  ProcessorId = GET_MPIDR_AFFINITY_BITS (ArmReadMpidr ());
> +  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
> +    CpuData = &mCpuMpData.CpuData[Index];
> +    if (CpuData->Info.ProcessorId == ProcessorId) {
> +      break;
> +    }
> +  }
> +
> +  *ProcessorNumber = Index;
> +  return EFI_SUCCESS;
> +}
> +
> +/** Adds the specified processor the list of failed processors.
> +
> +   @param ProcessorIndex The processor index to add.
> +   @param ApState        Processor state.
> +
> +**/
> +STATIC
> +VOID
> +AddProcessorToFailedList (
> +  UINTN      ProcessorIndex,
> +  CPU_STATE  ApState
> +  )
> +{
> +  UINTN    Index;
> +  BOOLEAN  Found;
> +
> +  Found = FALSE;
> +
> +  if (ApState == CpuStateIdle) {
> +    return;
> +  }
> +
> +  // If we are retrying make sure we don't double count
> +  for (Index = 0; Index < mCpuMpData.FailedListIndex; Index++) {
> +    if (mCpuMpData.FailedList[Index] == ProcessorIndex) {
> +      Found = TRUE;
> +      break;
> +    }
> +  }
> +
> +  /* If the CPU isn't already in the FailedList, add it */
> +  if (!Found) {
> +    mCpuMpData.FailedList[mCpuMpData.FailedListIndex++] = Index;
> +  }
> +}
> +
> +/** Handles the StartupAllAPs case where the timeout has occurred.
> +
> +**/
> +STATIC
> +VOID
> +ProcessStartupAllAPsTimeout (
> +  VOID
> +  )
> +{
> +  CPU_AP_DATA  *CpuData;
> +  UINTN        Index;
> +
> +  if (mCpuMpData.FailedList == NULL) {
> +    return;
> +  }
> +
> +  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
> +    CpuData = &mCpuMpData.CpuData[Index];
> +    if (IsProcessorBSP (Index)) {
> +      // Skip BSP
> +      continue;
> +    }
> +
> +    if (!IsProcessorEnabled (Index)) {
> +      // Skip Disabled processors
> +      continue;
> +    }
> +
> +    CpuData = &mCpuMpData.CpuData[Index];
> +    AddProcessorToFailedList (Index, GetApState (CpuData));
> +  }
> +}
> +
> +/** Updates the status of the APs.
> +
> +   @param[in] ProcessorIndex The index of the AP to update.
> +**/
> +STATIC
> +VOID
> +UpdateApStatus (
> +  IN UINTN  ProcessorIndex
> +  )
> +{
> +  EFI_STATUS   Status;
> +  CPU_AP_DATA  *CpuData;
> +  CPU_AP_DATA  *NextCpuData;
> +  CPU_STATE    State;
> +  UINTN        NextNumber;
> +
> +  CpuData = &mCpuMpData.CpuData[ProcessorIndex];
> +
> +  if (IsProcessorBSP (ProcessorIndex)) {
> +    // Skip BSP
> +    return;
> +  }
> +
> +  if (!IsProcessorEnabled (ProcessorIndex)) {
> +    // Skip Disabled processors
> +    return;
> +  }
> +
> +  State = GetApState (CpuData);
> +
> +  switch (State) {
> +    case CpuStateFinished:
> +      if (mCpuMpData.SingleThread) {
> +        Status = GetNextBlockedNumber (&NextNumber);
> +        if (!EFI_ERROR (Status)) {
> +          NextCpuData = &mCpuMpData.CpuData[NextNumber];
> +
> +          NextCpuData->State = CpuStateReady;
> +
> +          SetApProcedure (
> +            NextCpuData,
> +            mCpuMpData.Procedure,
> +            mCpuMpData.ProcedureArgument
> +            );
> +        }
> +      }
> +
> +      CpuData->State = CpuStateIdle;
> +      mCpuMpData.FinishCount++;
> +      break;
> +
> +    default:
> +      break;
> +  }
> +}
> +
> +/**
> +  If a timeout is specified in StartupAllAps(), a timer is set, which invokes
> +  this procedure periodically to check whether all APs have finished.
> +
> +  @param[in] Event   The WaitEvent the user supplied.
> +  @param[in] Context The event context.
> +**/
> +STATIC
> +VOID
> +EFIAPI
> +CheckAllAPsStatus (
> +  IN  EFI_EVENT  Event,
> +  IN  VOID       *Context
> +  )
> +{
> +  UINTN  Index;
> +
> +  if (mCpuMpData.TimeoutActive) {
> +    mCpuMpData.Timeout -= CalculateAndStallInterval (mCpuMpData.Timeout);
> +  }
> +
> +  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
> +    UpdateApStatus (Index);
> +  }
> +
> +  if (mCpuMpData.TimeoutActive && (mCpuMpData.Timeout == 0)) {
> +    ProcessStartupAllAPsTimeout ();
> +
> +    // Force terminal exit
> +    mCpuMpData.FinishCount = mCpuMpData.StartCount;
> +  }
> +
> +  if (mCpuMpData.FinishCount != mCpuMpData.StartCount) {
> +    return;
> +  }
> +
> +  gBS->SetTimer (
> +         mCpuMpData.CheckAllAPsEvent,
> +         TimerCancel,
> +         0
> +         );
> +
> +  if (mCpuMpData.FailedListIndex == 0) {
> +    if (mCpuMpData.FailedList != NULL) {
> +      FreePool (mCpuMpData.FailedList);
> +      mCpuMpData.FailedList = NULL;
> +    }
> +  }
> +
> +  gBS->SignalEvent (mCpuMpData.WaitEvent);
> +}
> +
> +/** Invoked periodically via a timer to check the state of the processor.
> +
> +   @param Event   The event supplied by the timer expiration.
> +   @param Context The processor context.
> +
> +**/
> +STATIC
> +VOID
> +EFIAPI
> +CheckThisAPStatus (
> +  IN  EFI_EVENT  Event,
> +  IN  VOID       *Context
> +  )
> +{
> +  EFI_STATUS   Status;
> +  CPU_AP_DATA  *CpuData;
> +  CPU_STATE    State;
> +
> +  CpuData             = Context;
> +  CpuData->TimeTaken += POLL_INTERVAL_US;
> +
> +  State = GetApState (CpuData);
> +
> +  if (State == CpuStateFinished) {
> +    Status = gBS->SetTimer (CpuData->CheckThisAPEvent, TimerCancel, 0);
> +    ASSERT_EFI_ERROR (Status);
> +
> +    if (mCpuMpData.WaitEvent != NULL) {
> +      Status = gBS->SignalEvent (mCpuMpData.WaitEvent);
> +      ASSERT_EFI_ERROR (Status);
> +    }
> +
> +    CpuData->State = CpuStateIdle;
> +  }
> +
> +  if (CpuData->TimeTaken > CpuData->Timeout) {
> +    if (mCpuMpData.WaitEvent != NULL) {
> +      Status = gBS->SignalEvent (mCpuMpData.WaitEvent);
> +      ASSERT_EFI_ERROR (Status);
> +    }
> +  }
> +}
> +
> +/**
> +  This function is called by all processors (both BSP and AP) once and collects
> +  MP related data.
> +
> +  @param BSP            TRUE if the processor is the BSP.
> +  @param Mpidr          The MPIDR for the specified processor. This should be
> +                        the full MPIDR and not only the affinity bits.
> +  @param ProcessorIndex The index of the processor.
> +
> +  @return EFI_SUCCESS if the data for the processor collected and filled in.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +FillInProcessorInformation (
> +  IN BOOLEAN  BSP,
> +  IN UINTN    Mpidr,
> +  IN UINTN    ProcessorIndex
> +  )
> +{
> +  EFI_PROCESSOR_INFORMATION  *CpuInfo;
> +
> +  CpuInfo = &mCpuMpData.CpuData[ProcessorIndex].Info;
> +
> +  CpuInfo->ProcessorId = GET_MPIDR_AFFINITY_BITS (Mpidr);
> +  CpuInfo->StatusFlag  = PROCESSOR_ENABLED_BIT | PROCESSOR_HEALTH_STATUS_BIT;
> +
> +  if (BSP) {
> +    CpuInfo->StatusFlag |= PROCESSOR_AS_BSP_BIT;
> +  }
> +
> +  if (Mpidr & MPIDR_MT_BIT) {
> +    CpuInfo->Location.Package = GET_MPIDR_AFF2 (Mpidr);
> +    CpuInfo->Location.Core    = GET_MPIDR_AFF1 (Mpidr);
> +    CpuInfo->Location.Thread  = GET_MPIDR_AFF0 (Mpidr);
> +
> +    CpuInfo->ExtendedInformation.Location2.Package = GET_MPIDR_AFF3 (Mpidr);
> +    CpuInfo->ExtendedInformation.Location2.Die     = GET_MPIDR_AFF2 (Mpidr);
> +    CpuInfo->ExtendedInformation.Location2.Core    = GET_MPIDR_AFF1 (Mpidr);
> +    CpuInfo->ExtendedInformation.Location2.Thread  = GET_MPIDR_AFF0 (Mpidr);
> +  } else {
> +    CpuInfo->Location.Package = GET_MPIDR_AFF1 (Mpidr);
> +    CpuInfo->Location.Core    = GET_MPIDR_AFF0 (Mpidr);
> +    CpuInfo->Location.Thread  = 0;
> +
> +    CpuInfo->ExtendedInformation.Location2.Package = GET_MPIDR_AFF2 (Mpidr);
> +    CpuInfo->ExtendedInformation.Location2.Die     = GET_MPIDR_AFF1 (Mpidr);
> +    CpuInfo->ExtendedInformation.Location2.Core    = GET_MPIDR_AFF0 (Mpidr);
> +    CpuInfo->ExtendedInformation.Location2.Thread  = 0;
> +  }
> +
> +  CpuInfo->ExtendedInformation.Location2.Package = 0;
> +  CpuInfo->ExtendedInformation.Location2.Module  = 0;
> +  CpuInfo->ExtendedInformation.Location2.Tile    = 0;
> +
> +  mCpuMpData.CpuData[ProcessorIndex].State = BSP ? CpuStateBusy : CpuStateIdle;
> +
> +  mCpuMpData.CpuData[ProcessorIndex].Procedure = NULL;
> +  mCpuMpData.CpuData[ProcessorIndex].Parameter = NULL;
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/** Initializes the MP Services system data
> +
> +   @param NumberOfProcessors The number of processors, both BSP and AP.
> +   @param CpuInfo            CPU information gathered earlier during boot.
> +
> +**/
> +VOID
> +MpInitLibInitialize (
> +  IN   UINTN                NumberOfProcessors,
> +  IN   ARM_PROCESSOR_TABLE  *CpuInfo
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINTN       Index;
> +  EFI_EVENT   ReadyToBootEvent;
> +
> +  //
> +  // Clear the data structure area first.
> +  //
> +  ZeroMem (&mCpuMpData, sizeof (CPU_MP_DATA));
> +  //
> +  // First BSP fills and inits all known values, including its own records.
> +  //
> +  mCpuMpData.NumberOfProcessors        = NumberOfProcessors;
> +  mCpuMpData.NumberOfEnabledProcessors = NumberOfProcessors;
> +
> +  mCpuMpData.CpuData = AllocateZeroPool (
> +                         mCpuMpData.NumberOfProcessors *
> +                         sizeof (CPU_AP_DATA)
> +                         );
> +  ASSERT (mCpuMpData.CpuData != NULL);
> +
> +  /* Allocate one extra for the NULL entry at the end */
> +  gProcessorIDs = AllocatePool ((mCpuMpData.NumberOfProcessors + 1) * sizeof (UINT64));
> +  ASSERT (gProcessorIDs != NULL);
> +
> +  FillInProcessorInformation (TRUE, CpuInfo->ArmCpus[0].Mpidr, 0);
> +  gProcessorIDs[0] = mCpuMpData.CpuData[0].Info.ProcessorId;
> +
> +  Status = gBS->CreateEvent (
> +                  EVT_TIMER | EVT_NOTIFY_SIGNAL,
> +                  TPL_CALLBACK,
> +                  CheckAllAPsStatus,
> +                  NULL,
> +                  &mCpuMpData.CheckAllAPsEvent
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  gApStacksBase = AllocatePool (
> +                    mCpuMpData.NumberOfProcessors *
> +                    gApStackSize
> +                    );
> +  ASSERT (gApStacksBase != NULL);
> +
> +  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
> +    if (IsProcessorBSP (Index)) {
> +      /* Skip BSP */
> +      continue;
> +    }
> +
> +    FillInProcessorInformation (FALSE, CpuInfo->ArmCpus[Index].Mpidr, Index);
> +
> +    gProcessorIDs[Index] = mCpuMpData.CpuData[Index].Info.ProcessorId;
> +
> +    Status = gBS->CreateEvent (
> +                    EVT_TIMER | EVT_NOTIFY_SIGNAL,
> +                    TPL_CALLBACK,
> +                    CheckThisAPStatus,
> +                    (VOID *)&mCpuMpData.CpuData[Index],
> +                    &mCpuMpData.CpuData[Index].CheckThisAPEvent
> +                    );
> +    ASSERT (Status == EFI_SUCCESS);
> +  }
> +
> +  Status = EfiCreateEventReadyToBootEx (
> +             TPL_CALLBACK,
> +             ReadyToBootSignaled,
> +             NULL,
> +             &ReadyToBootEvent
> +             );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  gProcessorIDs[Index] = MAX_UINT32;
> +}
> +
> +/**
> +  Event notification function called when the EFI_EVENT_GROUP_READY_TO_BOOT is
> +  signaled. After this point, non-blocking mode is no longer allowed.
> +
> +  @param  Event     Event whose notification function is being invoked.
> +  @param  Context   The pointer to the notification function's context,
> +                    which is implementation-dependent.
> +
> +**/
> +STATIC
> +VOID
> +EFIAPI
> +ReadyToBootSignaled (
> +  IN  EFI_EVENT  Event,
> +  IN  VOID       *Context
> +  )
> +{
> +  mNonBlockingModeAllowed = FALSE;
> +}
> diff --git a/ArmPkg/Library/MpInitLib/InternalMpInitLib.h b/ArmPkg/Library/MpInitLib/InternalMpInitLib.h
> new file mode 100644
> index 000000000000..461d85ffd28f
> --- /dev/null
> +++ b/ArmPkg/Library/MpInitLib/InternalMpInitLib.h
> @@ -0,0 +1,359 @@
> +/** @file
> +
> +Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
> +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
> +Portions copyright (c) 2011, Apple Inc. All rights reserved.
> +
> +SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef MP_INTERNAL_INIT_LIB_H_
> +#define MP_INTERNAL_INIT_LIB_H_
> +
> +#include <Protocol/Cpu.h>
> +#include <Protocol/MpService.h>
> +
> +#include <Library/BaseLib.h>
> +#include <Library/UefiLib.h>
> +
> +
> +#define AP_STACK_SIZE 0x1000
> +
> +//
> +// Internal Data Structures
> +//
> +
> +//
> +// AP state
> +//
> +// The state transitions for an AP when it process a procedure are:
> +//  Idle ----> Ready ----> Busy ----> Idle
> +//       [BSP]       [AP]       [AP]
> +//
> +typedef enum {
> +  CpuStateIdle,
> +  CpuStateReady,
> +  CpuStateBlocked,
> +  CpuStateBusy,
> +  CpuStateFinished,
> +  CpuStateDisabled
> +} CPU_STATE;
> +
> +//
> +// Define Individual Processor Data block.
> +//
> +typedef struct {
> +  EFI_PROCESSOR_INFORMATION    Info;
> +  EFI_AP_PROCEDURE             Procedure;
> +  VOID                         *Parameter;
> +  CPU_STATE                    State;
> +  EFI_EVENT                    CheckThisAPEvent;
> +  UINTN                        Timeout;
> +  UINTN                        TimeTaken;
> +} CPU_AP_DATA;
> +
> +//
> +// Define MP data block which consumes individual processor block.
> +//
> +typedef struct {
> +  UINTN                   NumberOfProcessors;
> +  UINTN                   NumberOfEnabledProcessors;
> +  EFI_EVENT               CheckAllAPsEvent;
> +  EFI_EVENT               WaitEvent;
> +  UINTN                   FinishCount;
> +  UINTN                   StartCount;
> +  EFI_AP_PROCEDURE        Procedure;
> +  VOID                    *ProcedureArgument;
> +  BOOLEAN                 SingleThread;
> +  UINTN                   StartedNumber;
> +  CPU_AP_DATA             *CpuData;
> +  UINTN                   Timeout;
> +  UINTN                   *FailedList;
> +  UINTN                   FailedListIndex;
> +  BOOLEAN                 TimeoutActive;
> +} CPU_MP_DATA;
> +
> +EFI_STATUS
> +EFIAPI
> +CpuMpServicesInit (
> +  IN EFI_HANDLE        ImageHandle,
> +  IN EFI_SYSTEM_TABLE  *SystemTable
> +  );
> +
> +extern EFI_MP_SERVICES_PROTOCOL  mMpServicesTemplate;
> +
> +/** Secondary core entry point.
> +
> +**/
> +VOID  ApEntryPoint (
> +  VOID
> +  );
> +
> +/** C entry-point for the AP.
> +    This function gets called from the assembly function ApEntryPoint.
> +**/
> +VOID
> +ApProcedure (
> +  VOID
> +  );
> +
> +/** Turns on the specified core using PSCI and executes the user-supplied
> +    function that's been configured via a previous call to SetApProcedure.
> +
> +   @param ProcessorIndex The index of the core to turn on.
> +
> +   @retval EFI_SUCCESS       The processor was successfully turned on.
> +   @retval EFI_DEVICE_ERROR  An error occurred turning the processor on.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +DispatchCpu (
> +  IN UINTN ProcessorIndex
> +  );
> +
> +/** Returns whether the specified processor is the BSP.
> +
> +   @param[in] ProcessorIndex The index the processor to check.
> +
> +   @return TRUE if the processor is the BSP, FALSE otherwise.
> +**/
> +STATIC
> +BOOLEAN
> +IsProcessorBSP (
> +  UINTN ProcessorIndex
> +  );
> +
> +/** Returns whether the processor executing this function is the BSP.
> +
> +   @return Whether the current processor is the BSP.
> +**/
> +STATIC
> +BOOLEAN
> +IsCurrentProcessorBSP (
> +  VOID
> +  );
> +
> +/** Returns whether the specified processor is enabled.
> +
> +   @param[in] ProcessorIndex The index of the processor to check.
> +
> +   @return TRUE if the processor is enabled, FALSE otherwise.
> +**/
> +STATIC
> +BOOLEAN
> +IsProcessorEnabled (
> +  UINTN ProcessorIndex
> +  );
> +
> +/** Configures the processor context with the user-supplied procedure and
> +    argument.
> +
> +   @param CpuData           The processor context.
> +   @param Procedure         The user-supplied procedure.
> +   @param ProcedureArgument The user-supplied procedure argument.
> +
> +**/
> +STATIC
> +VOID
> +SetApProcedure (
> +  IN   CPU_AP_DATA         *CpuData,
> +  IN   EFI_AP_PROCEDURE    Procedure,
> +  IN   VOID                *ProcedureArgument
> +  );
> +
> +/**
> +  Get the Application Processors state.
> +
> +  @param[in]  CpuData    The pointer to CPU_AP_DATA of specified AP
> +
> +  @return  The AP status
> +**/
> +CPU_STATE
> +GetApState (
> +  IN  CPU_AP_DATA     *CpuData
> +  );
> +
> +/** Returns the index of the next processor that is blocked.
> +
> +   @param[out] NextNumber The index of the next blocked processor.
> +
> +   @retval EFI_SUCCESS   Successfully found the next blocked processor.
> +   @retval EFI_NOT_FOUND There are no blocked processors.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +GetNextBlockedNumber (
> +  OUT UINTN *NextNumber
> +  );
> +
> +/** Stalls the BSP for the minimum of gPollInterval and Timeout.
> +
> +   @param[in]  Timeout    The time limit in microseconds remaining for
> +                          APs to return from Procedure.
> +
> +   @retval     StallTime  Time of execution stall.
> +**/
> +STATIC
> +UINTN
> +CalculateAndStallInterval (
> +  IN UINTN Timeout
> +  );
> +
> +/** Returns whether all processors are in the idle state.
> +
> +   @return Whether all the processors are idle.
> +
> +**/
> +STATIC
> +BOOLEAN
> +CheckAllCpusReady (
> +  VOID
> +  );
> +
> +/** Sets up the state for the StartupAllAPs function.
> +
> +   @param SingleThread Whether the APs will execute sequentially.
> +
> +**/
> +STATIC
> +VOID
> +StartupAllAPsPrepareState (
> +  IN BOOLEAN SingleThread
> +  );
> +
> +/** Handles execution of StartupAllAPs when a WaitEvent has been specified.
> +
> +   @param Procedure         The user-supplied procedure.
> +   @param ProcedureArgument The user-supplied procedure argument.
> +   @param WaitEvent         The wait event to be signaled when the work is
> +                            complete or a timeout has occurred.
> +   @param TimeoutInMicroseconds The timeout for the work to be completed. Zero
> +                                indicates an infinite timeout.
> +
> +   @return EFI_SUCCESS on success.
> +**/
> +STATIC
> +EFI_STATUS
> +StartupAllAPsWithWaitEvent (
> +  IN EFI_AP_PROCEDURE Procedure,
> +  IN VOID             *ProcedureArgument,
> +  IN EFI_EVENT        WaitEvent,
> +  IN UINTN            TimeoutInMicroseconds
> +  );
> +
> +/** Handles execution of StartupAllAPs when no wait event has been specified.
> +
> +   @param Procedure             The user-supplied procedure.
> +   @param ProcedureArgument     The user-supplied procedure argument.
> +   @param TimeoutInMicroseconds The timeout for the work to be completed. Zero
> +                                indicates an infinite timeout.
> +   @param SingleThread          Whether the APs will execute sequentially.
> +   @param FailedCpuList         User-supplied pointer for list of failed CPUs.
> +
> +   @return EFI_SUCCESS on success.
> +**/
> +STATIC
> +EFI_STATUS
> +StartupAllAPsNoWaitEvent (
> +  IN EFI_AP_PROCEDURE Procedure,
> +  IN VOID             *ProcedureArgument,
> +  IN UINTN            TimeoutInMicroseconds,
> +  IN BOOLEAN          SingleThread,
> +  IN UINTN            **FailedCpuList
> +  );
> +
> +
> +/** Adds the specified processor the list of failed processors.
> +
> +   @param ProcessorIndex The processor index to add.
> +   @param ApState         Processor state.
> +
> +**/
> +STATIC
> +VOID
> +AddProcessorToFailedList (
> +  UINTN           ProcessorIndex,
> +  CPU_STATE       ApState
> +  );
> +
> +/** Handles the StartupAllAPs case where the timeout has occurred.
> +
> +**/
> +STATIC
> +VOID
> +ProcessStartupAllAPsTimeout (
> +  VOID
> +  );
> +
> +/**
> +  If a timeout is specified in StartupAllAps(), a timer is set, which invokes
> +  this procedure periodically to check whether all APs have finished.
> +
> +  @param[in] Event   The WaitEvent the user supplied.
> +  @param[in] Context The event context.
> +**/
> +STATIC
> +VOID
> +EFIAPI
> +CheckAllAPsStatus (
> +  IN  EFI_EVENT   Event,
> +  IN  VOID        *Context
> +  );
> +
> +/** Invoked periodically via a timer to check the state of the processor.
> +
> +   @param Event   The event supplied by the timer expiration.
> +   @param Context The processor context.
> +
> +**/
> +STATIC
> +VOID
> +EFIAPI
> +CheckThisAPStatus (
> +  IN  EFI_EVENT   Event,
> +  IN  VOID        *Context
> +  );
> +
> +/**
> +  This function is called by all processors (both BSP and AP) once and collects
> +  MP related data.
> +
> +  @param BSP            TRUE if the processor is the BSP.
> +  @param Mpidr          The MPIDR for the specified processor. This should be
> +                        the full MPIDR and not only the affinity bits.
> +  @param ProcessorIndex The index of the processor.
> +
> +  @return EFI_SUCCESS if the data for the processor collected and filled in.
> +
> +**/
> +STATIC
> +EFI_STATUS
> +FillInProcessorInformation (
> +  IN BOOLEAN      BSP,
> +  IN UINTN        Mpidr,
> +  IN UINTN        ProcessorIndex
> +  );
> +
> +/**
> +  Event notification function called when the EFI_EVENT_GROUP_READY_TO_BOOT is
> +  signaled. After this point, non-blocking mode is no longer allowed.
> +
> +  @param  Event     Event whose notification function is being invoked.
> +  @param  Context   The pointer to the notification function's context,
> +                    which is implementation-dependent.
> +
> +**/
> +STATIC
> +VOID
> +EFIAPI
> +ReadyToBootSignaled (
> +  IN  EFI_EVENT                Event,
> +  IN  VOID                     *Context
> +  );
> +
> +
> +#endif /* MP_INTERNAL_INIT_LIB_H_ */
> diff --git a/ArmVirtPkg/ArmVirt.dsc.inc b/ArmVirtPkg/ArmVirt.dsc.inc
> index 5a1598d90ca7..af6e48fd6cfc 100644
> --- a/ArmVirtPkg/ArmVirt.dsc.inc
> +++ b/ArmVirtPkg/ArmVirt.dsc.inc
> @@ -261,6 +261,9 @@
>  [LibraryClasses.ARM]
>    ArmSoftFloatLib|ArmPkg/Library/ArmSoftFloatLib/ArmSoftFloatLib.inf
>
> +[LibraryClasses.AARCH64]
> +  MpInitLib|ArmPkg/Library/MpInitLib/DxeMpInitLib.inf
> +
>  [BuildOptions]
>    RVCT:RELEASE_*_*_CC_FLAGS  = -DMDEPKG_NDEBUG
>
> --
> 2.31.1
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 2/2] ArmPkg: Add Library/MpInitLib to support EFI_MP_SERVICES_PROTOCOL
  2021-12-15 17:55   ` Ard Biesheuvel
@ 2021-12-15 18:15     ` Rebecca Cran
  0 siblings, 0 replies; 9+ messages in thread
From: Rebecca Cran @ 2021-12-15 18:15 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: edk2-devel-groups-io, Ard Biesheuvel, Gerd Hoffmann,
	Samer El-Haj-Mahmoud, Leif Lindholm, Sami Mujawar

On 12/15/21 10:55 AM, Ard Biesheuvel wrote:
>
> Perhaps I misunderstood your question about splitting up this patch,
> as surely, adding a completely new library can be broken out into a
> separate one?

Oh, of course it can! I just need to also split out the change to 
CpuDxe.inf.


-- 
Rebecca Cran


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 1/2] ArmPkg: Replace CoreId and ClusterId with Mpidr in ARM_CORE_INFO struct
  2021-12-15 17:53   ` Ard Biesheuvel
@ 2021-12-15 20:33     ` Rebecca Cran
  0 siblings, 0 replies; 9+ messages in thread
From: Rebecca Cran @ 2021-12-15 20:33 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: edk2-devel-groups-io, Ard Biesheuvel, Gerd Hoffmann,
	Samer El-Haj-Mahmoud, Leif Lindholm, Sami Mujawar

On 12/15/21 10:53 AM, Ard Biesheuvel wrote:
> On Wed, 15 Dec 2021 at 18:46, Rebecca Cran <rebecca@nuviainc.com> wrote:
>>   #define ARM_CORE_MASK     ARM_CORE_AFF0
>>   #define ARM_CLUSTER_MASK  ARM_CORE_AFF1
>> -#define GET_CORE_ID(MpId)            ((MpId) & ARM_CORE_MASK)
>> -#define GET_CLUSTER_ID(MpId)         (((MpId) & ARM_CLUSTER_MASK) >> 8)
>> +#define GET_CORE_ID(MpId)           ((MpId) & ARM_CORE_MASK)
>> +#define GET_CLUSTER_ID(MpId)        (((MpId) & ARM_CLUSTER_MASK) >> 8)
>>   #define GET_MPID(ClusterId, CoreId)  (((ClusterId) << 8) | (CoreId))
>> -#define PRIMARY_CORE_ID  (PcdGet32(PcdArmPrimaryCore) & ARM_CORE_MASK)
>> +#define GET_MPIDR_AFF0(MpId)  ((MpId) & ARM_CORE_AFF0)
>> +#define GET_MPIDR_AFF1(MpId)  (((MpId) & ARM_CORE_AFF1) >> 8)
>> +#define GET_MPIDR_AFF2(MpId)  (((MpId) & ARM_CORE_AFF2) >> 16)
>> +#define GET_MPIDR_AFF3(MpId)  (((MpId) & ARM_CORE_AFF3) >> 32)
>> +#define PRIMARY_CORE_ID       (PcdGet32(PcdArmPrimaryCore) & ARM_CORE_MASK)
>>
> Any reason in particular for these whitespace changes? If not, please
> omit them - reviewing changes in unfamiliar code is challenging enough
> without having to figure out which hunks actually matter and which
> ones don't.

Nope, that was a mistake I made when trying to integrate my changes 
following the Uncrustify work.

>     // Find the core in the ArmCoreTable
>     for (Index = 0; Index < ArmCoreCount; Index++) {
> -    if ((ArmCoreInfoTable[Index].ClusterId == ClusterId) && (ArmCoreInfoTable[Index].CoreId == CoreId)) {
> +    if ((GET_MPIDR_AFF1 (ArmCoreInfoTable[Index].Mpidr) == ClusterId) && (GET_MPIDR_AFF0 (ArmCoreInfoTable[Index].Mpidr) == CoreId)) {
> Please rewrap overly long lines.


Will do.


-- 
Rebecca Cran


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 2/2] ArmPkg: Add Library/MpInitLib to support EFI_MP_SERVICES_PROTOCOL
  2021-12-15 17:46 ` [PATCH v2 2/2] ArmPkg: Add Library/MpInitLib to support EFI_MP_SERVICES_PROTOCOL Rebecca Cran
  2021-12-15 17:55   ` Ard Biesheuvel
@ 2021-12-15 21:26   ` Rebecca Cran
       [not found]   ` <16C10ACB9CD70BF0.6360@groups.io>
  2 siblings, 0 replies; 9+ messages in thread
From: Rebecca Cran @ 2021-12-15 21:26 UTC (permalink / raw)
  To: devel, Ard Biesheuvel, Gerd Hoffmann, Samer El-Haj-Mahmoud,
	Leif Lindholm, Sami Mujawar

[-- Attachment #1: Type: text/plain, Size: 1380 bytes --]

On 12/15/21 10:46 AM, Rebecca Cran wrote:
> diff --git a/ArmPkg/Library/MpInitLib/DxeMpInitLib.inf b/ArmPkg/Library/MpInitLib/DxeMpInitLib.inf
> new file mode 100644
> index 000000000000..2275b6cca33a
> --- /dev/null
> +++ b/ArmPkg/Library/MpInitLib/DxeMpInitLib.inf
> @@ -0,0 +1,53 @@
> +#/** @file
> +#
> +# Component description file for the DxeMpInitLib module.
> +#
> +# Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +#**/
> +
> +[Defines]
> +  INF_VERSION                    = 1.29

I'm having problems getting the CI Ecc check to pass this file: it keep 
saying:

ERROR - EFI coding style error
ERROR - *Error code: 9001
ERROR - *The file headers should follow Doxygen special documentation 
blocks in section 2.3.5
ERROR - *file: 
D:\a\1\s\Build\.pytool\Plugin\EccCheck\ArmPkg\Library\MpInitLib\DxeMpInitLib.inf
ERROR - *Line number: 1
ERROR - *Header comment section must have Abstract information.

ERROR - --->Test Failed: EccCheck Test NO-TARGET returned 1


I've tried copying other .inf files in the tree: with the style above, 
removing the C-style "/**" and "**/" and adding an extra "#" (i.e. "## 
@file") without any success.

I've also moved the comment up to line 2 with no success. I was 
wondering if anyone could help me come up with a header that can pass CI?


Thanks.
Rebecca Cran


[-- Attachment #2: Type: text/html, Size: 9262 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [edk2-devel] [PATCH v2 2/2] ArmPkg: Add Library/MpInitLib to support EFI_MP_SERVICES_PROTOCOL
       [not found]   ` <16C10ACB9CD70BF0.6360@groups.io>
@ 2021-12-16  2:45     ` Rebecca Cran
  0 siblings, 0 replies; 9+ messages in thread
From: Rebecca Cran @ 2021-12-16  2:45 UTC (permalink / raw)
  To: devel, Ard Biesheuvel, Gerd Hoffmann, Samer El-Haj-Mahmoud,
	Leif Lindholm, Sami Mujawar

[-- Attachment #1: Type: text/plain, Size: 1608 bytes --]

I figured it out: it didn't like the generic wording "Component 
description file".


-- 
Rebecca Cran


On 12/15/21 2:26 PM, Rebecca Cran via groups.io wrote:
> On 12/15/21 10:46 AM, Rebecca Cran wrote:
>> diff --git a/ArmPkg/Library/MpInitLib/DxeMpInitLib.inf b/ArmPkg/Library/MpInitLib/DxeMpInitLib.inf
>> new file mode 100644
>> index 000000000000..2275b6cca33a
>> --- /dev/null
>> +++ b/ArmPkg/Library/MpInitLib/DxeMpInitLib.inf
>> @@ -0,0 +1,53 @@
>> +#/** @file
>> +#
>> +# Component description file for the DxeMpInitLib module.
>> +#
>> +# Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
>> +#
>> +# SPDX-License-Identifier: BSD-2-Clause-Patent
>> +#
>> +#**/
>> +
>> +[Defines]
>> +  INF_VERSION                    = 1.29
>
> I'm having problems getting the CI Ecc check to pass this file: it 
> keep saying:
>
> ERROR - EFI coding style error
> ERROR - *Error code: 9001
> ERROR - *The file headers should follow Doxygen special documentation 
> blocks in section 2.3.5
> ERROR - *file: 
> D:\a\1\s\Build\.pytool\Plugin\EccCheck\ArmPkg\Library\MpInitLib\DxeMpInitLib.inf
> ERROR - *Line number: 1
> ERROR - *Header comment section must have Abstract information.
>
> ERROR - --->Test Failed: EccCheck Test NO-TARGET returned 1
>
>
> I've tried copying other .inf files in the tree: with the style above, 
> removing the C-style "/**" and "**/" and adding an extra "#" (i.e. "## 
> @file") without any success.
>
> I've also moved the comment up to line 2 with no success. I was 
> wondering if anyone could help me come up with a header that can pass CI?
>
>
> Thanks.
> Rebecca Cran
>
> 

[-- Attachment #2: Type: text/html, Size: 10034 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2021-12-16  2:45 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2021-12-15 17:46 [PATCH v2 0/2] ArmPkg,ArmVirtPkg: Add support EFI_MP_SERVICES_PROTOCOL on AARCH64 Rebecca Cran
2021-12-15 17:46 ` [PATCH v2 1/2] ArmPkg: Replace CoreId and ClusterId with Mpidr in ARM_CORE_INFO struct Rebecca Cran
2021-12-15 17:53   ` Ard Biesheuvel
2021-12-15 20:33     ` Rebecca Cran
2021-12-15 17:46 ` [PATCH v2 2/2] ArmPkg: Add Library/MpInitLib to support EFI_MP_SERVICES_PROTOCOL Rebecca Cran
2021-12-15 17:55   ` Ard Biesheuvel
2021-12-15 18:15     ` Rebecca Cran
2021-12-15 21:26   ` Rebecca Cran
     [not found]   ` <16C10ACB9CD70BF0.6360@groups.io>
2021-12-16  2:45     ` [edk2-devel] " Rebecca Cran

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox