Chao,
Do you mind putting the lib content under 
UefiCpuPkg/Library/MpInitLib/LoongArch64/?

It also follows the guidelines and avoid creating too many folders under 
Library folder.

Thanks,
Ray
> -----Original Message-----
> From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Chao Li
> Sent: Friday, January 12, 2024 4:24 PM
> To: devel@edk2.groups.io
> Cc: Dong, Eric <eric.d...@intel.com>; Ni, Ray <ray...@intel.com>; Kumar,
> Rahul R <rahul.r.ku...@intel.com>; Gerd Hoffmann <kra...@redhat.com>
> Subject: [edk2-devel] [PATCH v7 15/37] UefiCpuPkg: Add multiprocessor
> library for LoongArch64
> 
> Added a new library named LoongArch64MpInitLib.
> 
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584
> 
> Cc: Eric Dong <eric.d...@intel.com>
> Cc: Ray Ni <ray...@intel.com>
> Cc: Rahul Kumar <rahul1.ku...@intel.com>
> Cc: Gerd Hoffmann <kra...@redhat.com>
> Signed-off-by: Chao Li <lic...@loongson.cn>
> Acked-by: Ray Ni <ray...@intel.com>
> ---
>  .../LoongArch64MpInitLib/DxeMpInitLib.inf     |   45 +
>  .../LoongArch64MpInitLib/DxeMpInitLib.uni     |   15 +
>  .../Library/LoongArch64MpInitLib/DxeMpLib.c   |  480 +++++
>  .../Library/LoongArch64MpInitLib/MpLib.c      | 1596 +++++++++++++++++
>  .../Library/LoongArch64MpInitLib/MpLib.h      |  361 ++++
>  .../LoongArch64MpInitLib/PeiMpInitLib.inf     |   37 +
>  .../LoongArch64MpInitLib/PeiMpInitLib.uni     |   15 +
>  .../Library/LoongArch64MpInitLib/PeiMpLib.c   |  404 +++++
>  UefiCpuPkg/UefiCpuPkg.dsc                     |    2 +
>  9 files changed, 2955 insertions(+)
>  create mode 100644
> UefiCpuPkg/Library/LoongArch64MpInitLib/DxeMpInitLib.inf
>  create mode 100644
> UefiCpuPkg/Library/LoongArch64MpInitLib/DxeMpInitLib.uni
>  create mode 100644
> UefiCpuPkg/Library/LoongArch64MpInitLib/DxeMpLib.c
>  create mode 100644 UefiCpuPkg/Library/LoongArch64MpInitLib/MpLib.c
>  create mode 100644 UefiCpuPkg/Library/LoongArch64MpInitLib/MpLib.h
>  create mode 100644
> UefiCpuPkg/Library/LoongArch64MpInitLib/PeiMpInitLib.inf
>  create mode 100644
> UefiCpuPkg/Library/LoongArch64MpInitLib/PeiMpInitLib.uni
>  create mode 100644 UefiCpuPkg/Library/LoongArch64MpInitLib/PeiMpLib.c
> 
> diff --git a/UefiCpuPkg/Library/LoongArch64MpInitLib/DxeMpInitLib.inf
> b/UefiCpuPkg/Library/LoongArch64MpInitLib/DxeMpInitLib.inf
> new file mode 100644
> index 0000000000..519af41209
> --- /dev/null
> +++ b/UefiCpuPkg/Library/LoongArch64MpInitLib/DxeMpInitLib.inf
> @@ -0,0 +1,45 @@
> +## @file
> +#  LoongArch64 MP initialize support functions for DXE phase.
> +#
> +#  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights
> reserved.<BR>
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +
> +[Defines]
> +  INF_VERSION                    = 1.29
> +  BASE_NAME                      = DxeMpInitLib
> +  MODULE_UNI_FILE                = DxeMpInitLib.uni
> +  FILE_GUID                      = C3B9ACAA-B67C-D3E0-E70D-7982B6EA2931
> +  MODULE_TYPE                    = DXE_DRIVER
> +  VERSION_STRING                 = 1.1
> +  LIBRARY_CLASS                  = MpInitLib|DXE_DRIVER
> +
> +[Sources.common]
> +  DxeMpLib.c
> +  MpLib.c
> +  MpLib.h
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  UefiCpuPkg/UefiCpuPkg.dec
> +
> +[LibraryClasses]
> +  BaseLib
> +  CpuLib
> +  DebugAgentLib
> +  HobLib
> +  MemoryAllocationLib
> +  SynchronizationLib
> +  TimerLib
> +  UefiLib
> +
> +[Protocols]
> +  gEfiTimerArchProtocolGuid                     ## SOMETIMES_CONSUMES
> +
> +[Pcd]
> +  gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber           ##
> CONSUMES
> +  gUefiCpuPkgTokenSpaceGuid.PcdCpuApInitTimeOutInMicroSeconds         ##
> CONSUMES
> +
> gUefiCpuPkgTokenSpaceGuid.PcdCpuApStatusCheckIntervalInMicroSeconds
> ## CONSUMES
> diff --git a/UefiCpuPkg/Library/LoongArch64MpInitLib/DxeMpInitLib.uni
> b/UefiCpuPkg/Library/LoongArch64MpInitLib/DxeMpInitLib.uni
> new file mode 100644
> index 0000000000..c1c67291a6
> --- /dev/null
> +++ b/UefiCpuPkg/Library/LoongArch64MpInitLib/DxeMpInitLib.uni
> @@ -0,0 +1,15 @@
> +// /** @file
> +// MP Initialize Library instance for DXE driver.
> +//
> +// MP Initialize Library instance for DXE driver.
> +//
> +// Copyright (c) 2024, Loongson Technology Corporation Limited. All rights
> reserved.<BR>
> +//
> +// SPDX-License-Identifier: BSD-2-Clause-Patent
> +//
> +// **/
> +
> +
> +#string STR_MODULE_ABSTRACT             #language en-US "MP Initialize
> Library instance for DXE driver."
> +
> +#string STR_MODULE_DESCRIPTION          #language en-US "MP Initialize
> Library instance for DXE driver."
> diff --git a/UefiCpuPkg/Library/LoongArch64MpInitLib/DxeMpLib.c
> b/UefiCpuPkg/Library/LoongArch64MpInitLib/DxeMpLib.c
> new file mode 100644
> index 0000000000..739da77e32
> --- /dev/null
> +++ b/UefiCpuPkg/Library/LoongArch64MpInitLib/DxeMpLib.c
> @@ -0,0 +1,480 @@
> +/** @file
> +  LoongArch64 MP initialize support functions for DXE phase.
> +
> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights
> reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include "MpLib.h"
> +
> +#include <Library/DebugAgentLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/UefiLib.h>
> +
> +#include <Protocol/Timer.h>
> +
> +CPU_MP_DATA       *mCpuMpData            = NULL;
> +EFI_EVENT         mCheckAllApsEvent      = NULL;
> +volatile BOOLEAN  mStopCheckAllApsStatus = TRUE;
> +
> +/**
> +  Enable Debug Agent to support source debugging on AP function.
> +
> +**/
> +VOID
> +EnableDebugAgent (
> +  VOID
> +  )
> +{
> +  //
> +  // Initialize Debug Agent to support source level debug in DXE phase
> +  //
> +  InitializeDebugAgent (DEBUG_AGENT_INIT_DXE_AP, NULL, NULL);
> +}
> +
> +/**
> +  Get the pointer to CPU MP Data structure.
> +
> +  @return  The pointer to CPU MP Data structure.
> +**/
> +CPU_MP_DATA *
> +GetCpuMpData (
> +  VOID
> +  )
> +{
> +  ASSERT (mCpuMpData != NULL);
> +  return mCpuMpData;
> +}
> +
> +/**
> +  Save the pointer to CPU MP Data structure.
> +
> +  @param[in] CpuMpData  The pointer to CPU MP Data structure will be
> saved.
> +**/
> +VOID
> +SaveCpuMpData (
> +  IN CPU_MP_DATA  *CpuMpData
> +  )
> +{
> +  mCpuMpData = CpuMpData;
> +}
> +
> +/**
> +  Get available EfiBootServicesCode memory below 4GB by specified size.
> +
> +  This buffer is required to safely transfer AP from real address mode to
> +  protected mode or long mode, due to the fact that the buffer returned by
> +  GetWakeupBuffer() may be marked as non-executable.
> +
> +  @param[in] BufferSize   Wakeup transition buffer size.
> +
> +  @retval other   Return wakeup transition buffer address below 4GB.
> +  @retval 0       Cannot find free memory below 4GB.
> +**/
> +UINTN
> +GetModeTransitionBuffer (
> +  IN UINTN  BufferSize
> +  )
> +{
> +  return 0;
> +}
> +
> +/**
> +  Checks APs status and updates APs status if needed.
> +
> +**/
> +VOID
> +CheckAndUpdateApsStatus (
> +  VOID
> +  )
> +{
> +  UINTN        ProcessorNumber;
> +  EFI_STATUS   Status;
> +  CPU_MP_DATA  *CpuMpData;
> +
> +  CpuMpData = GetCpuMpData ();
> +
> +  //
> +  // First, check whether pending StartupAllAPs() exists.
> +  //
> +  if (CpuMpData->WaitEvent != NULL) {
> +    Status = CheckAllAPs ();
> +    //
> +    // If all APs finish for StartupAllAPs(), signal the WaitEvent for it.
> +    //
> +    if (Status != EFI_NOT_READY) {
> +      Status               = gBS->SignalEvent (CpuMpData->WaitEvent);
> +      CpuMpData->WaitEvent = NULL;
> +    }
> +  }
> +
> +  //
> +  // Second, check whether pending StartupThisAPs() callings exist.
> +  //
> +  for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount;
> ProcessorNumber++) {
> +    if (CpuMpData->CpuData[ProcessorNumber].WaitEvent == NULL) {
> +      continue;
> +    }
> +
> +    Status = CheckThisAP (ProcessorNumber);
> +
> +    if (Status != EFI_NOT_READY) {
> +      gBS->SignalEvent (CpuMpData->CpuData[ProcessorNumber].WaitEvent);
> +      CpuMpData->CpuData[ProcessorNumber].WaitEvent = NULL;
> +    }
> +  }
> +}
> +
> +/**
> +  Checks APs' status periodically.
> +
> +  This function is triggered by timer periodically to check the
> +  state of APs for StartupAllAPs() and StartupThisAP() executed
> +  in non-blocking mode.
> +
> +  @param[in]  Event    Event triggered.
> +  @param[in]  Context  Parameter passed with the event.
> +
> +**/
> +VOID
> +EFIAPI
> +CheckApsStatus (
> +  IN  EFI_EVENT  Event,
> +  IN  VOID       *Context
> +  )
> +{
> +  //
> +  // If CheckApsStatus() is not stopped, otherwise return immediately.
> +  //
> +  if (!mStopCheckAllApsStatus) {
> +    CheckAndUpdateApsStatus ();
> +  }
> +}
> +
> +/**
> +  Initialize global data for MP support.
> +
> +  @param[in] CpuMpData  The pointer to CPU MP Data structure.
> +**/
> +VOID
> +InitMpGlobalData (
> +  IN CPU_MP_DATA  *CpuMpData
> +  )
> +{
> +  EFI_STATUS  Status;
> +
> +  SaveCpuMpData (CpuMpData);
> +
> +  Status = gBS->CreateEvent (
> +                  EVT_TIMER | EVT_NOTIFY_SIGNAL,
> +                  TPL_NOTIFY,
> +                  CheckApsStatus,
> +                  NULL,
> +                  &mCheckAllApsEvent
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  //
> +  // Set timer to check all APs status.
> +  //
> +  Status = gBS->SetTimer (
> +                  mCheckAllApsEvent,
> +                  TimerPeriodic,
> +                  EFI_TIMER_PERIOD_MICROSECONDS (
> +                    PcdGet32 (PcdCpuApStatusCheckIntervalInMicroSeconds)
> +                    )
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +}
> +
> +/**
> +  This service executes a caller provided function on all enabled APs.
> +
> +  @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 MpInitLibStartupAllAPs() or
> +                                      MPInitLibStartupThisAP().
> +                                      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 
> Initialization
> +                                      library, 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_UNSUPPORTED         WaitEvent is not NULL if non-blocking
> mode is not
> +                                  supported.
> +  @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_NOT_READY           MP Initialize Library is not initialized.
> +  @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_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;
> +
> +  //
> +  // Temporarily stop checkAllApsStatus for avoid resource dead-lock.
> +  //
> +  mStopCheckAllApsStatus = TRUE;
> +
> +  Status = StartupAllCPUsWorker (
> +             Procedure,
> +             SingleThread,
> +             TRUE,
> +             WaitEvent,
> +             TimeoutInMicroseconds,
> +             ProcedureArgument,
> +             FailedCpuList
> +             );
> +
> +  //
> +  // Start checkAllApsStatus
> +  //
> +  mStopCheckAllApsStatus = FALSE;
> +
> +  return Status;
> +}
> +
> +/**
> +  This service lets the caller get one enabled AP to execute a 
> caller-provided
> +  function.
> +
> +  @param[in]  Procedure               A pointer to the function to be run on 
> the
> +                                      designated AP 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
> +                                      MpInitLibGetNumberOfProcessors().
> +  @param[in]  WaitEvent               The event created by the caller with
> CreateEvent()
> +                                      service.  If it is NULL, then execute 
> in
> +                                      blocking mode. BSP waits until this AP 
> 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 this AP,
> +                                      and go on executing immediately. If 
> this AP
> +                                      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
> +                                      this AP to finish this Procedure, 
> either for
> +                                      blocking or non-blocking mode. Zero 
> means
> +                                      infinity.  If the timeout expires 
> before
> +                                      this AP returns from Procedure, then 
> Procedure
> +                                      on the AP is terminated. The
> +                                      AP is available for next function 
> assigned
> +                                      by MpInitLibStartupAllAPs() or
> +                                      MpInitLibStartupThisAP().
> +                                      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
> on the
> +                                      specified AP.
> +  @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_UNSUPPORTED         WaitEvent is not NULL if non-blocking
> mode is not
> +                                  supported.
> +  @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_READY           MP Initialize Library is not initialized.
> +  @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_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;
> +
> +  //
> +  // temporarily stop checkAllApsStatus for avoid resource dead-lock.
> +  //
> +  mStopCheckAllApsStatus = TRUE;
> +
> +  Status = StartupThisAPWorker (
> +             Procedure,
> +             ProcessorNumber,
> +             WaitEvent,
> +             TimeoutInMicroseconds,
> +             ProcedureArgument,
> +             Finished
> +             );
> +
> +  mStopCheckAllApsStatus = FALSE;
> +
> +  return Status;
> +}
> +
> +/**
> +  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] 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
> +                               MpInitLibGetNumberOfProcessors().
> +  @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_DEVICE_ERROR        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.
> +  @retval EFI_NOT_READY           MP Initialize Library is not initialized.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +MpInitLibSwitchBSP (
> +  IN UINTN    ProcessorNumber,
> +  IN BOOLEAN  EnableOldBSP
> +  )
> +{
> +  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] ProcessorNumber   The handle number of 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
> +                               MpInitLibGetNumberOfProcessors().
> +  @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.
> +  @retval EFI_NOT_READY           MP Initialize Library is not initialized.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +MpInitLibEnableDisableAP (
> +  IN  UINTN    ProcessorNumber,
> +  IN  BOOLEAN  EnableAP,
> +  IN  UINT32   *HealthFlag OPTIONAL
> +  )
> +{
> +  return EFI_UNSUPPORTED;
> +}
> diff --git a/UefiCpuPkg/Library/LoongArch64MpInitLib/MpLib.c
> b/UefiCpuPkg/Library/LoongArch64MpInitLib/MpLib.c
> new file mode 100644
> index 0000000000..edc2670dca
> --- /dev/null
> +++ b/UefiCpuPkg/Library/LoongArch64MpInitLib/MpLib.c
> @@ -0,0 +1,1596 @@
> +/** @file
> +  LoongArch64 CPU MP Initialize Library common functions.
> +
> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights
> reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include "MpLib.h"
> +
> +#include <Library/BaseLib.h>
> +#include <Register/LoongArch64/Csr.h>
> +
> +EFI_GUID  mCpuInitMpLibHobGuid      = CPU_INIT_MP_LIB_HOB_GUID;
> +EFI_GUID  mProcessorResourceHobGuid =
> PROCESSOR_RESOURCE_HOB_GUID;
> +
> +/**
> +  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;
> +}
> +
> +/**
> +  Set the Application Processors state.
> +
> +  @param[in]   CpuData    The pointer to CPU_AP_DATA of specified AP
> +  @param[in]   State      The AP status
> +**/
> +VOID
> +SetApState (
> +  IN  CPU_AP_DATA  *CpuData,
> +  IN  CPU_STATE    State
> +  )
> +{
> +  AcquireSpinLock (&CpuData->ApLock);
> +  CpuData->State = State;
> +  ReleaseSpinLock (&CpuData->ApLock);
> +}
> +
> +/**
> +  Get APIC ID of the executing processor.
> +
> +  @return  32-bit APIC ID of the executing processor.
> +**/
> +UINT32
> +GetApicId (
> +  VOID
> +  )
> +{
> +  UINTN  CpuNum;
> +
> +  CpuNum = CsrRead (LOONGARCH_CSR_CPUNUM);
> +
> +  return CpuNum & 0x3ff;
> +}
> +
> +/**
> +  Find the current Processor number by APIC ID.
> +
> +  @param[in]  CpuMpData         Pointer to PEI CPU MP Data
> +  @param[out] ProcessorNumber   Return the pocessor number found
> +
> +  @retval EFI_SUCCESS          ProcessorNumber is found and returned.
> +  @retval EFI_NOT_FOUND        ProcessorNumber is not found.
> +**/
> +EFI_STATUS
> +GetProcessorNumber (
> +  IN CPU_MP_DATA  *CpuMpData,
> +  OUT UINTN       *ProcessorNumber
> +  )
> +{
> +  UINTN            TotalProcessorNumber;
> +  UINTN            Index;
> +  CPU_INFO_IN_HOB  *CpuInfoInHob;
> +
> +  CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData-
> >CpuInfoInHob;
> +
> +  TotalProcessorNumber = CpuMpData->CpuCount;
> +  for (Index = 0; Index < TotalProcessorNumber; Index++) {
> +    if (CpuInfoInHob[Index].ApicId == GetApicId ()) {
> +      *ProcessorNumber = Index;
> +      return EFI_SUCCESS;
> +    }
> +  }
> +
> +  return EFI_NOT_FOUND;
> +}
> +
> +/**
> +  Sort the APIC ID of all processors.
> +
> +  This function sorts the APIC ID of all processors so that processor number 
> is
> +  assigned in the ascending order of APIC ID which eases MP debugging.
> +
> +  @param[in] CpuMpData        Pointer to PEI CPU MP Data
> +**/
> +VOID
> +SortApicId (
> +  IN CPU_MP_DATA  *CpuMpData
> +  )
> +{
> +  UINTN            Index1;
> +  UINTN            Index2;
> +  UINTN            Index3;
> +  UINT32           ApicId;
> +  CPU_INFO_IN_HOB  CpuInfo;
> +  UINT32           ApCount;
> +  CPU_INFO_IN_HOB  *CpuInfoInHob;
> +  volatile UINT32  *StartupApSignal;
> +
> +  ApCount      = CpuMpData->CpuCount - 1;
> +  CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData-
> >CpuInfoInHob;
> +  if (ApCount != 0) {
> +    for (Index1 = 0; Index1 < ApCount; Index1++) {
> +      Index3 = Index1;
> +      //
> +      // Sort key is the hardware default APIC ID
> +      //
> +      ApicId = CpuInfoInHob[Index1].ApicId;
> +      for (Index2 = Index1 + 1; Index2 <= ApCount; Index2++) {
> +        if (ApicId > CpuInfoInHob[Index2].ApicId) {
> +          Index3 = Index2;
> +          ApicId = CpuInfoInHob[Index2].ApicId;
> +        }
> +      }
> +
> +      if (Index3 != Index1) {
> +        CopyMem (&CpuInfo, &CpuInfoInHob[Index3], sizeof
> (CPU_INFO_IN_HOB));
> +        CopyMem (
> +          &CpuInfoInHob[Index3],
> +          &CpuInfoInHob[Index1],
> +          sizeof (CPU_INFO_IN_HOB)
> +          );
> +        CopyMem (&CpuInfoInHob[Index1], &CpuInfo, sizeof
> (CPU_INFO_IN_HOB));
> +
> +        //
> +        // Also exchange the StartupApSignal.
> +        //
> +        StartupApSignal                            = CpuMpData-
> >CpuData[Index3].StartupApSignal;
> +        CpuMpData->CpuData[Index3].StartupApSignal =
> +          CpuMpData->CpuData[Index1].StartupApSignal;
> +        CpuMpData->CpuData[Index1].StartupApSignal = StartupApSignal;
> +      }
> +    }
> +
> +    //
> +    // Get the processor number for the BSP
> +    //
> +    ApicId = GetApicId ();
> +    for (Index1 = 0; Index1 < CpuMpData->CpuCount; Index1++) {
> +      if (CpuInfoInHob[Index1].ApicId == ApicId) {
> +        CpuMpData->BspNumber = (UINT32)Index1;
> +        break;
> +      }
> +    }
> +  }
> +}
> +
> +/**
> +  Get pointer to Processor Resource Data structure from GUIDd HOB.
> +
> +  @return  The pointer to Processor Resource Data structure.
> +**/
> +PROCESSOR_RESOURCE_DATA *
> +GetProcessorResourceDataFromGuidedHob (
> +  VOID
> +  )
> +{
> +  EFI_HOB_GUID_TYPE        *GuidHob;
> +  VOID                     *DataInHob;
> +  PROCESSOR_RESOURCE_DATA  *ResourceData;
> +
> +  ResourceData = NULL;
> +  GuidHob      = GetFirstGuidHob (&mProcessorResourceHobGuid);
> +  if (GuidHob != NULL) {
> +    DataInHob    = GET_GUID_HOB_DATA (GuidHob);
> +    ResourceData = (PROCESSOR_RESOURCE_DATA *)(*(UINTN *)DataInHob);
> +  }
> +
> +  return ResourceData;
> +}
> +
> +/**
> +  This function will get CPU count in the system.
> +
> +  @param[in] CpuMpData        Pointer to PEI CPU MP Data
> +
> +  @return  CPU count detected
> +**/
> +UINTN
> +CollectProcessorCount (
> +  IN CPU_MP_DATA  *CpuMpData
> +  )
> +{
> +  PROCESSOR_RESOURCE_DATA  *ProcessorResourceData;
> +
> +  ProcessorResourceData = NULL;
> +
> +  //
> +  // Set the default loop mode for APs.
> +  //
> +  CpuMpData->ApLoopMode = ApInRunLoop;
> +
> +  //
> +  // Beacuse LoongArch does not have SIPI now, the APIC ID must be
> obtained before
> +  // calling IPI to wake up the APs. If NULL is obtained, NODE0 Core0
> Mailbox0 is used
> +  // as the first broadcast method to wake up all APs, and all of APs will 
> read
> NODE0
> +  // Core0 Mailbox0 in an infinit loop.
> +  //
> +  ProcessorResourceData = GetProcessorResourceDataFromGuidedHob ();
> +
> +  if (ProcessorResourceData != NULL) {
> +    CpuMpData->ApLoopMode   = ApInHltLoop;
> +    CpuMpData->CpuCount     = ProcessorResourceData->CpuCount;
> +    CpuMpData->CpuInfoInHob = (UINTN)(ProcessorResourceData-
> >CpuInfoInHob);
> +  }
> +
> +  //
> +  // Send 1st broadcast IPI to APs to wakeup APs
> +  //
> +  CpuMpData->InitFlag = ApInitConfig;
> +  WakeUpAP (CpuMpData, TRUE, 0, NULL, NULL, FALSE);
> +  CpuMpData->InitFlag = ApInitDone;
> +
> +  //
> +  // When InitFlag == ApInitConfig, WakeUpAP () guarantees all APs are
> checked in.
> +  // FinishedCount is the number of check-in APs.
> +  //
> +  CpuMpData->CpuCount = CpuMpData->FinishedCount + 1;
> +  ASSERT (CpuMpData->CpuCount <= PcdGet32
> (PcdCpuMaxLogicalProcessorNumber));
> +
> +  //
> +  // Wait for all APs finished the initialization
> +  //
> +  while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) {
> +    CpuPause ();
> +  }
> +
> +  //
> +  // Sort BSP/Aps by CPU APIC ID in ascending order
> +  //
> +  SortApicId (CpuMpData);
> +
> +  DEBUG ((DEBUG_INFO, "MpInitLib: Find %d processors in system.\n",
> CpuMpData->CpuCount));
> +
> +  return CpuMpData->CpuCount;
> +}
> +
> +/**
> +  Initialize CPU AP Data when AP is wakeup at the first time.
> +
> +  @param[in, out] CpuMpData        Pointer to PEI CPU MP Data
> +  @param[in]      ProcessorNumber  The handle number of processor
> +  @param[in]      BistData         Processor BIST data
> +
> +**/
> +VOID
> +InitializeApData (
> +  IN OUT CPU_MP_DATA  *CpuMpData,
> +  IN     UINTN        ProcessorNumber,
> +  IN     UINT32       BistData
> +  )
> +{
> +  CPU_INFO_IN_HOB  *CpuInfoInHob;
> +
> +  CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)(CpuMpData-
> >CpuInfoInHob);
> +
> +  CpuInfoInHob[ProcessorNumber].ApicId = GetApicId ();
> +  CpuInfoInHob[ProcessorNumber].Health = BistData;
> +
> +  CpuMpData->CpuData[ProcessorNumber].Waiting    = FALSE;
> +  CpuMpData->CpuData[ProcessorNumber].CpuHealthy = (BistData == 0) ?
> TRUE : FALSE;
> +
> +  InitializeSpinLock (&CpuMpData->CpuData[ProcessorNumber].ApLock);
> +  SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);
> +}
> +
> +/**
> +  Ap wake up function.
> +
> +  Ap will wait for scheduling here, and if the IPI or wake-up signal is 
> enabled,
> +  Ap will preform the corresponding functions.
> +
> +  @param[in] ApIndex          Number of current executing AP
> +  @param[in] ExchangeInfo     Pointer to the MP exchange info buffer
> +**/
> +VOID
> +EFIAPI
> +ApWakeupFunction (
> +  IN UINTN                 ApIndex,
> +  IN MP_CPU_EXCHANGE_INFO  *ExchangeInfo
> +  )
> +{
> +  CPU_MP_DATA       *CpuMpData;
> +  UINTN             ProcessorNumber;
> +  volatile UINT32   *ApStartupSignalBuffer;
> +  EFI_AP_PROCEDURE  Procedure;
> +  VOID              *Parameter;
> +
> +  CpuMpData = ExchangeInfo->CpuMpData;
> +
> +  while (TRUE) {
> +    if (CpuMpData->InitFlag == ApInitConfig) {
> +      ProcessorNumber = ApIndex;
> +      //
> +      // If the AP can running to here, then the BIST must be zero.
> +      //
> +      InitializeApData (CpuMpData, ProcessorNumber, 0);
> +      ApStartupSignalBuffer = CpuMpData-
> >CpuData[ProcessorNumber].StartupApSignal;
> +    } else {
> +      //
> +      // Execute AP function if AP is ready
> +      //
> +      GetProcessorNumber (CpuMpData, &ProcessorNumber);
> +
> +      //
> +      // Clear AP start-up signal when AP waken up
> +      //
> +      ApStartupSignalBuffer = CpuMpData-
> >CpuData[ProcessorNumber].StartupApSignal;
> +      InterlockedCompareExchange32 (
> +        (UINT32 *)ApStartupSignalBuffer,
> +        WAKEUP_AP_SIGNAL,
> +        0
> +        );
> +
> +      //
> +      // Invoke AP function here
> +      //
> +      if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) ==
> CpuStateReady) {
> +        Procedure = (EFI_AP_PROCEDURE)CpuMpData-
> >CpuData[ProcessorNumber].ApFunction;
> +        Parameter = (VOID *)CpuMpData-
> >CpuData[ProcessorNumber].ApFunctionArgument;
> +        if (Procedure != NULL) {
> +          SetApState (&CpuMpData->CpuData[ProcessorNumber],
> CpuStateBusy);
> +          Procedure (Parameter);
> +        }
> +
> +        SetApState (&CpuMpData->CpuData[ProcessorNumber],
> CpuStateFinished);
> +      }
> +    }
> +
> +    //
> +    // Updates the finished count
> +    //
> +    InterlockedIncrement ((UINT32 *)&CpuMpData->FinishedCount);
> +
> +    while (TRUE) {
> +      //
> +      // Clean per-core mail box registers.
> +      //
> +      IoCsrWrite64 (LOONGARCH_IOCSR_MBUF0, 0x0);
> +      IoCsrWrite64 (LOONGARCH_IOCSR_MBUF1, 0x0);
> +      IoCsrWrite64 (LOONGARCH_IOCSR_MBUF2, 0x0);
> +      IoCsrWrite64 (LOONGARCH_IOCSR_MBUF3, 0x0);
> +
> +      //
> +      // Enable IPI interrupt and global interrupt
> +      //
> +      EnableLocalInterrupts (BIT12);
> +      IoCsrWrite32 (LOONGARCH_IOCSR_IPI_EN, 0xFFFFFFFFU);
> +      EnableInterrupts ();
> +
> +      //
> +      // Ap entry HLT mode
> +      //
> +      CpuSleep ();
> +
> +      //
> +      // Disable global interrupts when wake up
> +      //
> +      DisableInterrupts ();
> +
> +      //
> +      // Update CpuMpData
> +      //
> +      if (CpuMpData != ExchangeInfo->CpuMpData) {
> +        CpuMpData = ExchangeInfo->CpuMpData;
> +        GetProcessorNumber (CpuMpData, &ProcessorNumber);
> +        ApStartupSignalBuffer = CpuMpData-
> >CpuData[ProcessorNumber].StartupApSignal;
> +      }
> +
> +      //
> +      // Break out of the loop if wake up signal is not NULL.
> +      //
> +      if (*ApStartupSignalBuffer == WAKEUP_AP_SIGNAL) {
> +        break;
> +      }
> +    }
> +  }
> +}
> +
> +/**
> +  Calculate timeout value and return the current performance counter value.
> +
> +  Calculate the number of performance counter ticks required for a timeout.
> +  If TimeoutInMicroseconds is 0, return value is also 0, which is recognized
> +  as infinity.
> +
> +  @param[in]  TimeoutInMicroseconds   Timeout value in microseconds.
> +  @param[out] CurrentTime             Returns the current value of the
> performance counter.
> +
> +  @return Expected time stamp counter for timeout.
> +          If TimeoutInMicroseconds is 0, return value is also 0, which is
> recognized
> +          as infinity.
> +
> +**/
> +UINT64
> +CalculateTimeout (
> +  IN  UINTN   TimeoutInMicroseconds,
> +  OUT UINT64  *CurrentTime
> +  )
> +{
> +  UINT64  TimeoutInSeconds;
> +  UINT64  TimestampCounterFreq;
> +
> +  //
> +  // Read the current value of the performance counter
> +  //
> +  *CurrentTime = GetPerformanceCounter ();
> +
> +  //
> +  // If TimeoutInMicroseconds is 0, return value is also 0, which is 
> recognized
> +  // as infinity.
> +  //
> +  if (TimeoutInMicroseconds == 0) {
> +    return 0;
> +  }
> +
> +  //
> +  // GetPerformanceCounterProperties () returns the timestamp counter's
> frequency
> +  // in Hz.
> +  //
> +  TimestampCounterFreq = GetPerformanceCounterProperties (NULL, NULL);
> +
> +  //
> +  // Check the potential overflow before calculate the number of ticks for 
> the
> timeout value.
> +  //
> +  if (DivU64x64Remainder (MAX_UINT64, TimeoutInMicroseconds, NULL) <
> TimestampCounterFreq) {
> +    //
> +    // Convert microseconds into seconds if direct multiplication overflows
> +    //
> +    TimeoutInSeconds = DivU64x32 (TimeoutInMicroseconds, 1000000);
> +    //
> +    // Assertion if the final tick count exceeds MAX_UINT64
> +    //
> +    ASSERT (DivU64x64Remainder (MAX_UINT64, TimeoutInSeconds,
> NULL) >= TimestampCounterFreq);
> +    return MultU64x64 (TimestampCounterFreq, TimeoutInSeconds);
> +  } else {
> +    //
> +    // No overflow case, multiply the return value with
> TimeoutInMicroseconds and then divide
> +    // it by 1,000,000, to get the number of ticks for the timeout value.
> +    //
> +    return DivU64x32 (
> +             MultU64x64 (
> +               TimestampCounterFreq,
> +               TimeoutInMicroseconds
> +               ),
> +             1000000
> +             );
> +  }
> +}
> +
> +/**
> +  Checks whether timeout expires.
> +
> +  Check whether the number of elapsed performance counter ticks required
> for
> +  a timeout condition has been reached.
> +  If Timeout is zero, which means infinity, return value is always FALSE.
> +
> +  @param[in, out]  PreviousTime   On input,  the value of the performance
> counter
> +                                  when it was last read.
> +                                  On output, the current value of the 
> performance
> +                                  counter
> +  @param[in]       TotalTime      The total amount of elapsed time in
> performance
> +                                  counter ticks.
> +  @param[in]       Timeout        The number of performance counter ticks
> required
> +                                  to reach a timeout condition.
> +
> +  @retval TRUE                    A timeout condition has been reached.
> +  @retval FALSE                   A timeout condition has not been reached.
> +
> +**/
> +BOOLEAN
> +CheckTimeout (
> +  IN OUT UINT64  *PreviousTime,
> +  IN     UINT64  *TotalTime,
> +  IN     UINT64  Timeout
> +  )
> +{
> +  UINT64  Start;
> +  UINT64  End;
> +  UINT64  CurrentTime;
> +  INT64   Delta;
> +  INT64   Cycle;
> +
> +  if (Timeout == 0) {
> +    return FALSE;
> +  }
> +
> +  GetPerformanceCounterProperties (&Start, &End);
> +  Cycle = End - Start;
> +  if (Cycle < 0) {
> +    Cycle = -Cycle;
> +  }
> +
> +  Cycle++;
> +  CurrentTime = GetPerformanceCounter ();
> +  Delta       = (INT64)(CurrentTime - *PreviousTime);
> +  if (Start > End) {
> +    Delta = -Delta;
> +  }
> +
> +  if (Delta < 0) {
> +    Delta += Cycle;
> +  }
> +
> +  *TotalTime   += Delta;
> +  *PreviousTime = CurrentTime;
> +  if (*TotalTime > Timeout) {
> +    return TRUE;
> +  }
> +
> +  return FALSE;
> +}
> +
> +/**
> +  Helper function that waits until the finished AP count reaches the 
> specified
> +  limit, or the specified timeout elapses (whichever comes first).
> +
> +  @param[in] CpuMpData        Pointer to CPU MP Data.
> +  @param[in] FinishedApLimit  The number of finished APs to wait for.
> +  @param[in] TimeLimit        The number of microseconds to wait for.
> +**/
> +VOID
> +TimedWaitForApFinish (
> +  IN CPU_MP_DATA  *CpuMpData,
> +  IN UINT32       FinishedApLimit,
> +  IN UINT32       TimeLimit
> +  )
> +{
> +  //
> +  // CalculateTimeout() and CheckTimeout() consider a TimeLimit of 0
> +  // "infinity", so check for (TimeLimit == 0) explicitly.
> +  //
> +  if (TimeLimit == 0) {
> +    return;
> +  }
> +
> +  CpuMpData->TotalTime    = 0;
> +  CpuMpData->ExpectedTime = CalculateTimeout (
> +                              TimeLimit,
> +                              &CpuMpData->CurrentTime
> +                              );
> +  while (CpuMpData->FinishedCount < FinishedApLimit &&
> +         !CheckTimeout (
> +            &CpuMpData->CurrentTime,
> +            &CpuMpData->TotalTime,
> +            CpuMpData->ExpectedTime
> +            ))
> +  {
> +    CpuPause ();
> +  }
> +
> +  if (CpuMpData->FinishedCount >= FinishedApLimit) {
> +    DEBUG ((
> +      DEBUG_VERBOSE,
> +      "%a: reached FinishedApLimit=%u in %Lu microseconds\n",
> +      __func__,
> +      FinishedApLimit,
> +      DivU64x64Remainder (
> +        MultU64x32 (CpuMpData->TotalTime, 1000000),
> +        GetPerformanceCounterProperties (NULL, NULL),
> +        NULL
> +        )
> +      ));
> +  }
> +}
> +
> +/**
> +  Wait for AP wakeup and write AP start-up signal till AP is waken up.
> +
> +  @param[in] ApStartupSignalBuffer  Pointer to AP wakeup signal
> +**/
> +VOID
> +WaitApWakeup (
> +  IN volatile UINT32  *ApStartupSignalBuffer
> +  )
> +{
> +  //
> +  // If AP is waken up, StartupApSignal should be cleared.
> +  // Otherwise, write StartupApSignal again till AP waken up.
> +  //
> +  while (InterlockedCompareExchange32 (
> +           (UINT32 *)ApStartupSignalBuffer,
> +           WAKEUP_AP_SIGNAL,
> +           WAKEUP_AP_SIGNAL
> +           ) != 0)
> +  {
> +    CpuPause ();
> +  }
> +}
> +
> +/**
> +  This function will fill the exchange info structure.
> +
> +  @param[in] CpuMpData          Pointer to CPU MP Data
> +
> +**/
> +VOID
> +FillExchangeInfoData (
> +  IN CPU_MP_DATA  *CpuMpData
> +  )
> +{
> +  volatile MP_CPU_EXCHANGE_INFO  *ExchangeInfo;
> +
> +  if (!CpuMpData->MpCpuExchangeInfo) {
> +    CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO
> *)AllocatePool (sizeof (MP_CPU_EXCHANGE_INFO));
> +  }
> +
> +  ExchangeInfo            = CpuMpData->MpCpuExchangeInfo;
> +  ExchangeInfo->CpuMpData = CpuMpData;
> +}
> +
> +/**
> +  This function will be called by BSP to wakeup AP.
> +
> +  @param[in] CpuMpData          Pointer to CPU MP Data
> +  @param[in] Broadcast          TRUE:  Send broadcast IPI to all APs
> +                                FALSE: Send IPI to AP by ApicId
> +  @param[in] ProcessorNumber    The handle number of specified processor
> +  @param[in] Procedure          The function to be invoked by AP
> +  @param[in] ProcedureArgument  The argument to be passed into AP
> function
> +  @param[in] WakeUpDisabledAps  Whether need to wake up disabled APs in
> broadcast mode. Currently not used on LoongArch.
> +**/
> +VOID
> +WakeUpAP (
> +  IN CPU_MP_DATA       *CpuMpData,
> +  IN BOOLEAN           Broadcast,
> +  IN UINTN             ProcessorNumber,
> +  IN EFI_AP_PROCEDURE  Procedure               OPTIONAL,
> +  IN VOID              *ProcedureArgument      OPTIONAL,
> +  IN BOOLEAN           WakeUpDisabledAps
> +  )
> +{
> +  volatile MP_CPU_EXCHANGE_INFO  *ExchangeInfo;
> +  UINTN                          Index;
> +  CPU_AP_DATA                    *CpuData;
> +  CPU_INFO_IN_HOB                *CpuInfoInHob;
> +
> +  CpuMpData->FinishedCount = 0;
> +
> +  CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData-
> >CpuInfoInHob;
> +
> +  if (CpuMpData->InitFlag != ApInitDone) {
> +    FillExchangeInfoData (CpuMpData);
> +  }
> +
> +  ExchangeInfo = CpuMpData->MpCpuExchangeInfo;
> +  //
> +  // If InitFlag is ApInitConfig, broadcasts all APs to initize themselves.
> +  //
> +  if (CpuMpData->InitFlag == ApInitConfig) {
> +    DEBUG ((DEBUG_INFO, "%a: func 0x%llx, ExchangeInfo 0x%llx\n",
> __func__, ApWakeupFunction, (UINTN)ExchangeInfo));
> +    if (CpuMpData->ApLoopMode == ApInHltLoop) {
> +      for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
> +        if (Index != CpuMpData->BspNumber) {
> +          IoCsrWrite64 (
> +            LOONGARCH_IOCSR_MBUF_SEND,
> +            (IOCSR_MBUF_SEND_BLOCKING |
> +             (IOCSR_MBUF_SEND_BOX_HI (0x3) <<
> IOCSR_MBUF_SEND_BOX_SHIFT) |
> +             (CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) |
> +             ((UINTN)(ExchangeInfo) & IOCSR_MBUF_SEND_H32_MASK))
> +            );
> +          IoCsrWrite64 (
> +            LOONGARCH_IOCSR_MBUF_SEND,
> +            (IOCSR_MBUF_SEND_BLOCKING |
> +             (IOCSR_MBUF_SEND_BOX_LO (0x3) <<
> IOCSR_MBUF_SEND_BOX_SHIFT) |
> +             (CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) |
> +             ((UINTN)ExchangeInfo) << IOCSR_MBUF_SEND_BUF_SHIFT)
> +            );
> +
> +          IoCsrWrite64 (
> +            LOONGARCH_IOCSR_MBUF_SEND,
> +            (IOCSR_MBUF_SEND_BLOCKING |
> +             (IOCSR_MBUF_SEND_BOX_HI (0x0) <<
> IOCSR_MBUF_SEND_BOX_SHIFT) |
> +             (CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) |
> +             ((UINTN)(ApWakeupFunction) & IOCSR_MBUF_SEND_H32_MASK))
> +            );
> +          IoCsrWrite64 (
> +            LOONGARCH_IOCSR_MBUF_SEND,
> +            (IOCSR_MBUF_SEND_BLOCKING |
> +             (IOCSR_MBUF_SEND_BOX_LO (0x0) <<
> IOCSR_MBUF_SEND_BOX_SHIFT) |
> +             (CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) |
> +             ((UINTN)ApWakeupFunction) << IOCSR_MBUF_SEND_BUF_SHIFT)
> +            );
> +
> +          //
> +          // Send IPI 4 interrupt to wake up APs.
> +          //
> +          IoCsrWrite64 (
> +            LOONGARCH_IOCSR_IPI_SEND,
> +            (IOCSR_MBUF_SEND_BLOCKING |
> +             (CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) |
> +             0x2 // Bit 2
> +            )
> +            );
> +        }
> +      }
> +    } else {
> +      IoCsrWrite64 (LOONGARCH_IOCSR_MBUF3, (UINTN)ExchangeInfo);
> +      IoCsrWrite64 (LOONGARCH_IOCSR_MBUF0,
> (UINTN)ApWakeupFunction);
> +    }
> +
> +    TimedWaitForApFinish (
> +      CpuMpData,
> +      PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1,
> +      PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds)
> +      );
> +  } else {
> +    if (Broadcast) {
> +      for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
> +        if (Index != CpuMpData->BspNumber) {
> +          CpuData = &CpuMpData->CpuData[Index];
> +          if ((GetApState (CpuData) == CpuStateDisabled)
> && !WakeUpDisabledAps) {
> +            continue;
> +          }
> +
> +          CpuData->ApFunction         = (UINTN)Procedure;
> +          CpuData->ApFunctionArgument = (UINTN)ProcedureArgument;
> +          SetApState (CpuData, CpuStateReady);
> +          *(UINT32 *)CpuData->StartupApSignal = WAKEUP_AP_SIGNAL;
> +
> +          //
> +          // Send IPI 4 interrupt to wake up APs.
> +          //
> +          IoCsrWrite64 (
> +            LOONGARCH_IOCSR_IPI_SEND,
> +            (IOCSR_MBUF_SEND_BLOCKING |
> +             (CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) |
> +             0x2 // Bit 2
> +            )
> +            );
> +        }
> +      }
> +
> +      //
> +      // Wait all APs waken up.
> +      //
> +      for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
> +        CpuData = &CpuMpData->CpuData[Index];
> +        if (Index != CpuMpData->BspNumber) {
> +          WaitApWakeup (CpuData->StartupApSignal);
> +        }
> +      }
> +    } else {
> +      CpuData                     = &CpuMpData->CpuData[ProcessorNumber];
> +      CpuData->ApFunction         = (UINTN)Procedure;
> +      CpuData->ApFunctionArgument = (UINTN)ProcedureArgument;
> +      SetApState (CpuData, CpuStateReady);
> +      //
> +      // Wakeup specified AP
> +      //
> +      *(UINT32 *)CpuData->StartupApSignal = WAKEUP_AP_SIGNAL;
> +
> +      //
> +      // Send IPI 4 interrupt to wake up APs.
> +      //
> +      IoCsrWrite64 (
> +        LOONGARCH_IOCSR_IPI_SEND,
> +        (IOCSR_MBUF_SEND_BLOCKING |
> +         (CpuInfoInHob[ProcessorNumber].ApicId <<
> IOCSR_MBUF_SEND_CPU_SHIFT) |
> +         0x2 // Bit 2
> +        )
> +        );
> +
> +      //
> +      // Wait specified AP waken up
> +      //
> +      WaitApWakeup (CpuData->StartupApSignal);
> +    }
> +  }
> +}
> +
> +/**
> +  Searches for the next waiting AP.
> +
> +  Search for the next AP that is put in waiting state by single-threaded
> StartupAllAPs().
> +
> +  @param[out]  NextProcessorNumber  Pointer to the processor number of
> the next waiting AP.
> +
> +  @retval EFI_SUCCESS          The next waiting AP has been found.
> +  @retval EFI_NOT_FOUND        No waiting AP exists.
> +
> +**/
> +EFI_STATUS
> +GetNextWaitingProcessorNumber (
> +  OUT UINTN  *NextProcessorNumber
> +  )
> +{
> +  UINTN        ProcessorNumber;
> +  CPU_MP_DATA  *CpuMpData;
> +
> +  CpuMpData = GetCpuMpData ();
> +
> +  for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount;
> ProcessorNumber++) {
> +    if (CpuMpData->CpuData[ProcessorNumber].Waiting) {
> +      *NextProcessorNumber = ProcessorNumber;
> +      return EFI_SUCCESS;
> +    }
> +  }
> +
> +  return EFI_NOT_FOUND;
> +}
> +
> +/** Checks status of specified AP.
> +
> +  This function checks whether the specified AP has finished the task 
> assigned
> +  by StartupThisAP(), and whether timeout expires.
> +
> +  @param[in]  ProcessorNumber       The handle number of processor.
> +
> +  @retval EFI_SUCCESS           Specified AP has finished task assigned by
> StartupThisAPs().
> +  @retval EFI_TIMEOUT           The timeout expires.
> +  @retval EFI_NOT_READY         Specified AP has not finished task and 
> timeout
> has not expired.
> +**/
> +EFI_STATUS
> +CheckThisAP (
> +  IN UINTN  ProcessorNumber
> +  )
> +{
> +  CPU_MP_DATA  *CpuMpData;
> +  CPU_AP_DATA  *CpuData;
> +
> +  CpuMpData = GetCpuMpData ();
> +  CpuData   = &CpuMpData->CpuData[ProcessorNumber];
> +
> +  //
> +  // If the AP finishes for StartupThisAP(), return EFI_SUCCESS.
> +  //
> +  if (GetApState (CpuData) == CpuStateFinished) {
> +    if (CpuData->Finished != NULL) {
> +      *(CpuData->Finished) = TRUE;
> +    }
> +
> +    SetApState (CpuData, CpuStateIdle);
> +    return EFI_SUCCESS;
> +  } else {
> +    //
> +    // If timeout expires for StartupThisAP(), report timeout.
> +    //
> +    if (CheckTimeout (&CpuData->CurrentTime, &CpuData->TotalTime,
> CpuData->ExpectedTime)) {
> +      if (CpuData->Finished != NULL) {
> +        *(CpuData->Finished) = FALSE;
> +      }
> +
> +      return EFI_TIMEOUT;
> +    }
> +  }
> +
> +  return EFI_NOT_READY;
> +}
> +
> +/**
> +  Checks status of all APs.
> +
> +  This function checks whether all APs have finished task assigned by
> StartupAllAPs(),
> +  and whether timeout expires.
> +
> +  @retval EFI_SUCCESS           All APs have finished task assigned by
> StartupAllAPs().
> +  @retval EFI_TIMEOUT           The timeout expires.
> +  @retval EFI_NOT_READY         APs have not finished task and timeout has
> not expired.
> +**/
> +EFI_STATUS
> +CheckAllAPs (
> +  VOID
> +  )
> +{
> +  UINTN        ProcessorNumber;
> +  UINTN        NextProcessorNumber;
> +  EFI_STATUS   Status;
> +  CPU_MP_DATA  *CpuMpData;
> +  CPU_AP_DATA  *CpuData;
> +
> +  CpuMpData = GetCpuMpData ();
> +
> +  NextProcessorNumber = 0;
> +
> +  //
> +  // Go through all APs that are responsible for the StartupAllAPs().
> +  //
> +  for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount;
> ProcessorNumber++) {
> +    if (!CpuMpData->CpuData[ProcessorNumber].Waiting) {
> +      continue;
> +    }
> +
> +    CpuData = &CpuMpData->CpuData[ProcessorNumber];
> +    //
> +    // Check the CPU state of AP. If it is CpuStateIdle, then the AP has 
> finished
> its task.
> +    // Only BSP and corresponding AP access this unit of CPU Data. This means
> the AP will not modify the
> +    // value of state after setting the it to CpuStateIdle, so BSP can 
> safely make
> use of its value.
> +    //
> +    if (GetApState (CpuData) == CpuStateFinished) {
> +      CpuMpData->RunningCount--;
> +      CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE;
> +      SetApState (CpuData, CpuStateIdle);
> +
> +      //
> +      // If in Single Thread mode, then search for the next waiting AP for
> execution.
> +      //
> +      if (CpuMpData->SingleThread) {
> +        Status = GetNextWaitingProcessorNumber (&NextProcessorNumber);
> +
> +        if (!EFI_ERROR (Status)) {
> +          WakeUpAP (
> +            CpuMpData,
> +            FALSE,
> +            (UINT32)NextProcessorNumber,
> +            CpuMpData->Procedure,
> +            CpuMpData->ProcArguments,
> +            TRUE
> +            );
> +        }
> +      }
> +    }
> +  }
> +
> +  //
> +  // If all APs finish, return EFI_SUCCESS.
> +  //
> +  if (CpuMpData->RunningCount == 0) {
> +    return EFI_SUCCESS;
> +  }
> +
> +  //
> +  // If timeout expires, report timeout.
> +  //
> +  if (CheckTimeout (
> +        &CpuMpData->CurrentTime,
> +        &CpuMpData->TotalTime,
> +        CpuMpData->ExpectedTime
> +        )
> +      )
> +  {
> +    return EFI_TIMEOUT;
> +  }
> +
> +  return EFI_NOT_READY;
> +}
> +
> +/**
> +  Worker function to execute a caller provided function on all enabled APs.
> +
> +  @param[in]  Procedure               A pointer to the function to be run on
> +                                      enabled APs of the system.
> +  @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]  ExcludeBsp              Whether let BSP also trig this task.
> +  @param[in]  WaitEvent               The event created by the caller with
> CreateEvent()
> +                                      service.
> +  @param[in]  TimeoutInMicroseconds   Indicates the time limit in
> microseconds for
> +                                      APs to return from Procedure, either 
> for
> +                                      blocking or non-blocking mode.
> +  @param[in]  ProcedureArgument       The parameter passed into Procedure
> for
> +                                      all APs.
> +  @param[out] FailedCpuList           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.
> +
> +  @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 others                  Failed to Startup all APs.
> +
> +**/
> +EFI_STATUS
> +StartupAllCPUsWorker (
> +  IN  EFI_AP_PROCEDURE  Procedure,
> +  IN  BOOLEAN           SingleThread,
> +  IN  BOOLEAN           ExcludeBsp,
> +  IN  EFI_EVENT         WaitEvent               OPTIONAL,
> +  IN  UINTN             TimeoutInMicroseconds,
> +  IN  VOID              *ProcedureArgument      OPTIONAL,
> +  OUT UINTN             **FailedCpuList         OPTIONAL
> +  )
> +{
> +  EFI_STATUS   Status;
> +  CPU_MP_DATA  *CpuMpData;
> +  UINTN        ProcessorCount;
> +  UINTN        ProcessorNumber;
> +  UINTN        CallerNumber;
> +  CPU_AP_DATA  *CpuData;
> +  BOOLEAN      HasEnabledAp;
> +  CPU_STATE    ApState;
> +
> +  CpuMpData = GetCpuMpData ();
> +
> +  if (FailedCpuList != NULL) {
> +    *FailedCpuList = NULL;
> +  }
> +
> +  if ((CpuMpData->CpuCount == 1) && ExcludeBsp) {
> +    return EFI_NOT_STARTED;
> +  }
> +
> +  if (Procedure == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // Check whether caller processor is BSP
> +  //
> +  MpInitLibWhoAmI (&CallerNumber);
> +  if (CallerNumber != CpuMpData->BspNumber) {
> +    return EFI_DEVICE_ERROR;
> +  }
> +
> +  //
> +  // Update AP state
> +  //
> +  CheckAndUpdateApsStatus ();
> +
> +  ProcessorCount = CpuMpData->CpuCount;
> +  HasEnabledAp   = FALSE;
> +  //
> +  // Check whether all enabled APs are idle.
> +  // If any enabled AP is not idle, return EFI_NOT_READY.
> +  //
> +  for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount;
> ProcessorNumber++) {
> +    CpuData = &CpuMpData->CpuData[ProcessorNumber];
> +    if (ProcessorNumber != CpuMpData->BspNumber) {
> +      ApState = GetApState (CpuData);
> +      if (ApState != CpuStateDisabled) {
> +        HasEnabledAp = TRUE;
> +        if (ApState != CpuStateIdle) {
> +          //
> +          // If any enabled APs are busy, return EFI_NOT_READY.
> +          //
> +          return EFI_NOT_READY;
> +        }
> +      }
> +    }
> +  }
> +
> +  if (!HasEnabledAp && ExcludeBsp) {
> +    //
> +    // If no enabled AP exists and not include Bsp to do the procedure, 
> return
> EFI_NOT_STARTED.
> +    //
> +    return EFI_NOT_STARTED;
> +  }
> +
> +  CpuMpData->RunningCount = 0;
> +  for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount;
> ProcessorNumber++) {
> +    CpuData          = &CpuMpData->CpuData[ProcessorNumber];
> +    CpuData->Waiting = FALSE;
> +    if (ProcessorNumber != CpuMpData->BspNumber) {
> +      if (CpuData->State == CpuStateIdle) {
> +        //
> +        // Mark this processor as responsible for current calling.
> +        //
> +        CpuData->Waiting = TRUE;
> +        CpuMpData->RunningCount++;
> +      }
> +    }
> +  }
> +
> +  CpuMpData->Procedure     = Procedure;
> +  CpuMpData->ProcArguments = ProcedureArgument;
> +  CpuMpData->SingleThread  = SingleThread;
> +  CpuMpData->FinishedCount = 0;
> +  CpuMpData->ExpectedTime  = CalculateTimeout (
> +                               TimeoutInMicroseconds,
> +                               &CpuMpData->CurrentTime
> +                               );
> +  CpuMpData->TotalTime = 0;
> +  CpuMpData->WaitEvent = WaitEvent;
> +
> +  if (!SingleThread) {
> +    WakeUpAP (CpuMpData, TRUE, 0, Procedure, ProcedureArgument,
> FALSE);
> +  } else {
> +    for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount;
> ProcessorNumber++) {
> +      if (ProcessorNumber == CallerNumber) {
> +        continue;
> +      }
> +
> +      if (CpuMpData->CpuData[ProcessorNumber].Waiting) {
> +        WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure,
> ProcedureArgument, TRUE);
> +        break;
> +      }
> +    }
> +  }
> +
> +  if (!ExcludeBsp) {
> +    //
> +    // Start BSP.
> +    //
> +    Procedure (ProcedureArgument);
> +  }
> +
> +  Status = EFI_SUCCESS;
> +  if (WaitEvent == NULL) {
> +    do {
> +      Status = CheckAllAPs ();
> +    } while (Status == EFI_NOT_READY);
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  Worker function to let the caller get one enabled AP to execute a caller-
> provided
> +  function.
> +
> +  @param[in]  Procedure               A pointer to the function to be run on
> +                                      enabled APs of the system.
> +  @param[in]  ProcessorNumber         The handle number of the AP.
> +  @param[in]  WaitEvent               The event created by the caller with
> CreateEvent()
> +                                      service.
> +  @param[in]  TimeoutInMicroseconds   Indicates the time limit in
> microseconds for
> +                                      APs to return from Procedure, either 
> for
> +                                      blocking or non-blocking mode.
> +  @param[in]  ProcedureArgument       The parameter passed into Procedure
> for
> +                                      all APs.
> +  @param[out] Finished                If AP returns from Procedure before the
> +                                      timeout expires, its content is set to 
> TRUE.
> +                                      Otherwise, the value is set to FALSE.
> +
> +  @retval EFI_SUCCESS             In blocking mode, specified AP finished 
> before
> +                                  the timeout expires.
> +  @retval others                  Failed to Startup AP.
> +
> +**/
> +EFI_STATUS
> +StartupThisAPWorker (
> +  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;
> +  CPU_MP_DATA  *CpuMpData;
> +  CPU_AP_DATA  *CpuData;
> +  UINTN        CallerNumber;
> +
> +  CpuMpData = GetCpuMpData ();
> +
> +  if (Finished != NULL) {
> +    *Finished = FALSE;
> +  }
> +
> +  //
> +  // Check whether caller processor is BSP
> +  //
> +  MpInitLibWhoAmI (&CallerNumber);
> +  if (CallerNumber != CpuMpData->BspNumber) {
> +    return EFI_DEVICE_ERROR;
> +  }
> +
> +  //
> +  // Check whether processor with the handle specified by ProcessorNumber
> exists
> +  //
> +  if (ProcessorNumber >= CpuMpData->CpuCount) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  //
> +  // Check whether specified processor is BSP
> +  //
> +  if (ProcessorNumber == CpuMpData->BspNumber) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // Check parameter Procedure
> +  //
> +  if (Procedure == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // Update AP state
> +  //
> +  CheckAndUpdateApsStatus ();
> +
> +  //
> +  // Check whether specified AP is disabled
> +  //
> +  if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) ==
> CpuStateDisabled) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  CpuData               = &CpuMpData->CpuData[ProcessorNumber];
> +  CpuData->WaitEvent    = WaitEvent;
> +  CpuData->Finished     = Finished;
> +  CpuData->ExpectedTime = CalculateTimeout (TimeoutInMicroseconds,
> &CpuData->CurrentTime);
> +  CpuData->TotalTime    = 0;
> +
> +  WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure,
> ProcedureArgument, FALSE);
> +
> +  //
> +  // If WaitEvent is NULL, execute in blocking mode.
> +  // BSP checks AP's state until it finishes or TimeoutInMicrosecsond 
> expires.
> +  //
> +  Status = EFI_SUCCESS;
> +  if (WaitEvent == NULL) {
> +    do {
> +      Status = CheckThisAP (ProcessorNumber);
> +    } while (Status == EFI_NOT_READY);
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  This service executes a caller provided function on all enabled CPUs.
> +
> +  @param[in]  Procedure               A pointer to the function to be run on
> +                                      enabled APs of the system. See type
> +                                      EFI_AP_PROCEDURE.
> +  @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. TimeoutInMicroseconds is 
> ignored
> +                                      for BSP.
> +  @param[in]  ProcedureArgument       The parameter passed into Procedure
> for
> +                                      all APs.
> +
> +  @retval EFI_SUCCESS             In blocking mode, all CPUs have finished 
> before
> +                                  the timeout expired.
> +  @retval EFI_SUCCESS             In non-blocking mode, function has been
> dispatched
> +                                  to all enabled CPUs.
> +  @retval EFI_DEVICE_ERROR        Caller processor is AP.
> +  @retval EFI_NOT_READY           Any enabled APs are busy.
> +  @retval EFI_NOT_READY           MP Initialize Library is not initialized.
> +  @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
> +MpInitLibStartupAllCPUs (
> +  IN  EFI_AP_PROCEDURE  Procedure,
> +  IN  UINTN             TimeoutInMicroseconds,
> +  IN  VOID              *ProcedureArgument      OPTIONAL
> +  )
> +{
> +  return StartupAllCPUsWorker (
> +           Procedure,
> +           TRUE,
> +           FALSE,
> +           NULL,
> +           TimeoutInMicroseconds,
> +           ProcedureArgument,
> +           NULL
> +           );
> +}
> +
> +/**
> +  MP Initialize Library initialization.
> +
> +  This service will allocate AP reset vector and wakeup all APs to do APs
> +  initialization.
> +
> +  This service must be invoked before all other MP Initialize Library
> +  service are invoked.
> +
> +  @retval  EFI_SUCCESS           MP initialization succeeds.
> +  @retval  Others                MP initialization fails.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +MpInitLibInitialize (
> +  VOID
> +  )
> +{
> +  CPU_MP_DATA      *OldCpuMpData;
> +  CPU_INFO_IN_HOB  *CpuInfoInHob;
> +  UINT32           MaxLogicalProcessorNumber;
> +  UINTN            BufferSize;
> +  UINTN            MonitorBufferSize;
> +  VOID             *MpBuffer;
> +  CPU_MP_DATA      *CpuMpData;
> +  UINTN            Index;
> +
> +  OldCpuMpData = GetCpuMpDataFromGuidedHob ();
> +  if (OldCpuMpData == NULL) {
> +    MaxLogicalProcessorNumber = PcdGet32
> (PcdCpuMaxLogicalProcessorNumber);
> +  } else {
> +    MaxLogicalProcessorNumber = OldCpuMpData->CpuCount;
> +  }
> +
> +  ASSERT (MaxLogicalProcessorNumber != 0);
> +
> +  MonitorBufferSize = sizeof (WAKEUP_AP_SIGNAL) *
> MaxLogicalProcessorNumber;
> +
> +  BufferSize  = 0;
> +  BufferSize += MonitorBufferSize;
> +  BufferSize += sizeof (CPU_MP_DATA);
> +  BufferSize += (sizeof (CPU_AP_DATA) + sizeof (CPU_INFO_IN_HOB))*
> MaxLogicalProcessorNumber;
> +  MpBuffer    = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize));
> +  ASSERT (MpBuffer != NULL);
> +  ZeroMem (MpBuffer, BufferSize);
> +
> +  CpuMpData = (CPU_MP_DATA *)MpBuffer;
> +
> +  CpuMpData->CpuCount     = 1;
> +  CpuMpData->BspNumber    = 0;
> +  CpuMpData->CpuData      = (CPU_AP_DATA *)(CpuMpData + 1);
> +  CpuMpData->CpuInfoInHob = (UINT64)(UINTN)(CpuMpData->CpuData +
> MaxLogicalProcessorNumber);
> +
> +  InitializeSpinLock (&CpuMpData->MpLock);
> +
> +  //
> +  // Set BSP basic information
> +  //
> +  InitializeApData (CpuMpData, 0, 0);
> +
> +  //
> +  // Set up APs wakeup signal buffer
> +  //
> +  for (Index = 0; Index < MaxLogicalProcessorNumber; Index++) {
> +    CpuMpData->CpuData[Index].StartupApSignal =
> +      (UINT32 *)((MpBuffer + BufferSize - MonitorBufferSize) + (sizeof
> (WAKEUP_AP_SIGNAL) * Index));
> +  }
> +
> +  if (OldCpuMpData == NULL) {
> +    if (MaxLogicalProcessorNumber > 1) {
> +      //
> +      // Wakeup all APs and calculate the processor count in system
> +      //
> +      CollectProcessorCount (CpuMpData);
> +    }
> +  } else {
> +    //
> +    // APs have been wakeup before, just get the CPU Information
> +    // from HOB
> +    //
> +    CpuMpData->CpuCount          = OldCpuMpData->CpuCount;
> +    CpuMpData->BspNumber         = OldCpuMpData->BspNumber;
> +    CpuMpData->CpuInfoInHob      = OldCpuMpData->CpuInfoInHob;
> +    CpuMpData->MpCpuExchangeInfo = OldCpuMpData-
> >MpCpuExchangeInfo;
> +
> +    CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData-
> >CpuInfoInHob;
> +    for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
> +      InitializeSpinLock (&CpuMpData->CpuData[Index].ApLock);
> +      CpuMpData->CpuData[Index].CpuHealthy = (CpuInfoInHob[Index].Health
> == 0) ? TRUE : FALSE;
> +    }
> +
> +    if (CpuMpData->CpuCount > 1) {
> +      //
> +      // Only needs to use this flag for DXE phase to update the wake up
> +      // buffer. Wakeup buffer allocated in PEI phase is no longer valid
> +      // in DXE.
> +      //
> +      CpuMpData->InitFlag = ApInitReconfig;
> +      WakeUpAP (CpuMpData, TRUE, 0, NULL, NULL, TRUE);
> +
> +      //
> +      // Wait for all APs finished initialization
> +      //
> +      while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) {
> +        CpuPause ();
> +      }
> +
> +      CpuMpData->InitFlag = ApInitDone;
> +    }
> +
> +    if (MaxLogicalProcessorNumber > 1) {
> +      for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
> +        SetApState (&CpuMpData->CpuData[Index], CpuStateIdle);
> +      }
> +    }
> +  }
> +
> +  //
> +  // Initialize global data for MP support
> +  //
> +  InitMpGlobalData (CpuMpData);
> +
> +  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]  ProcessorNumber       The handle number of processor.
> +  @param[out] ProcessorInfoBuffer   A pointer to the buffer where
> information for
> +                                    the requested processor is deposited.
> +  @param[out]  HealthData            Return processor health data.
> +
> +  @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.
> +  @retval EFI_NOT_READY           MP Initialize Library is not initialized.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +MpInitLibGetProcessorInfo (
> +  IN  UINTN                      ProcessorNumber,
> +  OUT EFI_PROCESSOR_INFORMATION  *ProcessorInfoBuffer,
> +  OUT EFI_HEALTH_FLAGS           *HealthData  OPTIONAL
> +  )
> +{
> +  CPU_MP_DATA      *CpuMpData;
> +  UINTN            CallerNumber;
> +  CPU_INFO_IN_HOB  *CpuInfoInHob;
> +
> +  CpuMpData    = GetCpuMpData ();
> +  CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData-
> >CpuInfoInHob;
> +
> +  //
> +  // Check whether caller processor is BSP
> +  //
> +  MpInitLibWhoAmI (&CallerNumber);
> +  if (CallerNumber != CpuMpData->BspNumber) {
> +    return EFI_DEVICE_ERROR;
> +  }
> +
> +  if (ProcessorInfoBuffer == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (ProcessorNumber >= CpuMpData->CpuCount) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  ProcessorInfoBuffer->ProcessorId =
> (UINT64)CpuInfoInHob[ProcessorNumber].ApicId;
> +  ProcessorInfoBuffer->StatusFlag  = 0;
> +  if (ProcessorNumber == CpuMpData->BspNumber) {
> +    ProcessorInfoBuffer->StatusFlag |= PROCESSOR_AS_BSP_BIT;
> +  }
> +
> +  if (CpuMpData->CpuData[ProcessorNumber].CpuHealthy) {
> +    ProcessorInfoBuffer->StatusFlag |= PROCESSOR_HEALTH_STATUS_BIT;
> +  }
> +
> +  if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) ==
> CpuStateDisabled) {
> +    ProcessorInfoBuffer->StatusFlag &= ~PROCESSOR_ENABLED_BIT;
> +  } else {
> +    ProcessorInfoBuffer->StatusFlag |= PROCESSOR_ENABLED_BIT;
> +  }
> +
> +  if (HealthData != NULL) {
> +    HealthData->Uint32 = CpuInfoInHob[ProcessorNumber].Health;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This return the handle number for the calling processor.  This service may 
> be
> +  called from the BSP and APs.
> +
> +  @param[out] ProcessorNumber  Pointer to the handle number of 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
> +                               MpInitLibGetNumberOfProcessors().
> +
> +  @retval EFI_SUCCESS             The current processor handle number was
> returned
> +                                  in ProcessorNumber.
> +  @retval EFI_INVALID_PARAMETER   ProcessorNumber is NULL.
> +  @retval EFI_NOT_READY           MP Initialize Library is not initialized.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +MpInitLibWhoAmI (
> +  OUT UINTN  *ProcessorNumber
> +  )
> +{
> +  CPU_MP_DATA  *CpuMpData;
> +
> +  if (ProcessorNumber == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  CpuMpData = GetCpuMpData ();
> +
> +  return GetProcessorNumber (CpuMpData, ProcessorNumber);
> +}
> +
> +/**
> +  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[out] NumberOfProcessors          Pointer to the total number of
> logical
> +                                          processors in the system, 
> including the BSP
> +                                          and disabled APs.
> +  @param[out] NumberOfEnabledProcessors   Pointer to the number of
> enabled logical
> +                                          processors that exist in system, 
> including
> +                                          the BSP.
> +
> +  @retval EFI_SUCCESS             The number of logical processors and 
> enabled
> +                                  logical processors was retrieved.
> +  @retval EFI_DEVICE_ERROR        The calling processor is an AP.
> +  @retval EFI_INVALID_PARAMETER   NumberOfProcessors is NULL and
> NumberOfEnabledProcessors
> +                                  is NULL.
> +  @retval EFI_NOT_READY           MP Initialize Library is not initialized.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +MpInitLibGetNumberOfProcessors (
> +  OUT UINTN  *NumberOfProcessors        OPTIONAL,
> +  OUT UINTN  *NumberOfEnabledProcessors OPTIONAL
> +  )
> +{
> +  CPU_MP_DATA  *CpuMpData;
> +  UINTN        CallerNumber;
> +  UINTN        ProcessorNumber;
> +  UINTN        EnabledProcessorNumber;
> +  UINTN        Index;
> +
> +  CpuMpData = GetCpuMpData ();
> +
> +  if ((NumberOfProcessors == NULL) && (NumberOfEnabledProcessors ==
> NULL)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  //
> +  // Check whether caller processor is BSP
> +  //
> +  MpInitLibWhoAmI (&CallerNumber);
> +  if (CallerNumber != CpuMpData->BspNumber) {
> +    return EFI_DEVICE_ERROR;
> +  }
> +
> +  ProcessorNumber        = CpuMpData->CpuCount;
> +  EnabledProcessorNumber = 0;
> +  for (Index = 0; Index < ProcessorNumber; Index++) {
> +    if (GetApState (&CpuMpData->CpuData[Index]) != CpuStateDisabled) {
> +      EnabledProcessorNumber++;
> +    }
> +  }
> +
> +  if (NumberOfProcessors != NULL) {
> +    *NumberOfProcessors = ProcessorNumber;
> +  }
> +
> +  if (NumberOfEnabledProcessors != NULL) {
> +    *NumberOfEnabledProcessors = EnabledProcessorNumber;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Get pointer to CPU MP Data structure from GUIDed HOB.
> +
> +  @return  The pointer to CPU MP Data structure.
> +**/
> +CPU_MP_DATA *
> +GetCpuMpDataFromGuidedHob (
> +  VOID
> +  )
> +{
> +  EFI_HOB_GUID_TYPE  *GuidHob;
> +  VOID               *DataInHob;
> +  CPU_MP_DATA        *CpuMpData;
> +
> +  CpuMpData = NULL;
> +  GuidHob   = GetFirstGuidHob (&mCpuInitMpLibHobGuid);
> +
> +  if (GuidHob != NULL) {
> +    DataInHob = GET_GUID_HOB_DATA (GuidHob);
> +    CpuMpData = (CPU_MP_DATA *)(*(UINTN *)DataInHob);
> +  }
> +
> +  return CpuMpData;
> +}
> diff --git a/UefiCpuPkg/Library/LoongArch64MpInitLib/MpLib.h
> b/UefiCpuPkg/Library/LoongArch64MpInitLib/MpLib.h
> new file mode 100644
> index 0000000000..ae5f63d267
> --- /dev/null
> +++ b/UefiCpuPkg/Library/LoongArch64MpInitLib/MpLib.h
> @@ -0,0 +1,361 @@
> +/** @file
> +  Common header file for LoongArch MP Initialize Library.
> +
> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights
> reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef MP_LIB_H_
> +#define MP_LIB_H_
> +
> +#include <PiPei.h>
> +#include <Library/PeiServicesLib.h>
> +
> +#include <Library/MpInitLib.h>
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/CpuLib.h>
> +#include <Library/SynchronizationLib.h>
> +#include <Library/TimerLib.h>
> +#include <Library/HobLib.h>
> +
> +#define WAKEUP_AP_SIGNAL  SIGNATURE_32 ('S', 'T', 'A', 'P')
> +
> +#define CPU_INIT_MP_LIB_HOB_GUID \
> +  { \
> +    0x58eb6a19, 0x3699, 0x4c68, { 0xa8, 0x36, 0xda, 0xcd, 0x8e, 0xdc, 0xad,
> 0x4a } \
> +  }
> +
> +#define PROCESSOR_RESOURCE_HOB_GUID \
> +  { \
> +    0xb855c7fe, 0xa758, 0x701f, { 0xa7, 0x30, 0x87, 0xf3, 0x9c, 0x03, 0x46,
> 0x7e } \
> +  }
> +
> +//
> +// AP loop state when APs are in idle state
> +// It's value is the same with PcdCpuApLoopMode
> +//
> +typedef enum {
> +  ApInHltLoop = 1,
> +  ApInRunLoop = 2
> +} AP_LOOP_MODE;
> +
> +//
> +// AP initialization state during APs wakeup
> +//
> +typedef enum {
> +  ApInitConfig   = 1,
> +  ApInitReconfig = 2,
> +  ApInitDone     = 3
> +} AP_INIT_STATE;
> +
> +//
> +// AP state
> +//
> +typedef enum {
> +  CpuStateIdle,
> +  CpuStateReady,
> +  CpuStateBusy,
> +  CpuStateFinished,
> +  CpuStateDisabled
> +} CPU_STATE;
> +
> +//
> +// AP related data
> +//
> +typedef struct {
> +  SPIN_LOCK             ApLock;
> +  volatile UINT32       *StartupApSignal;
> +  volatile UINTN        ApFunction;
> +  volatile UINTN        ApFunctionArgument;
> +  BOOLEAN               CpuHealthy;
> +  volatile CPU_STATE    State;
> +  BOOLEAN               Waiting;
> +  BOOLEAN               *Finished;
> +  UINT64                ExpectedTime;
> +  UINT64                CurrentTime;
> +  UINT64                TotalTime;
> +  EFI_EVENT             WaitEvent;
> +} CPU_AP_DATA;
> +
> +//
> +// Basic CPU information saved in Guided HOB.
> +// Because the contents will be shard between PEI and DXE,
> +// we need to make sure the each fields offset same in different
> +// architecture.
> +//
> +#pragma pack (1)
> +typedef struct {
> +  UINT32    ApicId;
> +  UINT32    Health;
> +} CPU_INFO_IN_HOB;
> +#pragma pack ()
> +
> +typedef struct _CPU_MP_DATA CPU_MP_DATA;
> +
> +#pragma pack(1)
> +
> +//
> +// MP CPU exchange information for AP reset code
> +// This structure is required to be packed because fixed field offsets
> +// into this structure are used in assembly code in this module
> +//
> +typedef struct {
> +  CPU_MP_DATA    *CpuMpData;
> +} MP_CPU_EXCHANGE_INFO;
> +
> +#pragma pack()
> +
> +typedef struct {
> +  SPIN_LOCK    Lock;
> +  UINT32       CpuCount;
> +  UINT64       CpuInfoInHob;
> +} PROCESSOR_RESOURCE_DATA;
> +
> +//
> +// CPU MP Data save in memory
> +//
> +struct _CPU_MP_DATA {
> +  UINT64                           CpuInfoInHob;
> +  UINT32                           CpuCount;
> +  UINT32                           BspNumber;
> +  //
> +  // The above fields data will be passed from PEI to DXE
> +  // Please make sure the fields offset same in the different
> +  // architecture.
> +  //
> +  SPIN_LOCK                        MpLock;
> +
> +  volatile UINT32                  FinishedCount;
> +  UINT32                           RunningCount;
> +  BOOLEAN                          SingleThread;
> +  EFI_AP_PROCEDURE                 Procedure;
> +  VOID                             *ProcArguments;
> +  BOOLEAN                          *Finished;
> +  UINT64                           ExpectedTime;
> +  UINT64                           CurrentTime;
> +  UINT64                           TotalTime;
> +  EFI_EVENT                        WaitEvent;
> +
> +  AP_INIT_STATE                    InitFlag;
> +  UINT8                            ApLoopMode;
> +  CPU_AP_DATA                      *CpuData;
> +  volatile MP_CPU_EXCHANGE_INFO    *MpCpuExchangeInfo;
> +};
> +
> +extern EFI_GUID  mCpuInitMpLibHobGuid;
> +extern EFI_GUID  mProcessorResourceHobGuid;
> +
> +/**
> +  Get the pointer to CPU MP Data structure.
> +
> +  @return  The pointer to CPU MP Data structure.
> +**/
> +CPU_MP_DATA *
> +GetCpuMpData (
> +  VOID
> +  );
> +
> +/**
> +  Save the pointer to CPU MP Data structure.
> +
> +  @param[in] CpuMpData  The pointer to CPU MP Data structure will be
> saved.
> +**/
> +VOID
> +SaveCpuMpData (
> +  IN CPU_MP_DATA  *CpuMpData
> +  );
> +
> +/**
> +  This function will be called by BSP to wakeup AP.
> +
> +  @param[in] CpuMpData          Pointer to CPU MP Data
> +  @param[in] Broadcast          TRUE:  Send broadcast IPI to all APs
> +                                FALSE: Send IPI to AP by ApicId
> +  @param[in] ProcessorNumber    The handle number of specified processor
> +  @param[in] Procedure          The function to be invoked by AP
> +  @param[in] ProcedureArgument  The argument to be passed into AP
> function
> +  @param[in] WakeUpDisabledAps  Whether need to wake up disabled APs in
> broadcast mode.
> +**/
> +VOID
> +WakeUpAP (
> +  IN CPU_MP_DATA       *CpuMpData,
> +  IN BOOLEAN           Broadcast,
> +  IN UINTN             ProcessorNumber,
> +  IN EFI_AP_PROCEDURE  Procedure               OPTIONAL,
> +  IN VOID              *ProcedureArgument      OPTIONAL,
> +  IN BOOLEAN           WakeUpDisabledAps
> +  );
> +
> +/**
> +  Initialize global data for MP support.
> +
> +  @param[in] CpuMpData  The pointer to CPU MP Data structure.
> +**/
> +VOID
> +InitMpGlobalData (
> +  IN CPU_MP_DATA  *CpuMpData
> +  );
> +
> +/**
> +  Worker function to execute a caller provided function on all enabled APs.
> +
> +  @param[in]  Procedure               A pointer to the function to be run on
> +                                      enabled APs of the system.
> +  @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.
> +  @param[in]  TimeoutInMicroseconds   Indicates the time limit in
> microseconds for
> +                                      APs to return from Procedure, either 
> for
> +                                      blocking or non-blocking mode.
> +  @param[in]  ProcedureArgument       The parameter passed into Procedure
> for
> +                                      all APs.
> +  @param[out] FailedCpuList           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.
> +
> +  @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 others                  Failed to Startup all APs.
> +
> +**/
> +EFI_STATUS
> +StartupAllCPUsWorker (
> +  IN  EFI_AP_PROCEDURE  Procedure,
> +  IN  BOOLEAN           SingleThread,
> +  IN  BOOLEAN           ExcludeBsp,
> +  IN  EFI_EVENT         WaitEvent               OPTIONAL,
> +  IN  UINTN             TimeoutInMicroseconds,
> +  IN  VOID              *ProcedureArgument      OPTIONAL,
> +  OUT UINTN             **FailedCpuList         OPTIONAL
> +  );
> +
> +/**
> +  Worker function to let the caller get one enabled AP to execute a caller-
> provided
> +  function.
> +
> +  @param[in]  Procedure               A pointer to the function to be run on
> +                                      enabled APs of the system.
> +  @param[in]  ProcessorNumber         The handle number of the AP.
> +  @param[in]  WaitEvent               The event created by the caller with
> CreateEvent()
> +                                      service.
> +  @param[in]  TimeoutInMicroseconds   Indicates the time limit in
> microseconds for
> +                                      APs to return from Procedure, either 
> for
> +                                      blocking or non-blocking mode.
> +  @param[in]  ProcedureArgument       The parameter passed into Procedure
> for
> +                                      all APs.
> +  @param[out] Finished                If AP returns from Procedure before the
> +                                      timeout expires, its content is set to 
> TRUE.
> +                                      Otherwise, the value is set to FALSE.
> +
> +  @retval EFI_SUCCESS             In blocking mode, specified AP finished 
> before
> +                                  the timeout expires.
> +  @retval others                  Failed to Startup AP.
> +
> +**/
> +EFI_STATUS
> +StartupThisAPWorker (
> +  IN  EFI_AP_PROCEDURE  Procedure,
> +  IN  UINTN             ProcessorNumber,
> +  IN  EFI_EVENT         WaitEvent               OPTIONAL,
> +  IN  UINTN             TimeoutInMicroseconds,
> +  IN  VOID              *ProcedureArgument      OPTIONAL,
> +  OUT BOOLEAN           *Finished               OPTIONAL
> +  );
> +
> +/**
> +  Worker function to let the caller enable or disable an AP from this point
> onward.
> +  This service may only be called from the BSP.
> +  This instance will be added in the future.
> +
> +  @param[in] ProcessorNumber   The handle number of AP.
> +  @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.
> +
> +  @retval EFI_SUCCESS          The specified AP was enabled or disabled
> successfully.
> +  @retval others               Failed to Enable/Disable AP.
> +
> +**/
> +EFI_STATUS
> +EnableDisableApWorker (
> +  IN  UINTN    ProcessorNumber,
> +  IN  BOOLEAN  EnableAP,
> +  IN  UINT32   *HealthFlag OPTIONAL
> +  );
> +
> +/**
> +  Get pointer to CPU MP Data structure from GUIDed HOB.
> +
> +  @return  The pointer to CPU MP Data structure.
> +**/
> +CPU_MP_DATA *
> +GetCpuMpDataFromGuidedHob (
> +  VOID
> +  );
> +
> +/** Checks status of specified AP.
> +
> +  This function checks whether the specified AP has finished the task 
> assigned
> +  by StartupThisAP(), and whether timeout expires.
> +
> +  @param[in]  ProcessorNumber       The handle number of processor.
> +
> +  @retval EFI_SUCCESS           Specified AP has finished task assigned by
> StartupThisAPs().
> +  @retval EFI_TIMEOUT           The timeout expires.
> +  @retval EFI_NOT_READY         Specified AP has not finished task and 
> timeout
> has not expired.
> +**/
> +EFI_STATUS
> +CheckThisAP (
> +  IN UINTN  ProcessorNumber
> +  );
> +
> +/**
> +  Checks status of all APs.
> +
> +  This function checks whether all APs have finished task assigned by
> StartupAllAPs(),
> +  and whether timeout expires.
> +
> +  @retval EFI_SUCCESS           All APs have finished task assigned by
> StartupAllAPs().
> +  @retval EFI_TIMEOUT           The timeout expires.
> +  @retval EFI_NOT_READY         APs have not finished task and timeout has
> not expired.
> +**/
> +EFI_STATUS
> +CheckAllAPs (
> +  VOID
> +  );
> +
> +/**
> +  Checks APs status and updates APs status if needed.
> +
> +**/
> +VOID
> +CheckAndUpdateApsStatus (
> +  VOID
> +  );
> +
> +/**
> +  Enable Debug Agent to support source debugging on AP function.
> +  This instance will added in the future.
> +
> +**/
> +VOID
> +EnableDebugAgent (
> +  VOID
> +  );
> +
> +#endif
> diff --git a/UefiCpuPkg/Library/LoongArch64MpInitLib/PeiMpInitLib.inf
> b/UefiCpuPkg/Library/LoongArch64MpInitLib/PeiMpInitLib.inf
> new file mode 100644
> index 0000000000..7ecda0cce8
> --- /dev/null
> +++ b/UefiCpuPkg/Library/LoongArch64MpInitLib/PeiMpInitLib.inf
> @@ -0,0 +1,37 @@
> +## @file
> +#  LoongArch64 MP initialize support functions for PEI phase.
> +#
> +#  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights
> reserved.<BR>
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 1.29
> +  BASE_NAME                      = PeiMpInitLib
> +  MODULE_UNI_FILE                = PeiMpInitLib.uni
> +  FILE_GUID                      = ED078F16-A2D3-2F34-9267-E24D56E91FCF
> +  MODULE_TYPE                    = PEIM
> +  VERSION_STRING                 = 1.1
> +  LIBRARY_CLASS                  = MpInitLib|PEIM
> +
> +[Sources.common]
> +  PeiMpLib.c
> +  MpLib.c
> +  MpLib.h
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  UefiCpuPkg/UefiCpuPkg.dec
> +
> +[LibraryClasses]
> +  BaseLib
> +  CpuLib
> +  HobLib
> +  MemoryAllocationLib
> +  SynchronizationLib
> +  TimerLib
> +
> +[Pcd]
> +  gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber        ##
> CONSUMES
> +  gUefiCpuPkgTokenSpaceGuid.PcdCpuApInitTimeOutInMicroSeconds      ##
> CONSUMES
> diff --git a/UefiCpuPkg/Library/LoongArch64MpInitLib/PeiMpInitLib.uni
> b/UefiCpuPkg/Library/LoongArch64MpInitLib/PeiMpInitLib.uni
> new file mode 100644
> index 0000000000..51fdc618c6
> --- /dev/null
> +++ b/UefiCpuPkg/Library/LoongArch64MpInitLib/PeiMpInitLib.uni
> @@ -0,0 +1,15 @@
> +// /** @file
> +// MP Initialize Library instance for PEI driver.
> +//
> +// MP Initialize Library instance for PEI driver.
> +//
> +// Copyright (c) 2024, Loongson Technology Corporation Limited. All rights
> reserved.<BR>
> +//
> +// SPDX-License-Identifier: BSD-2-Clause-Patent
> +//
> +// **/
> +
> +
> +#string STR_MODULE_ABSTRACT             #language en-US "MP Initialize
> Library instance for PEI driver."
> +
> +#string STR_MODULE_DESCRIPTION          #language en-US "MP Initialize
> Library instance for PEI driver."
> diff --git a/UefiCpuPkg/Library/LoongArch64MpInitLib/PeiMpLib.c
> b/UefiCpuPkg/Library/LoongArch64MpInitLib/PeiMpLib.c
> new file mode 100644
> index 0000000000..d1c5e55b57
> --- /dev/null
> +++ b/UefiCpuPkg/Library/LoongArch64MpInitLib/PeiMpLib.c
> @@ -0,0 +1,404 @@
> +/** @file
> +  LoongArch64 MP initialize support functions for PEI phase.
> +
> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights
> reserved.<BR>
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include "MpLib.h"
> +
> +/**
> +  Enable Debug Agent to support source debugging on AP function.
> +
> +**/
> +VOID
> +EnableDebugAgent (
> +  VOID
> +  )
> +{
> +}
> +
> +/**
> +  Get pointer to CPU MP Data structure.
> +
> +  @return  The pointer to CPU MP Data structure.
> +**/
> +CPU_MP_DATA *
> +GetCpuMpData (
> +  VOID
> +  )
> +{
> +  CPU_MP_DATA  *CpuMpData;
> +
> +  CpuMpData = GetCpuMpDataFromGuidedHob ();
> +  ASSERT (CpuMpData != NULL);
> +  return CpuMpData;
> +}
> +
> +/**
> +  Save the pointer to CPU MP Data structure.
> +
> +  @param[in] CpuMpData  The pointer to CPU MP Data structure will be
> saved.
> +**/
> +VOID
> +SaveCpuMpData (
> +  IN CPU_MP_DATA  *CpuMpData
> +  )
> +{
> +  UINT64  Data64;
> +
> +  //
> +  // Build location of CPU MP DATA buffer in HOB
> +  //
> +  Data64 = (UINT64)(UINTN)CpuMpData;
> +  BuildGuidDataHob (
> +    &mCpuInitMpLibHobGuid,
> +    (VOID *)&Data64,
> +    sizeof (UINT64)
> +    );
> +}
> +
> +/**
> +  Save the Processor Resource Data.
> +
> +  @param[in] ResourceData  The pointer to Processor Resource Data
> structure will be saved.
> +**/
> +VOID
> +SaveProcessorResourceData (
> +  IN PROCESSOR_RESOURCE_DATA  *ResourceData
> +  )
> +{
> +  UINT64  Data64;
> +
> +  //
> +  // Build location of Processor Resource Data buffer in HOB
> +  //
> +  Data64 = (UINT64)(UINTN)ResourceData;
> +  BuildGuidDataHob (
> +    &mProcessorResourceHobGuid,
> +    (VOID *)&Data64,
> +    sizeof (UINT64)
> +    );
> +}
> +
> +/**
> +  Get available EfiBootServicesCode memory below 4GB by specified size.
> +
> +  This buffer is required to safely transfer AP from real address mode to
> +  protected mode or long mode, due to the fact that the buffer returned by
> +  GetWakeupBuffer() may be marked as non-executable.
> +
> +  @param[in] BufferSize   Wakeup transition buffer size.
> +
> +  @retval other   Return wakeup transition buffer address below 4GB.
> +  @retval 0       Cannot find free memory below 4GB.
> +**/
> +UINTN
> +GetModeTransitionBuffer (
> +  IN UINTN  BufferSize
> +  )
> +{
> +  //
> +  // PEI phase doesn't need to do such transition. So simply return 0.
> +  //
> +  return 0;
> +}
> +
> +/**
> +  Checks APs status and updates APs status if needed.
> +
> +**/
> +VOID
> +CheckAndUpdateApsStatus (
> +  VOID
> +  )
> +{
> +}
> +
> +/**
> +  Initialize global data for MP support.
> +
> +  @param[in] CpuMpData  The pointer to CPU MP Data structure.
> +**/
> +VOID
> +InitMpGlobalData (
> +  IN CPU_MP_DATA  *CpuMpData
> +  )
> +{
> +  SaveCpuMpData (CpuMpData);
> +}
> +
> +/**
> +  This service executes a caller provided function on all enabled APs.
> +
> +  @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 MpInitLibStartupAllAPs() or
> +                                      MPInitLibStartupThisAP().
> +                                      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 
> Initialization
> +                                      library, 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_UNSUPPORTED         WaitEvent is not NULL if non-blocking
> mode is not
> +                                  supported.
> +  @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_NOT_READY           MP Initialize Library is not initialized.
> +  @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_AP_PROCEDURE  Procedure,
> +  IN  BOOLEAN           SingleThread,
> +  IN  EFI_EVENT         WaitEvent               OPTIONAL,
> +  IN  UINTN             TimeoutInMicroseconds,
> +  IN  VOID              *ProcedureArgument      OPTIONAL,
> +  OUT UINTN             **FailedCpuList         OPTIONAL
> +  )
> +{
> +  if (WaitEvent != NULL) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  return StartupAllCPUsWorker (
> +           Procedure,
> +           SingleThread,
> +           TRUE,
> +           NULL,
> +           TimeoutInMicroseconds,
> +           ProcedureArgument,
> +           FailedCpuList
> +           );
> +}
> +
> +/**
> +  This service lets the caller get one enabled AP to execute a 
> caller-provided
> +  function.
> +
> +  @param[in]  Procedure               A pointer to the function to be run on 
> the
> +                                      designated AP 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
> +                                      MpInitLibGetNumberOfProcessors().
> +  @param[in]  WaitEvent               The event created by the caller with
> CreateEvent()
> +                                      service.  If it is NULL, then execute 
> in
> +                                      blocking mode. BSP waits until this AP 
> 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 this AP,
> +                                      and go on executing immediately. If 
> this AP
> +                                      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
> +                                      this AP to finish this Procedure, 
> either for
> +                                      blocking or non-blocking mode. Zero 
> means
> +                                      infinity.  If the timeout expires 
> before
> +                                      this AP returns from Procedure, then 
> Procedure
> +                                      on the AP is terminated. The
> +                                      AP is available for next function 
> assigned
> +                                      by MpInitLibStartupAllAPs() or
> +                                      MpInitLibStartupThisAP().
> +                                      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
> on the
> +                                      specified AP.
> +  @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_UNSUPPORTED         WaitEvent is not NULL if non-blocking
> mode is not
> +                                  supported.
> +  @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_READY           MP Initialize Library is not initialized.
> +  @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_AP_PROCEDURE  Procedure,
> +  IN  UINTN             ProcessorNumber,
> +  IN  EFI_EVENT         WaitEvent               OPTIONAL,
> +  IN  UINTN             TimeoutInMicroseconds,
> +  IN  VOID              *ProcedureArgument      OPTIONAL,
> +  OUT BOOLEAN           *Finished               OPTIONAL
> +  )
> +{
> +  if (WaitEvent != NULL) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  return StartupThisAPWorker (
> +           Procedure,
> +           ProcessorNumber,
> +           NULL,
> +           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.
> +
> +  @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
> +                               MpInitLibGetNumberOfProcessors().
> +  @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_DEVICE_ERROR        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.
> +  @retval EFI_NOT_READY           MP Initialize Library is not initialized.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +MpInitLibSwitchBSP (
> +  IN UINTN     ProcessorNumber,
> +  IN  BOOLEAN  EnableOldBSP
> +  )
> +{
> +  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] ProcessorNumber   The handle number of 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
> +                               MpInitLibGetNumberOfProcessors().
> +  @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.
> +  @retval EFI_NOT_READY           MP Initialize Library is not initialized.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +MpInitLibEnableDisableAP (
> +  IN  UINTN    ProcessorNumber,
> +  IN  BOOLEAN  EnableAP,
> +  IN  UINT32   *HealthFlag OPTIONAL
> +  )
> +{
> +  return EFI_UNSUPPORTED;
> +}
> diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
> index 738013ec0c..0f0bce0029 100644
> --- a/UefiCpuPkg/UefiCpuPkg.dsc
> +++ b/UefiCpuPkg/UefiCpuPkg.dsc
> @@ -213,6 +213,8 @@
> 
> UefiCpuPkg/Library/LoongArch64CpuExceptionHandlerLib/DxeCpuException
> HandlerLib.inf
>    UefiCpuPkg/Library/LoongArch64CpuMmuLib/PeiCpuMmuLib.inf
>    UefiCpuPkg/Library/LoongArch64CpuMmuLib/DxeCpuMmuLib.inf
> +  UefiCpuPkg/Library/LoongArch64MpInitLib/PeiMpInitLib.inf
> +  UefiCpuPkg/Library/LoongArch64MpInitLib/DxeMpInitLib.inf
> 
>  [BuildOptions]
>    *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
> --
> 2.27.0
> 
> 
> 
> 
> 



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#113696): https://edk2.groups.io/g/devel/message/113696
Mute This Topic: https://groups.io/mt/103679458/21656
Group Owner: devel+ow...@edk2.groups.io
Unsubscribe: 
https://edk2.groups.io/g/devel/leave/9847357/21656/1706620634/xyzzy 
[arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-


Reply via email to