public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2
@ 2024-01-26  6:27 Chao Li
  2024-01-26  6:27 ` [edk2-devel] [PATCH v8 01/37] MdePkg: Add the header file named Csr.h for LoongArch64 Chao Li
                   ` (41 more replies)
  0 siblings, 42 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:27 UTC (permalink / raw)
  To: devel
  Cc: Michael D Kinney, Liming Gao, Zhiguang Liu, Eric Dong, Ray Ni,
	Rahul Kumar, Gerd Hoffmann, Leif Lindholm, Ard Biesheuvel,
	Abner Chang, Daniel Schaefer, Sami Mujawar, Jiewen Yao,
	Jordan Justen, Andrei Warkentin, Laszlo Ersek, Sunil V L,
	Bibo Mao

This patch set will enable LoongArch virtual machine in edk2, the new
LoongArch virtual machine is located in OvmfPkg/LoongArchVirt/, it is a
generic platform that dose not require any actual hardware.

Patch1-Patch14: Submit the common library and driver for LoongArch
virtual machine and real hardware. Such as base help functions,
exception handel, MMU library, multiprocessor library etc.

Patch15-Patch16: Add PrePiCpuIoSize for LoongArch64. and move ArmVirtPkg
two PCDs into OvmfPkg for easier use by other architectures.

Patch17-Patch29: LoongArch virtual machine private code, include SEC and
PEI phase code, some library and drivers.

Patch30: Add LoongArchVirt's self introduction-file.

Modfied modues: MdePkg, UefiCpuPkg, EmbeddedPkg, ArmVirtPkg, OvmfPkg.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

PR: https://github.com/tianocore/edk2/pull/5208

**Changes from V1 to V2:**
1. Add Ray's Ack signature to patch 13.
2. Add LoongArchVirt's self introduction-file. And made a standalone
patch for this change.

**Changes from V2 to V3:**
Compared with V2, there are 9 more patches, removed 2 libraries and 1
driver from OvmfPkg/LoongArchVrt.

New patches:
MdePkg: Add a new library named PeiServicesTablePointerLibReg
MdePkg: Add method of LoongArch64 to PeiServicesTablePointerLibReg
MdePkg: Add a PCD feature flag named PcdPciIoTranslationIsEnabled
UefiCpuPkg: Add MMIO method in CpuIo2Dxe
ArmVirtPkg: Enable UefiCpuPkg version CpuIo2Dxe
ArmPkg: Remove ArmPciCpuIo2Dxe from ArmPkg
OvmfPkg/RiscVVirt: Enable UefiCpuPkg version CpuIo2Dxe
OvmfPkg/RiscVVirt: Remove PciCpuIo2Dxe from RiscVVirt
ArmVirtPkg: Move the FdtSerialPortAddressLib to OvmfPkg
ArmVirtPkg: Move the PcdTerminalTypeGuidBuffer into OvmfPkg
ArmVirtPkg: Move PlatformBootManagerLib to OvmfPkg

For the review opinions:
1. Add MMIO method to CpuIo2Dxe driver to accommodate more ARCH that
require MMIO method, enable it on ARM, RISCV64 and LOONGARCH64.
Questioner: Gerd, Sunil.

2. Move the FdtSerialProtAddressLib to OvmfPkg and enabled it on ARM and
LOONGARCH64. Questioner: Gerd, Laszlo.

3. Add a new library in MdePkg named PeiServiceTablePointerLibReg for
the Register Mechanism and enabled it on LOONGARCH64. Questioner: Laszlo.

4. Moved the ARM version of PlatformBootManagerLib to OvmfPkg/Library, and
enabled it on ARM and LOONGARCH64. Questioner: Laszlo, Gerd.

5. Adjust the order of some inf file in ArmVirtPkg. Questioner: Sami.

6. Move the CpuMmuLib.h some architecture-specific PTE #defines into
Csr.h. Questioner: Andrei.

**Changes from V3 to V4:**
Compared with V3, 1 patch moving ArmPciCpuIo2Dxe has been removed.
Adjusted the code of the LoongArch64 CPU timer library. Rename
PeiServiceTablePointerLibReg to PeiServiceTablePointerLibKs0. Fixed some
typos. Remove some APIs of CpuMmuLib.

For the review opinions:
1. Removed the patch that removed ArmPciCpuIo2Dxe, there are 15 platforms
in edk2-platforms were affected, so wait for this patch series to be
merged, then submit a patch to remove it. Questioner: Leif.

2. Renamed the PeiServiceTablePointLibReg to PeiServiceTablePointLibKs0.
Currently, this library is specific to LoongArch architecture.
Questioner: Laszlo, Liming.

3. Adjust some logic of LoongArch64 CPU timer library. Using the safe
INT library and adjust the bit width of stable counter. Questioner: Laszlo.

4. Removed some APIs from CpuMmuLib. Change some APIs to private.
Questioner: Ray.

**Changes from V4 to V5:**
Compared with V4, 1 patch adding PcdPciIoTranslationIsEnabled in MdePkg
has been removed.
Copy ArmPciCpuIo2Dxe to UefiCpuPkg and renamed it to CpuMmio2Dxe.
Restore the definitions in MdePkg/Include/Protocol/DebugSupport.h, and
just add some comments.

For the review opinions:
1. Copy ArmPciCpuIo2Dxe to UefiCpuPkg so that more other ARCHs can use
it easily. Questioner: Ray, Ard.

2. Restore the changes of MdePkg/Include/Protocol/DebugSupport.h.
Questioner: Liming.

3. Ia32 X64 and LoongArch64 use a same API definition,
InitializeFloatingPointUnits in CpuLib.h. Questioner: Liming.

4. Adjust API definition in CpuMmuLib.h. Questioner: Ray.

**Changes from V5 to V6:**
1. Added Sunil "Reviewed-by" message to patches 20 and 21.
2. Added Bibo "Reviced-by" message to patches 27, 29, 32 and 34.
3. Added the IPI handler in SEC and PEI stage. Modified patches 11 and
15.
4. Adjust some code styles to suit CI ECC.
5. Adjust the way of AP wakeup from IPI.
6. Adjust the order between setting exceptions and copying exception
handlers.

**Changes from V6 to V7:**
Compared with V6, 1 patch has been added.

Added Bibo "Reviewed-by" message to new patches 26, 27, 29, 31, 32, 36,
37.

Added Liming "Reviewed-by" message to all MdePkg changes.

For the review opinions:
1. Moved the changes to OvmfPkg.dec from old patch 24 to new patch 23.
Questioner: Laszlo.

2. Separate the changes to DebugSupport.h from old patch 11 and
create a new patch 10. Questioner: Liming.

**Changes from V7 to V8:**
Adjusted the folders layout in UefiCpuPkg, most LoongArch64 libraries and
drivers have been placed in current matching folders.

Removed Ray "Reviewed-by" message to patches 11, 12, 14, 15, 16. Because
them are moved to other current matching folders in UefiCpuPkg, they
need to be reviewed again.

For the review opinions:
1. Moved remove FdtSerialPortAddressLib action in ArmVirtPkg.dec from
patch 25 to 23. Questioner: Laszlo.

2. Moved removal of ArmVirtPkg.dec dependency in
PlatformBootManagerLib.inf from patch 25 to 24. Questioner: Laszlo.

Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Leif Lindholm <quic_llindhol@quicinc.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Abner Chang <abner.chang@amd.com>
Cc: Daniel Schaefer <git@danielschaefer.me>
Cc: Sami Mujawar <sami.mujawar@arm.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Andrei Warkentin <andrei.warkentin@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Sunil V L <sunilvl@ventanamicro.com>
Cc: Bibo Mao <maobibo@loongson.cn>

Chao Li (37):
  MdePkg: Add the header file named Csr.h for LoongArch64
  MdePkg: Add LoongArch64 FPU function set into BaseCpuLib
  MdePkg: Add LoongArch64 exception function set into BaseLib
  MdePkg: Add LoongArch64 local interrupt function set into BaseLib
  MdePkg: Add LoongArch Cpucfg function
  MdePkg: Add read stable counter operation for LoongArch
  MdePkg: Add CSR operation for LoongArch
  MdePkg: Add IOCSR operation for LoongArch
  MdePkg: Add a new library named PeiServicesTablePointerLibKs0
  MdePkg: Add some comments for LoongArch exceptions
  UefiCpuPkg: Add LoongArch64 CPU Timer instance
  UefiCpuPkg: Add CPU exception library for LoongArch
  UefiCpuPkg: Add CpuMmuLib.h to UefiCpuPkg
  UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  UefiCpuPkg: Add multiprocessor library for LoongArch64
  UefiCpuPkg: Add CpuDxe driver for LoongArch64
  EmbeddedPkg: Add PcdPrePiCpuIoSize width for LOONGARCH64
  ArmVirtPkg: Move PCD of FDT base address and FDT padding to OvmfPkg
  UefiCpuPkg: Add a new CPU IO 2 driver named CpuMmio2Dxe
  ArmVirtPkg: Enable CpuMmio2Dxe
  OvmfPkg/RiscVVirt: Enable CpuMmio2Dxe
  OvmfPkg/RiscVVirt: Remove PciCpuIo2Dxe from RiscVVirt
  ArmVirtPkg: Move the FdtSerialPortAddressLib to OvmfPkg
  ArmVirtPkg: Move two PCD variables into OvmfPkg
  ArmVirtPkg: Move PlatformBootManagerLib to OvmfPkg
  OvmfPkg/LoongArchVirt: Add stable timer driver
  OvmfPkg/LoongArchVirt: Add a NULL library named
    CollectApResouceLibNull
  OvmfPkg/LoongArchVirt: Add serial port hook library
  OvmfPkg/LoongArchVirt: Add the early serial port output library
  OvmfPkg/LoongArchVirt: Add real time clock library
  OvmfPkg/LoongArchVirt: Add NorFlashQemuLib
  OvmfPkg/LoongArchVirt: Add FdtQemuFwCfgLib
  OvmfPkg/LoongArchVirt: Add reset system library
  OvmfPkg/LoongArchVirt: Support SEC phase
  OvmfPkg/LoongArchVirt: Support PEI phase
  OvmfPkg/LoongArchVirt: Add build file
  OvmfPkg/LoongArchVirt: Add self introduction file

 ArmVirtPkg/ArmVirt.dsc.inc                    |    2 +-
 ArmVirtPkg/ArmVirtCloudHv.dsc                 |    4 +-
 ArmVirtPkg/ArmVirtCloudHv.fdf                 |    2 +-
 ArmVirtPkg/ArmVirtKvmTool.dsc                 |    4 +-
 ArmVirtPkg/ArmVirtKvmTool.fdf                 |    2 +-
 ArmVirtPkg/ArmVirtPkg.ci.yaml                 |    1 -
 ArmVirtPkg/ArmVirtPkg.dec                     |   28 -
 ArmVirtPkg/ArmVirtQemu.dsc                    |    8 +-
 ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc          |    2 +-
 ArmVirtPkg/ArmVirtQemuKernel.dsc              |    8 +-
 ArmVirtPkg/ArmVirtXen.dsc                     |    2 +-
 .../ArmVirtPsciResetSystemPeiLib.inf          |    3 +-
 .../CloudHvVirtMemInfoPeiLib.inf              |    3 +-
 .../DebugLibFdtPL011UartFlash.inf             |    3 +-
 .../EarlyFdt16550SerialPortHookLib.inf        |    3 +-
 .../EarlyFdtPL011SerialPortLib.inf            |    3 +-
 .../KvmtoolPlatformPeiLib.inf                 |    5 +-
 .../Library/PlatformPeiLib/PlatformPeiLib.inf |   10 +-
 .../QemuVirtMemInfoPeiLib.inf                 |    3 +-
 .../PrePi/ArmVirtPrePiUniCoreRelocatable.inf  |    3 +-
 EmbeddedPkg/EmbeddedPkg.dec                   |    3 +
 MdePkg/Include/Library/BaseLib.h              |  221 +++
 MdePkg/Include/Library/CpuLib.h               |   43 +-
 .../Library/PeiServicesTablePointerLib.h      |    9 +-
 MdePkg/Include/Protocol/DebugSupport.h        |   14 +
 MdePkg/Include/Register/LoongArch64/Cpucfg.h  |  565 ++++++
 MdePkg/Include/Register/LoongArch64/Csr.h     |  263 +++
 MdePkg/Library/BaseCpuLib/BaseCpuLib.inf      |    7 +-
 .../Library/BaseCpuLib/LoongArch/DisableFpu.S |   17 +
 .../Library/BaseCpuLib/LoongArch/EnableFpu.S  |   17 +
 .../BaseCpuLib/LoongArch/InitializeFpu.S      |   51 +
 MdePkg/Library/BaseLib/BaseLib.inf            |    6 +
 MdePkg/Library/BaseLib/LoongArch64/AsmCsr.S   |  422 +++++
 MdePkg/Library/BaseLib/LoongArch64/Cpucfg.S   |   26 +
 MdePkg/Library/BaseLib/LoongArch64/Csr.c      |   81 +
 .../BaseLib/LoongArch64/DisableInterrupts.S   |   22 +-
 .../BaseLib/LoongArch64/EnableInterrupts.S    |   22 +-
 .../BaseLib/LoongArch64/ExceptionBase.S       |   41 +
 MdePkg/Library/BaseLib/LoongArch64/IoCsr.S    |  120 ++
 .../BaseLib/LoongArch64/ReadStableCounter.S   |   24 +
 .../PeiServicesTablePointer.c                 |   87 +
 .../PeiServicesTablePointerLibKs0.inf         |   37 +
 .../PeiServicesTablePointerLibKs0.uni         |   20 +
 MdePkg/MdePkg.ci.yaml                         |    3 +-
 MdePkg/MdePkg.dsc                             |    3 +
 .../Include/Library/FdtSerialPortAddressLib.h |    0
 .../FdtSerialPortAddressLib.c                 |    0
 .../FdtSerialPortAddressLib.inf               |    2 +-
 .../PlatformBootManagerLibLight}/PlatformBm.c |    0
 .../PlatformBootManagerLibLight}/PlatformBm.h |    0
 .../PlatformBootManagerLib.inf                |    7 +-
 .../PlatformBootManagerLibLight}/QemuKernel.c |    0
 .../Drivers/StableTimerDxe/Timer.c            |  381 ++++
 .../Drivers/StableTimerDxe/Timer.h            |  127 ++
 .../Drivers/StableTimerDxe/TimerDxe.inf       |   41 +
 .../CollectApResourceLibNull.c                |   38 +
 .../CollectApResourceLibNull.inf              |   31 +
 .../CollectApResourceLibNull.uni              |    9 +
 .../EarlyFdtSerialPortLib16550.c              |  815 +++++++++
 .../EarlyFdtSerialPortLib16550.inf            |   46 +
 .../EarlyFdt16550SerialPortHookLib.c          |   52 +
 .../EarlyFdt16550SerialPortHookLib.inf        |   37 +
 .../Fdt16550SerialPortHookLib.c               |   39 +
 .../Fdt16550SerialPortHookLib.inf             |   33 +
 .../Fdt16550SerialPortHookLib.uni             |   14 +
 .../FdtQemuFwCfgLib/FdtQemuFwCfgPeiLib.c      |  504 +++++
 .../FdtQemuFwCfgLib/FdtQemuFwCfgPeiLib.inf    |   42 +
 .../FdtQemuFwCfgLib/QemuFwCfgLibInternal.h    |   73 +
 .../Library/FdtQemuFwCfgLib/QemuFwCfgPei.c    |  117 ++
 .../DxeLsRealTimeClockLib.c                   |  327 ++++
 .../DxeLsRealTimeClockLib.inf                 |   41 +
 .../LsRealTimeClockLib/LsRealTimeClock.h      |   47 +
 .../PeiLsRealTimeClockLib.c                   |   31 +
 .../PeiLsRealTimeClockLib.inf                 |   29 +
 .../Library/NorFlashQemuLib/NorFlashQemuLib.c |  140 ++
 .../NorFlashQemuLib/NorFlashQemuLib.inf       |   43 +
 .../BaseResetSystemAcpiGed.c                  |  148 ++
 .../BaseResetSystemAcpiGedLib.inf             |   36 +
 .../DxeResetSystemAcpiGed.c                   |  259 +++
 .../DxeResetSystemAcpiGedLib.inf              |   41 +
 .../ResetSystemAcpiLib/ResetSystemAcpiGed.c   |  125 ++
 .../ResetSystemAcpiLib/ResetSystemAcpiGed.h   |   23 +
 OvmfPkg/LoongArchVirt/LoongArchVirt.fdf.inc   |   34 +
 OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc   |  679 +++++++
 OvmfPkg/LoongArchVirt/LoongArchVirtQemu.fdf   |  313 ++++
 OvmfPkg/LoongArchVirt/PlatformPei/Fv.c        |   39 +
 OvmfPkg/LoongArchVirt/PlatformPei/MemDetect.c |  201 ++
 OvmfPkg/LoongArchVirt/PlatformPei/Platform.c  |  393 ++++
 OvmfPkg/LoongArchVirt/PlatformPei/Platform.h  |  146 ++
 .../LoongArchVirt/PlatformPei/PlatformPei.inf |   72 +
 OvmfPkg/LoongArchVirt/Readme.md               |   67 +
 OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S |  184 ++
 OvmfPkg/LoongArchVirt/Sec/SecMain.c           |  507 ++++++
 OvmfPkg/LoongArchVirt/Sec/SecMain.inf         |   53 +
 OvmfPkg/LoongArchVirt/VarStore.fdf.inc        |   67 +
 OvmfPkg/OvmfPkg.dec                           |   32 +
 OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc           |    2 +-
 OvmfPkg/RiscVVirt/RiscVVirtQemu.fdf           |    2 +-
 UefiCpuPkg/CpuDxe/CpuDxe.inf                  |   23 +-
 UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.c        |  454 +++++
 UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.h        |  288 +++
 UefiCpuPkg/CpuDxe/LoongArch64/CpuMp.c         |  544 ++++++
 UefiCpuPkg/CpuDxe/LoongArch64/Exception.c     |  159 ++
 .../CpuMmio2Dxe/CpuMmio2Dxe.c                 |    8 +-
 .../CpuMmio2Dxe/CpuMmio2Dxe.inf               |   16 +-
 UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.uni        |   18 +
 UefiCpuPkg/Include/Library/CpuMmuLib.h        |   62 +
 .../DxeCpuExceptionHandlerLib.inf             |   34 +-
 .../LoongArch/DxeExceptionLib.c               |  198 ++
 .../LoongArch/ExceptionCommon.c               |  171 ++
 .../LoongArch/ExceptionCommon.h               |  131 ++
 .../LoongArch64/ArchExceptionHandler.c        |  268 +++
 .../LoongArch64/ExceptionHandlerAsm.S         |  366 ++++
 .../LoongArch/SecPeiExceptionLib.c            |  102 ++
 .../SecPeiCpuExceptionHandlerLib.inf          |   29 +-
 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf |   36 +
 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni |   14 +
 .../CpuMmuLib/LoongArch64/CommonMmuLib.c      |  988 ++++++++++
 .../CpuMmuLib/LoongArch64/CommonMmuLib.h      |   43 +
 .../Library/CpuMmuLib/LoongArch64/Page.h      |  279 +++
 .../CpuMmuLib/LoongArch64/PeiCpuMmuLib.c      |  178 ++
 .../Library/CpuMmuLib/LoongArch64/Tlb.h       |   48 +
 .../CpuMmuLib/LoongArch64/TlbOperation.S      |   44 +
 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf |   44 +
 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni |   14 +
 .../Library/CpuTimerLib/BaseCpuTimerLib.inf   |    9 +-
 .../CpuTimerLib/LoongArch64/CpuTimerLib.c     |  251 +++
 UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf |   27 +-
 .../Library/MpInitLib/LoongArch64/DxeMpLib.c  |  480 +++++
 .../Library/MpInitLib/LoongArch64/MpLib.c     | 1621 +++++++++++++++++
 .../Library/MpInitLib/LoongArch64/MpLib.h     |  361 ++++
 .../Library/MpInitLib/LoongArch64/PeiMpLib.c  |  404 ++++
 UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf |   27 +-
 UefiCpuPkg/UefiCpuPkg.dec                     |    9 +
 UefiCpuPkg/UefiCpuPkg.dsc                     |    6 +
 135 files changed, 16320 insertions(+), 146 deletions(-)
 create mode 100644 MdePkg/Include/Register/LoongArch64/Cpucfg.h
 create mode 100644 MdePkg/Include/Register/LoongArch64/Csr.h
 create mode 100644 MdePkg/Library/BaseCpuLib/LoongArch/DisableFpu.S
 create mode 100644 MdePkg/Library/BaseCpuLib/LoongArch/EnableFpu.S
 create mode 100644 MdePkg/Library/BaseCpuLib/LoongArch/InitializeFpu.S
 create mode 100644 MdePkg/Library/BaseLib/LoongArch64/AsmCsr.S
 create mode 100644 MdePkg/Library/BaseLib/LoongArch64/Cpucfg.S
 create mode 100644 MdePkg/Library/BaseLib/LoongArch64/Csr.c
 create mode 100644 MdePkg/Library/BaseLib/LoongArch64/ExceptionBase.S
 create mode 100644 MdePkg/Library/BaseLib/LoongArch64/IoCsr.S
 create mode 100644 MdePkg/Library/BaseLib/LoongArch64/ReadStableCounter.S
 create mode 100644 MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointer.c
 create mode 100644 MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointerLibKs0.inf
 create mode 100644 MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointerLibKs0.uni
 rename {ArmVirtPkg => OvmfPkg}/Include/Library/FdtSerialPortAddressLib.h (100%)
 rename {ArmVirtPkg => OvmfPkg}/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c (100%)
 rename {ArmVirtPkg => OvmfPkg}/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf (90%)
 rename {ArmVirtPkg/Library/PlatformBootManagerLib => OvmfPkg/Library/PlatformBootManagerLibLight}/PlatformBm.c (100%)
 rename {ArmVirtPkg/Library/PlatformBootManagerLib => OvmfPkg/Library/PlatformBootManagerLibLight}/PlatformBm.h (100%)
 rename {ArmVirtPkg/Library/PlatformBootManagerLib => OvmfPkg/Library/PlatformBootManagerLibLight}/PlatformBootManagerLib.inf (87%)
 rename {ArmVirtPkg/Library/PlatformBootManagerLib => OvmfPkg/Library/PlatformBootManagerLibLight}/QemuKernel.c (100%)
 create mode 100644 OvmfPkg/LoongArchVirt/Drivers/StableTimerDxe/Timer.c
 create mode 100644 OvmfPkg/LoongArchVirt/Drivers/StableTimerDxe/Timer.h
 create mode 100644 OvmfPkg/LoongArchVirt/Drivers/StableTimerDxe/TimerDxe.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/CollectApResouceLibNull/CollectApResourceLibNull.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/CollectApResouceLibNull/CollectApResourceLibNull.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/CollectApResouceLibNull/CollectApResourceLibNull.uni
 create mode 100644 OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.uni
 create mode 100644 OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/FdtQemuFwCfgPeiLib.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/FdtQemuFwCfgPeiLib.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/QemuFwCfgLibInternal.h
 create mode 100644 OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/QemuFwCfgPei.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/DxeLsRealTimeClockLib.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/DxeLsRealTimeClockLib.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/LsRealTimeClock.h
 create mode 100644 OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/PeiLsRealTimeClockLib.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/PeiLsRealTimeClockLib.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/NorFlashQemuLib/NorFlashQemuLib.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/NorFlashQemuLib/NorFlashQemuLib.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGed.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGedLib.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGed.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGedLib.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.h
 create mode 100644 OvmfPkg/LoongArchVirt/LoongArchVirt.fdf.inc
 create mode 100644 OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc
 create mode 100644 OvmfPkg/LoongArchVirt/LoongArchVirtQemu.fdf
 create mode 100644 OvmfPkg/LoongArchVirt/PlatformPei/Fv.c
 create mode 100644 OvmfPkg/LoongArchVirt/PlatformPei/MemDetect.c
 create mode 100644 OvmfPkg/LoongArchVirt/PlatformPei/Platform.c
 create mode 100644 OvmfPkg/LoongArchVirt/PlatformPei/Platform.h
 create mode 100644 OvmfPkg/LoongArchVirt/PlatformPei/PlatformPei.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Readme.md
 create mode 100644 OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S
 create mode 100644 OvmfPkg/LoongArchVirt/Sec/SecMain.c
 create mode 100644 OvmfPkg/LoongArchVirt/Sec/SecMain.inf
 create mode 100644 OvmfPkg/LoongArchVirt/VarStore.fdf.inc
 create mode 100644 UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.c
 create mode 100644 UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.h
 create mode 100644 UefiCpuPkg/CpuDxe/LoongArch64/CpuMp.c
 create mode 100644 UefiCpuPkg/CpuDxe/LoongArch64/Exception.c
 rename OvmfPkg/RiscVVirt/PciCpuIo2Dxe/PciCpuIo2Dxe.c => UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.c (96%)
 rename OvmfPkg/RiscVVirt/PciCpuIo2Dxe/PciCpuIo2Dxe.inf => UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf (61%)
 create mode 100644 UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.uni
 create mode 100644 UefiCpuPkg/Include/Library/CpuMmuLib.h
 create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/DxeExceptionLib.c
 create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.c
 create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.h
 create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ArchExceptionHandler.c
 create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ExceptionHandlerAsm.S
 create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/SecPeiExceptionLib.c
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
 create mode 100644 UefiCpuPkg/Library/CpuTimerLib/LoongArch64/CpuTimerLib.c
 create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/DxeMpLib.c
 create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.c
 create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.h
 create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/PeiMpLib.c

-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114526): https://edk2.groups.io/g/devel/message/114526
Mute This Topic: https://groups.io/mt/103971622/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 01/37] MdePkg: Add the header file named Csr.h for LoongArch64
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
@ 2024-01-26  6:27 ` Chao Li
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 02/37] MdePkg: Add LoongArch64 FPU function set into BaseCpuLib Chao Li
                   ` (40 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:27 UTC (permalink / raw)
  To: devel; +Cc: Michael D Kinney, Liming Gao, Zhiguang Liu

Adding Csr.h for LoongArch64, it is use for accessing the CSR registers.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Acked-by: Michael D Kinney <michael.d.kinney@intel.com>
Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
---
 MdePkg/Include/Register/LoongArch64/Csr.h | 263 ++++++++++++++++++++++
 1 file changed, 263 insertions(+)
 create mode 100644 MdePkg/Include/Register/LoongArch64/Csr.h

diff --git a/MdePkg/Include/Register/LoongArch64/Csr.h b/MdePkg/Include/Register/LoongArch64/Csr.h
new file mode 100644
index 0000000000..aa22a26564
--- /dev/null
+++ b/MdePkg/Include/Register/LoongArch64/Csr.h
@@ -0,0 +1,263 @@
+/** @file
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - EXC     - Exception
+    - CSR     - CPU Status Register
+**/
+
+#ifndef LOONGARCH_CSR_H_
+#define LOONGARCH_CSR_H_
+
+#include <Base.h>
+
+//
+// CSR register numbers
+//
+
+//
+// Basic CSR registers
+//
+#define LOONGARCH_CSR_CRMD     0x0
+#define LOONGARCH_CSR_PRMD     0x1
+#define LOONGARCH_CSR_EUEN     0x2
+#define CSR_EUEN_LBTEN_SHIFT   3
+#define CSR_EUEN_LBTEN         (0x1ULL << CSR_EUEN_LBTEN_SHIFT)
+#define CSR_EUEN_LASXEN_SHIFT  2
+#define CSR_EUEN_LASXEN        (0x1ULL << CSR_EUEN_LASXEN_SHIFT)
+#define CSR_EUEN_LSXEN_SHIFT   1
+#define CSR_EUEN_LSXEN         (0x1ULL << CSR_EUEN_LSXEN_SHIFT)
+#define CSR_EUEN_FPEN_SHIFT    0
+#define CSR_EUEN_FPEN          (0x1ULL << CSR_EUEN_FPEN_SHIFT)
+#define LOONGARCH_CSR_MISC     0x3
+#define LOONGARCH_CSR_ECFG     0x4
+
+#define LOONGARCH_CSR_ESTAT       0x5
+#define CSR_ESTAT_ESUBCODE_SHIFT  22
+#define CSR_ESTAT_ESUBCODE_WIDTH  9
+#define CSR_ESTAT_ESUBCODE        (0x1ffULL << CSR_ESTAT_ESUBCODE_SHIFT)
+#define CSR_ESTAT_EXC_SHIFT       16
+#define CSR_ESTAT_EXC_WIDTH       6
+#define CSR_ESTAT_EXC             (0x3FULL << CSR_ESTAT_EXC_SHIFT)
+#define CSR_ESTAT_IS_SHIFT        0
+#define CSR_ESTAT_IS_WIDTH        15
+#define CSR_ESTAT_IS              (0x7FFFULL << CSR_ESTAT_IS_SHIFT)
+
+#define LOONGARCH_CSR_ERA    0x6
+#define LOONGARCH_CSR_BADV   0x7
+#define LOONGARCH_CSR_BADI   0x8
+#define LOONGARCH_CSR_EBASE  0xC     // Exception entry base address
+
+//
+// TLB related CSR registers
+//
+#define LOONGARCH_CSR_TLBIDX      0x10      // TLB Index, EHINV, PageSize, NP
+#define LOONGARCH_CSR_TLBEHI      0x11      // TLB EntryHi
+#define LOONGARCH_CSR_TLBELO0     0x12      // TLB EntryLo0
+#define LOONGARCH_CSR_TLBELO1     0x13      // TLB EntryLo1
+#define LOONGARCH_CSR_ASID        0x18      // ASID
+#define LOONGARCH_CSR_PGDL        0x19      // Page table base address when VA[47] = 0
+#define LOONGARCH_CSR_PGDH        0x1A      // Page table base address when VA[47] = 1
+#define LOONGARCH_CSR_PGD         0x1B      // Page table base
+#define LOONGARCH_CSR_PWCTL0      0x1C      // PWCtl0
+#define LOONGARCH_CSR_PWCTL1      0x1D      // PWCtl1
+#define LOONGARCH_CSR_STLBPGSIZE  0x1E
+#define LOONGARCH_CSR_RVACFG      0x1F
+
+///
+/// Page table property definitions
+///
+#define PAGE_VALID_SHIFT   0
+#define PAGE_DIRTY_SHIFT   1
+#define PAGE_PLV_SHIFT     2  // 2~3, two bits
+#define CACHE_SHIFT        4  // 4~5, two bits
+#define PAGE_GLOBAL_SHIFT  6
+#define PAGE_HUGE_SHIFT    6  // HUGE is a PMD bit
+
+#define PAGE_HGLOBAL_SHIFT  12 // HGlobal is a PMD bit
+#define PAGE_PFN_SHIFT      12
+#define PAGE_PFN_END_SHIFT  48
+#define PAGE_NO_READ_SHIFT  61
+#define PAGE_NO_EXEC_SHIFT  62
+#define PAGE_RPLV_SHIFT     63
+
+///
+/// Used by TLB hardware (placed in EntryLo*)
+///
+#define PAGE_VALID    ((UINTN)(1) << PAGE_VALID_SHIFT)
+#define PAGE_DIRTY    ((UINTN)(1) << PAGE_DIRTY_SHIFT)
+#define PAGE_PLV      ((UINTN)(3) << PAGE_PLV_SHIFT)
+#define PAGE_GLOBAL   ((UINTN)(1) << PAGE_GLOBAL_SHIFT)
+#define PAGE_HUGE     ((UINTN)(1) << PAGE_HUGE_SHIFT)
+#define PAGE_HGLOBAL  ((UINTN)(1) << PAGE_HGLOBAL_SHIFT)
+#define PAGE_NO_READ  ((UINTN)(1) << PAGE_NO_READ_SHIFT)
+#define PAGE_NO_EXEC  ((UINTN)(1) << PAGE_NO_EXEC_SHIFT)
+#define PAGE_RPLV     ((UINTN)(1) << PAGE_RPLV_SHIFT)
+#define CACHE_MASK    ((UINTN)(3) << CACHE_SHIFT)
+#define PFN_SHIFT     (EFI_PAGE_SHIFT - 12 + PAGE_PFN_SHIFT)
+
+#define PLV_KERNEL  0
+#define PLV_USER    3
+
+#define PAGE_USER    (PLV_USER << PAGE_PLV_SHIFT)
+#define PAGE_KERNEL  (PLV_KERN << PAGE_PLV_SHIFT)
+
+#define CACHE_SUC  (0 << CACHE_SHIFT) // Strong-ordered UnCached
+#define CACHE_CC   (1 << CACHE_SHIFT) // Coherent Cached
+#define CACHE_WUC  (2 << CACHE_SHIFT) // Weak-ordered UnCached
+
+//
+// Config CSR registers
+//
+#define LOONGARCH_CSR_CPUNUM  0x20    // CPU core number
+#define LOONGARCH_CSR_PRCFG1  0x21    // Config1
+#define LOONGARCH_CSR_PRCFG2  0x22    // Config2
+#define LOONGARCH_CSR_PRCFG3  0x23    // Config3
+
+//
+// Kscratch registers
+//
+#define LOONGARCH_CSR_KS0  0x30
+#define LOONGARCH_CSR_KS1  0x31
+#define LOONGARCH_CSR_KS2  0x32
+#define LOONGARCH_CSR_KS3  0x33
+#define LOONGARCH_CSR_KS4  0x34
+#define LOONGARCH_CSR_KS5  0x35
+#define LOONGARCH_CSR_KS6  0x36
+#define LOONGARCH_CSR_KS7  0x37
+#define LOONGARCH_CSR_KS8  0x38
+
+//
+// Stable timer registers
+//
+#define LOONGARCH_CSR_TMID           0x40  // Timer ID
+#define LOONGARCH_CSR_TMCFG          0x41
+#define LOONGARCH_CSR_TMCFG_EN       (1ULL << 0)
+#define LOONGARCH_CSR_TMCFG_PERIOD   (1ULL << 1)
+#define LOONGARCH_CSR_TMCFG_TIMEVAL  (0x3FFFFFFFFFFFULL << 2)
+#define LOONGARCH_CSR_TVAL           0x42    // Timer value
+#define LOONGARCH_CSR_CNTC           0x43    // Timer offset
+#define LOONGARCH_CSR_TINTCLR        0x44    // Timer interrupt clear
+
+//
+// TLB refill exception base address
+//
+#define LOONGARCH_CSR_TLBREBASE  0x88    // TLB refill exception entry
+#define LOONGARCH_CSR_TLBRBADV   0x89    // TLB refill badvaddr
+#define LOONGARCH_CSR_TLBRERA    0x8a    // TLB refill ERA
+#define LOONGARCH_CSR_TLBRSAVE   0x8b    // KScratch for TLB refill exception
+#define LOONGARCH_CSR_TLBRELO0   0x8c    // TLB refill entrylo0
+#define LOONGARCH_CSR_TLBRELO1   0x8d    // TLB refill entrylo1
+#define LOONGARCH_CSR_TLBREHI    0x8e    // TLB refill entryhi
+
+//
+// Direct map windows registers
+//
+#define LOONGARCH_CSR_DMWIN0  0x180   // 64 direct map win0: MEM & IF
+#define LOONGARCH_CSR_DMWIN1  0x181   // 64 direct map win1: MEM & IF
+#define LOONGARCH_CSR_DMWIN2  0x182   // 64 direct map win2: MEM
+#define LOONGARCH_CSR_DMWIN3  0x183   // 64 direct map win3: MEM
+//
+// CSR register numbers end
+//
+
+//
+// IOCSR register numbers
+//
+#define LOONGARCH_IOCSR_FEATURES  0x8
+#define  IOCSRF_TEMP              (1ULL << 0)
+#define  IOCSRF_NODECNT           (1ULL << 1)
+#define  IOCSRF_MSI               (1ULL << 2)
+#define  IOCSRF_EXTIOI            (1ULL << 3)
+#define  IOCSRF_CSRIPI            (1ULL << 4)
+#define  IOCSRF_FREQCSR           (1ULL << 5)
+#define  IOCSRF_FREQSCALE         (1ULL << 6)
+#define  IOCSRF_DVFSV1            (1ULL << 7)
+#define  IOCSRF_EXTIOI_DECODE     (1ULL << 9)
+#define  IOCSRF_FLATMODE          (1ULL << 10)
+#define  IOCSRF_VM                (1ULL << 11)
+
+#define LOONGARCH_IOCSR_VENDOR  0x10
+
+#define LOONGARCH_IOCSR_CPUNAME  0x20
+
+#define LOONGARCH_IOCSR_NODECNT  0x408
+
+#define LOONGARCH_IOCSR_MISC_FUNC     0x420
+#define  IOCSR_MISC_FUNC_TIMER_RESET  (1ULL << 21)
+#define  IOCSR_MISC_FUNC_EXT_IOI_EN   (1ULL << 48)
+
+#define LOONGARCH_IOCSR_CPUTEMP  0x428
+
+//
+// PerCore CSR, only accessable by local cores
+//
+#define LOONGARCH_IOCSR_IPI_STATUS  0x1000
+#define LOONGARCH_IOCSR_IPI_EN      0x1004
+#define LOONGARCH_IOCSR_IPI_SET     0x1008
+#define LOONGARCH_IOCSR_IPI_CLEAR   0x100c
+#define LOONGARCH_IOCSR_MBUF0       0x1020
+#define LOONGARCH_IOCSR_MBUF1       0x1028
+#define LOONGARCH_IOCSR_MBUF2       0x1030
+#define LOONGARCH_IOCSR_MBUF3       0x1038
+
+#define LOONGARCH_IOCSR_IPI_SEND   0x1040
+#define  IOCSR_IPI_SEND_IP_SHIFT   0
+#define  IOCSR_IPI_SEND_CPU_SHIFT  16
+#define  IOCSR_IPI_SEND_BLOCKING   (1ULL << 31)
+
+#define LOONGARCH_IOCSR_MBUF_SEND   0x1048
+#define  IOCSR_MBUF_SEND_BLOCKING   (1ULL << 31)
+#define  IOCSR_MBUF_SEND_BOX_SHIFT  2
+#define  IOCSR_MBUF_SEND_BOX_LO(box)  (box << 1)
+#define  IOCSR_MBUF_SEND_BOX_HI(box)  ((box << 1) + 1)
+#define  IOCSR_MBUF_SEND_CPU_SHIFT  16
+#define  IOCSR_MBUF_SEND_BUF_SHIFT  32
+#define  IOCSR_MBUF_SEND_H32_MASK   0xFFFFFFFF00000000ULL
+
+#define LOONGARCH_IOCSR_ANY_SEND    0x1158
+#define  IOCSR_ANY_SEND_BLOCKING    (1ULL << 31)
+#define  IOCSR_ANY_SEND_CPU_SHIFT   16
+#define  IOCSR_ANY_SEND_MASK_SHIFT  27
+#define  IOCSR_ANY_SEND_BUF_SHIFT   32
+#define  IOCSR_ANY_SEND_H32_MASK    0xFFFFFFFF00000000ULL
+
+//
+// Register offset and bit definition for CSR access
+//
+#define LOONGARCH_IOCSR_TIMER_CFG   0x1060
+#define LOONGARCH_IOCSR_TIMER_TICK  0x1070
+#define  IOCSR_TIMER_CFG_RESERVED   BIT63
+#define  IOCSR_TIMER_CFG_PERIODIC   BIT62
+#define  IOCSR_TIMER_CFG_EN         BIT61
+#define  IOCSR_TIMER_MASK           0x0FFFFFFFFFFFFULL
+#define  IOCSR_TIMER_INITVAL_RST    (0xFFFFULL << 48)
+//
+// IOCSR register numbers end
+//
+
+//
+// Invalid addr with global=1 or matched asid in current TLB
+//
+#define INVTLB_ADDR_GTRUE_OR_ASID  0x6
+
+//
+// Bits 8 and 9 of FPU Status Register specify the rounding mode
+//
+#define FPU_CSR_RM  0x300
+#define FPU_CSR_RN  0x000   // nearest
+#define FPU_CSR_RZ  0x100   // towards zero
+#define FPU_CSR_RU  0x200   // towards +Infinity
+#define FPU_CSR_RD  0x300   // towards -Infinity
+
+#define DEFAULT_PAGE_SIZE     0x0c
+#define CSR_TLBIDX_SIZE_MASK  0x3f000000
+#define CSR_TLBIDX_PS_SHIFT   24
+#define CSR_TLBIDX_SIZE       CSR_TLBIDX_PS_SHIFT
+#define CSR_TLBREHI_PS_SHIFT  0x0
+#define CSR_TLBREHI_PS        0x3f
+
+#endif
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114527): https://edk2.groups.io/g/devel/message/114527
Mute This Topic: https://groups.io/mt/103971633/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 02/37] MdePkg: Add LoongArch64 FPU function set into BaseCpuLib
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
  2024-01-26  6:27 ` [edk2-devel] [PATCH v8 01/37] MdePkg: Add the header file named Csr.h for LoongArch64 Chao Li
@ 2024-01-26  6:28 ` Chao Li
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 03/37] MdePkg: Add LoongArch64 exception function set into BaseLib Chao Li
                   ` (39 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:28 UTC (permalink / raw)
  To: devel; +Cc: Michael D Kinney, Liming Gao, Zhiguang Liu

Adding InitializeFloatingPointUnits, EnableFloatingPointUnits and
DisableFloatingPointUnits functions for LoongArch64.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Acked-by: Michael D Kinney <michael.d.kinney@intel.com>
Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
---
 MdePkg/Include/Library/CpuLib.h               | 43 ++++++++++++++--
 MdePkg/Library/BaseCpuLib/BaseCpuLib.inf      |  7 ++-
 .../Library/BaseCpuLib/LoongArch/DisableFpu.S | 17 +++++++
 .../Library/BaseCpuLib/LoongArch/EnableFpu.S  | 17 +++++++
 .../BaseCpuLib/LoongArch/InitializeFpu.S      | 51 +++++++++++++++++++
 5 files changed, 128 insertions(+), 7 deletions(-)
 create mode 100644 MdePkg/Library/BaseCpuLib/LoongArch/DisableFpu.S
 create mode 100644 MdePkg/Library/BaseCpuLib/LoongArch/EnableFpu.S
 create mode 100644 MdePkg/Library/BaseCpuLib/LoongArch/InitializeFpu.S

diff --git a/MdePkg/Include/Library/CpuLib.h b/MdePkg/Include/Library/CpuLib.h
index 3f29937dc7..27f3f82ab9 100644
--- a/MdePkg/Include/Library/CpuLib.h
+++ b/MdePkg/Include/Library/CpuLib.h
@@ -8,6 +8,7 @@
   As a result, these services could not be defined in the Base Library.
 
 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
 SPDX-License-Identifier: BSD-2-Clause-Patent
 
 **/
@@ -41,14 +42,16 @@ CpuFlushTlb (
   VOID
   );
 
-#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
+#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64) || defined (MDE_CPU_LOONGARCH64)
 
 /**
+  Initialize the CPU floating point units.
+
   Initializes floating point units for requirement of UEFI specification.
-  This function initializes floating-point control word to 0x027F (all exceptions
-  masked,double-precision, round-to-nearest) and multimedia-extensions control word
-  (if supported) to 0x1F80 (all exceptions masked, round-to-nearest, flush to zero
-  for masked underflow).
+  For IA32 and X64, this function initializes floating-point control word to 0x027F
+  (all exceptions masked,double-precision, round-to-nearest) and multimedia-extensions
+  control word (if supported) to 0x1F80 (all exceptions masked, round-to-nearest,
+  flush to zero for masked underflow).
 **/
 VOID
 EFIAPI
@@ -56,6 +59,10 @@ InitializeFloatingPointUnits (
   VOID
   );
 
+#endif
+
+#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
+
 /**
   Determine if the standard CPU signature is "AuthenticAMD".
   @retval TRUE  The CPU signature matches.
@@ -89,4 +96,30 @@ GetCpuSteppingId (
 
 #endif
 
+#if defined (MDE_CPU_LOONGARCH64)
+
+/**
+  Enable the CPU floating point units.
+
+  Enable the CPU floating point units.
+**/
+VOID
+EFIAPI
+EnableFloatingPointUnits (
+  VOID
+  );
+
+/**
+  Disable the CPU floating point units.
+
+  Disable the CPU floating point units.
+**/
+VOID
+EFIAPI
+DisableFloatingPointUnits (
+  VOID
+  );
+
+#endif
+
 #endif
diff --git a/MdePkg/Library/BaseCpuLib/BaseCpuLib.inf b/MdePkg/Library/BaseCpuLib/BaseCpuLib.inf
index 9a162afe6d..89f6272f11 100644
--- a/MdePkg/Library/BaseCpuLib/BaseCpuLib.inf
+++ b/MdePkg/Library/BaseCpuLib/BaseCpuLib.inf
@@ -65,8 +65,11 @@
   RiscV/Cpu.S
 
 [Sources.LOONGARCH64]
-  LoongArch/CpuFlushTlb.S | GCC
-  LoongArch/CpuSleep.S    | GCC
+  LoongArch/CpuFlushTlb.S   | GCC
+  LoongArch/CpuSleep.S      | GCC
+  LoongArch/InitializeFpu.S | GCC
+  LoongArch/EnableFpu.S     | GCC
+  LoongArch/DisableFpu.S    | GCC
 
 [Packages]
   MdePkg/MdePkg.dec
diff --git a/MdePkg/Library/BaseCpuLib/LoongArch/DisableFpu.S b/MdePkg/Library/BaseCpuLib/LoongArch/DisableFpu.S
new file mode 100644
index 0000000000..33c6bf3411
--- /dev/null
+++ b/MdePkg/Library/BaseCpuLib/LoongArch/DisableFpu.S
@@ -0,0 +1,17 @@
+#------------------------------------------------------------------------------
+#
+# DisableFloatingPointUnits() for LoongArch64
+#
+# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#------------------------------------------------------------------------------
+ASM_GLOBAL ASM_PFX(DisableFloatingPointUnits)
+
+ASM_PFX(DisableFloatingPointUnits):
+  li.w        $t0, 0x1
+  csrxchg     $zero, $t0, 0x2
+
+  jirl $zero, $ra, 0
+  .end
diff --git a/MdePkg/Library/BaseCpuLib/LoongArch/EnableFpu.S b/MdePkg/Library/BaseCpuLib/LoongArch/EnableFpu.S
new file mode 100644
index 0000000000..3e4f7411f1
--- /dev/null
+++ b/MdePkg/Library/BaseCpuLib/LoongArch/EnableFpu.S
@@ -0,0 +1,17 @@
+#------------------------------------------------------------------------------
+#
+# EnableFloatingPointUnits() for LoongArch64
+#
+# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#------------------------------------------------------------------------------
+ASM_GLOBAL ASM_PFX(EnableFloatingPointUnits)
+
+ASM_PFX(EnableFloatingPointUnits):
+  li.w        $t0, 0x1
+  csrxchg     $t0, $t0, 0x2
+
+  jirl $zero, $ra, 0
+  .end
diff --git a/MdePkg/Library/BaseCpuLib/LoongArch/InitializeFpu.S b/MdePkg/Library/BaseCpuLib/LoongArch/InitializeFpu.S
new file mode 100644
index 0000000000..2cea5558a6
--- /dev/null
+++ b/MdePkg/Library/BaseCpuLib/LoongArch/InitializeFpu.S
@@ -0,0 +1,51 @@
+#------------------------------------------------------------------------------
+#
+# InitializeFloatingPointUnits() for LoongArch64
+#
+# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#------------------------------------------------------------------------------
+ASM_GLOBAL ASM_PFX(InitializeFloatingPointUnits)
+
+ASM_PFX(InitializeFloatingPointUnits):
+  li.d        $t0, 0x0      // RNE mode
+  movgr2fcsr  $r0, $t0
+  li.d        $t1, -1       // SNaN
+
+  movgr2fr.d  $f0, $t1
+  movgr2fr.d  $f1, $t1
+  movgr2fr.d  $f2, $t1
+  movgr2fr.d  $f3, $t1
+  movgr2fr.d  $f4, $t1
+  movgr2fr.d  $f5, $t1
+  movgr2fr.d  $f6, $t1
+  movgr2fr.d  $f7, $t1
+  movgr2fr.d  $f8, $t1
+  movgr2fr.d  $f9, $t1
+  movgr2fr.d  $f10, $t1
+  movgr2fr.d  $f11, $t1
+  movgr2fr.d  $f12, $t1
+  movgr2fr.d  $f13, $t1
+  movgr2fr.d  $f14, $t1
+  movgr2fr.d  $f15, $t1
+  movgr2fr.d  $f16, $t1
+  movgr2fr.d  $f17, $t1
+  movgr2fr.d  $f18, $t1
+  movgr2fr.d  $f19, $t1
+  movgr2fr.d  $f20, $t1
+  movgr2fr.d  $f21, $t1
+  movgr2fr.d  $f22, $t1
+  movgr2fr.d  $f23, $t1
+  movgr2fr.d  $f24, $t1
+  movgr2fr.d  $f25, $t1
+  movgr2fr.d  $f26, $t1
+  movgr2fr.d  $f27, $t1
+  movgr2fr.d  $f28, $t1
+  movgr2fr.d  $f29, $t1
+  movgr2fr.d  $f30, $t1
+  movgr2fr.d  $f31, $t1
+
+  jirl $zero, $ra, 0
+  .end
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114528): https://edk2.groups.io/g/devel/message/114528
Mute This Topic: https://groups.io/mt/103971634/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 03/37] MdePkg: Add LoongArch64 exception function set into BaseLib
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
  2024-01-26  6:27 ` [edk2-devel] [PATCH v8 01/37] MdePkg: Add the header file named Csr.h for LoongArch64 Chao Li
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 02/37] MdePkg: Add LoongArch64 FPU function set into BaseCpuLib Chao Li
@ 2024-01-26  6:28 ` Chao Li
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 04/37] MdePkg: Add LoongArch64 local interrupt " Chao Li
                   ` (38 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:28 UTC (permalink / raw)
  To: devel; +Cc: Michael D Kinney, Liming Gao, Zhiguang Liu

Adding SetExceptionBaseAddress and SetTlbRebaseAddress functions
for LoongArch64.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Acked-by: Michael D Kinney <michael.d.kinney@intel.com>
Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
---
 MdePkg/Include/Library/BaseLib.h              | 20 +++++++++
 MdePkg/Library/BaseLib/BaseLib.inf            |  1 +
 .../BaseLib/LoongArch64/ExceptionBase.S       | 41 +++++++++++++++++++
 3 files changed, 62 insertions(+)
 create mode 100644 MdePkg/Library/BaseLib/LoongArch64/ExceptionBase.S

diff --git a/MdePkg/Include/Library/BaseLib.h b/MdePkg/Include/Library/BaseLib.h
index ca0d06c7f3..7117c4288f 100644
--- a/MdePkg/Include/Library/BaseLib.h
+++ b/MdePkg/Include/Library/BaseLib.h
@@ -287,6 +287,26 @@ typedef struct {
 
 #define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT  8
 
+/*
+ * Set the exception base address for LoongArch.
+ *
+ * @param  ExceptionBaseAddress   The exception base address, must be aligned greater than or qeual to 4K .
+ */
+VOID
+SetExceptionBaseAddress (
+  IN UINT64
+  );
+
+/*
+ * Set the TlbRebase address for LoongArch.
+ *
+ * @param  TlbRebaseAddress   The TlbRebase address, must be aligned greater than or qeual to 4K .
+ */
+VOID
+SetTlbRebaseAddress (
+  IN UINT64
+  );
+
 #endif // defined (MDE_CPU_LOONGARCH64)
 
 //
diff --git a/MdePkg/Library/BaseLib/BaseLib.inf b/MdePkg/Library/BaseLib/BaseLib.inf
index 6b46949be3..22b38b59e7 100644
--- a/MdePkg/Library/BaseLib/BaseLib.inf
+++ b/MdePkg/Library/BaseLib/BaseLib.inf
@@ -420,6 +420,7 @@
   LoongArch64/CpuPause.S            | GCC
   LoongArch64/SetJumpLongJump.S     | GCC
   LoongArch64/SwitchStack.S         | GCC
+  LoongArch64/ExceptionBase.S       | GCC
 
 [Packages]
   MdePkg/MdePkg.dec
diff --git a/MdePkg/Library/BaseLib/LoongArch64/ExceptionBase.S b/MdePkg/Library/BaseLib/LoongArch64/ExceptionBase.S
new file mode 100644
index 0000000000..b6e90a8f28
--- /dev/null
+++ b/MdePkg/Library/BaseLib/LoongArch64/ExceptionBase.S
@@ -0,0 +1,41 @@
+#------------------------------------------------------------------------------
+#
+# LoongArch set exception base address operations
+#
+# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#------------------------------------------------------------------------------
+
+#include <Base.h>
+#include <Register/LoongArch64/Csr.h>
+
+ASM_GLOBAL ASM_PFX(SetExceptionBaseAddress)
+ASM_GLOBAL ASM_PFX(SetTlbRebaseAddress)
+
+#/**
+#  Set the exception base address for LoongArch.
+#
+#  @param  ExceptionBaseAddress   The exception base address, must be aligned greater than or qeual to 4K .
+#**/
+ASM_PFX(SetExceptionBaseAddress):
+  csrrd   $t0, LOONGARCH_CSR_ECFG
+  li.d    $t1, ~(BIT16 | BIT17 | BIT18)
+  and     $t0, $t0, $t1
+  csrwr   $t0, LOONGARCH_CSR_ECFG
+
+  move    $t0, $a0
+  csrwr   $t0, LOONGARCH_CSR_EBASE
+  jirl    $zero, $ra, 0
+
+#/**
+#  Set the TlbRebase address for LoongArch.
+#
+#  @param  TlbRebaseAddress   The TlbRebase address, must be aligned greater than or qeual to 4K .
+#**/
+ASM_PFX(SetTlbRebaseAddress):
+  move    $t0, $a0
+  csrwr   $t0, LOONGARCH_CSR_TLBREBASE
+  jirl    $zero, $ra, 0
+.end
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114529): https://edk2.groups.io/g/devel/message/114529
Mute This Topic: https://groups.io/mt/103971637/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 04/37] MdePkg: Add LoongArch64 local interrupt function set into BaseLib
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (2 preceding siblings ...)
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 03/37] MdePkg: Add LoongArch64 exception function set into BaseLib Chao Li
@ 2024-01-26  6:28 ` Chao Li
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 05/37] MdePkg: Add LoongArch Cpucfg function Chao Li
                   ` (37 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:28 UTC (permalink / raw)
  To: devel; +Cc: Michael D Kinney, Liming Gao, Zhiguang Liu

Adding LoongArch local interrupt function set, which is used to control
the opening or closing of the local interrupt when the global interrupt
is enabled.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Acked-by: Michael D Kinney <michael.d.kinney@intel.com>
Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
---
 MdePkg/Include/Library/BaseLib.h              | 20 +++++++++++++++++
 .../BaseLib/LoongArch64/DisableInterrupts.S   | 22 ++++++++++++++-----
 .../BaseLib/LoongArch64/EnableInterrupts.S    | 22 ++++++++++++++-----
 3 files changed, 54 insertions(+), 10 deletions(-)

diff --git a/MdePkg/Include/Library/BaseLib.h b/MdePkg/Include/Library/BaseLib.h
index 7117c4288f..91322d2dfa 100644
--- a/MdePkg/Include/Library/BaseLib.h
+++ b/MdePkg/Include/Library/BaseLib.h
@@ -307,6 +307,26 @@ SetTlbRebaseAddress (
   IN UINT64
   );
 
+/**
+  Enables local CPU interrupts.
+
+  @param  Needs to enable local interrupt bit.
+**/
+VOID
+EnableLocalInterrupts (
+  IN UINT16
+  );
+
+/**
+  Disables local CPU interrupts.
+
+  @param  Needs to disable local interrupt bit.
+**/
+VOID
+DisableLocalInterrupts (
+  IN UINT16
+  );
+
 #endif // defined (MDE_CPU_LOONGARCH64)
 
 //
diff --git a/MdePkg/Library/BaseLib/LoongArch64/DisableInterrupts.S b/MdePkg/Library/BaseLib/LoongArch64/DisableInterrupts.S
index 0f228339af..8f9ee888b1 100644
--- a/MdePkg/Library/BaseLib/LoongArch64/DisableInterrupts.S
+++ b/MdePkg/Library/BaseLib/LoongArch64/DisableInterrupts.S
@@ -1,21 +1,33 @@
 #------------------------------------------------------------------------------
 #
-# LoongArch interrupt disable
+# LoongArch interrupt disable operations
 #
-# Copyright (c) 2022, Loongson Technology Corporation Limited. All rights reserved.<BR>
+# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
 #
 # SPDX-License-Identifier: BSD-2-Clause-Patent
 #
 #------------------------------------------------------------------------------
 
+#include <Base.h>
+#include <Register/LoongArch64/Csr.h>
+
+ASM_GLOBAL ASM_PFX(DisableLocalInterrupts)
 ASM_GLOBAL ASM_PFX(DisableInterrupts)
 
 #/**
-#  Disables CPU interrupts.
+#  Disables local CPU interrupts.
+#
+#  @param  Needs to disable local interrupt bit.
 #**/
+ASM_PFX(DisableLocalInterrupts):
+  csrxchg $zero, $a0, LOONGARCH_CSR_ECFG
+  jirl    $zero, $ra, 0
 
+#/**
+#  Disables global CPU interrupts.
+#**/
 ASM_PFX(DisableInterrupts):
-  li.w    $t0, 0x4
-  csrxchg $zero, $t0, 0x0
+  li.w    $t0, BIT2
+  csrxchg $zero, $t0, LOONGARCH_CSR_CRMD
   jirl    $zero, $ra, 0
   .end
diff --git a/MdePkg/Library/BaseLib/LoongArch64/EnableInterrupts.S b/MdePkg/Library/BaseLib/LoongArch64/EnableInterrupts.S
index 3c34fb2cdd..126c7b49b3 100644
--- a/MdePkg/Library/BaseLib/LoongArch64/EnableInterrupts.S
+++ b/MdePkg/Library/BaseLib/LoongArch64/EnableInterrupts.S
@@ -1,21 +1,33 @@
 #------------------------------------------------------------------------------
 #
-# LoongArch interrupt enable
+# LoongArch interrupt enable operations
 #
-# Copyright (c) 2022, Loongson Technology Corporation Limited. All rights reserved.<BR>
+# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
 #
 # SPDX-License-Identifier: BSD-2-Clause-Patent
 #
 #------------------------------------------------------------------------------
 
+#include <Base.h>
+#include <Register/LoongArch64/Csr.h>
+
+ASM_GLOBAL ASM_PFX(EnableLocalInterrupts)
 ASM_GLOBAL ASM_PFX(EnableInterrupts)
 
 #/**
-#  Enables CPU interrupts.
+#  Enables local CPU interrupts.
+#
+#  @param  Needs to enable local interrupt bit.
 #**/
+ASM_PFX(EnableLocalInterrupts):
+  csrxchg $a0, $a0, LOONGARCH_CSR_ECFG
+  jirl    $zero, $ra, 0
 
+#/**
+#  Enables global CPU interrupts.
+#**/
 ASM_PFX(EnableInterrupts):
-  li.w    $t0, 0x4
-  csrxchg $t0, $t0, 0x0
+  li.w    $t0, BIT2
+  csrxchg $t0, $t0, LOONGARCH_CSR_CRMD
   jirl    $zero, $ra, 0
   .end
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114530): https://edk2.groups.io/g/devel/message/114530
Mute This Topic: https://groups.io/mt/103971639/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 05/37] MdePkg: Add LoongArch Cpucfg function
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (3 preceding siblings ...)
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 04/37] MdePkg: Add LoongArch64 local interrupt " Chao Li
@ 2024-01-26  6:28 ` Chao Li
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 06/37] MdePkg: Add read stable counter operation for LoongArch Chao Li
                   ` (36 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:28 UTC (permalink / raw)
  To: devel; +Cc: Michael D Kinney, Liming Gao, Zhiguang Liu

Add LoongArch AsmCpucfg function and Cpucfg definitions.

Also added Include/Register/LoongArch64/Cpucfg.h to IgnoreFiles of
EccCheck.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Acked-by: Michael D Kinney <michael.d.kinney@intel.com>
Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
---
 MdePkg/Include/Library/BaseLib.h             |  12 +
 MdePkg/Include/Register/LoongArch64/Cpucfg.h | 565 +++++++++++++++++++
 MdePkg/Library/BaseLib/BaseLib.inf           |   1 +
 MdePkg/Library/BaseLib/LoongArch64/Cpucfg.S  |  26 +
 MdePkg/MdePkg.ci.yaml                        |   3 +-
 5 files changed, 606 insertions(+), 1 deletion(-)
 create mode 100644 MdePkg/Include/Register/LoongArch64/Cpucfg.h
 create mode 100644 MdePkg/Library/BaseLib/LoongArch64/Cpucfg.S

diff --git a/MdePkg/Include/Library/BaseLib.h b/MdePkg/Include/Library/BaseLib.h
index 91322d2dfa..d64e406b7c 100644
--- a/MdePkg/Include/Library/BaseLib.h
+++ b/MdePkg/Include/Library/BaseLib.h
@@ -327,6 +327,18 @@ DisableLocalInterrupts (
   IN UINT16
   );
 
+/**
+  Read CPUCFG register.
+
+  @param  Index  Specifies the register number of the CPUCFG to read the data.
+  @param  Data   A pointer to the variable used to store the CPUCFG register value.
+**/
+VOID
+AsmCpucfg (
+  IN  UINT32  Index,
+  OUT UINT32  *Data
+  );
+
 #endif // defined (MDE_CPU_LOONGARCH64)
 
 //
diff --git a/MdePkg/Include/Register/LoongArch64/Cpucfg.h b/MdePkg/Include/Register/LoongArch64/Cpucfg.h
new file mode 100644
index 0000000000..570748b194
--- /dev/null
+++ b/MdePkg/Include/Register/LoongArch64/Cpucfg.h
@@ -0,0 +1,565 @@
+/** @file
+  CPUCFG definitions.
+
+  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef CPUCFG_H_
+#define CPUCFG_H_
+
+/**
+  CPUCFG REG0 Information
+
+  @code
+  CPUCFG_REG0_INFO_DATA
+ **/
+#define CPUCFG_REG0_INFO  0x0
+
+/**
+  CPUCFG REG0 Information returned data.
+  #CPUCFG_REG0_INFO
+ **/
+typedef union {
+  struct {
+    ///
+    /// [Bit 31:0] Processor Identity.
+    ///
+    UINT32    PRID : 32;
+  } Bits;
+  ///
+  /// All bit fields as a 32-bit value
+  ///
+  UINT32    Uint32;
+} CPUCFG_REG0_INFO_DATA;
+
+/**
+  CPUCFG REG1 Information
+
+  @code
+  CPUCFG_REG1_INFO_DATA
+ **/
+#define CPUCFG_REG1_INFO  0x1
+
+/**
+  CPUCFG REG1 Information returned data.
+  #CPUCFG_REG1_INFO
+ **/
+typedef union {
+  struct {
+    ///
+    /// [Bit 1:0] Architecture:
+    ///           2'b00 indicates the implementation of simplified LoongAarch32;
+    ///           2'b01 indicates the implementation of LoongAarch32;
+    ///           2'b10 indicates the implementation of LoongAarch64;
+    ///           2'b11 reserved;
+    ///
+    UINT32    ARCH      : 2;
+    ///
+    /// [Bit 2] Paging mapping mode. A value of 1 indicates the processor MMU supports
+    /// page mapping mode.
+    ///
+    UINT32    PGMMU     : 1;
+    ///
+    /// [Bit 3] A value of 1 indicates the processor supports the IOCSR instruction.
+    ///
+    UINT32    IOCSR     : 1;
+    ///
+    /// [Bit 11:4] Physical address bits. The supported physical address bits PALEN value
+    /// minus 1.
+    ///
+    UINT32    PALEN     : 8;
+    ///
+    /// [Bit 19:12] Virtual address bits. The supported virtual address bits VALEN value
+    /// minus 1.
+    ///
+    UINT32    VALEN     : 8;
+    ///
+    /// [Bit 20] Non-aligned Memory Access. A value of 1 indicates the processor supports
+    /// non-aligned memory access.
+    ///
+    UINT32    UAL       : 1;
+    ///
+    /// [Bit 21] Page Read Inhibit. A value of 1 indicates the processor supports page
+    /// attribute of "Read Inhibit".
+    ///
+    UINT32    RI        : 1;
+    ///
+    /// [Bit 22] Page Execution Protection. A value of 1 indicates the processor supports
+    /// page attribute of "Execution Protection".
+    ///
+    UINT32    EP        : 1;
+    ///
+    /// [Bit 23] A value of 1 indicates the processor supports for page attributes of RPLV.
+    ///
+    UINT32    RPLV      : 1;
+    ///
+    /// [Bit 24] Huge Page. A value of 1 indicates the processor supports page attribute
+    /// of huge page.
+    ///
+    UINT32    HP        : 1;
+    ///
+    /// [Bit 25] A value of 1 indicates that the string of processor product information
+    /// is recorded at address 0 of the IOCSR access space.
+    ///
+    UINT32    IOCSR_BRD : 1;
+    ///
+    /// [Bit 26] A value of 1 indicates that the external interrupt uses the message
+    /// interrupt mode, otherwise it is the level interrupt line mode.
+    ///
+    UINT32    MSG_INT   : 1;
+    ///
+    /// [Bit 31:27] Reserved.
+    ///
+    UINT32    Reserved  : 5;
+  } Bits;
+  ///
+  /// All bit fields as a 32-bit value
+  ///
+  UINT32    Uint32;
+} CPUCFG_REG1_INFO_DATA;
+
+/**
+  CPUCFG REG2 Information
+
+  @code
+  CPUCFG_REG2_INFO_DATA
+ **/
+#define CPUCFG_REG2_INFO  0x2
+
+/**
+  CPUCFG REG2 Information returned data.
+  #CPUCFG_REG2_INFO
+ **/
+typedef union {
+  struct {
+    ///
+    /// [Bit 0] Basic Floating-Point. A value of 1 indicates the processor supports basic
+    /// floating-point instructions.
+    ///
+    UINT32    FP       : 1;
+    ///
+    /// [Bit 1] Sigle-Precision. A value of 1 indicates the processor supports sigle-precision
+    /// floating-point numbers.
+    ///
+    UINT32    FP_SP    : 1;
+    ///
+    /// [Bit 2] Double-Precision. A value of 1 indicates the processor supports double-precision
+    /// floating-point numbers.
+    ///
+    UINT32    FP_DP    : 1;
+    ///
+    /// [Bit 5:3] The version number of the floating-point arithmetic standard. 1 is the initial
+    /// version number, indicating that it is compatible with the IEEE 754-2008 standard.
+    ///
+    UINT32    FP_ver   : 3;
+    ///
+    /// [Bit 6] 128-bit Vector Extension. A value of 1 indicates the processor supports 128-bit
+    /// vector extension.
+    ///
+    UINT32    LSX      : 1;
+    ///
+    /// [Bit 7] 256-bit Vector Extension. A value of 1 indicates the processor supports 256-bit
+    /// vector extension.
+    ///
+    UINT32    LASX     : 1;
+    ///
+    /// [Bit 8] Complex Vector Operation Instructions. A value of 1 indicates the processor supports
+    /// complex vector operation instructions.
+    ///
+    UINT32    COMPLEX  : 1;
+    ///
+    /// [Bit 9] Encryption And Decryption Vector Instructions. A value of 1 indicates the processor
+    /// supports encryption and decryption vector instructions.
+    ///
+    UINT32    CRYPTO   : 1;
+    ///
+    /// [Bit 10] Virtualization Expansion. A value of 1 indicates the processor supports
+    /// virtualization expansion.
+    ///
+    UINT32    LVZ      : 1;
+    ///
+    /// [Bit 13:11] The version number of the virtualization hardware acceleration specification.
+    /// 1 is the initial version number.
+    ///
+    UINT32    LVZ_ver  : 3;
+    ///
+    /// [Bit 14] Constant Frequency Counter And Timer. A value of 1 indicates the processor supports
+    /// constant frequency counter and timer.
+    ///
+    UINT32    LLFTP    : 1;
+    ///
+    /// [Bit 17:15] Constant frequency counter and timer version number. 1 is the initial version.
+    ///
+    UINT32    LLTP_ver : 3;
+    ///
+    /// [Bit 18] X86 Binary Translation Extension. A value of 1 indicates the processor supports
+    /// X86 binary translation extension.
+    ///
+    UINT32    LBT_X86  : 1;
+    ///
+    /// [Bit 19] ARM Binary Translation Extension. A value of 1 indicates the processor supports
+    /// ARM binary translation extension.
+    ///
+    UINT32    LBT_ARM  : 1;
+    ///
+    /// [Bit 20] MIPS Binary Translation Extension. A value of 1 indicates the processor supports
+    /// MIPS binary translation extension.
+    ///
+    UINT32    LBT_MIPS : 1;
+    ///
+    /// [Bit 21] Software Page Table Walking Instruction. A value of 1 indicates the processor
+    /// supports software page table walking instruction.
+    ///
+    UINT32    LSPW     : 1;
+    ///
+    /// [Bit 22] Atomic Memory Access Instruction. A value of 1 indicates the processor supports
+    /// AM* atomic memory access instruction.
+    ///
+    UINT32    LAM      : 1;
+    ///
+    /// [Bit 31:23] Reserved.
+    ///
+    UINT32    Reserved : 9;
+  } Bits;
+  ///
+  /// All bit fields as a 32-bit value
+  ///
+  UINT32    Uint32;
+} CPUCFG_REG2_INFO_DATA;
+
+/**
+  CPUCFG REG3 Information
+
+  @code
+  CPUCFG_REG3_INFO_DATA
+ **/
+#define CPUCFG_REG3_INFO  0x3
+
+/**
+  CPUCFG REG3 Information returned data.
+  #CPUCFG_REG3_INFO
+ **/
+typedef union {
+  struct {
+    ///
+    /// [Bit 0] Hardware Cache Coherent DMA. A value of 1 indicates the processor supports
+    /// hardware cache coherent DMA.
+    ///
+    UINT32    CCDMA     : 1;
+    ///
+    /// [Bit 1] Store Fill Buffer. A value of 1 indicates the processor supports store fill
+    /// buffer (SFB).
+    ///
+    UINT32    SFB       : 1;
+    ///
+    /// [Bit 2] Uncache Accelerate. A value of 1 indicates the processor supports uncache
+    /// accelerate.
+    ///
+    UINT32    UCACC     : 1;
+    ///
+    /// [Bit 3] A value of 1 indicates the processor supports LL instruction to fetch exclusive
+    /// block function.
+    ///
+    UINT32    LLEXC     : 1;
+    ///
+    /// [Bit 4] A value of 1 indicates the processor supports random delay function after SC
+    /// instruction.
+    ///
+    UINT32    SCDLY     : 1;
+    ///
+    /// [Bit 5] A value of 1 indicates the processor supports LL automatic with dbar function.
+    ///
+    UINT32    LLDBAR    : 1;
+    ///
+    /// [Bit 6] A value of 1 indicates the processor supports the hardware maintains the
+    /// consistency between ITLB and TLB.
+    ///
+    UINT32    ITLBT     : 1;
+    ///
+    /// [Bit 7] A value of 1 indicates the processor supports the hardware maintains the data
+    /// consistency between ICache and DCache in one processor core.
+    ///
+    UINT32    ICACHET   : 1;
+    ///
+    /// [Bit 10:8] The maximum number of directory levels supported by the page walk instruction.
+    ///
+    UINT32    SPW_LVL   : 3;
+    ///
+    /// [Bit 11] A value of 1 indicates the processor supports the page walk instruction fills
+    /// the TLB in half when it encounters a large page.
+    ///
+    UINT32    SPW_HP_HF : 1;
+    ///
+    /// [Bit 12] Virtual Address Range. A value of 1 indicates the processor supports the software
+    /// configuration can be used to shorten the virtual address range.
+    ///
+    UINT32    RVA       : 1;
+    ///
+    /// [Bit 16:13] The maximum configurable virtual address is shortened by -1.
+    ///
+    UINT32    RVAMAX_1  : 4;
+    ///
+    /// [Bit 31:17] Reserved.
+    ///
+    UINT32    Reserved  : 15;
+  } Bits;
+  ///
+  /// All bit fields as a 32-bit value
+  ///
+  UINT32    Uint32;
+} CPUCFG_REG3_INFO_DATA;
+
+/**
+  CPUCFG REG4 Information
+
+  @code
+  CPUCFG_REG4_INFO_DATA
+ **/
+#define CPUCFG_REG4_INFO  0x4
+
+/**
+  CPUCFG REG4 Information returned data.
+  #CPUCFG_REG4_INFO
+ **/
+typedef union {
+  struct {
+    ///
+    /// [Bit 31:0] Constant frequency timer and the crystal frequency corresponding to the clock
+    /// used by the timer.
+    ///
+    UINT32    CC_FREQ : 32;
+  } Bits;
+  ///
+  /// All bit fields as a 32-bit value
+  ///
+  UINT32    Uint32;
+} CPUCFG_REG4_INFO_DATA;
+
+/**
+  CPUCFG REG5 Information
+
+  @code
+  CPUCFG_REG5_INFO_DATA
+ **/
+#define CPUCFG_REG5_INFO  0x5
+
+/**
+  CPUCFG REG5 Information returned data.
+  #CPUCFG_REG5_INFO
+ **/
+typedef union {
+  struct {
+    ///
+    /// [Bit 15:0] Constant frequency timer and the corresponding multiplication factor of the
+    /// clock used by the timer.
+    ///
+    UINT32    CC_MUL : 16;
+    ///
+    /// [Bit 31:16] Constant frequency timer and the division coefficient corresponding to the
+    /// clock used by the timer
+    ///
+    UINT32    CC_DIV : 16;
+  } Bits;
+  ///
+  /// All bit fields as a 32-bit value
+  ///
+  UINT32    Uint32;
+} CPUCFG_REG5_INFO_DATA;
+
+/**
+  CPUCFG REG6 Information
+
+  @code
+  CPUCFG_REG6_INFO_DATA
+ **/
+#define CPUCFG_REG6_INFO  0x6
+
+/**
+  CPUCFG REG6 Information returned data.
+  #CPUCFG_REG6_INFO
+ **/
+typedef union {
+  struct {
+    ///
+    /// [Bit 0] Performance Counter. A value of 1 indicates the processor supports performance
+    /// counter.
+    ///
+    UINT32    PMP      : 1;
+    ///
+    /// [Bit 3:1] In the performance monitor, the architecture defines the version number of the
+    /// event, and 1 is the initial version
+    ///
+    UINT32    PMVER    : 3;
+    ///
+    /// [Bit 7:4] Number of performance monitors minus 1.
+    ///
+    UINT32    PMNUM    : 4;
+    ///
+    /// [Bit 13:8] Number of bits of a performance monitor minus 1.
+    ///
+    UINT32    PMBITS   : 6;
+    ///
+    /// [Bit 14] A value of 1 indicates the processor supports reading performance counter in user mode.
+    ///
+    UINT32    UPM      : 1;
+    ///
+    /// [Bit 31:15] Reserved.
+    ///
+    UINT32    Reserved : 17;
+  } Bits;
+  ///
+  /// All bit fields as a 32-bit value
+  ///
+  UINT32    Uint32;
+} CPUCFG_REG6_INFO_DATA;
+
+/**
+  CPUCFG REG16 Information
+
+  @code
+  CPUCFG_REG16_INFO_DATA
+ **/
+#define CPUCFG_REG16_INFO  0x10
+
+/**
+  CPUCFG REG16 Information returned data.
+  #CPUCFG_REG16_INFO
+ **/
+typedef union {
+  struct {
+    ///
+    /// [Bit 0] A value of 1 indicates the processor has a first-level instruction cache
+    /// or a first-level unified cache
+    ///
+    UINT32    L1_IU_Present   : 1;
+    ///
+    /// [Bit 1] A value of 1 indicates that the cache shown by L1 IU_Present is the
+    /// unified cache.
+    ///
+    UINT32    L1_IU_Unify     : 1;
+    ///
+    /// [Bit 2] A value of 1 indicates the processor has a first-level data cache.
+    ///
+    UINT32    L1_D_Present    : 1;
+    ///
+    /// [Bit 3] A value of 1 indicates the processor has a second-level instruction cache
+    /// or a second-level unified cache.
+    ///
+    UINT32    L2_IU_Present   : 1;
+    ///
+    /// [Bit 4] A value of 1 indicates that the cache shown by L2 IU_Present is the
+    /// unified cache.
+    ///
+    UINT32    L2_IU_Unify     : 1;
+    ///
+    /// [Bit 5] A value of 1 indicates that the cache shown by L2 IU_Present is private
+    /// to each core.
+    ///
+    UINT32    L2_IU_Private   : 1;
+    ///
+    /// [Bit 6] A value of 1 indicates that the cache shown by L2 IU_Present has an inclusive
+    /// relationship to the lower levels (L1).
+    ///
+    UINT32    L2_IU_Inclusive : 1;
+    ///
+    /// [Bit 7] A value of 1 indicates the processor has a second-level data cache.
+    ///
+    UINT32    L2_D_Present    : 1;
+    ///
+    /// [Bit 8] A value of 1 indicates that the second-level data cache is private to each core.
+    ///
+    UINT32    L2_D_Private    : 1;
+    ///
+    /// [Bit 9] A value of 1 indicates that the second-level data cache has a containment
+    /// relationship to the lower level (L1).
+    ///
+    UINT32    L2_D_Inclusive  : 1;
+    ///
+    /// [Bit 10] A value of 1 indicates the processor has a three-level instruction cache
+    /// or a second-level unified Cache.
+    ///
+    UINT32    L3_IU_Present   : 1;
+    ///
+    /// [Bit 11] A value of 1 indicates that the cache shown by L3 IU_Present is the
+    /// unified cache.
+    ///
+    UINT32    L3_IU_Unify     : 1;
+    ///
+    /// [Bit 12] A value of 1 indicates that the cache shown by L3 IU_Present is private
+    /// to each core.
+    ///
+    UINT32    L3_IU_Private   : 1;
+    ///
+    /// [Bit 13] A value of 1 indicates that the cache shown by L3 IU_Present has an inclusive
+    /// relationship to the lower levels (L1 and L2).
+    ///
+    UINT32    L3_IU_Inclusive : 1;
+    ///
+    /// [Bit 14] A value of 1 indicates the processor has a three-level data cache.
+    ///
+    UINT32    L3_D_Present    : 1;
+    ///
+    /// [Bit 15] A value of 1 indicates that the three-level data cache is private to each core.
+    ///
+    UINT32    L3_D_Private    : 1;
+    ///
+    /// [Bit 16] A value of 1 indicates that the three-level data cache has a containment
+    /// relationship to the lower level (L1 and L2).
+    ///
+    UINT32    L3_D_Inclusive  : 1;
+    ///
+    /// [Bit 31:17] Reserved.
+    ///
+    UINT32    Reserved        : 15;
+  } Bits;
+  ///
+  /// All bit fields as a 32-bit value
+  ///
+  UINT32    Uint32;
+} CPUCFG_REG16_INFO_DATA;
+
+/**
+  CPUCFG REG17, REG18, REG19 and REG20 Information
+
+  @code
+  CPUCFG_CACHE_INFO_DATA
+ **/
+#define CPUCFG_REG17_INFO  0x11 /// L1 unified cache.
+#define CPUCFG_REG18_INFO  0x12 /// L1 data cache.
+#define CPUCFG_REG19_INFO  0x13 /// L2 unified cache.
+#define CPUCFG_REG20_INFO  0x14 /// L3 unified cache.
+
+/**
+  CPUCFG CACHE Information returned data.
+  #CPUCFG_REG17_INFO
+  #CPUCFG_REG18_INFO
+  #CPUCFG_REG19_INFO
+  #CPUCFG_REG20_INFO
+ **/
+typedef union {
+  struct {
+    ///
+    /// [Bit 15:0] Number of channels minus 1.
+    ///
+    UINT32    Way_1         : 16;
+    ///
+    /// [Bit 23:16] Log2 (number of cache rows per channel).
+    ///
+    UINT32    Index_log2    : 8;
+    ///
+    /// [Bit 30:24] Log2 (cache row bytes).
+    ///
+    UINT32    Linesize_log2 : 7;
+    ///
+    /// [Bit 31] Reserved.
+    ///
+    UINT32    Reserved      : 1;
+  } Bits;
+  ///
+  /// All bit fields as a 32-bit value
+  ///
+  UINT32    Uint32;
+} CPUCFG_CACHE_INFO_DATA;
+#endif
diff --git a/MdePkg/Library/BaseLib/BaseLib.inf b/MdePkg/Library/BaseLib/BaseLib.inf
index 22b38b59e7..2f1e3b3d91 100644
--- a/MdePkg/Library/BaseLib/BaseLib.inf
+++ b/MdePkg/Library/BaseLib/BaseLib.inf
@@ -421,6 +421,7 @@
   LoongArch64/SetJumpLongJump.S     | GCC
   LoongArch64/SwitchStack.S         | GCC
   LoongArch64/ExceptionBase.S       | GCC
+  LoongArch64/Cpucfg.S              | GCC
 
 [Packages]
   MdePkg/MdePkg.dec
diff --git a/MdePkg/Library/BaseLib/LoongArch64/Cpucfg.S b/MdePkg/Library/BaseLib/LoongArch64/Cpucfg.S
new file mode 100644
index 0000000000..8b3f842f9e
--- /dev/null
+++ b/MdePkg/Library/BaseLib/LoongArch64/Cpucfg.S
@@ -0,0 +1,26 @@
+#------------------------------------------------------------------------------
+#
+# AsmCpucfg for LoongArch
+#
+# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#------------------------------------------------------------------------------
+
+ASM_GLOBAL ASM_PFX(AsmCpucfg)
+
+#/**
+#  Read CPUCFG register.
+#
+#  @param   a0 Specifies the register number of the CPUCFG to read the data.
+#  @param   a1 Pointer to the variable used to store the CPUCFG register value.
+#
+#**/
+
+ASM_PFX(AsmCpucfg):
+  cpucfg  $t0, $a0
+  stptr.d $t0, $a1, 0
+
+  jirl    $zero, $ra, 0
+  .end
diff --git a/MdePkg/MdePkg.ci.yaml b/MdePkg/MdePkg.ci.yaml
index 1d3d8327b1..f2d81af080 100644
--- a/MdePkg/MdePkg.ci.yaml
+++ b/MdePkg/MdePkg.ci.yaml
@@ -80,7 +80,8 @@
             "Include/Register/Amd/SmramSaveStateMap.h",
             "Test/UnitTest/Library/DevicePathLib/TestDevicePathLib.c",
             "Test/UnitTest/Library/DevicePathLib/TestDevicePathLib.h",
-            "Test/UnitTest/Library/DevicePathLib/TestDevicePathStringConversions.c"
+            "Test/UnitTest/Library/DevicePathLib/TestDevicePathStringConversions.c",
+            "Include/Register/LoongArch64/Cpucfg.h"
         ]
     },
     ## options defined ci/Plugin/CompilerPlugin
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114531): https://edk2.groups.io/g/devel/message/114531
Mute This Topic: https://groups.io/mt/103971641/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 06/37] MdePkg: Add read stable counter operation for LoongArch
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (4 preceding siblings ...)
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 05/37] MdePkg: Add LoongArch Cpucfg function Chao Li
@ 2024-01-26  6:28 ` Chao Li
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 07/37] MdePkg: Add CSR " Chao Li
                   ` (35 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:28 UTC (permalink / raw)
  To: devel; +Cc: Michael D Kinney, Liming Gao, Zhiguang Liu

Add LoongArch gets stable counter ASM function.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Acked-by: Michael D Kinney <michael.d.kinney@intel.com>
Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
---
 MdePkg/Include/Library/BaseLib.h              | 12 ++++++++++
 MdePkg/Library/BaseLib/BaseLib.inf            |  1 +
 .../BaseLib/LoongArch64/ReadStableCounter.S   | 24 +++++++++++++++++++
 3 files changed, 37 insertions(+)
 create mode 100644 MdePkg/Library/BaseLib/LoongArch64/ReadStableCounter.S

diff --git a/MdePkg/Include/Library/BaseLib.h b/MdePkg/Include/Library/BaseLib.h
index d64e406b7c..29009adbc9 100644
--- a/MdePkg/Include/Library/BaseLib.h
+++ b/MdePkg/Include/Library/BaseLib.h
@@ -339,6 +339,18 @@ AsmCpucfg (
   OUT UINT32  *Data
   );
 
+/**
+  Gets the timer count value.
+
+  @param[] VOID
+  @retval  timer count value.
+
+**/
+UINTN
+AsmReadStableCounter (
+  VOID
+  );
+
 #endif // defined (MDE_CPU_LOONGARCH64)
 
 //
diff --git a/MdePkg/Library/BaseLib/BaseLib.inf b/MdePkg/Library/BaseLib/BaseLib.inf
index 2f1e3b3d91..1dad587b0c 100644
--- a/MdePkg/Library/BaseLib/BaseLib.inf
+++ b/MdePkg/Library/BaseLib/BaseLib.inf
@@ -422,6 +422,7 @@
   LoongArch64/SwitchStack.S         | GCC
   LoongArch64/ExceptionBase.S       | GCC
   LoongArch64/Cpucfg.S              | GCC
+  LoongArch64/ReadStableCounter.S   | GCC
 
 [Packages]
   MdePkg/MdePkg.dec
diff --git a/MdePkg/Library/BaseLib/LoongArch64/ReadStableCounter.S b/MdePkg/Library/BaseLib/LoongArch64/ReadStableCounter.S
new file mode 100644
index 0000000000..aa74ff603e
--- /dev/null
+++ b/MdePkg/Library/BaseLib/LoongArch64/ReadStableCounter.S
@@ -0,0 +1,24 @@
+#------------------------------------------------------------------------------
+#
+# LoongArch Read Stable Counter
+#
+# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#------------------------------------------------------------------------------
+
+ASM_GLOBAL ASM_PFX(AsmReadStableCounter)
+
+#/**
+#  Gets the timer count value.
+#
+#  @param[] VOID
+#  @retval  timer count value.
+#
+#**/
+
+ASM_PFX(AsmReadStableCounter):
+  rdtime.d   $a0, $zero
+  jirl       $zero, $ra, 0
+  .end
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114532): https://edk2.groups.io/g/devel/message/114532
Mute This Topic: https://groups.io/mt/103971643/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 07/37] MdePkg: Add CSR operation for LoongArch
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (5 preceding siblings ...)
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 06/37] MdePkg: Add read stable counter operation for LoongArch Chao Li
@ 2024-01-26  6:28 ` Chao Li
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 08/37] MdePkg: Add IOCSR " Chao Li
                   ` (34 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:28 UTC (permalink / raw)
  To: devel; +Cc: Michael D Kinney, Liming Gao, Zhiguang Liu, Bibo Mao

Add CsrRead, CsrWrite and CsrXChg functions for LoongArch, and use them
to operate the CSR register of LoongArch architecture.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Co-authored-by: Bibo Mao <maobibo@loongson.cn>
Acked-by: Michael D Kinney <michael.d.kinney@intel.com>
Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
---
 MdePkg/Include/Library/BaseLib.h            |  45 +++
 MdePkg/Library/BaseLib/BaseLib.inf          |   2 +
 MdePkg/Library/BaseLib/LoongArch64/AsmCsr.S | 422 ++++++++++++++++++++
 MdePkg/Library/BaseLib/LoongArch64/Csr.c    |  81 ++++
 4 files changed, 550 insertions(+)
 create mode 100644 MdePkg/Library/BaseLib/LoongArch64/AsmCsr.S
 create mode 100644 MdePkg/Library/BaseLib/LoongArch64/Csr.c

diff --git a/MdePkg/Include/Library/BaseLib.h b/MdePkg/Include/Library/BaseLib.h
index 29009adbc9..4e97368ae2 100644
--- a/MdePkg/Include/Library/BaseLib.h
+++ b/MdePkg/Include/Library/BaseLib.h
@@ -351,6 +351,51 @@ AsmReadStableCounter (
   VOID
   );
 
+/**
+  CSR read operation.
+
+  @param[in]  Select   CSR read instruction select values.
+
+  @return     The return value of csrrd instruction, return -1 means no CSR instruction
+              is found.
+**/
+UINTN
+CsrRead (
+  IN UINT16  Select
+  );
+
+/**
+  CSR write operation.
+
+  @param[in]  Select   CSR write instruction select values.
+  @param[in]  Value    The csrwr will write the value.
+
+  @return     The return value of csrwr instruction, that is, store the old value of
+              the register, return -1 means no CSR instruction is found.
+**/
+UINTN
+CsrWrite (
+  IN UINT16  Select,
+  IN UINTN   Value
+  );
+
+/**
+  CSR exchange operation.
+
+  @param[in]  Select   CSR exchange instruction select values.
+  @param[in]  Value    The csrxchg will write the value.
+  @param[in]  Mask     The csrxchg mask value.
+
+  @return     The return value of csrxchg instruction, that is, store the old value of
+              the register, return -1 means no CSR instruction is found.
+**/
+UINTN
+CsrXChg (
+  IN UINT16  Select,
+  IN UINTN   Value,
+  IN UINTN   Mask
+  );
+
 #endif // defined (MDE_CPU_LOONGARCH64)
 
 //
diff --git a/MdePkg/Library/BaseLib/BaseLib.inf b/MdePkg/Library/BaseLib/BaseLib.inf
index 1dad587b0c..7c46306883 100644
--- a/MdePkg/Library/BaseLib/BaseLib.inf
+++ b/MdePkg/Library/BaseLib/BaseLib.inf
@@ -410,7 +410,9 @@
 [Sources.LOONGARCH64]
   Math64.c
   Unaligned.c
+  LoongArch64/Csr.c
   LoongArch64/InternalSwitchStack.c
+  LoongArch64/AsmCsr.S              | GCC
   LoongArch64/GetInterruptState.S   | GCC
   LoongArch64/EnableInterrupts.S    | GCC
   LoongArch64/DisableInterrupts.S   | GCC
diff --git a/MdePkg/Library/BaseLib/LoongArch64/AsmCsr.S b/MdePkg/Library/BaseLib/LoongArch64/AsmCsr.S
new file mode 100644
index 0000000000..3a879411f5
--- /dev/null
+++ b/MdePkg/Library/BaseLib/LoongArch64/AsmCsr.S
@@ -0,0 +1,422 @@
+#------------------------------------------------------------------------------
+#
+# LoongArch ASM CSR operation functions
+#
+# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#------------------------------------------------------------------------------
+
+#include <Register/LoongArch64/Csr.h>
+
+ASM_GLOBAL ASM_PFX (AsmCsrRead)
+ASM_GLOBAL ASM_PFX (AsmCsrWrite)
+ASM_GLOBAL ASM_PFX (AsmCsrXChg)
+
+.macro AsmCsrRd Sel
+  csrrd   $a0, \Sel
+  jirl    $zero, $ra, 0
+.endm
+
+.macro AsmCsrWr Sel
+  csrwr   $a0, \Sel
+  jirl    $zero, $ra, 0
+.endm
+
+.macro AsmCsrXChange Sel
+  csrxchg $a0, $a1, \Sel
+  jirl    $zero, $ra, 0
+.endm
+
+ASM_PFX(AsmCsrRead):
+  blt      $a0, $zero, ReadSelNumErr
+  li.w     $t0, LOONGARCH_CSR_EBASE
+  bltu     $t0, $a0, TlbCsrRd
+
+BasicCsrRd:
+  la.pcrel $t0, BasicCsrRead
+  alsl.d   $t0, $a0, $t0, 3
+  jirl     $zero, $t0, 0
+
+TlbCsrRd:
+  li.w     $t0, LOONGARCH_CSR_TLBIDX
+  bltu     $a0, $t0, ReadSelNumErr
+  li.w     $t0, LOONGARCH_CSR_RVACFG
+  bltu     $t0, $a0, CfgCsrRd
+  la.pcrel $t0, TlbCsrRead
+  addi.w   $t1, $a0, -LOONGARCH_CSR_TLBIDX
+  alsl.d   $t0, $t1, $t0, 3
+  jirl     $zero, $t0, 0
+
+CfgCsrRd:
+  li.w     $t0, LOONGARCH_CSR_CPUNUM
+  bltu     $a0, $t0, ReadSelNumErr
+  li.w     $t0, LOONGARCH_CSR_PRCFG3
+  bltu     $t0, $a0, KcsCsrRd
+  la.pcrel $t0, CfgCsrRead
+  addi.w   $t1, $a0, -LOONGARCH_CSR_CPUNUM
+  alsl.d   $t0, $t1, $t0, 3
+  jirl     $zero, $t0, 0
+
+KcsCsrRd:
+  li.w     $t0, LOONGARCH_CSR_KS0
+  bltu     $a0, $t0, ReadSelNumErr
+  li.w     $t0, LOONGARCH_CSR_KS8
+  bltu     $t0, $a0, StableTimerCsrRd
+  la.pcrel $t0, KcsCsrRead
+  addi.w   $t1, $a0, -LOONGARCH_CSR_KS0
+  alsl.d   $t0, $t1, $t0, 3
+  jirl     $zero, $t0, 0
+
+StableTimerCsrRd:
+  li.w     $t0, LOONGARCH_CSR_TMID
+  bltu     $a0, $t0, ReadSelNumErr
+  li.w     $t0, LOONGARCH_CSR_TINTCLR
+  bltu     $t0, $a0, TlbRefillCsrRd
+  la.pcrel $t0, StableTimerCsrRead
+  addi.w   $t1, $a0, -LOONGARCH_CSR_TMID
+  alsl.d   $t0, $t1, $t0, 3
+  jirl     $zero, $t0, 0
+
+TlbRefillCsrRd:
+  li.w     $t0, LOONGARCH_CSR_TLBREBASE
+  bltu     $a0, $t0, ReadSelNumErr
+  li.w     $t0, LOONGARCH_CSR_TLBREHI
+  bltu     $t0, $a0, DirMapCsrRd
+  la.pcrel $t0, TlbRefillCsrRead
+  addi.w   $t1, $a0, -LOONGARCH_CSR_TLBREBASE
+  alsl.d   $t0, $t1, $t0, 3
+  jirl     $zero, $t0, 0
+
+DirMapCsrRd:
+  li.w     $t0, LOONGARCH_CSR_DMWIN0
+  bltu     $a0, $t0, ReadSelNumErr
+  li.w     $t0, LOONGARCH_CSR_DMWIN3
+  bltu     $t0, $a0, ReadSelNumErr
+  la.pcrel $t0, DirMapCsrRead
+  addi.w   $t1, $a0, -LOONGARCH_CSR_DMWIN0
+  alsl.d   $t0, $t1, $t0, 3
+  jirl     $zero, $t0, 0
+
+ReadSelNumErr:
+  addi.d   $a0, $zero, -1
+  jirl     $zero, $ra, 0
+
+BasicCsrRead:
+  CsrSel = LOONGARCH_CSR_CRMD
+  .rept LOONGARCH_CSR_EBASE - LOONGARCH_CSR_CRMD + 1
+    AsmCsrRd CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+TlbCsrRead:
+  CsrSel = LOONGARCH_CSR_TLBIDX
+  .rept LOONGARCH_CSR_RVACFG - LOONGARCH_CSR_TLBIDX + 1
+    AsmCsrRd CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+CfgCsrRead:
+  CsrSel = LOONGARCH_CSR_CPUNUM
+  .rept LOONGARCH_CSR_PRCFG3 - LOONGARCH_CSR_CPUNUM + 1
+    AsmCsrRd CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+KcsCsrRead:
+  CsrSel = LOONGARCH_CSR_KS0
+  .rept LOONGARCH_CSR_KS8 - LOONGARCH_CSR_KS0 + 1
+    AsmCsrRd CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+StableTimerCsrRead:
+  CsrSel = LOONGARCH_CSR_TMID
+  .rept LOONGARCH_CSR_TINTCLR - LOONGARCH_CSR_TMID + 1
+    AsmCsrRd CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+TlbRefillCsrRead:
+  CsrSel = LOONGARCH_CSR_TLBREBASE
+  .rept LOONGARCH_CSR_TLBREHI - LOONGARCH_CSR_TLBREBASE + 1
+    AsmCsrRd CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+DirMapCsrRead:
+  CsrSel = LOONGARCH_CSR_DMWIN0
+  .rept LOONGARCH_CSR_DMWIN3 - LOONGARCH_CSR_DMWIN0 + 1
+    AsmCsrRd CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+ASM_PFX(AsmCsrWrite):
+  blt      $a0, $zero, WriteSelNumErr
+  li.w     $t0, LOONGARCH_CSR_EBASE
+  bltu     $t0, $a0, TlbCsrWr
+
+BasicCsrWr:
+  la.pcrel $t0, BasicCsrWrite
+  alsl.d   $t0, $a0, $t0, 3
+  move     $a0, $a1
+  jirl     $zero, $t0, 0
+
+TlbCsrWr:
+  li.w     $t0, LOONGARCH_CSR_TLBIDX
+  bltu     $a0, $t0, WriteSelNumErr
+  li.w     $t0, LOONGARCH_CSR_RVACFG
+  bltu     $t0, $a0, CfgCsrWr
+  la.pcrel $t0, TlbCsrWrite
+  addi.w   $t1, $a0, -LOONGARCH_CSR_TLBIDX
+  alsl.d   $t0, $t1, $t0, 3
+  move     $a0, $a1
+  jirl     $zero, $t0, 0
+
+CfgCsrWr:
+  li.w     $t0, LOONGARCH_CSR_CPUNUM
+  bltu     $a0, $t0, WriteSelNumErr
+  li.w     $t0, LOONGARCH_CSR_PRCFG3
+  bltu     $t0, $a0, KcsCsrWr
+  la.pcrel $t0, CfgCsrWrite
+  addi.w   $t1, $a0, -LOONGARCH_CSR_CPUNUM
+  alsl.d   $t0, $t1, $t0, 3
+  move     $a0, $a1
+  jirl     $zero, $t0, 0
+
+KcsCsrWr:
+  li.w     $t0, LOONGARCH_CSR_KS0
+  bltu     $a0, $t0, WriteSelNumErr
+  li.w     $t0, LOONGARCH_CSR_KS8
+  bltu     $t0, $a0, StableTimerCsrWr
+  la.pcrel $t0, KcsCsrWrite
+  addi.w   $t1, $a0, -LOONGARCH_CSR_KS0
+  alsl.d   $t0, $t1, $t0, 3
+  move     $a0, $a1
+  jirl     $zero, $t0, 0
+
+StableTimerCsrWr:
+  li.w     $t0, LOONGARCH_CSR_TMID
+  bltu     $a0, $t0, WriteSelNumErr
+  li.w     $t0, LOONGARCH_CSR_TINTCLR
+  bltu     $t0, $a0, TlbRefillCsrWr
+  la.pcrel $t0, StableTimerCsrWrite
+  addi.w   $t1, $a0, -LOONGARCH_CSR_TMID
+  alsl.d   $t0, $t1, $t0, 3
+  move     $a0, $a1
+  jirl     $zero, $t0, 0
+
+TlbRefillCsrWr:
+  li.w     $t0, LOONGARCH_CSR_TLBREBASE
+  bltu     $a0, $t0, WriteSelNumErr
+  li.w     $t0, LOONGARCH_CSR_TLBREHI
+  bltu     $t0, $a0, DirMapCsrWr
+  la.pcrel $t0, TlbRefillCsrWrite
+  addi.w   $t1, $a0, -LOONGARCH_CSR_TLBREBASE
+  alsl.d   $t0, $t1, $t0, 3
+  move     $a0, $a1
+  jirl     $zero, $t0, 0
+
+DirMapCsrWr:
+  li.w     $t0, LOONGARCH_CSR_DMWIN0
+  bltu     $a0, $t0, WriteSelNumErr
+  li.w     $t0, LOONGARCH_CSR_DMWIN3
+  bltu     $t0, $a0, WriteSelNumErr
+  la.pcrel $t0, DirMapCsrWrite
+  addi.w   $t1, $a0, -LOONGARCH_CSR_DMWIN0
+  alsl.d   $t0, $t1, $t0, 3
+  move     $a0, $a1
+  jirl     $zero, $t0, 0
+
+WriteSelNumErr:
+  addi.d   $a0, $zero, -1
+  jirl     $zero, $ra, 0
+
+BasicCsrWrite:
+  CsrSel = LOONGARCH_CSR_CRMD
+  .rept LOONGARCH_CSR_EBASE - LOONGARCH_CSR_CRMD + 1
+    AsmCsrWr CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+TlbCsrWrite:
+  CsrSel = LOONGARCH_CSR_TLBIDX
+  .rept LOONGARCH_CSR_RVACFG - LOONGARCH_CSR_TLBIDX + 1
+    AsmCsrWr CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+CfgCsrWrite:
+  CsrSel = LOONGARCH_CSR_CPUNUM
+  .rept LOONGARCH_CSR_PRCFG3 - LOONGARCH_CSR_CPUNUM + 1
+    AsmCsrWr CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+KcsCsrWrite:
+  CsrSel = LOONGARCH_CSR_KS0
+  .rept LOONGARCH_CSR_KS8 - LOONGARCH_CSR_KS0 + 1
+    AsmCsrWr CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+StableTimerCsrWrite:
+  CsrSel = LOONGARCH_CSR_TMID
+  .rept LOONGARCH_CSR_TINTCLR - LOONGARCH_CSR_TMID + 1
+    AsmCsrWr CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+TlbRefillCsrWrite:
+  CsrSel = LOONGARCH_CSR_TLBREBASE
+  .rept LOONGARCH_CSR_TLBREHI - LOONGARCH_CSR_TLBREBASE + 1
+    AsmCsrWr CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+DirMapCsrWrite:
+  CsrSel = LOONGARCH_CSR_DMWIN0
+  .rept LOONGARCH_CSR_DMWIN3 - LOONGARCH_CSR_DMWIN0 + 1
+    AsmCsrWr CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+
+ASM_PFX(AsmCsrXChg):
+  blt      $a0, $zero, XchgSelNumErr
+  li.w     $t0, LOONGARCH_CSR_EBASE
+  bltu     $t0, $a0, TlbCsrXchg
+
+BasicCsrXchg:
+  la.pcrel $t0, BasicCsrXchange
+  alsl.d   $t0, $a0, $t0, 3
+  move     $a0, $a1
+  move     $a1, $a2
+  jirl     $zero, $t0, 0
+
+TlbCsrXchg:
+  li.w     $t0, LOONGARCH_CSR_TLBIDX
+  bltu     $a0, $t0, XchgSelNumErr
+  li.w     $t0, LOONGARCH_CSR_RVACFG
+  bltu     $t0, $a0, CfgCsrXchg
+  la.pcrel $t0, TlbCsrXchange
+  addi.w   $t1, $a0, -LOONGARCH_CSR_TLBIDX
+  alsl.d   $t0, $t1, $t0, 3
+  move     $a0, $a1
+  move     $a1, $a2
+  jirl     $zero, $t0, 0
+
+CfgCsrXchg:
+  li.w     $t0, LOONGARCH_CSR_CPUNUM
+  bltu     $a0, $t0, XchgSelNumErr
+  li.w     $t0, LOONGARCH_CSR_PRCFG3
+  bltu     $t0, $a0, KcsCsrXchg
+  la.pcrel $t0, CfgCsrXchange
+  addi.w   $t1, $a0, -LOONGARCH_CSR_CPUNUM
+  alsl.d   $t0, $t1, $t0, 3
+  move     $a0, $a1
+  move     $a1, $a2
+  jirl     $zero, $t0, 0
+
+KcsCsrXchg:
+  li.w     $t0, LOONGARCH_CSR_KS0
+  bltu     $a0, $t0, XchgSelNumErr
+  li.w     $t0, LOONGARCH_CSR_KS8
+  bltu     $t0, $a0, StableTimerCsrXchg
+  la.pcrel $t0, KcsCsrXchange
+  addi.w   $t1, $a0, -LOONGARCH_CSR_KS0
+  alsl.d   $t0, $t1, $t0, 3
+  move     $a0, $a1
+  move     $a1, $a2
+  jirl     $zero, $t0, 0
+
+StableTimerCsrXchg:
+  li.w     $t0, LOONGARCH_CSR_TMID
+  bltu     $a0, $t0, XchgSelNumErr
+  li.w     $t0, LOONGARCH_CSR_TINTCLR
+  bltu     $t0, $a0, TlbRefillCsrXchg
+  la.pcrel $t0, StableTimerCsrXchange
+  addi.w   $t1, $a0, -LOONGARCH_CSR_TMID
+  alsl.d   $t0, $t1, $t0, 3
+  move     $a0, $a1
+  move     $a1, $a2
+  jirl     $zero, $t0, 0
+
+TlbRefillCsrXchg:
+  li.w     $t0, LOONGARCH_CSR_TLBREBASE
+  bltu     $a0, $t0, XchgSelNumErr
+  li.w     $t0, LOONGARCH_CSR_TLBREHI
+  bltu     $t0, $a0, DirMapCsrXchg
+  la.pcrel $t0, TlbRefillCsrXchange
+  addi.w   $t1, $a0, -LOONGARCH_CSR_TLBREBASE
+  alsl.d   $t0, $t1, $t0, 3
+  move     $a0, $a1
+  move     $a1, $a2
+  jirl     $zero, $t0, 0
+
+DirMapCsrXchg:
+  li.w     $t0, LOONGARCH_CSR_DMWIN0
+  bltu     $a0, $t0, XchgSelNumErr
+  li.w     $t0, LOONGARCH_CSR_DMWIN3
+  bltu     $t0, $a0, XchgSelNumErr
+  la.pcrel $t0, DirMapCsrXchange
+  addi.w   $t1, $a0, -LOONGARCH_CSR_DMWIN0
+  alsl.d   $t0, $t1, $t0, 3
+  move     $a0, $a1
+  move     $a1, $a2
+  jirl     $zero, $t0, 0
+
+XchgSelNumErr:
+  addi.d   $a0, $zero, -1
+  jirl     $zero, $ra, 0
+
+BasicCsrXchange:
+  CsrSel = LOONGARCH_CSR_CRMD
+  .rept LOONGARCH_CSR_EBASE - LOONGARCH_CSR_CRMD + 1
+    AsmCsrXChange CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+TlbCsrXchange:
+  CsrSel = LOONGARCH_CSR_TLBIDX
+  .rept LOONGARCH_CSR_RVACFG - LOONGARCH_CSR_TLBIDX + 1
+    AsmCsrXChange CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+CfgCsrXchange:
+  CsrSel = LOONGARCH_CSR_CPUNUM
+  .rept LOONGARCH_CSR_PRCFG3 - LOONGARCH_CSR_CPUNUM + 1
+    AsmCsrXChange CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+KcsCsrXchange:
+  CsrSel = LOONGARCH_CSR_KS0
+  .rept LOONGARCH_CSR_KS8 - LOONGARCH_CSR_KS0 + 1
+    AsmCsrXChange CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+StableTimerCsrXchange:
+  CsrSel = LOONGARCH_CSR_TMID
+  .rept LOONGARCH_CSR_TINTCLR - LOONGARCH_CSR_TMID + 1
+    AsmCsrXChange CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+TlbRefillCsrXchange:
+  CsrSel = LOONGARCH_CSR_TLBREBASE
+  .rept LOONGARCH_CSR_TLBREHI - LOONGARCH_CSR_TLBREBASE + 1
+    AsmCsrXChange CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+
+DirMapCsrXchange:
+  CsrSel = LOONGARCH_CSR_DMWIN0
+  .rept LOONGARCH_CSR_DMWIN3 - LOONGARCH_CSR_DMWIN0 + 1
+    AsmCsrXChange CsrSel
+    CsrSel = CsrSel + 1
+  .endr
+.end
diff --git a/MdePkg/Library/BaseLib/LoongArch64/Csr.c b/MdePkg/Library/BaseLib/LoongArch64/Csr.c
new file mode 100644
index 0000000000..f2ec80b38d
--- /dev/null
+++ b/MdePkg/Library/BaseLib/LoongArch64/Csr.c
@@ -0,0 +1,81 @@
+/** @file
+  LoongArch CSR operation functions.
+
+  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+UINTN
+AsmCsrRead (
+  IN UINT16  Select
+  );
+
+UINTN
+AsmCsrWrite (
+  IN UINT16  Select,
+  IN UINTN   Value
+  );
+
+UINTN
+AsmCsrXChg (
+  IN UINT16  Select,
+  IN UINTN   Value,
+  IN UINTN   Mask
+  );
+
+/**
+  CSR read operation.
+
+  @param[in]  Select   CSR read instruction select values.
+
+  @return     The return value of csrrd instruction, return -1 means Select is out of support.
+**/
+UINTN
+EFIAPI
+CsrRead (
+  IN UINT16  Select
+  )
+{
+  return AsmCsrRead (Select);
+}
+
+/**
+  CSR write operation.
+
+  @param[in]       Select  CSR write instruction select values.
+  @param[in, out]  Value   The csrwr will write the value.
+
+  @return     The return value of csrwr instruction, that is, store the old value of
+              the register, return -1 means Select is out of support.
+**/
+UINTN
+EFIAPI
+CsrWrite (
+  IN     UINT16  Select,
+  IN OUT UINTN   Value
+  )
+{
+  return AsmCsrWrite (Select, Value);
+}
+
+/**
+  CSR exchange operation.
+
+  @param[in]       Select   CSR exchange instruction select values.
+  @param[in, out]  Value    The csrxchg will write the value.
+  @param[in]       Mask     The csrxchg mask value.
+
+  @return     The return value of csrxchg instruction, that is, store the old value of
+              the register, return -1 means Select is out of support.
+**/
+UINTN
+EFIAPI
+CsrXChg (
+  IN     UINT16  Select,
+  IN OUT UINTN   Value,
+  IN     UINTN   Mask
+  )
+{
+  return AsmCsrXChg (Select, Value, Mask);
+}
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114533): https://edk2.groups.io/g/devel/message/114533
Mute This Topic: https://groups.io/mt/103971645/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 08/37] MdePkg: Add IOCSR operation for LoongArch
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (6 preceding siblings ...)
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 07/37] MdePkg: Add CSR " Chao Li
@ 2024-01-26  6:28 ` Chao Li
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 09/37] MdePkg: Add a new library named PeiServicesTablePointerLibKs0 Chao Li
                   ` (33 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:28 UTC (permalink / raw)
  To: devel; +Cc: Michael D Kinney, Liming Gao, Zhiguang Liu

Add IoCsrRead8, IoCsrRead16, IoCsrRead32, IoCsrRead64, IoCsrWrite8,
IoCsrWrite16, IoCsrWrite32, IoCsrWrite64 to operate the IOCSR registers
of LoongArch architecture.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Acked-by: Michael D Kinney <michael.d.kinney@intel.com>
Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
---
 MdePkg/Include/Library/BaseLib.h           | 112 +++++++++++++++++++
 MdePkg/Library/BaseLib/BaseLib.inf         |   1 +
 MdePkg/Library/BaseLib/LoongArch64/IoCsr.S | 120 +++++++++++++++++++++
 3 files changed, 233 insertions(+)
 create mode 100644 MdePkg/Library/BaseLib/LoongArch64/IoCsr.S

diff --git a/MdePkg/Include/Library/BaseLib.h b/MdePkg/Include/Library/BaseLib.h
index 4e97368ae2..1fff0fb224 100644
--- a/MdePkg/Include/Library/BaseLib.h
+++ b/MdePkg/Include/Library/BaseLib.h
@@ -396,6 +396,118 @@ CsrXChg (
   IN UINTN   Mask
   );
 
+/**
+  IO CSR read byte operation.
+
+  @param[in]  Select   IO CSR read instruction select values.
+
+  @return     The return value of iocsrrd.b instruction.
+
+**/
+UINT8
+IoCsrRead8 (
+  IN UINTN  Select
+  );
+
+/**
+  IO CSR read half word operation.
+
+  @param[in]  Select   IO CSR read instruction select values.
+
+  @return     The return value of iocsrrd.h instruction.
+
+**/
+UINT16
+IoCsrRead16 (
+  IN UINTN  Select
+  );
+
+/**
+  IO CSR read word operation.
+
+  @param[in]  Select   IO CSR read instruction select values.
+
+  @return     The return value of iocsrrd.w instruction.
+
+**/
+UINT32
+IoCsrRead32 (
+  IN UINTN  Select
+  );
+
+/**
+  IO CSR read double word operation. Only for LoongArch64.
+
+  @param[in]  Select   IO CSR read instruction select values.
+
+  @return     The return value of iocsrrd.d instruction.
+
+**/
+UINT64
+IoCsrRead64 (
+  IN UINTN  Select
+  );
+
+/**
+  IO CSR write byte operation.
+
+  @param[in]  Select   IO CSR write instruction select values.
+  @param[in]  Value    The iocsrwr.b will write the value.
+
+  @return     VOID.
+
+**/
+VOID
+IoCsrWrite8 (
+  IN UINTN  Select,
+  IN UINT8  Value
+  );
+
+/**
+  IO CSR write half word operation.
+
+  @param[in]  Select   IO CSR write instruction select values.
+  @param[in]  Value    The iocsrwr.h will write the value.
+
+  @return     VOID.
+
+**/
+VOID
+IoCsrWrite16 (
+  IN UINTN   Select,
+  IN UINT16  Value
+  );
+
+/**
+  IO CSR write word operation.
+
+  @param[in]  Select   IO CSR write instruction select values.
+  @param[in]  Value    The iocsrwr.w will write the value.
+
+  @return     VOID.
+
+**/
+VOID
+IoCsrWrite32 (
+  IN UINTN   Select,
+  IN UINT32  Value
+  );
+
+/**
+  IO CSR write double word operation. Only for LoongArch64.
+
+  @param[in]  Select   IO CSR write instruction select values.
+  @param[in]  Value    The iocsrwr.d will write the value.
+
+  @return     VOID.
+
+**/
+VOID
+IoCsrWrite64 (
+  IN UINTN   Select,
+  IN UINT64  Value
+  );
+
 #endif // defined (MDE_CPU_LOONGARCH64)
 
 //
diff --git a/MdePkg/Library/BaseLib/BaseLib.inf b/MdePkg/Library/BaseLib/BaseLib.inf
index 7c46306883..4dbe94be71 100644
--- a/MdePkg/Library/BaseLib/BaseLib.inf
+++ b/MdePkg/Library/BaseLib/BaseLib.inf
@@ -413,6 +413,7 @@
   LoongArch64/Csr.c
   LoongArch64/InternalSwitchStack.c
   LoongArch64/AsmCsr.S              | GCC
+  LoongArch64/IoCsr.S               | GCC
   LoongArch64/GetInterruptState.S   | GCC
   LoongArch64/EnableInterrupts.S    | GCC
   LoongArch64/DisableInterrupts.S   | GCC
diff --git a/MdePkg/Library/BaseLib/LoongArch64/IoCsr.S b/MdePkg/Library/BaseLib/LoongArch64/IoCsr.S
new file mode 100644
index 0000000000..a659908bc4
--- /dev/null
+++ b/MdePkg/Library/BaseLib/LoongArch64/IoCsr.S
@@ -0,0 +1,120 @@
+#------------------------------------------------------------------------------
+#
+# LoongArch ASM IO CSR operation functions
+#
+# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#------------------------------------------------------------------------------
+
+ASM_GLOBAL ASM_PFX (IoCsrRead8)
+ASM_GLOBAL ASM_PFX (IoCsrRead16)
+ASM_GLOBAL ASM_PFX (IoCsrRead32)
+ASM_GLOBAL ASM_PFX (IoCsrRead64)
+
+ASM_GLOBAL ASM_PFX (IoCsrWrite8)
+ASM_GLOBAL ASM_PFX (IoCsrWrite16)
+ASM_GLOBAL ASM_PFX (IoCsrWrite32)
+ASM_GLOBAL ASM_PFX (IoCsrWrite64)
+
+#/**
+#  IO CSR read byte operation.
+#
+#  @param[in]  Select   IO CSR read instruction select values.
+#
+#  @return     The return value of iocsrrd.b instruction.
+#
+#**/
+ASM_PFX (IoCsrRead8):
+  iocsrrd.b   $a0, $a0
+  jirl        $zero, $ra, 0
+
+#/**
+#  IO CSR read half word operation.
+#
+#  @param[in]  Select   IO CSR read instruction select values.
+#
+#  @return     The return value of iocsrrd.h instruction.
+#
+#**/
+ASM_PFX (IoCsrRead16):
+  iocsrrd.h   $a0, $a0
+  jirl        $zero, $ra, 0
+
+#/**
+#  IO CSR read word operation.
+#
+#  @param[in]  Select   IO CSR read instruction select values.
+#
+#  @return     The return value of iocsrrd.w instruction.
+#
+#**/
+ASM_PFX (IoCsrRead32):
+  iocsrrd.w   $a0, $a0
+  jirl        $zero, $ra, 0
+
+#/**
+#  IO CSR read double word operation. Only for LoongArch64.
+#
+#  @param[in]  Select   IO CSR read instruction select values.
+#
+#  @return     The return value of iocsrrd.d instruction.
+#
+#**/
+ASM_PFX (IoCsrRead64):
+  iocsrrd.d   $a0, $a0
+  jirl        $zero, $ra, 0
+
+#/**
+#  IO CSR write byte operation.
+#
+#  @param[in]  Select   IO CSR write instruction select values.
+#  @param[in]  Value    The iocsrwr.b will write the value.
+#
+#  @return     VOID.
+#
+#**/
+ASM_PFX (IoCsrWrite8):
+  iocsrwr.b   $a1, $a0
+  jirl        $zero, $ra, 0
+
+#/**
+#  IO CSR write half word operation.
+#
+#  @param[in]  Select   IO CSR write instruction select values.
+#  @param[in]  Value    The iocsrwr.h will write the value.
+#
+#  @return     VOID.
+#
+#**/
+ASM_PFX (IoCsrWrite16):
+  iocsrwr.h   $a1, $a0
+  jirl        $zero, $ra, 0
+
+#/**
+#  IO CSR write word operation.
+#
+#  @param[in]  Select   IO CSR write instruction select values.
+#  @param[in]  Value    The iocsrwr.w will write the value.
+#
+#  @return     VOID.
+#
+#**/
+ASM_PFX (IoCsrWrite32):
+  iocsrwr.w   $a1, $a0
+  jirl        $zero, $ra, 0
+
+#/**
+#  IO CSR write double word operation. Only for LoongArch64.
+#
+#  @param[in]  Select   IO CSR write instruction select values.
+#  @param[in]  Value    The iocsrwr.d will write the value.
+#
+#  @return     VOID.
+#
+#**/
+ASM_PFX (IoCsrWrite64):
+  iocsrwr.d   $a1, $a0
+  jirl        $zero, $ra, 0
+  .end
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114534): https://edk2.groups.io/g/devel/message/114534
Mute This Topic: https://groups.io/mt/103971647/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 09/37] MdePkg: Add a new library named PeiServicesTablePointerLibKs0
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (7 preceding siblings ...)
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 08/37] MdePkg: Add IOCSR " Chao Li
@ 2024-01-26  6:28 ` Chao Li
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 10/37] MdePkg: Add some comments for LoongArch exceptions Chao Li
                   ` (32 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:28 UTC (permalink / raw)
  To: devel; +Cc: Michael D Kinney, Liming Gao, Zhiguang Liu, Laszlo Ersek

Adding PeiServicesTablePointerLibKs0 for LoongArch64, which provides
setting and getting the PEI service table pointer through the CSR KS0
register.

The idea of this library is derived from
ArmPkg/Library/PeiServicesTablePointerLib/

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
---
 .../Library/PeiServicesTablePointerLib.h      |  9 +-
 .../PeiServicesTablePointer.c                 | 87 +++++++++++++++++++
 .../PeiServicesTablePointerLibKs0.inf         | 37 ++++++++
 .../PeiServicesTablePointerLibKs0.uni         | 20 +++++
 MdePkg/MdePkg.dsc                             |  3 +
 5 files changed, 152 insertions(+), 4 deletions(-)
 create mode 100644 MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointer.c
 create mode 100644 MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointerLibKs0.inf
 create mode 100644 MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointerLibKs0.uni

diff --git a/MdePkg/Include/Library/PeiServicesTablePointerLib.h b/MdePkg/Include/Library/PeiServicesTablePointerLib.h
index 61635eff00..f85c38363c 100644
--- a/MdePkg/Include/Library/PeiServicesTablePointerLib.h
+++ b/MdePkg/Include/Library/PeiServicesTablePointerLib.h
@@ -52,10 +52,11 @@ SetPeiServicesTablePointer (
   immediately preceding the Interrupt Descriptor Table (IDT) in memory.
   For X64 CPUs, the PEI Services Table pointer is stored in the 8 bytes
   immediately preceding the Interrupt Descriptor Table (IDT) in memory.
-  For Itanium and ARM CPUs, a the PEI Services Table Pointer is stored in
-  a dedicated CPU register.  This means that there is no memory storage
-  associated with storing the PEI Services Table pointer, so no additional
-  migration actions are required for Itanium or ARM CPUs.
+  For Itanium, ARM and LoongArch CPUs, a the PEI Services Table Pointer
+  is stored in a dedicated CPU register.  This means that there is no
+  memory storage associated with storing the PEI Services Table pointer,
+  so no additional migration actions are required for Itanium, ARM and
+  LoongArch CPUs.
 
 **/
 VOID
diff --git a/MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointer.c b/MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointer.c
new file mode 100644
index 0000000000..f9800936b2
--- /dev/null
+++ b/MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointer.c
@@ -0,0 +1,87 @@
+/** @file
+  PEI Services Table Pointer Library For Reigseter Mechanism.
+
+  This library is used for PEIM which does executed from flash device directly but
+  executed in memory.
+
+  Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+  Copyright (c) 2011 Hewlett-Packard Corporation. All rights reserved.<BR>
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiPei.h>
+#include <Library/DebugLib.h>
+#include <Library/PeiServicesTablePointerLib.h>
+#include <Register/LoongArch64/Csr.h>
+
+/**
+  Caches a pointer PEI Services Table.
+
+  Caches the pointer to the PEI Services Table specified by PeiServicesTablePointer
+  in a platform specific manner.
+
+  If PeiServicesTablePointer is NULL, then ASSERT().
+
+  @param    PeiServicesTablePointer   The address of PeiServices pointer.
+**/
+VOID
+EFIAPI
+SetPeiServicesTablePointer (
+  IN CONST EFI_PEI_SERVICES  **PeiServicesTablePointer
+  )
+{
+  ASSERT (PeiServicesTablePointer != NULL);
+  CsrWrite (LOONGARCH_CSR_KS0, (UINTN)PeiServicesTablePointer);
+}
+
+/**
+  Retrieves the cached value of the PEI Services Table pointer.
+
+  Returns the cached value of the PEI Services Table pointer in a CPU specific manner
+  as specified in the CPU binding section of the Platform Initialization Pre-EFI
+  Initialization Core Interface Specification.
+
+  If the cached PEI Services Table pointer is NULL, then ASSERT().
+
+  @return  The pointer to PeiServices.
+
+**/
+CONST EFI_PEI_SERVICES **
+EFIAPI
+GetPeiServicesTablePointer (
+  VOID
+  )
+{
+  CONST EFI_PEI_SERVICES  **PeiServices;
+
+  PeiServices = (CONST EFI_PEI_SERVICES **)(CsrRead (LOONGARCH_CSR_KS0));
+  ASSERT (PeiServices != NULL);
+  return PeiServices;
+}
+
+/**
+  Perform CPU specific actions required to migrate the PEI Services Table
+  pointer from temporary RAM to permanent RAM.
+
+  For IA32 CPUs, the PEI Services Table pointer is stored in the 4 bytes
+  immediately preceding the Interrupt Descriptor Table (IDT) in memory.
+  For X64 CPUs, the PEI Services Table pointer is stored in the 8 bytes
+  immediately preceding the Interrupt Descriptor Table (IDT) in memory.
+  For Itanium, ARM and LoongArch CPUs, a the PEI Services Table Pointer
+  is stored in a dedicated CPU register.  This means that there is no
+  memory storage associated with storing the PEI Services Table pointer,
+  so no additional migration actions are required for Itanium, ARM and
+  LoongArch CPUs.
+
+**/
+VOID
+EFIAPI
+MigratePeiServicesTablePointer (
+  VOID
+  )
+{
+  return;
+}
diff --git a/MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointerLibKs0.inf b/MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointerLibKs0.inf
new file mode 100644
index 0000000000..513f62517d
--- /dev/null
+++ b/MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointerLibKs0.inf
@@ -0,0 +1,37 @@
+## @file
+# Instance of PEI Services Table Pointer Library using register CSR KS0 for the table pointer.
+#
+# PEI Services Table Pointer Library implementation that retrieves a pointer to the
+# PEI Services Table from a CPU register. Applies to modules that execute from
+# read-only memory.
+#
+# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2011 Hewlett-Packard Corporation. All rights reserved.<BR>
+# 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                      = PeiServicesTablePointerLib
+  MODULE_UNI_FILE                = PeiServicesTablePointerLibKs0.uni
+  FILE_GUID                      = 619950D1-7C5F-EA1B-D6DD-2FF7B0A4A2B7
+  MODULE_TYPE                    = PEIM
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = PeiServicesTablePointerLib|PEIM PEI_CORE SEC
+
+#
+#  VALID_ARCHITECTURES           = LOONGARCH64
+#
+
+[Sources]
+  PeiServicesTablePointer.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  DebugLib
diff --git a/MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointerLibKs0.uni b/MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointerLibKs0.uni
new file mode 100644
index 0000000000..a1db86b0b7
--- /dev/null
+++ b/MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointerLibKs0.uni
@@ -0,0 +1,20 @@
+// /** @file
+// Instance of PEI Services Table Pointer Library using register CSR KS0 for the table pointer.
+//
+// PEI Services Table Pointer Library implementation that retrieves a pointer to the
+// PEI Services Table from a CPU register. Applies to modules that execute from
+// read-only memory.
+//
+// Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+// Copyright (c) 2011 Hewlett-Packard Corporation. All rights reserved.<BR>
+// 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 "Instance of PEI Services Table Pointer Library using CPU register for the table pointer"
+
+#string STR_MODULE_DESCRIPTION          #language en-US "The PEI Services Table Pointer Library implementation that retrieves a pointer to the PEI Services Table from a CPU register. Applies to modules that execute from read-only memory."
+
diff --git a/MdePkg/MdePkg.dsc b/MdePkg/MdePkg.dsc
index 3abd1a1e23..109224c527 100644
--- a/MdePkg/MdePkg.dsc
+++ b/MdePkg/MdePkg.dsc
@@ -200,4 +200,7 @@
   MdePkg/Library/BaseSerialPortLibRiscVSbiLib/BaseSerialPortLibRiscVSbiLib.inf
   MdePkg/Library/BaseSerialPortLibRiscVSbiLib/BaseSerialPortLibRiscVSbiLibRam.inf
 
+[Components.LOONGARCH64]
+  MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointerLibKs0.inf
+
 [BuildOptions]
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114535): https://edk2.groups.io/g/devel/message/114535
Mute This Topic: https://groups.io/mt/103971648/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 10/37] MdePkg: Add some comments for LoongArch exceptions
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (8 preceding siblings ...)
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 09/37] MdePkg: Add a new library named PeiServicesTablePointerLibKs0 Chao Li
@ 2024-01-26  6:28 ` Chao Li
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 11/37] UefiCpuPkg: Add LoongArch64 CPU Timer instance Chao Li
                   ` (31 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:28 UTC (permalink / raw)
  To: devel; +Cc: Michael D Kinney, Liming Gao, Zhiguang Liu

Added some comments for registing LoongArch exceptions.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Michael D Kinney <michael.d.kinney@intel.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Zhiguang Liu <zhiguang.liu@intel.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn>
---
 MdePkg/Include/Protocol/DebugSupport.h | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/MdePkg/Include/Protocol/DebugSupport.h b/MdePkg/Include/Protocol/DebugSupport.h
index 9742663619..06f99ba7f7 100644
--- a/MdePkg/Include/Protocol/DebugSupport.h
+++ b/MdePkg/Include/Protocol/DebugSupport.h
@@ -683,6 +683,20 @@ typedef struct {
 //
 // LoongArch processor exception types.
 //
+// The exception types is located in the CSR ESTAT
+// register offset 16 bits, width 6 bits.
+//
+// If you want to register an exception hook, you can
+// shfit the number left by 16 bits, and the exception
+// handler will know the types.
+//
+// For example:
+// mCpu->CpuRegisterInterruptHandler (
+//         mCpu,
+//         (EXCEPT_LOONGARCH_PPI << CSR_ESTAT_EXC_SHIFT),
+//         PpiExceptionHandler
+//         );
+//
 #define EXCEPT_LOONGARCH_INT   0
 #define EXCEPT_LOONGARCH_PIL   1
 #define EXCEPT_LOONGARCH_PIS   2
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114536): https://edk2.groups.io/g/devel/message/114536
Mute This Topic: https://groups.io/mt/103971649/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 11/37] UefiCpuPkg: Add LoongArch64 CPU Timer instance
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (9 preceding siblings ...)
  2024-01-26  6:28 ` [edk2-devel] [PATCH v8 10/37] MdePkg: Add some comments for LoongArch exceptions Chao Li
@ 2024-01-26  6:29 ` Chao Li
  2024-02-02  3:24   ` Ni, Ray
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 12/37] UefiCpuPkg: Add CPU exception library for LoongArch Chao Li
                   ` (30 subsequent siblings)
  41 siblings, 1 reply; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:29 UTC (permalink / raw)
  To: devel; +Cc: Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann

Add the LoongArch64 CPU Timer instance to CpuTimerLib, using CPUCFG 0x4
and 0x5 for Stable Counter frequency.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
---
 .../Library/CpuTimerLib/BaseCpuTimerLib.inf   |   9 +-
 .../CpuTimerLib/LoongArch64/CpuTimerLib.c     | 251 ++++++++++++++++++
 2 files changed, 258 insertions(+), 2 deletions(-)
 create mode 100644 UefiCpuPkg/Library/CpuTimerLib/LoongArch64/CpuTimerLib.c

diff --git a/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf b/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf
index de0648de91..7e6152ef7e 100644
--- a/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf
+++ b/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf
@@ -5,6 +5,7 @@
 #  counter features are provided by the processors time stamp counter.
 #
 #  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
+#  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
 #  SPDX-License-Identifier: BSD-2-Clause-Patent
 #
 ##
@@ -18,18 +19,22 @@
   LIBRARY_CLASS                  = TimerLib
   MODULE_UNI_FILE                = BaseCpuTimerLib.uni
 
-[Sources]
+[Sources.IA32, Sources.X64]
   CpuTimerLib.c
   BaseCpuTimerLib.c
 
+[Sources.LOONGARCH64]
+  LoongArch64/CpuTimerLib.c
+
 [Packages]
   MdePkg/MdePkg.dec
   UefiCpuPkg/UefiCpuPkg.dec
 
 [LibraryClasses]
   BaseLib
-  PcdLib
   DebugLib
+  PcdLib
+  SafeIntLib
 
 [Pcd]
   gUefiCpuPkgTokenSpaceGuid.PcdCpuCoreCrystalClockFrequency  ## CONSUMES
diff --git a/UefiCpuPkg/Library/CpuTimerLib/LoongArch64/CpuTimerLib.c b/UefiCpuPkg/Library/CpuTimerLib/LoongArch64/CpuTimerLib.c
new file mode 100644
index 0000000000..a5ae8d0185
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuTimerLib/LoongArch64/CpuTimerLib.c
@@ -0,0 +1,251 @@
+/** @file
+  CPUCFG 0x4 and 0x5 for Stable Counter frequency instance of Timer Library.
+
+  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Base.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/SafeIntLib.h>
+#include <Library/TimerLib.h>
+#include <Register/LoongArch64/Cpucfg.h>
+
+/**
+  Calculate clock frequency using CPUCFG 0x4 and 0x5 registers.
+
+  @param  VOID.
+
+  @return The frequency in Hz.
+
+**/
+STATIC
+UINT64
+CalcConstFreq (
+  VOID
+  )
+{
+  UINT32                 BaseFreq;
+  UINT64                 ClockMultiplier;
+  UINT32                 ClockDivide;
+  CPUCFG_REG4_INFO_DATA  CcFreq;
+  CPUCFG_REG5_INFO_DATA  CpucfgReg5Data;
+  UINT64                 StableTimerFreq;
+
+  //
+  // Get the the crystal frequency corresponding to the constant
+  // frequency timer and the clock used by the timer.
+  //
+  AsmCpucfg (CPUCFG_REG4_INFO, &CcFreq.Uint32);
+
+  //
+  // Get the multiplication factor and frequency division factor
+  // corresponding to the constant frequency timer and the clock
+  // used by the timer.
+  //
+  AsmCpucfg (CPUCFG_REG5_INFO, &CpucfgReg5Data.Uint32);
+
+  BaseFreq        = CcFreq.Bits.CC_FREQ;
+  ClockMultiplier = CpucfgReg5Data.Bits.CC_MUL & 0xFFFF;
+  ClockDivide     = CpucfgReg5Data.Bits.CC_DIV & 0xFFFF;
+
+  if ((BaseFreq == 0x0) || (ClockMultiplier == 0x0) || (ClockDivide == 0x0)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "LoongArch Stable Timer is not available in the CPU, hence this library cannot be used.\n"
+      ));
+    ASSERT (FALSE);
+    CpuDeadLoop ();
+  }
+
+  StableTimerFreq = ((ClockMultiplier * BaseFreq) / ClockDivide);
+
+  if (StableTimerFreq == 0x0) {
+    ASSERT (FALSE);
+  }
+
+  return StableTimerFreq;
+}
+
+/**
+  Stalls the CPU for at least the given number of microseconds.
+
+  Stalls the CPU for the number of microseconds specified by MicroSeconds.
+
+  @param  MicroSeconds  The minimum number of microseconds to delay.
+
+  @return MicroSeconds
+
+**/
+UINTN
+EFIAPI
+MicroSecondDelay (
+  IN UINTN  MicroSeconds
+  )
+{
+  UINT64         CurrentTicks, ExceptedTicks, Remaining;
+  RETURN_STATUS  Status;
+
+  Status = SafeUint64Mult (MicroSeconds, CalcConstFreq (), &Remaining);
+  ASSERT_RETURN_ERROR (Status);
+
+  ExceptedTicks  = DivU64x32 (Remaining, 1000000U);
+  CurrentTicks   = AsmReadStableCounter ();
+  ExceptedTicks += CurrentTicks;
+
+  do {
+    CurrentTicks = AsmReadStableCounter ();
+  } while (CurrentTicks < ExceptedTicks);
+
+  return MicroSeconds;
+}
+
+/**
+  Stalls the CPU for at least the given number of nanoseconds.
+
+  Stalls the CPU for the number of nanoseconds specified by NanoSeconds.
+
+  @param  NanoSeconds The minimum number of nanoseconds to delay.
+
+  @return NanoSeconds
+
+**/
+UINTN
+EFIAPI
+NanoSecondDelay (
+  IN UINTN  NanoSeconds
+  )
+{
+  UINTN  MicroSeconds;
+
+  // Round up to 1us Tick Number
+  MicroSeconds  = NanoSeconds / 1000;
+  MicroSeconds += ((NanoSeconds % 1000) == 0) ? 0 : 1;
+
+  MicroSecondDelay (MicroSeconds);
+
+  return NanoSeconds;
+}
+
+/**
+  Retrieves the current value of a 64-bit free running Stable Counter.
+
+  The LoongArch defines a constant frequency timer, whose main body is a
+  64-bit counter called StableCounter. StableCounter is set to 0 after
+  reset, and then increments by 1 every counting clock cycle. When the
+  count reaches all 1s, it automatically wraps around to 0 and continues
+  to increment.
+  The properties of the Stable Counter can be retrieved from
+  GetPerformanceCounterProperties().
+
+  @return The current value of the Stable Counter.
+
+**/
+UINT64
+EFIAPI
+GetPerformanceCounter (
+  VOID
+  )
+{
+  //
+  // Just return the value of Stable Counter.
+  //
+  return AsmReadStableCounter ();
+}
+
+/**
+  Retrieves the 64-bit frequency in Hz and the range of Stable Counter
+  values.
+
+  If StartValue is not NULL, then the value that the stbale counter starts
+  with immediately after is it rolls over is returned in StartValue. If
+  EndValue is not NULL, then the value that the stable counter end with
+  immediately before it rolls over is returned in EndValue. The 64-bit
+  frequency of the system frequency in Hz is always returned.
+
+  @param  StartValue  The value the stable counter starts with when it
+                      rolls over.
+  @param  EndValue    The value that the stable counter ends with before
+                      it rolls over.
+
+  @return The frequency in Hz.
+
+**/
+UINT64
+EFIAPI
+GetPerformanceCounterProperties (
+  OUT UINT64  *StartValue   OPTIONAL,
+  OUT UINT64  *EndValue     OPTIONAL
+  )
+{
+  if (StartValue != NULL) {
+    *StartValue = 0;
+  }
+
+  if (EndValue != NULL) {
+    *EndValue = 0xFFFFFFFFFFFFFFFFULL;
+  }
+
+  return CalcConstFreq ();
+}
+
+/**
+  Converts elapsed ticks of performance counter to time in nanoseconds.
+
+  This function converts the elapsed ticks of running performance counter to
+  time value in unit of nanoseconds.
+
+  @param  Ticks     The number of elapsed ticks of running performance counter.
+
+  @return The elapsed time in nanoseconds.
+
+**/
+UINT64
+EFIAPI
+GetTimeInNanoSecond (
+  IN UINT64  Ticks
+  )
+{
+  UINT64         Frequency;
+  UINT64         NanoSeconds;
+  UINT64         Remainder;
+  INTN           Shift;
+  RETURN_STATUS  Status;
+
+  Frequency = GetPerformanceCounterProperties (NULL, NULL);
+
+  //
+  //          Ticks
+  // Time = --------- x 1,000,000,000
+  //        Frequency
+  //
+  Status = SafeUint64Mult (
+             DivU64x64Remainder (Ticks, Frequency, &Remainder),
+             1000000000u,
+             &NanoSeconds
+             );
+
+  //
+  // Ensure (Remainder * 1,000,000,000) will not overflow 64-bit.
+  // Since 2^29 < 1,000,000,000 = 0x3B9ACA00 < 2^30, Remainder should < 2^(64-30) = 2^34,
+  // i.e. highest bit set in Remainder should <= 33.
+  //
+  Shift     = MAX (0, HighBitSet64 (Remainder) - 33);
+  Remainder = RShiftU64 (Remainder, (UINTN)Shift);
+  Frequency = RShiftU64 (Frequency, (UINTN)Shift);
+
+  Status = SafeUint64Add (
+             NanoSeconds,
+             DivU64x64Remainder (
+               MultU64x32 (Remainder, 1000000000u),
+               Frequency,
+               NULL
+               ),
+             &NanoSeconds
+             );
+  ASSERT_RETURN_ERROR (Status);
+
+  return NanoSeconds;
+}
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114537): https://edk2.groups.io/g/devel/message/114537
Mute This Topic: https://groups.io/mt/103971650/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 12/37] UefiCpuPkg: Add CPU exception library for LoongArch
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (10 preceding siblings ...)
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 11/37] UefiCpuPkg: Add LoongArch64 CPU Timer instance Chao Li
@ 2024-01-26  6:29 ` Chao Li
  2024-02-02  3:30   ` Ni, Ray
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 13/37] UefiCpuPkg: Add CpuMmuLib.h to UefiCpuPkg Chao Li
                   ` (29 subsequent siblings)
  41 siblings, 1 reply; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:29 UTC (permalink / raw)
  To: devel; +Cc: Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann, Baoqi Zhang

Added LoongArch exception handler into CpuExceptionHandlerLib.

Adjust the file order in INF of CpuExceptionHandlerLib with alphabetical
order.

Adjust files order in CpuExceptionHandlerLib INF in alphabetical order.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Co-authored-by: Baoqi Zhang <zhangbaoqi@loongson.cn>
---
 .../DxeCpuExceptionHandlerLib.inf             |  34 +-
 .../LoongArch/DxeExceptionLib.c               | 198 ++++++++++
 .../LoongArch/ExceptionCommon.c               | 171 ++++++++
 .../LoongArch/ExceptionCommon.h               | 131 +++++++
 .../LoongArch64/ArchExceptionHandler.c        | 268 +++++++++++++
 .../LoongArch64/ExceptionHandlerAsm.S         | 366 ++++++++++++++++++
 .../LoongArch/SecPeiExceptionLib.c            | 102 +++++
 .../SecPeiCpuExceptionHandlerLib.inf          |  29 +-
 UefiCpuPkg/UefiCpuPkg.dec                     |   5 +
 9 files changed, 1283 insertions(+), 21 deletions(-)
 create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/DxeExceptionLib.c
 create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.c
 create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.h
 create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ArchExceptionHandler.c
 create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ExceptionHandlerAsm.S
 create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/SecPeiExceptionLib.c

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
index fdbebadab9..f5bacbe2bc 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
@@ -18,25 +18,32 @@
 #
 # The following information is for reference only and not required by the build tools.
 #
-#  VALID_ARCHITECTURES           = IA32 X64
+#  VALID_ARCHITECTURES           = IA32 X64 LOONGARCH64
 #
 
 [Sources.Ia32]
-  Ia32/ExceptionHandlerAsm.nasm
-  Ia32/ExceptionTssEntryAsm.nasm
   Ia32/ArchExceptionHandler.c
   Ia32/ArchInterruptDefs.h
+  Ia32/ExceptionHandlerAsm.nasm
+  Ia32/ExceptionTssEntryAsm.nasm
 
 [Sources.X64]
-  X64/ExceptionHandlerAsm.nasm
   X64/ArchExceptionHandler.c
   X64/ArchInterruptDefs.h
+  X64/ExceptionHandlerAsm.nasm
 
-[Sources.common]
+[Sources.Ia32, Sources.X64]
   CpuExceptionCommon.h
   CpuExceptionCommon.c
-  PeiDxeSmmCpuException.c
   DxeException.c
+  PeiDxeSmmCpuException.c
+
+[Sources.LoongArch64]
+  LoongArch/DxeExceptionLib.c
+  LoongArch/ExceptionCommon.h
+  LoongArch/ExceptionCommon.c
+  LoongArch/LoongArch64/ArchExceptionHandler.c
+  LoongArch/LoongArch64/ExceptionHandlerAsm.S | GCC
 
 [Pcd]
   gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard
@@ -51,16 +58,19 @@
   MdeModulePkg/MdeModulePkg.dec
   UefiCpuPkg/UefiCpuPkg.dec
 
-[LibraryClasses]
+[LibraryClasses.common]
   BaseLib
-  SerialPortLib
+  CpuLib
+  DebugLib
+  MemoryAllocationLib
+  PeCoffGetEntryPointLib
   PrintLib
+  SerialPortLib
   SynchronizationLib
-  LocalApicLib
-  PeCoffGetEntryPointLib
-  MemoryAllocationLib
-  DebugLib
+
+[LibraryClasses.Ia32, LibraryClasses.X64]
   CcExitLib
+  LocalApicLib
 
 [BuildOptions]
   XCODE:*_*_X64_NASM_FLAGS = -D NO_ABSOLUTE_RELOCS_IN_TEXT
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/DxeExceptionLib.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/DxeExceptionLib.c
new file mode 100644
index 0000000000..2c5d202b33
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/DxeExceptionLib.c
@@ -0,0 +1,198 @@
+/** @file DxeExceptionLib.c
+
+  LoongArch exception library implemenation for DXE modules.
+
+  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/CpuExceptionHandlerLib.h>
+#include <Library/CpuLib.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/DebugLib.h>
+#include <Library/SerialPortLib.h>
+#include <Protocol/DebugSupport.h>
+#include <Register/LoongArch64/Csr.h>
+
+#include "ExceptionCommon.h"
+
+EFI_EXCEPTION_CALLBACK  ExternalInterruptHandler[MAX_LOONGARCH_INTERRUPT + 1] = { 0 };
+EFI_EXCEPTION_CALLBACK  ExceptionHandler[MAX_LOONGARCH_EXCEPTION + 1]         = { 0 };
+
+/**
+  Registers a function to be called from the processor interrupt or exception handler.
+
+  This function registers and enables the handler specified by InterruptHandler for a processor
+  interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
+  handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
+  The installed handler is called once for each processor interrupt or exception.
+
+  @param  InterruptType    A pointer to the processor's current interrupt state. Set to TRUE if interrupts
+                           are enabled and FALSE if interrupts are disabled.
+  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
+                           when a processor interrupt occurs. If this parameter is NULL, then the handler
+                           will be uninstalled.
+
+  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
+  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was
+                                previously installed.
+  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
+                                previously installed.
+  @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
+
+**/
+EFI_STATUS
+RegisterCpuInterruptHandler (
+  IN EFI_EXCEPTION_TYPE         InterruptType,
+  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
+  )
+{
+  EFI_EXCEPTION_TYPE  ExceptionType;
+
+  ExceptionType = InterruptType & CSR_ESTAT_EXC;
+
+  if (ExceptionType != 0) {
+    //
+    // Exception
+    //
+    if (ExceptionType > EXCEPT_LOONGARCH_FPE) {
+      return EFI_UNSUPPORTED;
+    }
+
+    ExceptionType >>= CSR_ESTAT_EXC_SHIFT;
+
+    if ((InterruptHandler == NULL) && (ExceptionHandler[InterruptType] == NULL)) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    if ((InterruptHandler != NULL) && (ExceptionHandler[ExceptionType] != NULL)) {
+      return EFI_ALREADY_STARTED;
+    }
+
+    ExceptionHandler[ExceptionType] = InterruptHandler;
+  } else {
+    //
+    // Interrupt
+    //
+    if (InterruptType > MAX_LOONGARCH_INTERRUPT) {
+      return EFI_UNSUPPORTED;
+    }
+
+    if ((InterruptHandler == NULL) && (ExternalInterruptHandler[InterruptType] == NULL)) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    if ((InterruptHandler != NULL) && (ExternalInterruptHandler[InterruptType] != NULL)) {
+      return EFI_ALREADY_STARTED;
+    }
+
+    ExternalInterruptHandler[InterruptType] = InterruptHandler;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Common exception handler.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+
+**/
+VOID
+EFIAPI
+CommonExceptionHandler (
+  IN     EFI_EXCEPTION_TYPE  ExceptionType,
+  IN OUT EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  EFI_EXCEPTION_TYPE  InterruptType;
+
+  if (ExceptionType == EXCEPT_LOONGARCH_INT) {
+    //
+    // Interrupt
+    //
+    InterruptType = GetInterruptType (SystemContext);
+    if (InterruptType == 0xFF) {
+      ExceptionType = InterruptType;
+    } else {
+      if ((ExternalInterruptHandler != NULL) && (ExternalInterruptHandler[InterruptType] != NULL)) {
+        ExternalInterruptHandler[InterruptType](InterruptType, SystemContext);
+        return;
+      }
+    }
+  } else if (ExceptionType == EXCEPT_LOONGARCH_FPD) {
+    EnableFloatingPointUnits ();
+    InitializeFloatingPointUnits ();
+    return;
+  } else {
+    //
+    // Exception
+    //
+    ExceptionType >>= CSR_ESTAT_EXC_SHIFT;
+    if ((ExceptionHandler != NULL) && (ExceptionHandler[ExceptionType] != NULL)) {
+      ExceptionHandler[ExceptionType](ExceptionType, SystemContext);
+      return;
+    }
+  }
+
+  //
+  // Only the TLB refill exception use the same entry point as normal exceptions.
+  //
+  if (CsrRead (LOONGARCH_CSR_TLBRERA) & 0x1) {
+    ExceptionType = mExceptionKnownNameNum - 1; // Use only to dump the exception context.
+  }
+
+  DefaultExceptionHandler (ExceptionType, SystemContext);
+}
+
+/**
+  Initializes all CPU exceptions entries and provides the default exception handlers.
+
+  Caller should try to get an array of interrupt and/or exception vectors that are in use and need to
+  persist by EFI_VECTOR_HANDOFF_INFO defined in PI 1.3 specification.
+  If caller cannot get reserved vector list or it does not exists, set VectorInfo to NULL.
+  If VectorInfo is not NULL, the exception vectors will be initialized per vector attribute accordingly.
+
+  @param[in]  VectorInfo    Pointer to reserved vector list.
+
+  @retval EFI_SUCCESS           CPU Exception Entries have been successfully initialized
+                                with default exception handlers.
+  @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL.
+  @retval EFI_UNSUPPORTED       This function is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeCpuExceptionHandlers (
+  IN EFI_VECTOR_HANDOFF_INFO  *VectorInfo OPTIONAL
+  )
+{
+  return EFI_SUCCESS;
+}
+
+/**
+  Setup separate stacks for certain exception handlers.
+  If the input Buffer and BufferSize are both NULL, use global variable if possible.
+
+  @param[in]       Buffer        Point to buffer used to separate exception stack.
+  @param[in, out]  BufferSize    On input, it indicates the byte size of Buffer.
+                                 If the size is not enough, the return status will
+                                 be EFI_BUFFER_TOO_SMALL, and output BufferSize
+                                 will be the size it needs.
+
+  @retval EFI_SUCCESS             The stacks are assigned successfully.
+  @retval EFI_UNSUPPORTED         This function is not supported.
+  @retval EFI_BUFFER_TOO_SMALL    This BufferSize is too small.
+**/
+EFI_STATUS
+EFIAPI
+InitializeSeparateExceptionStacks (
+  IN     VOID   *Buffer,
+  IN OUT UINTN  *BufferSize
+  )
+{
+  return EFI_SUCCESS;
+}
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.c
new file mode 100644
index 0000000000..801c8393e8
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.c
@@ -0,0 +1,171 @@
+/** @file DxeExceptionLib.c
+
+  CPU Exception Handler Library common functions.
+
+  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/PeCoffGetEntryPointLib.h>
+#include <Library/PrintLib.h>
+#include <Library/SerialPortLib.h>
+#include <Register/LoongArch64/Csr.h>
+#include "ExceptionCommon.h"
+
+CONST CHAR8  mExceptionReservedStr[] = "Reserved";
+CONST CHAR8  *mExceptionNameStr[]    = {
+  "#INT - Interrupt(CSR.ECFG.VS=0)",
+  "#PIL - Page invalid exception for Load option",
+  "#PIS - Page invalid exception for Store operation",
+  "#PIF - Page invalid exception for Fetch operation",
+  "#PME - Page modification exception",
+  "#PNR - Page non-readable exception",
+  "#PNX - Page non-executable exception",
+  "#PPI - Page privilege level illegal exception",
+  "#ADE - Address error exception",
+  "#ALE - Address alignment fault exception",
+  "#BCE - Bound check exception",
+  "#SYS - System call exception",
+  "#BRK - Beeakpoint exception",
+  "#INE - Instruction non-defined exception",
+  "#IPE - Instruction privilege error exception",
+  "#FPD - Floating-point instruction disable exception",
+  "#SXD - 128-bit vector (SIMD instructions) expansion instruction disable exception",
+  "#ASXD - 256-bit vector (Advanced SIMD instructions) expansion instruction disable exception",
+  "#FPE - Floating-Point error exception",
+  "#WPE - WatchPoint Exception for Fetch watchpoint or Memory load/store watchpoint",
+  "#BTD - Binary Translation expansion instruction Disable exception",
+  "#BTE - Binary Translation related exceptions",
+  "#GSPR - Guest Sensitive Privileged Resource exception",
+  "#HVC - HyperVisor Call exception",
+  "#GCXC - Guest CSR Software/Hardware Change exception",
+  "#TBR - TLB refill exception" // !!! NOTICE: Because the TLB refill exception is not instructed in ECODE, so the TLB refill exception must be the last one!
+};
+
+INTN  mExceptionKnownNameNum = (sizeof (mExceptionNameStr) / sizeof (CHAR8 *));
+
+/**
+  Get ASCII format string exception name by exception type.
+
+  @param ExceptionType  Exception type.
+
+  @return  ASCII format string exception name.
+
+**/
+CONST CHAR8 *
+GetExceptionNameStr (
+  IN EFI_EXCEPTION_TYPE  ExceptionType
+  )
+{
+  if ((UINTN)ExceptionType < mExceptionKnownNameNum) {
+    return mExceptionNameStr[ExceptionType];
+  } else {
+    return mExceptionReservedStr;
+  }
+}
+
+/**
+  Prints a message to the serial port.
+
+  @param  Format      Format string for the message to print.
+  @param  ...         Variable argument list whose contents are accessed
+                      based on the format string specified by Format.
+
+**/
+VOID
+EFIAPI
+InternalPrintMessage (
+  IN  CONST CHAR8  *Format,
+  ...
+  )
+{
+  CHAR8    Buffer[MAX_DEBUG_MESSAGE_LENGTH];
+  VA_LIST  Marker;
+
+  //
+  // Convert the message to an ASCII String
+  //
+  VA_START (Marker, Format);
+  AsciiVSPrint (Buffer, sizeof (Buffer), Format, Marker);
+  VA_END (Marker);
+
+  //
+  // Send the print string to a Serial Port
+  //
+  SerialPortWrite ((UINT8 *)Buffer, AsciiStrLen (Buffer));
+}
+
+/**
+  Find and display image base address and return image base and its entry point.
+
+  @param CurrentEra      Current instruction pointer.
+
+**/
+VOID
+DumpModuleImageInfo (
+  IN  UINTN  CurrentEra
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       Pe32Data;
+  VOID        *PdbPointer;
+  VOID        *EntryPoint;
+
+  Pe32Data = PeCoffSearchImageBase (CurrentEra);
+  if (Pe32Data == 0) {
+    InternalPrintMessage ("!!!! Can't find image information. !!!!\n");
+  } else {
+    //
+    // Find Image Base entry point
+    //
+    Status = PeCoffLoaderGetEntryPoint ((VOID *)Pe32Data, &EntryPoint);
+    if (EFI_ERROR (Status)) {
+      EntryPoint = NULL;
+    }
+
+    InternalPrintMessage ("!!!! Find image based on IP(0x%x) ", CurrentEra);
+    PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)Pe32Data);
+    if (PdbPointer != NULL) {
+      InternalPrintMessage ("%a", PdbPointer);
+    } else {
+      InternalPrintMessage ("(No PDB) ");
+    }
+
+    InternalPrintMessage (
+      " (ImageBase=%016lp, EntryPoint=%016p) !!!!\n",
+      (VOID *)Pe32Data,
+      EntryPoint
+      );
+  }
+}
+
+/**
+  Default exception handler.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+
+**/
+VOID
+EFIAPI
+DefaultExceptionHandler (
+  IN     EFI_EXCEPTION_TYPE  ExceptionType,
+  IN OUT EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  //
+  // Initialize the serial port before dumping.
+  //
+  SerialPortInitialize ();
+  //
+  // Display ExceptionType, CPU information and Image information
+  //
+  DumpImageAndCpuContent (ExceptionType, SystemContext);
+
+  //
+  // Enter a dead loop.
+  //
+  CpuDeadLoop ();
+}
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.h b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.h
new file mode 100644
index 0000000000..e326b73e3f
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.h
@@ -0,0 +1,131 @@
+/** @file DxeExceptionLib.h
+
+  Common header file for CPU Exception Handler Library.
+
+  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef EXCEPTION_COMMON_H_
+#define EXCEPTION_COMMON_H_
+
+#define MAX_DEBUG_MESSAGE_LENGTH  0x100
+
+//
+// For coding convenience, define the maximum valid
+// LoongArch exception.
+// Since UEFI V2.11, it will be present in DebugSupport.h.
+//
+#define MAX_LOONGARCH_EXCEPTION  64
+
+extern INTN  mExceptionKnownNameNum;
+
+/**
+  Get ASCII format string exception name by exception type.
+
+  @param[in] ExceptionType  Exception type.
+
+  @return    ASCII format string exception name.
+
+**/
+CONST CHAR8 *
+GetExceptionNameStr (
+  IN EFI_EXCEPTION_TYPE  ExceptionType
+  );
+
+/**
+  Prints a message to the serial port.
+
+  @param[in]  Format      Format string for the message to print.
+  @param[in]  ...         Variable argument list whose contents are accessed
+                      based on the format string specified by Format.
+
+**/
+VOID
+EFIAPI
+InternalPrintMessage (
+  IN  CONST CHAR8  *Format,
+  ...
+  );
+
+/**
+  Find and display image base address and return image base and its entry point.
+
+  @param[in] CurrentEip      Current instruction pointer.
+
+**/
+VOID
+DumpModuleImageInfo (
+  IN UINTN  CurrentEip
+  );
+
+/**
+  IPI Interrupt Handler.
+
+  @param InterruptType    The type of interrupt that occurred
+  @param SystemContext    A pointer to the system context when the interrupt occurred
+**/
+VOID
+EFIAPI
+IpiInterruptHandler (
+  IN EFI_EXCEPTION_TYPE  InterruptType,
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  );
+
+/**
+  Default exception handler.
+
+  @param[in] ExceptionType  Exception type.
+  @param[in] SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+
+**/
+VOID
+EFIAPI
+DefaultExceptionHandler (
+  IN     EFI_EXCEPTION_TYPE  ExceptionType,
+  IN OUT EFI_SYSTEM_CONTEXT  SystemContext
+  );
+
+/**
+  Display CPU information.
+
+  @param[in] ExceptionType  Exception type.
+  @param[in] SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+
+**/
+VOID
+DumpImageAndCpuContent (
+  IN EFI_EXCEPTION_TYPE  ExceptionType,
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  );
+
+/**
+  Get exception types
+
+  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+
+  @return     Exception type.
+
+**/
+EFI_EXCEPTION_TYPE
+EFIAPI
+GetExceptionType (
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  );
+
+/**
+  Get Common interrupt types
+
+  @param[in] SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+
+  @return    Interrupt type.
+
+**/
+EFI_EXCEPTION_TYPE
+EFIAPI
+GetInterruptType (
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  );
+
+#endif
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ArchExceptionHandler.c
new file mode 100644
index 0000000000..c0219deba5
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ArchExceptionHandler.c
@@ -0,0 +1,268 @@
+/** @file ArchExceptionHandler.c
+
+  LoongArch64 CPU Exception Handler.
+
+  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h>
+#include <Register/LoongArch64/Csr.h>
+#include "ExceptionCommon.h"
+
+/**
+  Get Exception Type
+
+  @param[in] SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+
+  @return    LoongArch64 exception type.
+
+**/
+EFI_EXCEPTION_TYPE
+EFIAPI
+GetExceptionType (
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  EFI_EXCEPTION_TYPE  ExceptionType;
+
+  ExceptionType = (SystemContext.SystemContextLoongArch64->ESTAT & CSR_ESTAT_EXC);
+  return ExceptionType;
+}
+
+/**
+  Get Interrupt Type
+
+  @param[in] SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+
+  @return    LoongArch64 intrrupt type.
+
+**/
+EFI_EXCEPTION_TYPE
+EFIAPI
+GetInterruptType (
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  EFI_EXCEPTION_TYPE  InterruptType;
+
+  for (InterruptType = 0; InterruptType <= EXCEPT_LOONGARCH_INT_IPI; InterruptType++) {
+    if (SystemContext.SystemContextLoongArch64->ESTAT & (1 << InterruptType)) {
+      //
+      // 0  - EXCEPT_LOONGARCH_INT_SIP0
+      // 1  - EXCEPT_LOONGARCH_INT_SIP1
+      // 2  - EXCEPT_LOONGARCH_INT_IP0
+      // 3  - EXCEPT_LOONGARCH_INT_IP1
+      // 4  - EXCEPT_LOONGARCH_INT_IP2
+      // 5  - EXCEPT_LOONGARCH_INT_IP3
+      // 6  - EXCEPT_LOONGARCH_INT_IP4
+      // 7  - EXCEPT_LOONGARCH_INT_IP5
+      // 8  - EXCEPT_LOONGARCH_INT_IP6
+      // 9  - EXCEPT_LOONGARCH_INT_IP7
+      // 10 - EXCEPT_LOONGARCH_INT_PMC
+      // 11 - EXCEPT_LOONGARCH_INT_TIMER
+      // 12 - EXCEPT_LOONGARCH_INT_IPI
+      // Greater than EXCEPT_LOONGARCH_INI_IPI is currently invalid.
+      //
+      return InterruptType;
+    }
+  }
+
+  //
+  // Invalid IRQ
+  //
+  return 0xFF;
+}
+
+/**
+  Display CPU information.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+
+**/
+VOID
+EFIAPI
+DumpCpuContext (
+  IN EFI_EXCEPTION_TYPE  ExceptionType,
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  InternalPrintMessage (
+    "\n!!!! LoongArch64 Exception Type - %02x(%a) !!!!\n",
+    ExceptionType,
+    GetExceptionNameStr (ExceptionType)
+    );
+
+  //
+  // Dump TLB refill ERA and BADV
+  //
+  if (ExceptionType == (mExceptionKnownNameNum - 1)) {
+    InternalPrintMessage ("TLB refill ERA  0x%llx\n", (CsrRead (LOONGARCH_CSR_TLBRERA) & (~0x3ULL)));
+    InternalPrintMessage ("TLB refill BADV  0x%llx\n", CsrRead (LOONGARCH_CSR_TLBRBADV));
+  }
+
+  //
+  // Dump the general registers
+  //
+  InternalPrintMessage (
+    "Zero  - 0x%016lx, RA  - 0x%016lx, TP - 0x%016lx, SP - 0x%016lx\n",
+    SystemContext.SystemContextLoongArch64->R0,
+    SystemContext.SystemContextLoongArch64->R1,
+    SystemContext.SystemContextLoongArch64->R2,
+    SystemContext.SystemContextLoongArch64->R3
+    );
+  InternalPrintMessage (
+    "  A0  - 0x%016lx, A1  - 0x%016lx, A2 - 0x%016lx, A3 - 0x%016lx\n",
+    SystemContext.SystemContextLoongArch64->R4,
+    SystemContext.SystemContextLoongArch64->R5,
+    SystemContext.SystemContextLoongArch64->R6,
+    SystemContext.SystemContextLoongArch64->R7
+    );
+  InternalPrintMessage (
+    "  A4  - 0x%016lx, A5  - 0x%016lx, A6 - 0x%016lx, A7 - 0x%016lx\n",
+    SystemContext.SystemContextLoongArch64->R8,
+    SystemContext.SystemContextLoongArch64->R9,
+    SystemContext.SystemContextLoongArch64->R10,
+    SystemContext.SystemContextLoongArch64->R11
+    );
+  InternalPrintMessage (
+    "  T0  - 0x%016lx, T1  - 0x%016lx, T2 - 0x%016lx, T3 - 0x%016lx\n",
+    SystemContext.SystemContextLoongArch64->R12,
+    SystemContext.SystemContextLoongArch64->R13,
+    SystemContext.SystemContextLoongArch64->R14,
+    SystemContext.SystemContextLoongArch64->R15
+    );
+  InternalPrintMessage (
+    "  T4  - 0x%016lx, T5  - 0x%016lx, T6 - 0x%016lx, T7 - 0x%016lx\n",
+    SystemContext.SystemContextLoongArch64->R16,
+    SystemContext.SystemContextLoongArch64->R17,
+    SystemContext.SystemContextLoongArch64->R18,
+    SystemContext.SystemContextLoongArch64->R19
+    );
+  InternalPrintMessage (
+    "  T8  - 0x%016lx, R21 - 0x%016lx, FP - 0x%016lx, S0 - 0x%016lx\n",
+    SystemContext.SystemContextLoongArch64->R20,
+    SystemContext.SystemContextLoongArch64->R21,
+    SystemContext.SystemContextLoongArch64->R22,
+    SystemContext.SystemContextLoongArch64->R23
+    );
+  InternalPrintMessage (
+    "  S1  - 0x%016lx, S2  - 0x%016lx, S3 - 0x%016lx, S4 - 0x%016lx\n",
+    SystemContext.SystemContextLoongArch64->R24,
+    SystemContext.SystemContextLoongArch64->R25,
+    SystemContext.SystemContextLoongArch64->R26,
+    SystemContext.SystemContextLoongArch64->R27
+    );
+  InternalPrintMessage (
+    "  S5  - 0x%016lx, S6  - 0x%016lx, S7 - 0x%016lx, S8 - 0x%016lx\n",
+    SystemContext.SystemContextLoongArch64->R28,
+    SystemContext.SystemContextLoongArch64->R29,
+    SystemContext.SystemContextLoongArch64->R30,
+    SystemContext.SystemContextLoongArch64->R31
+    );
+  InternalPrintMessage ("\n");
+
+  //
+  // Dump the CSR registers
+  //
+  InternalPrintMessage (
+    "CRMD  - 0x%016lx, PRMD  - 0x%016lx, EUEN - 0x%016lx, MISC - 0x%016lx\n",
+    SystemContext.SystemContextLoongArch64->CRMD,
+    SystemContext.SystemContextLoongArch64->PRMD,
+    SystemContext.SystemContextLoongArch64->EUEN,
+    SystemContext.SystemContextLoongArch64->MISC
+    );
+  InternalPrintMessage (
+    "ECFG  - 0x%016lx, ESTAT - 0x%016lx, ERA  - 0x%016lx, BADV - 0x%016lx\n",
+    SystemContext.SystemContextLoongArch64->ECFG,
+    SystemContext.SystemContextLoongArch64->ESTAT,
+    SystemContext.SystemContextLoongArch64->ERA,
+    SystemContext.SystemContextLoongArch64->BADV
+    );
+  InternalPrintMessage (
+    "BADI  - 0x%016lx\n",
+    SystemContext.SystemContextLoongArch64->BADI
+    );
+}
+
+/**
+  Display CPU information.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+
+**/
+VOID
+DumpImageAndCpuContent (
+  IN EFI_EXCEPTION_TYPE  ExceptionType,
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  DumpCpuContext (ExceptionType, SystemContext);
+
+  if (ExceptionType == (mExceptionKnownNameNum - 1)) {
+    //
+    // Dump TLB refill image info
+    //
+    DumpModuleImageInfo ((CsrRead (LOONGARCH_CSR_TLBRERA) & (~0x3ULL)));
+  } else {
+    DumpModuleImageInfo (SystemContext.SystemContextLoongArch64->ERA);
+  }
+}
+
+/**
+  IPI Interrupt Handler.
+
+  @param InterruptType    The type of interrupt that occurred
+  @param SystemContext    A pointer to the system context when the interrupt occurred
+**/
+VOID
+EFIAPI
+IpiInterruptHandler (
+  IN EFI_EXCEPTION_TYPE  InterruptType,
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  UINTN  ResumeVector;
+  UINTN  Parameter;
+
+  //
+  // Clear interrupt.
+  //
+  IoCsrWrite32 (LOONGARCH_IOCSR_IPI_CLEAR, IoCsrRead32 (LOONGARCH_IOCSR_IPI_STATUS));
+
+  //
+  // Get the resume vector and parameter if populated.
+  //
+  ResumeVector = IoCsrRead64 (LOONGARCH_IOCSR_MBUF0);
+  Parameter    = IoCsrRead64 (LOONGARCH_IOCSR_MBUF3);
+
+  //
+  // Clean up current processor mailbox 0 and mailbox 3.
+  //
+  IoCsrWrite64 (LOONGARCH_IOCSR_MBUF0, 0x0);
+  IoCsrWrite64 (LOONGARCH_IOCSR_MBUF3, 0x0);
+
+  //
+  // If mailbox 0 is non-NULL, it means that the BSP or other cores called the IPI to wake
+  // up the current core and let it use the resume vector stored in mailbox 0.
+  //
+  // If both the resume vector and parameter are non-NULL, it means that the IPI was
+  // called in the BIOS.
+  //
+  // The situation where the resume vector is non-NULL and the parameter is NULL has been
+  // processed after the exception entry is pushed onto the stack.
+  //
+  if ((ResumeVector != 0) && (Parameter != 0)) {
+    SystemContext.SystemContextLoongArch64->ERA = ResumeVector;
+    //
+    // Set $a0 as APIC ID and $a1 as parameter value.
+    //
+    SystemContext.SystemContextLoongArch64->R4 = CsrRead (LOONGARCH_CSR_CPUNUM);
+    SystemContext.SystemContextLoongArch64->R5 = Parameter;
+  }
+
+  MemoryFence ();
+}
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ExceptionHandlerAsm.S b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ExceptionHandlerAsm.S
new file mode 100644
index 0000000000..7c692e01c1
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ExceptionHandlerAsm.S
@@ -0,0 +1,366 @@
+#------------------------------------------------------------------------------
+#
+# LoongArch64 ASM exception handler
+#
+# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#------------------------------------------------------------------------------
+
+#include <Library/BaseLib.h>
+#include <Library/CpuLib.h>
+#include <Register/LoongArch64/Csr.h>
+
+#define RSIZE                 8           // 64 bit mode register size
+#define GP_REG_CONTEXT_SIZE   32 * RSIZE  // General-purpose registers size
+#define FP_REG_CONTEXT_SIZE   34 * RSIZE  // Floating-point registers size
+#define CSR_REG_CONTEXT_SIZE  9  * RSIZE  // CSR registers size
+
+ASM_GLOBAL ASM_PFX(ExceptionEntry)
+ASM_GLOBAL ASM_PFX(ExceptionEntryStart)
+ASM_GLOBAL ASM_PFX(ExceptionEntryEnd)
+
+ASM_PFX(ExceptionEntry):
+  move    $s0, $a0
+  bl      GetExceptionType        // Exception type stored in register a0
+  move    $a1, $s0                // SystemContxt
+  bl      CommonExceptionHandler
+
+PopContext:
+  //
+  // Not sure if interrupts are turned on during the exception handler, anyway disable interrupts here.
+  // It will be turned on when the instruction 'ertn' is executed.
+  //
+  bl      DisableInterrupts
+
+  bl      GetExceptionType        // Get current exception type, and stored in register a0
+
+  // Check whether the FPE is changed during interrupt handler, if ture restore it.
+  ld.d    $t1, $sp, (LOONGARCH_CSR_EUEN * RSIZE + GP_REG_CONTEXT_SIZE)
+  csrrd   $t0, LOONGARCH_CSR_EUEN        // Current EUEN
+  andi    $t0, $t0, CSR_EUEN_FPEN
+  andi    $t1, $t1, CSR_EUEN_FPEN
+  li.d    $t2, EXCEPT_LOONGARCH_INT
+  bne     $a0, $t2, PopRegs
+  beq     $t0, $t1, PopRegs
+  beqz    $t1, CloseFP
+  bl      EnableFloatingPointUnits
+  b       PopRegs
+
+CloseFP:
+  bl      DisableFloatingPointUnits
+
+PopRegs:
+  //
+  // Pop CSR reigsters
+  //
+  addi.d  $sp, $sp, GP_REG_CONTEXT_SIZE
+
+  ld.d    $t0, $sp, LOONGARCH_CSR_CRMD * RSIZE
+  csrwr   $t0, LOONGARCH_CSR_CRMD
+  ld.d    $t0, $sp, LOONGARCH_CSR_PRMD * RSIZE
+  csrwr   $t0, LOONGARCH_CSR_PRMD
+  ld.d    $t0, $sp, LOONGARCH_CSR_ECFG * RSIZE
+  csrwr   $t0, LOONGARCH_CSR_ECFG
+  ld.d    $t0, $sp, LOONGARCH_CSR_ERA * RSIZE
+  csrwr   $t0, LOONGARCH_CSR_ERA
+
+  addi.d  $sp, $sp, CSR_REG_CONTEXT_SIZE  // Fource change the stack pointer befor pop the FP registers.
+
+  beqz    $t1, PopGP                      // If the FPE not set, only pop the GP registers.
+
+  //
+  // Pop FP registers
+  //
+  fld.d  $fa0, $sp, 0 * RSIZE
+  fld.d  $fa1, $sp, 1 * RSIZE
+  fld.d  $fa2, $sp, 2 * RSIZE
+  fld.d  $fa3, $sp, 3 * RSIZE
+  fld.d  $fa4, $sp, 4 * RSIZE
+  fld.d  $fa5, $sp, 5 * RSIZE
+  fld.d  $fa6, $sp, 6 * RSIZE
+  fld.d  $fa7, $sp, 7 * RSIZE
+  fld.d  $ft0, $sp, 8 * RSIZE
+  fld.d  $ft1, $sp, 9 * RSIZE
+  fld.d  $ft2, $sp, 10 * RSIZE
+  fld.d  $ft3, $sp, 11 * RSIZE
+  fld.d  $ft4, $sp, 12 * RSIZE
+  fld.d  $ft5, $sp, 13 * RSIZE
+  fld.d  $ft6, $sp, 14 * RSIZE
+  fld.d  $ft7, $sp, 15 * RSIZE
+  fld.d  $ft8, $sp, 16 * RSIZE
+  fld.d  $ft9, $sp, 17 * RSIZE
+  fld.d  $ft10, $sp, 18 * RSIZE
+  fld.d  $ft11, $sp, 19 * RSIZE
+  fld.d  $ft12, $sp, 20 * RSIZE
+  fld.d  $ft13, $sp, 21 * RSIZE
+  fld.d  $ft14, $sp, 22 * RSIZE
+  fld.d  $ft15, $sp, 23 * RSIZE
+  fld.d  $fs0, $sp, 24 * RSIZE
+  fld.d  $fs1, $sp, 25 * RSIZE
+  fld.d  $fs2, $sp, 26 * RSIZE
+  fld.d  $fs3, $sp, 27 * RSIZE
+  fld.d  $fs4, $sp, 28 * RSIZE
+  fld.d  $fs5, $sp, 29 * RSIZE
+  fld.d  $fs6, $sp, 30 * RSIZE
+  fld.d  $fs7, $sp, 31 * RSIZE
+
+  ld.d        $t0, $sp, 32 * RSIZE
+  movgr2fcsr  $r0, $t0             // Pop the fcsr0 register.
+
+  //
+  // Pop the fcc0-fcc7 registers.
+  //
+  ld.d        $t0, $sp, 33 * RSIZE
+  bstrpick.d  $t1, $t0, 7, 0
+  movgr2cf    $fcc0, $t1
+  bstrpick.d  $t1, $t0, 15, 8
+  movgr2cf    $fcc1, $t1
+  bstrpick.d  $t1, $t0, 23, 16
+  movgr2cf    $fcc2, $t1
+  bstrpick.d  $t1, $t0, 31, 24
+  movgr2cf    $fcc3, $t1
+  bstrpick.d  $t1, $t0, 39, 32
+  movgr2cf    $fcc4, $t1
+  bstrpick.d  $t1, $t0, 47, 40
+  movgr2cf    $fcc5, $t1
+  bstrpick.d  $t1, $t0, 55, 48
+  movgr2cf    $fcc6, $t1
+  bstrpick.d  $t1, $t0, 63, 56
+  movgr2cf    $fcc7, $t1
+
+PopGP:
+  //
+  // Pop GP registers
+  //
+  addi.d  $sp, $sp, -(GP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE)
+  ld.d    $ra, $sp, 1 * RSIZE
+  ld.d    $tp, $sp, 2 * RSIZE
+  ld.d    $a0, $sp, 4 * RSIZE
+  ld.d    $a1, $sp, 5 * RSIZE
+  ld.d    $a2, $sp, 6 * RSIZE
+  ld.d    $a3, $sp, 7 * RSIZE
+  ld.d    $a4, $sp, 8 * RSIZE
+  ld.d    $a5, $sp, 9 * RSIZE
+  ld.d    $a6, $sp, 10 * RSIZE
+  ld.d    $a7, $sp, 11 * RSIZE
+  ld.d    $t0, $sp, 12 * RSIZE
+  ld.d    $t1, $sp, 13 * RSIZE
+  ld.d    $t2, $sp, 14 * RSIZE
+  ld.d    $t3, $sp, 15 * RSIZE
+  ld.d    $t4, $sp, 16 * RSIZE
+  ld.d    $t5, $sp, 17 * RSIZE
+  ld.d    $t6, $sp, 18 * RSIZE
+  ld.d    $t7, $sp, 19 * RSIZE
+  ld.d    $t8, $sp, 20 * RSIZE
+  ld.d    $r21, $sp, 21 * RSIZE
+  ld.d    $fp, $sp, 22 * RSIZE
+  ld.d    $s0, $sp, 23 * RSIZE
+  ld.d    $s1, $sp, 24 * RSIZE
+  ld.d    $s2, $sp, 25 * RSIZE
+  ld.d    $s3, $sp, 26 * RSIZE
+  ld.d    $s4, $sp, 27 * RSIZE
+  ld.d    $s5, $sp, 28 * RSIZE
+  ld.d    $s6, $sp, 29 * RSIZE
+  ld.d    $s7, $sp, 30 * RSIZE
+  ld.d    $s8, $sp, 31 * RSIZE
+  ld.d    $sp, $sp, 3 * RSIZE
+
+  ertn // Returen from exception.
+//
+// End of ExceptionEntry
+//
+
+ASM_PFX(ExceptionEntryStart):
+  //
+  // Store the old stack pointer in preparation for pushing the exception context onto the new stack.
+  //
+  csrwr   $sp, LOONGARCH_CSR_KS0
+
+  csrrd   $sp, LOONGARCH_CSR_KS0
+
+  //
+  // Push GP registers
+  //
+  addi.d  $sp, $sp, -(GP_REG_CONTEXT_SIZE + FP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE)
+  st.d    $zero, $sp, 0 * RSIZE
+  st.d    $ra, $sp, 1 * RSIZE
+  st.d    $tp, $sp, 2 * RSIZE
+  st.d    $a0, $sp, 4 * RSIZE
+  st.d    $a1, $sp, 5 * RSIZE
+  st.d    $a2, $sp, 6 * RSIZE
+  st.d    $a3, $sp, 7 * RSIZE
+  st.d    $a4, $sp, 8 * RSIZE
+  st.d    $a5, $sp, 9 * RSIZE
+  st.d    $a6, $sp, 10 * RSIZE
+  st.d    $a7, $sp, 11 * RSIZE
+  st.d    $t0, $sp, 12 * RSIZE
+  st.d    $t1, $sp, 13 * RSIZE
+  st.d    $t2, $sp, 14 * RSIZE
+  st.d    $t3, $sp, 15 * RSIZE
+  st.d    $t4, $sp, 16 * RSIZE
+  st.d    $t5, $sp, 17 * RSIZE
+  st.d    $t6, $sp, 18 * RSIZE
+  st.d    $t7, $sp, 19 * RSIZE
+  st.d    $t8, $sp, 20 * RSIZE
+  st.d    $r21, $sp, 21 * RSIZE
+  st.d    $fp, $sp, 22 * RSIZE
+  st.d    $s0, $sp, 23 * RSIZE
+  st.d    $s1, $sp, 24 * RSIZE
+  st.d    $s2, $sp, 25 * RSIZE
+  st.d    $s3, $sp, 26 * RSIZE
+  st.d    $s4, $sp, 27 * RSIZE
+  st.d    $s5, $sp, 28 * RSIZE
+  st.d    $s6, $sp, 29 * RSIZE
+  st.d    $s7, $sp, 30 * RSIZE
+  st.d    $s8, $sp, 31 * RSIZE
+  csrrd   $t0, LOONGARCH_CSR_KS0  // Read the old stack pointer.
+  st.d    $t0, $sp, 3 * RSIZE
+
+  //
+  // Push CSR registers
+  //
+  addi.d  $sp, $sp, GP_REG_CONTEXT_SIZE
+
+  csrrd   $t0, LOONGARCH_CSR_CRMD
+  st.d    $t0, $sp, LOONGARCH_CSR_CRMD * RSIZE
+  csrrd   $t0, LOONGARCH_CSR_PRMD
+  st.d    $t0, $sp, LOONGARCH_CSR_PRMD * RSIZE
+  csrrd   $t0, LOONGARCH_CSR_EUEN
+  st.d    $t0, $sp, LOONGARCH_CSR_EUEN * RSIZE
+  csrrd   $t0, LOONGARCH_CSR_MISC
+  st.d    $t0, $sp, LOONGARCH_CSR_MISC * RSIZE
+  csrrd   $t0, LOONGARCH_CSR_ECFG
+  st.d    $t0, $sp, LOONGARCH_CSR_ECFG * RSIZE
+  csrrd   $t0, LOONGARCH_CSR_ESTAT
+  st.d    $t0, $sp, LOONGARCH_CSR_ESTAT * RSIZE
+  csrrd   $t0, LOONGARCH_CSR_ERA
+  st.d    $t0, $sp, LOONGARCH_CSR_ERA * RSIZE
+  csrrd   $t0, LOONGARCH_CSR_BADV
+  st.d    $t0, $sp, LOONGARCH_CSR_BADV * RSIZE
+  csrrd   $t0, LOONGARCH_CSR_BADI
+  st.d    $t0, $sp, LOONGARCH_CSR_BADI * RSIZE
+
+  //
+  // Push FP registers
+  //
+  addi.d  $sp, $sp, CSR_REG_CONTEXT_SIZE
+
+  csrrd   $t0, LOONGARCH_CSR_EUEN
+  andi    $t0, $t0, CSR_EUEN_FPEN
+  beqz    $t0, PushRegDone
+
+  fst.d  $fa0, $sp, 0 * RSIZE
+  fst.d  $fa1, $sp, 1 * RSIZE
+  fst.d  $fa2, $sp, 2 * RSIZE
+  fst.d  $fa3, $sp, 3 * RSIZE
+  fst.d  $fa4, $sp, 4 * RSIZE
+  fst.d  $fa5, $sp, 5 * RSIZE
+  fst.d  $fa6, $sp, 6 * RSIZE
+  fst.d  $fa7, $sp, 7 * RSIZE
+  fst.d  $ft0, $sp, 8 * RSIZE
+  fst.d  $ft1, $sp, 9 * RSIZE
+  fst.d  $ft2, $sp, 10 * RSIZE
+  fst.d  $ft3, $sp, 11 * RSIZE
+  fst.d  $ft4, $sp, 12 * RSIZE
+  fst.d  $ft5, $sp, 13 * RSIZE
+  fst.d  $ft6, $sp, 14 * RSIZE
+  fst.d  $ft7, $sp, 15 * RSIZE
+  fst.d  $ft8, $sp, 16 * RSIZE
+  fst.d  $ft9, $sp, 17 * RSIZE
+  fst.d  $ft10, $sp, 18 * RSIZE
+  fst.d  $ft11, $sp, 19 * RSIZE
+  fst.d  $ft12, $sp, 20 * RSIZE
+  fst.d  $ft13, $sp, 21 * RSIZE
+  fst.d  $ft14, $sp, 22 * RSIZE
+  fst.d  $ft15, $sp, 23 * RSIZE
+  fst.d  $fs0, $sp, 24 * RSIZE
+  fst.d  $fs1, $sp, 25 * RSIZE
+  fst.d  $fs2, $sp, 26 * RSIZE
+  fst.d  $fs3, $sp, 27 * RSIZE
+  fst.d  $fs4, $sp, 28 * RSIZE
+  fst.d  $fs5, $sp, 29 * RSIZE
+  fst.d  $fs6, $sp, 30 * RSIZE
+  fst.d  $fs7, $sp, 31 * RSIZE
+
+  movfcsr2gr  $t3, $r0
+  st.d        $t3, $sp, 32 * RSIZE  // Push the FCSR0 register.
+
+  //
+  // Push the fcc0-fcc7 registers.
+  //
+  movcf2gr    $t3, $fcc0
+  or          $t2, $t3, $zero
+  movcf2gr    $t3, $fcc1
+  bstrins.d   $t2, $t3, 0xf, 0x8
+  movcf2gr    $t3, $fcc2
+  bstrins.d   $t2, $t3, 0x17, 0x10
+  movcf2gr    $t3, $fcc3
+  bstrins.d   $t2, $t3, 0x1f, 0x18
+  movcf2gr    $t3, $fcc4
+  bstrins.d   $t2, $t3, 0x27, 0x20
+  movcf2gr    $t3, $fcc5
+  bstrins.d   $t2, $t3, 0x2f, 0x28
+  movcf2gr    $t3, $fcc6
+  bstrins.d   $t2, $t3, 0x37, 0x30
+  movcf2gr    $t3, $fcc7
+  bstrins.d   $t2, $t3, 0x3f, 0x38
+  st.d        $t2, $sp, 33 * RSIZE
+  //
+  // Push exception context down
+  //
+
+PushRegDone:
+  //
+  // Process IPI only when mailbox3 is NULL and mailbox0 is no-NULL.
+  //
+  li.d      $t0, LOONGARCH_IOCSR_MBUF0
+  iocsrrd.d $a0, $t0
+  beqz      $a0, EntryConmmonHanlder
+
+  li.d      $t0, LOONGARCH_IOCSR_MBUF3
+  iocsrrd.d $t1, $t0
+  bnez      $t1, EntryConmmonHanlder
+
+  csrrd     $t0, LOONGARCH_CSR_ESTAT
+  srli.d    $t0, $t0, 12
+  andi      $t0, $t0, 0x1
+  beqz      $t0, EntryConmmonHanlder
+
+  //
+  // Clean up current processor mailbox 0 and mailbox 3.
+  //
+  li.d      $t0, LOONGARCH_IOCSR_MBUF0
+  iocsrwr.d $zero, $t0
+  li.d      $t0, LOONGARCH_IOCSR_MBUF3
+  iocsrwr.d $zero, $t0
+
+  //
+  // Clear IPI interrupt.
+  //
+  li.d      $t0, LOONGARCH_IOCSR_IPI_STATUS
+  iocsrrd.w $t1, $t0
+  li.d      $t0, LOONGARCH_IOCSR_IPI_CLEAR
+  iocsrwr.w $t1, $t0
+
+  //
+  // Only kernel stage BSP calls IPI without parameters. Clean up the PIE and make sure
+  // global interrupts are turned off for the current processor when jumping to the kernel.
+  //
+  csrwr     $a0, LOONGARCH_CSR_ERA         // Update ERA
+  li.w      $t0, BIT2                      // IE
+  csrxchg   $zero, $t0, LOONGARCH_CSR_PRMD // Clean PIE
+
+  //
+  // Return this exception and jump to kernel using ERA.
+  //
+  ertn
+
+EntryConmmonHanlder:
+  addi.d  $sp, $sp, -(GP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE)
+  move    $a0, $sp
+  la.abs  $ra, ExceptionEntry
+  jirl    $zero, $ra, 0
+ASM_PFX(ExceptionEntryEnd):
+.end
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/SecPeiExceptionLib.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/SecPeiExceptionLib.c
new file mode 100644
index 0000000000..7588d2050b
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/SecPeiExceptionLib.c
@@ -0,0 +1,102 @@
+/** @file SecPeiExceptionLib.c
+
+  LoongArch exception library implemenation for PEI and SEC modules.
+
+  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/CpuLib.h>
+#include <Library/CpuExceptionHandlerLib.h>
+#include <Library/DebugLib.h>
+#include <Library/SerialPortLib.h>
+#include <Protocol/DebugSupport.h>
+#include <Register/LoongArch64/Csr.h>
+
+#include "ExceptionCommon.h"
+
+/**
+  Registers a function to be called from the processor interrupt or exception handler.
+
+  Always return EFI_UNSUPPORTED in the SEC exception initialization module.
+
+  @param  InterruptType    A pointer to the processor's current interrupt state. Set to TRUE if interrupts
+                           are enabled and FALSE if interrupts are disabled.
+  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
+                           when a processor interrupt occurs. If this parameter is NULL, then the handler
+                           will be uninstalled.
+
+  @retval EFI_UNSUPPORTED  The interrupt specified by InterruptType is not supported.
+
+**/
+EFI_STATUS
+RegisterCpuInterruptHandler (
+  IN EFI_EXCEPTION_TYPE         InterruptType,
+  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Common exception handler.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+
+**/
+VOID
+EFIAPI
+CommonExceptionHandler (
+  IN     EFI_EXCEPTION_TYPE  ExceptionType,
+  IN OUT EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  EFI_EXCEPTION_TYPE  InterruptType;
+
+  if (ExceptionType == EXCEPT_LOONGARCH_INT) {
+    //
+    // Interrupt
+    //
+    InterruptType = GetInterruptType (SystemContext);
+    if (InterruptType == EXCEPT_LOONGARCH_INT_IPI) {
+      //
+      // APs may wake up via IPI IRQ during the SEC or PEI phase, clear the IPI interrupt and
+      // perform the remaining work.
+      //
+      IpiInterruptHandler (InterruptType, SystemContext);
+      return;
+    } else {
+      ExceptionType = InterruptType;
+    }
+  } else {
+    //
+    // Exception
+    //
+    ExceptionType >>= CSR_ESTAT_EXC_SHIFT;
+  }
+
+  DefaultExceptionHandler (ExceptionType, SystemContext);
+}
+
+/**
+  Initializes all CPU exceptions entries and provides the default exception handlers.
+
+  Always return EFI_SUCCESS in the SEC exception initialization module.
+
+  @param[in]  VectorInfo    Pointer to reserved vector list.
+
+  @retval EFI_SUCCESS       CPU Exception Entries have been successfully initialized
+                            with default exception handlers.
+
+**/
+EFI_STATUS
+EFIAPI
+InitializeCpuExceptionHandlers (
+  IN EFI_VECTOR_HANDOFF_INFO  *VectorInfo OPTIONAL
+  )
+{
+  return EFI_SUCCESS;
+}
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
index e7b1144f69..6bb194ea77 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
@@ -2,6 +2,7 @@
 #  CPU Exception Handler library instance for SEC/PEI modules.
 #
 #  Copyright (c) 2012 - 2022, Intel Corporation. All rights reserved.<BR>
+#  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
 #  SPDX-License-Identifier: BSD-2-Clause-Patent
 #
 ##
@@ -18,37 +19,47 @@
 #
 # The following information is for reference only and not required by the build tools.
 #
-#  VALID_ARCHITECTURES           = IA32 X64
+#  VALID_ARCHITECTURES           = IA32 X64 LOONGARCH64
 #
 
 [Sources.Ia32]
-  Ia32/ExceptionHandlerAsm.nasm
-  Ia32/ExceptionTssEntryAsm.nasm
   Ia32/ArchExceptionHandler.c
   Ia32/ArchInterruptDefs.h
+  Ia32/ExceptionHandlerAsm.nasm
+  Ia32/ExceptionTssEntryAsm.nasm
 
 [Sources.X64]
-  X64/SecPeiExceptionHandlerAsm.nasm
   X64/ArchExceptionHandler.c
   X64/ArchInterruptDefs.h
+  X64/SecPeiExceptionHandlerAsm.nasm
 
-[Sources.common]
+[Sources.Ia32, Sources.X64]
   CpuExceptionCommon.h
   CpuExceptionCommon.c
   SecPeiCpuException.c
 
+[Sources.LoongArch64]
+  LoongArch/ExceptionCommon.h
+  LoongArch/ExceptionCommon.c
+  LoongArch/SecPeiExceptionLib.c
+  LoongArch/LoongArch64/ArchExceptionHandler.c
+  LoongArch/LoongArch64/ExceptionHandlerAsm.S | GCC
+
 [Packages]
   MdePkg/MdePkg.dec
   MdeModulePkg/MdeModulePkg.dec
   UefiCpuPkg/UefiCpuPkg.dec
 
-[LibraryClasses]
+[LibraryClasses.common]
   BaseLib
-  SerialPortLib
-  PrintLib
-  LocalApicLib
+  CpuLib
   PeCoffGetEntryPointLib
+  PrintLib
+  SerialPortLib
+
+[LibraryClasses.Ia32, LibraryClasses.X64]
   CcExitLib
+  LocalApicLib
 
 [Pcd]
   gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard
diff --git a/UefiCpuPkg/UefiCpuPkg.dec b/UefiCpuPkg/UefiCpuPkg.dec
index 571b59b36f..f5febe46ba 100644
--- a/UefiCpuPkg/UefiCpuPkg.dec
+++ b/UefiCpuPkg/UefiCpuPkg.dec
@@ -3,6 +3,7 @@
 #
 # Copyright (c) 2007 - 2023, Intel Corporation. All rights reserved.<BR>
 # Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.<BR>
+# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
 #
 # SPDX-License-Identifier: BSD-2-Clause-Patent
 #
@@ -385,6 +386,10 @@
   # @Prompt Enable performance collecting when processor trace is enabled.
   gUefiCpuPkgTokenSpaceGuid.PcdCpuProcTracePerformanceCollecting|FALSE|BOOLEAN|0x60000020
 
+  ## This PCD Contains the pointer to a CPU exception vector base address.
+  # @Prompt The pointer to a CPU exception vector base address.
+  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress|0x0|UINT64|0x60000022
+
 [PcdsFixedAtBuild.X64, PcdsPatchableInModule.X64, PcdsDynamic.X64, PcdsDynamicEx.X64]
   ## Indicate access to non-SMRAM memory is restricted to reserved, runtime and ACPI NVS type after SmmReadyToLock.
   #  MMIO access is always allowed regardless of the value of this PCD.
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114538): https://edk2.groups.io/g/devel/message/114538
Mute This Topic: https://groups.io/mt/103971651/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 13/37] UefiCpuPkg: Add CpuMmuLib.h to UefiCpuPkg
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (11 preceding siblings ...)
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 12/37] UefiCpuPkg: Add CPU exception library for LoongArch Chao Li
@ 2024-01-26  6:29 ` Chao Li
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib " Chao Li
                   ` (28 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:29 UTC (permalink / raw)
  To: devel
  Cc: Eric Dong, Ray Ni, Laszlo Ersek, Rahul Kumar, Gerd Hoffmann,
	Leif Lindholm, Ard Biesheuvel, Sami Mujawar, Sunil V L,
	Andrei Warkentin

Add a new header file CpuMmuLib.h, whitch is referenced from
ArmPkg/Include/Library/ArmMmuLib.h. Currently, only support for
LoongArch64 is added, and more architectures can be accommodated in the
future.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Leif Lindholm <quic_llindhol@quicinc.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Sami Mujawar <sami.mujawar@arm.com>
Cc: Sunil V L <sunilvl@ventanamicro.com>
Cc: Andrei Warkentin <andrei.warkentin@intel.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Reviewed-by: Andrei Warkentin <andrei.warkentin@intel.com>
Reviewed-by: Ray Ni <ray.ni@intel.com>
---
 UefiCpuPkg/Include/Library/CpuMmuLib.h | 62 ++++++++++++++++++++++++++
 UefiCpuPkg/UefiCpuPkg.dec              |  4 ++
 2 files changed, 66 insertions(+)
 create mode 100644 UefiCpuPkg/Include/Library/CpuMmuLib.h

diff --git a/UefiCpuPkg/Include/Library/CpuMmuLib.h b/UefiCpuPkg/Include/Library/CpuMmuLib.h
new file mode 100644
index 0000000000..f88ec4eb2e
--- /dev/null
+++ b/UefiCpuPkg/Include/Library/CpuMmuLib.h
@@ -0,0 +1,62 @@
+/** @file
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef CPU_MMU_LIB_H_
+#define CPU_MMU_LIB_H_
+
+#include <Uefi/UefiBaseType.h>
+
+typedef struct {
+  EFI_PHYSICAL_ADDRESS    PhysicalBase;
+  EFI_VIRTUAL_ADDRESS     VirtualBase;
+  UINTN                   Length;
+  UINTN                   Attributes;
+} MEMORY_REGION_DESCRIPTOR;
+
+/**
+  Finds the first of the length and memory properties of the memory region corresponding
+  to the specified base address.
+
+  @param[in]       BaseAddress       To find the base address of the memory region.
+  @param[in, out]  RegionLength      Pointer holding:
+                                      - At entry, the length of the memory region
+                                        expected to be found.
+                                      - At exit, the length of the memory region found.
+  @param[out]      RegionAttributes  Properties of the memory region found.
+
+  @retval  EFI_SUCCESS    The corresponding memory area was successfully found
+           EFI_NOT_FOUND    No memory area found
+**/
+EFI_STATUS
+EFIAPI
+GetMemoryRegionAttributes (
+  IN     UINTN  BaseAddress,
+  IN OUT UINTN  *RegionLength,
+  OUT    UINTN  *RegionAttributes
+  );
+
+/**
+  Sets the Attributes  of the specified memory region.
+
+  @param[in]  BaseAddress    The base address of the memory region to set the Attributes.
+  @param[in]  Length         The length of the memory region to set the Attributes.
+  @param[in]  Attributes     The Attributes to be set.
+  @param[in]  AttributeMask  Mask of memory attributes to take into account.
+
+  @retval  EFI_SUCCESS    The Attributes was set successfully
+**/
+EFI_STATUS
+EFIAPI
+SetMemoryRegionAttributes (
+  IN EFI_PHYSICAL_ADDRESS  BaseAddress,
+  IN UINTN                 Length,
+  IN UINTN                 Attributes,
+  IN UINT64                AttributeMask
+  );
+
+#endif // CPU_MMU_LIB_H_
diff --git a/UefiCpuPkg/UefiCpuPkg.dec b/UefiCpuPkg/UefiCpuPkg.dec
index f5febe46ba..9992626e62 100644
--- a/UefiCpuPkg/UefiCpuPkg.dec
+++ b/UefiCpuPkg/UefiCpuPkg.dec
@@ -62,6 +62,10 @@
   ##  @libraryclass  Provides function for manipulating x86 paging structures.
   CpuPageTableLib|Include/Library/CpuPageTableLib.h
 
+[LibraryClasses.LoongArch64]
+  ##  @libraryclass  Provides macros and functions for the memory management unit.
+  CpuMmuLib|Include/Library/CpuMmuLib.h
+
   ## @libraryclass   Provides functions for manipulating smram savestate registers.
   MmSaveStateLib|Include/Library/MmSaveStateLib.h
 
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114539): https://edk2.groups.io/g/devel/message/114539
Mute This Topic: https://groups.io/mt/103971652/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (12 preceding siblings ...)
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 13/37] UefiCpuPkg: Add CpuMmuLib.h to UefiCpuPkg Chao Li
@ 2024-01-26  6:29 ` Chao Li
  2024-01-31  9:47   ` Laszlo Ersek
  2024-01-31 10:33   ` Pedro Falcato
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 15/37] UefiCpuPkg: Add multiprocessor library for LoongArch64 Chao Li
                   ` (27 subsequent siblings)
  41 siblings, 2 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:29 UTC (permalink / raw)
  To: devel
  Cc: Eric Dong, Ray Ni, Laszlo Ersek, Rahul Kumar, Gerd Hoffmann,
	Baoqi Zhang, Dongyan Qian, Xianglai Li, Bibo Mao

Add a new library named CpuMmuLib and add a LoongArch64 instance with in
the library.
It provides two-stage MMU libraryinstances, PEI and DXE.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Co-authored-by: Baoqi Zhang <zhangbaoqi@loongson.cn>
Co-authored-by: Dongyan Qian <qiandongyan@loongson.cn>
Co-authored-by: Xianglai Li <lixianglai@loongson.cn>
Co-authored-by: Bibo Mao <maobibo@loongson.cn>
---
 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf |  36 +
 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni |  14 +
 .../CpuMmuLib/LoongArch64/CommonMmuLib.c      | 988 ++++++++++++++++++
 .../CpuMmuLib/LoongArch64/CommonMmuLib.h      |  43 +
 .../Library/CpuMmuLib/LoongArch64/Page.h      | 279 +++++
 .../CpuMmuLib/LoongArch64/PeiCpuMmuLib.c      | 178 ++++
 .../Library/CpuMmuLib/LoongArch64/Tlb.h       |  48 +
 .../CpuMmuLib/LoongArch64/TlbOperation.S      |  44 +
 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf |  44 +
 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni |  14 +
 UefiCpuPkg/UefiCpuPkg.dsc                     |   4 +
 11 files changed, 1692 insertions(+)
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni

diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
new file mode 100644
index 0000000000..bfce3ce96d
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
@@ -0,0 +1,36 @@
+## @file
+#  CPU Memory Map Unit DXE phase driver.
+#
+#  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                      = DxeCpuMmuLib
+  MODULE_UNI_FILE                = DxeCpuMmuLib.uni
+  FILE_GUID                      = DA8F0232-FB14-42F0-922C-63104D2C70BE
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = CpuMmuLib | DXE_DRIVER
+  CONSTRUCTOR                    = MmuInitialize
+
+#
+#  VALID_ARCHITECTURES           = LOONGARCH64
+#
+
+[Sources.LoongArch64]
+  LoongArch64/TlbOperation.S   | GCC
+  LoongArch64/CommonMmuLib.c
+  LoongArch64/Page.h
+  LoongArch64/Tlb.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+  UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+  DebugLib
+  MemoryAllocationLib
diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
new file mode 100644
index 0000000000..7342249516
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
@@ -0,0 +1,14 @@
+// /** @file
+// CPU Memory Manager Unit library instance for DXE modules.
+//
+// CPU Memory Manager Unit library instance for DXE modules.
+//
+// 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 "CPU Memory Manager Unit library instance for DXE modules."
+
+#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for DXE modules."
diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
new file mode 100644
index 0000000000..2e852c3371
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
@@ -0,0 +1,988 @@
+/** @file
+
+  CPU Memory Map Unit Handler Library common functions.
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - Pgd or Pgd or PGD    - Page Global Directory
+    - Pud or Pud or PUD    - Page Upper Directory
+    - Pmd or Pmd or PMD    - Page Middle Directory
+    - Pte or pte or PTE    - Page Table Entry
+    - Val or VAL or val    - Value
+    - Dir    - Directory
+**/
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/CpuMmuLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Register/LoongArch64/Csr.h>
+#include "Tlb.h"
+#include "Page.h"
+
+#define SWAP_PAGE_DIR              CsrRead(LOONGARCH_CSR_PGDL)
+#define EFI_MEMORY_CACHETYPE_MASK  (EFI_MEMORY_UC  | \
+                                    EFI_MEMORY_WC  | \
+                                    EFI_MEMORY_WT  | \
+                                    EFI_MEMORY_WB  | \
+                                    EFI_MEMORY_UCE   \
+                                    )
+
+BOOLEAN  mMmuInited = FALSE;
+
+/**
+  Check to see if mmu successfully initializes.
+
+  @param  VOID.
+
+  @retval  TRUE  Initialization has been completed.
+           FALSE Initialization did not complete.
+**/
+STATIC
+BOOLEAN
+MmuIsInit (
+  VOID
+  )
+{
+  if (mMmuInited || (SWAP_PAGE_DIR != 0)) {
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+/**
+  Iterates through the page directory to initialize it.
+
+  @param  Dst  A pointer to the directory of the page to initialize.
+  @param  Num  The number of page directories to initialize.
+  @param  Src  A pointer to the data used to initialize the page directory.
+
+  @return VOID.
+**/
+STATIC
+VOID
+PageDirInit (
+  IN VOID   *Dst,
+  IN UINTN  Num,
+  IN VOID   *Src
+  )
+{
+  UINTN  *Ptr;
+  UINTN  *End;
+  UINTN  Entry;
+
+  Entry = (UINTN)Src;
+  Ptr   = (UINTN *)Dst;
+  End   = Ptr + Num;
+
+  for ( ; Ptr < End; Ptr++) {
+    *Ptr = Entry;
+  }
+
+  return;
+}
+
+/**
+  Gets the virtual address corresponding to the page global directory table entry.
+
+  @param  Address  the virtual address for the table entry.
+
+  @retval PGD A pointer to get the table item.
+**/
+STATIC
+PGD *
+PgdOffset (
+  IN UINTN  Address
+  )
+{
+  return (PGD *)(SWAP_PAGE_DIR) + PGD_INDEX (Address);
+}
+
+/**
+  Gets the virtual address corresponding to the page upper directory table entry.
+
+  @param  Pgd  A pointer to a page global directory table entry.
+  @param  Address  the virtual address for the table entry.
+
+  @retval PUD A pointer to get the table item.
+**/
+STATIC
+PUD *
+PudOffset (
+  IN PGD    *Pgd,
+  IN UINTN  Address
+  )
+{
+  UINTN  PgdVal;
+
+  PgdVal = (UINTN)PGD_VAL (*Pgd);
+
+  return (PUD *)PgdVal + PUD_INDEX (Address);
+}
+
+/**
+  Gets the virtual address corresponding to the page middle directory table entry.
+
+  @param  Pud  A pointer to a page upper directory table entry.
+  @param  Address  the virtual address for the table entry.
+
+  @retval PMD A pointer to get the table item.
+**/
+STATIC
+PMD *
+PmdOffset (
+  IN PUD    *Pud,
+  IN UINTN  Address
+  )
+{
+  UINTN  PudVal;
+
+  PudVal = PUD_VAL (*Pud);
+
+  return (PMD *)PudVal + PMD_INDEX (Address);
+}
+
+/**
+  Gets the virtual address corresponding to the page table entry.
+
+  @param  Pmd  A pointer to a page middle directory table entry.
+  @param  Address  the virtual address for the table entry.
+
+  @retval PTE A pointer to get the table item.
+**/
+STATIC
+PTE *
+PteOffset (
+  IN PMD    *Pmd,
+  IN UINTN  Address
+  )
+{
+  UINTN  PmdVal;
+
+  PmdVal = (UINTN)PMD_VAL (*Pmd);
+
+  return (PTE *)PmdVal + PTE_INDEX (Address);
+}
+
+/**
+  Sets the value of the page table entry.
+
+  @param  Pte  A pointer to a page table entry.
+  @param  PteVal  The value of the page table entry to set.
+
+**/
+STATIC
+VOID
+SetPte (
+  IN PTE  *Pte,
+  IN PTE  PteVal
+  )
+{
+  *Pte = PteVal;
+}
+
+/**
+  Sets the value of the page global directory.
+
+  @param  Pgd  A pointer to a page global directory.
+  @param  Pud  The value of the page global directory to set.
+
+**/
+STATIC
+VOID
+SetPgd (
+  IN PGD  *Pgd,
+  IN PUD  *Pud
+  )
+{
+  *Pgd = (PGD) {
+    ((UINTN)Pud)
+  };
+}
+
+/**
+  Sets the value of the page upper directory.
+
+  @param  Pud  A pointer to a page upper directory.
+  @param  Pmd  The value of the page upper directory to set.
+
+**/
+STATIC
+VOID
+SetPud (
+  IN PUD  *Pud,
+  IN PMD  *Pmd
+  )
+{
+  *Pud = (PUD) {
+    ((UINTN)Pmd)
+  };
+}
+
+/**
+  Sets the value of the page middle directory.
+
+  @param  Pmd  A pointer to a page middle directory.
+  @param  Pte  The value of the page middle directory to set.
+
+**/
+STATIC
+VOID
+SetPmd (
+  IN PMD  *Pmd,
+  IN PTE  *Pte
+  )
+{
+  *Pmd = (PMD) {
+    ((UINTN)Pte)
+  };
+}
+
+/**
+  Free up memory space occupied by page tables.
+
+  @param  Pte  A pointer to the page table.
+
+**/
+VOID
+PteFree (
+  IN PTE  *Pte
+  )
+{
+  FreePages ((VOID *)Pte, 1);
+}
+
+/**
+  Free up memory space occupied by page middle directory.
+
+  @param  Pmd  A pointer to the page middle directory.
+
+**/
+VOID
+PmdFree (
+  IN PMD  *Pmd
+  )
+{
+  FreePages ((VOID *)Pmd, 1);
+}
+
+/**
+  Free up memory space occupied by page upper directory.
+
+  @param  Pud  A pointer to the page upper directory.
+
+**/
+VOID
+PudFree (
+  IN PUD  *Pud
+  )
+{
+  FreePages ((VOID *)Pud, 1);
+}
+
+/**
+  Requests the memory space required for the page upper directory,
+  initializes it, and places it in the specified page global directory
+
+  @param  Pgd  A pointer to the page global directory.
+
+  @retval  EFI_SUCCESS  Memory request successful.
+  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
+**/
+STATIC
+EFI_STATUS
+PudAlloc (
+  IN PGD  *Pgd
+  )
+{
+  PUD  *Pud;
+
+  Pud = (PUD *)AllocatePages (1);
+  if (Pud == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  PageDirInit ((VOID *)Pud, ENTRYS_PER_PUD, (VOID *)INVALID_PAGE);
+
+  SetPgd (Pgd, Pud);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Requests the memory space required for the page middle directory,
+  initializes it, and places it in the specified page upper directory
+
+  @param  Pud  A pointer to the page upper directory.
+
+  @retval  EFI_SUCCESS  Memory request successful.
+  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
+**/
+STATIC
+EFI_STATUS
+PmdAlloc (
+  IN PUD  *Pud
+  )
+{
+  PMD  *Pmd;
+
+  Pmd = (PMD *)AllocatePages (1);
+  if (!Pmd) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  PageDirInit ((VOID *)Pmd, ENTRYS_PER_PMD, (VOID *)INVALID_PAGE);
+
+  SetPud (Pud, Pmd);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Requests the memory space required for the page table,
+  initializes it, and places it in the specified page middle directory
+
+  @param  Pmd  A pointer to the page middle directory.
+
+  @retval  EFI_SUCCESS  Memory request successful.
+  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
+**/
+STATIC
+EFI_STATUS
+PteAlloc (
+  IN PMD  *Pmd
+  )
+{
+  PTE  *Pte;
+
+  Pte = (PTE *)AllocatePages (1);
+  if (!Pte) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Pte = ZeroMem (Pte, EFI_PAGE_SIZE);
+
+  SetPmd (Pmd, Pte);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Requests the memory space required for the page upper directory,
+  initializes it, and places it in the specified page global directory,
+  and get the page upper directory entry corresponding to the virtual address.
+
+  @param  Pgd      A pointer to the page global directory.
+  @param  Address  The corresponding virtual address of the page table entry.
+
+  @retval          A pointer to the page upper directory entry. Return NULL, if
+                   allocate the memory buffer is fail.
+**/
+STATIC
+PUD *
+PudAllocGet (
+  IN PGD    *Pgd,
+  IN UINTN  Address
+  )
+{
+  EFI_STATUS  Status;
+
+  if (PGD_IS_EMPTY (*Pgd)) {
+    Status = PudAlloc (Pgd);
+    ASSERT_EFI_ERROR (Status);
+    if (EFI_ERROR (Status)) {
+      return NULL;
+    }
+  }
+
+  return PudOffset (Pgd, Address);
+}
+
+/**
+  Requests the memory space required for the page middle directory,
+  initializes it, and places it in the specified page upper directory,
+  and get the page middle directory entry corresponding to the virtual address.
+
+  @param  Pud      A pointer to the page upper directory.
+  @param  Address  The corresponding virtual address of the page table entry.
+
+  @retval          A pointer to the page middle directory entry. Return NULL, if
+                   allocate the memory buffer is fail.
+**/
+STATIC
+PMD *
+PmdAllocGet (
+  IN PUD    *Pud,
+  IN UINTN  Address
+  )
+{
+  EFI_STATUS  Status;
+
+  if (PUD_IS_EMPTY (*Pud)) {
+    Status = PmdAlloc (Pud);
+    ASSERT_EFI_ERROR (Status);
+    if (EFI_ERROR (Status)) {
+      return NULL;
+    }
+  }
+
+  return PmdOffset (Pud, Address);
+}
+
+/**
+  Requests the memory space required for the page table,
+  initializes it, and places it in the specified page middle directory,
+  and get the page table entry corresponding to the virtual address.
+
+  @param  Pmd      A pointer to the page upper directory.
+  @param  Address  The corresponding virtual address of the page table entry.
+
+  @retval          A pointer to the page table entry. Return NULL, if allocate
+                   the memory buffer is fail.
+**/
+STATIC
+PTE *
+PteAllocGet (
+  IN PMD    *Pmd,
+  IN UINTN  Address
+  )
+{
+  EFI_STATUS  Status;
+
+  if (PMD_IS_EMPTY (*Pmd)) {
+    Status = PteAlloc (Pmd);
+    ASSERT_EFI_ERROR (Status);
+    if (EFI_ERROR (Status)) {
+      return NULL;
+    }
+  }
+
+  return PteOffset (Pmd, Address);
+}
+
+/**
+  Gets the physical address of the page table entry corresponding to the specified virtual address.
+
+  @param  Address  The corresponding virtual address of the page table entry.
+
+  @retval  A pointer to the page table entry.
+  @retval  NULL
+**/
+STATIC
+PTE *
+GetPteAddress (
+  IN UINTN  Address
+  )
+{
+  PGD  *Pgd;
+  PUD  *Pud;
+  PMD  *Pmd;
+
+  Pgd = PgdOffset (Address);
+
+  if (PGD_IS_EMPTY (*Pgd)) {
+    return NULL;
+  }
+
+  Pud = PudOffset (Pgd, Address);
+
+  if (PUD_IS_EMPTY (*Pud)) {
+    return NULL;
+  }
+
+  Pmd = PmdOffset (Pud, Address);
+  if (PMD_IS_EMPTY (*Pmd)) {
+    return NULL;
+  }
+
+  if (IS_HUGE_PAGE (Pmd->PmdVal)) {
+    return ((PTE *)Pmd);
+  }
+
+  return PteOffset (Pmd, Address);
+}
+
+/**
+  Gets the Attributes of Huge Page.
+
+  @param  Pmd  A pointer to the page middle directory.
+
+  @retval     Value of Attributes.
+**/
+STATIC
+UINTN
+GetHugePageAttributes (
+  IN  PMD  *Pmd
+  )
+{
+  UINTN  Attributes;
+  UINTN  GlobalFlag;
+  UINTN  HugeVal;
+
+  HugeVal     = PMD_VAL (*Pmd);
+  Attributes  = HugeVal & (~HUGEP_PAGE_MASK);
+  GlobalFlag  = ((Attributes & (1 << PAGE_HGLOBAL_SHIFT)) >> PAGE_HGLOBAL_SHIFT) << PAGE_GLOBAL_SHIFT;
+  Attributes &= ~(1 << PAGE_HGLOBAL_SHIFT);
+  Attributes |= GlobalFlag;
+  return Attributes;
+}
+
+/**
+  Establishes a page table entry based on the specified memory region.
+
+  @param  Pmd  A pointer to the page middle directory.
+  @param  Address  The memory space start address.
+  @param  End  The end address of the memory space.
+  @param  Attributes  Memory space Attributes.
+
+  @retval     EFI_SUCCESS   The page table entry was created successfully.
+  @retval     EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.
+**/
+STATIC
+EFI_STATUS
+MemoryMapPteRange (
+  IN PMD    *Pmd,
+  IN UINTN  Address,
+  IN UINTN  End,
+  IN UINTN  Attributes
+  )
+{
+  PTE      *Pte;
+  PTE      PteVal;
+  BOOLEAN  UpDate;
+
+  Pte = PteAllocGet (Pmd, Address);
+  if (!Pte) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  DEBUG ((
+    DEBUG_INFO,
+    "%a %d Address %p End %p  Attributes %llx\n",
+    __func__,
+    __LINE__,
+    Address,
+    End,
+    Attributes
+    ));
+
+  do {
+    UpDate = FALSE;
+    PteVal = MAKE_PTE (Address, Attributes);
+
+    if ((!PTE_IS_EMPTY (*Pte)) &&
+        (PTE_VAL (*Pte) != PTE_VAL (PteVal)))
+    {
+      UpDate = TRUE;
+    }
+
+    SetPte (Pte, PteVal);
+    if (UpDate) {
+      InvalidTlb (Address);
+    }
+  } while (Pte++, Address += EFI_PAGE_SIZE, Address != End);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Convert Huge Page to Page.
+
+  @param  Pmd  A pointer to the page middle directory.
+  @param  Address  The memory space start address.
+  @param  End  The end address of the memory space.
+  @param  Attributes  Memory space Attributes.
+
+  @retval  EFI_SUCCESS   The page table entry was created successfully.
+  @retval  EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.
+**/
+STATIC
+EFI_STATUS
+ConvertHugePageToPage (
+  IN  PMD   *Pmd,
+  IN UINTN  Address,
+  IN UINTN  End,
+  IN UINTN  Attributes
+  )
+{
+  UINTN       OldAttributes;
+  UINTN       HugePageEnd;
+  UINTN       HugePageStart;
+  EFI_STATUS  Status;
+
+  Status = EFI_SUCCESS;
+
+  if ((PMD_IS_EMPTY (*Pmd)) ||
+      (!IS_HUGE_PAGE (Pmd->PmdVal)))
+  {
+    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
+  } else {
+    OldAttributes = GetHugePageAttributes (Pmd);
+    if (Attributes == OldAttributes) {
+      return Status;
+    }
+
+    SetPmd (Pmd, (PTE *)(INVALID_PAGE));
+    HugePageStart = Address & PMD_MASK;
+    HugePageEnd   = HugePageStart + HUGE_PAGE_SIZE;
+    ASSERT (HugePageEnd >= End);
+
+    if (Address > HugePageStart) {
+      Status |= MemoryMapPteRange (Pmd, HugePageStart, Address, OldAttributes);
+    }
+
+    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
+
+    if (End < HugePageEnd) {
+      Status |= MemoryMapPteRange (Pmd, End, HugePageEnd, OldAttributes);
+    }
+  }
+
+  return Status;
+}
+
+/**
+  Establishes a page middle directory based on the specified memory region.
+
+  @param  Pud  A pointer to the page upper directory.
+  @param  Address  The memory space start address.
+  @param  End  The end address of the memory space.
+  @param  Attributes  Memory space Attributes.
+
+  @retval     EFI_SUCCESS   The page middle directory was created successfully.
+  @retval     EFI_OUT_OF_RESOURCES  Page middle directory establishment failed due to resource exhaustion.
+**/
+STATIC
+EFI_STATUS
+MemoryMapPmdRange (
+  IN PUD    *Pud,
+  IN UINTN  Address,
+  IN UINTN  End,
+  IN UINTN  Attributes
+  )
+{
+  PMD      *Pmd;
+  UINTN    Next;
+  PTE      PteVal;
+  BOOLEAN  UpDate;
+
+  Pmd = PmdAllocGet (Pud, Address);
+  if (Pmd == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  do {
+    Next = PMD_ADDRESS_END (Address, End);
+    if (((Address & (~PMD_MASK)) == 0) &&
+        ((Next &  (~PMD_MASK)) == 0) &&
+        (PMD_IS_EMPTY (*Pmd) || IS_HUGE_PAGE (Pmd->PmdVal)))
+    {
+      UpDate = FALSE;
+      PteVal = MAKE_HUGE_PTE (Address, Attributes);
+
+      if ((!PMD_IS_EMPTY (*Pmd)) &&
+          (PMD_VAL (*Pmd) != PTE_VAL (PteVal)))
+      {
+        UpDate = TRUE;
+      }
+
+      SetPmd (Pmd, (PTE *)PteVal.PteVal);
+      if (UpDate) {
+        InvalidTlb (Address);
+      }
+    } else {
+      ConvertHugePageToPage (Pmd, Address, Next, Attributes);
+    }
+  } while (Pmd++, Address = Next, Address != End);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Establishes a page upper directory based on the specified memory region.
+
+  @param  Pgd  A pointer to the page global directory.
+  @param  Address  The memory space start address.
+  @param  End  The end address of the memory space.
+  @param  Attributes  Memory space Attributes.
+
+  @retval     EFI_SUCCESS   The page upper directory was created successfully.
+  @retval     EFI_OUT_OF_RESOURCES  Page upper directory establishment failed due to resource exhaustion.
+**/
+STATIC
+EFI_STATUS
+MemoryMapPudRange (
+  IN PGD    *Pgd,
+  IN UINTN  Address,
+  IN UINTN  End,
+  IN UINTN  Attributes
+  )
+{
+  PUD    *Pud;
+  UINTN  Next;
+
+  Pud = PudAllocGet (Pgd, Address);
+  if (Pud == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  do {
+    Next = PUD_ADDRESS_END (Address, End);
+    if (EFI_ERROR (MemoryMapPmdRange (Pud, Address, Next, Attributes))) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+  } while (Pud++, Address = Next, Address != End);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Establishes a page global directory based on the specified memory region.
+
+  @param  Start  The memory space start address.
+  @param  End  The end address of the memory space.
+  @param  Attributes  Memory space Attributes.
+
+  @retval     EFI_SUCCESS   The page global directory was created successfully.
+  @retval     EFI_OUT_OF_RESOURCES  Page global directory establishment failed due to resource exhaustion.
+**/
+STATIC
+EFI_STATUS
+MemoryMapPageRange (
+  IN UINTN  Start,
+  IN UINTN  End,
+  IN UINTN  Attributes
+  )
+{
+  PGD         *Pgd;
+  UINTN       Next;
+  UINTN       Address;
+  EFI_STATUS  Err;
+
+  Address = Start;
+
+  /* Get PGD(PTE PMD PUD PGD) in PageTables */
+  Pgd = PgdOffset (Address);
+  do {
+    Next = PGD_ADDRESS_END (Address, End);
+    /* Get Next Align Page to Map */
+    Err = MemoryMapPudRange (Pgd, Address, Next, Attributes);
+    if (Err) {
+      return Err;
+    }
+  } while (Pgd++, Address = Next, Address != End);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Page tables are established from memory-mapped tables.
+
+  @param  MemoryRegion   A pointer to a memory-mapped table entry.
+
+  @retval     EFI_SUCCESS   The page table was created successfully.
+  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
+**/
+EFI_STATUS
+FillTranslationTable (
+  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
+  )
+{
+  return MemoryMapPageRange (
+           MemoryRegion->VirtualBase,
+           (MemoryRegion->Length + MemoryRegion->VirtualBase),
+           MemoryRegion->Attributes
+           );
+}
+
+/**
+  Convert EFI Attributes to Loongarch Attributes.
+
+  @param[in]  EfiAttributes     Efi Attributes.
+
+  @retval  Corresponding architecture attributes.
+**/
+UINTN
+EFIAPI
+EfiAttributeConverse (
+  IN UINTN  EfiAttributes
+  )
+{
+  UINTN  LoongArchAttributes;
+
+  LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | PLV_KERNEL | PAGE_GLOBAL;
+
+  switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
+    case EFI_MEMORY_UC:
+      LoongArchAttributes |= CACHE_SUC;
+      break;
+    case EFI_MEMORY_WC:
+      LoongArchAttributes |= CACHE_WUC;
+      break;
+    case EFI_MEMORY_WT:
+    case EFI_MEMORY_WB:
+      LoongArchAttributes |= CACHE_CC;
+      break;
+    default:
+      LoongArchAttributes |= CACHE_CC;
+      break;
+  }
+
+  // Write protection attributes
+  if (((EfiAttributes & EFI_MEMORY_RO) != 0) ||
+      ((EfiAttributes & EFI_MEMORY_WP) != 0))
+  {
+    LoongArchAttributes &= ~PAGE_DIRTY;
+  }
+
+  if ((EfiAttributes & EFI_MEMORY_RP) != 0) {
+    LoongArchAttributes |= PAGE_NO_READ;
+  }
+
+  // eXecute protection attribute
+  if ((EfiAttributes & EFI_MEMORY_XP) != 0) {
+    LoongArchAttributes |= PAGE_NO_EXEC;
+  }
+
+  return LoongArchAttributes;
+}
+
+/**
+  Finds the first of the length and memory properties of the memory region corresponding
+  to the specified base address.
+
+  @param[in]       BaseAddress       To find the base address of the memory region.
+  @param[in, out]  RegionLength      Pointer holding:
+                                      - At entry, the length of the memory region
+                                        expected to be found.
+                                      - At exit, the length of the memory region found.
+  @param[out]      RegionAttributes  Properties of the memory region found.
+
+  @retval  EFI_SUCCESS           The corresponding memory area was successfully found
+           EFI_NOT_FOUND         No memory area found
+           EFI_OUT_OF_RESOURCES  Base address or expected memory region exceeds the maximum
+                                 address.
+**/
+EFI_STATUS
+EFIAPI
+GetMemoryRegionAttributes (
+  IN     UINTN  BaseAddress,
+  IN OUT UINTN  *RegionLength,
+  OUT    UINTN  *RegionAttributes
+  )
+{
+  PTE    *Pte;
+  UINTN  Attributes;
+  UINTN  AttributesTmp;
+  UINTN  MaxAddress;
+  UINTN  EndAddress;
+  UINTN  AddSize;
+
+  if (!MmuIsInit ()) {
+    return EFI_UNSUPPORTED;
+  }
+
+  EndAddress = BaseAddress + *RegionLength;
+  MaxAddress = LShiftU64 (1ULL, MAX_VA_BITS) - 1;
+
+  // Clean the value to prepare output to find region size.
+  *RegionLength = 0x0;
+
+  if ((BaseAddress >= MaxAddress) || (EndAddress >= MaxAddress)) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Pte = GetPteAddress (BaseAddress);
+
+  if (Pte == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  Attributes = GET_PAGE_ATTRIBUTES (*Pte);
+  if (IS_HUGE_PAGE (Pte->PteVal)) {
+    *RegionAttributes = Attributes & (~(PAGE_HUGE));
+  } else {
+    *RegionAttributes = Attributes;
+  }
+
+  do {
+    Pte = GetPteAddress (BaseAddress);
+    if (Pte == NULL) {
+      return EFI_SUCCESS;
+    }
+
+    AttributesTmp = GET_PAGE_ATTRIBUTES (*Pte);
+    if (AttributesTmp == Attributes) {
+      if (IS_HUGE_PAGE (Pte->PteVal)) {
+        AddSize = HUGE_PAGE_SIZE;
+      } else {
+        AddSize = EFI_PAGE_SIZE;
+      }
+
+      *RegionLength += AddSize;
+      BaseAddress   += AddSize;
+    } else {
+      return EFI_SUCCESS;
+    }
+  } while (BaseAddress <= EndAddress);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Sets the Attributes  of the specified memory region
+
+  @param[in]  BaseAddress    The base address of the memory region to set the Attributes.
+  @param[in]  Length         The length of the memory region to set the Attributes.
+  @param[in]  Attributes     The Attributes to be set.
+  @param[in]  AttributeMask  Mask of memory attributes to take into account.
+
+  @retval  EFI_SUCCESS    The Attributes was set successfully
+**/
+EFI_STATUS
+EFIAPI
+SetMemoryRegionAttributes (
+  IN EFI_PHYSICAL_ADDRESS  BaseAddress,
+  IN UINTN                 Length,
+  IN UINTN                 Attributes,
+  IN UINT64                AttributeMask
+  )
+{
+  EFI_STATUS  Status;
+
+  if (!MmuIsInit ()) {
+    return EFI_UNSUPPORTED;
+  }
+
+  Attributes = EfiAttributeConverse (Attributes);
+  Status     = MemoryMapPageRange (BaseAddress, BaseAddress + Length, Attributes);
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+/**
+  Check to see if mmu successfully initializes and saves the result.
+
+  @param[in]  ImageHandle  The firmware allocated handle for the EFI image.
+  @param[in]  SystemTable  A pointer to the EFI System Table.
+
+  @retval  RETURN_SUCCESS    Initialization succeeded.
+**/
+RETURN_STATUS
+MmuInitialize (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  if (SWAP_PAGE_DIR != 0) {
+    mMmuInited = TRUE;
+  }
+
+  return RETURN_SUCCESS;
+}
diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
new file mode 100644
index 0000000000..d8c922c8fa
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
@@ -0,0 +1,43 @@
+/** @file
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - Dir    - Directory
+**/
+
+#ifndef  MMU_LIB_CORE_H_
+#define  MMU_LIB_CORE_H_
+
+/**
+  Iterates through the page directory to initialize it.
+
+  @param  Dst  A pointer to the directory of the page to initialize.
+  @param  Num  The number of page directories to initialize.
+  @param  Src  A pointer to the data used to initialize the page directory.
+
+  @retval VOID.
+**/
+VOID
+PageDirInit (
+  IN VOID   *dest,
+  IN UINTN  Count,
+  IN VOID   *src
+  );
+
+/**
+  Page tables are established from memory-mapped tables.
+
+  @param  MemoryRegion   A pointer to a memory-mapped table entry.
+
+  @retval     EFI_SUCCESS   The page table was created successfully.
+  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
+**/
+EFI_STATUS
+FillTranslationTable (
+  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
+  );
+
+#endif // MMU_LIB_CORE_H_
diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
new file mode 100644
index 0000000000..bac4f52327
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
@@ -0,0 +1,279 @@
+/** @file
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - Pgd or Pgd or PGD    - Page Global Directory
+    - Pud or Pud or PUD    - Page Upper Directory
+    - Pmd or Pmd or PMD    - Page Middle Directory
+    - Pte or pte or PTE    - Page Table Entry
+    - Val or VAL or val    - Value
+    - Dir    - Directory
+**/
+
+#ifndef PAGE_H_
+#define PAGE_H_
+
+#include <Library/CpuMmuLib.h>
+
+#define MAX_VA_BITS  47
+#define PGD_WIDE     (8)
+#define PUD_WIDE     (9)
+#define PMD_WIDE     (9)
+#define PTE_WIDE     (9)
+
+#define ENTRYS_PER_PGD  (1 << PGD_WIDE)
+#define ENTRYS_PER_PUD  (1 << PUD_WIDE)
+#define ENTRYS_PER_PMD  (1 << PMD_WIDE)
+#define ENTRYS_PER_PTE  (1 << PTE_WIDE)
+
+#define PGD_SHIFT  (PUD_SHIFT + PUD_WIDE)
+#define PUD_SHIFT  (PMD_SHIFT + PMD_WIDE)
+#define PMD_SHIFT  (EFI_PAGE_SHIFT + PTE_WIDE)
+#define PTE_SHIFT  (EFI_PAGE_SHIFT)
+
+#define PGD_SIZE  (1UL << PGD_SHIFT)
+#define PUD_SIZE  (1UL << PUD_SHIFT)
+#define PMD_SIZE  (1UL << PMD_SHIFT)
+
+#define PGD_MASK   (~(PGD_SIZE-1))
+#define PUD_MASK   (~(PUD_SIZE-1))
+#define PMD_MASK   (~(PMD_SIZE-1))
+#define PAGE_MASK  (~(EFI_PAGE_SIZE - 1))
+#define PFN_MASK   (~(((UINTN)(1) << (EFI_PAGE_SHIFT)) - 1) & \
+                   (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
+
+#define HUGEP_PAGE_MASK  (~(((UINTN)(1) << (PMD_SHIFT)) - 1) & \
+                          (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
+
+#define INVALID_PAGE  0
+
+typedef struct {
+  UINTN    PgdVal;
+} PGD;
+typedef struct {
+  UINTN    PudVal;
+} PUD;
+typedef struct {
+  UINTN    PmdVal;
+} PMD;
+typedef struct {
+  UINTN    PteVal;
+} PTE;
+
+/**
+  Gets the value of the page global directory table entry.
+
+  @param  x    Page global directory struct variables.
+
+  @retval   the value of the page global directory table entry.
+ **/
+#define PGD_VAL(x)  ((x).PgdVal)
+
+/**
+  Gets the value of the page upper directory table entry.
+
+  @param  x    Page upper directory struct variables.
+
+  @retval  the value of the page upper directory table entry.
+ **/
+#define PUD_VAL(x)  ((x).PudVal)
+
+/**
+  Gets the value of the page middle directory table entry.
+
+  @param  x    Page middle directory struct variables.
+
+  @retval  the value of the page middle directory table entry.
+ **/
+#define PMD_VAL(x)  ((x).PmdVal)
+
+/**
+  Gets the value of the page table entry.
+
+  @param  x    Page table entry struct variables.
+
+  @retval  the value of the page table entry.
+ **/
+#define PTE_VAL(x)  ((x).PteVal)
+
+#define PGD_TABLE_SIZE  (ENTRYS_PER_PGD * sizeof(PGD))
+#define PUD_TABLE_SIZE  (ENTRYS_PER_PUD * sizeof(PUD))
+#define PMD_TABLE_SIZE  (ENTRYS_PER_PMD * sizeof(PMD))
+#define PTE_TABLE_SIZE  (ENTRYS_PER_PTE * sizeof(PTE))
+
+/**
+  Gets the physical address of the record in the page table entry.
+
+  @param  x    Page table entry struct variables.
+
+  @retval  the value of the physical address.
+ **/
+#define GET_PAGE_ATTRIBUTES(x)  (UINTN) {(PTE_VAL(x) & ~PFN_MASK)}
+
+/**
+  Gets the virtual address of the next block of the specified virtual address
+  that is aligned with the size of the global page directory mapping.
+
+  @param  Address  Specifies the virtual address.
+  @param  End    The end address of the memory region.
+
+  @retval   the specified virtual address  of the next block.
+ **/
+#define PGD_ADDRESS_END(Address, End)                  \
+({                                                     \
+  UINTN Boundary = ((Address) + PGD_SIZE) & PGD_MASK;  \
+  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
+})
+
+/**
+  Gets the virtual address of the next block of the specified virtual address
+  that is aligned with the size of the page upper directory mapping.
+
+  @param  Address  Specifies the virtual address.
+  @param  End    The end address of the memory region.
+
+  @retval   the specified virtual address  of the next block.
+ **/
+#define PUD_ADDRESS_END(Address, End)                  \
+({                                                     \
+  UINTN Boundary = ((Address) + PUD_SIZE) & PUD_MASK;  \
+  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
+})
+
+/**
+  Gets the virtual address of the next block of the specified virtual address
+  that is aligned with the size of the page middle directory mapping.
+
+  @param  Address  Specifies the virtual address.
+  @param  End    The end address of the memory region.
+
+  @retval   the specified virtual address  of the next block.
+ **/
+#define PMD_ADDRESS_END(Address, End)                  \
+({                                                     \
+  UINTN Boundary = ((Address) + PMD_SIZE) & PMD_MASK;  \
+  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
+})
+
+/**
+  Get Specifies the virtual address corresponding to the index of the page global directory table entry.
+
+  @param  Address  Specifies the virtual address.
+
+  @retval   the index of the page global directory table entry.
+ **/
+#define PGD_INDEX(Address)  (((Address) >> PGD_SHIFT) & (ENTRYS_PER_PGD-1))
+
+/**
+  Get Specifies the virtual address corresponding to the index of the page upper directory table entry.
+
+  @param  Address  Specifies the virtual address.
+  @param  End    The end address of the memory region.
+
+  @retval   the index of the page upper directory table entry.
+ **/
+#define PUD_INDEX(Address)  (((Address) >> PUD_SHIFT) & (ENTRYS_PER_PUD - 1))
+
+/**
+  Get Specifies the virtual address corresponding to the index of the page middle directory table entry.
+
+  @param  Address  Specifies the virtual address.
+
+  @retval   the index of the page middle directory table entry.
+ **/
+#define PMD_INDEX(Address)  (((Address) >> PMD_SHIFT) & (ENTRYS_PER_PMD - 1))
+
+/**
+  Get Specifies the virtual address corresponding to the index of the page table entry.
+
+  @param  Address  Specifies the virtual address.
+
+  @retval   the index of the page table entry.
+ **/
+#define PTE_INDEX(Address)  (((Address) >> EFI_PAGE_SHIFT) & (ENTRYS_PER_PTE - 1))
+
+/**
+  Calculates the value of the page table entry based on the specified virtual address and properties.
+
+  @param  Address  Specifies the virtual address.
+  @param  Attributes  Specifies the Attributes.
+
+  @retval    the value of the page table entry.
+ **/
+#define MAKE_PTE(Address, Attributes)  (PTE){((((Address) >> EFI_PAGE_SHIFT) << 12) | (Attributes))}
+
+/**
+  Get Global bit from Attributes
+
+  @param  Attributes  Specifies the Attributes.
+ * */
+#define GET_GLOBALBIT(Attributes)  ((Attributes & PAGE_GLOBAL) >> PAGE_GLOBAL_SHIFT)
+
+/**
+  Calculates the value of the Huge page table entry based on the specified virtual address and properties.
+
+  @param  Address  Specifies the virtual address.
+  @param  Attributes  Specifies the Attributes.
+
+  @retval    the value of the HUGE page table entry.
+ **/
+#define MAKE_HUGE_PTE(Address, Attributes)  (PTE){(((((Address) >> PMD_SHIFT) << PMD_SHIFT) | \
+                                             ((Attributes) | (GET_GLOBALBIT(Attributes) << PAGE_HGLOBAL_SHIFT) | \
+                                             PAGE_HUGE)))}
+
+/**
+  Check whether the large page table entry is.
+
+  @param  Val The value of the page table entry.
+
+  @retval    1   Is huge page table entry.
+  @retval    0   Isn't huge page table entry.
+**/
+#define IS_HUGE_PAGE(Val)  ((((Val) & PAGE_HUGE) == PAGE_HUGE) && \
+                            (((Val) & PAGE_HGLOBAL) == PAGE_HGLOBAL))
+
+#define HUGE_PAGE_SIZE  (PMD_SIZE)
+
+/**
+  Check that the global page directory table entry is empty.
+
+  @param  pgd   the global page directory struct variables.
+
+  @retval    1   The page table is invalid.
+  @retval    0   The page table is valid.
+**/
+#define PGD_IS_EMPTY(Val)  (PGD_VAL(Val) == INVALID_PAGE)
+
+/**
+  Check that the page upper directory table entry is empty.
+
+  @param  pud   Page upper directory struct variables.
+
+  @retval    1   The page table is invalid.
+  @retval    0   The page table is valid.
+**/
+#define PUD_IS_EMPTY(Val)  (PUD_VAL(Val) == INVALID_PAGE)
+
+/**
+  Check that the page middle directory table entry is empty.
+
+  @param  pmd   Page middle directory struct variables.
+
+  @retval    1   The page table is invalid.
+  @retval    0   The page table is valid.
+**/
+#define PMD_IS_EMPTY(Val)  (PMD_VAL(Val) == INVALID_PAGE)
+
+/**
+  Check that the page the page table entry is empty.
+
+  @param  pte   Page table entry struct variables.
+
+  @retval    1   The page table is invalid.
+  @retval    0   The page table is valid.
+**/
+#define PTE_IS_EMPTY(Val)  (!(PTE_VAL(Val) & (~PAGE_VALID)))
+#endif // PAGE_H_
diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
new file mode 100644
index 0000000000..c214e8d847
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
@@ -0,0 +1,178 @@
+/** @file
+  CPU Memory Map Unit PEI phase driver.
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - Tlb      - Translation Lookaside Buffer
+**/
+
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/CpuMmuLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Register/LoongArch64/Csr.h>
+
+#include "Page.h"
+#include "Tlb.h"
+#include "CommonMmuLib.h"
+
+//
+// For coding convenience, define the maximum valid
+// LoongArch exception.
+// Since UEFI V2.11, it will be present in DebugSupport.h.
+//
+#define MAX_LOONGARCH_EXCEPTION  64
+
+/**
+  Create a page table and initialize the memory management unit(MMU).
+
+  @param[in]   MemoryTable           A pointer to a memory ragion table.
+  @param[out]  TranslationTableBase  A pointer to a translation table base address.
+  @param[out]  TranslationTableSize  A pointer to a translation table base size.
+
+  @retval  EFI_SUCCESS                Configure MMU successfully.
+           EFI_INVALID_PARAMETER      MemoryTable is NULL.
+           EFI_UNSUPPORTED            Out of memory space or size not aligned.
+**/
+EFI_STATUS
+EFIAPI
+ConfigureMemoryManagementUnit (
+  IN  MEMORY_REGION_DESCRIPTOR  *MemoryTable,
+  OUT VOID                      **TranslationTableBase OPTIONAL,
+  OUT UINTN                     *TranslationTableSize  OPTIONAL
+  )
+{
+  PGD            *SwapperPageDir;
+  UINTN          PgdShift;
+  UINTN          PgdWide;
+  UINTN          PudShift;
+  UINTN          PudWide;
+  UINTN          PmdShift;
+  UINTN          PmdWide;
+  UINTN          PteShift;
+  UINTN          PteWide;
+  UINTN          Length;
+  UINTN          TlbReEntry;
+  UINTN          TlbReEntryOffset;
+  UINTN          Remaining;
+  RETURN_STATUS  Status;
+
+  SwapperPageDir = NULL;
+  PgdShift       = PGD_SHIFT;
+  PgdWide        = PGD_WIDE;
+  PudShift       = PUD_SHIFT;
+  PudWide        = PUD_WIDE;
+  PmdShift       = PMD_SHIFT;
+  PmdWide        = PMD_WIDE;
+  PteShift       = PTE_SHIFT;
+  PteWide        = PTE_WIDE;
+
+  if (MemoryTable == NULL) {
+    ASSERT (MemoryTable != NULL);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  SwapperPageDir = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
+  ZeroMem (SwapperPageDir, PGD_TABLE_SIZE);
+
+  if (SwapperPageDir == NULL) {
+    goto FreeTranslationTable;
+  }
+
+  CsrWrite (LOONGARCH_CSR_PGDL, (UINTN)SwapperPageDir);
+
+  while (MemoryTable->Length != 0) {
+    DEBUG ((
+      DEBUG_INFO,
+      "%a %d VirtualBase %p VirtualEnd %p Attributes %p .\n",
+      __func__,
+      __LINE__,
+      MemoryTable->VirtualBase,
+      (MemoryTable->Length + MemoryTable->VirtualBase),
+      MemoryTable->Attributes
+      ));
+
+    Status = FillTranslationTable (MemoryTable);
+    if (EFI_ERROR (Status)) {
+      goto FreeTranslationTable;
+    }
+
+    MemoryTable++;
+  }
+
+  //
+  // TLB Re-entry address at the end of exception vector, a vector is up to 512 bytes,
+  // so the starting address is: total exception vector size + total interrupt vector size + base.
+  // The total size of TLB handler and exception vector size and interrupt vector size should not
+  // be lager than 64KB.
+  //
+  Length           = (UINTN)HandleTlbRefillEnd - (UINTN)HandleTlbRefillStart;
+  TlbReEntryOffset = (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * 512;
+  Remaining        = TlbReEntryOffset % SIZE_4KB;
+  if (Remaining != 0x0) {
+    TlbReEntryOffset += (SIZE_4KB - Remaining);
+  }
+
+  TlbReEntry = PcdGet64 (PcdCpuExceptionVectorBaseAddress) + TlbReEntryOffset;
+  if ((TlbReEntryOffset + Length) > SIZE_64KB) {
+    goto FreeTranslationTable;
+  }
+
+  //
+  // Ensure that TLB refill exception base address alignment is equals to 4KB and is valid.
+  //
+  if (TlbReEntry & (SIZE_4KB - 1)) {
+    goto FreeTranslationTable;
+  }
+
+  CopyMem ((VOID *)TlbReEntry, HandleTlbRefillStart, Length);
+  InvalidateInstructionCacheRange ((VOID *)(UINTN)HandleTlbRefillStart, Length);
+
+  DEBUG ((
+    DEBUG_INFO,
+    "%a  %d PteShift %d PteWide %d PmdShift %d PmdWide %d PudShift %d PudWide %d PgdShift %d PgdWide %d.\n",
+    __func__,
+    __LINE__,
+    PteShift,
+    PteWide,
+    PmdShift,
+    PmdWide,
+    PudShift,
+    PudWide,
+    PgdShift,
+    PgdWide
+    ));
+
+  //
+  // Set the address of TLB refill exception handler
+  //
+  SetTlbRebaseAddress ((UINTN)TlbReEntry);
+
+  //
+  // Set page size
+  //
+  CsrXChg (LOONGARCH_CSR_TLBIDX, (DEFAULT_PAGE_SIZE << CSR_TLBIDX_SIZE), CSR_TLBIDX_SIZE_MASK);
+  CsrWrite (LOONGARCH_CSR_STLBPGSIZE, DEFAULT_PAGE_SIZE);
+  CsrXChg (LOONGARCH_CSR_TLBREHI, (DEFAULT_PAGE_SIZE << CSR_TLBREHI_PS_SHIFT), CSR_TLBREHI_PS);
+
+  CsrWrite (LOONGARCH_CSR_PWCTL0, (PteShift | PteWide << 5 | PmdShift << 10 | PmdWide << 15 | PudShift << 20 | PudWide << 25));
+  CsrWrite (LOONGARCH_CSR_PWCTL1, (PgdShift | PgdWide << 6));
+
+  DEBUG ((DEBUG_INFO, "%a %d Enable Mmu Start PageBassAddress %p.\n", __func__, __LINE__, SwapperPageDir));
+
+  return EFI_SUCCESS;
+
+FreeTranslationTable:
+  if (SwapperPageDir != NULL) {
+    FreePages (SwapperPageDir, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
+  }
+
+  return EFI_UNSUPPORTED;
+}
diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
new file mode 100644
index 0000000000..9a681ce8e1
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
@@ -0,0 +1,48 @@
+/** @file
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef TLB_H_
+#define TLB_H_
+
+/**
+  Invalid corresponding TLB entries are based on the address given
+
+  @param Address The address corresponding to the invalid page table entry
+
+  @retval  none
+**/
+VOID
+InvalidTlb (
+  UINTN  Address
+  );
+
+/**
+  TLB refill handler start.
+
+  @param  none
+
+  @retval none
+**/
+VOID
+HandleTlbRefillStart (
+  VOID
+  );
+
+/**
+  TLB refill handler end.
+
+  @param  none
+
+  @retval none
+**/
+VOID
+HandleTlbRefillEnd (
+  VOID
+  );
+
+#endif // TLB_H_
diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
new file mode 100644
index 0000000000..c9a8c16336
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
@@ -0,0 +1,44 @@
+#------------------------------------------------------------------------------
+#
+# TLB operation functions
+#
+# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#-----------------------------------------------------------------------------
+
+#include <Register/LoongArch64/Csr.h>
+
+ASM_GLOBAL ASM_PFX(HandleTlbRefillStart)
+ASM_GLOBAL ASM_PFX(HandleTlbRefillEnd)
+ASM_GLOBAL ASM_PFX(InvalidTlb)
+
+#
+#  Refill the page table.
+#  @param  VOID
+#  @retval  VOID
+#
+ASM_PFX(HandleTlbRefillStart):
+  csrwr   $t0, LOONGARCH_CSR_TLBRSAVE
+  csrrd   $t0, LOONGARCH_CSR_PGD
+  lddir   $t0, $t0, 3   #Put pud BaseAddress into T0
+  lddir   $t0, $t0, 2   #Put pmd BaseAddress into T0
+  lddir   $t0, $t0, 1   #Put pte BaseAddress into T0
+  ldpte   $t0, 0
+  ldpte   $t0, 1
+  tlbfill   // refill hi,lo0,lo1
+  csrrd   $t0, LOONGARCH_CSR_TLBRSAVE
+  ertn
+ASM_PFX(HandleTlbRefillEnd):
+
+#
+# Invalid corresponding TLB entries are based on the address given
+# @param a0 The address corresponding to the invalid page table entry
+# @retval  none
+#
+ASM_PFX(InvalidTlb):
+    invtlb  INVTLB_ADDR_GTRUE_OR_ASID, $zero, $a0
+    jirl    $zero, $ra, 0
+
+    .end
diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
new file mode 100644
index 0000000000..45b15db4c9
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
@@ -0,0 +1,44 @@
+## @file
+#  CPU Memory Map Unit PEI phase driver.
+#
+#  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                      = PeiCpuMmuLib
+  MODULE_UNI_FILE                = PeiCpuMmuLib.uni
+  FILE_GUID                      = F67EB983-AC2A-7550-AB69-3BC51A1C895B
+  MODULE_TYPE                    = PEIM
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = CpuMmuLib | SEC PEIM
+
+#
+#  VALID_ARCHITECTURES           = LOONGARCH64
+#
+
+[Sources.LoongArch64]
+  LoongArch64/TlbOperation.S   | GCC
+  LoongArch64/CommonMmuLib.c
+  LoongArch64/PeiCpuMmuLib.c
+  LoongArch64/CommonMmuLib.h
+  LoongArch64/Tlb.h
+  LoongArch64/Page.h
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  UefiCpuPkg/UefiCpuPkg.dec
+
+[PCD]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask
+  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress
+
+[LibraryClasses]
+  CacheMaintenanceLib
+  DebugLib
+  MemoryAllocationLib
+  PcdLib
diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
new file mode 100644
index 0000000000..3e21334f3e
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
@@ -0,0 +1,14 @@
+// /** @file
+// CPU Memory Manager Unit library instance for PEI modules.
+//
+// CPU Memory Manager Unit library instance for PEI modules.
+//
+// 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 "CPU Memory Manager Unit library instance for PEI modules."
+
+#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for PEI modules."
diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
index 28eed85bce..178dc3c0f9 100644
--- a/UefiCpuPkg/UefiCpuPkg.dsc
+++ b/UefiCpuPkg/UefiCpuPkg.dsc
@@ -207,5 +207,9 @@
   UefiCpuPkg/CpuTimerDxeRiscV64/CpuTimerDxeRiscV64.inf
   UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf
 
+[Components.LOONGARCH64]
+  UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
+  UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.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 (#114540): https://edk2.groups.io/g/devel/message/114540
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 15/37] UefiCpuPkg: Add multiprocessor library for LoongArch64
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (13 preceding siblings ...)
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib " Chao Li
@ 2024-01-26  6:29 ` Chao Li
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 16/37] UefiCpuPkg: Add CpuDxe driver " Chao Li
                   ` (26 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:29 UTC (permalink / raw)
  To: devel; +Cc: Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann

Added LoongArch multiprocessor initialization instance into MpInitLib.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
---
 UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf |   27 +-
 .../Library/MpInitLib/LoongArch64/DxeMpLib.c  |  480 +++++
 .../Library/MpInitLib/LoongArch64/MpLib.c     | 1621 +++++++++++++++++
 .../Library/MpInitLib/LoongArch64/MpLib.h     |  361 ++++
 .../Library/MpInitLib/LoongArch64/PeiMpLib.c  |  404 ++++
 UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf |   27 +-
 6 files changed, 2902 insertions(+), 18 deletions(-)
 create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/DxeMpLib.c
 create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.c
 create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.h
 create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/PeiMpLib.c

diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
index 55e46d4a1f..6db26f5fec 100644
--- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
+++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
@@ -2,6 +2,7 @@
 #  MP Initialize Library instance for DXE driver.
 #
 #  Copyright (c) 2016 - 2023, Intel Corporation. All rights reserved.<BR>
+#  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
 #  SPDX-License-Identifier: BSD-2-Clause-Patent
 #
 ##
@@ -18,7 +19,7 @@
 #
 # The following information is for reference only and not required by the build tools.
 #
-#  VALID_ARCHITECTURES           = IA32 X64
+#  VALID_ARCHITECTURES           = IA32 X64 LOONGARCH64
 #
 
 [Sources.IA32]
@@ -31,7 +32,7 @@
   X64/MpFuncs.nasm
   X64/CreatePageTable.c
 
-[Sources.common]
+[Sources.IA32, Sources.X64]
   AmdSev.c
   MpEqu.inc
   DxeMpLib.c
@@ -40,24 +41,32 @@
   Microcode.c
   MpHandOff.h
 
+[Sources.LoongArch64]
+  LoongArch64/DxeMpLib.c
+  LoongArch64/MpLib.c
+  LoongArch64/MpLib.h
+
 [Packages]
   MdePkg/MdePkg.dec
   MdeModulePkg/MdeModulePkg.dec
   UefiCpuPkg/UefiCpuPkg.dec
 
-[LibraryClasses]
+[LibraryClasses.common]
   BaseLib
-  LocalApicLib
-  MemoryAllocationLib
-  HobLib
-  MtrrLib
   CpuLib
-  UefiBootServicesTableLib
   DebugAgentLib
-  SynchronizationLib
+  HobLib
+  MemoryAllocationLib
   PcdLib
+  UefiBootServicesTableLib
+  SynchronizationLib
+
+[LibraryClasses.IA32, LibraryClasses.X64]
   CcExitLib
+  LocalApicLib
   MicrocodeLib
+  MtrrLib
+
 [LibraryClasses.X64]
   CpuPageTableLib
 
diff --git a/UefiCpuPkg/Library/MpInitLib/LoongArch64/DxeMpLib.c b/UefiCpuPkg/Library/MpInitLib/LoongArch64/DxeMpLib.c
new file mode 100644
index 0000000000..739da77e32
--- /dev/null
+++ b/UefiCpuPkg/Library/MpInitLib/LoongArch64/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/MpInitLib/LoongArch64/MpLib.c b/UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.c
new file mode 100644
index 0000000000..930d34aa3d
--- /dev/null
+++ b/UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.c
@@ -0,0 +1,1621 @@
+/** @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>
+
+#define INVALID_APIC_ID  0xFFFFFFFF
+
+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) {
+    Index2 = 0;
+    for (Index1 = (PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1); Index1 > 0; Index1--) {
+      if (CpuInfoInHob[Index1].ApicId != INVALID_APIC_ID) {
+        if (Index1 == ApCount) {
+          break;
+        } else {
+          for ( ; Index2 <= ApCount; Index2++) {
+            if (CpuInfoInHob[Index2].ApicId == INVALID_APIC_ID) {
+              CopyMem (&CpuInfoInHob[Index2], &CpuInfoInHob[Index1], sizeof (CPU_INFO_IN_HOB));
+              CpuMpData->CpuData[Index2]  = CpuMpData->CpuData[Index1];
+              CpuInfoInHob[Index1].ApicId = INVALID_APIC_ID;
+              break;
+            }
+          }
+        }
+      } else {
+        continue;
+      }
+    }
+
+    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 and initialization APs ApicId status.
+  //
+  for (Index = 0; Index < MaxLogicalProcessorNumber; Index++) {
+    CpuMpData->CpuData[Index].StartupApSignal =
+      (UINT32 *)((MpBuffer + BufferSize - MonitorBufferSize) + (sizeof (WAKEUP_AP_SIGNAL) * Index));
+    if ((OldCpuMpData == NULL) && (Index != CpuMpData->BspNumber)) {
+      ((CPU_INFO_IN_HOB  *)CpuMpData->CpuInfoInHob)[Index].ApicId = INVALID_APIC_ID;
+    }
+  }
+
+  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/MpInitLib/LoongArch64/MpLib.h b/UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.h
new file mode 100644
index 0000000000..b9c6c55b41
--- /dev/null
+++ b/UefiCpuPkg/Library/MpInitLib/LoongArch64/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 MP_CPU_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 MP_CPU_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/MpInitLib/LoongArch64/PeiMpLib.c b/UefiCpuPkg/Library/MpInitLib/LoongArch64/PeiMpLib.c
new file mode 100644
index 0000000000..d1c5e55b57
--- /dev/null
+++ b/UefiCpuPkg/Library/MpInitLib/LoongArch64/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/Library/MpInitLib/PeiMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
index bc3d716aa9..36ee6b9c29 100644
--- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
+++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
@@ -2,6 +2,7 @@
 #  MP Initialize Library instance for PEI driver.
 #
 #  Copyright (c) 2016 - 2021, Intel Corporation. All rights reserved.<BR>
+#  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
 #  SPDX-License-Identifier: BSD-2-Clause-Patent
 #
 ##
@@ -18,7 +19,7 @@
 #
 # The following information is for reference only and not required by the build tools.
 #
-#  VALID_ARCHITECTURES           = IA32 X64
+#  VALID_ARCHITECTURES           = IA32 X64 LOONGARCH64
 #
 
 [Sources.IA32]
@@ -29,7 +30,7 @@
   X64/AmdSev.c
   X64/MpFuncs.nasm
 
-[Sources.common]
+[Sources.IA32, Sources.X64]
   AmdSev.c
   MpEqu.inc
   PeiMpLib.c
@@ -37,23 +38,31 @@
   MpLib.h
   Microcode.c
   MpHandOff.h
+
+[Sources.LoongArch64]
+  LoongArch64/PeiMpLib.c
+  LoongArch64/MpLib.c
+  LoongArch64/MpLib.h
+
 [Packages]
   MdePkg/MdePkg.dec
   UefiCpuPkg/UefiCpuPkg.dec
   MdeModulePkg/MdeModulePkg.dec
 
-[LibraryClasses]
+[LibraryClasses.common]
   BaseLib
-  LocalApicLib
-  MemoryAllocationLib
-  HobLib
-  MtrrLib
   CpuLib
-  SynchronizationLib
-  PeiServicesLib
+  HobLib
+  MemoryAllocationLib
   PcdLib
+  PeiServicesLib
+  SynchronizationLib
+
+[LibraryClasses.IA32, LibraryClasses.X64]
   CcExitLib
+  LocalApicLib
   MicrocodeLib
+  MtrrLib
 
 [Pcd]
   gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber        ## CONSUMES
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114541): https://edk2.groups.io/g/devel/message/114541
Mute This Topic: https://groups.io/mt/103971654/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 16/37] UefiCpuPkg: Add CpuDxe driver for LoongArch64
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (14 preceding siblings ...)
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 15/37] UefiCpuPkg: Add multiprocessor library for LoongArch64 Chao Li
@ 2024-01-26  6:29 ` Chao Li
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 17/37] EmbeddedPkg: Add PcdPrePiCpuIoSize width for LOONGARCH64 Chao Li
                   ` (25 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:29 UTC (permalink / raw)
  To: devel
  Cc: Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian

Added LoongArch64 CPU driver into CpuDxe.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Co-authored-by: Baoqi Zhang <zhangbaoqi@loongson.cn>
Co-authored-by: Dongyan Qian <qiandongyan@loongson.cn>
---
 UefiCpuPkg/CpuDxe/CpuDxe.inf              |  23 +-
 UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.c    | 454 ++++++++++++++++++
 UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.h    | 288 ++++++++++++
 UefiCpuPkg/CpuDxe/LoongArch64/CpuMp.c     | 544 ++++++++++++++++++++++
 UefiCpuPkg/CpuDxe/LoongArch64/Exception.c | 159 +++++++
 5 files changed, 1464 insertions(+), 4 deletions(-)
 create mode 100644 UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.c
 create mode 100644 UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.h
 create mode 100644 UefiCpuPkg/CpuDxe/LoongArch64/CpuMp.c
 create mode 100644 UefiCpuPkg/CpuDxe/LoongArch64/Exception.c

diff --git a/UefiCpuPkg/CpuDxe/CpuDxe.inf b/UefiCpuPkg/CpuDxe/CpuDxe.inf
index 1d3e9f8cdb..18ebd2eb2c 100644
--- a/UefiCpuPkg/CpuDxe/CpuDxe.inf
+++ b/UefiCpuPkg/CpuDxe/CpuDxe.inf
@@ -3,6 +3,7 @@
 #
 #  Copyright (c) 2008 - 2019, Intel Corporation. All rights reserved.<BR>
 #  Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
+#  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
 #
 #  SPDX-License-Identifier: BSD-2-Clause-Patent
 #
@@ -22,17 +23,16 @@
   MdeModulePkg/MdeModulePkg.dec
   UefiCpuPkg/UefiCpuPkg.dec
 
-[LibraryClasses]
+[LibraryClasses.common]
   BaseLib
   BaseMemoryLib
   CpuLib
+  CacheMaintenanceLib
   DebugLib
   DxeServicesTableLib
   MemoryAllocationLib
-  MtrrLib
   UefiBootServicesTableLib
   UefiDriverEntryPoint
-  LocalApicLib
   UefiLib
   CpuExceptionHandlerLib
   HobLib
@@ -41,7 +41,14 @@
   TimerLib
   PeCoffGetEntryPointLib
 
-[Sources]
+[LibraryClasses.IA32, LibraryClasses.X64]
+  LocalApicLib
+  MtrrLib
+
+[LibraryClasses.LoongArch64]
+  CpuMmuLib
+
+[Sources.IA32, Sources.X64]
   CpuDxe.c
   CpuDxe.h
   CpuGdt.c
@@ -59,6 +66,13 @@
   X64/CpuAsm.nasm
   X64/PagingAttribute.c
 
+[Sources.LoongArch64]
+  CpuMp.h
+  LoongArch64/CpuDxe.c
+  LoongArch64/CpuMp.c
+  LoongArch64/Exception.c
+  LoongArch64/CpuDxe.h
+
 [Protocols]
   gEfiCpuArchProtocolGuid                       ## PRODUCES
   gEfiMpServiceProtocolGuid                     ## PRODUCES
@@ -77,6 +91,7 @@
   gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard                       ## CONSUMES
   gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPropertyMask               ## CONSUMES
   gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask    ## CONSUMES
+  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress            ## CONSUMES
   gUefiCpuPkgTokenSpaceGuid.PcdCpuStackSwitchExceptionList              ## CONSUMES
   gUefiCpuPkgTokenSpaceGuid.PcdCpuKnownGoodStackSize                    ## CONSUMES
   gEfiMdeModulePkgTokenSpaceGuid.PcdTdxSharedBitMask                    ## CONSUMES
diff --git a/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.c b/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.c
new file mode 100644
index 0000000000..65ed0b3913
--- /dev/null
+++ b/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.c
@@ -0,0 +1,454 @@
+/** @file CpuDxe.c
+
+  CPU DXE Module to produce CPU ARCH Protocol.
+
+  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "CpuDxe.h"
+#include "CpuMp.h"
+#include <Guid/IdleLoopEvent.h>
+#include <Library/CpuMmuLib.h>
+#include <Library/TimerLib.h>
+#include <Register/LoongArch64/Csr.h>
+
+UINT64  mTimerPeriod = 0;
+
+/**
+  IPI Interrupt Handler.
+
+  @param InterruptType    The type of interrupt that occurred
+  @param SystemContext    A pointer to the system context when the interrupt occurred
+**/
+VOID
+EFIAPI
+IpiInterruptHandler (
+  IN EFI_EXCEPTION_TYPE  InterruptType,
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  );
+
+//
+// Globals used to initialize the protocol
+//
+EFI_HANDLE             mCpuHandle = NULL;
+EFI_CPU_ARCH_PROTOCOL  gCpu       = {
+  CpuFlushCpuDataCache,
+  CpuEnableInterrupt,
+  CpuDisableInterrupt,
+  CpuGetInterruptState,
+  CpuInit,
+  CpuRegisterInterruptHandler,
+  CpuGetTimerValue,
+  CpuSetMemoryAttributes,
+  0,          // NumberOfTimers
+  4,          // DmaBufferAlignment
+};
+
+/**
+  This function flushes the range of addresses from Start to Start+Length
+  from the processor's data cache. If Start is not aligned to a cache line
+  boundary, then the bytes before Start to the preceding cache line boundary
+  are also flushed. If Start+Length is not aligned to a cache line boundary,
+  then the bytes past Start+Length to the end of the next cache line boundary
+  are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be
+  supported. If the data cache is fully coherent with all DMA operations, then
+  this function can just return EFI_SUCCESS. If the processor does not support
+  flushing a range of the data cache, then the entire data cache can be flushed.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  Start            The beginning physical address to flush from the processor's data
+                           cache.
+  @param  Length           The number of bytes to flush from the processor's data cache. This
+                           function may flush more bytes than Length specifies depending upon
+                           the granularity of the flush operation that the processor supports.
+  @param  FlushType        Specifies the type of flush operation to perform.
+
+  @retval EFI_SUCCESS           The address range from Start to Start+Length was flushed from
+                                the processor's data cache.
+  @retval EFI_INVALID_PARAMETER The processor does not support the cache flush type specified
+                                by FlushType.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuFlushCpuDataCache (
+  IN EFI_CPU_ARCH_PROTOCOL  *This,
+  IN EFI_PHYSICAL_ADDRESS   Start,
+  IN UINT64                 Length,
+  IN EFI_CPU_FLUSH_TYPE     FlushType
+  )
+{
+  switch (FlushType) {
+    case EfiCpuFlushTypeWriteBack:
+      WriteBackDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length);
+      break;
+    case EfiCpuFlushTypeInvalidate:
+      InvalidateDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length);
+      break;
+    case EfiCpuFlushTypeWriteBackInvalidate:
+      WriteBackInvalidateDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length);
+      break;
+    default:
+      return EFI_INVALID_PARAMETER;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function enables interrupt processing by the processor.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+
+  @retval EFI_SUCCESS           Interrupts are enabled on the processor.
+  @retval EFI_DEVICE_ERROR      Interrupts could not be enabled on the processor.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuEnableInterrupt (
+  IN EFI_CPU_ARCH_PROTOCOL  *This
+  )
+{
+  EnableInterrupts ();
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function disables interrupt processing by the processor.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+
+  @retval EFI_SUCCESS           Interrupts are disabled on the processor.
+  @retval EFI_DEVICE_ERROR      Interrupts could not be disabled on the processor.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuDisableInterrupt (
+  IN EFI_CPU_ARCH_PROTOCOL  *This
+  )
+{
+  DisableInterrupts ();
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function retrieves the processor's current interrupt state a returns it in
+  State. If interrupts are currently enabled, then TRUE is returned. If interrupts
+  are currently disabled, then FALSE is returned.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  State            A pointer to the processor's current interrupt state. Set to TRUE if
+                           interrupts are enabled and FALSE if interrupts are disabled.
+
+  @retval EFI_SUCCESS           The processor's current interrupt state was returned in State.
+  @retval EFI_INVALID_PARAMETER State is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuGetInterruptState (
+  IN  EFI_CPU_ARCH_PROTOCOL  *This,
+  OUT BOOLEAN                *State
+  )
+{
+  if (State == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *State = GetInterruptState ();
+  return EFI_SUCCESS;
+}
+
+/**
+  This function generates an INIT on the processor. If this function succeeds, then the
+  processor will be reset, and control will not be returned to the caller. If InitType is
+  not supported by this processor, or the processor cannot programmatically generate an
+  INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error
+  occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  InitType         The type of processor INIT to perform.
+
+  @retval EFI_SUCCESS           The processor INIT was performed. This return code should never be seen.
+  @retval EFI_UNSUPPORTED       The processor INIT operation specified by InitType is not supported
+                                by this processor.
+  @retval EFI_DEVICE_ERROR      The processor INIT failed.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuInit (
+  IN EFI_CPU_ARCH_PROTOCOL  *This,
+  IN EFI_CPU_INIT_TYPE      InitType
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Registers a function to be called from the CPU interrupt handler.
+
+  @param  This                   Protocol instance structure
+  @param  InterruptType          Defines which interrupt to hook. IA-32
+                                 valid range is 0x00 through 0xFF
+  @param  InterruptHandler       A pointer to a function of type
+                                 EFI_CPU_INTERRUPT_HANDLER that is called
+                                 when a processor interrupt occurs.  A null
+                                 pointer is an error condition.
+
+  @retval EFI_SUCCESS            If handler installed or uninstalled.
+  @retval EFI_ALREADY_STARTED    InterruptHandler is not NULL, and a handler
+                                 for InterruptType was previously installed.
+  @retval EFI_INVALID_PARAMETER  InterruptHandler is NULL, and a handler for
+                                 InterruptType was not previously installed.
+  @retval EFI_UNSUPPORTED        The interrupt specified by InterruptType
+                                 is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuRegisterInterruptHandler (
+  IN EFI_CPU_ARCH_PROTOCOL      *This,
+  IN EFI_EXCEPTION_TYPE         InterruptType,
+  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
+  )
+{
+  return RegisterInterruptHandler (InterruptType, InterruptHandler);
+}
+
+/**
+  Returns a timer value from one of the CPU's internal timers. There is no
+  inherent time interval between ticks but is a function of the CPU frequency.
+
+  @param  This                - Protocol instance structure.
+  @param  TimerIndex          - Specifies which CPU timer is requested.
+  @param  TimerValue          - Pointer to the returned timer value.
+  @param  TimerPeriod         - A pointer to the amount of time that passes
+                                in femtoseconds (10-15) for each increment
+                                of TimerValue. If TimerValue does not
+                                increment at a predictable rate, then 0 is
+                                returned.  The amount of time that has
+                                passed between two calls to GetTimerValue()
+                                can be calculated with the formula
+                                (TimerValue2 - TimerValue1) * TimerPeriod.
+                                This parameter is optional and may be NULL.
+
+  @retval EFI_SUCCESS           - If the CPU timer count was returned.
+  @retval EFI_UNSUPPORTED       - If the CPU does not have any readable timers.
+  @retval EFI_DEVICE_ERROR      - If an error occurred while reading the timer.
+  @retval EFI_INVALID_PARAMETER - TimerIndex is not valid or TimerValue is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuGetTimerValue (
+  IN  EFI_CPU_ARCH_PROTOCOL  *This,
+  IN  UINT32                 TimerIndex,
+  OUT UINT64                 *TimerValue,
+  OUT UINT64                 *TimerPeriod   OPTIONAL
+  )
+{
+  UINT64  BeginValue;
+  UINT64  EndValue;
+
+  if (TimerValue == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (TimerIndex != 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *TimerValue = AsmReadStableCounter ();
+
+  if (TimerPeriod != NULL) {
+    if (mTimerPeriod == 0) {
+      //
+      // Read time stamp counter before and after delay of 100 microseconds
+      //
+      BeginValue = AsmReadStableCounter ();
+      MicroSecondDelay (100);
+      EndValue = AsmReadStableCounter ();
+      //
+      // Calculate the actual frequency
+      //
+      mTimerPeriod = DivU64x64Remainder (
+                       MultU64x32 (
+                         1000 * 1000 * 1000,
+                         100
+                         ),
+                       EndValue - BeginValue,
+                       NULL
+                       );
+    }
+
+    *TimerPeriod = mTimerPeriod;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function modifies the attributes for the memory region specified by BaseAddress and
+  Length from their current attributes to the attributes specified by Attributes.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  BaseAddress      The physical address that is the start address of a memory region.
+  @param  Length           The size in bytes of the memory region.
+  @param  EfiAttributes    The bit mask of attributes to set for the memory region.
+
+  @retval EFI_SUCCESS           The attributes were set for the memory region.
+  @retval EFI_ACCESS_DENIED     The attributes for the memory resource range specified by
+                                BaseAddress and Length cannot be modified.
+  @retval EFI_INVALID_PARAMETER Length is zero.
+  @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of
+                                the memory resource range.
+  @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory
+                                resource range specified by BaseAddress and Length.
+                                The bit mask of attributes is not support for the memory resource
+                                range specified by BaseAddress and Length.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuSetMemoryAttributes (
+  IN EFI_CPU_ARCH_PROTOCOL  *This,
+  IN EFI_PHYSICAL_ADDRESS   BaseAddress,
+  IN UINT64                 Length,
+  IN UINT64                 EfiAttributes
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       LoongArchAttributes;
+  UINTN       RegionBaseAddress;
+  UINTN       RegionLength;
+  UINTN       RegionLoongArchAttributes;
+
+  RegionLength = Length;
+  Status       = EFI_SUCCESS;
+
+  if ((BaseAddress & (EFI_PAGE_SIZE - 1)) != 0) {
+    //
+    // Minimum granularity is SIZE_4KB.
+    //
+    DEBUG ((
+      DEBUG_INFO,
+      "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum granularity is SIZE_4KB\n",
+      BaseAddress,
+      Length,
+      EfiAttributes
+      ));
+
+    Status = EFI_UNSUPPORTED;
+
+    return Status;
+  }
+
+  //
+  // Convert the 'Attribute' into LoongArch Attribute
+  //
+  LoongArchAttributes = EfiAttributeConverse (EfiAttributes);
+
+  //
+  // Get the region starting from 'BaseAddress' and its 'Attribute'
+  //
+  RegionBaseAddress = BaseAddress;
+  Status            = GetMemoryRegionAttributes (
+                        RegionBaseAddress,
+                        &RegionLength,
+                        &RegionLoongArchAttributes
+                        );
+
+  //
+  // Data & Instruction Caches are flushed when we set new memory attributes.
+  // So, we only set the attributes if the new region is different.
+  //
+  if ((Status == EFI_NOT_FOUND) || (RegionLoongArchAttributes != LoongArchAttributes) ||
+      ((BaseAddress + Length) > (RegionBaseAddress + RegionLength)))
+  {
+    Status = SetMemoryRegionAttributes (BaseAddress, Length, EfiAttributes, 0x0);
+  }
+
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
+
+/**
+  Callback function for idle events.
+
+  @param  Event                 Event whose notification function is being invoked.
+  @param  Context               The pointer to the notification function's context,
+                                which is implementation-dependent.
+
+**/
+VOID
+EFIAPI
+IdleLoopEventCallback (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+{
+  CpuSleep ();
+}
+
+/**
+  Initialize the state information for the CPU Architectural Protocol.
+
+  @param ImageHandle     Image handle this driver.
+  @param SystemTable     Pointer to the System Table.
+
+  @retval EFI_SUCCESS           Thread can be successfully created
+  @retval EFI_OUT_OF_RESOURCES  Cannot allocate protocol data structure
+  @retval EFI_DEVICE_ERROR      Cannot create the thread
+
+**/
+EFI_STATUS
+InitializeCpu (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+  EFI_EVENT   IdleLoopEvent;
+
+  InitializeExceptions (&gCpu);
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &mCpuHandle,
+                  &gEfiCpuArchProtocolGuid,
+                  &gCpu,
+                  NULL
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  Status = gCpu.RegisterInterruptHandler (
+                  &gCpu,
+                  EXCEPT_LOONGARCH_INT_IPI,
+                  IpiInterruptHandler
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Setup a callback for idle events
+  //
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  IdleLoopEventCallback,
+                  NULL,
+                  &gIdleLoopEventGuid,
+                  &IdleLoopEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  InitializeMpSupport ();
+
+  return Status;
+}
diff --git a/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.h b/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.h
new file mode 100644
index 0000000000..8bfbfa3442
--- /dev/null
+++ b/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.h
@@ -0,0 +1,288 @@
+/** @file CpuDxe.c
+
+  CPU DXE Module to produce CPU ARCH Protocol.
+
+  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef CPU_DXE_H_
+#define CPU_DXE_H_
+
+#include <Uefi.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/CpuLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/MpInitLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PeCoffGetEntryPointLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include <Guid/DebugImageInfoTable.h>
+#include <Protocol/Cpu.h>
+#include <Protocol/DebugSupport.h>
+#include <Protocol/LoadedImage.h>
+
+//
+// For coding convenience, define the maximum valid
+// LoongArch exception.
+// Since UEFI V2.11, it will be present in DebugSupport.h.
+//
+#define MAX_LOONGARCH_EXCEPTION  64
+
+/*
+  This function flushes the range of addresses from Start to Start+Length
+  from the processor's data cache. If Start is not aligned to a cache line
+  boundary, then the bytes before Start to the preceding cache line boundary
+  are also flushed. If Start+Length is not aligned to a cache line boundary,
+  then the bytes past Start+Length to the end of the next cache line boundary
+  are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be
+  supported. If the data cache is fully coherent with all DMA operations, then
+  this function can just return EFI_SUCCESS. If the processor does not support
+  flushing a range of the data cache, then the entire data cache can be flushed.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  Start            The beginning physical address to flush from the processor's data
+                           cache.
+  @param  Length           The number of bytes to flush from the processor's data cache. This
+                           function may flush more bytes than Length specifies depending upon
+                           the granularity of the flush operation that the processor supports.
+  @param  FlushType        Specifies the type of flush operation to perform.
+
+  @retval EFI_SUCCESS           The address range from Start to Start+Length was flushed from
+                                the processor's data cache.
+  @retval EFI_UNSUPPORTEDT      The processor does not support the cache flush type specified
+                                by FlushType.
+  @retval EFI_DEVICE_ERROR      The address range from Start to Start+Length could not be flushed
+                                from the processor's data cache.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuFlushCpuDataCache (
+  IN EFI_CPU_ARCH_PROTOCOL  *This,
+  IN EFI_PHYSICAL_ADDRESS   Start,
+  IN UINT64                 Length,
+  IN EFI_CPU_FLUSH_TYPE     FlushType
+  );
+
+/**
+  This function enables interrupt processing by the processor.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+
+  @retval EFI_SUCCESS           Interrupts are enabled on the processor.
+  @retval EFI_DEVICE_ERROR      Interrupts could not be enabled on the processor.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuEnableInterrupt (
+  IN EFI_CPU_ARCH_PROTOCOL  *This
+  );
+
+/**
+  This function disables interrupt processing by the processor.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+
+  @retval EFI_SUCCESS           Interrupts are disabled on the processor.
+  @retval EFI_DEVICE_ERROR      Interrupts could not be disabled on the processor.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuDisableInterrupt (
+  IN EFI_CPU_ARCH_PROTOCOL  *This
+  );
+
+/**
+  This function retrieves the processor's current interrupt state a returns it in
+  State. If interrupts are currently enabled, then TRUE is returned. If interrupts
+  are currently disabled, then FALSE is returned.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  State            A pointer to the processor's current interrupt state. Set to TRUE if
+                           interrupts are enabled and FALSE if interrupts are disabled.
+
+  @retval EFI_SUCCESS           The processor's current interrupt state was returned in State.
+  @retval EFI_INVALID_PARAMETER State is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuGetInterruptState (
+  IN  EFI_CPU_ARCH_PROTOCOL  *This,
+  OUT BOOLEAN                *State
+  );
+
+/**
+  This function generates an INIT on the processor. If this function succeeds, then the
+  processor will be reset, and control will not be returned to the caller. If InitType is
+  not supported by this processor, or the processor cannot programmatically generate an
+  INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error
+  occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  InitType         The type of processor INIT to perform.
+
+  @retval EFI_SUCCESS           The processor INIT was performed. This return code should never be seen.
+  @retval EFI_UNSUPPORTED       The processor INIT operation specified by InitType is not supported
+                                by this processor.
+  @retval EFI_DEVICE_ERROR      The processor INIT failed.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuInit (
+  IN EFI_CPU_ARCH_PROTOCOL  *This,
+  IN EFI_CPU_INIT_TYPE      InitType
+  );
+
+/**
+  Registers a function to be called from the CPU interrupt handler.
+
+  @param  This                   Protocol instance structure
+  @param  InterruptType          Defines which interrupt to hook. IA-32
+                                 valid range is 0x00 through 0xFF
+  @param  InterruptHandler       A pointer to a function of type
+                                 EFI_CPU_INTERRUPT_HANDLER that is called
+                                 when a processor interrupt occurs.  A null
+                                 pointer is an error condition.
+
+  @retval EFI_SUCCESS            If handler installed or uninstalled.
+  @retval EFI_ALREADY_STARTED    InterruptHandler is not NULL, and a handler
+                                 for InterruptType was previously installed.
+  @retval EFI_INVALID_PARAMETER  InterruptHandler is NULL, and a handler for
+                                 InterruptType was not previously installed.
+  @retval EFI_UNSUPPORTED        The interrupt specified by InterruptType
+                                 is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuRegisterInterruptHandler (
+  IN EFI_CPU_ARCH_PROTOCOL      *This,
+  IN EFI_EXCEPTION_TYPE         InterruptType,
+  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
+  );
+
+/**
+  Returns a timer value from one of the CPU's internal timers. There is no
+  inherent time interval between ticks but is a function of the CPU frequency.
+
+  @param  This                - Protocol instance structure.
+  @param  TimerIndex          - Specifies which CPU timer is requested.
+  @param  TimerValue          - Pointer to the returned timer value.
+  @param  TimerPeriod         - A pointer to the amount of time that passes
+                                in femtoseconds (10-15) for each increment
+                                of TimerValue. If TimerValue does not
+                                increment at a predictable rate, then 0 is
+                                returned.  The amount of time that has
+                                passed between two calls to GetTimerValue()
+                                can be calculated with the formula
+                                (TimerValue2 - TimerValue1) * TimerPeriod.
+                                This parameter is optional and may be NULL.
+
+  @retval EFI_SUCCESS           - If the CPU timer count was returned.
+  @retval EFI_UNSUPPORTED       - If the CPU does not have any readable timers.
+  @retval EFI_DEVICE_ERROR      - If an error occurred while reading the timer.
+  @retval EFI_INVALID_PARAMETER - TimerIndex is not valid or TimerValue is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuGetTimerValue (
+  IN  EFI_CPU_ARCH_PROTOCOL  *This,
+  IN  UINT32                 TimerIndex,
+  OUT UINT64                 *TimerValue,
+  OUT UINT64                 *TimerPeriod   OPTIONAL
+  );
+
+/**
+  This function registers and enables the handler specified by InterruptHandler for a processor
+  interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
+  handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
+  The installed handler is called once for each processor interrupt or exception.
+
+  @param  InterruptType    A pointer to the processor's current interrupt state. Set to TRUE if interrupts
+                           are enabled and FALSE if interrupts are disabled.
+  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
+                           when a processor interrupt occurs. If this parameter is NULL, then the handler
+                           will be uninstalled.
+
+  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
+  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was
+                                previously installed.
+  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
+                                previously installed.
+  @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
+
+**/
+EFI_STATUS
+RegisterInterruptHandler (
+  IN EFI_EXCEPTION_TYPE         InterruptType,
+  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
+  );
+
+/**
+  This function modifies the attributes for the memory region specified by BaseAddress and
+  Length from their current attributes to the attributes specified by Attributes.
+
+  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
+  @param  BaseAddress      The physical address that is the start address of a memory region.
+  @param  Length           The size in bytes of the memory region.
+  @param  Attributes       The bit mask of attributes to set for the memory region.
+
+  @retval EFI_SUCCESS           The attributes were set for the memory region.
+  @retval EFI_ACCESS_DENIED     The attributes for the memory resource range specified by
+                                BaseAddress and Length cannot be modified.
+  @retval EFI_INVALID_PARAMETER Length is zero.
+  @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of
+                                the memory resource range.
+  @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory
+                                resource range specified by BaseAddress and Length.
+                                The bit mask of attributes is not support for the memory resource
+                                range specified by BaseAddress and Length.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuSetMemoryAttributes (
+  IN EFI_CPU_ARCH_PROTOCOL  *This,
+  IN EFI_PHYSICAL_ADDRESS   BaseAddress,
+  IN UINT64                 Length,
+  IN UINT64                 Attributes
+  );
+
+/**
+  Initialize interrupt handling for DXE phase.
+
+  @param  Cpu A pointer of EFI_CPU_ARCH_PROTOCOL instance.
+
+  @return VOID.
+
+**/
+VOID
+InitializeExceptions (
+  IN EFI_CPU_ARCH_PROTOCOL  *gCpu
+  );
+
+/**
+  Converts EFI Attributes to corresponding architecture Attributes.
+
+  @param[in]  EfiAttributes     Efi Attributes.
+
+  @retval  Corresponding architecture attributes.
+**/
+UINTN
+EFIAPI
+EfiAttributeConverse (
+  IN UINTN  EfiAttributes
+  );
+
+#endif // CPU_DXE_H_
diff --git a/UefiCpuPkg/CpuDxe/LoongArch64/CpuMp.c b/UefiCpuPkg/CpuDxe/LoongArch64/CpuMp.c
new file mode 100644
index 0000000000..3325914e53
--- /dev/null
+++ b/UefiCpuPkg/CpuDxe/LoongArch64/CpuMp.c
@@ -0,0 +1,544 @@
+/** @file
+  CPU DXE Module to produce CPU MP Protocol.
+
+  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "CpuDxe.h"
+#include "CpuMp.h"
+
+EFI_HANDLE  mMpServiceHandle    = NULL;
+UINTN       mNumberOfProcessors = 1;
+
+EFI_MP_SERVICES_PROTOCOL  mMpServicesTemplate = {
+  GetNumberOfProcessors,
+  GetProcessorInfo,
+  StartupAllAPs,
+  StartupThisAP,
+  SwitchBSP,
+  EnableDisableAP,
+  WhoAmI
+};
+
+/**
+  This service retrieves the number of logical processor in the platform
+  and the number of those logical processors that are enabled on this boot.
+  This service may only be called from the BSP.
+
+  This function is used to retrieve the following information:
+    - The number of logical processors that are present in the system.
+    - The number of enabled logical processors in the system at the instant
+      this call is made.
+
+  Because MP Service Protocol provides services to enable and disable processors
+  dynamically, the number of enabled logical processors may vary during the
+  course of a boot session.
+
+  If this service is called from an AP, then EFI_DEVICE_ERROR is returned.
+  If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then
+  EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors
+  is returned in NumberOfProcessors, the number of currently enabled processor
+  is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned.
+
+  @param[in]  This                        A pointer to the EFI_MP_SERVICES_PROTOCOL
+                                          instance.
+  @param[out] NumberOfProcessors          Pointer to the total number of logical
+                                          processors in the system, including the BSP
+                                          and disabled APs.
+  @param[out] NumberOfEnabledProcessors   Pointer to the number of enabled logical
+                                          processors that exist in system, including
+                                          the BSP.
+
+  @retval EFI_SUCCESS             The number of logical processors and enabled
+                                  logical processors was retrieved.
+  @retval EFI_DEVICE_ERROR        The calling processor is an AP.
+  @retval EFI_INVALID_PARAMETER   NumberOfProcessors is NULL.
+  @retval EFI_INVALID_PARAMETER   NumberOfEnabledProcessors is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+GetNumberOfProcessors (
+  IN  EFI_MP_SERVICES_PROTOCOL  *This,
+  OUT UINTN                     *NumberOfProcessors,
+  OUT UINTN                     *NumberOfEnabledProcessors
+  )
+{
+  if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return MpInitLibGetNumberOfProcessors (
+           NumberOfProcessors,
+           NumberOfEnabledProcessors
+           );
+}
+
+/**
+  Gets detailed MP-related information on the requested processor at the
+  instant this call is made. This service may only be called from the BSP.
+
+  This service retrieves detailed MP-related information about any processor
+  on the platform. Note the following:
+    - The processor information may change during the course of a boot session.
+    - The information presented here is entirely MP related.
+
+  Information regarding the number of caches and their sizes, frequency of operation,
+  slot numbers is all considered platform-related information and is not provided
+  by this service.
+
+  @param[in]  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL
+                                    instance.
+  @param[in]  ProcessorNumber       The handle number of processor.
+  @param[out] ProcessorInfoBuffer   A pointer to the buffer where information for
+                                    the requested processor is deposited.
+
+  @retval EFI_SUCCESS             Processor information was returned.
+  @retval EFI_DEVICE_ERROR        The calling processor is an AP.
+  @retval EFI_INVALID_PARAMETER   ProcessorInfoBuffer is NULL.
+  @retval EFI_NOT_FOUND           The processor with the handle specified by
+                                  ProcessorNumber does not exist in the platform.
+
+**/
+EFI_STATUS
+EFIAPI
+GetProcessorInfo (
+  IN  EFI_MP_SERVICES_PROTOCOL   *This,
+  IN  UINTN                      ProcessorNumber,
+  OUT EFI_PROCESSOR_INFORMATION  *ProcessorInfoBuffer
+  )
+{
+  return MpInitLibGetProcessorInfo (ProcessorNumber, ProcessorInfoBuffer, NULL);
+}
+
+/**
+  This service executes a caller provided function on all enabled APs. APs can
+  run either simultaneously or one at a time in sequence. This service supports
+  both blocking and non-blocking requests. The non-blocking requests use EFI
+  events so the BSP can detect when the APs have finished. This service may only
+  be called from the BSP.
+
+  This function is used to dispatch all the enabled APs to the function specified
+  by Procedure.  If any enabled AP is busy, then EFI_NOT_READY is returned
+  immediately and Procedure is not started on any AP.
+
+  If SingleThread is TRUE, all the enabled APs execute the function specified by
+  Procedure one by one, in ascending order of processor handle number. Otherwise,
+  all the enabled APs execute the function specified by Procedure simultaneously.
+
+  If WaitEvent is NULL, execution is in blocking mode. The BSP waits until all
+  APs finish or TimeoutInMicroseconds expires. Otherwise, execution is in non-blocking
+  mode, and the BSP returns from this service without waiting for APs. If a
+  non-blocking mode is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
+  is signaled, then EFI_UNSUPPORTED must be returned.
+
+  If the timeout specified by TimeoutInMicroseconds expires before all APs return
+  from Procedure, then Procedure on the failed APs is terminated. All enabled APs
+  are always available for further calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+  and EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). If FailedCpuList is not NULL, its
+  content points to the list of processor handle numbers in which Procedure was
+  terminated.
+
+  Note: It is the responsibility of the consumer of the EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+  to make sure that the nature of the code that is executed on the BSP and the
+  dispatched APs is well controlled. The MP Services Protocol does not guarantee
+  that the Procedure function is MP-safe. Hence, the tasks that can be run in
+  parallel are limited to certain independent tasks and well-controlled exclusive
+  code. EFI services and protocols may not be called by APs unless otherwise
+  specified.
+
+  In blocking execution mode, BSP waits until all APs finish or
+  TimeoutInMicroseconds expires.
+
+  In non-blocking execution mode, BSP is freed to return to the caller and then
+  proceed to the next task without having to wait for APs. The following
+  sequence needs to occur in a non-blocking execution mode:
+
+    -# The caller that intends to use this MP Services Protocol in non-blocking
+       mode creates WaitEvent by calling the EFI CreateEvent() service.  The caller
+       invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the parameter WaitEvent
+       is not NULL, then StartupAllAPs() executes in non-blocking mode. It requests
+       the function specified by Procedure to be started on all the enabled APs,
+       and releases the BSP to continue with other tasks.
+    -# The caller can use the CheckEvent() and WaitForEvent() services to check
+       the state of the WaitEvent created in step 1.
+    -# When the APs complete their task or TimeoutInMicroSecondss expires, the MP
+       Service signals WaitEvent by calling the EFI SignalEvent() function. If
+       FailedCpuList is not NULL, its content is available when WaitEvent is
+       signaled. If all APs returned from Procedure prior to the timeout, then
+       FailedCpuList is set to NULL. If not all APs return from Procedure before
+       the timeout, then FailedCpuList is filled in with the list of the failed
+       APs. The buffer is allocated by MP Service Protocol using AllocatePool().
+       It is the caller's responsibility to free the buffer with FreePool() service.
+    -# This invocation of SignalEvent() function informs the caller that invoked
+       EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() that either all the APs completed
+       the specified task or a timeout occurred. The contents of FailedCpuList
+       can be examined to determine which APs did not complete the specified task
+       prior to the timeout.
+
+  @param[in]  This                    A pointer to the EFI_MP_SERVICES_PROTOCOL
+                                      instance.
+  @param[in]  Procedure               A pointer to the function to be run on
+                                      enabled APs of the system. See type
+                                      EFI_AP_PROCEDURE.
+  @param[in]  SingleThread            If TRUE, then all the enabled APs execute
+                                      the function specified by Procedure one by
+                                      one, in ascending order of processor handle
+                                      number.  If FALSE, then all the enabled APs
+                                      execute the function specified by Procedure
+                                      simultaneously.
+  @param[in]  WaitEvent               The event created by the caller with CreateEvent()
+                                      service.  If it is NULL, then execute in
+                                      blocking mode. BSP waits until all APs finish
+                                      or TimeoutInMicroseconds expires.  If it's
+                                      not NULL, then execute in non-blocking mode.
+                                      BSP requests the function specified by
+                                      Procedure to be started on all the enabled
+                                      APs, and go on executing immediately. If
+                                      all return from Procedure, or TimeoutInMicroseconds
+                                      expires, this event is signaled. The BSP
+                                      can use the CheckEvent() or WaitForEvent()
+                                      services to check the state of event.  Type
+                                      EFI_EVENT is defined in CreateEvent() in
+                                      the Unified Extensible Firmware Interface
+                                      Specification.
+  @param[in]  TimeoutInMicroseconds   Indicates the time limit in microseconds for
+                                      APs to return from Procedure, either for
+                                      blocking or non-blocking mode. Zero means
+                                      infinity.  If the timeout expires before
+                                      all APs return from Procedure, then Procedure
+                                      on the failed APs is terminated. All enabled
+                                      APs are available for next function assigned
+                                      by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+                                      or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
+                                      If the timeout expires in blocking mode,
+                                      BSP returns EFI_TIMEOUT.  If the timeout
+                                      expires in non-blocking mode, WaitEvent
+                                      is signaled with SignalEvent().
+  @param[in]  ProcedureArgument       The parameter passed into Procedure for
+                                      all APs.
+  @param[out] FailedCpuList           If NULL, this parameter is ignored. Otherwise,
+                                      if all APs finish successfully, then its
+                                      content is set to NULL. If not all APs
+                                      finish before timeout expires, then its
+                                      content is set to address of the buffer
+                                      holding handle numbers of the failed APs.
+                                      The buffer is allocated by MP Service Protocol,
+                                      and it's the caller's responsibility to
+                                      free the buffer with FreePool() service.
+                                      In blocking mode, it is ready for consumption
+                                      when the call returns. In non-blocking mode,
+                                      it is ready when WaitEvent is signaled.  The
+                                      list of failed CPU is terminated by
+                                      END_OF_CPU_LIST.
+
+  @retval EFI_SUCCESS             In blocking mode, all APs have finished before
+                                  the timeout expired.
+  @retval EFI_SUCCESS             In non-blocking mode, function has been dispatched
+                                  to all enabled APs.
+  @retval EFI_UNSUPPORTED         A non-blocking mode request was made after the
+                                  UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
+                                  signaled.
+  @retval EFI_DEVICE_ERROR        Caller processor is AP.
+  @retval EFI_NOT_STARTED         No enabled APs exist in the system.
+  @retval EFI_NOT_READY           Any enabled APs are busy.
+  @retval EFI_TIMEOUT             In blocking mode, the timeout expired before
+                                  all enabled APs have finished.
+  @retval EFI_INVALID_PARAMETER   Procedure is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+StartupAllAPs (
+  IN  EFI_MP_SERVICES_PROTOCOL  *This,
+  IN  EFI_AP_PROCEDURE          Procedure,
+  IN  BOOLEAN                   SingleThread,
+  IN  EFI_EVENT                 WaitEvent               OPTIONAL,
+  IN  UINTN                     TimeoutInMicroseconds,
+  IN  VOID                      *ProcedureArgument      OPTIONAL,
+  OUT UINTN                     **FailedCpuList         OPTIONAL
+  )
+{
+  return MpInitLibStartupAllAPs (
+           Procedure,
+           SingleThread,
+           WaitEvent,
+           TimeoutInMicroseconds,
+           ProcedureArgument,
+           FailedCpuList
+           );
+}
+
+/**
+  This service lets the caller get one enabled AP to execute a caller-provided
+  function. The caller can request the BSP to either wait for the completion
+  of the AP or just proceed with the next task by using the EFI event mechanism.
+  See EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() for more details on non-blocking
+  execution support.  This service may only be called from the BSP.
+
+  This function is used to dispatch one enabled AP to the function specified by
+  Procedure passing in the argument specified by ProcedureArgument.  If WaitEvent
+  is NULL, execution is in blocking mode. The BSP waits until the AP finishes or
+  TimeoutInMicroSecondss expires. Otherwise, execution is in non-blocking mode.
+  BSP proceeds to the next task without waiting for the AP. If a non-blocking mode
+  is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled,
+  then EFI_UNSUPPORTED must be returned.
+
+  If the timeout specified by TimeoutInMicroseconds expires before the AP returns
+  from Procedure, then execution of Procedure by the AP is terminated. The AP is
+  available for subsequent calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and
+  EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
+
+  @param[in]  This                    A pointer to the EFI_MP_SERVICES_PROTOCOL
+                                      instance.
+  @param[in]  Procedure               A pointer to the function to be run on 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
+                                      EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+  @param[in]  WaitEvent               The event created by the caller with CreateEvent()
+                                      service.  If it is NULL, then execute in
+                                      blocking mode. BSP waits until 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 EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
+                                      or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
+                                      If the timeout expires in blocking mode,
+                                      BSP returns EFI_TIMEOUT.  If the timeout
+                                      expires in non-blocking mode, WaitEvent
+                                      is signaled with SignalEvent().
+  @param[in]  ProcedureArgument       The parameter passed into Procedure 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_DEVICE_ERROR        The calling processor is an AP.
+  @retval EFI_TIMEOUT             In blocking mode, the timeout expired before
+                                  the specified AP has finished.
+  @retval EFI_NOT_READY           The specified AP is busy.
+  @retval EFI_NOT_FOUND           The processor with the handle specified by
+                                  ProcessorNumber does not exist.
+  @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the BSP or disabled AP.
+  @retval EFI_INVALID_PARAMETER   Procedure is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+StartupThisAP (
+  IN  EFI_MP_SERVICES_PROTOCOL  *This,
+  IN  EFI_AP_PROCEDURE          Procedure,
+  IN  UINTN                     ProcessorNumber,
+  IN  EFI_EVENT                 WaitEvent               OPTIONAL,
+  IN  UINTN                     TimeoutInMicroseconds,
+  IN  VOID                      *ProcedureArgument      OPTIONAL,
+  OUT BOOLEAN                   *Finished               OPTIONAL
+  )
+{
+  return MpInitLibStartupThisAP (
+           Procedure,
+           ProcessorNumber,
+           WaitEvent,
+           TimeoutInMicroseconds,
+           ProcedureArgument,
+           Finished
+           );
+}
+
+/**
+  This service switches the requested AP to be the BSP from that point onward.
+  This service changes the BSP for all purposes.   This call can only be performed
+  by the current BSP.
+
+  This service switches the requested AP to be the BSP from that point onward.
+  This service changes the BSP for all purposes. The new BSP can take over the
+  execution of the old BSP and continue seamlessly from where the old one left
+  off. This service may not be supported after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
+  is signaled.
+
+  If the BSP cannot be switched prior to the return from this service, then
+  EFI_UNSUPPORTED must be returned.
+
+  @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+  @param[in] ProcessorNumber   The handle number of AP that is to become the new
+                               BSP. The range is from 0 to the total number of
+                               logical processors minus 1. The total number of
+                               logical processors can be retrieved by
+                               EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+  @param[in] EnableOldBSP      If TRUE, then the old BSP will be listed as an
+                               enabled AP. Otherwise, it will be disabled.
+
+  @retval EFI_SUCCESS             BSP successfully switched.
+  @retval EFI_UNSUPPORTED         Switching the BSP cannot be completed prior to
+                                  this service returning.
+  @retval EFI_UNSUPPORTED         Switching the BSP is not supported.
+  @retval EFI_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.
+
+**/
+EFI_STATUS
+EFIAPI
+SwitchBSP (
+  IN EFI_MP_SERVICES_PROTOCOL  *This,
+  IN  UINTN                    ProcessorNumber,
+  IN  BOOLEAN                  EnableOldBSP
+  )
+{
+  return MpInitLibSwitchBSP (ProcessorNumber, EnableOldBSP);
+}
+
+/**
+  This service lets the caller enable or disable an AP from this point onward.
+  This service may only be called from the BSP.
+
+  This service allows the caller enable or disable an AP from this point onward.
+  The caller can optionally specify the health status of the AP by Health. If
+  an AP is being disabled, then the state of the disabled AP is implementation
+  dependent. If an AP is enabled, then the implementation must guarantee that a
+  complete initialization sequence is performed on the AP, so the AP is in a state
+  that is compatible with an MP operating system. This service may not be supported
+  after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled.
+
+  If the enable or disable AP operation cannot be completed prior to the return
+  from this service, then EFI_UNSUPPORTED must be returned.
+
+  @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+  @param[in] ProcessorNumber   The handle number of AP.
+                               The range is from 0 to the total number of
+                               logical processors minus 1. The total number of
+                               logical processors can be retrieved by
+                               EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+  @param[in] EnableAP          Specifies the new state for the processor for
+                               enabled, FALSE for disabled.
+  @param[in] HealthFlag        If not NULL, a pointer to a value that specifies
+                               the new health status of the AP. This flag
+                               corresponds to StatusFlag defined in
+                               EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only
+                               the PROCESSOR_HEALTH_STATUS_BIT is used. All other
+                               bits are ignored.  If it is NULL, this parameter
+                               is ignored.
+
+  @retval EFI_SUCCESS             The specified AP was enabled or disabled successfully.
+  @retval EFI_UNSUPPORTED         Enabling or disabling an AP cannot be completed
+                                  prior to this service returning.
+  @retval EFI_UNSUPPORTED         Enabling or disabling an AP is not supported.
+  @retval EFI_DEVICE_ERROR        The calling processor is an AP.
+  @retval EFI_NOT_FOUND           Processor with the handle specified by ProcessorNumber
+                                  does not exist.
+  @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the BSP.
+
+**/
+EFI_STATUS
+EFIAPI
+EnableDisableAP (
+  IN  EFI_MP_SERVICES_PROTOCOL  *This,
+  IN  UINTN                     ProcessorNumber,
+  IN  BOOLEAN                   EnableAP,
+  IN  UINT32                    *HealthFlag OPTIONAL
+  )
+{
+  return MpInitLibEnableDisableAP (ProcessorNumber, EnableAP, HealthFlag);
+}
+
+/**
+  This return the handle number for the calling processor.  This service may be
+  called from the BSP and APs.
+
+  This service returns the processor handle number for the calling processor.
+  The returned value is in the range from 0 to the total number of logical
+  processors minus 1. The total number of logical processors can be retrieved
+  with EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). This service may be
+  called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER
+  is returned. Otherwise, the current processors handle number is returned in
+  ProcessorNumber, and EFI_SUCCESS is returned.
+
+  @param[in]  This             A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+  @param[out] ProcessorNumber  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
+                               EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
+
+  @retval EFI_SUCCESS             The current processor handle number was returned
+                                  in ProcessorNumber.
+  @retval EFI_INVALID_PARAMETER   ProcessorNumber is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+WhoAmI (
+  IN EFI_MP_SERVICES_PROTOCOL  *This,
+  OUT UINTN                    *ProcessorNumber
+  )
+{
+  return MpInitLibWhoAmI (ProcessorNumber);
+}
+
+/**
+  Initialize Multi-processor support.
+**/
+VOID
+InitializeMpSupport (
+  VOID
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       NumberOfProcessors;
+  UINTN       NumberOfEnabledProcessors;
+
+  //
+  // Wakeup APs to do initialization
+  //
+  Status = MpInitLibInitialize ();
+  ASSERT_EFI_ERROR (Status);
+
+  MpInitLibGetNumberOfProcessors (&NumberOfProcessors, &NumberOfEnabledProcessors);
+  mNumberOfProcessors = NumberOfProcessors;
+  DEBUG ((DEBUG_INFO, "Detect CPU count: %d\n", mNumberOfProcessors));
+
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &mMpServiceHandle,
+                  &gEfiMpServiceProtocolGuid,
+                  &mMpServicesTemplate,
+                  NULL
+                  );
+  ASSERT_EFI_ERROR (Status);
+}
diff --git a/UefiCpuPkg/CpuDxe/LoongArch64/Exception.c b/UefiCpuPkg/CpuDxe/LoongArch64/Exception.c
new file mode 100644
index 0000000000..96def89936
--- /dev/null
+++ b/UefiCpuPkg/CpuDxe/LoongArch64/Exception.c
@@ -0,0 +1,159 @@
+/** @file Exception.c
+
+  CPU DXE Module initialization exception instance.
+
+  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "CpuDxe.h"
+#include <Guid/VectorHandoffTable.h>
+#include <Library/CpuExceptionHandlerLib.h>
+#include <Register/LoongArch64/Csr.h>
+
+VOID
+ExceptionEntryStart (
+  VOID
+  );
+
+VOID
+ExceptionEntryEnd (
+  VOID
+  );
+
+/**
+  This function registers and enables the handler specified by InterruptHandler for a processor
+  interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
+  handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
+  The installed handler is called once for each processor interrupt or exception.
+
+  @param  InterruptType    A pointer to the processor's current interrupt state. Set to TRUE if interrupts
+                           are enabled and FALSE if interrupts are disabled.
+  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
+                           when a processor interrupt occurs. If this parameter is NULL, then the handler
+                           will be uninstalled.
+
+  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
+  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was
+                                previously installed.
+  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
+                                previously installed.
+  @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
+
+**/
+EFI_STATUS
+RegisterInterruptHandler (
+  IN EFI_EXCEPTION_TYPE         InterruptType,
+  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
+  )
+{
+  return (EFI_STATUS)RegisterCpuInterruptHandler (InterruptType, InterruptHandler);
+}
+
+/**
+  Update the exception start entry code.
+
+  @retval EFI_SUCCESS           Update the exception start entry code down.
+  @retval EFI_OUT_OF_RESOURCES  The start entry code size out of bounds.
+
+**/
+EFI_STATUS
+EFIAPI
+UpdateExceptionStartEntry (
+  VOID
+  )
+{
+  EFI_PHYSICAL_ADDRESS  ExceptionStartEntry;
+  UINTN                 VectorLength;
+  UINTN                 MaxLength;
+  UINTN                 MaxSizeOfVector;
+
+  VectorLength = (UINTN)ExceptionEntryEnd - (UINTN)ExceptionEntryStart;
+
+  //
+  // A vector is up to 512 bytes.
+  //
+  MaxSizeOfVector = 512;
+  MaxLength       = (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * MaxSizeOfVector;
+
+  if (VectorLength > MaxLength) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  ExceptionStartEntry = PcdGet64 (PcdCpuExceptionVectorBaseAddress);
+
+  InvalidateInstructionCacheRange ((VOID *)ExceptionStartEntry, VectorLength);
+  CopyMem ((VOID *)ExceptionStartEntry, (VOID *)ExceptionEntryStart, VectorLength);
+  InvalidateInstructionCacheRange ((VOID *)ExceptionStartEntry, VectorLength);
+  InvalidateDataCache ();
+
+  //
+  // If PcdCpuExceptionVectorBaseAddress is not used during SEC and PEI stages, the exception
+  // base addres is set to PcdCpuExceptionVectorBaseAddress.
+  //
+  if (CsrRead (LOONGARCH_CSR_EBASE) != ExceptionStartEntry) {
+    SetExceptionBaseAddress (ExceptionStartEntry);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Initialize interrupt handling for DXE phase.
+
+  @param  Cpu A pointer of EFI_CPU_ARCH_PROTOCOL instance.
+
+  @return VOID.
+
+**/
+VOID
+InitializeExceptions (
+  IN EFI_CPU_ARCH_PROTOCOL  *Cpu
+  )
+{
+  EFI_STATUS               Status;
+  EFI_VECTOR_HANDOFF_INFO  *VectorInfoList;
+  EFI_VECTOR_HANDOFF_INFO  *VectorInfo;
+  BOOLEAN                  IrqEnabled;
+
+  VectorInfo = (EFI_VECTOR_HANDOFF_INFO *)NULL;
+  Status     = EfiGetSystemConfigurationTable (&gEfiVectorHandoffTableGuid, (VOID **)&VectorInfoList);
+
+  if ((Status == EFI_SUCCESS) && (VectorInfoList != NULL)) {
+    VectorInfo = VectorInfoList;
+  }
+
+  //
+  // Disable interrupts
+  //
+  Cpu->GetInterruptState (Cpu, &IrqEnabled);
+  if (IrqEnabled) {
+    Cpu->DisableInterrupt (Cpu);
+  }
+
+  //
+  // Update the Exception Start Entry code to point into CpuDxe.
+  //
+  Status = UpdateExceptionStartEntry ();
+  if (EFI_ERROR (Status)) {
+    DebugPrint (EFI_D_ERROR, "[%a]: Exception start entry code out of bounds!\n", __func__);
+    ASSERT_EFI_ERROR (Status);
+  }
+
+  //
+  // Intialize the CpuExceptionHandlerLib so we take over the exception vector table from the DXE Core
+  //
+  Status = InitializeCpuExceptionHandlers (VectorInfo);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Enable interrupts
+  //
+  DebugPrint (EFI_D_INFO, "InitializeExceptions,IrqEnabled = %x\n", IrqEnabled);
+  if (!IrqEnabled) {
+    Status = Cpu->EnableInterrupt (Cpu);
+  }
+
+  ASSERT_EFI_ERROR (Status);
+}
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114542): https://edk2.groups.io/g/devel/message/114542
Mute This Topic: https://groups.io/mt/103971655/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 17/37] EmbeddedPkg: Add PcdPrePiCpuIoSize width for LOONGARCH64
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (15 preceding siblings ...)
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 16/37] UefiCpuPkg: Add CpuDxe driver " Chao Li
@ 2024-01-26  6:29 ` Chao Li
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 18/37] ArmVirtPkg: Move PCD of FDT base address and FDT padding to OvmfPkg Chao Li
                   ` (24 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:29 UTC (permalink / raw)
  To: devel; +Cc: Leif Lindholm, Ard Biesheuvel, Abner Chang, Daniel Schaefer

Added LoongArch64 architecture CPU IO width.

https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Leif Lindholm <quic_llindhol@quicinc.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Abner Chang <abner.chang@amd.com>
Cc: Daniel Schaefer <git@danielschaefer.me>
Signed-off-by: Chao Li <lichao@loongson.cn>
Reviewed-by: Leif Lindholm <quic_llindhol@quicinc.com>
---
 EmbeddedPkg/EmbeddedPkg.dec | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/EmbeddedPkg/EmbeddedPkg.dec b/EmbeddedPkg/EmbeddedPkg.dec
index b4834e8b4f..5dfbbc2933 100644
--- a/EmbeddedPkg/EmbeddedPkg.dec
+++ b/EmbeddedPkg/EmbeddedPkg.dec
@@ -166,6 +166,9 @@
 [PcdsFixedAtBuild.X64]
   gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize|16|UINT8|0x00000011
 
+[PcdsFixedAtBuild.LOONGARCH64]
+  gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize|16|UINT8|0x00000011
+
 [PcdsFixedAtBuild.common, PcdsDynamic.common]
   #
   # Value to add to a host address to obtain a device address, using
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114543): https://edk2.groups.io/g/devel/message/114543
Mute This Topic: https://groups.io/mt/103971656/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 18/37] ArmVirtPkg: Move PCD of FDT base address and FDT padding to OvmfPkg
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (16 preceding siblings ...)
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 17/37] EmbeddedPkg: Add PcdPrePiCpuIoSize width for LOONGARCH64 Chao Li
@ 2024-01-26  6:29 ` Chao Li
  2024-02-01 23:20   ` Laszlo Ersek
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 19/37] UefiCpuPkg: Add a new CPU IO 2 driver named CpuMmio2Dxe Chao Li
                   ` (23 subsequent siblings)
  41 siblings, 1 reply; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:29 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Laszlo Ersek, Leif Lindholm, Sami Mujawar,
	Gerd Hoffmann, Jiewen Yao, Jordan Justen

Moved PcdDeviceTreeInitialBaseAddress and PcdDeviceTreeAllocationPadding
to OvmfPkg for easier use by other architectures.

Build-tested only (with "ArmVirtQemu.dsc").

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Leif Lindholm <quic_llindhol@quicinc.com>
Cc: Sami Mujawar <sami.mujawar@arm.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Reviewed-by: Sami Mujawar <sami.mujawar@arm.com>
---
 ArmVirtPkg/ArmVirtCloudHv.dsc                     |  2 +-
 ArmVirtPkg/ArmVirtKvmTool.dsc                     |  2 +-
 ArmVirtPkg/ArmVirtPkg.dec                         | 14 --------------
 ArmVirtPkg/ArmVirtQemu.dsc                        |  2 +-
 ArmVirtPkg/ArmVirtQemuKernel.dsc                  |  2 +-
 ArmVirtPkg/ArmVirtXen.dsc                         |  2 +-
 .../ArmVirtPsciResetSystemPeiLib.inf              |  3 ++-
 .../CloudHvVirtMemInfoPeiLib.inf                  |  3 ++-
 .../DebugLibFdtPL011UartFlash.inf                 |  3 ++-
 .../EarlyFdt16550SerialPortHookLib.inf            |  3 ++-
 .../EarlyFdtPL011SerialPortLib.inf                |  3 ++-
 .../KvmtoolPlatformPeiLib.inf                     |  5 +++--
 .../Library/PlatformPeiLib/PlatformPeiLib.inf     | 10 +++++-----
 .../QemuVirtMemInfoLib/QemuVirtMemInfoPeiLib.inf  |  3 ++-
 .../PrePi/ArmVirtPrePiUniCoreRelocatable.inf      |  3 ++-
 OvmfPkg/OvmfPkg.dec                               | 15 +++++++++++++++
 16 files changed, 42 insertions(+), 33 deletions(-)

diff --git a/ArmVirtPkg/ArmVirtCloudHv.dsc b/ArmVirtPkg/ArmVirtCloudHv.dsc
index 2cb89ce10c..76c0d28544 100644
--- a/ArmVirtPkg/ArmVirtCloudHv.dsc
+++ b/ArmVirtPkg/ArmVirtCloudHv.dsc
@@ -129,7 +129,7 @@
   gArmTokenSpaceGuid.PcdSystemMemoryBase|0x40000000
 
   # initial location of the device tree blob passed by Cloud Hypervisor -- base of DRAM
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress|0x40000000
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress|0x40000000
 
   gEfiMdeModulePkgTokenSpaceGuid.PcdResetOnMemoryTypeInformationChange|FALSE
   gEfiMdeModulePkgTokenSpaceGuid.PcdBootManagerMenuFile|{ 0x21, 0xaa, 0x2c, 0x46, 0x14, 0x76, 0x03, 0x45, 0x83, 0x6e, 0x8a, 0xb6, 0xf4, 0x66, 0x23, 0x31 }
diff --git a/ArmVirtPkg/ArmVirtKvmTool.dsc b/ArmVirtPkg/ArmVirtKvmTool.dsc
index f50d53bf15..cac4fe06d3 100644
--- a/ArmVirtPkg/ArmVirtKvmTool.dsc
+++ b/ArmVirtPkg/ArmVirtKvmTool.dsc
@@ -179,7 +179,7 @@
   # We are booting from RAM using the Linux kernel boot protocol,
   # x0 will point to the DTB image in memory.
   #
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress|0x0
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress|0x0
 
   gArmTokenSpaceGuid.PcdFdBaseAddress|0x0
   gArmTokenSpaceGuid.PcdFvBaseAddress|0x0
diff --git a/ArmVirtPkg/ArmVirtPkg.dec b/ArmVirtPkg/ArmVirtPkg.dec
index 313aebda90..05d2d36c1d 100644
--- a/ArmVirtPkg/ArmVirtPkg.dec
+++ b/ArmVirtPkg/ArmVirtPkg.dec
@@ -42,20 +42,6 @@
   gArmVirtTokenSpaceGuid.PcdTpm2SupportEnabled|FALSE|BOOLEAN|0x00000004
 
 [PcdsFixedAtBuild, PcdsPatchableInModule]
-  #
-  # This is the physical address where the device tree is expected to be stored
-  # upon first entry into UEFI. This needs to be a FixedAtBuild PCD, so that we
-  # can do a first pass over the device tree in the SEC phase to discover the
-  # UART base address.
-  #
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress|0x0|UINT64|0x00000001
-
-  #
-  # Padding in bytes to add to the device tree allocation, so that the DTB can
-  # be modified in place (default: 256 bytes)
-  #
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeAllocationPadding|256|UINT32|0x00000002
-
   #
   # Binary representation of the GUID that determines the terminal type. The
   # size must be exactly 16 bytes. The default value corresponds to
diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc
index 30e3cfc8b9..cf306cac08 100644
--- a/ArmVirtPkg/ArmVirtQemu.dsc
+++ b/ArmVirtPkg/ArmVirtQemu.dsc
@@ -201,7 +201,7 @@
   gArmTokenSpaceGuid.PcdSystemMemoryBase|0x40000000
 
   # initial location of the device tree blob passed by QEMU -- base of DRAM
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress|0x40000000
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress|0x40000000
 
   gEfiMdeModulePkgTokenSpaceGuid.PcdResetOnMemoryTypeInformationChange|FALSE
   gEfiMdeModulePkgTokenSpaceGuid.PcdBootManagerMenuFile|{ 0x21, 0xaa, 0x2c, 0x46, 0x14, 0x76, 0x03, 0x45, 0x83, 0x6e, 0x8a, 0xb6, 0xf4, 0x66, 0x23, 0x31 }
diff --git a/ArmVirtPkg/ArmVirtQemuKernel.dsc b/ArmVirtPkg/ArmVirtQemuKernel.dsc
index b50f8e84a3..c0d079e28d 100644
--- a/ArmVirtPkg/ArmVirtQemuKernel.dsc
+++ b/ArmVirtPkg/ArmVirtQemuKernel.dsc
@@ -198,7 +198,7 @@
   # Define a default initial address for the device tree.
   # Ignored if x0 != 0 at entry.
   #
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress|0x40000000
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress|0x40000000
 
   gArmTokenSpaceGuid.PcdFdBaseAddress|0x0
   gArmTokenSpaceGuid.PcdFvBaseAddress|0x0
diff --git a/ArmVirtPkg/ArmVirtXen.dsc b/ArmVirtPkg/ArmVirtXen.dsc
index f0d15b823b..5809832e66 100644
--- a/ArmVirtPkg/ArmVirtXen.dsc
+++ b/ArmVirtPkg/ArmVirtXen.dsc
@@ -115,7 +115,7 @@
   #
   gArmTokenSpaceGuid.PcdSystemMemoryBase|0x0
   gArmTokenSpaceGuid.PcdSystemMemorySize|0x0
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress|0x0
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress|0x0
 
   gArmTokenSpaceGuid.PcdFdBaseAddress|0x0
   gArmTokenSpaceGuid.PcdFvBaseAddress|0x0
diff --git a/ArmVirtPkg/Library/ArmVirtPsciResetSystemPeiLib/ArmVirtPsciResetSystemPeiLib.inf b/ArmVirtPkg/Library/ArmVirtPsciResetSystemPeiLib/ArmVirtPsciResetSystemPeiLib.inf
index 3a65706e8d..79217d296d 100644
--- a/ArmVirtPkg/Library/ArmVirtPsciResetSystemPeiLib/ArmVirtPsciResetSystemPeiLib.inf
+++ b/ArmVirtPkg/Library/ArmVirtPsciResetSystemPeiLib/ArmVirtPsciResetSystemPeiLib.inf
@@ -26,6 +26,7 @@
   EmbeddedPkg/EmbeddedPkg.dec
   MdeModulePkg/MdeModulePkg.dec
   MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
   ArmSmcLib
@@ -36,4 +37,4 @@
   HobLib
 
 [Pcd]
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
diff --git a/ArmVirtPkg/Library/CloudHvVirtMemInfoLib/CloudHvVirtMemInfoPeiLib.inf b/ArmVirtPkg/Library/CloudHvVirtMemInfoLib/CloudHvVirtMemInfoPeiLib.inf
index 666b5d9711..6df26ccd64 100644
--- a/ArmVirtPkg/Library/CloudHvVirtMemInfoLib/CloudHvVirtMemInfoPeiLib.inf
+++ b/ArmVirtPkg/Library/CloudHvVirtMemInfoLib/CloudHvVirtMemInfoPeiLib.inf
@@ -26,6 +26,7 @@
   EmbeddedPkg/EmbeddedPkg.dec
   MdeModulePkg/MdeModulePkg.dec
   MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
   ArmLib
@@ -44,4 +45,4 @@
 [FixedPcd]
   gArmTokenSpaceGuid.PcdFdSize
   gArmTokenSpaceGuid.PcdFvSize
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
diff --git a/ArmVirtPkg/Library/DebugLibFdtPL011Uart/DebugLibFdtPL011UartFlash.inf b/ArmVirtPkg/Library/DebugLibFdtPL011Uart/DebugLibFdtPL011UartFlash.inf
index 7870ca2ae4..f35a0913f0 100644
--- a/ArmVirtPkg/Library/DebugLibFdtPL011Uart/DebugLibFdtPL011UartFlash.inf
+++ b/ArmVirtPkg/Library/DebugLibFdtPL011Uart/DebugLibFdtPL011UartFlash.inf
@@ -30,6 +30,7 @@
   ArmPlatformPkg/ArmPlatformPkg.dec
   ArmVirtPkg/ArmVirtPkg.dec
   MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
   BaseLib
@@ -41,7 +42,7 @@
   PrintLib
 
 [Pcd]
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress # Flash.c
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress # Flash.c
   gEfiMdePkgTokenSpaceGuid.PcdDebugClearMemoryValue
   gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask
   gEfiMdePkgTokenSpaceGuid.PcdFixedDebugPrintErrorLevel
diff --git a/ArmVirtPkg/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.inf b/ArmVirtPkg/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.inf
index 22aba53d9b..3e2303b7f4 100644
--- a/ArmVirtPkg/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.inf
+++ b/ArmVirtPkg/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.inf
@@ -29,7 +29,8 @@
   ArmVirtPkg/ArmVirtPkg.dec
   MdeModulePkg/MdeModulePkg.dec
   MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
 
 [Pcd]
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
   gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterBase
diff --git a/ArmVirtPkg/Library/FdtPL011SerialPortLib/EarlyFdtPL011SerialPortLib.inf b/ArmVirtPkg/Library/FdtPL011SerialPortLib/EarlyFdtPL011SerialPortLib.inf
index f47692f06a..e677f1d9e7 100644
--- a/ArmVirtPkg/Library/FdtPL011SerialPortLib/EarlyFdtPL011SerialPortLib.inf
+++ b/ArmVirtPkg/Library/FdtPL011SerialPortLib/EarlyFdtPL011SerialPortLib.inf
@@ -28,9 +28,10 @@
   MdePkg/MdePkg.dec
   ArmPlatformPkg/ArmPlatformPkg.dec
   ArmVirtPkg/ArmVirtPkg.dec
+  OvmfPkg/OvmfPkg.dec
 
 [Pcd]
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
 
 [FixedPcd]
   gEfiMdePkgTokenSpaceGuid.PcdUartDefaultBaudRate
diff --git a/ArmVirtPkg/Library/KvmtoolPlatformPeiLib/KvmtoolPlatformPeiLib.inf b/ArmVirtPkg/Library/KvmtoolPlatformPeiLib/KvmtoolPlatformPeiLib.inf
index f201aee50c..77c0b923bd 100644
--- a/ArmVirtPkg/Library/KvmtoolPlatformPeiLib/KvmtoolPlatformPeiLib.inf
+++ b/ArmVirtPkg/Library/KvmtoolPlatformPeiLib/KvmtoolPlatformPeiLib.inf
@@ -24,6 +24,7 @@
   EmbeddedPkg/EmbeddedPkg.dec
   MdeModulePkg/MdeModulePkg.dec
   MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
   DebugLib
@@ -34,12 +35,12 @@
 
 [FixedPcd]
   gArmTokenSpaceGuid.PcdFvSize
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeAllocationPadding
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeAllocationPadding
 
 [Pcd]
   gArmTokenSpaceGuid.PcdFvBaseAddress
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
   gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
 
 [Guids]
   gFdtHobGuid
diff --git a/ArmVirtPkg/Library/PlatformPeiLib/PlatformPeiLib.inf b/ArmVirtPkg/Library/PlatformPeiLib/PlatformPeiLib.inf
index b867d8bb89..e9a34b6e2e 100644
--- a/ArmVirtPkg/Library/PlatformPeiLib/PlatformPeiLib.inf
+++ b/ArmVirtPkg/Library/PlatformPeiLib/PlatformPeiLib.inf
@@ -41,16 +41,16 @@
 
 [FixedPcd]
   gArmTokenSpaceGuid.PcdFvSize
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeAllocationPadding
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeAllocationPadding
 
 [Pcd]
   gArmTokenSpaceGuid.PcdFvBaseAddress
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
-  gEfiSecurityPkgTokenSpaceGuid.PcdTpmBaseAddress         ## SOMETIMES_PRODUCES
+  gEfiSecurityPkgTokenSpaceGuid.PcdTpmBaseAddress             ## SOMETIMES_PRODUCES
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
 
 [Ppis]
-  gOvmfTpmDiscoveredPpiGuid                               ## SOMETIMES_PRODUCES
-  gPeiTpmInitializationDonePpiGuid                        ## SOMETIMES_PRODUCES
+  gOvmfTpmDiscoveredPpiGuid                                   ## SOMETIMES_PRODUCES
+  gPeiTpmInitializationDonePpiGuid                            ## SOMETIMES_PRODUCES
 
 [Guids]
   gEarlyPL011BaseAddressGuid
diff --git a/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoPeiLib.inf b/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoPeiLib.inf
index f045e39a41..76c3c5d3c8 100644
--- a/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoPeiLib.inf
+++ b/ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoPeiLib.inf
@@ -26,6 +26,7 @@
   EmbeddedPkg/EmbeddedPkg.dec
   MdeModulePkg/MdeModulePkg.dec
   MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
   ArmLib
@@ -44,4 +45,4 @@
   gArmTokenSpaceGuid.PcdSystemMemorySize
   gArmTokenSpaceGuid.PcdFdSize
   gArmTokenSpaceGuid.PcdFvSize
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
diff --git a/ArmVirtPkg/PrePi/ArmVirtPrePiUniCoreRelocatable.inf b/ArmVirtPkg/PrePi/ArmVirtPrePiUniCoreRelocatable.inf
index 7edf501808..6b9244bd1a 100755
--- a/ArmVirtPkg/PrePi/ArmVirtPrePiUniCoreRelocatable.inf
+++ b/ArmVirtPkg/PrePi/ArmVirtPrePiUniCoreRelocatable.inf
@@ -35,6 +35,7 @@
   ArmPkg/ArmPkg.dec
   ArmPlatformPkg/ArmPlatformPkg.dec
   ArmVirtPkg/ArmVirtPkg.dec
+  OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
   BaseLib
@@ -93,6 +94,6 @@
 [Pcd]
   gArmTokenSpaceGuid.PcdSystemMemoryBase
   gArmTokenSpaceGuid.PcdSystemMemorySize
-  gArmVirtTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
   gArmTokenSpaceGuid.PcdFdBaseAddress
   gArmTokenSpaceGuid.PcdFvBaseAddress
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index b44fa039f7..7bc2bf1674 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -386,6 +386,21 @@
   #
   gUefiOvmfPkgTokenSpaceGuid.PcdForceNoAcpi|0x0|BOOLEAN|0x69
 
+[PcdsFixedAtBuild, PcdsPatchableInModule]
+  #
+  # This is the physical address where the device tree is expected to be stored
+  # upon first entry into UEFI. This needs to be a FixedAtBuild PCD, so that we
+  # can do a first pass over the device tree in the SEC phase to discover the
+  # UART base address.
+  #
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress|0x0|UINT64|0x6e
+
+  #
+  # Padding in bytes to add to the device tree allocation, so that the DTB can
+  # be modified in place (default: 256 bytes)
+  #
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeAllocationPadding|256|UINT32|0x6f
+
 [PcdsFeatureFlag]
   gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderPciTranslation|TRUE|BOOLEAN|0x1c
   gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderMmioTranslation|FALSE|BOOLEAN|0x1d
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114544): https://edk2.groups.io/g/devel/message/114544
Mute This Topic: https://groups.io/mt/103971659/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 19/37] UefiCpuPkg: Add a new CPU IO 2 driver named CpuMmio2Dxe
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (17 preceding siblings ...)
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 18/37] ArmVirtPkg: Move PCD of FDT base address and FDT padding to OvmfPkg Chao Li
@ 2024-01-26  6:29 ` Chao Li
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 20/37] ArmVirtPkg: Enable CpuMmio2Dxe Chao Li
                   ` (22 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:29 UTC (permalink / raw)
  To: devel
  Cc: Ray Ni, Laszlo Ersek, Rahul Kumar, Gerd Hoffmann, Leif Lindholm,
	Ard Biesheuvel, Sami Mujawar

CpuIo2Dxe only supports IO to access to CPU IO. Some ARCHs that do not
implement ports for CPU IO require MMIO to access PCI IO, and they
pretty much put the IO devices under the LPC bus, which is usually under
the PCIe/PCI bus. CpuMmio2Dxe was added to meet these needs.

CpuMmio2Dxe depends on PcdPciIoTranslation. The code is copied from
ArmPkg.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ray Ni <ray.ni@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Leif Lindholm <quic_llindhol@quicinc.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Sami Mujawar <sami.mujawar@arm.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Reviewed-by: Ray Ni <ray.ni@intel.com>
---
 UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.c   | 557 +++++++++++++++++++++++++
 UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf |  48 +++
 UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.uni |  18 +
 UefiCpuPkg/UefiCpuPkg.dsc              |   2 +
 4 files changed, 625 insertions(+)
 create mode 100644 UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.c
 create mode 100644 UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf
 create mode 100644 UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.uni

diff --git a/UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.c b/UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.c
new file mode 100644
index 0000000000..32ccac1cc6
--- /dev/null
+++ b/UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.c
@@ -0,0 +1,557 @@
+/** @file
+  Produces the CPU I/O 2 Protocol.
+
+Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
+Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+
+#include <Protocol/CpuIo2.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#define MAX_IO_PORT_ADDRESS  0xFFFF
+
+//
+// Handle for the CPU I/O 2 Protocol
+//
+STATIC EFI_HANDLE  mHandle = NULL;
+
+//
+// Lookup table for increment values based on transfer widths
+//
+STATIC CONST UINT8  mInStride[] = {
+  1, // EfiCpuIoWidthUint8
+  2, // EfiCpuIoWidthUint16
+  4, // EfiCpuIoWidthUint32
+  8, // EfiCpuIoWidthUint64
+  0, // EfiCpuIoWidthFifoUint8
+  0, // EfiCpuIoWidthFifoUint16
+  0, // EfiCpuIoWidthFifoUint32
+  0, // EfiCpuIoWidthFifoUint64
+  1, // EfiCpuIoWidthFillUint8
+  2, // EfiCpuIoWidthFillUint16
+  4, // EfiCpuIoWidthFillUint32
+  8  // EfiCpuIoWidthFillUint64
+};
+
+//
+// Lookup table for increment values based on transfer widths
+//
+STATIC CONST UINT8  mOutStride[] = {
+  1, // EfiCpuIoWidthUint8
+  2, // EfiCpuIoWidthUint16
+  4, // EfiCpuIoWidthUint32
+  8, // EfiCpuIoWidthUint64
+  1, // EfiCpuIoWidthFifoUint8
+  2, // EfiCpuIoWidthFifoUint16
+  4, // EfiCpuIoWidthFifoUint32
+  8, // EfiCpuIoWidthFifoUint64
+  0, // EfiCpuIoWidthFillUint8
+  0, // EfiCpuIoWidthFillUint16
+  0, // EfiCpuIoWidthFillUint32
+  0  // EfiCpuIoWidthFillUint64
+};
+
+/**
+  Check parameters to a CPU I/O 2 Protocol service request.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  @param[in] MmioOperation  TRUE for an MMIO operation, FALSE for I/O Port operation.
+  @param[in] Width          Signifies the width of the I/O or Memory operation.
+  @param[in] Address        The base address of the I/O operation.
+  @param[in] Count          The number of I/O operations to perform. The number of
+                            bytes moved is Width size * Count, starting at Address.
+  @param[in] Buffer         For read operations, the destination buffer to store the results.
+                            For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The parameters for this request pass the checks.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+STATIC
+EFI_STATUS
+CpuIoCheckParameter (
+  IN BOOLEAN                    MmioOperation,
+  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN UINT64                     Address,
+  IN UINTN                      Count,
+  IN VOID                       *Buffer
+  )
+{
+  UINT64  MaxCount;
+  UINT64  Limit;
+
+  //
+  // Check to see if Buffer is NULL
+  //
+  if (Buffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Check to see if Width is in the valid range
+  //
+  if ((UINT32)Width >= EfiCpuIoWidthMaximum) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // For FIFO type, the target address won't increase during the access,
+  // so treat Count as 1
+  //
+  if ((Width >= EfiCpuIoWidthFifoUint8) && (Width <= EfiCpuIoWidthFifoUint64)) {
+    Count = 1;
+  }
+
+  //
+  // Check to see if Width is in the valid range for I/O Port operations
+  //
+  Width = (EFI_CPU_IO_PROTOCOL_WIDTH)(Width & 0x03);
+  if (!MmioOperation && (Width == EfiCpuIoWidthUint64)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Check to see if Address is aligned
+  //
+  if ((Address & (UINT64)(mInStride[Width] - 1)) != 0) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Check to see if any address associated with this transfer exceeds the maximum
+  // allowed address.  The maximum address implied by the parameters passed in is
+  // Address + Size * Count.  If the following condition is met, then the transfer
+  // is not supported.
+  //
+  //    Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1
+  //
+  // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count
+  // can also be the maximum integer value supported by the CPU, this range
+  // check must be adjusted to avoid all overflow conditions.
+  //
+  // The following form of the range check is equivalent but assumes that
+  // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1).
+  //
+  Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS);
+  if (Count == 0) {
+    if (Address > Limit) {
+      return EFI_UNSUPPORTED;
+    }
+  } else {
+    MaxCount = RShiftU64 (Limit, Width);
+    if (MaxCount < (Count - 1)) {
+      return EFI_UNSUPPORTED;
+    }
+
+    if (Address > LShiftU64 (MaxCount - Count + 1, Width)) {
+      return EFI_UNSUPPORTED;
+    }
+  }
+
+  //
+  // Check to see if Buffer is aligned
+  //
+  if (((UINTN)Buffer & ((MIN (sizeof (UINTN), mInStride[Width])  - 1))) != 0) {
+    return EFI_UNSUPPORTED;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Reads memory-mapped registers.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
+  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
+  each of the Count operations that is performed.
+
+  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
+  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times on the same Address.
+
+  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
+  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times from the first element of Buffer.
+
+  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
+  @param[in]  Width    Signifies the width of the I/O or Memory operation.
+  @param[in]  Address  The base address of the I/O operation.
+  @param[in]  Count    The number of I/O operations to perform. The number of
+                       bytes moved is Width size * Count, starting at Address.
+  @param[out] Buffer   For read operations, the destination buffer to store the results.
+                       For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The data was read from or written to the PI system.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CpuMemoryServiceRead (
+  IN  EFI_CPU_IO2_PROTOCOL       *This,
+  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN  UINT64                     Address,
+  IN  UINTN                      Count,
+  OUT VOID                       *Buffer
+  )
+{
+  EFI_STATUS                 Status;
+  UINT8                      InStride;
+  UINT8                      OutStride;
+  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;
+  UINT8                      *Uint8Buffer;
+
+  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Select loop based on the width of the transfer
+  //
+  InStride       = mInStride[Width];
+  OutStride      = mOutStride[Width];
+  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH)(Width & 0x03);
+  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {
+    if (OperationWidth == EfiCpuIoWidthUint8) {
+      *Uint8Buffer = MmioRead8 ((UINTN)Address);
+    } else if (OperationWidth == EfiCpuIoWidthUint16) {
+      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);
+    } else if (OperationWidth == EfiCpuIoWidthUint32) {
+      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);
+    } else if (OperationWidth == EfiCpuIoWidthUint64) {
+      *((UINT64 *)Uint8Buffer) = MmioRead64 ((UINTN)Address);
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Writes memory-mapped registers.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
+  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
+  each of the Count operations that is performed.
+
+  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
+  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times on the same Address.
+
+  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
+  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times from the first element of Buffer.
+
+  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
+  @param[in]  Width    Signifies the width of the I/O or Memory operation.
+  @param[in]  Address  The base address of the I/O operation.
+  @param[in]  Count    The number of I/O operations to perform. The number of
+                       bytes moved is Width size * Count, starting at Address.
+  @param[in]  Buffer   For read operations, the destination buffer to store the results.
+                       For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The data was read from or written to the PI system.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CpuMemoryServiceWrite (
+  IN EFI_CPU_IO2_PROTOCOL       *This,
+  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN UINT64                     Address,
+  IN UINTN                      Count,
+  IN VOID                       *Buffer
+  )
+{
+  EFI_STATUS                 Status;
+  UINT8                      InStride;
+  UINT8                      OutStride;
+  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;
+  UINT8                      *Uint8Buffer;
+
+  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Select loop based on the width of the transfer
+  //
+  InStride       = mInStride[Width];
+  OutStride      = mOutStride[Width];
+  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH)(Width & 0x03);
+  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {
+    if (OperationWidth == EfiCpuIoWidthUint8) {
+      MmioWrite8 ((UINTN)Address, *Uint8Buffer);
+    } else if (OperationWidth == EfiCpuIoWidthUint16) {
+      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));
+    } else if (OperationWidth == EfiCpuIoWidthUint32) {
+      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));
+    } else if (OperationWidth == EfiCpuIoWidthUint64) {
+      MmioWrite64 ((UINTN)Address, *((UINT64 *)Uint8Buffer));
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Reads I/O registers.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
+  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
+  each of the Count operations that is performed.
+
+  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
+  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times on the same Address.
+
+  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
+  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times from the first element of Buffer.
+
+  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
+  @param[in]  Width    Signifies the width of the I/O or Memory operation.
+  @param[in]  Address  The base address of the I/O operation.
+  @param[in]  Count    The number of I/O operations to perform. The number of
+                       bytes moved is Width size * Count, starting at Address.
+  @param[out] Buffer   For read operations, the destination buffer to store the results.
+                       For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The data was read from or written to the PI system.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CpuIoServiceRead (
+  IN  EFI_CPU_IO2_PROTOCOL       *This,
+  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN  UINT64                     Address,
+  IN  UINTN                      Count,
+  OUT VOID                       *Buffer
+  )
+{
+  EFI_STATUS                 Status;
+  UINT8                      InStride;
+  UINT8                      OutStride;
+  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;
+  UINT8                      *Uint8Buffer;
+
+  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Address += PcdGet64 (PcdPciIoTranslation);
+
+  //
+  // Select loop based on the width of the transfer
+  //
+  InStride       = mInStride[Width];
+  OutStride      = mOutStride[Width];
+  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH)(Width & 0x03);
+
+  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {
+    if (OperationWidth == EfiCpuIoWidthUint8) {
+      *Uint8Buffer = MmioRead8 ((UINTN)Address);
+    } else if (OperationWidth == EfiCpuIoWidthUint16) {
+      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);
+    } else if (OperationWidth == EfiCpuIoWidthUint32) {
+      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Write I/O registers.
+
+  The I/O operations are carried out exactly as requested. The caller is responsible
+  for satisfying any alignment and I/O width restrictions that a PI System on a
+  platform might require. For example on some platforms, width requests of
+  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
+  be handled by the driver.
+
+  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
+  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
+  each of the Count operations that is performed.
+
+  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
+  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times on the same Address.
+
+  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
+  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
+  incremented for each of the Count operations that is performed. The read or
+  write operation is performed Count times from the first element of Buffer.
+
+  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
+  @param[in]  Width    Signifies the width of the I/O or Memory operation.
+  @param[in]  Address  The base address of the I/O operation.
+  @param[in]  Count    The number of I/O operations to perform. The number of
+                       bytes moved is Width size * Count, starting at Address.
+  @param[in]  Buffer   For read operations, the destination buffer to store the results.
+                       For write operations, the source buffer from which to write data.
+
+  @retval EFI_SUCCESS            The data was read from or written to the PI system.
+  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
+  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
+  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
+                                 and Count is not valid for this PI system.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CpuIoServiceWrite (
+  IN EFI_CPU_IO2_PROTOCOL       *This,
+  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,
+  IN UINT64                     Address,
+  IN UINTN                      Count,
+  IN VOID                       *Buffer
+  )
+{
+  EFI_STATUS                 Status;
+  UINT8                      InStride;
+  UINT8                      OutStride;
+  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;
+  UINT8                      *Uint8Buffer;
+
+  //
+  // Make sure the parameters are valid
+  //
+  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Address += PcdGet64 (PcdPciIoTranslation);
+
+  //
+  // Select loop based on the width of the transfer
+  //
+  InStride       = mInStride[Width];
+  OutStride      = mOutStride[Width];
+  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH)(Width & 0x03);
+
+  for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {
+    if (OperationWidth == EfiCpuIoWidthUint8) {
+      MmioWrite8 ((UINTN)Address, *Uint8Buffer);
+    } else if (OperationWidth == EfiCpuIoWidthUint16) {
+      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));
+    } else if (OperationWidth == EfiCpuIoWidthUint32) {
+      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+//
+// CPU I/O 2 Protocol instance
+//
+STATIC EFI_CPU_IO2_PROTOCOL  mCpuMmio2 = {
+  {
+    CpuMemoryServiceRead,
+    CpuMemoryServiceWrite
+  },
+  {
+    CpuIoServiceRead,
+    CpuIoServiceWrite
+  }
+};
+
+/**
+  The user Entry Point for module CpuIo2Dxe. The user code starts with this function.
+
+  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
+  @param[in] SystemTable    A pointer to the EFI System Table.
+
+  @retval EFI_SUCCESS       The entry point is executed successfully.
+  @retval other             Some error occurs when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMmio2Initialize (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+
+  ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiCpuIo2ProtocolGuid);
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &mHandle,
+                  &gEfiCpuIo2ProtocolGuid,
+                  &mCpuMmio2,
+                  NULL
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf b/UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf
new file mode 100644
index 0000000000..32577be7ea
--- /dev/null
+++ b/UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf
@@ -0,0 +1,48 @@
+## @file
+#  Produces the CPU I/O 2 Protocol by using the services of the I/O Library.
+#
+# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
+# 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                      = CpuMmio2Dxe
+  MODULE_UNI_FILE                = CpuMmio2Dxe.uni
+  FILE_GUID                      = FBC36D76-CF22-2584-DBD8-85FF765BAEF1
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = CpuMmio2Initialize
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = ARM AARCH64 LOONGARCH64 RISCV64
+#
+
+[Sources]
+  CpuMmio2Dxe.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  BaseLib
+  DebugLib
+  IoLib
+  PcdLib
+  UefiBootServicesTableLib
+
+[Pcd]
+  gEfiMdePkgTokenSpaceGuid.PcdPciIoTranslation
+
+[Protocols]
+  gEfiCpuIo2ProtocolGuid                         ## PRODUCES
+
+[Depex]
+  TRUE
diff --git a/UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.uni b/UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.uni
new file mode 100644
index 0000000000..af3b1a656f
--- /dev/null
+++ b/UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.uni
@@ -0,0 +1,18 @@
+// /** @file
+// Produces the CPU I/O 2 Protocol by using the services of the I/O Library.
+//
+// Produces the CPU I/O 2 Protocol by using the services of the I/O Library.
+//
+// Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+// Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
+// 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 "Produces the CPU I/O 2 Protocol by using the services of the I/O Library"
+
+#string STR_MODULE_DESCRIPTION          #language en-US "Produces the CPU I/O 2 Protocol by using the services of the I/O Library."
+
diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
index 178dc3c0f9..4aa1fd218b 100644
--- a/UefiCpuPkg/UefiCpuPkg.dsc
+++ b/UefiCpuPkg/UefiCpuPkg.dsc
@@ -206,8 +206,10 @@
   UefiCpuPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.inf
   UefiCpuPkg/CpuTimerDxeRiscV64/CpuTimerDxeRiscV64.inf
   UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf
+  UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf
 
 [Components.LOONGARCH64]
+  UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf
   UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
   UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
 
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114545): https://edk2.groups.io/g/devel/message/114545
Mute This Topic: https://groups.io/mt/103971663/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 20/37] ArmVirtPkg: Enable CpuMmio2Dxe
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (18 preceding siblings ...)
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 19/37] UefiCpuPkg: Add a new CPU IO 2 driver named CpuMmio2Dxe Chao Li
@ 2024-01-26  6:29 ` Chao Li
  2024-02-01 22:19   ` Laszlo Ersek
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 21/37] OvmfPkg/RiscVVirt: " Chao Li
                   ` (21 subsequent siblings)
  41 siblings, 1 reply; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:29 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Laszlo Ersek, Leif Lindholm, Sami Mujawar,
	Gerd Hoffmann

CpuMmio2Dxe is supports MMIO, enable it.

Build-tested only (with "ArmVirtQemu.dsc").

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Leif Lindholm <quic_llindhol@quicinc.com>
Cc: Sami Mujawar <sami.mujawar@arm.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
---
 ArmVirtPkg/ArmVirtCloudHv.dsc        | 2 +-
 ArmVirtPkg/ArmVirtCloudHv.fdf        | 2 +-
 ArmVirtPkg/ArmVirtKvmTool.dsc        | 2 +-
 ArmVirtPkg/ArmVirtKvmTool.fdf        | 2 +-
 ArmVirtPkg/ArmVirtQemu.dsc           | 2 +-
 ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc | 2 +-
 ArmVirtPkg/ArmVirtQemuKernel.dsc     | 2 +-
 7 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/ArmVirtPkg/ArmVirtCloudHv.dsc b/ArmVirtPkg/ArmVirtCloudHv.dsc
index 76c0d28544..5cb2a609b1 100644
--- a/ArmVirtPkg/ArmVirtCloudHv.dsc
+++ b/ArmVirtPkg/ArmVirtCloudHv.dsc
@@ -341,7 +341,7 @@
   #
   # PCI support
   #
-  ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf {
+  UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf {
     <LibraryClasses>
       NULL|OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
   }
diff --git a/ArmVirtPkg/ArmVirtCloudHv.fdf b/ArmVirtPkg/ArmVirtCloudHv.fdf
index 56d1ea6e8c..8554efc294 100644
--- a/ArmVirtPkg/ArmVirtCloudHv.fdf
+++ b/ArmVirtPkg/ArmVirtCloudHv.fdf
@@ -201,7 +201,7 @@ READ_LOCK_STATUS   = TRUE
   #
   # PCI support
   #
-  INF ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf
+  INF UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf
   INF MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf
   INF MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
   INF OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf
diff --git a/ArmVirtPkg/ArmVirtKvmTool.dsc b/ArmVirtPkg/ArmVirtKvmTool.dsc
index cac4fe06d3..20da331966 100644
--- a/ArmVirtPkg/ArmVirtKvmTool.dsc
+++ b/ArmVirtPkg/ArmVirtKvmTool.dsc
@@ -372,7 +372,7 @@
   #
   # PCI support
   #
-  ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf {
+  UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf {
     <LibraryClasses>
       NULL|OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
       NULL|OvmfPkg/Library/BaseCachingPciExpressLib/BaseCachingPciExpressLib.inf
diff --git a/ArmVirtPkg/ArmVirtKvmTool.fdf b/ArmVirtPkg/ArmVirtKvmTool.fdf
index 82aff47673..cdf756c112 100644
--- a/ArmVirtPkg/ArmVirtKvmTool.fdf
+++ b/ArmVirtPkg/ArmVirtKvmTool.fdf
@@ -195,7 +195,7 @@ READ_LOCK_STATUS   = TRUE
   #
   # PCI support
   #
-  INF ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf
+  INF UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf
   INF MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf
   INF MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
   INF OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc
index cf306cac08..dbd2396c78 100644
--- a/ArmVirtPkg/ArmVirtQemu.dsc
+++ b/ArmVirtPkg/ArmVirtQemu.dsc
@@ -526,7 +526,7 @@
   #
   # PCI support
   #
-  ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf {
+  UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf {
     <LibraryClasses>
       NULL|OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
   }
diff --git a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
index 9b3e37d5c9..c5d097ffb9 100644
--- a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
+++ b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
@@ -153,7 +153,7 @@ READ_LOCK_STATUS   = TRUE
   #
   # PCI support
   #
-  INF ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf
+  INF UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf
   INF MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf
   INF MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
   INF OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf
diff --git a/ArmVirtPkg/ArmVirtQemuKernel.dsc b/ArmVirtPkg/ArmVirtQemuKernel.dsc
index c0d079e28d..6a6ecfc12a 100644
--- a/ArmVirtPkg/ArmVirtQemuKernel.dsc
+++ b/ArmVirtPkg/ArmVirtQemuKernel.dsc
@@ -431,7 +431,7 @@
   #
   # PCI support
   #
-  ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf {
+  UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf {
     <LibraryClasses>
       NULL|OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
   }
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114546): https://edk2.groups.io/g/devel/message/114546
Mute This Topic: https://groups.io/mt/103971664/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 21/37] OvmfPkg/RiscVVirt: Enable CpuMmio2Dxe
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (19 preceding siblings ...)
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 20/37] ArmVirtPkg: Enable CpuMmio2Dxe Chao Li
@ 2024-01-26  6:30 ` Chao Li
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 22/37] OvmfPkg/RiscVVirt: Remove PciCpuIo2Dxe from RiscVVirt Chao Li
                   ` (20 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:30 UTC (permalink / raw)
  To: devel; +Cc: Sunil V L, Andrei Warkentin

CpuMmio2Dxe is supports MMIO, enable it.

Build-tested only (with "RiscVVirtQemu.dsc").

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Sunil V L <sunilvl@ventanamicro.com>
Cc: Andrei Warkentin <andrei.warkentin@intel.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Reviewed-by: Sunil V L <sunilvl@ventanamicro.com>
---
 OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc | 2 +-
 OvmfPkg/RiscVVirt/RiscVVirtQemu.fdf | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc b/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc
index 774dc81840..27f24648e8 100644
--- a/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc
+++ b/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc
@@ -433,7 +433,7 @@
   #
   # PCI support
   #
-  OvmfPkg/RiscVVirt/PciCpuIo2Dxe/PciCpuIo2Dxe.inf {
+  UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf {
     <LibraryClasses>
       NULL|OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
   }
diff --git a/OvmfPkg/RiscVVirt/RiscVVirtQemu.fdf b/OvmfPkg/RiscVVirt/RiscVVirtQemu.fdf
index 8121b9e579..e579f5b9b7 100644
--- a/OvmfPkg/RiscVVirt/RiscVVirtQemu.fdf
+++ b/OvmfPkg/RiscVVirt/RiscVVirtQemu.fdf
@@ -184,7 +184,7 @@ INF  OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
 #
 # PCI support
 #
-INF  OvmfPkg/RiscVVirt/PciCpuIo2Dxe/PciCpuIo2Dxe.inf
+INF  UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf
 INF  MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf
 INF  MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
 INF  OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114547): https://edk2.groups.io/g/devel/message/114547
Mute This Topic: https://groups.io/mt/103971665/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 22/37] OvmfPkg/RiscVVirt: Remove PciCpuIo2Dxe from RiscVVirt
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (20 preceding siblings ...)
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 21/37] OvmfPkg/RiscVVirt: " Chao Li
@ 2024-01-26  6:30 ` Chao Li
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 23/37] ArmVirtPkg: Move the FdtSerialPortAddressLib to OvmfPkg Chao Li
                   ` (19 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:30 UTC (permalink / raw)
  To: devel; +Cc: Sunil V L, Andrei Warkentin

CpuIo2Dxe is already used by RiscVVirt, so remove it.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Sunil V L <sunilvl@ventanamicro.com>
Cc: Andrei Warkentin <andrei.warkentin@intel.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Reviewed-by: Sunil V L <sunilvl@ventanamicro.com>
---
 OvmfPkg/RiscVVirt/PciCpuIo2Dxe/PciCpuIo2Dxe.c | 557 ------------------
 .../RiscVVirt/PciCpuIo2Dxe/PciCpuIo2Dxe.inf   |  48 --
 2 files changed, 605 deletions(-)
 delete mode 100644 OvmfPkg/RiscVVirt/PciCpuIo2Dxe/PciCpuIo2Dxe.c
 delete mode 100644 OvmfPkg/RiscVVirt/PciCpuIo2Dxe/PciCpuIo2Dxe.inf

diff --git a/OvmfPkg/RiscVVirt/PciCpuIo2Dxe/PciCpuIo2Dxe.c b/OvmfPkg/RiscVVirt/PciCpuIo2Dxe/PciCpuIo2Dxe.c
deleted file mode 100644
index f3bf07e631..0000000000
--- a/OvmfPkg/RiscVVirt/PciCpuIo2Dxe/PciCpuIo2Dxe.c
+++ /dev/null
@@ -1,557 +0,0 @@
-/** @file
-  Produces the CPU I/O 2 Protocol.
-
-Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>
-Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
-Copyright (c) 2022, Ventana Micro Systems Inc. All rights reserved.<BR>
-
-SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include <PiDxe.h>
-
-#include <Protocol/CpuIo2.h>
-
-#include <Library/BaseLib.h>
-#include <Library/DebugLib.h>
-#include <Library/IoLib.h>
-#include <Library/PcdLib.h>
-#include <Library/UefiBootServicesTableLib.h>
-
-#define MAX_IO_PORT_ADDRESS  0xFFFF
-
-//
-// Handle for the CPU I/O 2 Protocol
-//
-STATIC EFI_HANDLE  mHandle = NULL;
-
-//
-// Lookup table for increment values based on transfer widths
-//
-STATIC CONST UINT8  mInStride[] = {
-  1, // EfiCpuIoWidthUint8
-  2, // EfiCpuIoWidthUint16
-  4, // EfiCpuIoWidthUint32
-  8, // EfiCpuIoWidthUint64
-  0, // EfiCpuIoWidthFifoUint8
-  0, // EfiCpuIoWidthFifoUint16
-  0, // EfiCpuIoWidthFifoUint32
-  0, // EfiCpuIoWidthFifoUint64
-  1, // EfiCpuIoWidthFillUint8
-  2, // EfiCpuIoWidthFillUint16
-  4, // EfiCpuIoWidthFillUint32
-  8  // EfiCpuIoWidthFillUint64
-};
-
-//
-// Lookup table for increment values based on transfer widths
-//
-STATIC CONST UINT8  mOutStride[] = {
-  1, // EfiCpuIoWidthUint8
-  2, // EfiCpuIoWidthUint16
-  4, // EfiCpuIoWidthUint32
-  8, // EfiCpuIoWidthUint64
-  1, // EfiCpuIoWidthFifoUint8
-  2, // EfiCpuIoWidthFifoUint16
-  4, // EfiCpuIoWidthFifoUint32
-  8, // EfiCpuIoWidthFifoUint64
-  0, // EfiCpuIoWidthFillUint8
-  0, // EfiCpuIoWidthFillUint16
-  0, // EfiCpuIoWidthFillUint32
-  0  // EfiCpuIoWidthFillUint64
-};
-
-/**
-  Check parameters to a CPU I/O 2 Protocol service request.
-
-  The I/O operations are carried out exactly as requested. The caller is responsible
-  for satisfying any alignment and I/O width restrictions that a PI System on a
-  platform might require. For example on some platforms, width requests of
-  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
-  be handled by the driver.
-
-  @param[in] MmioOperation  TRUE for an MMIO operation, FALSE for I/O Port operation.
-  @param[in] Width          Signifies the width of the I/O or Memory operation.
-  @param[in] Address        The base address of the I/O operation.
-  @param[in] Count          The number of I/O operations to perform. The number of
-                            bytes moved is Width size * Count, starting at Address.
-  @param[in] Buffer         For read operations, the destination buffer to store the results.
-                            For write operations, the source buffer from which to write data.
-
-  @retval EFI_SUCCESS            The parameters for this request pass the checks.
-  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
-  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
-  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
-  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
-                                 and Count is not valid for this PI system.
-
-**/
-STATIC
-EFI_STATUS
-CpuIoCheckParameter (
-  IN BOOLEAN                    MmioOperation,
-  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,
-  IN UINT64                     Address,
-  IN UINTN                      Count,
-  IN VOID                       *Buffer
-  )
-{
-  UINT64  MaxCount;
-  UINT64  Limit;
-
-  //
-  // Check to see if Buffer is NULL
-  //
-  if (Buffer == NULL) {
-    return EFI_INVALID_PARAMETER;
-  }
-
-  //
-  // Check to see if Width is in the valid range
-  //
-  if ((UINT32)Width >= EfiCpuIoWidthMaximum) {
-    return EFI_INVALID_PARAMETER;
-  }
-
-  //
-  // For FIFO type, the target address won't increase during the access,
-  // so treat Count as 1
-  //
-  if ((Width >= EfiCpuIoWidthFifoUint8) && (Width <= EfiCpuIoWidthFifoUint64)) {
-    Count = 1;
-  }
-
-  //
-  // Check to see if Width is in the valid range for I/O Port operations
-  //
-  Width = (EFI_CPU_IO_PROTOCOL_WIDTH)(Width & 0x03);
-  if (!MmioOperation && (Width == EfiCpuIoWidthUint64)) {
-    return EFI_INVALID_PARAMETER;
-  }
-
-  //
-  // Check to see if Address is aligned
-  //
-  if ((Address & (UINT64)(mInStride[Width] - 1)) != 0) {
-    return EFI_UNSUPPORTED;
-  }
-
-  //
-  // Check to see if any address associated with this transfer exceeds the maximum
-  // allowed address.  The maximum address implied by the parameters passed in is
-  // Address + Size * Count.  If the following condition is met, then the transfer
-  // is not supported.
-  //
-  //    Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1
-  //
-  // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count
-  // can also be the maximum integer value supported by the CPU, this range
-  // check must be adjusted to avoid all overflow conditions.
-  //
-  // The following form of the range check is equivalent but assumes that
-  // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1).
-  //
-  Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS);
-  if (Count == 0) {
-    if (Address > Limit) {
-      return EFI_UNSUPPORTED;
-    }
-  } else {
-    MaxCount = RShiftU64 (Limit, Width);
-    if (MaxCount < (Count - 1)) {
-      return EFI_UNSUPPORTED;
-    }
-
-    if (Address > LShiftU64 (MaxCount - Count + 1, Width)) {
-      return EFI_UNSUPPORTED;
-    }
-  }
-
-  //
-  // Check to see if Buffer is aligned
-  //
-  if (((UINTN)Buffer & ((MIN (sizeof (UINTN), mInStride[Width])  - 1))) != 0) {
-    return EFI_UNSUPPORTED;
-  }
-
-  return EFI_SUCCESS;
-}
-
-/**
-  Reads memory-mapped registers.
-
-  The I/O operations are carried out exactly as requested. The caller is responsible
-  for satisfying any alignment and I/O width restrictions that a PI System on a
-  platform might require. For example on some platforms, width requests of
-  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
-  be handled by the driver.
-
-  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
-  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
-  each of the Count operations that is performed.
-
-  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
-  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
-  incremented for each of the Count operations that is performed. The read or
-  write operation is performed Count times on the same Address.
-
-  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
-  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
-  incremented for each of the Count operations that is performed. The read or
-  write operation is performed Count times from the first element of Buffer.
-
-  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
-  @param[in]  Width    Signifies the width of the I/O or Memory operation.
-  @param[in]  Address  The base address of the I/O operation.
-  @param[in]  Count    The number of I/O operations to perform. The number of
-                       bytes moved is Width size * Count, starting at Address.
-  @param[out] Buffer   For read operations, the destination buffer to store the results.
-                       For write operations, the source buffer from which to write data.
-
-  @retval EFI_SUCCESS            The data was read from or written to the PI system.
-  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
-  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
-  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
-  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
-                                 and Count is not valid for this PI system.
-
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-CpuMemoryServiceRead (
-  IN  EFI_CPU_IO2_PROTOCOL       *This,
-  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,
-  IN  UINT64                     Address,
-  IN  UINTN                      Count,
-  OUT VOID                       *Buffer
-  )
-{
-  EFI_STATUS                 Status;
-  UINT8                      InStride;
-  UINT8                      OutStride;
-  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;
-  UINT8                      *Uint8Buffer;
-
-  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);
-  if (EFI_ERROR (Status)) {
-    return Status;
-  }
-
-  //
-  // Select loop based on the width of the transfer
-  //
-  InStride       = mInStride[Width];
-  OutStride      = mOutStride[Width];
-  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH)(Width & 0x03);
-  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {
-    if (OperationWidth == EfiCpuIoWidthUint8) {
-      *Uint8Buffer = MmioRead8 ((UINTN)Address);
-    } else if (OperationWidth == EfiCpuIoWidthUint16) {
-      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);
-    } else if (OperationWidth == EfiCpuIoWidthUint32) {
-      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);
-    } else if (OperationWidth == EfiCpuIoWidthUint64) {
-      *((UINT64 *)Uint8Buffer) = MmioRead64 ((UINTN)Address);
-    }
-  }
-
-  return EFI_SUCCESS;
-}
-
-/**
-  Writes memory-mapped registers.
-
-  The I/O operations are carried out exactly as requested. The caller is responsible
-  for satisfying any alignment and I/O width restrictions that a PI System on a
-  platform might require. For example on some platforms, width requests of
-  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
-  be handled by the driver.
-
-  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
-  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
-  each of the Count operations that is performed.
-
-  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
-  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
-  incremented for each of the Count operations that is performed. The read or
-  write operation is performed Count times on the same Address.
-
-  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
-  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
-  incremented for each of the Count operations that is performed. The read or
-  write operation is performed Count times from the first element of Buffer.
-
-  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
-  @param[in]  Width    Signifies the width of the I/O or Memory operation.
-  @param[in]  Address  The base address of the I/O operation.
-  @param[in]  Count    The number of I/O operations to perform. The number of
-                       bytes moved is Width size * Count, starting at Address.
-  @param[in]  Buffer   For read operations, the destination buffer to store the results.
-                       For write operations, the source buffer from which to write data.
-
-  @retval EFI_SUCCESS            The data was read from or written to the PI system.
-  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
-  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
-  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
-  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
-                                 and Count is not valid for this PI system.
-
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-CpuMemoryServiceWrite (
-  IN EFI_CPU_IO2_PROTOCOL       *This,
-  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,
-  IN UINT64                     Address,
-  IN UINTN                      Count,
-  IN VOID                       *Buffer
-  )
-{
-  EFI_STATUS                 Status;
-  UINT8                      InStride;
-  UINT8                      OutStride;
-  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;
-  UINT8                      *Uint8Buffer;
-
-  Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);
-  if (EFI_ERROR (Status)) {
-    return Status;
-  }
-
-  //
-  // Select loop based on the width of the transfer
-  //
-  InStride       = mInStride[Width];
-  OutStride      = mOutStride[Width];
-  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH)(Width & 0x03);
-  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {
-    if (OperationWidth == EfiCpuIoWidthUint8) {
-      MmioWrite8 ((UINTN)Address, *Uint8Buffer);
-    } else if (OperationWidth == EfiCpuIoWidthUint16) {
-      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));
-    } else if (OperationWidth == EfiCpuIoWidthUint32) {
-      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));
-    } else if (OperationWidth == EfiCpuIoWidthUint64) {
-      MmioWrite64 ((UINTN)Address, *((UINT64 *)Uint8Buffer));
-    }
-  }
-
-  return EFI_SUCCESS;
-}
-
-/**
-  Reads I/O registers.
-
-  The I/O operations are carried out exactly as requested. The caller is responsible
-  for satisfying any alignment and I/O width restrictions that a PI System on a
-  platform might require. For example on some platforms, width requests of
-  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
-  be handled by the driver.
-
-  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
-  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
-  each of the Count operations that is performed.
-
-  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
-  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
-  incremented for each of the Count operations that is performed. The read or
-  write operation is performed Count times on the same Address.
-
-  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
-  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
-  incremented for each of the Count operations that is performed. The read or
-  write operation is performed Count times from the first element of Buffer.
-
-  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
-  @param[in]  Width    Signifies the width of the I/O or Memory operation.
-  @param[in]  Address  The base address of the I/O operation.
-  @param[in]  Count    The number of I/O operations to perform. The number of
-                       bytes moved is Width size * Count, starting at Address.
-  @param[out] Buffer   For read operations, the destination buffer to store the results.
-                       For write operations, the source buffer from which to write data.
-
-  @retval EFI_SUCCESS            The data was read from or written to the PI system.
-  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
-  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
-  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
-  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
-                                 and Count is not valid for this PI system.
-
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-CpuIoServiceRead (
-  IN  EFI_CPU_IO2_PROTOCOL       *This,
-  IN  EFI_CPU_IO_PROTOCOL_WIDTH  Width,
-  IN  UINT64                     Address,
-  IN  UINTN                      Count,
-  OUT VOID                       *Buffer
-  )
-{
-  EFI_STATUS                 Status;
-  UINT8                      InStride;
-  UINT8                      OutStride;
-  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;
-  UINT8                      *Uint8Buffer;
-
-  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);
-  if (EFI_ERROR (Status)) {
-    return Status;
-  }
-
-  Address += PcdGet64 (PcdPciIoTranslation);
-
-  //
-  // Select loop based on the width of the transfer
-  //
-  InStride       = mInStride[Width];
-  OutStride      = mOutStride[Width];
-  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH)(Width & 0x03);
-
-  for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {
-    if (OperationWidth == EfiCpuIoWidthUint8) {
-      *Uint8Buffer = MmioRead8 ((UINTN)Address);
-    } else if (OperationWidth == EfiCpuIoWidthUint16) {
-      *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);
-    } else if (OperationWidth == EfiCpuIoWidthUint32) {
-      *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);
-    }
-  }
-
-  return EFI_SUCCESS;
-}
-
-/**
-  Write I/O registers.
-
-  The I/O operations are carried out exactly as requested. The caller is responsible
-  for satisfying any alignment and I/O width restrictions that a PI System on a
-  platform might require. For example on some platforms, width requests of
-  EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will
-  be handled by the driver.
-
-  If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32,
-  or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for
-  each of the Count operations that is performed.
-
-  If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16,
-  EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is
-  incremented for each of the Count operations that is performed. The read or
-  write operation is performed Count times on the same Address.
-
-  If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16,
-  EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is
-  incremented for each of the Count operations that is performed. The read or
-  write operation is performed Count times from the first element of Buffer.
-
-  @param[in]  This     A pointer to the EFI_CPU_IO2_PROTOCOL instance.
-  @param[in]  Width    Signifies the width of the I/O or Memory operation.
-  @param[in]  Address  The base address of the I/O operation.
-  @param[in]  Count    The number of I/O operations to perform. The number of
-                       bytes moved is Width size * Count, starting at Address.
-  @param[in]  Buffer   For read operations, the destination buffer to store the results.
-                       For write operations, the source buffer from which to write data.
-
-  @retval EFI_SUCCESS            The data was read from or written to the PI system.
-  @retval EFI_INVALID_PARAMETER  Width is invalid for this PI system.
-  @retval EFI_INVALID_PARAMETER  Buffer is NULL.
-  @retval EFI_UNSUPPORTED        The Buffer is not aligned for the given Width.
-  @retval EFI_UNSUPPORTED        The address range specified by Address, Width,
-                                 and Count is not valid for this PI system.
-
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-CpuIoServiceWrite (
-  IN EFI_CPU_IO2_PROTOCOL       *This,
-  IN EFI_CPU_IO_PROTOCOL_WIDTH  Width,
-  IN UINT64                     Address,
-  IN UINTN                      Count,
-  IN VOID                       *Buffer
-  )
-{
-  EFI_STATUS                 Status;
-  UINT8                      InStride;
-  UINT8                      OutStride;
-  EFI_CPU_IO_PROTOCOL_WIDTH  OperationWidth;
-  UINT8                      *Uint8Buffer;
-
-  //
-  // Make sure the parameters are valid
-  //
-  Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);
-  if (EFI_ERROR (Status)) {
-    return Status;
-  }
-
-  Address += PcdGet64 (PcdPciIoTranslation);
-
-  //
-  // Select loop based on the width of the transfer
-  //
-  InStride       = mInStride[Width];
-  OutStride      = mOutStride[Width];
-  OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH)(Width & 0x03);
-
-  for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) {
-    if (OperationWidth == EfiCpuIoWidthUint8) {
-      MmioWrite8 ((UINTN)Address, *Uint8Buffer);
-    } else if (OperationWidth == EfiCpuIoWidthUint16) {
-      MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));
-    } else if (OperationWidth == EfiCpuIoWidthUint32) {
-      MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));
-    }
-  }
-
-  return EFI_SUCCESS;
-}
-
-//
-// CPU I/O 2 Protocol instance
-//
-STATIC EFI_CPU_IO2_PROTOCOL  mCpuIo2 = {
-  {
-    CpuMemoryServiceRead,
-    CpuMemoryServiceWrite
-  },
-  {
-    CpuIoServiceRead,
-    CpuIoServiceWrite
-  }
-};
-
-/**
-  The user Entry Point for module CpuIo2Dxe. The user code starts with this function.
-
-  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
-  @param[in] SystemTable    A pointer to the EFI System Table.
-
-  @retval EFI_SUCCESS       The entry point is executed successfully.
-  @retval other             Some error occurs when executing this entry point.
-
-**/
-EFI_STATUS
-EFIAPI
-PciCpuIo2Initialize (
-  IN EFI_HANDLE        ImageHandle,
-  IN EFI_SYSTEM_TABLE  *SystemTable
-  )
-{
-  EFI_STATUS  Status;
-
-  ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiCpuIo2ProtocolGuid);
-  Status = gBS->InstallMultipleProtocolInterfaces (
-                  &mHandle,
-                  &gEfiCpuIo2ProtocolGuid,
-                  &mCpuIo2,
-                  NULL
-                  );
-  ASSERT_EFI_ERROR (Status);
-
-  return Status;
-}
diff --git a/OvmfPkg/RiscVVirt/PciCpuIo2Dxe/PciCpuIo2Dxe.inf b/OvmfPkg/RiscVVirt/PciCpuIo2Dxe/PciCpuIo2Dxe.inf
deleted file mode 100644
index 4f78cfa406..0000000000
--- a/OvmfPkg/RiscVVirt/PciCpuIo2Dxe/PciCpuIo2Dxe.inf
+++ /dev/null
@@ -1,48 +0,0 @@
-## @file
-#  Produces the CPU I/O 2 Protocol by using the services of the I/O Library.
-#
-# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
-# Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
-# Copyright (c) 2022, Ventana Micro Systems Inc. All rights reserved.<BR>
-#
-# SPDX-License-Identifier: BSD-2-Clause-Patent
-#
-##
-
-[Defines]
-  INF_VERSION                    = 0x0001001B
-  BASE_NAME                      = PciCpuIo2Dxe
-  FILE_GUID                      = 9BD3C765-2579-4CF0-9349-D77205565030
-  MODULE_TYPE                    = DXE_DRIVER
-  VERSION_STRING                 = 1.0
-  ENTRY_POINT                    = PciCpuIo2Initialize
-
-#
-# The following information is for reference only and not required by the build tools.
-#
-#  VALID_ARCHITECTURES           = RISCV64
-#
-
-[Sources]
-  PciCpuIo2Dxe.c
-
-[Packages]
-  OvmfPkg/OvmfPkg.dec
-  MdePkg/MdePkg.dec
-
-[LibraryClasses]
-  UefiDriverEntryPoint
-  BaseLib
-  DebugLib
-  IoLib
-  PcdLib
-  UefiBootServicesTableLib
-
-[Pcd]
-  gEfiMdePkgTokenSpaceGuid.PcdPciIoTranslation
-
-[Protocols]
-  gEfiCpuIo2ProtocolGuid                         ## PRODUCES
-
-[Depex]
-  TRUE
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114548): https://edk2.groups.io/g/devel/message/114548
Mute This Topic: https://groups.io/mt/103971666/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 23/37] ArmVirtPkg: Move the FdtSerialPortAddressLib to OvmfPkg
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (21 preceding siblings ...)
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 22/37] OvmfPkg/RiscVVirt: Remove PciCpuIo2Dxe from RiscVVirt Chao Li
@ 2024-01-26  6:30 ` Chao Li
  2024-01-29 19:27   ` Laszlo Ersek
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 24/37] ArmVirtPkg: Move two PCD variables into OvmfPkg Chao Li
                   ` (18 subsequent siblings)
  41 siblings, 1 reply; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:30 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Laszlo Ersek, Leif Lindholm, Sami Mujawar,
	Gerd Hoffmann, Jiewen Yao

Move the FdtSerialPortAddressLib to Ovmfpkg so that other ARCH can
easily use it.

Build-tested only (with "ArmVirtQemu.dsc and OvmfPkgX64.dsc").

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Leif Lindholm <quic_llindhol@quicinc.com>
Cc: Sami Mujawar <sami.mujawar@arm.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
---
 ArmVirtPkg/ArmVirt.dsc.inc                                    | 2 +-
 ArmVirtPkg/ArmVirtPkg.dec                                     | 1 -
 .../Include/Library/FdtSerialPortAddressLib.h                 | 0
 .../Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c | 0
 .../FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf       | 2 +-
 OvmfPkg/OvmfPkg.dec                                           | 4 ++++
 6 files changed, 6 insertions(+), 3 deletions(-)
 rename {ArmVirtPkg => OvmfPkg}/Include/Library/FdtSerialPortAddressLib.h (100%)
 rename {ArmVirtPkg => OvmfPkg}/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c (100%)
 rename {ArmVirtPkg => OvmfPkg}/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf (90%)

diff --git a/ArmVirtPkg/ArmVirt.dsc.inc b/ArmVirtPkg/ArmVirt.dsc.inc
index 9b23ef97ec..2bc6a29eb1 100644
--- a/ArmVirtPkg/ArmVirt.dsc.inc
+++ b/ArmVirtPkg/ArmVirt.dsc.inc
@@ -122,7 +122,7 @@
   # ARM PL011 UART Driver
   PL011UartLib|ArmPlatformPkg/Library/PL011UartLib/PL011UartLib.inf
   SerialPortLib|ArmVirtPkg/Library/FdtPL011SerialPortLib/FdtPL011SerialPortLib.inf
-  FdtSerialPortAddressLib|ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf
+  FdtSerialPortAddressLib|OvmfPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf
 
   PeCoffExtraActionLib|ArmPkg/Library/DebugPeCoffExtraActionLib/DebugPeCoffExtraActionLib.inf
   #PeCoffExtraActionLib|MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf
diff --git a/ArmVirtPkg/ArmVirtPkg.dec b/ArmVirtPkg/ArmVirtPkg.dec
index 05d2d36c1d..a658c91031 100644
--- a/ArmVirtPkg/ArmVirtPkg.dec
+++ b/ArmVirtPkg/ArmVirtPkg.dec
@@ -27,7 +27,6 @@
 
 [LibraryClasses]
   ArmVirtMemInfoLib|Include/Library/ArmVirtMemInfoLib.h
-  FdtSerialPortAddressLib|Include/Library/FdtSerialPortAddressLib.h
 
 [Guids.common]
   gArmVirtTokenSpaceGuid = { 0x0B6F5CA7, 0x4F53, 0x445A, { 0xB7, 0x6E, 0x2E, 0x36, 0x5B, 0x80, 0x63, 0x66 } }
diff --git a/ArmVirtPkg/Include/Library/FdtSerialPortAddressLib.h b/OvmfPkg/Include/Library/FdtSerialPortAddressLib.h
similarity index 100%
rename from ArmVirtPkg/Include/Library/FdtSerialPortAddressLib.h
rename to OvmfPkg/Include/Library/FdtSerialPortAddressLib.h
diff --git a/ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c b/OvmfPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c
similarity index 100%
rename from ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c
rename to OvmfPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c
diff --git a/ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf b/OvmfPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf
similarity index 90%
rename from ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf
rename to OvmfPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf
index ae6d0d374b..e27742e9fa 100644
--- a/ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf
+++ b/OvmfPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf
@@ -18,9 +18,9 @@
   FdtSerialPortAddressLib.c
 
 [Packages]
-  ArmVirtPkg/ArmVirtPkg.dec
   EmbeddedPkg/EmbeddedPkg.dec
   MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
 
 [LibraryClasses]
   BaseLib
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 7bc2bf1674..13e69e6648 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -29,6 +29,10 @@
   ##  @libraryclass  Verify blobs read from the VMM
   BlobVerifierLib|Include/Library/BlobVerifierLib.h
 
+  ##  @libraryclass  FdtSerialPortAddressLib
+  #
+  FdtSerialPortAddressLib|Include/Library/FdtSerialPortAddressLib.h
+
   ##  @libraryclass  Loads and boots a Linux kernel image
   #
   LoadLinuxLib|Include/Library/LoadLinuxLib.h
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114549): https://edk2.groups.io/g/devel/message/114549
Mute This Topic: https://groups.io/mt/103971668/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 24/37] ArmVirtPkg: Move two PCD variables into OvmfPkg
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (22 preceding siblings ...)
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 23/37] ArmVirtPkg: Move the FdtSerialPortAddressLib to OvmfPkg Chao Li
@ 2024-01-26  6:30 ` Chao Li
  2024-01-29 19:49   ` Laszlo Ersek
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 25/37] ArmVirtPkg: Move PlatformBootManagerLib to OvmfPkg Chao Li
                   ` (17 subsequent siblings)
  41 siblings, 1 reply; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:30 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Laszlo Ersek, Leif Lindholm, Sami Mujawar,
	Gerd Hoffmann, Jiewen Yao

Move the PcdTerminalTypeGuidBuffer and PcdUninstallMemAttrProtocol into
OvmfPkg so other ARCH can easily use it.

Build-tested only (with "ArmVirtQemu.dsc and OvmfPkgX64.dsc").

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Leif Lindholm <quic_llindhol@quicinc.com>
Cc: Sami Mujawar <sami.mujawar@arm.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
---
 ArmVirtPkg/ArmVirtPkg.dec                           | 13 -------------
 ArmVirtPkg/ArmVirtQemu.dsc                          |  2 +-
 ArmVirtPkg/ArmVirtQemuKernel.dsc                    |  2 +-
 .../PlatformBootManagerLib.inf                      |  5 ++---
 OvmfPkg/OvmfPkg.dec                                 | 13 +++++++++++++
 5 files changed, 17 insertions(+), 18 deletions(-)

diff --git a/ArmVirtPkg/ArmVirtPkg.dec b/ArmVirtPkg/ArmVirtPkg.dec
index a658c91031..6aa5ea05f4 100644
--- a/ArmVirtPkg/ArmVirtPkg.dec
+++ b/ArmVirtPkg/ArmVirtPkg.dec
@@ -41,21 +41,8 @@
   gArmVirtTokenSpaceGuid.PcdTpm2SupportEnabled|FALSE|BOOLEAN|0x00000004
 
 [PcdsFixedAtBuild, PcdsPatchableInModule]
-  #
-  # Binary representation of the GUID that determines the terminal type. The
-  # size must be exactly 16 bytes. The default value corresponds to
-  # EFI_VT_100_GUID.
-  #
-  gArmVirtTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x65, 0x60, 0xA6, 0xDF, 0x19, 0xB4, 0xD3, 0x11, 0x9A, 0x2D, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D}|VOID*|0x00000007
-
   ##
   # This is the physical address of Rsdp which is the core struct of Acpi.
   # Cloud Hypervisor has no other way to pass Rsdp address to the guest except use a PCD.
   #
   gArmVirtTokenSpaceGuid.PcdCloudHvAcpiRsdpBaseAddress|0x0|UINT64|0x00000005
-
-  ##
-  # Whether the EFI memory attributes protocol should be uninstalled before
-  # invoking the OS loader. This may be needed to work around problematic
-  # builds of shim that use the protocol incorrectly.
-  gArmVirtTokenSpaceGuid.PcdUninstallMemAttrProtocol|FALSE|BOOLEAN|0x00000006
diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc
index dbd2396c78..147180f645 100644
--- a/ArmVirtPkg/ArmVirtQemu.dsc
+++ b/ArmVirtPkg/ArmVirtQemu.dsc
@@ -182,7 +182,7 @@
 !if $(TTY_TERMINAL) == TRUE
   gEfiMdePkgTokenSpaceGuid.PcdDefaultTerminalType|4
   # Set terminal type to TtyTerm, the value encoded is EFI_TTY_TERM_GUID
-  gArmVirtTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x80, 0x6d, 0x91, 0x7d, 0xb1, 0x5b, 0x8c, 0x45, 0xa4, 0x8f, 0xe2, 0x5f, 0xdd, 0x51, 0xef, 0x94}
+  gUefiOvmfPkgTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x80, 0x6d, 0x91, 0x7d, 0xb1, 0x5b, 0x8c, 0x45, 0xa4, 0x8f, 0xe2, 0x5f, 0xdd, 0x51, 0xef, 0x94}
 !else
   gEfiMdePkgTokenSpaceGuid.PcdDefaultTerminalType|1
 !endif
diff --git a/ArmVirtPkg/ArmVirtQemuKernel.dsc b/ArmVirtPkg/ArmVirtQemuKernel.dsc
index 6a6ecfc12a..c22a422353 100644
--- a/ArmVirtPkg/ArmVirtQemuKernel.dsc
+++ b/ArmVirtPkg/ArmVirtQemuKernel.dsc
@@ -147,7 +147,7 @@
 !if $(TTY_TERMINAL) == TRUE
   gEfiMdePkgTokenSpaceGuid.PcdDefaultTerminalType|4
   # Set terminal type to TtyTerm, the value encoded is EFI_TTY_TERM_GUID
-  gArmVirtTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x80, 0x6d, 0x91, 0x7d, 0xb1, 0x5b, 0x8c, 0x45, 0xa4, 0x8f, 0xe2, 0x5f, 0xdd, 0x51, 0xef, 0x94}
+  gUefiOvmfPkgTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x80, 0x6d, 0x91, 0x7d, 0xb1, 0x5b, 0x8c, 0x45, 0xa4, 0x8f, 0xe2, 0x5f, 0xdd, 0x51, 0xef, 0x94}
 !else
   gEfiMdePkgTokenSpaceGuid.PcdDefaultTerminalType|1
 !endif
diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
index 70e4ebf94a..8e7cd5605f 100644
--- a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
+++ b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
@@ -29,7 +29,6 @@
   QemuKernel.c
 
 [Packages]
-  ArmVirtPkg/ArmVirtPkg.dec
   MdeModulePkg/MdeModulePkg.dec
   MdePkg/MdePkg.dec
   OvmfPkg/OvmfPkg.dec
@@ -56,15 +55,15 @@
   UefiRuntimeServicesTableLib
 
 [FixedPcd]
-  gArmVirtTokenSpaceGuid.PcdUninstallMemAttrProtocol
   gEfiMdePkgTokenSpaceGuid.PcdUartDefaultBaudRate
   gEfiMdePkgTokenSpaceGuid.PcdUartDefaultDataBits
   gEfiMdePkgTokenSpaceGuid.PcdUartDefaultParity
   gEfiMdePkgTokenSpaceGuid.PcdUartDefaultStopBits
+  gUefiOvmfPkgTokenSpaceGuid.PcdUninstallMemAttrProtocol
 
 [Pcd]
-  gArmVirtTokenSpaceGuid.PcdTerminalTypeGuidBuffer
   gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut
+  gUefiOvmfPkgTokenSpaceGuid.PcdTerminalTypeGuidBuffer
 
 [Guids]
   gEfiEndOfDxeEventGroupGuid
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 13e69e6648..fbc81e4c80 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -405,6 +405,19 @@
   #
   gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeAllocationPadding|256|UINT32|0x6f
 
+  #
+  # Binary representation of the GUID that determines the terminal type. The
+  # size must be exactly 16 bytes. The default value corresponds to
+  # EFI_VT_100_GUID.
+  #
+  gUefiOvmfPkgTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x65, 0x60, 0xA6, 0xDF, 0x19, 0xB4, 0xD3, 0x11, 0x9A, 0x2D, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D}|VOID*|0x66
+
+  ##
+  # Whether the EFI memory attributes protocol should be uninstalled before
+  # invoking the OS loader. This may be needed to work around problematic
+  # builds of shim that use the protocol incorrectly.
+  gUefiOvmfPkgTokenSpaceGuid.PcdUninstallMemAttrProtocol|FALSE|BOOLEAN|0x67
+
 [PcdsFeatureFlag]
   gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderPciTranslation|TRUE|BOOLEAN|0x1c
   gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderMmioTranslation|FALSE|BOOLEAN|0x1d
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114550): https://edk2.groups.io/g/devel/message/114550
Mute This Topic: https://groups.io/mt/103971670/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 25/37] ArmVirtPkg: Move PlatformBootManagerLib to OvmfPkg
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (23 preceding siblings ...)
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 24/37] ArmVirtPkg: Move two PCD variables into OvmfPkg Chao Li
@ 2024-01-26  6:30 ` Chao Li
  2024-01-29 19:51   ` Laszlo Ersek
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 26/37] OvmfPkg/LoongArchVirt: Add stable timer driver Chao Li
                   ` (16 subsequent siblings)
  41 siblings, 1 reply; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:30 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Laszlo Ersek, Leif Lindholm, Sami Mujawar,
	Gerd Hoffmann, Jiewen Yao

Moved the PlatformBootManagerLib to OvmfPkg and renamed to
PlatformBootManagerLibLight for easy use by other ARCH.

Build-tested only (with "ArmVirtQemu.dsc and OvmfPkgX64.dsc").

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Leif Lindholm <quic_llindhol@quicinc.com>
Cc: Sami Mujawar <sami.mujawar@arm.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Lazlo Ersek <lersek@redhat.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
---
 ArmVirtPkg/ArmVirtPkg.ci.yaml                                   | 1 -
 ArmVirtPkg/ArmVirtQemu.dsc                                      | 2 +-
 ArmVirtPkg/ArmVirtQemuKernel.dsc                                | 2 +-
 .../Library/PlatformBootManagerLibLight}/PlatformBm.c           | 0
 .../Library/PlatformBootManagerLibLight}/PlatformBm.h           | 0
 .../PlatformBootManagerLibLight}/PlatformBootManagerLib.inf     | 2 +-
 .../Library/PlatformBootManagerLibLight}/QemuKernel.c           | 0
 7 files changed, 3 insertions(+), 4 deletions(-)
 rename {ArmVirtPkg/Library/PlatformBootManagerLib => OvmfPkg/Library/PlatformBootManagerLibLight}/PlatformBm.c (100%)
 rename {ArmVirtPkg/Library/PlatformBootManagerLib => OvmfPkg/Library/PlatformBootManagerLibLight}/PlatformBm.h (100%)
 rename {ArmVirtPkg/Library/PlatformBootManagerLib => OvmfPkg/Library/PlatformBootManagerLibLight}/PlatformBootManagerLib.inf (93%)
 rename {ArmVirtPkg/Library/PlatformBootManagerLib => OvmfPkg/Library/PlatformBootManagerLibLight}/QemuKernel.c (100%)

diff --git a/ArmVirtPkg/ArmVirtPkg.ci.yaml b/ArmVirtPkg/ArmVirtPkg.ci.yaml
index 506b0e72f0..b186d4eb42 100644
--- a/ArmVirtPkg/ArmVirtPkg.ci.yaml
+++ b/ArmVirtPkg/ArmVirtPkg.ci.yaml
@@ -24,7 +24,6 @@
         ],
         ## Both file path and directory path are accepted.
         "IgnoreFiles": [
-            "Library/PlatformBootManagerLib/PlatformBm.c"
         ]
     },
     ## options defined .pytool/Plugin/CompilerPlugin
diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc
index 147180f645..e48c75b5e9 100644
--- a/ArmVirtPkg/ArmVirtQemu.dsc
+++ b/ArmVirtPkg/ArmVirtQemu.dsc
@@ -70,7 +70,7 @@
 
   CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf
   BootLogoLib|MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf
-  PlatformBootManagerLib|ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
+  PlatformBootManagerLib|OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBootManagerLib.inf
   PlatformBmPrintScLib|OvmfPkg/Library/PlatformBmPrintScLib/PlatformBmPrintScLib.inf
   CustomizedDisplayLib|MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf
   FrameBufferBltLib|MdeModulePkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
diff --git a/ArmVirtPkg/ArmVirtQemuKernel.dsc b/ArmVirtPkg/ArmVirtQemuKernel.dsc
index c22a422353..668a65ba64 100644
--- a/ArmVirtPkg/ArmVirtQemuKernel.dsc
+++ b/ArmVirtPkg/ArmVirtQemuKernel.dsc
@@ -69,7 +69,7 @@
 
   CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf
   BootLogoLib|MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf
-  PlatformBootManagerLib|ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
+  PlatformBootManagerLib|OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBootManagerLib.inf
   PlatformBmPrintScLib|OvmfPkg/Library/PlatformBmPrintScLib/PlatformBmPrintScLib.inf
   CustomizedDisplayLib|MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf
   FrameBufferBltLib|MdeModulePkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.c b/OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBm.c
similarity index 100%
rename from ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.c
rename to OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBm.c
diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.h b/OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBm.h
similarity index 100%
rename from ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.h
rename to OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBm.h
diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBootManagerLib.inf
similarity index 93%
rename from ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
rename to OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBootManagerLib.inf
index 8e7cd5605f..f2fb69bd3c 100644
--- a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
+++ b/OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBootManagerLib.inf
@@ -20,7 +20,7 @@
 #
 # The following information is for reference only and not required by the build tools.
 #
-#  VALID_ARCHITECTURES           = ARM AARCH64
+#  VALID_ARCHITECTURES           = ARM AARCH64 LOONGARCH64
 #
 
 [Sources]
diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c b/OvmfPkg/Library/PlatformBootManagerLibLight/QemuKernel.c
similarity index 100%
rename from ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c
rename to OvmfPkg/Library/PlatformBootManagerLibLight/QemuKernel.c
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114551): https://edk2.groups.io/g/devel/message/114551
Mute This Topic: https://groups.io/mt/103971673/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 26/37] OvmfPkg/LoongArchVirt: Add stable timer driver
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (24 preceding siblings ...)
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 25/37] ArmVirtPkg: Move PlatformBootManagerLib to OvmfPkg Chao Li
@ 2024-01-26  6:30 ` Chao Li
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 27/37] OvmfPkg/LoongArchVirt: Add a NULL library named CollectApResouceLibNull Chao Li
                   ` (15 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:30 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Laszlo Ersek, Jiewen Yao, Jordan Justen,
	Gerd Hoffmann, Bibo Mao, Dongyan Qian, Baoqi Zhang

Add a CPU timer driver named StableTimerDxe, which proviedes
EFI_TIMER_ARCH_PROTOCOL for LoongArch.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Bibo Mao <maobibo@loongson.cn>
Cc: Dongyan Qian <qiandongyan@loongson.cn>
Signed-off-by: Chao Li <lichao@loongson.cn>
Co-authored-by: Baoqi Zhang <zhangbaoqi@loongson.cn>
Reviewed-by: Bibo Mao <maobibo@loongson.cn>
---
 .../Drivers/StableTimerDxe/Timer.c            | 381 ++++++++++++++++++
 .../Drivers/StableTimerDxe/Timer.h            | 127 ++++++
 .../Drivers/StableTimerDxe/TimerDxe.inf       |  41 ++
 3 files changed, 549 insertions(+)
 create mode 100644 OvmfPkg/LoongArchVirt/Drivers/StableTimerDxe/Timer.c
 create mode 100644 OvmfPkg/LoongArchVirt/Drivers/StableTimerDxe/Timer.h
 create mode 100644 OvmfPkg/LoongArchVirt/Drivers/StableTimerDxe/TimerDxe.inf

diff --git a/OvmfPkg/LoongArchVirt/Drivers/StableTimerDxe/Timer.c b/OvmfPkg/LoongArchVirt/Drivers/StableTimerDxe/Timer.c
new file mode 100644
index 0000000000..0e0f10970a
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Drivers/StableTimerDxe/Timer.c
@@ -0,0 +1,381 @@
+/** @file
+  Timer Architectural Protocol as defined in the DXE CIS
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Protocol/Cpu.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/TimerLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Register/LoongArch64/Csr.h>
+#include "Timer.h"
+
+//
+// The handle onto which the Timer Architectural Protocol will be installed
+//
+EFI_HANDLE  mTimerHandle             = NULL;
+EFI_EVENT   EfiExitBootServicesEvent = (EFI_EVENT)NULL;
+
+//
+// The Timer Architectural Protocol that this driver produces
+//
+EFI_TIMER_ARCH_PROTOCOL  mTimer = {
+  TimerDriverRegisterHandler,
+  TimerDriverSetTimerPeriod,
+  TimerDriverGetTimerPeriod,
+  TimerDriverGenerateSoftInterrupt
+};
+
+//
+// Pointer to the CPU Architectural Protocol instance
+//
+EFI_CPU_ARCH_PROTOCOL  *mCpu;
+
+//
+// The notification function to call on every timer interrupt.
+// A bug in the compiler prevents us from initializing this here.
+//
+EFI_TIMER_NOTIFY  mTimerNotifyFunction;
+
+/**
+  Sets the counter value for timer.
+
+  @param Count    The 16-bit counter value to program into stable timer.
+
+  @retval VOID
+**/
+VOID
+SetPitCount (
+  IN UINT64  Count
+  )
+{
+  if (Count <= 4) {
+    return;
+  }
+
+  Count &= LOONGARCH_CSR_TMCFG_TIMEVAL;
+  Count |= LOONGARCH_CSR_TMCFG_EN | LOONGARCH_CSR_TMCFG_PERIOD;
+  CsrWrite (LOONGARCH_CSR_TMCFG, Count);
+}
+
+/**
+  Timer Interrupt Handler.
+
+  @param InterruptType    The type of interrupt that occurred
+  @param SystemContext    A pointer to the system context when the interrupt occurred
+
+  @retval VOID
+**/
+VOID
+EFIAPI
+TimerInterruptHandler (
+  IN EFI_EXCEPTION_TYPE  InterruptType,
+  IN EFI_SYSTEM_CONTEXT  SystemContext
+  )
+{
+  EFI_TPL  OriginalTPL;
+
+  OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
+
+  //
+  // Clear interrupt.
+  //
+  CsrWrite (LOONGARCH_CSR_TINTCLR, 0x1);
+
+  if (mTimerNotifyFunction != NULL) {
+    //
+    // @bug : This does not handle missed timer interrupts
+    //
+    mTimerNotifyFunction (mTimerPeriod);
+  }
+
+  gBS->RestoreTPL (OriginalTPL);
+}
+
+/**
+  This function registers the handler NotifyFunction so it is called every time
+  the timer interrupt fires.  It also passes the amount of time since the last
+  handler call to the NotifyFunction.  If NotifyFunction is NULL, then the
+  handler is unregistered.  If the handler is registered, then EFI_SUCCESS is
+  returned.  If the CPU does not support registering a timer interrupt handler,
+  then EFI_UNSUPPORTED is returned.  If an attempt is made to register a handler
+  when a handler is already registered, then EFI_ALREADY_STARTED is returned.
+  If an attempt is made to unregister a handler when a handler is not registered,
+  then EFI_INVALID_PARAMETER is returned.  If an error occurs attempting to
+  register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR
+  is returned.
+
+  @param This             The EFI_TIMER_ARCH_PROTOCOL instance.
+  @param NotifyFunction   The function to call when a timer interrupt fires.  This
+                          function executes at TPL_HIGH_LEVEL.  The DXE Core will
+                          register a handler for the timer interrupt, so it can know
+                          how much time has passed.  This information is used to
+                          signal timer based events.  NULL will unregister the handler.
+
+  @retval        EFI_SUCCESS            The timer handler was registered.
+  @retval        EFI_UNSUPPORTED        The platform does not support timer interrupts.
+  @retval        EFI_ALREADY_STARTED    NotifyFunction is not NULL, and a handler is already
+                                        registered.
+  @retval        EFI_INVALID_PARAMETER  NotifyFunction is NULL, and a handler was not
+                                        previously registered.
+  @retval        EFI_DEVICE_ERROR       The timer handler could not be registered.
+**/
+EFI_STATUS
+EFIAPI
+TimerDriverRegisterHandler (
+  IN EFI_TIMER_ARCH_PROTOCOL  *This,
+  IN EFI_TIMER_NOTIFY         NotifyFunction
+  )
+{
+  //
+  // Check for invalid parameters
+  //
+  if ((NotifyFunction == NULL) && (mTimerNotifyFunction == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((NotifyFunction != NULL) && (mTimerNotifyFunction != NULL)) {
+    return EFI_ALREADY_STARTED;
+  }
+
+  mTimerNotifyFunction = NotifyFunction;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function adjusts the period of timer interrupts to the value specified
+  by TimerPeriod.  If the timer period is updated, then the selected timer
+  period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned.  If
+  the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.
+  If an error occurs while attempting to update the timer period, then the
+  timer hardware will be put back in its state prior to this call, and
+  EFI_DEVICE_ERROR is returned.  If TimerPeriod is 0, then the timer interrupt
+  is disabled.  This is not the same as disabling the CPU's interrupts.
+  Instead, it must either turn off the timer hardware, or it must adjust the
+  interrupt controller so that a CPU interrupt is not generated when the timer
+  interrupt fires.
+
+  @param This            The EFI_TIMER_ARCH_PROTOCOL instance.
+  @param TimerPeriod     The rate to program the timer interrupt in 100 nS units.  If
+                         the timer hardware is not programmable, then EFI_UNSUPPORTED is
+                         returned.  If the timer is programmable, then the timer period
+                         will be rounded up to the nearest timer period that is supported
+                         by the timer hardware.  If TimerPeriod is set to 0, then the
+                         timer interrupts will be disabled.
+
+  @retval        EFI_SUCCESS       The timer period was changed.
+  @retval        EFI_UNSUPPORTED   The platform cannot change the period of the timer interrupt.
+  @retval        EFI_DEVICE_ERROR  The timer period could not be changed due to a device error.
+**/
+EFI_STATUS
+EFIAPI
+TimerDriverSetTimerPeriod (
+  IN EFI_TIMER_ARCH_PROTOCOL  *This,
+  IN UINT64                   TimerPeriod
+  )
+{
+  UINT64  TimerCount;
+
+  if (TimerPeriod == 0) {
+    //
+    // Disable timer interrupt for a TimerPeriod of 0
+    //
+    mCpu->DisableInterrupt (mCpu);
+  } else {
+    TimerCount = TimerPeriod * GetPerformanceCounterProperties (NULL, NULL) / 10000000ULL;
+
+    if (TimerCount >= BIT48) {
+      TimerCount = 0;
+    }
+
+    //
+    // Program the stable timer with the new count value
+    //
+    mTimerTicks = TimerCount;
+    SetPitCount (TimerCount);
+
+    //
+    // Enable timer interrupt
+    //
+    mCpu->EnableInterrupt (mCpu);
+  }
+
+  //
+  // Save the new timer period
+  //
+  mTimerPeriod = TimerPeriod;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function retrieves the period of timer interrupts in 100 ns units,
+  returns that value in TimerPeriod, and returns EFI_SUCCESS.  If TimerPeriod
+  is NULL, then EFI_INVALID_PARAMETER is returned.  If a TimerPeriod of 0 is
+  returned, then the timer is currently disabled.
+
+  @param This            The EFI_TIMER_ARCH_PROTOCOL instance.
+  @param TimerPeriod     A pointer to the timer period to retrieve in 100 ns units.  If
+                         0 is returned, then the timer is currently disabled.
+
+  @retval EFI_SUCCESS            The timer period was returned in TimerPeriod.
+  @retval EFI_INVALID_PARAMETER  TimerPeriod is NULL.
+**/
+EFI_STATUS
+EFIAPI
+TimerDriverGetTimerPeriod (
+  IN EFI_TIMER_ARCH_PROTOCOL  *This,
+  OUT UINT64                  *TimerPeriod
+  )
+{
+  if (TimerPeriod == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *TimerPeriod = mTimerPeriod;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Disable the timer
+  DXE Core will disable the timer after all the event handlers have run.
+
+  @param[in]  Event   The Event that is being processed
+  @param[in]  Context Event Context
+**/
+VOID
+EFIAPI
+ExitBootServicesEvent (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+{
+  /*
+   * Disable timer interrupt when exiting boot service
+   */
+  CsrWrite (LOONGARCH_CSR_TMCFG, 0x0);
+}
+
+/**
+  This function generates a soft timer interrupt. If the platform does not support soft
+  timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.
+  If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler ()
+  service, then a soft timer interrupt will be generated. If the timer interrupt is
+  enabled when this service is called, then the registered handler will be invoked. The
+  registered handler should not be able to distinguish a hardware-generated timer
+  interrupt from a software-generated timer interrupt.
+
+  @param This              The EFI_TIMER_ARCH_PROTOCOL instance.
+
+  @retval EFI_SUCCESS       The soft timer interrupt was generated.
+  @retval EFI_UNSUPPORTED   The platform does not support the generation of soft timer interrupts.
+**/
+EFI_STATUS
+EFIAPI
+TimerDriverGenerateSoftInterrupt (
+  IN EFI_TIMER_ARCH_PROTOCOL  *This
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Initialize the Timer Architectural Protocol driver
+
+  @param ImageHandle     ImageHandle of the loaded driver
+  @param SystemTable     Pointer to the System Table
+
+  @retval EFI_SUCCESS            Timer Architectural Protocol created
+  @retval EFI_OUT_OF_RESOURCES   Not enough resources available to initialize driver.
+  @retval EFI_DEVICE_ERROR       A device error occurred attempting to initialize the driver.
+**/
+EFI_STATUS
+EFIAPI
+StableTimerDriverInitialize (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      TimerVector;
+
+  //
+  // Initialize the pointer to our notify function.
+  //
+  mTimerNotifyFunction = NULL;
+
+  //
+  // Make sure the Timer Architectural Protocol is not already installed in the system
+  //
+  ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiTimerArchProtocolGuid);
+
+  //
+  // Find the CPU architectural protocol.
+  //
+  Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&mCpu);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Force the timer to be disabled
+  //
+  Status = TimerDriverSetTimerPeriod (&mTimer, 0);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Calculate const frequence
+  //
+  DEBUG ((
+    DEBUG_INFO,
+    "===========Stable timer freq %d Hz=============\n",
+    GetPerformanceCounterProperties (NULL, NULL)
+    ));
+
+  //
+  // Install interrupt handler for Stable Timer #0 (ISA IRQ0)
+  //
+  TimerVector = EXCEPT_LOONGARCH_INT_TIMER;
+  Status      = mCpu->RegisterInterruptHandler (mCpu, TimerVector, TimerInterruptHandler);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Enable TI local timer interrupt
+  //
+  EnableLocalInterrupts (1 << EXCEPT_LOONGARCH_INT_TIMER);
+
+  //
+  // Force the timer to be enabled at its default period
+  //
+  Status = TimerDriverSetTimerPeriod (&mTimer, DEFAULT_TIMER_TICK_DURATION);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Install the Timer Architectural Protocol onto a new handle
+  //
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &mTimerHandle,
+                  &gEfiTimerArchProtocolGuid,
+                  &mTimer,
+                  NULL
+                  );
+
+  ASSERT_EFI_ERROR (Status);
+
+  // Register for an ExitBootServicesEvent
+  Status = gBS->CreateEvent (
+                  EVT_SIGNAL_EXIT_BOOT_SERVICES,
+                  TPL_NOTIFY,
+                  ExitBootServicesEvent,
+                  NULL,
+                  &EfiExitBootServicesEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/OvmfPkg/LoongArchVirt/Drivers/StableTimerDxe/Timer.h b/OvmfPkg/LoongArchVirt/Drivers/StableTimerDxe/Timer.h
new file mode 100644
index 0000000000..5efccdfa44
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Drivers/StableTimerDxe/Timer.h
@@ -0,0 +1,127 @@
+/** @file
+  Private data structures
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef TIMER_H_
+#define TIMER_H_
+
+#include <Protocol/Timer.h>
+
+#define DEFAULT_TIMER_TICK_DURATION  100000 // 10ms = 100000 100 ns units
+
+//
+// The current period of the timer interrupt
+//
+volatile UINT64  mTimerPeriod = 0;
+volatile UINT64  mTimerTicks  = 0;
+
+/**
+  This function adjusts the period of timer interrupts to the value specified
+  by TimerPeriod.  If the timer period is updated, then the selected timer
+  period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned.  If
+  the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.
+  If an error occurs while attempting to update the timer period, then the
+  timer hardware will be put back in its state prior to this call, and
+  EFI_DEVICE_ERROR is returned.  If TimerPeriod is 0, then the timer interrupt
+  is disabled.  This is not the same as disabling the CPU's interrupts.
+  Instead, it must either turn off the timer hardware, or it must adjust the
+  interrupt controller so that a CPU interrupt is not generated when the timer
+  interrupt fires.
+
+  @param This            The EFI_TIMER_ARCH_PROTOCOL instance.
+  @param NotifyFunction  The rate to program the timer interrupt in 100 nS units.  If
+                         the timer hardware is not programmable, then EFI_UNSUPPORTED is
+                         returned.  If the timer is programmable, then the timer period
+                         will be rounded up to the nearest timer period that is supported
+                         by the timer hardware.  If TimerPeriod is set to 0, then the
+                         timer interrupts will be disabled.
+
+  @retval        EFI_SUCCESS       The timer period was changed.
+  @retval        EFI_UNSUPPORTED   The platform cannot change the period of the timer interrupt.
+  @retval        EFI_DEVICE_ERROR  The timer period could not be changed due to a device error.
+**/
+EFI_STATUS
+EFIAPI
+TimerDriverRegisterHandler (
+  IN EFI_TIMER_ARCH_PROTOCOL  *This,
+  IN EFI_TIMER_NOTIFY         NotifyFunction
+  );
+
+/**
+  This function adjusts the period of timer interrupts to the value specified
+  by TimerPeriod.  If the timer period is updated, then the selected timer
+  period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned.  If
+  the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.
+  If an error occurs while attempting to update the timer period, then the
+  timer hardware will be put back in its state prior to this call, and
+  EFI_DEVICE_ERROR is returned.  If TimerPeriod is 0, then the timer interrupt
+  is disabled.  This is not the same as disabling the CPU's interrupts.
+  Instead, it must either turn off the timer hardware, or it must adjust the
+  interrupt controller so that a CPU interrupt is not generated when the timer
+  interrupt fires.
+
+  @param This            The EFI_TIMER_ARCH_PROTOCOL instance.
+  @param TimerPeriod     The rate to program the timer interrupt in 100 nS units.  If
+                         the timer hardware is not programmable, then EFI_UNSUPPORTED is
+                         returned.  If the timer is programmable, then the timer period
+                         will be rounded up to the nearest timer period that is supported
+                         by the timer hardware.  If TimerPeriod is set to 0, then the
+                         timer interrupts will be disabled.
+
+  @retval        EFI_SUCCESS       The timer period was changed.
+  @retval        EFI_UNSUPPORTED   The platform cannot change the period of the timer interrupt.
+  @retval        EFI_DEVICE_ERROR  The timer period could not be changed due to a device error.
+**/
+EFI_STATUS
+EFIAPI
+TimerDriverSetTimerPeriod (
+  IN EFI_TIMER_ARCH_PROTOCOL  *This,
+  IN UINT64                   TimerPeriod
+  );
+
+/**
+  This function retrieves the period of timer interrupts in 100 ns units,
+  returns that value in TimerPeriod, and returns EFI_SUCCESS.  If TimerPeriod
+  is NULL, then EFI_INVALID_PARAMETER is returned.  If a TimerPeriod of 0 is
+  returned, then the timer is currently disabled.
+
+  @param This            The EFI_TIMER_ARCH_PROTOCOL instance.
+  @param TimerPeriod     A pointer to the timer period to retrieve in 100 ns units.  If
+                         0 is returned, then the timer is currently disabled.
+
+  @retval EFI_SUCCESS            The timer period was returned in TimerPeriod.
+  @retval EFI_INVALID_PARAMETER  TimerPeriod is NULL.
+**/
+EFI_STATUS
+EFIAPI
+TimerDriverGetTimerPeriod (
+  IN EFI_TIMER_ARCH_PROTOCOL  *This,
+  OUT UINT64                  *TimerPeriod
+  );
+
+/**
+  This function generates a soft timer interrupt. If the platform does not support soft
+  timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.
+  If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler()
+  service, then a soft timer interrupt will be generated. If the timer interrupt is
+  enabled when this service is called, then the registered handler will be invoked. The
+  registered handler should not be able to distinguish a hardware-generated timer
+  interrupt from a software-generated timer interrupt.
+
+  @param This              The EFI_TIMER_ARCH_PROTOCOL instance.
+
+  @retval EFI_SUCCESS       The soft timer interrupt was generated.
+  @retval EFI_UNSUPPORTED   The platform does not support the generation of soft timer interrupts.
+**/
+EFI_STATUS
+EFIAPI
+TimerDriverGenerateSoftInterrupt (
+  IN EFI_TIMER_ARCH_PROTOCOL  *This
+  );
+
+#endif // TIMER_H_
diff --git a/OvmfPkg/LoongArchVirt/Drivers/StableTimerDxe/TimerDxe.inf b/OvmfPkg/LoongArchVirt/Drivers/StableTimerDxe/TimerDxe.inf
new file mode 100644
index 0000000000..7548a1df46
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Drivers/StableTimerDxe/TimerDxe.inf
@@ -0,0 +1,41 @@
+## @file
+#  Stable timer driver that provides Timer Arch protocol.
+#
+#  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                      = Timer
+  MODULE_UNI_FILE                = Timer.uni
+  FILE_GUID                      = AEBE2648-47A9-40FA-83FD-06AA88443BB2
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = StableTimerDriverInitialize
+
+#
+#  VALID_ARCHITECTURES           = LOONGARCH64
+#
+
+[Sources]
+  Timer.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  BaseLib
+  DebugLib
+  IoLib
+  TimerLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+
+[Protocols]
+  gEfiCpuArchProtocolGuid       ## CONSUMES
+  gEfiTimerArchProtocolGuid     ## PRODUCES
+
+[depex]
+  gEfiCpuArchProtocolGuid
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114552): https://edk2.groups.io/g/devel/message/114552
Mute This Topic: https://groups.io/mt/103971676/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 27/37] OvmfPkg/LoongArchVirt: Add a NULL library named CollectApResouceLibNull
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (25 preceding siblings ...)
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 26/37] OvmfPkg/LoongArchVirt: Add stable timer driver Chao Li
@ 2024-01-26  6:30 ` Chao Li
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 28/37] OvmfPkg/LoongArchVirt: Add serial port hook library Chao Li
                   ` (14 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:30 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Laszlo Ersek, Jiewen Yao, Jordan Justen,
	Gerd Hoffmann, Bibo Mao, Dongyan Qian

This Library is used to collect APs resources, but is currently NULL
for OvmfPkg, because it is not used by the LoongArch virtual machine.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Bibo Mao <maobibo@loongson.cn>
Cc: Dongyan Qian <qiandongyan@loongson.cn>
Signed-off-by: Chao Li <lichao@loongson.cn>
Reviewed-by: Bibo Mao <maobibo@loongson.cn>
---
 .../CollectApResourceLibNull.c                | 38 +++++++++++++++++++
 .../CollectApResourceLibNull.inf              | 31 +++++++++++++++
 .../CollectApResourceLibNull.uni              |  9 +++++
 3 files changed, 78 insertions(+)
 create mode 100644 OvmfPkg/LoongArchVirt/Library/CollectApResouceLibNull/CollectApResourceLibNull.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/CollectApResouceLibNull/CollectApResourceLibNull.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/CollectApResouceLibNull/CollectApResourceLibNull.uni

diff --git a/OvmfPkg/LoongArchVirt/Library/CollectApResouceLibNull/CollectApResourceLibNull.c b/OvmfPkg/LoongArchVirt/Library/CollectApResouceLibNull/CollectApResourceLibNull.c
new file mode 100644
index 0000000000..471418f11e
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/CollectApResouceLibNull/CollectApResourceLibNull.c
@@ -0,0 +1,38 @@
+/** @file
+  LoongArch64 CPU Collect AP resource NULL Library functions.
+
+  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiPei.h>
+#include <Library/BaseLib.h>
+#include <Library/HobLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Register/LoongArch64/Csr.h>
+#include "../../../UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.h"
+
+VOID
+SaveProcessorResourceData (
+  IN PROCESSOR_RESOURCE_DATA *
+  );
+
+VOID
+EFIAPI
+SaveProcessorResource (
+  PROCESSOR_RESOURCE_DATA  *mProcessorResource
+  )
+{
+  SaveProcessorResourceData (mProcessorResource);
+}
+
+VOID
+EFIAPI
+CollectAllProcessorResource (
+  VOID
+  )
+{
+  return;
+}
diff --git a/OvmfPkg/LoongArchVirt/Library/CollectApResouceLibNull/CollectApResourceLibNull.inf b/OvmfPkg/LoongArchVirt/Library/CollectApResouceLibNull/CollectApResourceLibNull.inf
new file mode 100644
index 0000000000..c166df6bbd
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/CollectApResouceLibNull/CollectApResourceLibNull.inf
@@ -0,0 +1,31 @@
+## @file
+#  LoongArch64 CPU Collect AP resource NULL Library.
+#
+#  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                      = CollectApResourceLibNull
+  MODULE_UNI_FILE                = CollectApResourceLibNull.uni
+  FILE_GUID                      = 8C3B54BF-6A9F-E8B4-4D57-67B3AB578DD6
+  MODULE_TYPE                    = PEIM
+  VERSION_STRING                 = 1.1
+  LIBRARY_CLASS                  = PEIM
+
+[Sources.common]
+  CollectApResourceLibNull.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  HobLib
+  MemoryAllocationLib
+
+[Pcd]
+  gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber
diff --git a/OvmfPkg/LoongArchVirt/Library/CollectApResouceLibNull/CollectApResourceLibNull.uni b/OvmfPkg/LoongArchVirt/Library/CollectApResouceLibNull/CollectApResourceLibNull.uni
new file mode 100644
index 0000000000..d1638ab11e
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/CollectApResouceLibNull/CollectApResourceLibNull.uni
@@ -0,0 +1,9 @@
+// @file
+//  LoongArch64 CPU Collect AP resource NULL Library.
+//
+//  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 "CPU Collect AP resource NULL Library."
+
+#string STR_MODULE_DESCRIPTION          #language en-US "CPU Collect AP resource NULL Library."
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114553): https://edk2.groups.io/g/devel/message/114553
Mute This Topic: https://groups.io/mt/103971677/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 28/37] OvmfPkg/LoongArchVirt: Add serial port hook library
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (26 preceding siblings ...)
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 27/37] OvmfPkg/LoongArchVirt: Add a NULL library named CollectApResouceLibNull Chao Li
@ 2024-01-26  6:30 ` Chao Li
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 29/37] OvmfPkg/LoongArchVirt: Add the early serial port output library Chao Li
                   ` (13 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:30 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Laszlo Ersek, Jiewen Yao, Jordan Justen,
	Gerd Hoffmann, Bibo Mao, Dongyan Qian

Add a serial port hook library in LoongArchVirt named
Fdt16550SerialProtHookLib, this library is referenced from ArmVirtPkg.

LoongArch QEMU virtual machine uses register of LOONGARCH_CSR_KS1 to
transfer serial port base addres from the PEI phase to the DXE phase.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Bibo Mao <maobibo@loongson.cn>
Cc: Dongyan Qian <qiandongyan@loongson.cn>
Signed-off-by: Chao Li <lichao@loongson.cn>
Reviewed-by: Bibo Mao <maobibo@loongson.cn>
---
 .../EarlyFdt16550SerialPortHookLib.c          | 52 +++++++++++++++++++
 .../EarlyFdt16550SerialPortHookLib.inf        | 37 +++++++++++++
 .../Fdt16550SerialPortHookLib.c               | 39 ++++++++++++++
 .../Fdt16550SerialPortHookLib.inf             | 33 ++++++++++++
 .../Fdt16550SerialPortHookLib.uni             | 14 +++++
 5 files changed, 175 insertions(+)
 create mode 100644 OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.uni

diff --git a/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.c b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.c
new file mode 100644
index 0000000000..9f1fcc970a
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.c
@@ -0,0 +1,52 @@
+/** @file
+  PEI Phase Early Platform Hook Library instance for 16550 Uart.
+
+  Copyright (c) 2020 - 2023, Arm Ltd. All rights reserved.<BR>
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/FdtSerialPortAddressLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PlatformHookLib.h>
+#include <Register/LoongArch64/Csr.h>
+
+/** Platform hook to retrieve the 16550 UART base address from the platform
+    Device tree and store it in the reigster LOONGARCH_CSR_KS1.
+
+  @retval RETURN_SUCCESS            Success.
+  @retval RETURN_INVALID_PARAMETER  A parameter was invalid.
+  @retval RETURN_NOT_FOUND          Serial port information not found.
+
+**/
+RETURN_STATUS
+EFIAPI
+PlatformHookSerialPortInitialize (
+  VOID
+  )
+{
+  RETURN_STATUS  Status;
+  VOID           *DeviceTreeBase;
+  UINT64         SerialConsoleAddress;
+
+  if (PcdGet64 (PcdSerialRegisterBase) != 0) {
+    return RETURN_SUCCESS;
+  }
+
+  DeviceTreeBase = (VOID *)(UINTN)PcdGet64 (PcdDeviceTreeInitialBaseAddress);
+  if (DeviceTreeBase == NULL) {
+    return RETURN_NOT_FOUND;
+  }
+
+  Status = FdtSerialGetConsolePort (DeviceTreeBase, &SerialConsoleAddress);
+  if (RETURN_ERROR (Status)) {
+    return Status;
+  }
+
+  CsrWrite (LOONGARCH_CSR_KS1, (UINTN)SerialConsoleAddress);
+
+  return RETURN_SUCCESS;
+}
diff --git a/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.inf b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.inf
new file mode 100644
index 0000000000..55b0c03a01
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.inf
@@ -0,0 +1,37 @@
+## @file
+#  PEI Phase Early Platform Hook Library instance for 16550 Uart.
+#
+#  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
+#  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                      = EarlyFdt16550SerialPortHookLib
+  MODULE_UNI_FILE                = Fdt16550SerialPortHookLib.uni
+  FILE_GUID                      = 6A5FEBCB-C676-A7C1-A96C-B79D4860EEC5
+  MODULE_TYPE                    = PEIM
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = PlatformHookLib|SEC PEI_CORE PEIM
+
+[Sources]
+  EarlyFdt16550SerialPortHookLib.c
+
+[LibraryClasses]
+  BaseLib
+  PcdLib
+  FdtLib
+  FdtSerialPortAddressLib
+
+[Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[Pcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterBase
diff --git a/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.c b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.c
new file mode 100644
index 0000000000..fd188f75b8
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.c
@@ -0,0 +1,39 @@
+/** @file
+  Platform Hook Library instance for 16550 Uart.
+
+  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PlatformHookLib.h>
+#include <Register/LoongArch64/Csr.h>
+
+/** Platform hook to retrieve the 16550 UART base address from register
+    LOONGARCH_CSR_KS1 that caches the UART base address from early boot
+    stage and store it in  PcdSerialRegisterBase.
+
+  @retval RETURN_SUCCESS    Success.
+  @retval RETURN_NOT_FOUND  Serial Port information not found.
+
+**/
+RETURN_STATUS
+EFIAPI
+PlatformHookSerialPortInitialize (
+  VOID
+  )
+{
+  UINT64  *UartBase;
+
+  if (PcdGet64 (PcdSerialRegisterBase) != 0) {
+    return RETURN_SUCCESS;
+  }
+
+  *UartBase = CsrRead (LOONGARCH_CSR_KS1);
+
+  return (RETURN_STATUS)PcdSet64S (PcdSerialRegisterBase, (UINTN)*UartBase);
+}
diff --git a/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.inf b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.inf
new file mode 100644
index 0000000000..b5fc03a284
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.inf
@@ -0,0 +1,33 @@
+## @file
+#  Platform Hook Library instance for 16550 Uart.
+#
+#  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
+#  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                      = Fdt16550SerialPortHookLib
+  MODULE_UNI_FILE                = Fdt16550SerialPortHookLib.uni
+  FILE_GUID                      = 808335DB-220E-A353-887C-9AA1B7D433A1
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = PlatformHookLib|DXE_CORE DXE_DRIVER UEFI_DRIVER DXE_RUNTIME_DRIVER UEFI_APPLICATION
+  CONSTRUCTOR                    = PlatformHookSerialPortInitialize
+
+[Sources]
+  Fdt16550SerialPortHookLib.c
+
+[LibraryClasses]
+  BaseLib
+  PcdLib
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterBase
diff --git a/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.uni b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.uni
new file mode 100644
index 0000000000..92eb2ed0b4
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.uni
@@ -0,0 +1,14 @@
+// /** @file
+// Platform Hook Library instance for 16550 Uart.
+//
+//
+// Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
+// 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 "Platform Hook Library instance for 16550 Uart."
+
+#string STR_MODULE_DESCRIPTION  #language en-US "Platform Hook Library instance for 16550 Uart."
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114554): https://edk2.groups.io/g/devel/message/114554
Mute This Topic: https://groups.io/mt/103971678/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 29/37] OvmfPkg/LoongArchVirt: Add the early serial port output library
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (27 preceding siblings ...)
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 28/37] OvmfPkg/LoongArchVirt: Add serial port hook library Chao Li
@ 2024-01-26  6:30 ` Chao Li
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 30/37] OvmfPkg/LoongArchVirt: Add real time clock library Chao Li
                   ` (12 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:30 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Laszlo Ersek, Jiewen Yao, Jordan Justen,
	Gerd Hoffmann, Bibo Mao, Dongyan Qian, Xianglai Li

Add a early serial port output library into LoongArchVirt that named
EarlyFdtSerialPortLib16550, this library is referenced from
MdeModulePkg.

This library is used in the PEI phase. Since the serial port address can
not be saved in memory of the LoongArch QEMU virtual machine in the PEI
phase, the serial prot base address will be obtained from the FDT before
each output.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Bibo Mao <maobibo@loongson.cn>
Cc: Dongyan Qian <qiandongyan@loongson.cn>
Signed-off-by: Chao Li <lichao@loongson.cn>
Co-authored-by: Xianglai Li <lixianglai@loongson.cn>
Reviewed-by: Bibo Mao <maobibo@loongson.cn>
---
 .../EarlyFdtSerialPortLib16550.c              | 815 ++++++++++++++++++
 .../EarlyFdtSerialPortLib16550.inf            |  46 +
 2 files changed, 861 insertions(+)
 create mode 100644 OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.inf

diff --git a/OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.c b/OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.c
new file mode 100644
index 0000000000..8cc108501c
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.c
@@ -0,0 +1,815 @@
+/** @file
+  16550 UART Serial Port library functions
+
+  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Library/BaseLib.h>
+#include <Library/FdtSerialPortAddressLib.h>
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/SerialPortLib.h>
+
+//
+// PCI Defintions.
+//
+#define PCI_BRIDGE_32_BIT_IO_SPACE  0x01
+
+//
+// 16550 UART register offsets and bitfields
+//
+#define R_UART_RXBUF       0    // LCR_DLAB = 0
+#define R_UART_TXBUF       0    // LCR_DLAB = 0
+#define R_UART_BAUD_LOW    0    // LCR_DLAB = 1
+#define R_UART_BAUD_HIGH   1    // LCR_DLAB = 1
+#define R_UART_IER         1    // LCR_DLAB = 0
+#define R_UART_FCR         2
+#define B_UART_FCR_FIFOE   BIT0
+#define B_UART_FCR_FIFO64  BIT5
+#define R_UART_LCR         3
+#define B_UART_LCR_DLAB    BIT7
+#define R_UART_MCR         4
+#define B_UART_MCR_DTRC    BIT0
+#define B_UART_MCR_RTS     BIT1
+#define R_UART_LSR         5
+#define B_UART_LSR_RXRDY   BIT0
+#define B_UART_LSR_TXRDY   BIT5
+#define B_UART_LSR_TEMT    BIT6
+#define R_UART_MSR         6
+#define B_UART_MSR_CTS     BIT4
+#define B_UART_MSR_DSR     BIT5
+#define B_UART_MSR_RI      BIT6
+#define B_UART_MSR_DCD     BIT7
+
+/**
+  Read an 8-bit 16550 register.  If PcdSerialUseMmio is TRUE, then the value is read from
+  MMIO space.  If PcdSerialUseMmio is FALSE, then the value is read from I/O space.  The
+  parameter Offset is added to the base address of the 16550 registers that is specified
+  by PcdSerialRegisterBase. PcdSerialRegisterAccessWidth specifies the MMIO space access
+  width and defaults to 8 bit access, and supports 8 or 32 bit access.
+
+  @param  Base    The base address register of UART device.
+  @param  Offset  The offset of the 16550 register to read.
+
+  @return The value read from the 16550 register.
+**/
+UINT8
+SerialPortReadRegister (
+  UINTN  Base,
+  UINTN  Offset
+  )
+{
+  if (PcdGetBool (PcdSerialUseMmio)) {
+    if (PcdGet8 (PcdSerialRegisterAccessWidth) == 32) {
+      return (UINT8)MmioRead32 (Base + Offset * PcdGet32 (PcdSerialRegisterStride));
+    }
+
+    return MmioRead8 (Base + Offset * PcdGet32 (PcdSerialRegisterStride));
+  } else {
+    return IoRead8 (Base + Offset * PcdGet32 (PcdSerialRegisterStride));
+  }
+}
+
+/**
+  Write an 8-bit 16550 register.  If PcdSerialUseMmio is TRUE, then the value is written to
+  MMIO space.  If PcdSerialUseMmio is FALSE, then the value is written to I/O space.  The
+  parameter Offset is added to the base address of the 16550 registers that is specified
+  by PcdSerialRegisterBase. PcdSerialRegisterAccessWidth specifies the MMIO space access
+  width and defaults to 8 bit access, and supports 8 or 32 bit access.
+
+  @param  Base    The base address register of UART device.
+  @param  Offset  The offset of the 16550 register to write.
+  @param  Value   The value to write to the 16550 register specified by Offset.
+
+  @return The value written to the 16550 register.
+**/
+UINT8
+SerialPortWriteRegister (
+  UINTN  Base,
+  UINTN  Offset,
+  UINT8  Value
+  )
+{
+  if (PcdGetBool (PcdSerialUseMmio)) {
+    if (PcdGet8 (PcdSerialRegisterAccessWidth) == 32) {
+      return (UINT8)MmioWrite32 (Base + Offset * PcdGet32 (PcdSerialRegisterStride), (UINT8)Value);
+    }
+
+    return MmioWrite8 (Base + Offset * PcdGet32 (PcdSerialRegisterStride), Value);
+  } else {
+    return IoWrite8 (Base + Offset * PcdGet32 (PcdSerialRegisterStride), Value);
+  }
+}
+
+/**
+  Retrieve the I/O or MMIO base address register for the PCI UART device.
+
+  This function assumes Root Bus Numer is Zero, and enables I/O and MMIO in PCI UART
+  Device if they are not already enabled.
+
+  @return  The base address register of the UART device.
+**/
+UINTN
+GetSerialRegisterBase (
+  VOID
+  )
+{
+  VOID           *Base;
+  RETURN_STATUS  Status;
+  UINT64         SerialConsoleAddress;
+
+  Base   = (VOID *)(UINTN)PcdGet64 (PcdDeviceTreeInitialBaseAddress);
+  Status = FdtSerialGetConsolePort (Base, &SerialConsoleAddress);
+  if (RETURN_ERROR (Status)) {
+    return (UINTN)0;
+  }
+
+  return SerialConsoleAddress;
+}
+
+/**
+  Return whether the hardware flow control signal allows writing.
+
+  @param  SerialRegisterBase The base address register of UART device.
+
+  @retval TRUE  The serial port is writable.
+  @retval FALSE The serial port is not writable.
+**/
+BOOLEAN
+SerialPortWritable (
+  UINTN  SerialRegisterBase
+  )
+{
+  if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {
+    if (PcdGetBool (PcdSerialDetectCable)) {
+      //
+      // Wait for both DSR and CTS to be set
+      //   DSR is set if a cable is connected.
+      //   CTS is set if it is ok to transmit data
+      //
+      //   DSR  CTS  Description                               Action
+      //   ===  ===  ========================================  ========
+      //    0    0   No cable connected.                       Wait
+      //    0    1   No cable connected.                       Wait
+      //    1    0   Cable connected, but not clear to send.   Wait
+      //    1    1   Cable connected, and clear to send.       Transmit
+      //
+      return (BOOLEAN)((SerialPortReadRegister (SerialRegisterBase, R_UART_MSR) & (B_UART_MSR_DSR | B_UART_MSR_CTS)) == (B_UART_MSR_DSR | B_UART_MSR_CTS));
+    } else {
+      //
+      // Wait for both DSR and CTS to be set OR for DSR to be clear.
+      //   DSR is set if a cable is connected.
+      //   CTS is set if it is ok to transmit data
+      //
+      //   DSR  CTS  Description                               Action
+      //   ===  ===  ========================================  ========
+      //    0    0   No cable connected.                       Transmit
+      //    0    1   No cable connected.                       Transmit
+      //    1    0   Cable connected, but not clear to send.   Wait
+      //    1    1   Cable connected, and clar to send.        Transmit
+      //
+      return (BOOLEAN)((SerialPortReadRegister (SerialRegisterBase, R_UART_MSR) & (B_UART_MSR_DSR | B_UART_MSR_CTS)) != (B_UART_MSR_DSR));
+    }
+  }
+
+  return TRUE;
+}
+
+/**
+  Initialize the serial device hardware.
+
+  If no initialization is required, then return RETURN_SUCCESS.
+  If the serial device was successfully initialized, then return RETURN_SUCCESS.
+  If the serial device could not be initialized, then return RETURN_DEVICE_ERROR.
+
+  @retval RETURN_SUCCESS        The serial device was initialized.
+  @retval RETURN_DEVICE_ERROR   The serial device could not be initialized.
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortInitialize (
+  VOID
+  )
+{
+  UINTN    SerialRegisterBase;
+  UINT32   Divisor;
+  UINT32   CurrentDivisor;
+  BOOLEAN  Initialized;
+
+  //
+  // Calculate divisor for baud generator
+  //    Ref_Clk_Rate / Baud_Rate / 16
+  //
+  Divisor = PcdGet32 (PcdSerialClockRate) / (PcdGet32 (PcdSerialBaudRate) * 16);
+  if ((PcdGet32 (PcdSerialClockRate) % (PcdGet32 (PcdSerialBaudRate) * 16)) >= PcdGet32 (PcdSerialBaudRate) * 8) {
+    Divisor++;
+  }
+
+  //
+  // Get the base address of the serial port in either I/O or MMIO space
+  //
+  SerialRegisterBase = GetSerialRegisterBase ();
+  if (SerialRegisterBase == 0) {
+    return RETURN_DEVICE_ERROR;
+  }
+
+  //
+  // See if the serial port is already initialized
+  //
+  Initialized = TRUE;
+  if ((SerialPortReadRegister (SerialRegisterBase, R_UART_LCR) & 0x3F) != (PcdGet8 (PcdSerialLineControl) & 0x3F)) {
+    Initialized = FALSE;
+  }
+
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_LCR) | B_UART_LCR_DLAB));
+  CurrentDivisor  =  SerialPortReadRegister (SerialRegisterBase, R_UART_BAUD_HIGH) << 8;
+  CurrentDivisor |= (UINT32)SerialPortReadRegister (SerialRegisterBase, R_UART_BAUD_LOW);
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_LCR) & ~B_UART_LCR_DLAB));
+  if (CurrentDivisor != Divisor) {
+    Initialized = FALSE;
+  }
+
+  if (Initialized) {
+    return RETURN_SUCCESS;
+  }
+
+  //
+  // Wait for the serial port to be ready.
+  // Verify that both the transmit FIFO and the shift register are empty.
+  //
+  while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) != (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) {
+  }
+
+  //
+  // Configure baud rate
+  //
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, B_UART_LCR_DLAB);
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_BAUD_HIGH, (UINT8)(Divisor >> 8));
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_BAUD_LOW, (UINT8)(Divisor & 0xff));
+
+  //
+  // Clear DLAB and configure Data Bits, Parity, and Stop Bits.
+  // Strip reserved bits from PcdSerialLineControl
+  //
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8)(PcdGet8 (PcdSerialLineControl) & 0x3F));
+
+  //
+  // Enable and reset FIFOs
+  // Strip reserved bits from PcdSerialFifoControl
+  //
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_FCR, 0x00);
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_FCR, (UINT8)(PcdGet8 (PcdSerialFifoControl) & (B_UART_FCR_FIFOE | B_UART_FCR_FIFO64)));
+
+  //
+  // Set FIFO Polled Mode by clearing IER after setting FCR
+  //
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_IER, 0x00);
+
+  //
+  // Put Modem Control Register(MCR) into its reset state of 0x00.
+  //
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, 0x00);
+
+  return RETURN_SUCCESS;
+}
+
+/**
+  Write data from buffer to serial device.
+
+  Writes NumberOfBytes data bytes from Buffer to the serial device.
+  The number of bytes actually written to the serial device is returned.
+  If the return value is less than NumberOfBytes, then the write operation failed.
+
+  If Buffer is NULL, then ASSERT().
+
+  If NumberOfBytes is zero, then return 0.
+
+  @param  Buffer           Pointer to the data buffer to be written.
+  @param  NumberOfBytes    Number of bytes to written to the serial device.
+
+  @retval 0                NumberOfBytes is 0.
+  @retval >0               The number of bytes written to the serial device.
+                           If this value is less than NumberOfBytes, then the write operation failed.
+
+**/
+UINTN
+EFIAPI
+SerialPortWrite (
+  IN UINT8  *Buffer,
+  IN UINTN  NumberOfBytes
+  )
+{
+  UINTN  SerialRegisterBase;
+  UINTN  Result;
+  UINTN  Index;
+  UINTN  FifoSize;
+
+  if (Buffer == NULL) {
+    return 0;
+  }
+
+  SerialRegisterBase = GetSerialRegisterBase ();
+  if (SerialRegisterBase == 0) {
+    return 0;
+  }
+
+  if (NumberOfBytes == 0) {
+    //
+    // Flush the hardware
+    //
+
+    //
+    // Wait for both the transmit FIFO and shift register empty.
+    //
+    while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) != (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) {
+    }
+
+    //
+    // Wait for the hardware flow control signal
+    //
+    while (!SerialPortWritable (SerialRegisterBase)) {
+    }
+
+    return 0;
+  }
+
+  //
+  // Compute the maximum size of the Tx FIFO
+  //
+  FifoSize = 1;
+  if ((PcdGet8 (PcdSerialFifoControl) & B_UART_FCR_FIFOE) != 0) {
+    if ((PcdGet8 (PcdSerialFifoControl) & B_UART_FCR_FIFO64) == 0) {
+      FifoSize = 16;
+    } else {
+      FifoSize = PcdGet32 (PcdSerialExtendedTxFifoSize);
+    }
+  }
+
+  Result = NumberOfBytes;
+  while (NumberOfBytes != 0) {
+    //
+    // Wait for the serial port to be ready, to make sure both the transmit FIFO
+    // and shift register empty.
+    //
+    while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) != (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) {
+    }
+
+    //
+    // Fill then entire Tx FIFO
+    //
+    for (Index = 0; Index < FifoSize && NumberOfBytes != 0; Index++, NumberOfBytes--, Buffer++) {
+      //
+      // Wait for the hardware flow control signal
+      //
+      while (!SerialPortWritable (SerialRegisterBase)) {
+      }
+
+      //
+      // Write byte to the transmit buffer.
+      //
+      SerialPortWriteRegister (SerialRegisterBase, R_UART_TXBUF, *Buffer);
+    }
+  }
+
+  return Result;
+}
+
+/**
+  Reads data from a serial device into a buffer.
+
+  @param  Buffer           Pointer to the data buffer to store the data read from the serial device.
+  @param  NumberOfBytes    Number of bytes to read from the serial device.
+
+  @retval 0                NumberOfBytes is 0.
+  @retval >0               The number of bytes read from the serial device.
+                           If this value is less than NumberOfBytes, then the read operation failed.
+**/
+UINTN
+EFIAPI
+SerialPortRead (
+  OUT UINT8  *Buffer,
+  IN  UINTN  NumberOfBytes
+  )
+{
+  UINTN  SerialRegisterBase;
+  UINTN  Result;
+  UINT8  Mcr;
+
+  if (NULL == Buffer) {
+    return 0;
+  }
+
+  SerialRegisterBase = GetSerialRegisterBase ();
+  if (SerialRegisterBase == 0) {
+    return 0;
+  }
+
+  Mcr = (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_MCR) & ~B_UART_MCR_RTS);
+
+  for (Result = 0; NumberOfBytes-- != 0; Result++, Buffer++) {
+    //
+    // Wait for the serial port to have some data.
+    //
+    while ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & B_UART_LSR_RXRDY) == 0) {
+      if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {
+        //
+        // Set RTS to let the peer send some data
+        //
+        SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, (UINT8)(Mcr | B_UART_MCR_RTS));
+      }
+    }
+
+    if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {
+      //
+      // Clear RTS to prevent peer from sending data
+      //
+      SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, Mcr);
+    }
+
+    //
+    // Read byte from the receive buffer.
+    //
+    *Buffer = SerialPortReadRegister (SerialRegisterBase, R_UART_RXBUF);
+  }
+
+  return Result;
+}
+
+/**
+  Polls a serial device to see if there is any data waiting to be read.
+
+  Polls aserial device to see if there is any data waiting to be read.
+  If there is data waiting to be read from the serial device, then TRUE is returned.
+  If there is no data waiting to be read from the serial device, then FALSE is returned.
+
+  @retval TRUE             Data is waiting to be read from the serial device.
+  @retval FALSE            There is no data waiting to be read from the serial device.
+**/
+BOOLEAN
+EFIAPI
+SerialPortPoll (
+  VOID
+  )
+{
+  UINTN  SerialRegisterBase;
+
+  SerialRegisterBase = GetSerialRegisterBase ();
+  if (SerialRegisterBase == 0) {
+    return FALSE;
+  }
+
+  //
+  // Read the serial port status
+  //
+  if ((SerialPortReadRegister (SerialRegisterBase, R_UART_LSR) & B_UART_LSR_RXRDY) != 0) {
+    if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {
+      //
+      // Clear RTS to prevent peer from sending data
+      //
+      SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_MCR) & ~B_UART_MCR_RTS));
+    }
+
+    return TRUE;
+  }
+
+  if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {
+    //
+    // Set RTS to let the peer send some data
+    //
+    SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, (UINT8)(SerialPortReadRegister (SerialRegisterBase, R_UART_MCR) | B_UART_MCR_RTS));
+  }
+
+  return FALSE;
+}
+
+/**
+  Sets the control bits on a serial device.
+
+  @param Control                Sets the bits of Control that are settable.
+
+  @retval RETURN_SUCCESS        The new control bits were set on the serial device.
+  @retval RETURN_UNSUPPORTED    The serial device does not support this operation.
+  @retval RETURN_DEVICE_ERROR   The serial device is not functioning correctly.
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortSetControl (
+  IN UINT32  Control
+  )
+{
+  UINTN  SerialRegisterBase;
+  UINT8  Mcr;
+
+  //
+  // First determine the parameter is invalid.
+  //
+  if ((Control & (~(EFI_SERIAL_REQUEST_TO_SEND | EFI_SERIAL_DATA_TERMINAL_READY |
+                    EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE))) != 0)
+  {
+    return RETURN_UNSUPPORTED;
+  }
+
+  SerialRegisterBase = GetSerialRegisterBase ();
+  if (SerialRegisterBase == 0) {
+    return RETURN_UNSUPPORTED;
+  }
+
+  //
+  // Read the Modem Control Register.
+  //
+  Mcr  = SerialPortReadRegister (SerialRegisterBase, R_UART_MCR);
+  Mcr &= (~(B_UART_MCR_DTRC | B_UART_MCR_RTS));
+
+  if ((Control & EFI_SERIAL_DATA_TERMINAL_READY) == EFI_SERIAL_DATA_TERMINAL_READY) {
+    Mcr |= B_UART_MCR_DTRC;
+  }
+
+  if ((Control & EFI_SERIAL_REQUEST_TO_SEND) == EFI_SERIAL_REQUEST_TO_SEND) {
+    Mcr |= B_UART_MCR_RTS;
+  }
+
+  //
+  // Write the Modem Control Register.
+  //
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_MCR, Mcr);
+
+  return RETURN_SUCCESS;
+}
+
+/**
+  Retrieve the status of the control bits on a serial device.
+
+  @param Control                A pointer to return the current control signals from the serial device.
+
+  @retval RETURN_SUCCESS        The control bits were read from the serial device.
+  @retval RETURN_UNSUPPORTED    The serial device does not support this operation.
+  @retval RETURN_DEVICE_ERROR   The serial device is not functioning correctly.
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortGetControl (
+  OUT UINT32  *Control
+  )
+{
+  UINTN  SerialRegisterBase;
+  UINT8  Msr;
+  UINT8  Mcr;
+  UINT8  Lsr;
+
+  SerialRegisterBase = GetSerialRegisterBase ();
+  if (SerialRegisterBase == 0) {
+    return RETURN_UNSUPPORTED;
+  }
+
+  *Control = 0;
+
+  //
+  // Read the Modem Status Register.
+  //
+  Msr = SerialPortReadRegister (SerialRegisterBase, R_UART_MSR);
+
+  if ((Msr & B_UART_MSR_CTS) == B_UART_MSR_CTS) {
+    *Control |= EFI_SERIAL_CLEAR_TO_SEND;
+  }
+
+  if ((Msr & B_UART_MSR_DSR) == B_UART_MSR_DSR) {
+    *Control |= EFI_SERIAL_DATA_SET_READY;
+  }
+
+  if ((Msr & B_UART_MSR_RI) == B_UART_MSR_RI) {
+    *Control |= EFI_SERIAL_RING_INDICATE;
+  }
+
+  if ((Msr & B_UART_MSR_DCD) == B_UART_MSR_DCD) {
+    *Control |= EFI_SERIAL_CARRIER_DETECT;
+  }
+
+  //
+  // Read the Modem Control Register.
+  //
+  Mcr = SerialPortReadRegister (SerialRegisterBase, R_UART_MCR);
+
+  if ((Mcr & B_UART_MCR_DTRC) == B_UART_MCR_DTRC) {
+    *Control |= EFI_SERIAL_DATA_TERMINAL_READY;
+  }
+
+  if ((Mcr & B_UART_MCR_RTS) == B_UART_MCR_RTS) {
+    *Control |= EFI_SERIAL_REQUEST_TO_SEND;
+  }
+
+  if (PcdGetBool (PcdSerialUseHardwareFlowControl)) {
+    *Control |= EFI_SERIAL_HARDWARE_FLOW_CONTROL_ENABLE;
+  }
+
+  //
+  // Read the Line Status Register.
+  //
+  Lsr = SerialPortReadRegister (SerialRegisterBase, R_UART_LSR);
+
+  if ((Lsr & (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) == (B_UART_LSR_TEMT | B_UART_LSR_TXRDY)) {
+    *Control |= EFI_SERIAL_OUTPUT_BUFFER_EMPTY;
+  }
+
+  if ((Lsr & B_UART_LSR_RXRDY) == 0) {
+    *Control |= EFI_SERIAL_INPUT_BUFFER_EMPTY;
+  }
+
+  return RETURN_SUCCESS;
+}
+
+/**
+  Sets the baud rate, receive FIFO depth, transmit/receice time out, parity,
+  data bits, and stop bits on a serial device.
+
+  @param BaudRate           The requested baud rate. A BaudRate value of 0 will use the
+                            device's default interface speed.
+                            On output, the value actually set.
+  @param ReveiveFifoDepth   The requested depth of the FIFO on the receive side of the
+                            serial interface. A ReceiveFifoDepth value of 0 will use
+                            the device's default FIFO depth.
+                            On output, the value actually set.
+  @param Timeout            The requested time out for a single character in microseconds.
+                            This timeout applies to both the transmit and receive side of the
+                            interface. A Timeout value of 0 will use the device's default time
+                            out value.
+                            On output, the value actually set.
+  @param Parity             The type of parity to use on this serial device. A Parity value of
+                            DefaultParity will use the device's default parity value.
+                            On output, the value actually set.
+  @param DataBits           The number of data bits to use on the serial device. A DataBits
+                            vaule of 0 will use the device's default data bit setting.
+                            On output, the value actually set.
+  @param StopBits           The number of stop bits to use on this serial device. A StopBits
+                            value of DefaultStopBits will use the device's default number of
+                            stop bits.
+                            On output, the value actually set.
+
+  @retval RETURN_SUCCESS            The new attributes were set on the serial device.
+  @retval RETURN_UNSUPPORTED        The serial device does not support this operation.
+  @retval RETURN_INVALID_PARAMETER  One or more of the attributes has an unsupported value.
+  @retval RETURN_DEVICE_ERROR       The serial device is not functioning correctly.
+**/
+RETURN_STATUS
+EFIAPI
+SerialPortSetAttributes (
+  IN OUT UINT64              *BaudRate,
+  IN OUT UINT32              *ReceiveFifoDepth,
+  IN OUT UINT32              *Timeout,
+  IN OUT EFI_PARITY_TYPE     *Parity,
+  IN OUT UINT8               *DataBits,
+  IN OUT EFI_STOP_BITS_TYPE  *StopBits
+  )
+{
+  UINTN   SerialRegisterBase;
+  UINT32  SerialBaudRate;
+  UINTN   Divisor;
+  UINT8   Lcr;
+  UINT8   LcrData;
+  UINT8   LcrParity;
+  UINT8   LcrStop;
+
+  SerialRegisterBase = GetSerialRegisterBase ();
+  if (SerialRegisterBase == 0) {
+    return RETURN_UNSUPPORTED;
+  }
+
+  //
+  // Check for default settings and fill in actual values.
+  //
+  if (*BaudRate == 0) {
+    *BaudRate = PcdGet32 (PcdSerialBaudRate);
+  }
+
+  SerialBaudRate = (UINT32)*BaudRate;
+
+  if (*DataBits == 0) {
+    LcrData   = (UINT8)(PcdGet8 (PcdSerialLineControl) & 0x3);
+    *DataBits = LcrData + 5;
+  } else {
+    if ((*DataBits < 5) || (*DataBits > 8)) {
+      return RETURN_INVALID_PARAMETER;
+    }
+
+    //
+    // Map 5..8 to 0..3
+    //
+    LcrData = (UINT8)(*DataBits - (UINT8)5);
+  }
+
+  if (*Parity == DefaultParity) {
+    LcrParity = (UINT8)((PcdGet8 (PcdSerialLineControl) >> 3) & 0x7);
+    switch (LcrParity) {
+      case 0:
+        *Parity = NoParity;
+        break;
+
+      case 3:
+        *Parity = EvenParity;
+        break;
+
+      case 1:
+        *Parity = OddParity;
+        break;
+
+      case 7:
+        *Parity = SpaceParity;
+        break;
+
+      case 5:
+        *Parity = MarkParity;
+        break;
+
+      default:
+        break;
+    }
+  } else {
+    switch (*Parity) {
+      case NoParity:
+        LcrParity = 0;
+        break;
+
+      case EvenParity:
+        LcrParity = 3;
+        break;
+
+      case OddParity:
+        LcrParity = 1;
+        break;
+
+      case SpaceParity:
+        LcrParity = 7;
+        break;
+
+      case MarkParity:
+        LcrParity = 5;
+        break;
+
+      default:
+        return RETURN_INVALID_PARAMETER;
+    }
+  }
+
+  if (*StopBits == DefaultStopBits) {
+    LcrStop = (UINT8)((PcdGet8 (PcdSerialLineControl) >> 2) & 0x1);
+    switch (LcrStop) {
+      case 0:
+        *StopBits = OneStopBit;
+        break;
+
+      case 1:
+        if (*DataBits == 5) {
+          *StopBits = OneFiveStopBits;
+        } else {
+          *StopBits = TwoStopBits;
+        }
+
+        break;
+
+      default:
+        break;
+    }
+  } else {
+    switch (*StopBits) {
+      case OneStopBit:
+        LcrStop = 0;
+        break;
+
+      case OneFiveStopBits:
+      case TwoStopBits:
+        LcrStop = 1;
+        break;
+
+      default:
+        return RETURN_INVALID_PARAMETER;
+    }
+  }
+
+  //
+  // Calculate divisor for baud generator
+  //    Ref_Clk_Rate / Baud_Rate / 16
+  //
+  Divisor = PcdGet32 (PcdSerialClockRate) / (SerialBaudRate * 16);
+  if ((PcdGet32 (PcdSerialClockRate) % (SerialBaudRate * 16)) >= SerialBaudRate * 8) {
+    Divisor++;
+  }
+
+  //
+  // Configure baud rate
+  //
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, B_UART_LCR_DLAB);
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_BAUD_HIGH, (UINT8)(Divisor >> 8));
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_BAUD_LOW, (UINT8)(Divisor & 0xff));
+
+  //
+  // Clear DLAB and configure Data Bits, Parity, and Stop Bits.
+  // Strip reserved bits from line control value
+  //
+  Lcr = (UINT8)((LcrParity << 3) | (LcrStop << 2) | LcrData);
+  SerialPortWriteRegister (SerialRegisterBase, R_UART_LCR, (UINT8)(Lcr & 0x3F));
+
+  return RETURN_SUCCESS;
+}
diff --git a/OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.inf b/OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.inf
new file mode 100644
index 0000000000..e6a805be6e
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.inf
@@ -0,0 +1,46 @@
+## @file
+#  SerialPortLib instance for 16550 UART.
+#
+#  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                      = EarlySerialPortLib16550
+  FILE_GUID                      = f4fb883d-8138-4f29-bb0c-c574e9312c74
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.1
+  LIBRARY_CLASS                  = SerialPortLib
+
+[Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  FdtSerialPortAddressLib
+  IoLib
+  PcdLib
+
+[Sources]
+  EarlyFdtSerialPortLib16550.c
+
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterAccessWidth     ## SOMETIMES_CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialUseMmio                 ## CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialUseHardwareFlowControl  ## CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialDetectCable             ## SOMETIMES_CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterBase            ## CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialBaudRate                ## CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialLineControl             ## CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialFifoControl             ## CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialClockRate               ## CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialPciDeviceInfo           ## CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialExtendedTxFifoSize      ## CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterStride          ## CONSUMES
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress      ## CONSUMES
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114555): https://edk2.groups.io/g/devel/message/114555
Mute This Topic: https://groups.io/mt/103971679/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 30/37] OvmfPkg/LoongArchVirt: Add real time clock library
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (28 preceding siblings ...)
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 29/37] OvmfPkg/LoongArchVirt: Add the early serial port output library Chao Li
@ 2024-01-26  6:30 ` Chao Li
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 31/37] OvmfPkg/LoongArchVirt: Add NorFlashQemuLib Chao Li
                   ` (11 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:30 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Laszlo Ersek, Jiewen Yao, Jordan Justen,
	Gerd Hoffmann, Bibo Mao, Dongyan Qian, Baoqi Zhang, Xianglai Li

This library is provides real time clock for LoongArch virtual machine.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Bibo Mao <maobibo@loongson.cn>
Cc: Dongyan Qian <qiandongyan@loongson.cn>
Signed-off-by: Chao Li <lichao@loongson.cn>
Co-authored-by: Baoqi Zhang <zhangbaoqi@loongson.cn>
Co-authored-by: Xianglai Li <lixianglai@loongson.cn>
Reviewed-by: Bibo Mao <maobibo@loongson.cn>
---
 .../DxeLsRealTimeClockLib.c                   | 327 ++++++++++++++++++
 .../DxeLsRealTimeClockLib.inf                 |  41 +++
 .../LsRealTimeClockLib/LsRealTimeClock.h      |  47 +++
 .../PeiLsRealTimeClockLib.c                   |  31 ++
 .../PeiLsRealTimeClockLib.inf                 |  29 ++
 5 files changed, 475 insertions(+)
 create mode 100644 OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/DxeLsRealTimeClockLib.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/DxeLsRealTimeClockLib.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/LsRealTimeClock.h
 create mode 100644 OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/PeiLsRealTimeClockLib.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/PeiLsRealTimeClockLib.inf

diff --git a/OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/DxeLsRealTimeClockLib.c b/OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/DxeLsRealTimeClockLib.c
new file mode 100644
index 0000000000..e990728069
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/DxeLsRealTimeClockLib.c
@@ -0,0 +1,327 @@
+/** @file
+  Implement EFI RealTimeClock runtime services via RTC Lib.
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Include/Base.h>
+#include <Library/BaseLib.h>
+#include <Library/HobLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/IoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/RealTimeClockLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeLib.h>
+
+#include "LsRealTimeClock.h"
+
+STATIC BOOLEAN    mInitialized = FALSE;
+STATIC EFI_EVENT  mRtcVirtualAddrChangeEvent;
+STATIC UINTN      mRtcBase;
+
+/*
+  Enable Real-time clock.
+
+  @param VOID
+
+  @retval  VOID
+ */
+VOID
+InitRtc (
+  VOID
+  )
+{
+  UINTN              Val;
+  EFI_HOB_GUID_TYPE  *GuidHob   = NULL;
+  VOID               *DataInHob = NULL;
+
+  if (!mInitialized) {
+    /* Enable rtc */
+    GuidHob = GetFirstGuidHob (&mRtcRegisterBaseAddressGuid);
+    if (GuidHob) {
+      DataInHob = GET_GUID_HOB_DATA (GuidHob);
+      mRtcBase  = (UINT64)(*(UINTN *)DataInHob);
+      Val       = MmioRead32 (mRtcBase + RTC_CTRL_REG);
+      Val      |= TOY_ENABLE_BIT | OSC_ENABLE_BIT;
+      MmioWrite32 (mRtcBase + RTC_CTRL_REG, Val);
+      mInitialized = TRUE;
+    } else {
+      DebugPrint (EFI_D_INFO, "RTC register address not found!\n");
+      ASSERT (FALSE);
+    }
+  }
+}
+
+/**
+  Returns the current time and date information, and the time-keeping capabilities
+  of the hardware platform.
+
+  @param  Time                   A pointer to storage to receive a snapshot of the current time.
+  @param  Capabilities           An optional pointer to a buffer to receive the real time clock
+                                 device's capabilities.
+
+  @retval EFI_SUCCESS            The operation completed successfully.
+  @retval EFI_INVALID_PARAMETER  Time is NULL.
+  @retval EFI_DEVICE_ERROR       The time could not be retrieved due to hardware error.
+  @retval EFI_SECURITY_VIOLATION The time could not be retrieved due to an authentication failure.
+**/
+EFI_STATUS
+EFIAPI
+LibGetTime (
+  OUT EFI_TIME               *Time,
+  OUT EFI_TIME_CAPABILITIES  *Capabilities
+  )
+{
+  UINT32  Val;
+
+  // Ensure Time is a valid pointer
+  if (Time == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Val        = MmioRead32 (mRtcBase + TOY_READ1_REG);
+  Time->Year = Val + 1900;
+
+  Val              = MmioRead32 (mRtcBase + TOY_READ0_REG);
+  Time->Month      = (Val >> TOY_MON_SHIFT) & TOY_MON_MASK;
+  Time->Day        = (Val >> TOY_DAY_SHIFT) & TOY_DAY_MASK;
+  Time->Hour       = (Val >> TOY_HOUR_SHIFT) & TOY_HOUR_MASK;
+  Time->Minute     = (Val >> TOY_MIN_SHIFT) & TOY_MIN_MASK;
+  Time->Second     = (Val >> TOY_SEC_SHIFT) & TOY_SEC_MASK;
+  Time->Nanosecond = 0;
+  return EFI_SUCCESS;
+}
+
+/**
+  Sets the current local time and date information.
+
+  @param  Time                  A pointer to the current time.
+
+  @retval EFI_SUCCESS           The operation completed successfully.
+  @retval EFI_INVALID_PARAMETER A time field is out of range.
+  @retval EFI_DEVICE_ERROR      The time could not be set due due to hardware error.
+**/
+EFI_STATUS
+EFIAPI
+LibSetTime (
+  IN  EFI_TIME  *Time
+  )
+{
+  UINT32  Val;
+
+  // Initialize the hardware if not already done
+
+  Val  = 0;
+  Val |= (Time->Second << TOY_SEC_SHIFT);
+  Val |= (Time->Minute << TOY_MIN_SHIFT);
+  Val |= (Time->Hour   << TOY_HOUR_SHIFT);
+  Val |= (Time->Day    << TOY_DAY_SHIFT);
+  Val |= (Time->Month  << TOY_MON_SHIFT);
+  MmioWrite32 (mRtcBase + TOY_WRITE0_REG, Val);
+
+  Val = Time->Year - 1900;
+  MmioWrite32 (mRtcBase + TOY_WRITE1_REG, Val);
+  return EFI_SUCCESS;
+}
+
+/**
+  Returns the current wakeup alarm clock setting.
+
+  @param  Enabled               Indicates if the alarm is currently enabled or disabled.
+  @param  Pending               Indicates if the alarm signal is pending and requires acknowledgement.
+  @param  Time                  The current alarm setting.
+
+  @retval EFI_SUCCESS           The alarm settings were returned.
+  @retval EFI_INVALID_PARAMETER Any parameter is NULL.
+  @retval EFI_DEVICE_ERROR      The wakeup time could not be retrieved due to a hardware error.
+**/
+EFI_STATUS
+EFIAPI
+LibGetWakeupTime (
+  OUT BOOLEAN   *Enabled,
+  OUT BOOLEAN   *Pending,
+  OUT EFI_TIME  *Time
+  )
+{
+  // Not a required feature
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Sets the system wakeup alarm clock time.
+
+  @param  Enabled               Enable or disable the wakeup alarm.
+  @param  Time                  If Enable is TRUE, the time to set the wakeup alarm for.
+
+  @retval EFI_SUCCESS           If Enable is TRUE, then the wakeup alarm was enabled. If
+                                Enable is FALSE, then the wakeup alarm was disabled.
+  @retval EFI_INVALID_PARAMETER A time field is out of range.
+  @retval EFI_DEVICE_ERROR      The wakeup time could not be set due to a hardware error.
+  @retval EFI_UNSUPPORTED       A wakeup timer is not supported on this platform.
+**/
+EFI_STATUS
+EFIAPI
+LibSetWakeupTime (
+  IN BOOLEAN    Enabled,
+  OUT EFI_TIME  *Time
+  )
+{
+  // Not a required feature
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Fixup internal data so that EFI can be call in virtual mode.
+  Call the passed in Child Notify event and convert any pointers in
+  lib to virtual mode.
+
+  @param[in]    Event   The Event that is being processed
+  @param[in]    Context Event Context
+**/
+STATIC
+VOID
+EFIAPI
+VirtualNotifyEvent (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+{
+  //
+  // Only needed if you are going to support the OS calling RTC functions in virtual mode.
+  // You will need to call EfiConvertPointer (). To convert any stored physical addresses
+  // to virtual address. After the OS transitions to calling in virtual mode, all future
+  // runtime calls will be made in virtual mode.
+  //
+  EfiConvertPointer (0x0, (VOID **)&mRtcBase);
+  return;
+}
+
+/** Add the RTC controller address range to the memory map.
+
+  @param [in]  ImageHandle  The handle to the image.
+  @param [in]  RtcPageBase  Base address of the RTC controller.
+
+  @retval EFI_SUCCESS             Success.
+  @retval EFI_INVALID_PARAMETER   A parameter is invalid.
+  @retval EFI_NOT_FOUND           Flash device not found.
+**/
+EFI_STATUS
+KvmtoolRtcMapMemory (
+  IN EFI_HANDLE            ImageHandle,
+  IN EFI_PHYSICAL_ADDRESS  RtcPageBase
+  )
+{
+  EFI_STATUS  Status;
+
+  Status = gDS->AddMemorySpace (
+                  EfiGcdMemoryTypeMemoryMappedIo,
+                  RtcPageBase,
+                  EFI_PAGE_SIZE,
+                  EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
+                  );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "Failed to add memory space. Status = %r\n",
+      Status
+      ));
+    return Status;
+  }
+
+  Status = gDS->AllocateMemorySpace (
+                  EfiGcdAllocateAddress,
+                  EfiGcdMemoryTypeMemoryMappedIo,
+                  0,
+                  EFI_PAGE_SIZE,
+                  &RtcPageBase,
+                  ImageHandle,
+                  NULL
+                  );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "Failed to allocate memory space. Status = %r\n",
+      Status
+      ));
+    gDS->RemoveMemorySpace (
+           RtcPageBase,
+           EFI_PAGE_SIZE
+           );
+    return Status;
+  }
+
+  Status = gDS->SetMemorySpaceAttributes (
+                  RtcPageBase,
+                  EFI_PAGE_SIZE,
+                  EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
+                  );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "Failed to set memory attributes. Status = %r\n",
+      Status
+      ));
+
+    gDS->FreeMemorySpace (
+           RtcPageBase,
+           EFI_PAGE_SIZE
+           );
+
+    gDS->RemoveMemorySpace (
+           RtcPageBase,
+           EFI_PAGE_SIZE
+           );
+  }
+
+  return Status;
+}
+
+/**
+  This is the declaration of an EFI image entry point. This can be the entry point to an application
+  written to this specification, an EFI boot service driver, or an EFI runtime driver.
+
+  @param  ImageHandle           Handle that identifies the loaded image.
+  @param  SystemTable           System Table for this image.
+
+  @retval EFI_SUCCESS           The operation completed successfully.
+**/
+EFI_STATUS
+EFIAPI
+LibRtcInitialize (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+
+  InitRtc ();
+  Status = KvmtoolRtcMapMemory (ImageHandle, (mRtcBase & ~EFI_PAGE_MASK));
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "Failed to map memory for loongson 7A RTC. Status = %r\n",
+      Status
+      ));
+    return Status;
+  }
+
+  //
+  // Register for the virtual address change event
+  //
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  VirtualNotifyEvent,
+                  NULL,
+                  &gEfiEventVirtualAddressChangeGuid,
+                  &mRtcVirtualAddrChangeEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+  return Status;
+}
diff --git a/OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/DxeLsRealTimeClockLib.inf b/OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/DxeLsRealTimeClockLib.inf
new file mode 100644
index 0000000000..4da0fa9f04
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/DxeLsRealTimeClockLib.inf
@@ -0,0 +1,41 @@
+## @file
+#  LoongArch64 CPU Real Time Clock DXE Phase Library.
+#
+# 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                      = LsRealTimeClockLib
+  FILE_GUID                      = 9793a3da-1869-4fdf-88b1-c6484341f50b
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = RealTimeClockLib
+
+#
+#  VALID_ARCHITECTURES           = LOONGARCH64
+#
+
+[Sources]
+  DxeLsRealTimeClockLib.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+
+[LibraryClasses]
+  DebugLib
+  DxeServicesTableLib
+  HobLib
+  IoLib
+  PcdLib
+  UefiRuntimeLib
+
+[Guids]
+  gEfiEventVirtualAddressChangeGuid
+
+[Depex]
+  TRUE
diff --git a/OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/LsRealTimeClock.h b/OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/LsRealTimeClock.h
new file mode 100644
index 0000000000..dcd8438c1f
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/LsRealTimeClock.h
@@ -0,0 +1,47 @@
+/** @file
+  Implement EFI RealTimeClock runtime services via RTC Lib.
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef LS_REAL_TIME_CLOCK_H_
+#define LS_REAL_TIME_CLOCK_H_
+
+#define TOY_WRITE0_REG  0x24
+#define TOY_WRITE1_REG  0x28
+#define TOY_READ0_REG   0x2c
+#define TOY_READ1_REG   0x30
+#define RTC_CTRL_REG    0x40
+
+/* TOY Enable bits */
+#define RTC_ENABLE_BIT  (1UL << 13)
+#define TOY_ENABLE_BIT  (1UL << 11)
+#define OSC_ENABLE_BIT  (1UL << 8)
+
+/*
+ * shift bits and filed mask
+ */
+#define TOY_MON_MASK   0x3f
+#define TOY_DAY_MASK   0x1f
+#define TOY_HOUR_MASK  0x1f
+#define TOY_MIN_MASK   0x3f
+#define TOY_SEC_MASK   0x3f
+#define TOY_MSEC_MASK  0xf
+
+#define TOY_MON_SHIFT   26
+#define TOY_DAY_SHIFT   21
+#define TOY_HOUR_SHIFT  16
+#define TOY_MIN_SHIFT   10
+#define TOY_SEC_SHIFT   4
+
+#define RTC_REGISTER_ADDRESS_HOB_GUID \
+  { \
+    0x0d7c012b, 0x79c1, 0xfa58, { 0x6f, 0x91, 0xbe, 0x3e, 0xee, 0x46, 0x5f, 0x71 } \
+  }
+
+EFI_GUID  mRtcRegisterBaseAddressGuid = RTC_REGISTER_ADDRESS_HOB_GUID;
+
+#endif // LS_REAL_TIME_CLOCK_H_
diff --git a/OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/PeiLsRealTimeClockLib.c b/OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/PeiLsRealTimeClockLib.c
new file mode 100644
index 0000000000..6929fb15e4
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/PeiLsRealTimeClockLib.c
@@ -0,0 +1,31 @@
+/** @file
+  Implement EFI RealTimeClock PEI phase RTC Lib.
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/HobLib.h>
+
+#include "LsRealTimeClock.h"
+
+VOID
+SaveRtcRegisterAddressHob (
+  UINT64  RtcRegisterBase
+  )
+{
+  UINT64  Data64;
+
+  //
+  // Build location of RTC register base address buffer in HOB
+  //
+  Data64 = (UINT64)(UINTN)RtcRegisterBase;
+
+  BuildGuidDataHob (
+    &mRtcRegisterBaseAddressGuid,
+    (VOID *)&Data64,
+    sizeof (UINT64)
+    );
+}
diff --git a/OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/PeiLsRealTimeClockLib.inf b/OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/PeiLsRealTimeClockLib.inf
new file mode 100644
index 0000000000..05ed6b2429
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/PeiLsRealTimeClockLib.inf
@@ -0,0 +1,29 @@
+## @file
+#  LoongArch64 CPU Real Time Clock PEI Phase Library.
+#
+# 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                      = PeiLsRealTimeClockLib
+  FILE_GUID                      = d4358430-15ec-9358-1b12-26dab955e9c6
+  MODULE_TYPE                    = PEIM
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = RealTimeClockLib|PEIM
+
+#
+#  VALID_ARCHITECTURES           = LOONGARCH64
+#
+
+[Sources]
+  PeiLsRealTimeClockLib.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  HobLib
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114556): https://edk2.groups.io/g/devel/message/114556
Mute This Topic: https://groups.io/mt/103971680/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 31/37] OvmfPkg/LoongArchVirt: Add NorFlashQemuLib
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (29 preceding siblings ...)
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 30/37] OvmfPkg/LoongArchVirt: Add real time clock library Chao Li
@ 2024-01-26  6:30 ` Chao Li
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 32/37] OvmfPkg/LoongArchVirt: Add FdtQemuFwCfgLib Chao Li
                   ` (10 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:30 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Laszlo Ersek, Jiewen Yao, Jordan Justen,
	Gerd Hoffmann, Bibo Mao, Dongyan Qian, Xianglai Li

Add NorFlashQemuLib for LoongArch, it is referenced from ArmVirtPkg.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Bibo Mao <maobibo@loongson.cn>
Cc: Dongyan Qian <qiandongyan@loongson.cn>
Signed-off-by: Chao Li <lichao@loongson.cn>
Co-authored-by: Xianglai Li <lixianglai@loongson.cn>
Co-authored-by: Bibo Mao <maobibo@loongson.cn>
Reviewed-by: Bibo Mao <maobibo@loongson.cn>
---
 .../Library/NorFlashQemuLib/NorFlashQemuLib.c | 140 ++++++++++++++++++
 .../NorFlashQemuLib/NorFlashQemuLib.inf       |  43 ++++++
 2 files changed, 183 insertions(+)
 create mode 100644 OvmfPkg/LoongArchVirt/Library/NorFlashQemuLib/NorFlashQemuLib.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/NorFlashQemuLib/NorFlashQemuLib.inf

diff --git a/OvmfPkg/LoongArchVirt/Library/NorFlashQemuLib/NorFlashQemuLib.c b/OvmfPkg/LoongArchVirt/Library/NorFlashQemuLib/NorFlashQemuLib.c
new file mode 100644
index 0000000000..ae9af09c4c
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/NorFlashQemuLib/NorFlashQemuLib.c
@@ -0,0 +1,140 @@
+/** @file
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/VirtNorFlashPlatformLib.h>
+
+#include <Protocol/FdtClient.h>
+
+#define QEMU_NOR_BLOCK_SIZE  SIZE_128KB
+
+EFI_STATUS
+VirtNorFlashPlatformInitialization (
+  VOID
+  )
+{
+  return EFI_SUCCESS;
+}
+
+STATIC VIRT_NOR_FLASH_DESCRIPTION  mNorFlashDevices;
+
+EFI_STATUS
+VirtNorFlashPlatformGetDevices (
+  OUT VIRT_NOR_FLASH_DESCRIPTION  **NorFlashDescriptions,
+  OUT UINT32                      *Count
+  )
+{
+  FDT_CLIENT_PROTOCOL  *FdtClient;
+  INT32                Node;
+  EFI_STATUS           Status;
+  EFI_STATUS           FindNodeStatus;
+  CONST UINT32         *Reg;
+  UINT32               PropSize;
+  UINT64               Base;
+  UINT64               Size;
+
+  Status = gBS->LocateProtocol (
+                  &gFdtClientProtocolGuid,
+                  NULL,
+                  (VOID **)&FdtClient
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  FindNodeStatus = FdtClient->FindCompatibleNode (
+                                FdtClient,
+                                "cfi-flash",
+                                &Node
+                                );
+  ASSERT_EFI_ERROR (FindNodeStatus);
+
+  Status = FdtClient->GetNodeProperty (
+                        FdtClient,
+                        Node,
+                        "reg",
+                        (CONST VOID **)&Reg,
+                        &PropSize
+                        );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "%a: GetNodeProperty () failed (Status == %r)\n",
+      __func__,
+      Status
+      ));
+    return Status;
+  }
+
+  ASSERT ((PropSize % (4 * sizeof (UINT32))) == 0);
+
+  if (PropSize < (4 * sizeof (UINT32))) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "%a: reg node size(%d) is too small \n",
+      __func__,
+      PropSize
+      ));
+    return EFI_NOT_FOUND;
+  }
+
+  Base = SwapBytes64 (ReadUnaligned64 ((VOID *)&Reg[0]));
+  Size = SwapBytes64 (ReadUnaligned64 ((VOID *)&Reg[2]));
+
+  mNorFlashDevices.DeviceBaseAddress = (UINTN)Base;
+  mNorFlashDevices.RegionBaseAddress = (UINTN)Base;
+  mNorFlashDevices.Size              = (UINTN)Size;
+  mNorFlashDevices.BlockSize         = QEMU_NOR_BLOCK_SIZE;
+
+  Status = PcdSet32S (PcdFlashNvStorageVariableBase, Base);
+  ASSERT_EFI_ERROR (Status);
+
+  /*
+   * Base is the value of PcdFlashNvStorageVariableBase,
+   * PcdFlashNvStorageFtwWorkingBase can be got by
+   *   PcdFlashNvStorageVariableBase + PcdFlashNvStorageVariableSize
+   */
+  Base  += PcdGet32 (PcdFlashNvStorageVariableSize);
+  Status = PcdSet32S (PcdFlashNvStorageFtwWorkingBase, Base);
+  ASSERT_EFI_ERROR (Status);
+
+  /*
+   * Now,Base is the value of PcdFlashNvStorageFtwWorkingBase,
+   * PcdFlashNvStorageFtwSpareBase can be got by
+   *   PcdFlashNvStorageFtwWorkingBase + PcdFlashNvStorageFtwWorkingSize.
+   */
+  Base  += PcdGet32 (PcdFlashNvStorageFtwWorkingSize);
+  Status = PcdSet32S (PcdFlashNvStorageFtwSpareBase, Base);
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // UEFI takes ownership of the NOR flash, and exposes its functionality
+  // through the UEFI Runtime Services GetVariable, SetVariable, etc. This
+  // means we need to disable it in the device tree to prevent the OS from
+  // attaching its device driver as well.
+  // Note that this also hides other flash banks, but the only other flash
+  // bank we expect to encounter is the one that carries the UEFI executable
+  // code, which is not intended to be guest updatable, and is usually backed
+  // in a readonly manner by QEMU anyway.
+  //
+  Status = FdtClient->SetNodeProperty (
+                        FdtClient,
+                        Node,
+                        "status",
+                        "disabled",
+                        sizeof ("disabled")
+                        );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_WARN, "Failed to set NOR flash status to 'disabled'\n"));
+  }
+
+  *NorFlashDescriptions = &mNorFlashDevices;
+  *Count                = 1;
+
+  return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/LoongArchVirt/Library/NorFlashQemuLib/NorFlashQemuLib.inf b/OvmfPkg/LoongArchVirt/Library/NorFlashQemuLib/NorFlashQemuLib.inf
new file mode 100644
index 0000000000..671684e738
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/NorFlashQemuLib/NorFlashQemuLib.inf
@@ -0,0 +1,43 @@
+## @file
+#
+#  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                      = NorFlashQemuLib
+  FILE_GUID                      = E225C90F-6CB9-8AF3-095B-2668FC633A57
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = VirtNorFlashPlatformLib
+
+[Sources]
+  NorFlashQemuLib.c
+
+[Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  DebugLib
+  UefiBootServicesTableLib
+
+[Protocols]
+  gFdtClientProtocolGuid          ## CONSUMES
+
+[Depex]
+  gFdtClientProtocolGuid
+
+[Pcd]
+gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize
+gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize
+gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize
+gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase
+gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase
+gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114557): https://edk2.groups.io/g/devel/message/114557
Mute This Topic: https://groups.io/mt/103971681/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 32/37] OvmfPkg/LoongArchVirt: Add FdtQemuFwCfgLib
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (30 preceding siblings ...)
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 31/37] OvmfPkg/LoongArchVirt: Add NorFlashQemuLib Chao Li
@ 2024-01-26  6:30 ` Chao Li
  2024-01-26  6:31 ` [edk2-devel] [PATCH v8 33/37] OvmfPkg/LoongArchVirt: Add reset system library Chao Li
                   ` (9 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:30 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Laszlo Ersek, Jiewen Yao, Jordan Justen,
	Gerd Hoffmann, Bibo Mao, Dongyan Qian, Xianglai Li

This library for PEI phase, and obtains the QemuFwCfg base address by
directly parsing the FDT, reads and writes the data in QemuFwCfg by
operating on the QemuFwCfg base address.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Bibo Mao <maobibo@loongson.cn>
Cc: Dongyan Qian <qiandongyan@loongson.cn>
Signed-off-by: Chao Li <lichao@loongson.cn>
Co-authored-by: Xianglai Li <lixianglai@loongson.cn>
Co-authored-by: Bibo Mao <maobibo@loongson.cn>
Reviewed-by: Bibo Mao <maobibo@loongson.cn>
---
 .../FdtQemuFwCfgLib/FdtQemuFwCfgPeiLib.c      | 504 ++++++++++++++++++
 .../FdtQemuFwCfgLib/FdtQemuFwCfgPeiLib.inf    |  42 ++
 .../FdtQemuFwCfgLib/QemuFwCfgLibInternal.h    |  73 +++
 .../Library/FdtQemuFwCfgLib/QemuFwCfgPei.c    | 117 ++++
 4 files changed, 736 insertions(+)
 create mode 100644 OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/FdtQemuFwCfgPeiLib.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/FdtQemuFwCfgPeiLib.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/QemuFwCfgLibInternal.h
 create mode 100644 OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/QemuFwCfgPei.c

diff --git a/OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/FdtQemuFwCfgPeiLib.c b/OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/FdtQemuFwCfgPeiLib.c
new file mode 100644
index 0000000000..a1f114b327
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/FdtQemuFwCfgPeiLib.c
@@ -0,0 +1,504 @@
+/** @file
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - FwCfg   - firmWare  Configure
+    - CTL   - Control
+**/
+
+#include <Base.h>
+#include <Uefi.h>
+#include <PiPei.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Uefi/UefiBaseType.h>
+#include <Library/HobLib.h>
+#include <libfdt.h>
+#include "QemuFwCfgLibInternal.h"
+
+EFI_GUID  mFwCfgSelectorAddressGuid = FW_CONFIG_SELECTOR_ADDRESS_HOB_GUID;
+EFI_GUID  mFwCfgDataAddressGuid     = FW_CONFIG_DATA_ADDRESS_HOB_GUID;
+
+STATIC UINTN  mFwCfgSelectorAddress;
+STATIC UINTN  mFwCfgDataAddress;
+
+/**
+  To get firmware configure selector address.
+
+  @param VOID
+
+  @retval  firmware configure selector address
+**/
+UINTN
+EFIAPI
+QemuGetFwCfgSelectorAddress (
+  VOID
+  )
+{
+  UINTN              FwCfgSelectorAddress;
+  EFI_HOB_GUID_TYPE  *GuidHob;
+  VOID               *DataInHob;
+
+  FwCfgSelectorAddress = mFwCfgSelectorAddress;
+  GuidHob              = NULL;
+  DataInHob            = NULL;
+
+  if (FwCfgSelectorAddress == 0) {
+    GuidHob              = GetFirstGuidHob (&mFwCfgSelectorAddressGuid);
+    DataInHob            = GET_GUID_HOB_DATA (GuidHob);
+    FwCfgSelectorAddress = (UINT64)(*(UINTN *)DataInHob);
+  }
+
+  return FwCfgSelectorAddress;
+}
+
+/**
+  To get firmware configure Data address.
+
+  @param VOID
+
+  @retval  firmware configure data address
+**/
+UINTN
+EFIAPI
+QemuGetFwCfgDataAddress (
+  VOID
+  )
+{
+  UINTN              FwCfgDataAddress;
+  EFI_HOB_GUID_TYPE  *GuidHob;
+  VOID               *DataInHob;
+
+  FwCfgDataAddress = mFwCfgDataAddress;
+  GuidHob          = NULL;
+  DataInHob        = NULL;
+
+  if (FwCfgDataAddress == 0) {
+    GuidHob          = GetFirstGuidHob (&mFwCfgDataAddressGuid);
+    DataInHob        = GET_GUID_HOB_DATA (GuidHob);
+    FwCfgDataAddress = (UINT64)(*(UINTN *)DataInHob);
+  }
+
+  return FwCfgDataAddress;
+}
+
+/**
+  Selects a firmware configuration item for reading.
+
+  Following this call, any data read from this item will start from
+  the beginning of the configuration item's data.
+
+  @param[in] QemuFwCfgItem - Firmware Configuration item to read
+**/
+VOID
+EFIAPI
+QemuFwCfgSelectItem (
+  IN FIRMWARE_CONFIG_ITEM  QemuFwCfgItem
+  )
+{
+  UINTN  FwCfgSelectorAddress;
+
+  FwCfgSelectorAddress = QemuGetFwCfgSelectorAddress ();
+  MmioWrite16 (FwCfgSelectorAddress, SwapBytes16 ((UINT16)(UINTN)QemuFwCfgItem));
+}
+
+/**
+  Slow READ_BYTES_FUNCTION.
+
+  @param[in]  The size of the data to be read.
+  @param[in]  Buffer    The buffer that stores the readout data.
+**/
+VOID
+EFIAPI
+MmioReadBytes (
+  IN UINTN  Size,
+  IN VOID   *Buffer OPTIONAL
+  )
+{
+  UINTN  Left;
+  UINT8  *Ptr;
+  UINT8  *End;
+  UINTN  FwCfgDataAddress;
+
+  Left = Size & 7;
+
+  Size -= Left;
+  Ptr   = Buffer;
+  End   = Ptr + Size;
+
+  FwCfgDataAddress = QemuGetFwCfgDataAddress ();
+  while (Ptr < End) {
+    *(UINT64 *)Ptr = MmioRead64 (FwCfgDataAddress);
+    Ptr           += 8;
+  }
+
+  if (Left & 4) {
+    *(UINT32 *)Ptr = MmioRead32 (FwCfgDataAddress);
+    Ptr           += 4;
+  }
+
+  if (Left & 2) {
+    *(UINT16 *)Ptr = MmioRead16 (FwCfgDataAddress);
+    Ptr           += 2;
+  }
+
+  if (Left & 1) {
+    *Ptr = MmioRead8 (FwCfgDataAddress);
+  }
+}
+
+/**
+  Slow WRITE_BYTES_FUNCTION.
+
+  @param[in]  The size of the data to be write.
+  @param[in]  Buffer    The buffer that stores the writein data.
+**/
+VOID
+EFIAPI
+MmioWriteBytes (
+  IN UINTN  Size,
+  IN VOID   *Buffer OPTIONAL
+  )
+{
+  UINTN  Idx;
+  UINTN  FwCfgDataAddress;
+
+  FwCfgDataAddress = QemuGetFwCfgDataAddress ();
+  for (Idx = 0; Idx < Size; ++Idx) {
+    MmioWrite8 (FwCfgDataAddress, ((UINT8 *)Buffer)[Idx]);
+  }
+}
+
+/**
+  Reads firmware configuration bytes into a buffer
+
+  @param[in] Size - Size in bytes to read
+  @param[in] Buffer - Buffer to store data into  (OPTIONAL if Size is 0)
+**/
+VOID
+EFIAPI
+InternalQemuFwCfgReadBytes (
+  IN UINTN  Size,
+  IN VOID   *Buffer  OPTIONAL
+  )
+{
+  if ((InternalQemuFwCfgDmaIsAvailable ()) &&
+      (Size <= MAX_UINT32))
+  {
+    InternalQemuFwCfgDmaBytes ((UINT32)Size, Buffer, FW_CFG_DMA_CTL_READ);
+    return;
+  }
+
+  MmioReadBytes (Size, Buffer);
+}
+
+/**
+  Reads firmware configuration bytes into a buffer
+
+  If called multiple times, then the data read will
+  continue at the offset of the firmware configuration
+  item where the previous read ended.
+
+  @param[in] Size - Size in bytes to read
+  @param[in] Buffer - Buffer to store data into
+**/
+VOID
+EFIAPI
+QemuFwCfgReadBytes (
+  IN UINTN  Size,
+  IN VOID   *Buffer
+  )
+{
+  if (InternalQemuFwCfgIsAvailable ()) {
+    InternalQemuFwCfgReadBytes (Size, Buffer);
+  } else {
+    ZeroMem (Buffer, Size);
+  }
+}
+
+/**
+  Write firmware configuration bytes from a buffer
+
+  If called multiple times, then the data written will
+  continue at the offset of the firmware configuration
+  item where the previous write ended.
+
+  @param[in] Size - Size in bytes to write
+  @param[in] Buffer - Buffer to read data from
+**/
+VOID
+EFIAPI
+QemuFwCfgWriteBytes (
+  IN UINTN  Size,
+  IN VOID   *Buffer
+  )
+{
+  if (InternalQemuFwCfgIsAvailable ()) {
+    if ((InternalQemuFwCfgDmaIsAvailable ()) &&
+        (Size <= MAX_UINT32))
+    {
+      InternalQemuFwCfgDmaBytes ((UINT32)Size, Buffer, FW_CFG_DMA_CTL_WRITE);
+      return;
+    }
+
+    MmioWriteBytes (Size, Buffer);
+  }
+}
+
+/**
+  Skip bytes in the firmware configuration item.
+
+  Increase the offset of the firmware configuration item without transferring
+  bytes between the item and a caller-provided buffer. Subsequent read, write
+  or skip operations will commence at the increased offset.
+
+  @param[in] Size  Number of bytes to skip.
+**/
+VOID
+EFIAPI
+QemuFwCfgSkipBytes (
+  IN UINTN  Size
+  )
+{
+  UINTN  ChunkSize;
+  UINT8  SkipBuffer[256];
+
+  if (!InternalQemuFwCfgIsAvailable ()) {
+    return;
+  }
+
+  if ((InternalQemuFwCfgDmaIsAvailable ()) &&
+      (Size <= MAX_UINT32))
+  {
+    InternalQemuFwCfgDmaBytes ((UINT32)Size, NULL, FW_CFG_DMA_CTL_SKIP);
+    return;
+  }
+
+  //
+  // Emulate the skip by reading data in chunks, and throwing it away. The
+  // implementation below is suitable even for phases where RAM or dynamic
+  // allocation is not available or appropriate. It also doesn't affect the
+  // static data footprint for client modules. Large skips are not expected,
+  // therefore this fallback is not performance critical. The size of
+  // SkipBuffer is thought not to exert a large pressure on the stack in any
+  // phase.
+  //
+  while (Size > 0) {
+    ChunkSize = MIN (Size, sizeof SkipBuffer);
+    MmioReadBytes (ChunkSize, SkipBuffer);
+    Size -= ChunkSize;
+  }
+}
+
+/**
+  Reads a UINT8 firmware configuration value
+
+  @return    Value of Firmware Configuration item read
+**/
+UINT8
+EFIAPI
+QemuFwCfgRead8 (
+  VOID
+  )
+{
+  UINT8  Result;
+
+  QemuFwCfgReadBytes (sizeof (Result), &Result);
+
+  return Result;
+}
+
+/**
+  Reads a UINT16 firmware configuration value
+
+  @return    Value of Firmware Configuration item read
+**/
+UINT16
+EFIAPI
+QemuFwCfgRead16 (
+  VOID
+  )
+{
+  UINT16  Result;
+
+  QemuFwCfgReadBytes (sizeof (Result), &Result);
+
+  return Result;
+}
+
+/**
+  Reads a UINT32 firmware configuration value
+
+  @return    Value of Firmware Configuration item read
+**/
+UINT32
+EFIAPI
+QemuFwCfgRead32 (
+  VOID
+  )
+{
+  UINT32  Result;
+
+  QemuFwCfgReadBytes (sizeof (Result), &Result);
+
+  return Result;
+}
+
+/**
+  Reads a UINT64 firmware configuration value
+
+  @return    Value of Firmware Configuration item read
+**/
+UINT64
+EFIAPI
+QemuFwCfgRead64 (
+  VOID
+  )
+{
+  UINT64  Result;
+
+  QemuFwCfgReadBytes (sizeof (Result), &Result);
+
+  return Result;
+}
+
+/**
+  Find the configuration item corresponding to the firmware configuration file.
+
+  @param[in]  Name - Name of file to look up.
+  @param[out] Item - Configuration item corresponding to the file, to be passed
+                     to QemuFwCfgSelectItem ().
+  @param[out] Size - Number of bytes in the file.
+
+  @return    RETURN_SUCCESS       If file is found.
+             RETURN_NOT_FOUND     If file is not found.
+             RETURN_UNSUPPORTED   If firmware configuration is unavailable.
+**/
+RETURN_STATUS
+EFIAPI
+QemuFwCfgFindFile (
+  IN   CONST CHAR8           *Name,
+  OUT  FIRMWARE_CONFIG_ITEM  *Item,
+  OUT  UINTN                 *Size
+  )
+{
+  UINT32  Count;
+  UINT32  Idx;
+
+  if (!InternalQemuFwCfgIsAvailable ()) {
+    return RETURN_UNSUPPORTED;
+  }
+
+  QemuFwCfgSelectItem (QemuFwCfgItemFileDir);
+  Count = SwapBytes32 (QemuFwCfgRead32 ());
+
+  for (Idx = 0; Idx < Count; ++Idx) {
+    UINT32  FileSize;
+    UINT16  FileSelect;
+    CHAR8   FileName[QEMU_FW_CFG_FNAME_SIZE];
+
+    FileSize   = QemuFwCfgRead32 ();
+    FileSelect = QemuFwCfgRead16 ();
+    QemuFwCfgRead16 (); // skip the field called "reserved"
+    InternalQemuFwCfgReadBytes (sizeof (FileName), FileName);
+
+    if (AsciiStrCmp (Name, FileName) == 0) {
+      *Item = SwapBytes16 (FileSelect);
+      *Size = SwapBytes32 (FileSize);
+      return RETURN_SUCCESS;
+    }
+  }
+
+  return RETURN_NOT_FOUND;
+}
+
+/**
+  firmware config initialize.
+
+  @param  VOID
+
+  @return    RETURN_SUCCESS  Initialization succeeded.
+**/
+RETURN_STATUS
+EFIAPI
+FdtQemuFwCfgInitialize (
+  VOID
+  )
+{
+  VOID          *DeviceTreeBase;
+  INT32         Node;
+  INT32         Prev;
+  CONST CHAR8   *Type;
+  INT32         Len;
+  CONST UINT64  *RegProp;
+  UINT64        FwCfgSelectorAddress;
+  UINT64        FwCfgDataAddress;
+  UINT64        FwCfgDataSize;
+
+  DeviceTreeBase = (VOID *)(UINTN)PcdGet64 (PcdDeviceTreeInitialBaseAddress);
+  ASSERT (DeviceTreeBase != NULL);
+  //
+  // Make sure we have a valid device tree blob
+  //
+  ASSERT (fdt_check_header (DeviceTreeBase) == 0);
+
+  for (Prev = 0; ; Prev = Node) {
+    Node = fdt_next_node (DeviceTreeBase, Prev, NULL);
+    if (Node < 0) {
+      break;
+    }
+
+    //
+    // Check for memory node
+    //
+    Type = fdt_getprop (DeviceTreeBase, Node, "compatible", &Len);
+    if ((Type) &&
+        (AsciiStrnCmp (Type, "qemu,fw-cfg-mmio", Len) == 0))
+    {
+      //
+      // Get the 'reg' property of this node. For now, we will assume
+      // two 8 byte quantities for base and size, respectively.
+      //
+      RegProp = fdt_getprop (DeviceTreeBase, Node, "reg", &Len);
+      if ((RegProp != 0) &&
+          (Len == (2 * sizeof (UINT64))))
+      {
+        FwCfgDataAddress     = SwapBytes64 (RegProp[0]);
+        FwCfgDataSize        = 8;
+        FwCfgSelectorAddress = FwCfgDataAddress + FwCfgDataSize;
+
+        mFwCfgSelectorAddress = FwCfgSelectorAddress;
+        mFwCfgDataAddress     = FwCfgDataAddress;
+
+        BuildGuidDataHob (
+          &mFwCfgSelectorAddressGuid,
+          (VOID *)&FwCfgSelectorAddress,
+          sizeof (UINT64)
+          );
+
+        BuildGuidDataHob (
+          &mFwCfgDataAddressGuid,
+          (VOID *)&FwCfgDataAddress,
+          sizeof (UINT64)
+          );
+        break;
+      } else {
+        DEBUG ((
+          DEBUG_ERROR,
+          "%a: Failed to parse FDT QemuCfg node\n",
+          __func__
+          ));
+        break;
+      }
+    }
+  }
+
+  return RETURN_SUCCESS;
+}
diff --git a/OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/FdtQemuFwCfgPeiLib.inf b/OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/FdtQemuFwCfgPeiLib.inf
new file mode 100644
index 0000000000..930933ce7d
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/FdtQemuFwCfgPeiLib.inf
@@ -0,0 +1,42 @@
+## @file
+#  initialized fw_cfg library.
+#
+#  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                      = FdtQemuFwCfgPeiLib
+  FILE_GUID                      = cdf9a9d5-7422-4dcb-b41d-607151ad320b
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = FtdQemuFwCfgLib|PEIM
+  CONSTRUCTOR                    = FdtQemuFwCfgInitialize
+
+#
+#  VALID_ARCHITECTURES           = LOONGARCH64
+#
+
+[Sources]
+  FdtQemuFwCfgPeiLib.c
+  QemuFwCfgPei.c
+
+[Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  FdtLib
+  HobLib
+  IoLib
+  MemoryAllocationLib
+
+[Pcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
diff --git a/OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/QemuFwCfgLibInternal.h b/OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/QemuFwCfgLibInternal.h
new file mode 100644
index 0000000000..983d1f4849
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/QemuFwCfgLibInternal.h
@@ -0,0 +1,73 @@
+/** @file
+  fw_cfg library implementation.
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - FwCfg   - firmWare  Configure
+**/
+
+#ifndef QEMU_FW_CFG_LIB_INTERNAL_H_
+#define QEMU_FW_CFG_LIB_INTERNAL_H_
+
+#define FW_CONFIG_SELECTOR_ADDRESS_HOB_GUID \
+  { \
+    0x3cc47b04, 0x0d3e, 0xaa64, { 0x06, 0xa6, 0x4b, 0xdc, 0x9a, 0x2c, 0x61, 0x19 } \
+  }
+
+#define FW_CONFIG_DATA_ADDRESS_HOB_GUID \
+  { \
+    0xef854788, 0x10f3, 0x8e7a, { 0x3e, 0xd0, 0x4d, 0x16, 0xc1, 0x79, 0x55, 0x2f } \
+  }
+
+/**
+  Returns a boolean indicating if the firmware configuration interface is
+  available for library-internal purposes.
+
+  This function never changes fw_cfg state.
+
+  @retval    TRUE   The interface is available internally.
+  @retval    FALSE  The interface is not available internally.
+**/
+BOOLEAN
+InternalQemuFwCfgIsAvailable (
+  VOID
+  );
+
+/**
+  Returns a boolean indicating whether QEMU provides the DMA-like access method
+  for fw_cfg.
+
+  @retval    TRUE   The DMA-like access method is available.
+  @retval    FALSE  The DMA-like access method is unavailable.
+**/
+BOOLEAN
+InternalQemuFwCfgDmaIsAvailable (
+  VOID
+  );
+
+/**
+  Transfer an array of bytes, or skip a number of bytes, using the DMA
+  interface.
+
+  @param[in]     Size     Size in bytes to transfer or skip.
+
+  @param[in,out] Buffer   Buffer to read data into or write data from. Ignored,
+                          and may be NULL, if Size is zero, or Control is
+                          FW_CFG_DMA_CTL_SKIP.
+
+  @param[in]     Control  One of the following:
+                          FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer.
+                          FW_CFG_DMA_CTL_READ  - read from fw_cfg into Buffer.
+                          FW_CFG_DMA_CTL_SKIP  - skip bytes in fw_cfg.
+**/
+VOID
+InternalQemuFwCfgDmaBytes (
+  IN     UINT32  Size,
+  IN OUT VOID    *Buffer OPTIONAL,
+  IN     UINT32  Control
+  );
+
+#endif // QEMU_FW_CFG_LIB_INTERNAL_H_
diff --git a/OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/QemuFwCfgPei.c b/OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/QemuFwCfgPei.c
new file mode 100644
index 0000000000..74e778aac7
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/QemuFwCfgPei.c
@@ -0,0 +1,117 @@
+/** @file
+  fw_cfg library implementation.
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - FwCfg   - firmWare  Configure
+**/
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/QemuFwCfgLib.h>
+
+#include "QemuFwCfgLibInternal.h"
+
+/**
+  Returns a boolean indicating if the firmware configuration interface
+  is available or not.
+
+  This function may change fw_cfg state.
+
+  @retval    TRUE   The interface is available
+  @retval    FALSE  The interface is not available
+**/
+BOOLEAN
+EFIAPI
+QemuFwCfgIsAvailable (
+  VOID
+  )
+{
+  UINT32  Signature;
+  UINT32  Revision;
+
+  QemuFwCfgSelectItem (QemuFwCfgItemSignature);
+  Signature = QemuFwCfgRead32 ();
+  DEBUG ((DEBUG_INFO, "FW CFG Signature: 0x%x\n", Signature));
+  QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion);
+  Revision = QemuFwCfgRead32 ();
+  DEBUG ((DEBUG_INFO, "FW CFG Revision: 0x%x\n", Revision));
+  if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) ||
+      (Revision < 1))
+  {
+    DEBUG ((DEBUG_INFO, "QemuFwCfg interface not supported.\n"));
+    return FALSE;
+  }
+
+  DEBUG ((DEBUG_INFO, "QemuFwCfg interface is supported.\n"));
+  return TRUE;
+}
+
+/**
+  Returns a boolean indicating if the firmware configuration interface is
+  available for library-internal purposes.
+
+  This function never changes fw_cfg state.
+
+  @retval    TRUE   The interface is available internally.
+  @retval    FALSE  The interface is not available internally.
+**/
+BOOLEAN
+InternalQemuFwCfgIsAvailable (
+  VOID
+  )
+{
+  //
+  // We always return TRUE, because the consumer of this library ought to have
+  // called QemuFwCfgIsAvailable before making other calls which would hit this
+  // path.
+  //
+  return TRUE;
+}
+
+/**
+  Returns a boolean indicating whether QEMU provides the DMA-like access method
+  for fw_cfg.
+
+  @retval    TRUE   The DMA-like access method is available.
+  @retval    FALSE  The DMA-like access method is unavailable.
+**/
+BOOLEAN
+InternalQemuFwCfgDmaIsAvailable (
+  VOID
+  )
+{
+  return FALSE;
+}
+
+/**
+  Transfer an array of bytes, or skip a number of bytes, using the DMA
+  interface.
+
+  @param[in]     Size     Size in bytes to transfer or skip.
+
+  @param[in, out] Buffer   Buffer to read data into or write data from. Ignored,
+                          and may be NULL, if Size is zero, or Control is
+                          FW_CFG_DMA_CTL_SKIP.
+
+  @param[in]     Control  One of the following:
+                          FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer.
+                          FW_CFG_DMA_CTL_READ  - read from fw_cfg into Buffer.
+                          FW_CFG_DMA_CTL_SKIP  - skip bytes in fw_cfg.
+**/
+VOID
+InternalQemuFwCfgDmaBytes (
+  IN     UINT32  Size,
+  IN OUT VOID    *Buffer OPTIONAL,
+  IN     UINT32  Control
+  )
+{
+  //
+  // We should never reach here
+  //
+  ASSERT (FALSE);
+  CpuDeadLoop ();
+}
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114558): https://edk2.groups.io/g/devel/message/114558
Mute This Topic: https://groups.io/mt/103971682/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 33/37] OvmfPkg/LoongArchVirt: Add reset system library
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (31 preceding siblings ...)
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 32/37] OvmfPkg/LoongArchVirt: Add FdtQemuFwCfgLib Chao Li
@ 2024-01-26  6:31 ` Chao Li
  2024-01-26  6:31 ` [edk2-devel] [PATCH v8 34/37] OvmfPkg/LoongArchVirt: Support SEC phase Chao Li
                   ` (8 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:31 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Laszlo Ersek, Jiewen Yao, Jordan Justen,
	Gerd Hoffmann, Bibo Mao, Dongyan Qian, Xianglai Li

This library provides interface related to restart and shudown the
LoongArch64 virtual machine.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Bibo Mao <maobibo@loongson.cn>
Cc: Dongyan Qian <qiandongyan@loongson.cn>
Signed-off-by: Chao Li <lichao@loongson.cn>
Co-authored-by: Xianglai Li <lixianglai@loongson.cn>
Co-authored-by: Bibo Mao <maobibo@loongson.cn>
Reviewed-by: Bibo Mao <maobibo@loongson.cn>
---
 .../BaseResetSystemAcpiGed.c                  | 148 ++++++++++
 .../BaseResetSystemAcpiGedLib.inf             |  36 +++
 .../DxeResetSystemAcpiGed.c                   | 259 ++++++++++++++++++
 .../DxeResetSystemAcpiGedLib.inf              |  41 +++
 .../ResetSystemAcpiLib/ResetSystemAcpiGed.c   | 125 +++++++++
 .../ResetSystemAcpiLib/ResetSystemAcpiGed.h   |  23 ++
 6 files changed, 632 insertions(+)
 create mode 100644 OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGed.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGedLib.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGed.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGedLib.inf
 create mode 100644 OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.c
 create mode 100644 OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.h

diff --git a/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGed.c b/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGed.c
new file mode 100644
index 0000000000..0d94a62a38
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGed.c
@@ -0,0 +1,148 @@
+/** @file
+  Base ResetSystem library implementation.
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include "ResetSystemAcpiGed.h"
+
+/**
+  Get configuration item data by the firmware configuration file name.
+
+  @param[in]  Name - Name of file to look up.
+
+  @return    VOID*       The Pointer of Value of Firmware Configuration item read.
+**/
+VOID *
+GetFwCfgData (
+  CONST CHAR8  *Name
+  )
+{
+  FIRMWARE_CONFIG_ITEM  FwCfgItem;
+  EFI_STATUS            Status;
+  UINTN                 FwCfgSize;
+  VOID                  *Data;
+
+  Status = QemuFwCfgFindFile (Name, &FwCfgItem, &FwCfgSize);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a %d read  %s error Status %d \n", __func__, __LINE__, Name, Status));
+    return NULL;
+  }
+
+  Data = AllocatePool (FwCfgSize);
+  if (Data == NULL) {
+    return NULL;
+  }
+
+  QemuFwCfgSelectItem (FwCfgItem);
+  QemuFwCfgReadBytes (FwCfgSize, Data);
+
+  return Data;
+}
+
+/**
+  Find the power manager related info from ACPI table
+
+  @retval RETURN_SUCCESS     Successfully find out all the required information.
+  @retval RETURN_NOT_FOUND   Failed to find the required info.
+**/
+STATIC EFI_STATUS
+GetPowerManagerByParseAcpiInfo (
+  VOID
+  )
+{
+  EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE     *Fadt       = NULL;
+  EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER  *Rsdp       = NULL;
+  EFI_ACPI_DESCRIPTION_HEADER                   *Xsdt       = NULL;
+  EFI_ACPI_DESCRIPTION_HEADER                   *Rsdt       = NULL;
+  VOID                                          *AcpiTables = NULL;
+  UINT32                                        *Entry32    = NULL;
+  UINTN                                         Entry32Num;
+  UINT32                                        *Signature = NULL;
+  UINTN                                         Idx;
+
+  Rsdp = GetFwCfgData ("etc/acpi/rsdp");
+  if (Rsdp == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a %d read etc/acpi/rsdp error \n", __func__, __LINE__));
+    return RETURN_NOT_FOUND;
+  }
+
+  AcpiTables = GetFwCfgData ("etc/acpi/tables");
+  if (AcpiTables == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a %d read etc/acpi/tables error \n", __func__, __LINE__));
+    FreePool (Rsdp);
+    return RETURN_NOT_FOUND;
+  }
+
+  Rsdt       = (EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)AcpiTables +  Rsdp->RsdtAddress);
+  Entry32    = (UINT32 *)(Rsdt + 1);
+  Entry32Num = (Rsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) >> 2;
+  for (Idx = 0; Idx < Entry32Num; Idx++) {
+    Signature = (UINT32 *)((UINTN)Entry32[Idx] + (UINTN)AcpiTables);
+    if (*Signature == EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
+      Fadt = (EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE *)Signature;
+      DEBUG ((DEBUG_INFO, "Found Fadt in Rsdt\n"));
+      goto Done;
+    }
+  }
+
+  Xsdt       = (EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)AcpiTables +  Rsdp->XsdtAddress);
+  Entry32    = (UINT32 *)(Xsdt + 1);
+  Entry32Num = (Xsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) >> 2;
+  for (Idx = 0; Idx < Entry32Num; Idx++) {
+    Signature = (UINT32 *)((UINTN)Entry32[Idx] + (UINTN)AcpiTables);
+    if (*Signature == EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
+      Fadt = (EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE *)Signature;
+      DEBUG ((DEBUG_INFO, "Found Fadt in Xsdt\n"));
+      goto Done;
+    }
+  }
+
+  FreePool (Rsdp);
+  FreePool (AcpiTables);
+  DEBUG ((DEBUG_ERROR, " Fadt Not Found\n"));
+  return RETURN_NOT_FOUND;
+
+Done:
+  mPowerManager.ResetRegAddr        = Fadt->ResetReg.Address;
+  mPowerManager.ResetValue          = Fadt->ResetValue;
+  mPowerManager.SleepControlRegAddr = Fadt->SleepControlReg.Address;
+  mPowerManager.SleepStatusRegAddr  = Fadt->SleepStatusReg.Address;
+
+  FreePool (Rsdp);
+  FreePool (AcpiTables);
+  return RETURN_SUCCESS;
+}
+
+/**
+  The constructor function to initialize mPowerManager.
+
+  @retval EFI_SUCCESS   initialize mPowerManager success.
+  @retval RETURN_NOT_FOUND   Failed to initialize mPowerManager.
+**/
+EFI_STATUS
+ResetSystemLibConstructor (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+
+  Status = GetPowerManagerByParseAcpiInfo ();
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "%a:%d\n", __func__, __LINE__));
+  }
+
+  ASSERT (mPowerManager.SleepControlRegAddr);
+  ASSERT (mPowerManager.SleepStatusRegAddr);
+  ASSERT (mPowerManager.ResetRegAddr);
+  return Status;
+}
diff --git a/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGedLib.inf b/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGedLib.inf
new file mode 100644
index 0000000000..e163b09284
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGedLib.inf
@@ -0,0 +1,36 @@
+## @file
+#  Base library instance for ResetSystem library class for LoongArch
+#
+#  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                      = ResetSystemLib
+  FILE_GUID                      = BA521997-9016-32B5-65DF-EA5F560A3837
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = ResetSystemLib|SEC PEI_CORE PEIM DXE_CORE
+  CONSTRUCTOR                    = ResetSystemLibConstructor
+
+#
+#  VALID_ARCHITECTURES           = LOONGARCH64
+#
+
+[Sources]
+  BaseResetSystemAcpiGed.c
+  ResetSystemAcpiGed.c
+
+[Packages]
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  DebugLib
+  IoLib
+  MemoryAllocationLib
+  QemuFwCfgLib
diff --git a/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGed.c b/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGed.c
new file mode 100644
index 0000000000..c3256ee4ec
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGed.c
@@ -0,0 +1,259 @@
+/** @file
+  Dxe ResetSystem library implementation.
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Base.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiRuntimeLib.h> // EfiConvertPointer()
+#include "ResetSystemAcpiGed.h"
+
+/**
+  Modifies the attributes to Runtime type for a page size memory region.
+
+  @param  BaseAddress            Specified start address
+
+  @retval EFI_SUCCESS           The attributes were set for the memory region.
+  @retval EFI_INVALID_PARAMETER Length is zero.
+  @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory
+                                resource range specified by BaseAddress and Length.
+  @retval EFI_UNSUPPORTED       The bit mask of attributes is not support for the memory resource
+                                range specified by BaseAddress and Length.
+  @retval EFI_ACCESS_DEFINED    The attributes for the memory resource range specified by
+                                BaseAddress and Length cannot be modified.
+  @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of
+                                the memory resource range.
+  @retval EFI_NOT_AVAILABLE_YET The attributes cannot be set because CPU architectural protocol is
+                                not available yet.
+**/
+EFI_STATUS
+SetMemoryAttributesRunTime (
+  UINTN  Address
+  )
+{
+  EFI_STATUS                       Status;
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR  Descriptor;
+
+  Address &= ~EFI_PAGE_MASK;
+
+  Status = gDS->GetMemorySpaceDescriptor (Address, &Descriptor);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "%a: GetMemorySpaceDescriptor failed\n", __func__));
+    return Status;
+  }
+
+  if (Descriptor.GcdMemoryType == EfiGcdMemoryTypeNonExistent) {
+    Status = gDS->AddMemorySpace (
+                    EfiGcdMemoryTypeMemoryMappedIo,
+                    Address,
+                    EFI_PAGE_SIZE,
+                    EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
+                    );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_INFO, "%a: AddMemorySpace failed\n", __func__));
+      return Status;
+    }
+
+    Status = gDS->SetMemorySpaceAttributes (
+                    Address,
+                    EFI_PAGE_SIZE,
+                    EFI_MEMORY_RUNTIME
+                    );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_INFO, "%a:%d SetMemorySpaceAttributes failed\n", __func__, __LINE__));
+      return Status;
+    }
+  } else if (!(Descriptor.Attributes & EFI_MEMORY_RUNTIME)) {
+    Status = gDS->SetMemorySpaceAttributes (
+                    Address,
+                    EFI_PAGE_SIZE,
+                    Descriptor.Attributes | EFI_MEMORY_RUNTIME
+                    );
+
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_INFO, "%a:%d SetMemorySpaceAttributes failed\n", __func__, __LINE__));
+      return Status;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Find the power manager related info from ACPI table
+
+  @retval RETURN_SUCCESS     Successfully find out all the required information.
+  @retval RETURN_NOT_FOUND   Failed to find the required info.
+**/
+STATIC EFI_STATUS
+GetPowerManagerByParseAcpiInfo (
+  VOID
+  )
+{
+  EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE     *Fadt    = NULL;
+  EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER  *Rsdp    = NULL;
+  EFI_ACPI_DESCRIPTION_HEADER                   *Xsdt    = NULL;
+  EFI_ACPI_DESCRIPTION_HEADER                   *Rsdt    = NULL;
+  UINT32                                        *Entry32 = NULL;
+  UINTN                                         Entry32Num;
+  UINT32                                        *Signature = NULL;
+  UINTN                                         Idx;
+  EFI_STATUS                                    Status;
+
+  Status = EfiGetSystemConfigurationTable (&gEfiAcpiTableGuid, (VOID **)&Rsdp);
+  if (EFI_ERROR (Status)) {
+    Status = EfiGetSystemConfigurationTable (&gEfiAcpi10TableGuid, (VOID **)&Rsdp);
+  }
+
+  if (EFI_ERROR (Status) || (Rsdp == NULL)) {
+    DEBUG ((DEBUG_ERROR, "EFI_ERROR or Rsdp == NULL\n"));
+    return RETURN_NOT_FOUND;
+  }
+
+  Rsdt       = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)Rsdp->RsdtAddress;
+  Entry32    = (UINT32 *)(UINTN)(Rsdt + 1);
+  Entry32Num = (Rsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) >> 2;
+  for (Idx = 0; Idx < Entry32Num; Idx++) {
+    Signature = (UINT32 *)(UINTN)Entry32[Idx];
+    if (*Signature == EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
+      Fadt = (EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE *)Signature;
+      DEBUG ((DEBUG_INFO, "Found Fadt in Rsdt\n"));
+      goto Done;
+    }
+  }
+
+  Xsdt       = (EFI_ACPI_DESCRIPTION_HEADER *)Rsdp->XsdtAddress;
+  Entry32    = (UINT32 *)(Xsdt + 1);
+  Entry32Num = (Xsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) >> 2;
+  for (Idx = 0; Idx < Entry32Num; Idx++) {
+    Signature = (UINT32 *)(UINTN)Entry32[Idx];
+    if (*Signature == EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
+      Fadt = (EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE *)Signature;
+      DEBUG ((DEBUG_INFO, "Found Fadt in Xsdt\n"));
+      goto Done;
+    }
+  }
+
+  DEBUG ((DEBUG_ERROR, " Fadt Not Found\n"));
+  return RETURN_NOT_FOUND;
+
+Done:
+  mPowerManager.ResetRegAddr        = Fadt->ResetReg.Address;
+  mPowerManager.ResetValue          = Fadt->ResetValue;
+  mPowerManager.SleepControlRegAddr = Fadt->SleepControlReg.Address;
+  mPowerManager.SleepStatusRegAddr  = Fadt->SleepStatusReg.Address;
+  return RETURN_SUCCESS;
+}
+
+/**
+  This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
+  event. It converts a pointer to a new virtual address.
+
+  @param[in] Event        Event whose notification function is being invoked.
+  @param[in] Context      Pointer to the notification function's context
+**/
+VOID
+EFIAPI
+ResetSystemLibAddressChangeEvent (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+{
+  EfiConvertPointer (0, (VOID **)&mPowerManager.SleepControlRegAddr);
+  EfiConvertPointer (0, (VOID **)&mPowerManager.SleepStatusRegAddr);
+  EfiConvertPointer (0, (VOID **)&mPowerManager.ResetRegAddr);
+}
+
+/**
+  Notification function of ACPI Table change.
+
+  This is a notification function registered on ACPI Table change event.
+  It saves the Century address stored in ACPI FADT table.
+
+  @param  Event        Event whose notification function is being invoked.
+  @param  Context      Pointer to the notification function's context.
+**/
+STATIC VOID
+AcpiNotificationEvent (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+{
+  EFI_STATUS  Status;
+
+  Status = GetPowerManagerByParseAcpiInfo ();
+  if (EFI_ERROR (Status)) {
+    return;
+  }
+
+  DEBUG ((DEBUG_INFO, "%a: sleepControl %llx\n", __func__, mPowerManager.SleepControlRegAddr));
+  ASSERT (mPowerManager.SleepControlRegAddr);
+  Status =  SetMemoryAttributesRunTime (mPowerManager.SleepControlRegAddr);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "%a:%d\n", __func__, __LINE__));
+    return;
+  }
+
+  DEBUG ((DEBUG_INFO, "%a: sleepStatus %llx\n", __func__, mPowerManager.SleepStatusRegAddr));
+  ASSERT (mPowerManager.SleepStatusRegAddr);
+  Status =  SetMemoryAttributesRunTime (mPowerManager.SleepStatusRegAddr);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "%a:%d\n", __func__, __LINE__));
+    return;
+  }
+
+  DEBUG ((DEBUG_INFO, "%a: ResetReg %llx\n", __func__, mPowerManager.ResetRegAddr));
+  ASSERT (mPowerManager.ResetRegAddr);
+  Status =  SetMemoryAttributesRunTime (mPowerManager.ResetRegAddr);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_INFO, "%a:%d\n", __func__, __LINE__));
+  }
+
+  return;
+}
+
+/**
+  The constructor function to Register ACPI Table change event and Address Change Event.
+
+  @retval EFI_SUCCESS   The constructor always returns RETURN_SUCCESS.
+**/
+EFI_STATUS
+EFIAPI
+ResetSystemLibConstructor (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+  EFI_EVENT   Event;
+  EFI_EVENT   ResetSystemVirtualNotifyEvent;
+
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_CALLBACK,
+                  AcpiNotificationEvent,
+                  NULL,
+                  &gEfiAcpiTableGuid,
+                  &Event
+                  );
+
+  //
+  // Register SetVirtualAddressMap () notify function
+  //
+  Status = gBS->CreateEvent (
+                  EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
+                  TPL_NOTIFY,
+                  ResetSystemLibAddressChangeEvent,
+                  NULL,
+                  &ResetSystemVirtualNotifyEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+  return Status;
+}
diff --git a/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGedLib.inf b/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGedLib.inf
new file mode 100644
index 0000000000..9815e2f6e7
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGedLib.inf
@@ -0,0 +1,41 @@
+## @file
+#  DXE library instance for ResetSystem library class for LoongArch.
+#
+#  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                      = ResetSystemLib
+  FILE_GUID                      = F05197D5-5827-AA61-FB2D-BC69259F17A9
+  MODULE_TYPE                    = DXE_RUNTIME_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = ResetSystemLib|DXE_DRIVER DXE_RUNTIME_DRIVER SMM_CORE DXE_SMM_DRIVER UEFI_DRIVER UEFI_APPLICATION
+  CONSTRUCTOR                    = ResetSystemLibConstructor
+
+#
+#  VALID_ARCHITECTURES           = LOONGARCH64
+#
+
+[Sources]
+  DxeResetSystemAcpiGed.c
+  ResetSystemAcpiGed.c
+
+[Packages]
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  DebugLib
+  DxeServicesTableLib
+  UefiBootServicesTableLib
+  UefiLib
+  UefiRuntimeLib
+
+[Guids]
+  gEfiAcpi10TableGuid                           ## PRODUCES           ## SystemTable
+  gEfiAcpiTableGuid                             ## PRODUCES           ## SystemTable
diff --git a/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.c b/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.c
new file mode 100644
index 0000000000..105382da35
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.c
@@ -0,0 +1,125 @@
+/** @file
+  ResetSystem library implementation.
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/BaseLib.h>        // CpuDeadLoop()
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/ResetSystemLib.h> // ResetCold()
+#include "ResetSystemAcpiGed.h"
+
+POWER_MANAGER  mPowerManager;
+
+/**
+  Calling this function causes a system-wide reset. This sets
+  all circuitry within the system to its initial state. This type of reset
+  is asynchronous to system operation and operates without regard to
+  cycle boundaries.
+
+  System reset should not return, if it returns, it means the system does
+  not support cold reset.
+**/
+STATIC VOID
+AcpiGedReset (
+  VOID
+  )
+{
+  MmioWrite8 (
+    (UINTN)mPowerManager.ResetRegAddr,
+    mPowerManager.ResetValue
+    );
+
+  CpuDeadLoop ();
+}
+
+/**
+  This function causes the system to enter a power state equivalent
+    to the ACPI S5 states.
+
+ * */
+STATIC VOID
+AcpiGedShutdown (
+  VOID
+  )
+{
+  MmioWrite8 (
+    (UINTN)mPowerManager.SleepControlRegAddr,
+    (1 << 5) /* enable bit */ |
+    (5 << 2) /* typ == S5  */
+    );
+
+  CpuDeadLoop ();
+}
+
+/**
+  This function causes a system-wide reset (cold reset), in which
+  all circuitry within the system returns to its initial state. This type of
+  reset is asynchronous to system operation and operates without regard to
+  cycle boundaries.
+
+  If this function returns, it means that the system does not support cold
+  reset.
+**/
+VOID EFIAPI
+ResetCold (
+  VOID
+  )
+{
+  AcpiGedReset ();
+}
+
+/**
+  This function causes a system-wide initialization (warm reset), in which all
+  processors are set to their initial state. Pending cycles are not corrupted.
+
+  If this function returns, it means that the system does not support warm
+  reset.
+**/
+VOID EFIAPI
+ResetWarm (
+  VOID
+  )
+{
+  AcpiGedReset ();
+}
+
+/**
+  This function causes a systemwide reset. The exact type of the reset is
+  defined by the EFI_GUID that follows the Null-terminated Unicode string passed
+  into ResetData. If the platform does not recognize the EFI_GUID in ResetData
+  the platform must pick a supported reset type to perform.The platform may
+  optionally log the parameters from any non-normal reset that occurs.
+
+  @param[in]  DataSize   The size, in bytes, of ResetData.
+  @param[in]  ResetData  The data buffer starts with a Null-terminated string,
+                         followed by the EFI_GUID.
+**/
+VOID
+EFIAPI
+ResetPlatformSpecific (
+  IN UINTN  DataSize,
+  IN VOID   *ResetData
+  )
+{
+  AcpiGedReset ();
+}
+
+/**
+  This function causes the system to enter a power state equivalent
+  to the ACPI G2/S5 or G3 states.
+
+  If this function returns, it means that the system does not support shut down
+  reset.
+**/
+VOID EFIAPI
+ResetShutdown (
+  VOID
+  )
+{
+  AcpiGedShutdown ();
+}
diff --git a/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.h b/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.h
new file mode 100644
index 0000000000..06e2572db0
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/ResetSystemAcpiGed.h
@@ -0,0 +1,23 @@
+/** @file
+  ResetSystem lib head file.
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef RESET_SYSTEM_ACPI_GED_H_
+#define RESET_SYSTEM_ACPI_GED_H_
+
+#include <Base.h>
+
+typedef struct {
+  UINT64    SleepControlRegAddr;
+  UINT64    SleepStatusRegAddr;
+  UINT64    ResetRegAddr;
+  UINT8     ResetValue;
+} POWER_MANAGER;
+
+extern POWER_MANAGER  mPowerManager;
+#endif // RESET_SYSTEM_ACPI_GED_H_
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114559): https://edk2.groups.io/g/devel/message/114559
Mute This Topic: https://groups.io/mt/103971684/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 34/37] OvmfPkg/LoongArchVirt: Support SEC phase
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (32 preceding siblings ...)
  2024-01-26  6:31 ` [edk2-devel] [PATCH v8 33/37] OvmfPkg/LoongArchVirt: Add reset system library Chao Li
@ 2024-01-26  6:31 ` Chao Li
  2024-01-26  6:31 ` [edk2-devel] [PATCH v8 35/37] OvmfPkg/LoongArchVirt: Support PEI phase Chao Li
                   ` (7 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:31 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Laszlo Ersek, Jiewen Yao, Jordan Justen,
	Gerd Hoffmann, Bibo Mao, Dongyan Qian, Xianglai Li

Add SEC code for LoongArch virtual machine.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Bibo Mao <maobibo@loongson.cn>
Cc: Dongyan Qian <qiandongyan@loongson.cn>
Signed-off-by: Chao Li <lichao@loongson.cn>
Co-authored-by: Xianglai Li <lixianglai@loongson.cn>
Co-authored-by: Bibo Mao <maobibo@loongson.cn>
Reviewed-by: Bibo Mao <maobibo@loongson.cn>
---
 OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S | 184 +++++++
 OvmfPkg/LoongArchVirt/Sec/SecMain.c           | 507 ++++++++++++++++++
 OvmfPkg/LoongArchVirt/Sec/SecMain.inf         |  53 ++
 3 files changed, 744 insertions(+)
 create mode 100644 OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S
 create mode 100644 OvmfPkg/LoongArchVirt/Sec/SecMain.c
 create mode 100644 OvmfPkg/LoongArchVirt/Sec/SecMain.inf

diff --git a/OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S b/OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S
new file mode 100644
index 0000000000..f3fa79289d
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Sec/LoongArch64/Start.S
@@ -0,0 +1,184 @@
+#------------------------------------------------------------------------------
+#
+# Start for Loongson LoongArch processor
+#
+# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#  @par Glossary:
+#    - CSR - CPU Status Register
+#    - EBASE - Exception Base Address
+#------------------------------------------------------------------------------
+#ifndef __ASSEMBLY__
+#define __ASSEMBLY__
+#endif
+
+#include <Library/BaseMemoryLib.h>
+#include <Register/LoongArch64/Csr.h>
+#include <Protocol/DebugSupport.h>
+
+#define BOOTCORE_ID  0
+//
+// For coding convenience, define the maximum valid
+// LoongArch exception.
+// Since UEFI V2.11, it will be present in DebugSupport.h.
+//
+#define MAX_LOONGARCH_EXCEPTION  64
+
+ASM_GLOBAL ASM_PFX(_ModuleEntryPoint)
+ASM_PFX(_ModuleEntryPoint):
+  /* Disable global interrupt */
+  bl       DisableInterrupts
+
+  /* Disable all local interrupt */
+  li.w     $a0, 0x1FFF
+  bl       DisableLocalInterrupts
+
+  /* Read physical cpu number id */
+  bl       GetApicId
+  li.d     $t0, BOOTCORE_ID  //0
+  bne      $a0, $t0, SlaveMain
+
+  /* Set BSP stack */
+  li.d     $t0, FixedPcdGet64(PcdOvmfSecPeiTempRamBase) + FixedPcdGet32(PcdOvmfSecPeiTempRamSize)  # stack base
+  move     $sp, $t0
+  addi.d   $sp, $sp, -0x8
+
+  /* Load the exception vector base address */
+  li.d     $s0, FixedPcdGet64(PcdCpuExceptionVectorBaseAddress)
+
+  /* Construct SEC and PEI step exception environment */
+  la.pcrel $a1, ExceptionEntryStart
+  la.pcrel $t0, ExceptionEntryEnd
+  sub.d    $a2, $t0, $a1
+  li.w     $t0, (MAX_LOONGARCH_EXCEPTION +  MAX_LOONGARCH_INTERRUPT) * 512
+  bgeu     $a2, $t0, DeadLoop
+  move     $a0, $s0
+  bl       CopyMem
+
+  /* Configure BSP reset ebase */
+  move     $a0, $s0
+  bl       SetExceptionBaseAddress
+
+CallEntry:
+  /* Call C function make sure parameter true */
+  li.d     $a0, FixedPcdGet64(PcdOvmfFdBaseAddress) # FW base
+  addi.d   $a1, $sp, 0x8
+  bl       SecCoreStartupWithStack
+# End of _ModuleEntryPoint
+
+ASM_PFX(ClearMailBox):
+  /* Clear mailbox */
+  li.d      $t1, LOONGARCH_IOCSR_MBUF3
+  iocsrwr.d $zero, $t1
+  li.d      $t1, LOONGARCH_IOCSR_MBUF2
+  iocsrwr.d $zero, $t1
+  li.d      $t1, LOONGARCH_IOCSR_MBUF1
+  iocsrwr.d $zero, $t1
+  li.d      $t1, LOONGARCH_IOCSR_MBUF0
+  iocsrwr.d $zero, $t1
+  jirl      $zero, $ra, 0
+# End of ClearMailBox
+
+ASM_PFX(EnableIPI):
+  /* Enable IPI interrupt */
+  li.w      $t1, BIT12
+  csrxchg   $t1, $t1, LOONGARCH_CSR_ECFG
+
+  li.w      $t2, 0xFFFFFFFFU
+  li.d      $t1, LOONGARCH_IOCSR_IPI_EN
+  iocsrwr.w $t2, $t1
+  jirl      $zero, $ra, 0
+# End of EeableIPI
+
+#/**
+#   Get APIC ID for every CPU.
+#
+#   @param   NULL
+#   @return  APICID
+#
+#   UINTN
+#   EFI_API
+#   GetApicId (
+#     VOID
+#     )
+#**/
+ASM_PFX(GetApicId):
+  csrrd $a0, LOONGARCH_CSR_CPUNUM
+  andi  $a0, $a0, 0x3ff
+  jirl  $zero, $ra, 0
+# End of GetApicId
+
+ASM_PFX(ApInitStack):
+  li.d   $t1, SIZE_1KB
+  csrrd  $t0, LOONGARCH_CSR_TMID
+  mul.d  $t1, $t0, $t1
+  li.d   $t2, FixedPcdGet32(PcdCpuMaxLogicalProcessorNumber)
+  bgeu   $t0, $t2, DeadLoop
+  li.d   $t0, FixedPcdGet64(PcdOvmfSecPeiTempRamBase) + FixedPcdGet32(PcdOvmfSecPeiTempRamSize) - SIZE_64KB
+  sub.d  $sp, $t0, $t1
+  addi.d $sp, $sp, -0x8
+  jirl   $zero, $ra, 0
+# End of ApInitStack
+
+ASM_PFX(SlaveMain):
+  /* Set AP exception handle in flash */
+  la.pcrel  $a0, ApException
+  bl        SetExceptionBaseAddress
+
+  /* Clean up local mail box and open INT */
+  bl        ClearMailBox
+  bl        EnableIPI
+  bl        EnableInterrupts
+
+WaitForWake:
+  /* Wait for wakeup */
+  bl        CpuSleep
+  b         WaitForWake
+# End of SlaveMain
+
+.align 12
+ASM_PFX(ApException):
+  csrrd     $t0, LOONGARCH_CSR_ESTAT
+  srli.d    $t0, $t0, 12
+  andi      $t0, $t0, 0x1
+  beqz      $t0, DeadLoop
+
+  li.d      $t0, LOONGARCH_IOCSR_IPI_STATUS
+  iocsrrd.w $t1, $t0
+  li.d      $t0, LOONGARCH_IOCSR_IPI_CLEAR
+  iocsrwr.w $t1, $t0
+
+  /* Read mail buf and jump to specified entry */
+  li.d      $t1, LOONGARCH_IOCSR_MBUF0
+  iocsrrd.d $t0, $t1
+  beqz      $t0, OutOfException
+  csrwr     $t0, LOONGARCH_CSR_ERA
+  li.d      $t0, LOONGARCH_IOCSR_MBUF3
+  iocsrrd.d $a1, $t0
+  bl        ClearMailBox
+  beqz      $a1, NoParameterCall
+
+  //
+  // If the parameters are not NULL, then calling happened in FW ENV.
+  // Set the EBASE to be the same as BSP.
+  //
+  li.d      $a0, FixedPcdGet64(PcdCpuExceptionVectorBaseAddress)
+  bl        SetExceptionBaseAddress
+
+  bl        ApInitStack
+  bl        GetApicId
+  b         OutOfException
+NoParameterCall:
+  li.w      $t0, BIT2 // IE
+  csrxchg   $zero, $t0, LOONGARCH_CSR_PRMD // Clean PIE
+
+OutOfException:
+  ertn
+# End of ApException
+
+ASM_PFX(DeadLoop):
+  b   DeadLoop
+# End of DeadLoop
+.end
diff --git a/OvmfPkg/LoongArchVirt/Sec/SecMain.c b/OvmfPkg/LoongArchVirt/Sec/SecMain.c
new file mode 100644
index 0000000000..a7f4324811
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Sec/SecMain.c
@@ -0,0 +1,507 @@
+/** @file
+  Main SEC phase code.  Transitions to PEI.
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiPei.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugAgentLib.h>
+#include <Library/DebugLib.h>
+#include <Library/ExtractGuidedSectionLib.h>
+#include <Library/IoLib.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PeCoffLib.h>
+#include <Library/PeCoffGetEntryPointLib.h>
+#include <Library/PeCoffExtraActionLib.h>
+#include <Library/PeimEntryPoint.h>
+
+#include <Ppi/TemporaryRamSupport.h>
+
+/**
+  temporary memory to permanent memory and do stack switching.
+
+  @param[in]  PeiServices     Pointer to the PEI Services Table.
+  @param[in]  TemporaryMemoryBase    Temporary Memory Base address.
+  @param[in]  PermanentMemoryBase   Permanent Memory Base address.
+  @param[in]  CopySize   The size of memory that needs to be migrated.
+
+  @retval   EFI_SUCCESS  Migration successful.
+**/
+EFI_STATUS
+EFIAPI
+TemporaryRamMigration (
+  IN CONST EFI_PEI_SERVICES  **PeiServices,
+  IN EFI_PHYSICAL_ADDRESS    TemporaryMemoryBase,
+  IN EFI_PHYSICAL_ADDRESS    PermanentMemoryBase,
+  IN UINTN                   CopySize
+  );
+
+EFI_PEI_TEMPORARY_RAM_SUPPORT_PPI  mTemporaryRamSupportPpi = {
+  TemporaryRamMigration
+};
+
+EFI_PEI_PPI_DESCRIPTOR  mPrivateDispatchTable[] = {
+  {
+    (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+    &gEfiTemporaryRamSupportPpiGuid,
+    &mTemporaryRamSupportPpi
+  },
+};
+
+/**
+  Locates a section within a series of sections
+  with the specified section type.
+
+  The Instance parameter indicates which instance of the section
+  type to return. (0 is first instance, 1 is second...)
+
+  @param[in]   Sections        The sections to search
+  @param[in]   SizeOfSections  Total size of all sections
+  @param[in]   SectionType     The section type to locate
+  @param[in]   Instance        The section instance number
+  @param[out]  FoundSection    The FFS section if found
+
+  @retval EFI_SUCCESS           The file and section was found
+  @retval EFI_NOT_FOUND         The file and section was not found
+  @retval EFI_VOLUME_CORRUPTED  The firmware volume was corrupted
+**/
+EFI_STATUS
+FindFfsSectionInstance (
+  IN  VOID                       *Sections,
+  IN  UINTN                      SizeOfSections,
+  IN  EFI_SECTION_TYPE           SectionType,
+  IN  UINTN                      Instance,
+  OUT EFI_COMMON_SECTION_HEADER  **FoundSection
+  )
+{
+  EFI_PHYSICAL_ADDRESS       CurrentAddress;
+  UINT32                     Size;
+  EFI_PHYSICAL_ADDRESS       EndOfSections;
+  EFI_COMMON_SECTION_HEADER  *Section;
+  EFI_PHYSICAL_ADDRESS       EndOfSection;
+
+  //
+  // Loop through the FFS file sections within the PEI Core FFS file
+  //
+  EndOfSection  = (EFI_PHYSICAL_ADDRESS)(UINTN)Sections;
+  EndOfSections = EndOfSection + SizeOfSections;
+  for ( ; ; ) {
+    if (EndOfSection == EndOfSections) {
+      break;
+    }
+
+    CurrentAddress = (EndOfSection + 3) & ~(3ULL);
+    if (CurrentAddress >= EndOfSections) {
+      return EFI_VOLUME_CORRUPTED;
+    }
+
+    Section = (EFI_COMMON_SECTION_HEADER *)(UINTN)CurrentAddress;
+
+    Size = SECTION_SIZE (Section);
+    if (Size < sizeof (*Section)) {
+      return EFI_VOLUME_CORRUPTED;
+    }
+
+    EndOfSection = CurrentAddress + Size;
+    if (EndOfSection > EndOfSections) {
+      return EFI_VOLUME_CORRUPTED;
+    }
+
+    //
+    // Look for the requested section type
+    //
+    if (Section->Type == SectionType) {
+      if (Instance == 0) {
+        *FoundSection = Section;
+        return EFI_SUCCESS;
+      } else {
+        Instance--;
+      }
+    }
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  Locates a section within a series of sections
+  with the specified section type.
+
+  @param[in]   Sections        The sections to search
+  @param[in]   SizeOfSections  Total size of all sections
+  @param[in]   SectionType     The section type to locate
+  @param[out]  FoundSection    The FFS section if found
+
+  @retval EFI_SUCCESS           The file and section was found
+  @retval EFI_NOT_FOUND         The file and section was not found
+  @retval EFI_VOLUME_CORRUPTED  The firmware volume was corrupted
+**/
+EFI_STATUS
+FindFfsSectionInSections (
+  IN  VOID                       *Sections,
+  IN  UINTN                      SizeOfSections,
+  IN  EFI_SECTION_TYPE           SectionType,
+  OUT EFI_COMMON_SECTION_HEADER  **FoundSection
+  )
+{
+  return FindFfsSectionInstance (
+           Sections,
+           SizeOfSections,
+           SectionType,
+           0,
+           FoundSection
+           );
+}
+
+/**
+  Locates a FFS file with the specified file type and a section
+  within that file with the specified section type.
+
+  @param[in]   Fv            The firmware volume to search
+  @param[in]   FileType      The file type to locate
+  @param[in]   SectionType   The section type to locate
+  @param[out]  FoundSection  The FFS section if found
+
+  @retval EFI_SUCCESS           The file and section was found
+  @retval EFI_NOT_FOUND         The file and section was not found
+  @retval EFI_VOLUME_CORRUPTED  The firmware volume was corrupted
+**/
+EFI_STATUS
+FindFfsFileAndSection (
+  IN  EFI_FIRMWARE_VOLUME_HEADER  *Fv,
+  IN  EFI_FV_FILETYPE             FileType,
+  IN  EFI_SECTION_TYPE            SectionType,
+  OUT EFI_COMMON_SECTION_HEADER   **FoundSection
+  )
+{
+  EFI_STATUS            Status;
+  EFI_PHYSICAL_ADDRESS  CurrentAddress;
+  EFI_PHYSICAL_ADDRESS  EndOfFirmwareVolume;
+  EFI_FFS_FILE_HEADER   *File;
+  UINT32                Size;
+  EFI_PHYSICAL_ADDRESS  EndOfFile;
+
+  if (Fv->Signature != EFI_FVH_SIGNATURE) {
+    DEBUG ((DEBUG_ERROR, "FV at %p does not have FV header signature\n", Fv));
+    return EFI_VOLUME_CORRUPTED;
+  }
+
+  CurrentAddress      = (EFI_PHYSICAL_ADDRESS)(UINTN)Fv;
+  EndOfFirmwareVolume = CurrentAddress + Fv->FvLength;
+
+  //
+  // Loop through the FFS files in the Boot Firmware Volume
+  //
+  for (EndOfFile = CurrentAddress + Fv->HeaderLength; ; ) {
+    CurrentAddress = (EndOfFile + 7) & ~(7ULL);
+    if (CurrentAddress > EndOfFirmwareVolume) {
+      return EFI_VOLUME_CORRUPTED;
+    }
+
+    File = (EFI_FFS_FILE_HEADER *)(UINTN)CurrentAddress;
+    Size = *(UINT32 *)File->Size & 0xffffff;
+    if (Size < (sizeof (*File) + sizeof (EFI_COMMON_SECTION_HEADER))) {
+      return EFI_VOLUME_CORRUPTED;
+    }
+
+    EndOfFile = CurrentAddress + Size;
+    if (EndOfFile > EndOfFirmwareVolume) {
+      return EFI_VOLUME_CORRUPTED;
+    }
+
+    //
+    // Look for the request file type
+    //
+    if (File->Type != FileType) {
+      continue;
+    }
+
+    Status = FindFfsSectionInSections (
+               (VOID *)(File + 1),
+               (UINTN)EndOfFile - (UINTN)(File + 1),
+               SectionType,
+               FoundSection
+               );
+    if (!EFI_ERROR (Status) ||
+        (Status == EFI_VOLUME_CORRUPTED))
+    {
+      return Status;
+    }
+  }
+}
+
+/**
+  Locates the PEI Core entry point address
+
+  @param[in]  Fv                 The firmware volume to search
+  @param[out] PeiCoreEntryPoint  The entry point of the PEI Core image
+
+  @retval EFI_SUCCESS           The file and section was found
+  @retval EFI_NOT_FOUND         The file and section was not found
+  @retval EFI_VOLUME_CORRUPTED  The firmware volume was corrupted
+**/
+EFI_STATUS
+FindPeiCoreImageBaseInFv (
+  IN  EFI_FIRMWARE_VOLUME_HEADER  *Fv,
+  OUT  EFI_PHYSICAL_ADDRESS       *PeiCoreImageBase
+  )
+{
+  EFI_STATUS                 Status;
+  EFI_COMMON_SECTION_HEADER  *Section;
+
+  Status = FindFfsFileAndSection (
+             Fv,
+             EFI_FV_FILETYPE_PEI_CORE,
+             EFI_SECTION_PE32,
+             &Section
+             );
+  if (EFI_ERROR (Status)) {
+    Status = FindFfsFileAndSection (
+               Fv,
+               EFI_FV_FILETYPE_PEI_CORE,
+               EFI_SECTION_TE,
+               &Section
+               );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "Unable to find PEI Core image\n"));
+      return Status;
+    }
+  }
+
+  *PeiCoreImageBase = (EFI_PHYSICAL_ADDRESS)(UINTN)(Section + 1);
+  return EFI_SUCCESS;
+}
+
+/**
+  Find and return Pei Core entry point.
+
+  It also find SEC and PEI Core file debug information. It will report them if
+  remote debug is enabled.
+**/
+VOID
+FindAndReportEntryPoints (
+  IN  EFI_FIRMWARE_VOLUME_HEADER  **BootFirmwareVolumePtr,
+  OUT EFI_PEI_CORE_ENTRY_POINT    *PeiCoreEntryPoint
+  )
+{
+  EFI_STATUS                    Status;
+  EFI_PHYSICAL_ADDRESS          PeiCoreImageBase = 0;
+  PE_COFF_LOADER_IMAGE_CONTEXT  ImageContext;
+
+  Status = FindPeiCoreImageBaseInFv (*BootFirmwareVolumePtr, &PeiCoreImageBase);
+  ASSERT (Status == EFI_SUCCESS);
+
+  ZeroMem ((VOID *)&ImageContext, sizeof (PE_COFF_LOADER_IMAGE_CONTEXT));
+
+  //
+  // Report PEI Core debug information when remote debug is enabled
+  //
+  ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)PeiCoreImageBase;
+  ImageContext.PdbPointer   = PeCoffLoaderGetPdbPointer ((VOID *)(UINTN)ImageContext.ImageAddress);
+  PeCoffLoaderRelocateImageExtraAction (&ImageContext);
+
+  //
+  // Find PEI Core entry point
+  //
+  Status = PeCoffLoaderGetEntryPoint ((VOID *)(UINTN)PeiCoreImageBase, (VOID **)PeiCoreEntryPoint);
+  if (EFI_ERROR (Status)) {
+    *PeiCoreEntryPoint = 0;
+  }
+
+  return;
+}
+
+/**
+  Find the peicore entry point and jump to the entry point to execute.
+
+  @param[in] Context    The first input parameter of InitializeDebugAgent().
+**/
+VOID
+EFIAPI
+SecStartupPhase2 (
+  IN VOID  *Context
+  )
+{
+  EFI_SEC_PEI_HAND_OFF        *SecCoreData;
+  EFI_FIRMWARE_VOLUME_HEADER  *BootFv;
+  EFI_PEI_CORE_ENTRY_POINT    PeiCoreEntryPoint;
+
+  SecCoreData = (EFI_SEC_PEI_HAND_OFF *)Context;
+
+  //
+  // Find PEI Core entry point. It will report SEC and Pei Core debug information if remote debug
+  // is enabled.
+  //
+  BootFv = (EFI_FIRMWARE_VOLUME_HEADER *)SecCoreData->BootFirmwareVolumeBase;
+  FindAndReportEntryPoints (&BootFv, &PeiCoreEntryPoint);
+  SecCoreData->BootFirmwareVolumeBase = BootFv;
+  SecCoreData->BootFirmwareVolumeSize = (UINTN)BootFv->FvLength;
+
+  DEBUG ((DEBUG_INFO, "Find Pei EntryPoint=%p\n", PeiCoreEntryPoint));
+
+  //
+  // Transfer the control to the PEI core
+  //
+  DEBUG ((DEBUG_INFO, "SecStartupPhase2 %p\n", PeiCoreEntryPoint));
+
+  (*PeiCoreEntryPoint)(SecCoreData, (EFI_PEI_PPI_DESCRIPTOR *)&mPrivateDispatchTable);
+
+  //
+  // If we get here then the PEI Core returned, which is not recoverable.
+  //
+  ASSERT (FALSE);
+  CpuDeadLoop ();
+}
+
+/**
+  Entry point to the C language phase of SEC. initialize some temporary memory and set up the stack,
+  the control is transferred to this function.
+
+  @param[in]  BootFv   The pointer to the PEI FV in memory.
+  @param[in]  TopOfCurrentStack  Top of Current Stack.
+**/
+VOID
+EFIAPI
+SecCoreStartupWithStack (
+  IN EFI_FIRMWARE_VOLUME_HEADER  *BootFv,
+  IN VOID                        *TopOfCurrentStack
+  )
+{
+  EFI_SEC_PEI_HAND_OFF        SecCoreData;
+  EFI_FIRMWARE_VOLUME_HEADER  *BootPeiFv = (EFI_FIRMWARE_VOLUME_HEADER *)BootFv;
+
+  DEBUG ((DEBUG_INFO, "Entering C environment\n"));
+
+  ProcessLibraryConstructorList (NULL, NULL);
+
+  DEBUG ((
+    DEBUG_INFO,
+    "SecCoreStartupWithStack (0x%lx, 0x%lx)\n",
+    (UINTN)BootFv,
+    (UINTN)TopOfCurrentStack
+    ));
+  DEBUG ((
+    DEBUG_INFO,
+    "(0x%lx, 0x%lx)\n",
+    (UINTN)(FixedPcdGet64 (PcdOvmfSecPeiTempRamBase)),
+    (UINTN)(FixedPcdGet32 (PcdOvmfSecPeiTempRamSize))
+    ));
+
+  // |-------------|       <-- TopOfCurrentStack
+  // |  BSP Stack  | 32k
+  // |-------------|
+  // |  BSP Heap   | 32k
+  // |-------------|       <-- SecCoreData.TemporaryRamBase
+  // |  Ap Stack   | 384k
+  // |-------------|
+  // |  Exception  | 64k
+  // |-------------|       <-- PcdOvmfSecPeiTempRamBase
+
+  ASSERT (
+    (UINTN)(FixedPcdGet64 (PcdOvmfSecPeiTempRamBase) +
+            FixedPcdGet32 (PcdOvmfSecPeiTempRamSize)) ==
+    (UINTN)TopOfCurrentStack
+    );
+
+  //
+  // Initialize SEC hand-off state
+  //
+  SecCoreData.DataSize = sizeof (EFI_SEC_PEI_HAND_OFF);
+
+  SecCoreData.TemporaryRamSize = (UINTN)SIZE_64KB;
+  SecCoreData.TemporaryRamBase = (VOID *)(FixedPcdGet64 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize) - SecCoreData.TemporaryRamSize);
+
+  SecCoreData.PeiTemporaryRamBase = SecCoreData.TemporaryRamBase;
+  SecCoreData.PeiTemporaryRamSize = SecCoreData.TemporaryRamSize >> 1;
+
+  SecCoreData.StackBase = (UINT8 *)SecCoreData.TemporaryRamBase + SecCoreData.PeiTemporaryRamSize;
+  SecCoreData.StackSize = SecCoreData.TemporaryRamSize >> 1;
+
+  SecCoreData.BootFirmwareVolumeBase = BootPeiFv;
+  SecCoreData.BootFirmwareVolumeSize = (UINTN)BootPeiFv->FvLength;
+
+  DEBUG ((
+    DEBUG_INFO,
+    "&SecCoreData.BootFirmwareVolumeBase=%lx SecCoreData.BootFirmwareVolumeBase=%lx\n",
+    (UINT64)&(SecCoreData.BootFirmwareVolumeBase),
+    (UINT64)(SecCoreData.BootFirmwareVolumeBase)
+    ));
+  DEBUG ((
+    DEBUG_INFO,
+    "&SecCoreData.BootFirmwareVolumeSize=%lx SecCoreData.BootFirmwareVolumeSize=%lx\n",
+    (UINT64)&(SecCoreData.BootFirmwareVolumeSize),
+    (UINT64)(SecCoreData.BootFirmwareVolumeSize)
+    ));
+
+  //
+  // Initialize Debug Agent to support source level debug in SEC/PEI phases before memory ready.
+  //
+  InitializeDebugAgent (DEBUG_AGENT_INIT_PREMEM_SEC, NULL, NULL);
+  SecStartupPhase2 (&SecCoreData);
+}
+
+/**
+  temporary memory to permanent memory and do stack switching.
+
+  @param[in]  PeiServices     Pointer to the PEI Services Table.
+  @param[in]  TemporaryMemoryBase    Temporary Memory Base address.
+  @param[in]  PermanentMemoryBase   Permanent Memory Base address.
+  @param[in]  CopySize   The size of memory that needs to be migrated.
+
+  @retval   EFI_SUCCESS  Migration successful.
+**/
+EFI_STATUS
+EFIAPI
+TemporaryRamMigration (
+  IN CONST EFI_PEI_SERVICES  **PeiServices,
+  IN EFI_PHYSICAL_ADDRESS    TemporaryMemoryBase,
+  IN EFI_PHYSICAL_ADDRESS    PermanentMemoryBase,
+  IN UINTN                   CopySize
+  )
+{
+  VOID                      *OldHeap;
+  VOID                      *NewHeap;
+  VOID                      *OldStack;
+  VOID                      *NewStack;
+  BASE_LIBRARY_JUMP_BUFFER  JumpBuffer;
+
+  DEBUG ((
+    DEBUG_INFO,
+    "TemporaryRamMigration (0x%Lx, 0x%Lx, 0x%Lx)\n",
+    TemporaryMemoryBase,
+    PermanentMemoryBase,
+    (UINT64)CopySize
+    ));
+
+  OldHeap = (VOID *)(UINTN)TemporaryMemoryBase;
+  NewHeap = (VOID *)((UINTN)PermanentMemoryBase + (CopySize >> 1));
+
+  OldStack = (VOID *)((UINTN)TemporaryMemoryBase + (CopySize >> 1));
+  NewStack = (VOID *)(UINTN)PermanentMemoryBase;
+
+  //
+  // Migrate Heap
+  //
+  CopyMem (NewHeap, OldHeap, CopySize >> 1);
+
+  //
+  // Migrate Stack
+  //
+  CopyMem (NewStack, OldStack, CopySize >> 1);
+
+  // Use SetJump ()/LongJump () to switch to a new stack.
+  //
+  if (SetJump (&JumpBuffer) == 0) {
+    JumpBuffer.SP = JumpBuffer.SP - (UINTN)OldStack + (UINTN)NewStack;
+    LongJump (&JumpBuffer, (UINTN)-1);
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/LoongArchVirt/Sec/SecMain.inf b/OvmfPkg/LoongArchVirt/Sec/SecMain.inf
new file mode 100644
index 0000000000..49264ebbab
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Sec/SecMain.inf
@@ -0,0 +1,53 @@
+## @file
+#  SEC Driver
+#
+#  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                      = SecMain
+  FILE_GUID                      = 57d02d4f-5a5d-4bfa-b7d6-ba0a4d2c72ce
+  MODULE_TYPE                    = SEC
+  VERSION_STRING                 = 1.0
+
+#
+#  VALID_ARCHITECTURES           = LOONGARCH64
+#
+
+[Sources]
+  LoongArch64/Start.S
+  SecMain.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  OvmfPkg/OvmfPkg.dec
+  UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  CpuExceptionHandlerLib
+  DebugAgentLib
+  DebugLib
+  IoLib
+  PcdLib
+  PeCoffLib
+  PeCoffGetEntryPointLib
+  PeCoffExtraActionLib
+
+[Ppis]
+  gEfiTemporaryRamSupportPpiGuid                # PPI ALWAYS_PRODUCED
+
+[FixedPcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
+
+  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress
+  gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber
+
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114560): https://edk2.groups.io/g/devel/message/114560
Mute This Topic: https://groups.io/mt/103971685/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 35/37] OvmfPkg/LoongArchVirt: Support PEI phase
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (33 preceding siblings ...)
  2024-01-26  6:31 ` [edk2-devel] [PATCH v8 34/37] OvmfPkg/LoongArchVirt: Support SEC phase Chao Li
@ 2024-01-26  6:31 ` Chao Li
  2024-01-26  6:31 ` [edk2-devel] [PATCH v8 36/37] OvmfPkg/LoongArchVirt: Add build file Chao Li
                   ` (6 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:31 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Laszlo Ersek, Jiewen Yao, Jordan Justen,
	Gerd Hoffmann, Bibo Mao, Dongyan Qian, Xianglai Li

Platfrom PEI module for LoongArch platfrom initialization.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Bibo Mao <maobibo@loongson.cn>
Cc: Dongyan Qian <qiandongyan@loongson.cn>
Signed-off-by: Chao Li <lichao@loongson.cn>
Co-authored-by: Xianglai Li <lixianglai@loongson.cn>
Co-authored-by: Bibo Mao <maobibo@loongson.cn>
Reviewed-by: Bibo Mao <maobibo@loongson.cn>
---
 OvmfPkg/LoongArchVirt/PlatformPei/Fv.c        |  39 ++
 OvmfPkg/LoongArchVirt/PlatformPei/MemDetect.c | 201 +++++++++
 OvmfPkg/LoongArchVirt/PlatformPei/Platform.c  | 393 ++++++++++++++++++
 OvmfPkg/LoongArchVirt/PlatformPei/Platform.h  | 146 +++++++
 .../LoongArchVirt/PlatformPei/PlatformPei.inf |  72 ++++
 5 files changed, 851 insertions(+)
 create mode 100644 OvmfPkg/LoongArchVirt/PlatformPei/Fv.c
 create mode 100644 OvmfPkg/LoongArchVirt/PlatformPei/MemDetect.c
 create mode 100644 OvmfPkg/LoongArchVirt/PlatformPei/Platform.c
 create mode 100644 OvmfPkg/LoongArchVirt/PlatformPei/Platform.h
 create mode 100644 OvmfPkg/LoongArchVirt/PlatformPei/PlatformPei.inf

diff --git a/OvmfPkg/LoongArchVirt/PlatformPei/Fv.c b/OvmfPkg/LoongArchVirt/PlatformPei/Fv.c
new file mode 100644
index 0000000000..d46326f135
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/PlatformPei/Fv.c
@@ -0,0 +1,39 @@
+/** @file
+  Build FV related hobs for platform.
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/PcdLib.h>
+#include "Platform.h"
+
+/**
+  Publish PEI & DXE (Decompressed) Memory based FVs to let PEI
+  and DXE know about them.
+
+  @retval EFI_SUCCESS   Platform PEI FVs were initialized successfully.
+**/
+EFI_STATUS
+PeiFvInitialization (
+  VOID
+  )
+{
+  DEBUG ((DEBUG_INFO, "Platform PEI Firmware Volume Initialization\n"));
+
+  //
+  // Create a memory allocation HOB for the PEI FV.
+  //
+  BuildMemoryAllocationHob (
+    FixedPcdGet64 (PcdOvmfSecPeiTempRamBase),
+    FixedPcdGet32 (PcdOvmfSecPeiTempRamSize),
+    EfiBootServicesData
+    );
+
+  return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/LoongArchVirt/PlatformPei/MemDetect.c b/OvmfPkg/LoongArchVirt/PlatformPei/MemDetect.c
new file mode 100644
index 0000000000..9c90413524
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/PlatformPei/MemDetect.c
@@ -0,0 +1,201 @@
+/** @file
+  Memory Detection for Virtual Machines.
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+//
+// The package level header files this module uses
+//
+#include <PiPei.h>
+
+//
+// The Library classes this module consumes
+//
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/IoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PeimEntryPoint.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/ResourcePublicationLib.h>
+#include <Register/LoongArch64/Csr.h>
+#include "Platform.h"
+
+#define MAX_VIRTUAL_MEMORY_MAP_DESCRIPTORS  (128)
+#define LOONGARCH_FW_RAM_TOP                BASE_256MB
+
+/**
+  Publish PEI core memory
+
+  @return EFI_SUCCESS     The PEIM initialized successfully.
+**/
+EFI_STATUS
+PublishPeiMemory (
+  VOID
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Base;
+  UINT64      Size;
+  UINT64      RamTop;
+
+  //
+  // Determine the range of memory to use during PEI
+  //
+  Base   = FixedPcdGet64 (PcdOvmfSecPeiTempRamBase) + FixedPcdGet32 (PcdOvmfSecPeiTempRamSize);
+  RamTop = LOONGARCH_FW_RAM_TOP;
+  Size   = RamTop - Base;
+
+  //
+  // Publish this memory to the PEI Core
+  //
+  Status = PublishSystemMemory (Base, Size);
+  ASSERT_EFI_ERROR (Status);
+
+  DEBUG ((DEBUG_INFO, "Publish Memory Initialize done.\n"));
+  return Status;
+}
+
+/**
+  Peform Memory Detection
+  Publish system RAM and reserve memory regions
+**/
+VOID
+InitializeRamRegions (
+  VOID
+  )
+{
+  EFI_STATUS            Status;
+  FIRMWARE_CONFIG_ITEM  FwCfgItem;
+  UINTN                 FwCfgSize;
+  MEMMAP_ENTRY          MemoryMapEntry;
+  MEMMAP_ENTRY          *StartEntry;
+  MEMMAP_ENTRY          *pEntry;
+  UINTN                 Processed;
+
+  Status = QemuFwCfgFindFile ("etc/memmap", &FwCfgItem, &FwCfgSize);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a %d read etc/memmap error Status %d \n", __func__, __LINE__, Status));
+    return;
+  }
+
+  if (FwCfgSize % sizeof MemoryMapEntry != 0) {
+    DEBUG ((DEBUG_ERROR, "no MemoryMapEntry FwCfgSize:%d\n", FwCfgSize));
+    return;
+  }
+
+  QemuFwCfgSelectItem (FwCfgItem);
+  StartEntry = AllocatePages (EFI_SIZE_TO_PAGES (FwCfgSize));
+  QemuFwCfgReadBytes (FwCfgSize, StartEntry);
+  for (Processed = 0; Processed < (FwCfgSize / sizeof MemoryMapEntry); Processed++) {
+    pEntry = StartEntry + Processed;
+    if (pEntry->Length == 0) {
+      continue;
+    }
+
+    DEBUG ((DEBUG_INFO, "MemmapEntry Base %p length %p  type %d\n", pEntry->BaseAddr, pEntry->Length, pEntry->Type));
+    if (pEntry->Type != EfiAcpiAddressRangeMemory) {
+      continue;
+    }
+
+    AddMemoryRangeHob (pEntry->BaseAddr, pEntry->BaseAddr + pEntry->Length);
+  }
+
+  //
+  // When 0 address protection is enabled,
+  // 0-4k memory needs to be preallocated to prevent UEFI applications from allocating use,
+  // such as grub
+  //
+  if (PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT0) {
+    BuildMemoryAllocationHob (
+      0,
+      EFI_PAGE_SIZE,
+      EfiBootServicesData
+      );
+  }
+}
+
+/**
+  Gets the Virtual Memory Map of corresponding platforms.
+
+  This Virtual Memory Map is used by initialize the MMU on corresponding
+  platforms.
+
+  @param[out]   MemoryTable    Array of MEMORY_REGION_DESCRIPTOR
+                               describing a Physical-to-Virtual Memory
+                               mapping. This array must be ended by a
+                               zero-filled entry. The allocated memory
+                               will not be freed.
+**/
+VOID
+EFIAPI
+GetMemoryMapPolicy (
+  OUT MEMORY_REGION_DESCRIPTOR  **MemoryTable
+  )
+{
+  EFI_STATUS                Status;
+  FIRMWARE_CONFIG_ITEM      FwCfgItem;
+  UINTN                     FwCfgSize;
+  MEMMAP_ENTRY              MemoryMapEntry;
+  MEMMAP_ENTRY              *StartEntry;
+  MEMMAP_ENTRY              *pEntry;
+  UINTN                     Processed;
+  MEMORY_REGION_DESCRIPTOR  *VirtualMemoryTable;
+  UINTN                     Index = 0;
+
+  ASSERT (MemoryTable != NULL);
+
+  VirtualMemoryTable = AllocatePool (
+                         sizeof (MEMORY_REGION_DESCRIPTOR) *
+                         MAX_VIRTUAL_MEMORY_MAP_DESCRIPTORS
+                         );
+
+  //
+  // Add the 0x10000000-0x20000000. In the virtual machine, this area use for CPU UART, flash, PIC etc.
+  //
+  VirtualMemoryTable[Index].PhysicalBase = 0x10000000;
+  VirtualMemoryTable[Index].VirtualBase  = VirtualMemoryTable[Index].PhysicalBase;
+  VirtualMemoryTable[Index].Length       = 0x10000000;
+  VirtualMemoryTable[Index].Attributes   = PAGE_VALID | PLV_KERNEL |  CACHE_SUC | PAGE_DIRTY | PAGE_GLOBAL;
+  ++Index;
+
+  Status = QemuFwCfgFindFile ("etc/memmap", &FwCfgItem, &FwCfgSize);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a %d read etc/memmap error Status %d \n", __func__, __LINE__, Status));
+    ZeroMem (&VirtualMemoryTable[Index], sizeof (MEMORY_REGION_DESCRIPTOR));
+    *MemoryTable = VirtualMemoryTable;
+    return;
+  }
+
+  if (FwCfgSize % sizeof MemoryMapEntry != 0) {
+    DEBUG ((DEBUG_ERROR, "no MemoryMapEntry FwCfgSize:%d\n", FwCfgSize));
+  }
+
+  QemuFwCfgSelectItem (FwCfgItem);
+  StartEntry = AllocatePages (EFI_SIZE_TO_PAGES (FwCfgSize));
+  QemuFwCfgReadBytes (FwCfgSize, StartEntry);
+  for (Processed = 0; Processed < (FwCfgSize / sizeof MemoryMapEntry); Processed++) {
+    pEntry = StartEntry + Processed;
+    if (pEntry->Length == 0) {
+      continue;
+    }
+
+    DEBUG ((DEBUG_INFO, "MemmapEntry Base %p length %p  type %d\n", pEntry->BaseAddr, pEntry->Length, pEntry->Type));
+    VirtualMemoryTable[Index].PhysicalBase = pEntry->BaseAddr;
+    VirtualMemoryTable[Index].VirtualBase  = VirtualMemoryTable[Index].PhysicalBase;
+    VirtualMemoryTable[Index].Length       = pEntry->Length;
+    VirtualMemoryTable[Index].Attributes   = PAGE_VALID | PLV_KERNEL |  CACHE_CC | PAGE_DIRTY | PAGE_GLOBAL;
+    ++Index;
+  }
+
+  FreePages (StartEntry, EFI_SIZE_TO_PAGES (FwCfgSize));
+  // End of Table
+  ZeroMem (&VirtualMemoryTable[Index], sizeof (MEMORY_REGION_DESCRIPTOR));
+  *MemoryTable = VirtualMemoryTable;
+}
diff --git a/OvmfPkg/LoongArchVirt/PlatformPei/Platform.c b/OvmfPkg/LoongArchVirt/PlatformPei/Platform.c
new file mode 100644
index 0000000000..10719c4459
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/PlatformPei/Platform.c
@@ -0,0 +1,393 @@
+/** @file
+  Platform PEI driver
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+  @par Glossary:
+    - Mem - Memory
+**/
+
+//
+// The package level header files this module uses
+//
+#include <PiPei.h>
+//
+// The Library classes this module consumes
+//
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HobLib.h>
+#include <Library/IoLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/MpInitLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PeimEntryPoint.h>
+#include <Library/PeiServicesLib.h>
+#include <Library/ResourcePublicationLib.h>
+#include <Library/PlatformHookLib.h>
+#include <Guid/MemoryTypeInformation.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Guid/FdtHob.h>
+#include <libfdt.h>
+#include <Ppi/MasterBootMode.h>
+#include <Register/LoongArch64/Cpucfg.h>
+#include <Register/LoongArch64/Csr.h>
+
+#include "Platform.h"
+
+VOID
+SaveRtcRegisterAddressHob (
+  UINT64  RtcRegisterBase
+  );
+
+/* TODO */
+EFI_MEMORY_TYPE_INFORMATION  mDefaultMemoryTypeInformation[] = {
+  { EfiReservedMemoryType,  0x004 },
+  { EfiRuntimeServicesData, 0x024 },
+  { EfiRuntimeServicesCode, 0x030 },
+  { EfiBootServicesCode,    0x180 },
+  { EfiBootServicesData,    0xF00 },
+  { EfiMaxMemoryType,       0x000 }
+};
+
+//
+// Module globals
+//
+CONST EFI_PEI_PPI_DESCRIPTOR  mPpiListBootMode = {
+  (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+  &gEfiPeiMasterBootModePpiGuid,
+  NULL
+};
+
+STATIC EFI_BOOT_MODE  mBootMode = BOOT_WITH_FULL_CONFIGURATION;
+
+/**
+  Create Reserved type memory range hand off block.
+
+  @param  MemoryBase    memory base address.
+  @param  MemoryLimit  memory length.
+
+  @return  VOID
+**/
+VOID
+AddReservedMemoryBaseSizeHob (
+  EFI_PHYSICAL_ADDRESS  MemoryBase,
+  UINT64                MemorySize
+  )
+{
+  BuildResourceDescriptorHob (
+    EFI_RESOURCE_MEMORY_RESERVED,
+    EFI_RESOURCE_ATTRIBUTE_PRESENT     |
+    EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
+    EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |
+    EFI_RESOURCE_ATTRIBUTE_TESTED,
+    MemoryBase,
+    MemorySize
+    );
+}
+
+/**
+  Create system type  memory range hand off block.
+
+  @param  MemoryBase    memory base address.
+  @param  MemoryLimit  memory length.
+
+  @return  VOID
+**/
+VOID
+AddMemoryBaseSizeHob (
+  EFI_PHYSICAL_ADDRESS  MemoryBase,
+  UINT64                MemorySize
+  )
+{
+  BuildResourceDescriptorHob (
+    EFI_RESOURCE_SYSTEM_MEMORY,
+    EFI_RESOURCE_ATTRIBUTE_PRESENT |
+    EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
+    EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |
+    EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |
+    EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |
+    EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE |
+    EFI_RESOURCE_ATTRIBUTE_TESTED,
+    MemoryBase,
+    MemorySize
+    );
+}
+
+/**
+  Create  memory range hand off block.
+
+  @param  MemoryBase    memory base address.
+  @param  MemoryLimit  memory length.
+
+  @return  VOID
+**/
+VOID
+AddMemoryRangeHob (
+  EFI_PHYSICAL_ADDRESS  MemoryBase,
+  EFI_PHYSICAL_ADDRESS  MemoryLimit
+  )
+{
+  AddMemoryBaseSizeHob (MemoryBase, (UINT64)(MemoryLimit - MemoryBase));
+}
+
+/**
+  Create  memory type information hand off block.
+
+  @param  VOID
+
+  @return  VOID
+**/
+VOID
+MemMapInitialization (
+  VOID
+  )
+{
+  DEBUG ((DEBUG_INFO, "==%a==\n", __func__));
+  //
+  // Create Memory Type Information HOB
+  //
+  BuildGuidDataHob (
+    &gEfiMemoryTypeInformationGuid,
+    mDefaultMemoryTypeInformation,
+    sizeof (mDefaultMemoryTypeInformation)
+    );
+}
+
+/** Get the Rtc base address from the DT.
+
+  This function fetches the node referenced in the "loongson,ls7a-rtc"
+  property of the "reg" node and returns the base address of
+  the RTC.
+
+  @param [in]   Fdt                   Pointer to a Flattened Device Tree (Fdt).
+  @param [out]  RtcBaseAddress  If success, contains the base address
+                                      of the Rtc.
+
+  @retval EFI_SUCCESS             The function completed successfully.
+  @retval EFI_NOT_FOUND           RTC info not found in DT.
+  @retval EFI_INVALID_PARAMETER   Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+GetRtcAddress (
+  IN  CONST VOID    *Fdt,
+  OUT       UINT64  *RtcBaseAddress
+  )
+{
+  INT32         Node;
+  INT32         Prev;
+  CONST CHAR8   *Type;
+  INT32         Len;
+  CONST UINT64  *RegProp;
+  EFI_STATUS    Status;
+
+  if ((Fdt == NULL) || (fdt_check_header (Fdt) != 0)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = EFI_NOT_FOUND;
+  for (Prev = 0; ; Prev = Node) {
+    Node = fdt_next_node (Fdt, Prev, NULL);
+    if (Node < 0) {
+      break;
+    }
+
+    //
+    // Check for memory node
+    //
+    Type = fdt_getprop (Fdt, Node, "compatible", &Len);
+    if ((Type) && (AsciiStrnCmp (Type, "loongson,ls7a-rtc", Len) == 0)) {
+      //
+      // Get the 'reg' property of this node. For now, we will assume
+      // two 8 byte quantities for base and size, respectively.
+      //
+      RegProp = fdt_getprop (Fdt, Node, "reg", &Len);
+      if ((RegProp != 0) && (Len == (2 * sizeof (UINT64)))) {
+        *RtcBaseAddress = SwapBytes64 (RegProp[0]);
+        Status          = RETURN_SUCCESS;
+        DEBUG ((DEBUG_INFO, "%a Len %d RtcBase %llx\n", __func__, Len, *RtcBaseAddress));
+        break;
+      } else {
+        DEBUG ((DEBUG_ERROR, "%a: Failed to parse FDT rtc node\n", __func__));
+        break;
+      }
+    }
+  }
+
+  return Status;
+}
+
+/**
+  Misc Initialization.
+
+  @param  VOID
+
+  @return  VOID
+**/
+VOID
+MiscInitialization (
+  VOID
+  )
+{
+  CPUCFG_REG1_INFO_DATA  CpucfgReg1Data;
+  UINT8                  CpuPhysMemAddressWidth;
+
+  DEBUG ((DEBUG_INFO, "==%a==\n", __func__));
+
+  //
+  // Get the the CPU physical memory address width.
+  //
+  AsmCpucfg (CPUCFG_REG1_INFO, &CpucfgReg1Data.Uint32);
+
+  CpuPhysMemAddressWidth = (UINT8)(CpucfgReg1Data.Bits.PALEN + 1);
+
+  //
+  // Creat CPU HOBs.
+  //
+  BuildCpuHob (CpuPhysMemAddressWidth, FixedPcdGet8 (PcdPrePiCpuIoSize));
+}
+
+/**
+  add fdt hand off block.
+
+  @param  VOID
+
+  @return  VOID
+**/
+VOID
+AddFdtHob (
+  VOID
+  )
+{
+  VOID           *Base;
+  VOID           *NewBase;
+  UINTN          FdtSize;
+  UINTN          FdtPages;
+  UINT64         *FdtHobData;
+  UINT64         RtcBaseAddress;
+  RETURN_STATUS  Status;
+
+  Base = (VOID *)(UINTN)PcdGet64 (PcdDeviceTreeInitialBaseAddress);
+  ASSERT (Base != NULL);
+
+  Status = GetRtcAddress (Base, &RtcBaseAddress);
+  if (RETURN_ERROR (Status)) {
+    return;
+  }
+
+  SaveRtcRegisterAddressHob (RtcBaseAddress);
+
+  FdtSize  = fdt_totalsize (Base) + PcdGet32 (PcdDeviceTreeAllocationPadding);
+  FdtPages = EFI_SIZE_TO_PAGES (FdtSize);
+  NewBase  = AllocatePages (FdtPages);
+  ASSERT (NewBase != NULL);
+  fdt_open_into (Base, NewBase, EFI_PAGES_TO_SIZE (FdtPages));
+
+  FdtHobData = BuildGuidHob (&gFdtHobGuid, sizeof *FdtHobData);
+  ASSERT (FdtHobData != NULL);
+  *FdtHobData = (UINTN)NewBase;
+}
+
+/**
+  Fetch the size of system memory from QEMU.
+
+  @param  VOID
+
+  @return  VOID
+**/
+VOID
+ReportSystemMemorySize (
+  VOID
+  )
+{
+  UINT64  RamSize;
+
+  QemuFwCfgSelectItem (QemuFwCfgItemRamSize);
+  RamSize = QemuFwCfgRead64 ();
+  DEBUG ((
+    DEBUG_INFO,
+    "%a: QEMU reports %dM system memory\n",
+    __func__,
+    RamSize/1024/1024
+    ));
+
+  //
+  // Assert false if QEMU report system memory size is less then 256M.
+  //
+  if (RamSize <= SIZE_256MB) {
+    ASSERT (FALSE);
+  }
+}
+
+/**
+  Perform Platform PEI initialization.
+
+  @param  FileHandle      Handle of the file being invoked.
+  @param  PeiServices     Describes the list of possible PEI Services.
+
+  @return EFI_SUCCESS     The PEIM initialized successfully.
+**/
+EFI_STATUS
+EFIAPI
+InitializePlatform (
+  IN       EFI_PEI_FILE_HANDLE  FileHandle,
+  IN CONST EFI_PEI_SERVICES     **PeiServices
+  )
+{
+  EFI_STATUS                Status;
+  UINTN                     TranslationTableSize;
+  MEMORY_REGION_DESCRIPTOR  *MemoryTable;
+
+  DEBUG ((DEBUG_INFO, "Platform PEIM Loaded\n"));
+
+  Status = PeiServicesSetBootMode (mBootMode);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = PeiServicesInstallPpi (&mPpiListBootMode);
+  ASSERT_EFI_ERROR (Status);
+
+  ReportSystemMemorySize ();
+
+  PublishPeiMemory ();
+
+  PeiFvInitialization ();
+  InitializeRamRegions ();
+  MemMapInitialization ();
+
+  Status = PlatformHookSerialPortInitialize ();
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Collect numbers of on line processors and all of APs APIC ID
+  // TODO: Current, the NULL library is used, this library will be populated in the future.
+  //
+  CollectAllProcessorResource ();
+
+  MiscInitialization ();
+
+  AddFdtHob ();
+
+  GetMemoryMapPolicy (&MemoryTable);
+  Status = ConfigureMemoryManagementUnit (
+             MemoryTable,
+             NULL,
+             &TranslationTableSize
+             );
+  ASSERT_EFI_ERROR (Status);
+
+  //
+  // Open MMU.
+  //
+  DEBUG ((DEBUG_INFO, "Open MMU start.\n"));
+  CsrXChg (LOONGARCH_CSR_CRMD, BIT4, BIT4|BIT3);
+  DEBUG ((DEBUG_INFO, "Open MMU done.\n"));
+
+  MpInitLibInitialize ();
+
+  return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/LoongArchVirt/PlatformPei/Platform.h b/OvmfPkg/LoongArchVirt/PlatformPei/Platform.h
new file mode 100644
index 0000000000..0774b0e54a
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/PlatformPei/Platform.h
@@ -0,0 +1,146 @@
+/** @file
+  Platform PEI module include file.
+
+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef PLATFORM_H_
+#define PLATFORM_H_
+
+#include <IndustryStandard/E820.h>
+#include <IndustryStandard/Pci22.h>
+#include <Library/CpuMmuLib.h>
+
+typedef struct {
+  UINT64    BaseAddr;
+  UINT64    Length;
+  UINT32    Type;
+  UINT32    Reserved;
+} MEMMAP_ENTRY;
+
+VOID
+EFIAPI
+CollectAllProcessorResource (
+  VOID
+  );
+
+VOID
+EFIAPI
+SaveProcessorResource (
+  VOID
+  );
+
+/**
+  Create system type  memory range hand off block.
+
+  @param  MemoryBase    memory base address.
+  @param  MemoryLimit  memory length.
+
+  @return  VOID
+**/
+VOID
+AddMemoryBaseSizeHob (
+  EFI_PHYSICAL_ADDRESS  MemoryBase,
+  UINT64                MemorySize
+  );
+
+/**
+  Create  memory range hand off block.
+
+  @param  MemoryBase    memory base address.
+  @param  MemoryLimit  memory length.
+
+  @return  VOID
+**/
+VOID
+AddMemoryRangeHob (
+  EFI_PHYSICAL_ADDRESS  MemoryBase,
+  EFI_PHYSICAL_ADDRESS  MemoryLimit
+  );
+
+/**
+  Create Reserved type memory range hand off block.
+
+  @param  MemoryBase    memory base address.
+  @param  MemoryLimit  memory length.
+
+  @return  VOID
+**/
+VOID
+AddReservedMemoryBaseSizeHob (
+  EFI_PHYSICAL_ADDRESS  MemoryBase,
+  UINT64                MemorySize
+  );
+
+/**
+  Publish PEI core memory
+
+  @return EFI_SUCCESS     The PEIM initialized successfully.
+**/
+EFI_STATUS
+PublishPeiMemory (
+  VOID
+  );
+
+/**
+  Publish system RAM and reserve memory regions
+
+  @return  VOID
+**/
+VOID
+InitializeRamRegions (
+  VOID
+  );
+
+/**
+  Publish PEI & DXE (Decompressed) Memory based FVs to let PEI
+  and DXE know about them.
+
+  @retval EFI_SUCCESS   Platform PEI FVs were initialized successfully.
+**/
+EFI_STATUS
+PeiFvInitialization (
+  VOID
+  );
+
+/**
+  Gets the Virtual Memory Map of corresponding platforms.
+
+  This Virtual Memory Map is used by initialize the MMU on corresponding
+  platforms.
+
+  @param[out]   MemoryTable    Array of MEMORY_REGION_DESCRIPTOR
+                               describing a Physical-to-Virtual Memory
+                               mapping. This array must be ended by a
+                               zero-filled entry. The allocated memory
+                               will not be freed.
+**/
+VOID
+EFIAPI
+GetMemoryMapPolicy (
+  OUT MEMORY_REGION_DESCRIPTOR  **MemoryTable
+  );
+
+/**
+  Create a page table and initialize the memory management unit(MMU).
+
+  @param[in]  MemoryTable           A pointer to a memory ragion table.
+  @param[out] TranslationTableBase  A pointer to a translation table base address.
+  @param[out] TranslationTableSize  A pointer to a translation table base size.
+
+  @retval  EFI_SUCCESS                Configure MMU successfully.
+           EFI_INVALID_PARAMETER      MemoryTable is NULL.
+           EFI_UNSUPPORTED            Out of memory space or size not aligned.
+**/
+EFI_STATUS
+EFIAPI
+ConfigureMemoryManagementUnit (
+  IN  MEMORY_REGION_DESCRIPTOR  *MemoryTable,
+  OUT VOID                      **TranslationTableBase OPTIONAL,
+  OUT UINTN                     *TranslationTableSize  OPTIONAL
+  );
+
+#endif // PLATFORM_H_
diff --git a/OvmfPkg/LoongArchVirt/PlatformPei/PlatformPei.inf b/OvmfPkg/LoongArchVirt/PlatformPei/PlatformPei.inf
new file mode 100644
index 0000000000..e793a4da85
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/PlatformPei/PlatformPei.inf
@@ -0,0 +1,72 @@
+## @file
+#  Platform PEI driver
+#
+#  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                      = PlatformPei
+  FILE_GUID                      = 4c0e81e5-e8e3-4eef-b24b-19b686e9ab53
+  MODULE_TYPE                    = PEIM
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = InitializePlatform
+
+#
+#  VALID_ARCHITECTURES           = LOONGARCH64
+#
+
+[Sources]
+  Fv.c
+  MemDetect.c
+  Platform.c
+
+[Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  OvmfPkg/OvmfPkg.dec
+  UefiCpuPkg/UefiCpuPkg.dec
+
+[Ppis]
+  gEfiPeiMasterBootModePpiGuid
+
+[Guids]
+  gEfiMemoryTypeInformationGuid
+  gFdtHobGuid
+
+[LibraryClasses]
+  BaseMemoryLib
+  CollectApResourceLib
+  CpuMmuLib
+  DebugLib
+  HobLib
+  IoLib
+  MemoryAllocationLib
+  MpInitLib
+  PcdLib
+  PeiResourcePublicationLib
+  PeiServicesLib
+  PeiServicesTablePointerLib
+  PeimEntryPoint
+  PlatformHookLib
+  QemuFwCfgLib
+  RealTimeClockLib
+  TimerLib
+
+[Pcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeAllocationPadding
+  gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask
+
+[FixedPcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize
+  gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize
+  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress
+
+[Depex]
+  TRUE
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114561): https://edk2.groups.io/g/devel/message/114561
Mute This Topic: https://groups.io/mt/103971687/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 36/37] OvmfPkg/LoongArchVirt: Add build file
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (34 preceding siblings ...)
  2024-01-26  6:31 ` [edk2-devel] [PATCH v8 35/37] OvmfPkg/LoongArchVirt: Support PEI phase Chao Li
@ 2024-01-26  6:31 ` Chao Li
  2024-01-26  6:31 ` [edk2-devel] [PATCH v8 37/37] OvmfPkg/LoongArchVirt: Add self introduction file Chao Li
                   ` (5 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:31 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Laszlo Ersek, Jiewen Yao, Jordan Justen,
	Gerd Hoffmann, Bibo Mao, Dongyan Qian, Xianglai Li

Add infrastructure files to build edk2 for LoongArch QEMU virtual
machine.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Bibo Mao <maobibo@loongson.cn>
Cc: Dongyan Qian <qiandongyan@loongson.cn>
Signed-off-by: Chao Li <lichao@loongson.cn>
Co-authored-by: Xianglai Li <lixianglai@loongson.cn>
Co-authored-by: Bibo Mao <maobibo@loongson.cn>
Reviewed-by: Bibo Mao <maobibo@loongson.cn>
---
 OvmfPkg/LoongArchVirt/LoongArchVirt.fdf.inc |  34 +
 OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc | 679 ++++++++++++++++++++
 OvmfPkg/LoongArchVirt/LoongArchVirtQemu.fdf | 313 +++++++++
 OvmfPkg/LoongArchVirt/VarStore.fdf.inc      |  67 ++
 4 files changed, 1093 insertions(+)
 create mode 100644 OvmfPkg/LoongArchVirt/LoongArchVirt.fdf.inc
 create mode 100644 OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc
 create mode 100644 OvmfPkg/LoongArchVirt/LoongArchVirtQemu.fdf
 create mode 100644 OvmfPkg/LoongArchVirt/VarStore.fdf.inc

diff --git a/OvmfPkg/LoongArchVirt/LoongArchVirt.fdf.inc b/OvmfPkg/LoongArchVirt/LoongArchVirt.fdf.inc
new file mode 100644
index 0000000000..22373bec6a
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/LoongArchVirt.fdf.inc
@@ -0,0 +1,34 @@
+## @file
+#
+#  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+DEFINE BLOCK_SIZE                 = 0x1000
+
+############################################################################
+# FW total
+DEFINE FW_BASE_ADDRESS            = 0x1c000000
+DEFINE FW_BLOCKS                  = 0x400
+DEFINE FW_SIZE                    = 0x400000
+
+############################################################################
+#Flash code layout
+#Set Sec size in flash
+DEFINE SECFV_SIZE                 = 0x00010000
+
+#Set Pei size in flash
+DEFINE PEIFV_SIZE                 = 0x00040000
+
+#Set Dxe size in flash
+DEFINE DXEFV_SIZE                 = 0x00350000
+
+#Set FVMAIN size
+DEFINE FVMAIN_SIZE                = $(SECFV_SIZE) + $(PEIFV_SIZE) +$(DXEFV_SIZE)
+
+#Set Memory layout
+DEFINE SEC_PEI_TEMP_RAM_BASE      = 0x10000
+DEFINE SEC_PEI_TEMP_RAM_SIZE      = 0x80000
+DEFINE DEVICE_TREE_RAM_BASE       = 0x100000
diff --git a/OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc b/OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc
new file mode 100644
index 0000000000..f1cb36edae
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc
@@ -0,0 +1,679 @@
+## @file
+#
+#  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+################################################################################
+#
+# Defines Section - statements that will be processed to create a Makefile.
+#
+###############################################################################
+[Defines]
+  PLATFORM_NAME                  = LoongArchVirtQemu
+  PLATFORMPKG_NAME               = LoongArchVirtQemu
+  PLATFORM_GUID                  = 7926ea52-b0dc-4ee8-ac63-341eebd84ed4
+  PLATFORM_VERSION               = 0.1
+  DSC_SPECIFICATION              = 1.29
+  OUTPUT_DIRECTORY               = Build/$(PLATFORM_NAME)
+  SUPPORTED_ARCHITECTURES        = LOONGARCH64
+  BUILD_TARGETS                  = DEBUG|RELEASE
+  SKUID_IDENTIFIER               = DEFAULT
+  FLASH_DEFINITION               = OvmfPkg/LoongArchVirt/LoongArchVirtQemu.fdf
+  TTY_TERMINAL                   = FALSE
+
+!include LoongArchVirt.fdf.inc
+
+  #
+  # Defines for default states.  These can be changed on the command line.
+  # -D FLAG=VALUE
+  DEFINE TTY_TERMINAL            = FALSE
+  DEFINE SECURE_BOOT_ENABLE      = FALSE
+  DEFINE TPM2_ENABLE             = FALSE
+  DEFINE TPM2_CONFIG_ENABLE      = FALSE
+
+  #
+  # Network definition
+  #
+  DEFINE NETWORK_IP6_ENABLE              = FALSE
+  DEFINE NETWORK_HTTP_BOOT_ENABLE        = FALSE
+  DEFINE NETWORK_SNP_ENABLE              = FALSE
+  DEFINE NETWORK_TLS_ENABLE              = FALSE
+  DEFINE NETWORK_ALLOW_HTTP_CONNECTIONS  = TRUE
+  DEFINE NETWORK_ISCSI_ENABLE            = FALSE
+
+!include NetworkPkg/NetworkDefines.dsc.inc
+############################################################################
+#
+# Defines for default states.  These can be changed on the command line.
+# -D FLAG=VALUE
+############################################################################
+[BuildOptions]
+  GCC:RELEASE_*_*_CC_FLAGS       = -DSPEEDUP
+
+  #
+  # Disable deprecated APIs.
+  #
+  GCC:*_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
+
+!include NetworkPkg/NetworkBuildOptions.dsc.inc
+
+[BuildOptions.LOONGARCH64.EDKII.SEC]
+  *_*_*_CC_FLAGS                 =
+
+#
+# Default page size is 16K for loongarch qemu tcg
+# code section separated with data section with 16K page alignment, else data
+# write operation in the same page with code section will cause qemu TB flush.
+#
+[BuildOptions.common.EDKII.DXE_CORE,BuildOptions.common.EDKII.DXE_DRIVER,BuildOptions.common.EDKII.UEFI_DRIVER,BuildOptions.common.EDKII.UEFI_APPLICATION]
+  GCC:*_*_*_DLINK_FLAGS = -z common-page-size=0x4000
+
+[BuildOptions.common.EDKII.DXE_RUNTIME_DRIVER]
+  GCC:*_*_LOONGARCH64_DLINK_FLAGS = -z common-page-size=0x10000
+
+################################################################################
+#
+# SKU Identification section - list of all SKU IDs supported by this Platform.
+#
+################################################################################
+[SkuIds]
+  0|DEFAULT
+
+################################################################################
+#
+# Library Class section - list of all Library Classes needed by this Platform.
+#
+################################################################################
+
+!include MdePkg/MdeLibs.dsc.inc
+
+[LibraryClasses.common]
+  PcdLib                           | MdePkg/Library/DxePcdLib/DxePcdLib.inf
+  TimerLib                         | UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf
+  PrintLib                         | MdePkg/Library/BasePrintLib/BasePrintLib.inf
+  BaseMemoryLib                    | MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
+
+  # Networking Requirements
+!include NetworkPkg/NetworkLibs.dsc.inc
+!if $(NETWORK_TLS_ENABLE) == TRUE
+  TlsLib|CryptoPkg/Library/TlsLib/TlsLib.inf
+!endif
+
+  # For stack protector support
+  NULL                             | MdePkg/Library/BaseStackCheckLib/BaseStackCheckLib.inf
+
+  BaseLib                          | MdePkg/Library/BaseLib/BaseLib.inf
+  SafeIntLib                       | MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf
+  TimeBaseLib                      | EmbeddedPkg/Library/TimeBaseLib/TimeBaseLib.inf
+  BmpSupportLib                    | MdeModulePkg/Library/BaseBmpSupportLib/BaseBmpSupportLib.inf
+  SynchronizationLib               | MdePkg/Library/BaseSynchronizationLib/BaseSynchronizationLib.inf
+  CpuLib                           | MdePkg/Library/BaseCpuLib/BaseCpuLib.inf
+  PerformanceLib                   | MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf
+  PeCoffLib                        | MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf
+  CacheMaintenanceLib              | MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf
+  UefiDecompressLib                | MdePkg/Library/BaseUefiDecompressLib/BaseUefiDecompressLib.inf
+  UefiHiiServicesLib               | MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf
+  HiiLib                           | MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf
+  CapsuleLib                       | MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf
+  DxeServicesLib                   | MdePkg/Library/DxeServicesLib/DxeServicesLib.inf
+  DxeServicesTableLib              | MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.inf
+  PeCoffGetEntryPointLib           | MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
+  PciLib                           | MdePkg/Library/BasePciLibPciExpress/BasePciLibPciExpress.inf
+  PciExpressLib                    | OvmfPkg/Library/BaseCachingPciExpressLib/BaseCachingPciExpressLib.inf
+  PciCapLib                        | OvmfPkg/Library/BasePciCapLib/BasePciCapLib.inf
+  PciCapPciSegmentLib              | OvmfPkg/Library/BasePciCapPciSegmentLib/BasePciCapPciSegmentLib.inf
+  PciCapPciIoLib                   | OvmfPkg/Library/UefiPciCapPciIoLib/UefiPciCapPciIoLib.inf
+  DxeHardwareInfoLib               | OvmfPkg/Library/HardwareInfoLib/DxeHardwareInfoLib.inf
+  IoLib                            | MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
+  FdtSerialPortAddressLib          | OvmfPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf
+  PlatformHookLib                  | OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/Fdt16550SerialPortHookLib.inf
+  SerialPortLib                    | MdeModulePkg/Library/BaseSerialPortLib16550/BaseSerialPortLib16550.inf
+  EfiResetSystemLib                | OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGedLib.inf
+  ResetSystemLib                   | OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/BaseResetSystemAcpiGedLib.inf
+
+  UefiLib                          | MdePkg/Library/UefiLib/UefiLib.inf
+  UefiBootServicesTableLib         | MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
+  UefiRuntimeServicesTableLib      | MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
+  UefiDriverEntryPoint             | MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf
+  UefiApplicationEntryPoint        | MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
+  DevicePathLib                    | MdePkg/Library/UefiDevicePathLibDevicePathProtocol/UefiDevicePathLibDevicePathProtocol.inf
+  FileHandleLib                    | MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf
+  SecurityManagementLib            | MdeModulePkg/Library/DxeSecurityManagementLib/DxeSecurityManagementLib.inf
+  UefiUsbLib                       | MdePkg/Library/UefiUsbLib/UefiUsbLib.inf
+  SerializeVariablesLib            | OvmfPkg/Library/SerializeVariablesLib/SerializeVariablesLib.inf
+  CustomizedDisplayLib             | MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf
+  DebugPrintErrorLevelLib          | MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
+  TpmMeasurementLib                | MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf
+  AuthVariableLib                  | MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf
+  VarCheckLib                      | MdeModulePkg/Library/VarCheckLib/VarCheckLib.inf
+  VariablePolicyLib                | MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf
+  VariablePolicyHelperLib          | MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf
+  SortLib                          | MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf
+  FdtLib                           | EmbeddedPkg/Library/FdtLib/FdtLib.inf
+  PciSegmentLib                    | MdePkg/Library/BasePciSegmentLibPci/BasePciSegmentLibPci.inf
+  PciHostBridgeLib                 | OvmfPkg/Fdt/FdtPciHostBridgeLib/FdtPciHostBridgeLib.inf
+  PciHostBridgeUtilityLib          | OvmfPkg/Library/PciHostBridgeUtilityLib/PciHostBridgeUtilityLib.inf
+  FileExplorerLib                  | MdeModulePkg/Library/FileExplorerLib/FileExplorerLib.inf
+  ImagePropertiesRecordLib         | MdeModulePkg/Library/ImagePropertiesRecordLib/ImagePropertiesRecordLib.inf
+
+!if $(HTTP_BOOT_ENABLE) == TRUE
+  HttpLib                          | MdeModulePkg/Library/DxeHttpLib/DxeHttpLib.inf
+!endif
+  UefiBootManagerLib               | MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf
+  OrderedCollectionLib             | MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrderedCollectionRedBlackTreeLib.inf
+  ReportStatusCodeLib              | MdePkg/Library/BaseReportStatusCodeLibNull/BaseReportStatusCodeLibNull.inf
+
+  PeCoffGetEntryPointLib           | MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
+  PeCoffExtraActionLib             | MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf
+  DebugAgentLib                    | MdeModulePkg/Library/DebugAgentLibNull/DebugAgentLibNull.inf
+
+  TpmPlatformHierarchyLib          | SecurityPkg/Library/PeiDxeTpmPlatformHierarchyLibNull/PeiDxeTpmPlatformHierarchyLib.inf
+  PlatformBmPrintScLib             | OvmfPkg/Library/PlatformBmPrintScLib/PlatformBmPrintScLib.inf
+  PlatformBootManagerLib           | OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBootManagerLib.inf
+  BootLogoLib                      | MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf
+  QemuBootOrderLib                 | OvmfPkg/Library/QemuBootOrderLib/QemuBootOrderLib.inf
+  QemuFwCfgSimpleParserLib         | OvmfPkg/Library/QemuFwCfgSimpleParserLib/QemuFwCfgSimpleParserLib.inf
+  QemuLoadImageLib                 | OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
+
+  #
+  # Virtio Support
+  #
+  VirtioLib                        | OvmfPkg/Library/VirtioLib/VirtioLib.inf
+  FrameBufferBltLib                | MdeModulePkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
+  QemuFwCfgLib                     | OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.inf
+  DebugLib                         | MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf
+  PeiServicesLib                   | MdePkg/Library/PeiServicesLib/PeiServicesLib.inf
+  VariableFlashInfoLib             | MdeModulePkg/Library/BaseVariableFlashInfoLib/BaseVariableFlashInfoLib.inf
+  VirtNorFlashPlatformLib          | OvmfPkg/LoongArchVirt/Library/NorFlashQemuLib/NorFlashQemuLib.inf
+  CollectApResourceLib             | OvmfPkg/LoongArchVirt/Library/CollectApResouceLibNull/CollectApResourceLibNull.inf
+
+[LibraryClasses.common.SEC]
+  PcdLib                           | MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+  ReportStatusCodeLib              | MdeModulePkg/Library/PeiReportStatusCodeLib/PeiReportStatusCodeLib.inf
+  HobLib                           | MdePkg/Library/PeiHobLib/PeiHobLib.inf
+  MemoryAllocationLib              | MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf
+  PeiServicesTablePointerLib       | MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointerLibKs0.inf
+  PlatformHookLib                  | OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.inf
+  SerialPortLib                    | OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.inf
+  CpuExceptionHandlerLib           | UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
+
+[LibraryClasses.common.PEI_CORE]
+  PcdLib                           | MdePkg/Library/PeiPcdLib/PeiPcdLib.inf
+  HobLib                           | MdePkg/Library/PeiHobLib/PeiHobLib.inf
+  PeiServicesTablePointerLib       | MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointerLibKs0.inf
+  MemoryAllocationLib              | MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf
+  PeiCoreEntryPoint                | MdePkg/Library/PeiCoreEntryPoint/PeiCoreEntryPoint.inf
+  ReportStatusCodeLib              | MdeModulePkg/Library/PeiReportStatusCodeLib/PeiReportStatusCodeLib.inf
+  OemHookStatusCodeLib             | MdeModulePkg/Library/OemHookStatusCodeLibNull/OemHookStatusCodeLibNull.inf
+  PeCoffGetEntryPointLib           | MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
+  QemuFwCfgLib                     | OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/FdtQemuFwCfgPeiLib.inf
+  PlatformHookLib                  | OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.inf
+  SerialPortLib                    | OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.inf
+
+[LibraryClasses.common.PEIM]
+  HobLib                           | MdePkg/Library/PeiHobLib/PeiHobLib.inf
+  PeiServicesTablePointerLib       | MdePkg/Library/PeiServicesTablePointerLibKs0/PeiServicesTablePointerLibKs0.inf
+  MemoryAllocationLib              | MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf
+  PeimEntryPoint                   | MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf
+  ReportStatusCodeLib              | MdeModulePkg/Library/PeiReportStatusCodeLib/PeiReportStatusCodeLib.inf
+  OemHookStatusCodeLib             | MdeModulePkg/Library/OemHookStatusCodeLibNull/OemHookStatusCodeLibNull.inf
+  PeCoffGetEntryPointLib           | MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
+  PeiResourcePublicationLib        | MdePkg/Library/PeiResourcePublicationLib/PeiResourcePublicationLib.inf
+  ExtractGuidedSectionLib          | MdePkg/Library/PeiExtractGuidedSectionLib/PeiExtractGuidedSectionLib.inf
+  PcdLib                           | MdePkg/Library/PeiPcdLib/PeiPcdLib.inf
+  QemuFwCfgS3Lib                   | OvmfPkg/Library/QemuFwCfgS3Lib/PeiQemuFwCfgS3LibFwCfg.inf
+  QemuFwCfgLib                     | OvmfPkg/LoongArchVirt/Library/FdtQemuFwCfgLib/FdtQemuFwCfgPeiLib.inf
+  CpuMmuLib                        | UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
+  MpInitLib                        | UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
+  PlatformHookLib                  | OvmfPkg/LoongArchVirt/Library/Fdt16550SerialPortHookLib/EarlyFdt16550SerialPortHookLib.inf
+  SerialPortLib                    | OvmfPkg/LoongArchVirt/Library/EarlyFdtSerialPortLib16550/EarlyFdtSerialPortLib16550.inf
+  RealTimeClockLib                 | OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/PeiLsRealTimeClockLib.inf
+
+[LibraryClasses.common.DXE_CORE]
+  HobLib                           | MdePkg/Library/DxeCoreHobLib/DxeCoreHobLib.inf
+  DxeCoreEntryPoint                | MdePkg/Library/DxeCoreEntryPoint/DxeCoreEntryPoint.inf
+  MemoryAllocationLib              | MdeModulePkg/Library/DxeCoreMemoryAllocationLib/DxeCoreMemoryAllocationLib.inf
+  ReportStatusCodeLib              | MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf
+  PciExpressLib                    | MdePkg/Library/BasePciExpressLib/BasePciExpressLib.inf
+  PciPcdProducerLib                | OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
+  CpuExceptionHandlerLib           | UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
+
+[LibraryClasses.common.DXE_RUNTIME_DRIVER]
+  PcdLib                           | MdePkg/Library/DxePcdLib/DxePcdLib.inf
+  HobLib                           | MdePkg/Library/DxeHobLib/DxeHobLib.inf
+  DxeCoreEntryPoint                | MdePkg/Library/DxeCoreEntryPoint/DxeCoreEntryPoint.inf
+  MemoryAllocationLib              | MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  ReportStatusCodeLib              | MdeModulePkg/Library/RuntimeDxeReportStatusCodeLib/RuntimeDxeReportStatusCodeLib.inf
+  UefiRuntimeLib                   | MdePkg/Library/UefiRuntimeLib/UefiRuntimeLib.inf
+  ExtractGuidedSectionLib          | MdePkg/Library/PeiExtractGuidedSectionLib/PeiExtractGuidedSectionLib.inf
+  QemuFwCfgS3Lib                   | OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
+  RealTimeClockLib                 | OvmfPkg/LoongArchVirt/Library/LsRealTimeClockLib/DxeLsRealTimeClockLib.inf
+  VariablePolicyLib                | MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLibRuntimeDxe.inf
+  QemuFwCfgLib                     | OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.inf
+  EfiResetSystemLib                | OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGedLib.inf
+  ResetSystemLib                   | OvmfPkg/LoongArchVirt/Library/ResetSystemAcpiLib/DxeResetSystemAcpiGedLib.inf
+  PciExpressLib                    | MdePkg/Library/BasePciExpressLib/BasePciExpressLib.inf
+!if $(TARGET) != RELEASE
+  DebugLib                         | MdePkg/Library/DxeRuntimeDebugLibSerialPort/DxeRuntimeDebugLibSerialPort.inf
+!endif
+
+[LibraryClasses.common.UEFI_DRIVER]
+  PcdLib                           | MdePkg/Library/DxePcdLib/DxePcdLib.inf
+  HobLib                           | MdePkg/Library/DxeHobLib/DxeHobLib.inf
+  DxeCoreEntryPoint                | MdePkg/Library/DxeCoreEntryPoint/DxeCoreEntryPoint.inf
+  MemoryAllocationLib              | MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  ReportStatusCodeLib              | MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf
+  UefiScsiLib                      | MdePkg/Library/UefiScsiLib/UefiScsiLib.inf
+  ExtractGuidedSectionLib          | MdePkg/Library/PeiExtractGuidedSectionLib/PeiExtractGuidedSectionLib.inf
+  QemuFwCfgLib                     | OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.inf
+  PciPcdProducerLib                | OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
+
+[LibraryClasses.common.DXE_DRIVER]
+  PcdLib                           | MdePkg/Library/DxePcdLib/DxePcdLib.inf
+  HobLib                           | MdePkg/Library/DxeHobLib/DxeHobLib.inf
+  MemoryAllocationLib              | MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  ReportStatusCodeLib              | MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf
+  UefiScsiLib                      | MdePkg/Library/UefiScsiLib/UefiScsiLib.inf
+  CpuExceptionHandlerLib           | UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
+  ExtractGuidedSectionLib          | MdePkg/Library/DxeExtractGuidedSectionLib/DxeExtractGuidedSectionLib.inf
+  QemuFwCfgS3Lib                   | OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
+  QemuFwCfgLib                     | OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgLibMmio.inf
+  PciPcdProducerLib                | OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
+  PciExpressLib                    | MdePkg/Library/BasePciExpressLib/BasePciExpressLib.inf
+  AcpiPlatformLib                  | OvmfPkg/Library/AcpiPlatformLib/DxeAcpiPlatformLib.inf
+  MpInitLib                        | UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
+
+[LibraryClasses.common.UEFI_APPLICATION]
+  PcdLib                           | MdePkg/Library/DxePcdLib/DxePcdLib.inf
+  HobLib                           | MdePkg/Library/DxeHobLib/DxeHobLib.inf
+  MemoryAllocationLib              | MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  ExtractGuidedSectionLib          | MdePkg/Library/DxeExtractGuidedSectionLib/DxeExtractGuidedSectionLib.inf
+  PciPcdProducerLib                | OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
+  PciExpressLib                    | MdePkg/Library/BasePciExpressLib/BasePciExpressLib.inf
+
+################################################################################
+#
+# Pcd Section - list of all EDK II PCD Entries defined by this Platform.
+#
+################################################################################
+[PcdsFeatureFlag]
+   gEfiMdeModulePkgTokenSpaceGuid.PcdHiiOsRuntimeSupport               | FALSE
+#  gEfiMdeModulePkgTokenSpaceGuid.PcdStatusCodeUseSerial               | TRUE
+#  gEfiMdeModulePkgTokenSpaceGuid.PcdStatusCodeUseMemory               | TRUE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSupportUefiDecompress        | TRUE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdConOutGopSupport                   | TRUE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdConOutUgaSupport                   | FALSE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdPciBusHotplugDeviceSupport         | FALSE
+  gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderPciTranslation            | TRUE
+  gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderMmioTranslation           | TRUE
+[PcdsFixedAtBuild]
+## BaseLib ##
+  gEfiMdePkgTokenSpaceGuid.PcdMaximumUnicodeStringLength               | 1000000
+  gEfiMdePkgTokenSpaceGuid.PcdMaximumAsciiStringLength                 | 1000000
+  gEfiMdePkgTokenSpaceGuid.PcdMaximumLinkedListLength                  | 1000000
+  gEfiMdePkgTokenSpaceGuid.PcdSpinLockTimeout                          | 10000000
+
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfFdBaseAddress                      | $(FW_BASE_ADDRESS)
+
+  gEfiMdeModulePkgTokenSpaceGuid.PcdStatusCodeMemorySize               | 1
+  gEfiMdeModulePkgTokenSpaceGuid.PcdResetOnMemoryTypeInformationChange | FALSE
+  gEfiMdePkgTokenSpaceGuid.PcdMaximumGuidedExtractHandler              | 0x10
+  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxVariableSize                    | 0x2000
+  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxHardwareErrorVariableSize       | 0x8000
+  gEfiMdeModulePkgTokenSpaceGuid.PcdVpdBaseAddress                     | 0x0
+  gEfiMdePkgTokenSpaceGuid.PcdReportStatusCodePropertyMask             | 0x07
+
+  # Use MMIO for accessing Serial port registers.
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialUseMmio                      | TRUE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialPciDeviceInfo                | {0xFF}
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialBaudRate                     | 115200
+
+  # DEBUG_INIT      0x00000001  // Initialization
+  # DEBUG_WARN      0x00000002  // Warnings
+  # DEBUG_LOAD      0x00000004  // Load events
+  # DEBUG_FS        0x00000008  // EFI File system
+  # DEBUG_POOL      0x00000010  // Alloc & Free (pool)
+  # DEBUG_PAGE      0x00000020  // Alloc & Free (page)
+  # DEBUG_INFO      0x00000040  // Informational debug messages
+  # DEBUG_DISPATCH  0x00000080  // PEI/DXE/SMM Dispatchers
+  # DEBUG_VARIABLE  0x00000100  // Variable
+  # DEBUG_BM        0x00000400  // Boot Manager
+  # DEBUG_BLKIO     0x00001000  // BlkIo Driver
+  # DEBUG_NET       0x00004000  // Network Io Driver
+  # DEBUG_UNDI      0x00010000  // UNDI Driver
+  # DEBUG_LOADFILE  0x00020000  // LoadFile
+  # DEBUG_EVENT     0x00080000  // Event messages
+  # DEBUG_GCD       0x00100000  // Global Coherency Database changes
+  # DEBUG_CACHE     0x00200000  // Memory range cachability changes
+  # DEBUG_VERBOSE   0x00400000  // Detailed debug messages that may
+  # DEBUG_ERROR     0x80000000  // Error
+  gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel                     | 0x8000004F
+
+  # DEBUG_ASSERT_ENABLED       0x01
+  # DEBUG_PRINT_ENABLED        0x02
+  # DEBUG_CODE_ENABLED         0x04
+  # CLEAR_MEMORY_ENABLED       0x08
+  # ASSERT_BREAKPOINT_ENABLED  0x10
+  # ASSERT_DEADLOOP_ENABLED    0x20
+!if $(TARGET) == RELEASE
+  gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask                        | 0x21
+!else
+  gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask                        | 0x2f
+!endif
+
+#######################################################################################
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase                  | $(SEC_PEI_TEMP_RAM_BASE)
+  gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamSize                  | $(SEC_PEI_TEMP_RAM_SIZE)
+  gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeInitialBaseAddress           | $(DEVICE_TREE_RAM_BASE)
+
+  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress           | gUefiOvmfPkgTokenSpaceGuid.PcdOvmfSecPeiTempRamBase
+
+  #
+  # minimal memory for uefi bios should be 512M
+  # 0x00000000 - 0x10000000
+  # 0x90000000 - 0xA0000000
+  #
+  gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiExposedTableVersions           | 0x06
+
+  gEfiMdeModulePkgTokenSpaceGuid.PcdBootManagerMenuFile                | { 0x21, 0xaa, 0x2c, 0x46, 0x14, 0x76, 0x03, 0x45, 0x83, 0x6e, 0x8a, 0xb6, 0xf4, 0x66, 0x23, 0x31 }
+
+  #
+  # Network Pcds
+  #
+!include NetworkPkg/NetworkPcds.dsc.inc
+
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize         | 0x40000
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareSize         | 0x40000
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingSize       | 0x40000
+
+  gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask   | 1
+
+################################################################################
+#
+# Pcd Dynamic Section - list of all EDK II PCD Entries defined by this Platform
+#
+################################################################################
+[PcdsDynamicDefault]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase         | 0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwSpareBase64       | 0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase64       | 0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableBase         | 0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase       | 0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageFtwWorkingBase64     | 0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdEmuVariableNvStoreReserved         | 0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration           | FALSE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdVideoHorizontalResolution          | 800
+  gEfiMdeModulePkgTokenSpaceGuid.PcdVideoVerticalResolution            | 600
+  gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut                      | 2
+
+  # Set video resolution for text setup.
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSetupVideoHorizontalResolution     | 640
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSetupVideoVerticalResolution       | 480
+
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSmbiosVersion                      | 0x0300
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSmbiosDocRev                       | 0x0
+
+  ## If TRUE, OvmfPkg/AcpiPlatformDxe will not wait for PCI
+  #  enumeration to complete before installing ACPI tables.
+  gEfiMdeModulePkgTokenSpaceGuid.PcdPciDisableBusEnumeration           |TRUE
+  gEfiMdePkgTokenSpaceGuid.PcdPciIoTranslation                         |0x0
+  # set PcdPciExpressBaseAddress to MAX_UINT64, which signifies that this
+  # PCD and PcdPciDisableBusEnumeration above have not been assigned yet
+  gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress                    |0xFFFFFFFFFFFFFFFF
+
+  #
+  # IPv4 and IPv6 PXE Boot support.
+  #
+  gEfiNetworkPkgTokenSpaceGuid.PcdIPv4PXESupport                       | 0x01
+  gEfiNetworkPkgTokenSpaceGuid.PcdIPv6PXESupport                       | 0x01
+
+  #
+  # SMBIOS entry point version
+  #
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSmbiosVersion|0x0300
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSmbiosDocRev|0x0
+  gUefiOvmfPkgTokenSpaceGuid.PcdQemuSmbiosValidated|TRUE
+
+[PcdsPatchableInModule.common]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterBase|0x0
+
+[Components]
+
+  #
+  # SEC Phase modules
+  #
+  OvmfPkg/LoongArchVirt/Sec/SecMain.inf
+
+  #
+  # PEI Phase modules
+  #
+  MdeModulePkg/Core/Pei/PeiMain.inf
+  MdeModulePkg/Universal/PCD/Pei/Pcd.inf  {
+    <LibraryClasses>
+      PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+  }
+  MdePkg/Library/PeiExtractGuidedSectionLib/PeiExtractGuidedSectionLib.inf
+  MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf {
+    <LibraryClasses>
+      NULL|MdeModulePkg/Library/LzmaCustomDecompressLib/LzmaCustomDecompressLib.inf
+  }
+
+  OvmfPkg/LoongArchVirt/PlatformPei/PlatformPei.inf {
+    <LibraryClasses>
+      PcdLib|MdePkg/Library/PeiPcdLib/PeiPcdLib.inf
+  }
+
+  #
+  # DXE Phase modules
+  #
+  MdeModulePkg/Core/Dxe/DxeMain.inf {
+    <LibraryClasses>
+      NULL                             | MdeModulePkg/Library/LzmaCustomDecompressLib/LzmaCustomDecompressLib.inf
+      DevicePathLib                    | MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
+      ExtractGuidedSectionLib          | MdePkg/Library/DxeExtractGuidedSectionLib/DxeExtractGuidedSectionLib.inf
+  }
+
+  MdeModulePkg/Universal/ReportStatusCodeRouter/RuntimeDxe/ReportStatusCodeRouterRuntimeDxe.inf
+  MdeModulePkg/Universal/StatusCodeHandler/RuntimeDxe/StatusCodeHandlerRuntimeDxe.inf
+  MdeModulePkg/Universal/PCD/Dxe/Pcd.inf  {
+   <LibraryClasses>
+      PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+  }
+
+  MdeModulePkg/Core/RuntimeDxe/RuntimeDxe.inf
+  UefiCpuPkg/CpuDxe/CpuDxe.inf {
+    <LibraryClasses>
+      CpuMmuLib | UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
+  }
+  MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
+  MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf
+  MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf
+  MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf
+  OvmfPkg/LoongArchVirt/Drivers/StableTimerDxe/TimerDxe.inf
+  MdeModulePkg/Universal/ResetSystemRuntimeDxe/ResetSystemRuntimeDxe.inf
+  MdeModulePkg/Universal/Metronome/Metronome.inf
+  EmbeddedPkg/RealTimeClockRuntimeDxe/RealTimeClockRuntimeDxe.inf
+
+  #
+  # Variable
+  #
+  OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf
+  MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
+  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf {
+    <LibraryClasses>
+      NULL|MdeModulePkg/Library/VarCheckUefiLib/VarCheckUefiLib.inf
+      NULL|EmbeddedPkg/Library/NvVarStoreFormattedLib/NvVarStoreFormattedLib.inf
+      BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
+  }
+
+  #
+  # Platform Driver
+  #
+  OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
+  OvmfPkg/VirtioScsiDxe/VirtioScsi.inf
+  OvmfPkg/VirtioRngDxe/VirtioRng.inf
+
+  #
+  # FAT filesystem + GPT/MBR partitioning + UDF filesystem + virtio-fs
+  #
+  MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
+  MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
+  MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
+  FatPkg/EnhancedFatDxe/Fat.inf
+  MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
+  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+
+  #
+  #BDS
+  #
+  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf {
+    <LibraryClasses>
+      DevicePathLib                    | MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
+      PcdLib                           | MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+  }
+  MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf
+  MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
+  MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
+  MdeModulePkg/Logo/LogoDxe.inf
+  MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
+  MdeModulePkg/Application/UiApp/UiApp.inf {
+    <LibraryClasses>
+      NULL|MdeModulePkg/Library/DeviceManagerUiLib/DeviceManagerUiLib.inf
+      NULL|MdeModulePkg/Library/BootManagerUiLib/BootManagerUiLib.inf
+      NULL|MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenanceManagerUiLib.inf
+  }
+
+  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf {
+    <LibraryClasses>
+      NULL|OvmfPkg/Library/BlobVerifierLibNull/BlobVerifierLibNull.inf
+  }
+
+  #
+  # Network Support
+  #
+#!include NetworkPkg/NetworkComponents.dsc.inc
+
+#  NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf {
+#    <LibraryClasses>
+#      NULL|OvmfPkg/Library/PxeBcPcdProducerLib/PxeBcPcdProducerLib.inf
+#  }
+
+!if $(NETWORK_TLS_ENABLE) == TRUE
+  NetworkPkg/TlsAuthConfigDxe/TlsAuthConfigDxe.inf {
+    <LibraryClasses>
+      NULL|OvmfPkg/Library/TlsAuthConfigLib/TlsAuthConfigLib.inf
+  }
+!endif
+  OvmfPkg/VirtioNetDxe/VirtioNet.inf
+
+  #
+  # IDE/SCSI
+  #
+  MdeModulePkg/Bus/Pci/SataControllerDxe/SataControllerDxe.inf
+  MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf
+  MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf
+
+  #
+  # NVME Driver
+  #
+  MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
+
+  #
+  # SMBIOS Support
+  #
+  MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf {
+    <LibraryClasses>
+      NULL                             | OvmfPkg/Library/SmbiosVersionLib/DetectSmbiosVersionLib.inf
+  }
+  OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.inf
+
+  #
+  # PCI
+  #
+  UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf {
+    <LibraryClasses>
+      NULL|OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
+      NULL|OvmfPkg/Library/BaseCachingPciExpressLib/BaseCachingPciExpressLib.inf
+  }
+  EmbeddedPkg/Drivers/FdtClientDxe/FdtClientDxe.inf
+  MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf {
+    <LibraryClasses>
+      NULL|OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
+      NULL|OvmfPkg/Library/BaseCachingPciExpressLib/BaseCachingPciExpressLib.inf
+  }
+  MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf {
+    <LibraryClasses>
+      NULL|OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
+      NULL|OvmfPkg/Library/BaseCachingPciExpressLib/BaseCachingPciExpressLib.inf
+  }
+  OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
+  OvmfPkg/Virtio10Dxe/Virtio10.inf
+
+  #
+  # Console
+  #
+  MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe.inf
+  MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxe.inf
+  MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf
+  MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
+  MdeModulePkg/Universal/SerialDxe/SerialDxe.inf
+  MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxe.inf {
+    <LibraryClasses>
+      PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf
+  }
+
+  #
+  # Video
+  #
+  OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf
+  OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf
+  OvmfPkg/VirtioGpuDxe/VirtioGpu.inf
+  OvmfPkg/PlatformDxe/Platform.inf
+
+  #
+  # Usb Support
+  #
+  MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf
+  MdeModulePkg/Bus/Pci/EhciDxe/EhciDxe.inf
+  MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf
+  MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf
+  MdeModulePkg/Bus/Usb/UsbKbDxe/UsbKbDxe.inf
+  MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf
+
+  #
+  # ACPI Support
+  #
+  MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf
+  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
+  OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf {
+    <LibraryClasses>
+      NULL|OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
+  }
+
+  #
+  #app
+  #
+  ShellPkg/Application/Shell/Shell.inf {
+    <LibraryClasses>
+      ShellCommandLib|ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.inf
+      NULL|ShellPkg/Library/UefiShellLevel2CommandsLib/UefiShellLevel2CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellLevel1CommandsLib/UefiShellLevel1CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellLevel3CommandsLib/UefiShellLevel3CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellDriver1CommandsLib/UefiShellDriver1CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellDebug1CommandsLib/UefiShellDebug1CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellInstall1CommandsLib/UefiShellInstall1CommandsLib.inf
+      NULL|ShellPkg/Library/UefiShellNetwork1CommandsLib/UefiShellNetwork1CommandsLib.inf
+      HandleParsingLib|ShellPkg/Library/UefiHandleParsingLib/UefiHandleParsingLib.inf
+      ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf
+      FileHandleLib|MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf
+      SortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf
+      PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
+      BcfgCommandLib|ShellPkg/Library/UefiShellBcfgCommandLib/UefiShellBcfgCommandLib.inf
+<PcdsFixedAtBuild>
+      gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0xFF
+      gEfiShellPkgTokenSpaceGuid.PcdShellLibAutoInitialize|FALSE
+      gEfiMdePkgTokenSpaceGuid.PcdUefiLibMaxPrintBufferSize|8000
+  }
diff --git a/OvmfPkg/LoongArchVirt/LoongArchVirtQemu.fdf b/OvmfPkg/LoongArchVirt/LoongArchVirtQemu.fdf
new file mode 100644
index 0000000000..6fc52173e8
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/LoongArchVirtQemu.fdf
@@ -0,0 +1,313 @@
+## @file
+#
+#  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+#####################################################################################################
+[Defines]
+!include LoongArchVirt.fdf.inc
+
+#####################################################################################################
+[FD.QEMU_EFI]
+BaseAddress   = $(FW_BASE_ADDRESS)
+Size          = $(FW_SIZE)
+ErasePolarity = 1
+BlockSize     = $(BLOCK_SIZE)
+NumBlocks     = $(FW_BLOCKS)
+
+0x0|$(FVMAIN_SIZE)
+FV = FVMAIN_COMPACT
+
+!include VarStore.fdf.inc
+
+#####################################################################################################
+[FV.DXEFV]
+FvNameGuid         = 5d19a5b3-130f-459b-a292-9270a9e6bc62
+BlockSize          = $(BLOCK_SIZE)
+FvAlignment        = 16
+ERASE_POLARITY     = 1
+MEMORY_MAPPED      = TRUE
+STICKY_WRITE       = TRUE
+LOCK_CAP           = TRUE
+LOCK_STATUS        = TRUE
+READ_DISABLED_CAP  = TRUE
+READ_ENABLED_CAP   = TRUE
+READ_STATUS        = TRUE
+READ_LOCK_CAP      = TRUE
+READ_LOCK_STATUS   = TRUE
+WRITE_DISABLED_CAP = TRUE
+WRITE_ENABLED_CAP  = TRUE
+WRITE_STATUS       = TRUE
+WRITE_LOCK_CAP     = TRUE
+WRITE_LOCK_STATUS  = TRUE
+
+APRIORI DXE {
+  INF  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
+  INF  MdeModulePkg/Universal/PCD/Dxe/Pcd.inf
+}
+
+#
+# DXE Phase modules
+#
+INF  MdeModulePkg/Core/Dxe/DxeMain.inf
+
+INF  MdeModulePkg/Universal/ReportStatusCodeRouter/RuntimeDxe/ReportStatusCodeRouterRuntimeDxe.inf
+INF  MdeModulePkg/Universal/StatusCodeHandler/RuntimeDxe/StatusCodeHandlerRuntimeDxe.inf
+INF  MdeModulePkg/Universal/PCD/Dxe/Pcd.inf
+INF  MdeModulePkg/Core/RuntimeDxe/RuntimeDxe.inf
+INF  UefiCpuPkg/CpuDxe/CpuDxe.inf
+INF  MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf
+INF  OvmfPkg/LoongArchVirt/Drivers/StableTimerDxe/TimerDxe.inf
+INF  MdeModulePkg/Universal/ResetSystemRuntimeDxe/ResetSystemRuntimeDxe.inf
+INF  MdeModulePkg/Universal/Metronome/Metronome.inf
+INF  MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
+INF  MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf
+INF  EmbeddedPkg/RealTimeClockRuntimeDxe/RealTimeClockRuntimeDxe.inf
+INF  MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf
+
+#
+# Variable
+#
+INF  OvmfPkg/VirtNorFlashDxe/VirtNorFlashDxe.inf
+INF  MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
+INF  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf
+#
+# PCI
+#
+INF  UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf
+INF  EmbeddedPkg/Drivers/FdtClientDxe/FdtClientDxe.inf
+INF  MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf
+INF  MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
+INF  OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
+INF  OvmfPkg/Virtio10Dxe/Virtio10.inf
+
+#
+# Platform Driver
+#
+INF  OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
+INF  OvmfPkg/VirtioScsiDxe/VirtioScsi.inf
+INF  OvmfPkg/VirtioRngDxe/VirtioRng.inf
+INF  OvmfPkg/VirtioNetDxe/VirtioNet.inf
+
+#
+# Console
+#
+INF  MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe.inf
+INF  MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxe.inf
+INF  MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf
+INF  MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
+INF  MdeModulePkg/Universal/SerialDxe/SerialDxe.inf
+INF  MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxe.inf
+
+#
+#Video
+#
+INF  OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf
+INF  OvmfPkg/QemuRamfbDxe/QemuRamfbDxe.inf
+INF  OvmfPkg/VirtioGpuDxe/VirtioGpu.inf
+INF  OvmfPkg/PlatformDxe/Platform.inf
+
+#
+# SATA/SCSI
+#
+INF  MdeModulePkg/Bus/Pci/SataControllerDxe/SataControllerDxe.inf
+INF  MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf
+INF  MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf
+
+#
+# NVME
+#
+INF MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
+
+#
+# Usb Support
+#
+INF  MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf
+INF  MdeModulePkg/Bus/Pci/EhciDxe/EhciDxe.inf
+INF  MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf
+INF  MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf
+INF  MdeModulePkg/Bus/Usb/UsbKbDxe/UsbKbDxe.inf
+INF  MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf
+
+#
+#BDS
+#
+INF  MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
+INF  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
+INF  MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf
+INF  MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
+INF  MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
+INF  MdeModulePkg/Logo/LogoDxe.inf
+INF  MdeModulePkg/Application/UiApp/UiApp.inf
+INF  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
+
+#
+#Smbios
+#
+INF  MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf
+INF  OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.inf
+
+#
+#Acpi
+#
+INF  MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf
+INF  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
+INF  OvmfPkg/AcpiPlatformDxe/AcpiPlatformDxe.inf
+
+#
+# Network modules
+#!include NetworkPkg/Network.fdf.inc
+
+#
+# File system
+#
+INF  MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
+INF  MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
+INF  MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
+INF  FatPkg/EnhancedFatDxe/Fat.inf
+INF  MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
+INF  OvmfPkg/VirtioFsDxe/VirtioFsDxe.inf
+
+#
+#Boot OS
+#
+INF ShellPkg/Application/Shell/Shell.inf
+
+#####################################################################################################
+[FV.FVMAIN_COMPACT]
+FvNameGuid         = af8c3fe8-9ce8-4548-884a-e3f4dd91f040
+FvAlignment        = 16
+ERASE_POLARITY     = 1
+MEMORY_MAPPED      = TRUE
+STICKY_WRITE       = TRUE
+LOCK_CAP           = TRUE
+LOCK_STATUS        = TRUE
+WRITE_DISABLED_CAP = TRUE
+WRITE_ENABLED_CAP  = TRUE
+WRITE_STATUS       = TRUE
+WRITE_LOCK_CAP     = TRUE
+WRITE_LOCK_STATUS  = TRUE
+READ_DISABLED_CAP  = TRUE
+READ_ENABLED_CAP   = TRUE
+READ_STATUS        = TRUE
+READ_LOCK_CAP      = TRUE
+READ_LOCK_STATUS   = TRUE
+
+#
+#
+#  PEI Phase priori modules
+#
+APRIORI PEI {
+  INF  MdeModulePkg/Universal/PCD/Pei/Pcd.inf
+}
+
+#
+#  SEC Phase modules
+#
+INF  OvmfPkg/LoongArchVirt/Sec/SecMain.inf
+
+#
+#  PEI Phase modules
+#
+INF  MdeModulePkg/Core/Pei/PeiMain.inf
+INF  MdeModulePkg/Universal/PCD/Pei/Pcd.inf
+INF  OvmfPkg/LoongArchVirt/PlatformPei/PlatformPei.inf
+INF  MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
+
+#
+# DXE Phase modules
+#
+FILE FV_IMAGE = 9E21FD93-9C72-4c15-8C4B-E77F1DB2D792 {
+   SECTION GUIDED EE4E5898-3914-4259-9D6E-DC7BD79403CF PROCESSING_REQUIRED = TRUE {
+     SECTION FV_IMAGE = DXEFV
+   }
+ }
+
+#####################################################################################################
+[Rule.Common.SEC]
+  FILE SEC = $(NAMED_GUID) {
+    TE       TE Align = Auto   $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI       STRING ="$(MODULE_NAME)" Optional
+  }
+
+#####################################################################################################
+[Rule.Common.PEI_CORE]
+  FILE PEI_CORE = $(NAMED_GUID) {
+    TE       TE   Align=Auto    $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI       STRING ="$(MODULE_NAME)" Optional
+  }
+
+#####################################################################################################
+[Rule.Common.PEIM]
+  FILE PEIM = $(NAMED_GUID) {
+     PEI_DEPEX PEI_DEPEX Optional        $(INF_OUTPUT)/$(MODULE_NAME).depex
+     PE32      PE32   Align=Auto         $(INF_OUTPUT)/$(MODULE_NAME).efi
+     UI        STRING="$(MODULE_NAME)" Optional
+  }
+
+#####################################################################################################
+[Rule.Common.DXE_CORE]
+  FILE DXE_CORE = $(NAMED_GUID) {
+    PE32     PE32           $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI       STRING="$(MODULE_NAME)" Optional
+  }
+
+#####################################################################################################
+[Rule.Common.DXE_DRIVER]
+  FILE DRIVER = $(NAMED_GUID) {
+    DXE_DEPEX    DXE_DEPEX Optional      $(INF_OUTPUT)/$(MODULE_NAME).depex
+    PE32         PE32                    $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI       STRING="$(MODULE_NAME)" Optional
+    RAW          ACPI  Optional            |.acpi
+    RAW          ASL   Optional            |.aml
+  }
+
+#####################################################################################################
+[Rule.Common.DXE_RUNTIME_DRIVER]
+  FILE DRIVER = $(NAMED_GUID) {
+    DXE_DEPEX    DXE_DEPEX     Optional $(INF_OUTPUT)/$(MODULE_NAME).depex
+    PE32         PE32                   $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI           STRING="$(MODULE_NAME)" Optional
+  }
+
+#####################################################################################################
+[Rule.Common.UEFI_DRIVER]
+  FILE DRIVER = $(NAMED_GUID) {
+    DXE_DEPEX    DXE_DEPEX Optional  $(INF_OUTPUT)/$(MODULE_NAME).depex
+    PE32     PE32                    $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI       STRING="$(MODULE_NAME)" Optional
+  }
+
+#####################################################################################################
+[Rule.Common.UEFI_DRIVER.BINARY]
+  FILE DRIVER = $(NAMED_GUID) {
+    DXE_DEPEX DXE_DEPEX Optional      |.depex
+    PE32      PE32                    |.efi
+    UI        STRING="$(MODULE_NAME)" Optional
+    VERSION   STRING="$(INF_VERSION)" Optional BUILD_NUM=$(BUILD_NUMBER)
+  }
+
+#####################################################################################################
+[Rule.Common.UEFI_APPLICATION]
+  FILE APPLICATION = $(NAMED_GUID) {
+    PE32     PE32                    $(INF_OUTPUT)/$(MODULE_NAME).efi
+    UI       STRING="$(MODULE_NAME)" Optional
+  }
+
+#####################################################################################################
+[Rule.Common.UEFI_APPLICATION.BINARY]
+  FILE APPLICATION = $(NAMED_GUID) {
+    PE32      PE32                    |.efi
+    UI        STRING="$(MODULE_NAME)" Optional
+    VERSION   STRING="$(INF_VERSION)" Optional BUILD_NUM=$(BUILD_NUMBER)
+  }
+
+#####################################################################################################
+[Rule.Common.USER_DEFINED.ACPITABLE]
+  FILE FREEFORM = $(NAMED_GUID) {
+    RAW ACPI               |.acpi
+    RAW ASL                |.aml
+  }
diff --git a/OvmfPkg/LoongArchVirt/VarStore.fdf.inc b/OvmfPkg/LoongArchVirt/VarStore.fdf.inc
new file mode 100644
index 0000000000..52ef0d482e
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/VarStore.fdf.inc
@@ -0,0 +1,67 @@
+## @file
+#
+#  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[FD.QEMU_VARS]
+BaseAddress   = 0x0
+Size          = 0x1000000
+ErasePolarity = 1
+BlockSize     = 0x20000
+NumBlocks     = 128
+
+0x00000000|0x00040000
+#NV_VARIABLE_STORE
+DATA = {
+  ## This is the EFI_FIRMWARE_VOLUME_HEADER
+  # ZeroVector []
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  # FileSystemGuid: gEfiSystemNvDataFvGuid         =
+  #   { 0xFFF12B8D, 0x7696, 0x4C8B,
+  #     { 0xA9, 0x85, 0x27, 0x47, 0x07, 0x5B, 0x4F, 0x50 }}
+  0x8D, 0x2B, 0xF1, 0xFF, 0x96, 0x76, 0x8B, 0x4C,
+  0xA9, 0x85, 0x27, 0x47, 0x07, 0x5B, 0x4F, 0x50,
+  # FvLength: 0xC0000
+  0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00,
+  # Signature "_FVH"       # Attributes
+  0x5f, 0x46, 0x56, 0x48, 0xff, 0xfe, 0x04, 0x00,
+  # HeaderLength # CheckSum # ExtHeaderOffset #Reserved #Revision
+  0x48, 0x00, 0x28, 0x09, 0x00, 0x00, 0x00, 0x02,
+  # Blockmap[0]: 0x3 Blocks * 0x40000 Bytes / Block
+  0x3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
+  # Blockmap[1]: End
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  ## This is the VARIABLE_STORE_HEADER
+  # It is compatible with SECURE_BOOT_ENABLE == FALSE as well.
+  # Signature: gEfiAuthenticatedVariableGuid =
+  #   { 0xaaf32c78, 0x947b, 0x439a,
+  #     { 0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77, 0x92 }}
+  0x78, 0x2c, 0xf3, 0xaa, 0x7b, 0x94, 0x9a, 0x43,
+  0xa1, 0x80, 0x2e, 0x14, 0x4e, 0xc3, 0x77, 0x92,
+  # Size: 0x40000 (gEfiMdeModulePkgTokenSpaceGuid.PcdFlashNvStorageVariableSize) -
+  #         0x48 (size of EFI_FIRMWARE_VOLUME_HEADER) = 0x3ffb8
+  # This can speed up the Variable Dispatch a bit.
+  0xB8, 0xFF, 0x03, 0x00,
+  # FORMATTED: 0x5A #HEALTHY: 0xFE #Reserved: UINT16 #Reserved1: UINT32
+  0x5A, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+}
+
+0x00040000|0x00040000
+#NV_FTW_WORKING
+DATA = {
+  # EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER->Signature = gEdkiiWorkingBlockSignatureGuid         =
+  #  { 0x9e58292b, 0x7c68, 0x497d, { 0xa0, 0xce, 0x65,  0x0, 0xfd, 0x9f, 0x1b, 0x95 }}
+  0x2b, 0x29, 0x58, 0x9e, 0x68, 0x7c, 0x7d, 0x49,
+  0xa0, 0xce, 0x65,  0x0, 0xfd, 0x9f, 0x1b, 0x95,
+  # Crc:UINT32            #WorkingBlockValid:1, WorkingBlockInvalid:1, Reserved
+  0x5b, 0xe7, 0xc6, 0x86, 0xFE, 0xFF, 0xFF, 0xFF,
+  # WriteQueueSize: UINT64
+  0xE0, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00
+}
+
+0x00080000|0x00040000
+#NV_FTW_SPARE
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114562): https://edk2.groups.io/g/devel/message/114562
Mute This Topic: https://groups.io/mt/103971688/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* [edk2-devel] [PATCH v8 37/37] OvmfPkg/LoongArchVirt: Add self introduction file
  2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
                   ` (35 preceding siblings ...)
  2024-01-26  6:31 ` [edk2-devel] [PATCH v8 36/37] OvmfPkg/LoongArchVirt: Add build file Chao Li
@ 2024-01-26  6:31 ` Chao Li
       [not found] ` <17ADD1D5A196C454.24595@groups.io>
                   ` (4 subsequent siblings)
  41 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-26  6:31 UTC (permalink / raw)
  To: devel
  Cc: Ard Biesheuvel, Laszlo Ersek, Jiewen Yao, Jordan Justen,
	Gerd Hoffmann, Bibo Mao, Dongyan Qian

Add self introduction file for LoongArch virtual machine.

BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584

Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Bibo Mao <maobibo@loongson.cn>
Cc: Dongyan Qian <qiandongyan@loongson.cn>
Signed-off-by: Chao Li <lichao@loongson.cn>
Reviewed-by: Bibo Mao <maobibo@loongson.cn>
---
 OvmfPkg/LoongArchVirt/Readme.md | 67 +++++++++++++++++++++++++++++++++
 1 file changed, 67 insertions(+)
 create mode 100644 OvmfPkg/LoongArchVirt/Readme.md

diff --git a/OvmfPkg/LoongArchVirt/Readme.md b/OvmfPkg/LoongArchVirt/Readme.md
new file mode 100644
index 0000000000..57fc74c296
--- /dev/null
+++ b/OvmfPkg/LoongArchVirt/Readme.md
@@ -0,0 +1,67 @@
+# LoongArch QEMU virt platform
+
+## Overview
+
+  LoongArch QEMU virt is a generic platform that dose not require any actual hardware.
+  The minimum required QEMU version is [8.1](https://gitlab.com/qemu-project/qemu/-/tags), the minimum required GCC version is [GCC13](https://gcc.gnu.org/gcc-13/), the minimum required Binutils version is [2.40](https://ftp.gnu.org/gnu/binutils/).
+
+## Prepare (X86 Linux Environment)
+
+### Fedora39
+Install LoongArch64 cross compiler, LoongArch system QEMU.
+
+    yum install gcc-loongarch64-linux-gnu
+    yum install qemu-system-loongarch64
+
+### Others X86 OS ENV
+#### Configure cross-tools
+
+**Download:**
+
+    wget https://github.com/loongson/build-tools/releases/download/2023.08.08/x86_64-cross-tools-loongarch64-binutils_2.41-gcc_13.2.0.tar.xz
+
+**Configure the cross-tools environment:**
+
+    mkdir /opt/loongarch64_cross-toolchain/
+    tar -vxf x86_64-cross-tools-loongarch64-binutils_2.41-gcc_13.2.0.tar.xz -C /opt/loongarch64_cross-toolchain/
+    export PATH=/opt/loongarch64_cross-toolchain/cross-tools/bin:$PATH
+
+Note: Please obtain [the latest cross-compilation](https://github.com/loongson/build-tools) toolchains.
+
+#### Build QEMU
+
+    git clone https://gitlab.com/qemu-project/qemu.git
+
+Note: Please refer to QEMU compilation rules, located in qemu/doc/system/loongarch/virt.rst.
+
+
+## Build LoongArch QEMU virtual machine firmware
+#### Get edk2 resouces
+
+    git clone --recurse-submodule https://github.com/tianocore/edk2.git
+
+#### Building LoongArch QEMU virt FW with GCC
+
+    export WORKSPACE=`pwd`
+    export GCC5_LOONGARCH64_PREFIX=loongarch64-unknown-linux-gnu-
+    export PACKAGES_PATH=$WORKSPACE/edk2
+    export EDK_TOOLS_PATH=$WORKSPACE/edk2/BaseTools
+    source edk2/edksetup.sh --reconfig
+    make -C edk2/BaseTools
+    source edk2/edksetup.sh BaseTools
+    build -b RELEASE -t GCC5 -a LOONGARCH64 -p OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc
+
+## Test LoongArch QEMU virtual machine firmware
+    qemu-system-loongarch64 \
+    -m 4G \
+    -M virt \
+    -smp 2 \
+    -cpu la464 \
+    -bios Build/LoongArchVirtQemu/RELEASE_GCC5/FV/QEMU_EFI.fd \
+    -serial stdio
+
+## Test LoongArch QEMU virtual machine OS
+
+* Download ArchLinux QCOW [images](https://mirrors.pku.edu.cn/loongarch/archlinux/images) for LoongArch.
+
+* [Running LoongArch ArchLinux on virtual machine](https://mirrors.pku.edu.cn/loongarch/archlinux/images/README.html).
-- 
2.27.0



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114563): https://edk2.groups.io/g/devel/message/114563
Mute This Topic: https://groups.io/mt/103971689/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 23/37] ArmVirtPkg: Move the FdtSerialPortAddressLib to OvmfPkg
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 23/37] ArmVirtPkg: Move the FdtSerialPortAddressLib to OvmfPkg Chao Li
@ 2024-01-29 19:27   ` Laszlo Ersek
  0 siblings, 0 replies; 89+ messages in thread
From: Laszlo Ersek @ 2024-01-29 19:27 UTC (permalink / raw)
  To: Chao Li, devel
  Cc: Ard Biesheuvel, Leif Lindholm, Sami Mujawar, Gerd Hoffmann,
	Jiewen Yao

On 1/26/24 07:30, Chao Li wrote:
> Move the FdtSerialPortAddressLib to Ovmfpkg so that other ARCH can
> easily use it.
> 
> Build-tested only (with "ArmVirtQemu.dsc and OvmfPkgX64.dsc").
> 
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584
> 
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Leif Lindholm <quic_llindhol@quicinc.com>
> Cc: Sami Mujawar <sami.mujawar@arm.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Chao Li <lichao@loongson.cn>
> ---
>  ArmVirtPkg/ArmVirt.dsc.inc                                    | 2 +-
>  ArmVirtPkg/ArmVirtPkg.dec                                     | 1 -
>  .../Include/Library/FdtSerialPortAddressLib.h                 | 0
>  .../Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c | 0
>  .../FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf       | 2 +-
>  OvmfPkg/OvmfPkg.dec                                           | 4 ++++
>  6 files changed, 6 insertions(+), 3 deletions(-)
>  rename {ArmVirtPkg => OvmfPkg}/Include/Library/FdtSerialPortAddressLib.h (100%)
>  rename {ArmVirtPkg => OvmfPkg}/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c (100%)
>  rename {ArmVirtPkg => OvmfPkg}/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf (90%)
> 
> diff --git a/ArmVirtPkg/ArmVirt.dsc.inc b/ArmVirtPkg/ArmVirt.dsc.inc
> index 9b23ef97ec..2bc6a29eb1 100644
> --- a/ArmVirtPkg/ArmVirt.dsc.inc
> +++ b/ArmVirtPkg/ArmVirt.dsc.inc
> @@ -122,7 +122,7 @@
>    # ARM PL011 UART Driver
>    PL011UartLib|ArmPlatformPkg/Library/PL011UartLib/PL011UartLib.inf
>    SerialPortLib|ArmVirtPkg/Library/FdtPL011SerialPortLib/FdtPL011SerialPortLib.inf
> -  FdtSerialPortAddressLib|ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf
> +  FdtSerialPortAddressLib|OvmfPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf
>  
>    PeCoffExtraActionLib|ArmPkg/Library/DebugPeCoffExtraActionLib/DebugPeCoffExtraActionLib.inf
>    #PeCoffExtraActionLib|MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf
> diff --git a/ArmVirtPkg/ArmVirtPkg.dec b/ArmVirtPkg/ArmVirtPkg.dec
> index 05d2d36c1d..a658c91031 100644
> --- a/ArmVirtPkg/ArmVirtPkg.dec
> +++ b/ArmVirtPkg/ArmVirtPkg.dec
> @@ -27,7 +27,6 @@
>  
>  [LibraryClasses]
>    ArmVirtMemInfoLib|Include/Library/ArmVirtMemInfoLib.h
> -  FdtSerialPortAddressLib|Include/Library/FdtSerialPortAddressLib.h
>  
>  [Guids.common]
>    gArmVirtTokenSpaceGuid = { 0x0B6F5CA7, 0x4F53, 0x445A, { 0xB7, 0x6E, 0x2E, 0x36, 0x5B, 0x80, 0x63, 0x66 } }
> diff --git a/ArmVirtPkg/Include/Library/FdtSerialPortAddressLib.h b/OvmfPkg/Include/Library/FdtSerialPortAddressLib.h
> similarity index 100%
> rename from ArmVirtPkg/Include/Library/FdtSerialPortAddressLib.h
> rename to OvmfPkg/Include/Library/FdtSerialPortAddressLib.h
> diff --git a/ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c b/OvmfPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c
> similarity index 100%
> rename from ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c
> rename to OvmfPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.c
> diff --git a/ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf b/OvmfPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf
> similarity index 90%
> rename from ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf
> rename to OvmfPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf
> index ae6d0d374b..e27742e9fa 100644
> --- a/ArmVirtPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf
> +++ b/OvmfPkg/Library/FdtSerialPortAddressLib/FdtSerialPortAddressLib.inf
> @@ -18,9 +18,9 @@
>    FdtSerialPortAddressLib.c
>  
>  [Packages]
> -  ArmVirtPkg/ArmVirtPkg.dec
>    EmbeddedPkg/EmbeddedPkg.dec
>    MdePkg/MdePkg.dec
> +  OvmfPkg/OvmfPkg.dec
>  
>  [LibraryClasses]
>    BaseLib
> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
> index 7bc2bf1674..13e69e6648 100644
> --- a/OvmfPkg/OvmfPkg.dec
> +++ b/OvmfPkg/OvmfPkg.dec
> @@ -29,6 +29,10 @@
>    ##  @libraryclass  Verify blobs read from the VMM
>    BlobVerifierLib|Include/Library/BlobVerifierLib.h
>  
> +  ##  @libraryclass  FdtSerialPortAddressLib
> +  #
> +  FdtSerialPortAddressLib|Include/Library/FdtSerialPortAddressLib.h
> +
>    ##  @libraryclass  Loads and boots a Linux kernel image
>    #
>    LoadLinuxLib|Include/Library/LoadLinuxLib.h

Reviewed-by: Laszlo Ersek <lersek@redhat.com>



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114737): https://edk2.groups.io/g/devel/message/114737
Mute This Topic: https://groups.io/mt/103971668/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/leave/12367111/7686176/1913456212/xyzzy [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 24/37] ArmVirtPkg: Move two PCD variables into OvmfPkg
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 24/37] ArmVirtPkg: Move two PCD variables into OvmfPkg Chao Li
@ 2024-01-29 19:49   ` Laszlo Ersek
  2024-01-30  1:24     ` Chao Li
  0 siblings, 1 reply; 89+ messages in thread
From: Laszlo Ersek @ 2024-01-29 19:49 UTC (permalink / raw)
  To: Chao Li, devel
  Cc: Ard Biesheuvel, Leif Lindholm, Sami Mujawar, Gerd Hoffmann,
	Jiewen Yao

On 1/26/24 07:30, Chao Li wrote:
> Move the PcdTerminalTypeGuidBuffer and PcdUninstallMemAttrProtocol into
> OvmfPkg so other ARCH can easily use it.
>
> Build-tested only (with "ArmVirtQemu.dsc and OvmfPkgX64.dsc").
>
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Leif Lindholm <quic_llindhol@quicinc.com>
> Cc: Sami Mujawar <sami.mujawar@arm.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Signed-off-by: Chao Li <lichao@loongson.cn>
> ---
>  ArmVirtPkg/ArmVirtPkg.dec                           | 13 -------------
>  ArmVirtPkg/ArmVirtQemu.dsc                          |  2 +-
>  ArmVirtPkg/ArmVirtQemuKernel.dsc                    |  2 +-
>  .../PlatformBootManagerLib.inf                      |  5 ++---
>  OvmfPkg/OvmfPkg.dec                                 | 13 +++++++++++++
>  5 files changed, 17 insertions(+), 18 deletions(-)
>
> diff --git a/ArmVirtPkg/ArmVirtPkg.dec b/ArmVirtPkg/ArmVirtPkg.dec
> index a658c91031..6aa5ea05f4 100644
> --- a/ArmVirtPkg/ArmVirtPkg.dec
> +++ b/ArmVirtPkg/ArmVirtPkg.dec
> @@ -41,21 +41,8 @@
>    gArmVirtTokenSpaceGuid.PcdTpm2SupportEnabled|FALSE|BOOLEAN|0x00000004
>
>  [PcdsFixedAtBuild, PcdsPatchableInModule]
> -  #
> -  # Binary representation of the GUID that determines the terminal type. The
> -  # size must be exactly 16 bytes. The default value corresponds to
> -  # EFI_VT_100_GUID.
> -  #
> -  gArmVirtTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x65, 0x60, 0xA6, 0xDF, 0x19, 0xB4, 0xD3, 0x11, 0x9A, 0x2D, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D}|VOID*|0x00000007
> -
>    ##
>    # This is the physical address of Rsdp which is the core struct of Acpi.
>    # Cloud Hypervisor has no other way to pass Rsdp address to the guest except use a PCD.
>    #
>    gArmVirtTokenSpaceGuid.PcdCloudHvAcpiRsdpBaseAddress|0x0|UINT64|0x00000005
> -
> -  ##
> -  # Whether the EFI memory attributes protocol should be uninstalled before
> -  # invoking the OS loader. This may be needed to work around problematic
> -  # builds of shim that use the protocol incorrectly.
> -  gArmVirtTokenSpaceGuid.PcdUninstallMemAttrProtocol|FALSE|BOOLEAN|0x00000006
> diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc
> index dbd2396c78..147180f645 100644
> --- a/ArmVirtPkg/ArmVirtQemu.dsc
> +++ b/ArmVirtPkg/ArmVirtQemu.dsc
> @@ -182,7 +182,7 @@
>  !if $(TTY_TERMINAL) == TRUE
>    gEfiMdePkgTokenSpaceGuid.PcdDefaultTerminalType|4
>    # Set terminal type to TtyTerm, the value encoded is EFI_TTY_TERM_GUID
> -  gArmVirtTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x80, 0x6d, 0x91, 0x7d, 0xb1, 0x5b, 0x8c, 0x45, 0xa4, 0x8f, 0xe2, 0x5f, 0xdd, 0x51, 0xef, 0x94}
> +  gUefiOvmfPkgTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x80, 0x6d, 0x91, 0x7d, 0xb1, 0x5b, 0x8c, 0x45, 0xa4, 0x8f, 0xe2, 0x5f, 0xdd, 0x51, 0xef, 0x94}
>  !else
>    gEfiMdePkgTokenSpaceGuid.PcdDefaultTerminalType|1
>  !endif
> diff --git a/ArmVirtPkg/ArmVirtQemuKernel.dsc b/ArmVirtPkg/ArmVirtQemuKernel.dsc
> index 6a6ecfc12a..c22a422353 100644
> --- a/ArmVirtPkg/ArmVirtQemuKernel.dsc
> +++ b/ArmVirtPkg/ArmVirtQemuKernel.dsc
> @@ -147,7 +147,7 @@
>  !if $(TTY_TERMINAL) == TRUE
>    gEfiMdePkgTokenSpaceGuid.PcdDefaultTerminalType|4
>    # Set terminal type to TtyTerm, the value encoded is EFI_TTY_TERM_GUID
> -  gArmVirtTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x80, 0x6d, 0x91, 0x7d, 0xb1, 0x5b, 0x8c, 0x45, 0xa4, 0x8f, 0xe2, 0x5f, 0xdd, 0x51, 0xef, 0x94}
> +  gUefiOvmfPkgTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x80, 0x6d, 0x91, 0x7d, 0xb1, 0x5b, 0x8c, 0x45, 0xa4, 0x8f, 0xe2, 0x5f, 0xdd, 0x51, 0xef, 0x94}
>  !else
>    gEfiMdePkgTokenSpaceGuid.PcdDefaultTerminalType|1
>  !endif
> diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
> index 70e4ebf94a..8e7cd5605f 100644
> --- a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
> +++ b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
> @@ -29,7 +29,6 @@
>    QemuKernel.c
>
>  [Packages]
> -  ArmVirtPkg/ArmVirtPkg.dec
>    MdeModulePkg/MdeModulePkg.dec
>    MdePkg/MdePkg.dec
>    OvmfPkg/OvmfPkg.dec
> @@ -56,15 +55,15 @@
>    UefiRuntimeServicesTableLib
>
>  [FixedPcd]
> -  gArmVirtTokenSpaceGuid.PcdUninstallMemAttrProtocol
>    gEfiMdePkgTokenSpaceGuid.PcdUartDefaultBaudRate
>    gEfiMdePkgTokenSpaceGuid.PcdUartDefaultDataBits
>    gEfiMdePkgTokenSpaceGuid.PcdUartDefaultParity
>    gEfiMdePkgTokenSpaceGuid.PcdUartDefaultStopBits
> +  gUefiOvmfPkgTokenSpaceGuid.PcdUninstallMemAttrProtocol
>
>  [Pcd]
> -  gArmVirtTokenSpaceGuid.PcdTerminalTypeGuidBuffer
>    gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut
> +  gUefiOvmfPkgTokenSpaceGuid.PcdTerminalTypeGuidBuffer
>
>  [Guids]
>    gEfiEndOfDxeEventGroupGuid
> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
> index 13e69e6648..fbc81e4c80 100644
> --- a/OvmfPkg/OvmfPkg.dec
> +++ b/OvmfPkg/OvmfPkg.dec
> @@ -405,6 +405,19 @@
>    #
>    gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeAllocationPadding|256|UINT32|0x6f
>
> +  #
> +  # Binary representation of the GUID that determines the terminal type. The
> +  # size must be exactly 16 bytes. The default value corresponds to
> +  # EFI_VT_100_GUID.
> +  #
> +  gUefiOvmfPkgTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x65, 0x60, 0xA6, 0xDF, 0x19, 0xB4, 0xD3, 0x11, 0x9A, 0x2D, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D}|VOID*|0x66
> +
> +  ##
> +  # Whether the EFI memory attributes protocol should be uninstalled before
> +  # invoking the OS loader. This may be needed to work around problematic
> +  # builds of shim that use the protocol incorrectly.
> +  gUefiOvmfPkgTokenSpaceGuid.PcdUninstallMemAttrProtocol|FALSE|BOOLEAN|0x67
> +
>  [PcdsFeatureFlag]
>    gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderPciTranslation|TRUE|BOOLEAN|0x1c
>    gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderMmioTranslation|FALSE|BOOLEAN|0x1d

This patch is hard to review; you can make it easier (for next time):

I suggest adding the following to your edk2 git config:

[core]
        attributesFile = BaseTools/Conf/gitattributes

[diff "ini"]
        xfuncname = ^\\[[A-Za-z0-9_., ]+]

Subsequently, the @@ hunk headers will show you the DEC file section
that's being modified; like this:

> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
> index 13e69e664842..fbc81e4c8070 100644
> --- a/OvmfPkg/OvmfPkg.dec
> +++ b/OvmfPkg/OvmfPkg.dec
> @@ -405,6 +405,19 @@ [PcdsFixedAtBuild, PcdsPatchableInModule]
>    #
>    gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeAllocationPadding|256|UINT32|0x6f
> 
> +  #
> +  # Binary representation of the GUID that determines the terminal type. The
> +  # size must be exactly 16 bytes. The default value corresponds to
> +  # EFI_VT_100_GUID.
> +  #
> +  gUefiOvmfPkgTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x65, 0x60, 0xA6, 0xDF, 0x19, 0xB4, 0xD3, 0x11, 0x9A, 0x2D, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D}|VOID*|0x66
> +
> +  ##
> +  # Whether the EFI memory attributes protocol should be uninstalled before
> +  # invoking the OS loader. This may be needed to work around problematic
> +  # builds of shim that use the protocol incorrectly.
> +  gUefiOvmfPkgTokenSpaceGuid.PcdUninstallMemAttrProtocol|FALSE|BOOLEAN|0x67
> +
>  [PcdsFeatureFlag]
>    gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderPciTranslation|TRUE|BOOLEAN|0x1c
>    gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderMmioTranslation|FALSE|BOOLEAN|0x1d

Note the "@@ [PcdsFixedAtBuild, PcdsPatchableInModule]" part.

This helps reviewers a lot.

Originally I thought you were moving these PCDs from the original
section

  [PcdsFixedAtBuild, PcdsPatchableInModule]

to the new section

  [PcdsDynamic, PcdsDynamicEx]

which would force a PCD type change.

(It is up to the DSC file to pick a type for the PCDs it assigns default
values to, but if the before-after type lists in the DEC file(s) have an
empty intersection, like it *seems* in this case, then the PCDs in the
DSC files cannot avoid a type change.)

In fact I didn't understand how this patch could successfully build.
After the patch, PcdTerminalTypeGuidBuffer could only be dynamic or
dynamicEx, but in the ArmVirt dsc files, the default values were still
set in section [PcdsFixedAtBuild.common]. So that would certainly cause
a build error.

But then I applied your entire set locally, and showed this patch with
the "smart" hunk headers enabled (as explained above). And that made me
realize this patch was actually correct.

The reason is that patch 18/37 "ArmVirtPkg: Move PCD of FDT base address
and FDT padding to OvmfPkg" introduces a new section to the OVMF DEC
file, [PcdsFixedAtBuild, PcdsPatchableInModule], and this PCD movement
targets the new section. So that's fine, but patch 18 is quite a bit
earlier in the series than this one; you can support reviewers by
including the section names in the hunk headers.

Reviewed-by: Laszlo Ersek <lersek@redhat.com>



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114739): https://edk2.groups.io/g/devel/message/114739
Mute This Topic: https://groups.io/mt/103971670/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/leave/12367111/7686176/1913456212/xyzzy [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 25/37] ArmVirtPkg: Move PlatformBootManagerLib to OvmfPkg
  2024-01-26  6:30 ` [edk2-devel] [PATCH v8 25/37] ArmVirtPkg: Move PlatformBootManagerLib to OvmfPkg Chao Li
@ 2024-01-29 19:51   ` Laszlo Ersek
  0 siblings, 0 replies; 89+ messages in thread
From: Laszlo Ersek @ 2024-01-29 19:51 UTC (permalink / raw)
  To: Chao Li, devel
  Cc: Ard Biesheuvel, Leif Lindholm, Sami Mujawar, Gerd Hoffmann,
	Jiewen Yao

On 1/26/24 07:30, Chao Li wrote:
> Moved the PlatformBootManagerLib to OvmfPkg and renamed to
> PlatformBootManagerLibLight for easy use by other ARCH.
> 
> Build-tested only (with "ArmVirtQemu.dsc and OvmfPkgX64.dsc").
> 
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584
> 
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Leif Lindholm <quic_llindhol@quicinc.com>
> Cc: Sami Mujawar <sami.mujawar@arm.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Lazlo Ersek <lersek@redhat.com>
> Signed-off-by: Chao Li <lichao@loongson.cn>
> ---
>  ArmVirtPkg/ArmVirtPkg.ci.yaml                                   | 1 -
>  ArmVirtPkg/ArmVirtQemu.dsc                                      | 2 +-
>  ArmVirtPkg/ArmVirtQemuKernel.dsc                                | 2 +-
>  .../Library/PlatformBootManagerLibLight}/PlatformBm.c           | 0
>  .../Library/PlatformBootManagerLibLight}/PlatformBm.h           | 0
>  .../PlatformBootManagerLibLight}/PlatformBootManagerLib.inf     | 2 +-
>  .../Library/PlatformBootManagerLibLight}/QemuKernel.c           | 0
>  7 files changed, 3 insertions(+), 4 deletions(-)
>  rename {ArmVirtPkg/Library/PlatformBootManagerLib => OvmfPkg/Library/PlatformBootManagerLibLight}/PlatformBm.c (100%)
>  rename {ArmVirtPkg/Library/PlatformBootManagerLib => OvmfPkg/Library/PlatformBootManagerLibLight}/PlatformBm.h (100%)
>  rename {ArmVirtPkg/Library/PlatformBootManagerLib => OvmfPkg/Library/PlatformBootManagerLibLight}/PlatformBootManagerLib.inf (93%)
>  rename {ArmVirtPkg/Library/PlatformBootManagerLib => OvmfPkg/Library/PlatformBootManagerLibLight}/QemuKernel.c (100%)
> 
> diff --git a/ArmVirtPkg/ArmVirtPkg.ci.yaml b/ArmVirtPkg/ArmVirtPkg.ci.yaml
> index 506b0e72f0..b186d4eb42 100644
> --- a/ArmVirtPkg/ArmVirtPkg.ci.yaml
> +++ b/ArmVirtPkg/ArmVirtPkg.ci.yaml
> @@ -24,7 +24,6 @@
>          ],
>          ## Both file path and directory path are accepted.
>          "IgnoreFiles": [
> -            "Library/PlatformBootManagerLib/PlatformBm.c"
>          ]
>      },
>      ## options defined .pytool/Plugin/CompilerPlugin
> diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc
> index 147180f645..e48c75b5e9 100644
> --- a/ArmVirtPkg/ArmVirtQemu.dsc
> +++ b/ArmVirtPkg/ArmVirtQemu.dsc
> @@ -70,7 +70,7 @@
>  
>    CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf
>    BootLogoLib|MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf
> -  PlatformBootManagerLib|ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
> +  PlatformBootManagerLib|OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBootManagerLib.inf
>    PlatformBmPrintScLib|OvmfPkg/Library/PlatformBmPrintScLib/PlatformBmPrintScLib.inf
>    CustomizedDisplayLib|MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf
>    FrameBufferBltLib|MdeModulePkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
> diff --git a/ArmVirtPkg/ArmVirtQemuKernel.dsc b/ArmVirtPkg/ArmVirtQemuKernel.dsc
> index c22a422353..668a65ba64 100644
> --- a/ArmVirtPkg/ArmVirtQemuKernel.dsc
> +++ b/ArmVirtPkg/ArmVirtQemuKernel.dsc
> @@ -69,7 +69,7 @@
>  
>    CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf
>    BootLogoLib|MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf
> -  PlatformBootManagerLib|ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
> +  PlatformBootManagerLib|OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBootManagerLib.inf
>    PlatformBmPrintScLib|OvmfPkg/Library/PlatformBmPrintScLib/PlatformBmPrintScLib.inf
>    CustomizedDisplayLib|MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf
>    FrameBufferBltLib|MdeModulePkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
> diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.c b/OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBm.c
> similarity index 100%
> rename from ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.c
> rename to OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBm.c
> diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.h b/OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBm.h
> similarity index 100%
> rename from ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.h
> rename to OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBm.h
> diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBootManagerLib.inf
> similarity index 93%
> rename from ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
> rename to OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBootManagerLib.inf
> index 8e7cd5605f..f2fb69bd3c 100644
> --- a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
> +++ b/OvmfPkg/Library/PlatformBootManagerLibLight/PlatformBootManagerLib.inf
> @@ -20,7 +20,7 @@
>  #
>  # The following information is for reference only and not required by the build tools.
>  #
> -#  VALID_ARCHITECTURES           = ARM AARCH64
> +#  VALID_ARCHITECTURES           = ARM AARCH64 LOONGARCH64
>  #
>  
>  [Sources]
> diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c b/OvmfPkg/Library/PlatformBootManagerLibLight/QemuKernel.c
> similarity index 100%
> rename from ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c
> rename to OvmfPkg/Library/PlatformBootManagerLibLight/QemuKernel.c

Reviewed-by: Laszlo Ersek <lersek@redhat.com>



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114740): https://edk2.groups.io/g/devel/message/114740
Mute This Topic: https://groups.io/mt/103971673/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/leave/12367111/7686176/1913456212/xyzzy [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 24/37] ArmVirtPkg: Move two PCD variables into OvmfPkg
  2024-01-29 19:49   ` Laszlo Ersek
@ 2024-01-30  1:24     ` Chao Li
  2024-01-30 16:45       ` Laszlo Ersek
  0 siblings, 1 reply; 89+ messages in thread
From: Chao Li @ 2024-01-30  1:24 UTC (permalink / raw)
  To: devel, lersek
  Cc: Ard Biesheuvel, Leif Lindholm, Sami Mujawar, Gerd Hoffmann,
	Jiewen Yao

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

Hi Laszlo,

My notes added below.


Thanks,
Chao
On 2024/1/30 03:49, Laszlo Ersek wrote:
> On 1/26/24 07:30, Chao Li wrote:
>> Move the PcdTerminalTypeGuidBuffer and PcdUninstallMemAttrProtocol into
>> OvmfPkg so other ARCH can easily use it.
>>
>> Build-tested only (with "ArmVirtQemu.dsc and OvmfPkgX64.dsc").
>>
>> BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>>
>> Cc: Ard Biesheuvel<ardb+tianocore@kernel.org>
>> Cc: Laszlo Ersek<lersek@redhat.com>
>> Cc: Leif Lindholm<quic_llindhol@quicinc.com>
>> Cc: Sami Mujawar<sami.mujawar@arm.com>
>> Cc: Gerd Hoffmann<kraxel@redhat.com>
>> Cc: Jiewen Yao<jiewen.yao@intel.com>
>> Signed-off-by: Chao Li<lichao@loongson.cn>
>> ---
>>   ArmVirtPkg/ArmVirtPkg.dec                           | 13 -------------
>>   ArmVirtPkg/ArmVirtQemu.dsc                          |  2 +-
>>   ArmVirtPkg/ArmVirtQemuKernel.dsc                    |  2 +-
>>   .../PlatformBootManagerLib.inf                      |  5 ++---
>>   OvmfPkg/OvmfPkg.dec                                 | 13 +++++++++++++
>>   5 files changed, 17 insertions(+), 18 deletions(-)
>>
>> diff --git a/ArmVirtPkg/ArmVirtPkg.dec b/ArmVirtPkg/ArmVirtPkg.dec
>> index a658c91031..6aa5ea05f4 100644
>> --- a/ArmVirtPkg/ArmVirtPkg.dec
>> +++ b/ArmVirtPkg/ArmVirtPkg.dec
>> @@ -41,21 +41,8 @@
>>     gArmVirtTokenSpaceGuid.PcdTpm2SupportEnabled|FALSE|BOOLEAN|0x00000004
>>
>>   [PcdsFixedAtBuild, PcdsPatchableInModule]
>> -  #
>> -  # Binary representation of the GUID that determines the terminal type. The
>> -  # size must be exactly 16 bytes. The default value corresponds to
>> -  # EFI_VT_100_GUID.
>> -  #
>> -  gArmVirtTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x65, 0x60, 0xA6, 0xDF, 0x19, 0xB4, 0xD3, 0x11, 0x9A, 0x2D, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D}|VOID*|0x00000007
>> -
>>     ##
>>     # This is the physical address of Rsdp which is the core struct of Acpi.
>>     # Cloud Hypervisor has no other way to pass Rsdp address to the guest except use a PCD.
>>     #
>>     gArmVirtTokenSpaceGuid.PcdCloudHvAcpiRsdpBaseAddress|0x0|UINT64|0x00000005
>> -
>> -  ##
>> -  # Whether the EFI memory attributes protocol should be uninstalled before
>> -  # invoking the OS loader. This may be needed to work around problematic
>> -  # builds of shim that use the protocol incorrectly.
>> -  gArmVirtTokenSpaceGuid.PcdUninstallMemAttrProtocol|FALSE|BOOLEAN|0x00000006
>> diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc
>> index dbd2396c78..147180f645 100644
>> --- a/ArmVirtPkg/ArmVirtQemu.dsc
>> +++ b/ArmVirtPkg/ArmVirtQemu.dsc
>> @@ -182,7 +182,7 @@
>>   !if $(TTY_TERMINAL) == TRUE
>>     gEfiMdePkgTokenSpaceGuid.PcdDefaultTerminalType|4
>>     # Set terminal type to TtyTerm, the value encoded is EFI_TTY_TERM_GUID
>> -  gArmVirtTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x80, 0x6d, 0x91, 0x7d, 0xb1, 0x5b, 0x8c, 0x45, 0xa4, 0x8f, 0xe2, 0x5f, 0xdd, 0x51, 0xef, 0x94}
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x80, 0x6d, 0x91, 0x7d, 0xb1, 0x5b, 0x8c, 0x45, 0xa4, 0x8f, 0xe2, 0x5f, 0xdd, 0x51, 0xef, 0x94}
>>   !else
>>     gEfiMdePkgTokenSpaceGuid.PcdDefaultTerminalType|1
>>   !endif
>> diff --git a/ArmVirtPkg/ArmVirtQemuKernel.dsc b/ArmVirtPkg/ArmVirtQemuKernel.dsc
>> index 6a6ecfc12a..c22a422353 100644
>> --- a/ArmVirtPkg/ArmVirtQemuKernel.dsc
>> +++ b/ArmVirtPkg/ArmVirtQemuKernel.dsc
>> @@ -147,7 +147,7 @@
>>   !if $(TTY_TERMINAL) == TRUE
>>     gEfiMdePkgTokenSpaceGuid.PcdDefaultTerminalType|4
>>     # Set terminal type to TtyTerm, the value encoded is EFI_TTY_TERM_GUID
>> -  gArmVirtTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x80, 0x6d, 0x91, 0x7d, 0xb1, 0x5b, 0x8c, 0x45, 0xa4, 0x8f, 0xe2, 0x5f, 0xdd, 0x51, 0xef, 0x94}
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x80, 0x6d, 0x91, 0x7d, 0xb1, 0x5b, 0x8c, 0x45, 0xa4, 0x8f, 0xe2, 0x5f, 0xdd, 0x51, 0xef, 0x94}
>>   !else
>>     gEfiMdePkgTokenSpaceGuid.PcdDefaultTerminalType|1
>>   !endif
>> diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
>> index 70e4ebf94a..8e7cd5605f 100644
>> --- a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
>> +++ b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
>> @@ -29,7 +29,6 @@
>>     QemuKernel.c
>>
>>   [Packages]
>> -  ArmVirtPkg/ArmVirtPkg.dec
>>     MdeModulePkg/MdeModulePkg.dec
>>     MdePkg/MdePkg.dec
>>     OvmfPkg/OvmfPkg.dec
>> @@ -56,15 +55,15 @@
>>     UefiRuntimeServicesTableLib
>>
>>   [FixedPcd]
>> -  gArmVirtTokenSpaceGuid.PcdUninstallMemAttrProtocol
>>     gEfiMdePkgTokenSpaceGuid.PcdUartDefaultBaudRate
>>     gEfiMdePkgTokenSpaceGuid.PcdUartDefaultDataBits
>>     gEfiMdePkgTokenSpaceGuid.PcdUartDefaultParity
>>     gEfiMdePkgTokenSpaceGuid.PcdUartDefaultStopBits
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdUninstallMemAttrProtocol
>>
>>   [Pcd]
>> -  gArmVirtTokenSpaceGuid.PcdTerminalTypeGuidBuffer
>>     gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdTerminalTypeGuidBuffer
>>
>>   [Guids]
>>     gEfiEndOfDxeEventGroupGuid
>> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
>> index 13e69e6648..fbc81e4c80 100644
>> --- a/OvmfPkg/OvmfPkg.dec
>> +++ b/OvmfPkg/OvmfPkg.dec
>> @@ -405,6 +405,19 @@
>>     #
>>     gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeAllocationPadding|256|UINT32|0x6f
>>
>> +  #
>> +  # Binary representation of the GUID that determines the terminal type. The
>> +  # size must be exactly 16 bytes. The default value corresponds to
>> +  # EFI_VT_100_GUID.
>> +  #
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x65, 0x60, 0xA6, 0xDF, 0x19, 0xB4, 0xD3, 0x11, 0x9A, 0x2D, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D}|VOID*|0x66
>> +
>> +  ##
>> +  # Whether the EFI memory attributes protocol should be uninstalled before
>> +  # invoking the OS loader. This may be needed to work around problematic
>> +  # builds of shim that use the protocol incorrectly.
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdUninstallMemAttrProtocol|FALSE|BOOLEAN|0x67
>> +
>>   [PcdsFeatureFlag]
>>     gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderPciTranslation|TRUE|BOOLEAN|0x1c
>>     gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderMmioTranslation|FALSE|BOOLEAN|0x1d
> This patch is hard to review; you can make it easier (for next time):
>
> I suggest adding the following to your edk2 git config:
>
> [core]
>          attributesFile = BaseTools/Conf/gitattributes
>
> [diff "ini"]
>          xfuncname = ^\\[[A-Za-z0-9_., ]+]
Next time I will add these two configures in my git config file, it will 
be reflected in V9 if it needed to commit.
>
> Subsequently, the @@ hunk headers will show you the DEC file section
> that's being modified; like this:
>
>> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
>> index 13e69e664842..fbc81e4c8070 100644
>> --- a/OvmfPkg/OvmfPkg.dec
>> +++ b/OvmfPkg/OvmfPkg.dec
>> @@ -405,6 +405,19 @@ [PcdsFixedAtBuild, PcdsPatchableInModule]
>>     #
>>     gUefiOvmfPkgTokenSpaceGuid.PcdDeviceTreeAllocationPadding|256|UINT32|0x6f
>>
>> +  #
>> +  # Binary representation of the GUID that determines the terminal type. The
>> +  # size must be exactly 16 bytes. The default value corresponds to
>> +  # EFI_VT_100_GUID.
>> +  #
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdTerminalTypeGuidBuffer|{0x65, 0x60, 0xA6, 0xDF, 0x19, 0xB4, 0xD3, 0x11, 0x9A, 0x2D, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D}|VOID*|0x66
>> +
>> +  ##
>> +  # Whether the EFI memory attributes protocol should be uninstalled before
>> +  # invoking the OS loader. This may be needed to work around problematic
>> +  # builds of shim that use the protocol incorrectly.
>> +  gUefiOvmfPkgTokenSpaceGuid.PcdUninstallMemAttrProtocol|FALSE|BOOLEAN|0x67
>> +
>>   [PcdsFeatureFlag]
>>     gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderPciTranslation|TRUE|BOOLEAN|0x1c
>>     gUefiOvmfPkgTokenSpaceGuid.PcdQemuBootOrderMmioTranslation|FALSE|BOOLEAN|0x1d
> Note the "@@ [PcdsFixedAtBuild, PcdsPatchableInModule]" part.
>
> This helps reviewers a lot.
>
> Originally I thought you were moving these PCDs from the original
> section
>
>    [PcdsFixedAtBuild, PcdsPatchableInModule]
>
> to the new section
>
>    [PcdsDynamic, PcdsDynamicEx]
>
> which would force a PCD type change.
>
> (It is up to the DSC file to pick a type for the PCDs it assigns default
> values to, but if the before-after type lists in the DEC file(s) have an
> empty intersection, like it *seems* in this case, then the PCDs in the
> DSC files cannot avoid a type change.)
>
> In fact I didn't understand how this patch could successfully build.
> After the patch, PcdTerminalTypeGuidBuffer could only be dynamic or
> dynamicEx, but in the ArmVirt dsc files, the default values were still
> set in section [PcdsFixedAtBuild.common]. So that would certainly cause
> a build error.
>
> But then I applied your entire set locally, and showed this patch with
> the "smart" hunk headers enabled (as explained above). And that made me
> realize this patch was actually correct.
>
> The reason is that patch 18/37 "ArmVirtPkg: Move PCD of FDT base address
> and FDT padding to OvmfPkg" introduces a new section to the OVMF DEC
> file, [PcdsFixedAtBuild, PcdsPatchableInModule], and this PCD movement
> targets the new section. So that's fine, but patch 18 is quite a bit
> earlier in the series than this one; you can support reviewers by
> including the section names in the hunk headers.

Ha, yes, patch 18 is added a new type, and I just add them under this 
new type and it worked. Do you recommend these two PCDs keep the type 
with PcdsFixedAtBuild only? If yes, in V9 I would move them under 
PcdsFixedAtBuild.

>
> Reviewed-by: Laszlo Ersek<lersek@redhat.com>
>
>
>
> 
>


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114755): https://edk2.groups.io/g/devel/message/114755
Mute This Topic: https://groups.io/mt/103971670/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 24/37] ArmVirtPkg: Move two PCD variables into OvmfPkg
  2024-01-30  1:24     ` Chao Li
@ 2024-01-30 16:45       ` Laszlo Ersek
  2024-01-31  1:30         ` Chao Li
  0 siblings, 1 reply; 89+ messages in thread
From: Laszlo Ersek @ 2024-01-30 16:45 UTC (permalink / raw)
  To: Chao Li, devel
  Cc: Ard Biesheuvel, Leif Lindholm, Sami Mujawar, Gerd Hoffmann,
	Jiewen Yao

On 1/30/24 02:24, Chao Li wrote:
> On 2024/1/30 03:49, Laszlo Ersek wrote:

>> I suggest adding the following to your edk2 git config:
>>
>> [core]
>>         attributesFile = BaseTools/Conf/gitattributes
>>
>> [diff "ini"]
>>         xfuncname = ^\\[[A-Za-z0-9_., ]+]

> Next time I will add these two configures in my git config file, it will
> be reflected in V9 if it needed to commit.

Thanks -- yes, that was what I meant. Certainly no need to repost the
series just because of that; it's just that getting these settings in
place "eventually" is good.

> Ha, yes, patch 18 is added a new type, and I just add them under this
> new type and it worked. Do you recommend these two PCDs keep the type
> with PcdsFixedAtBuild only? If yes, in V9 I would move them under
> PcdsFixedAtBuild.

No, I think you did the right thing. The original permitted storage type
list was [PcdsFixedAtBuild, PcdsPatchableInModule], and IIRC at least
the ArmVirtQemuKernel platform relies on patchable PCDs. So restricting
the storage type list to just [PcdsFixedAtBuild] across the move could
cause a regression, at least in theory -- so it's best not to disturb
that. Indeed, thus far we've not needed PcdsPatchableInModule in OVMF,
but now that OvmfPkg accommodates more widely used artifacts, we do!

Thanks!
Laszlo



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114803): https://edk2.groups.io/g/devel/message/114803
Mute This Topic: https://groups.io/mt/103971670/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/leave/12367111/7686176/1913456212/xyzzy [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 24/37] ArmVirtPkg: Move two PCD variables into OvmfPkg
  2024-01-30 16:45       ` Laszlo Ersek
@ 2024-01-31  1:30         ` Chao Li
  0 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-31  1:30 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: Ard Biesheuvel, Leif Lindholm, Sami Mujawar, Gerd Hoffmann,
	Jiewen Yao

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

OK, I got it, thank you for carefully reviewing my patch set, thank you 
very mach!


Thanks,
Chao
On 2024/1/31 00:45, Laszlo Ersek wrote:
> On 1/30/24 02:24, Chao Li wrote:
>> On 2024/1/30 03:49, Laszlo Ersek wrote:
>>> I suggest adding the following to your edk2 git config:
>>>
>>> [core]
>>>          attributesFile = BaseTools/Conf/gitattributes
>>>
>>> [diff "ini"]
>>>          xfuncname = ^\\[[A-Za-z0-9_., ]+]
>> Next time I will add these two configures in my git config file, it will
>> be reflected in V9 if it needed to commit.
> Thanks -- yes, that was what I meant. Certainly no need to repost the
> series just because of that; it's just that getting these settings in
> place "eventually" is good.
>
>> Ha, yes, patch 18 is added a new type, and I just add them under this
>> new type and it worked. Do you recommend these two PCDs keep the type
>> with PcdsFixedAtBuild only? If yes, in V9 I would move them under
>> PcdsFixedAtBuild.
> No, I think you did the right thing. The original permitted storage type
> list was [PcdsFixedAtBuild, PcdsPatchableInModule], and IIRC at least
> the ArmVirtQemuKernel platform relies on patchable PCDs. So restricting
> the storage type list to just [PcdsFixedAtBuild] across the move could
> cause a regression, at least in theory -- so it's best not to disturb
> that. Indeed, thus far we've not needed PcdsPatchableInModule in OVMF,
> but now that OvmfPkg accommodates more widely used artifacts, we do!
>
> Thanks!
> Laszlo


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114841): https://edk2.groups.io/g/devel/message/114841
Mute This Topic: https://groups.io/mt/103971670/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 11/37] UefiCpuPkg: Add LoongArch64 CPU Timer instance
       [not found] ` <17ADD1D5A196C454.24595@groups.io>
@ 2024-01-31  3:30   ` Chao Li
       [not found]   ` <17AF510405DE784C.15701@groups.io>
  1 sibling, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-31  3:30 UTC (permalink / raw)
  To: devel, Ray Ni; +Cc: Eric Dong, Rahul Kumar, Gerd Hoffmann

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

Hi Ray,

Can you please help to review this patch again?


Thanks,
Chao
On 2024/1/26 14:29, Chao Li wrote:
> Add the LoongArch64 CPU Timer instance to CpuTimerLib, using CPUCFG 0x4
> and 0x5 for Stable Counter frequency.
>
> BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>
> Cc: Eric Dong<eric.dong@intel.com>
> Cc: Ray Ni<ray.ni@intel.com>
> Cc: Rahul Kumar<rahul1.kumar@intel.com>
> Cc: Gerd Hoffmann<kraxel@redhat.com>
> Signed-off-by: Chao Li<lichao@loongson.cn>
> ---
>   .../Library/CpuTimerLib/BaseCpuTimerLib.inf   |   9 +-
>   .../CpuTimerLib/LoongArch64/CpuTimerLib.c     | 251 ++++++++++++++++++
>   2 files changed, 258 insertions(+), 2 deletions(-)
>   create mode 100644 UefiCpuPkg/Library/CpuTimerLib/LoongArch64/CpuTimerLib.c
>
> diff --git a/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf b/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf
> index de0648de91..7e6152ef7e 100644
> --- a/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf
> +++ b/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf
> @@ -5,6 +5,7 @@
>   #  counter features are provided by the processors time stamp counter.
>   #
>   #  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
> +#  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>   #  SPDX-License-Identifier: BSD-2-Clause-Patent
>   #
>   ##
> @@ -18,18 +19,22 @@
>     LIBRARY_CLASS                  = TimerLib
>     MODULE_UNI_FILE                = BaseCpuTimerLib.uni
>   
> -[Sources]
> +[Sources.IA32, Sources.X64]
>     CpuTimerLib.c
>     BaseCpuTimerLib.c
>   
> +[Sources.LOONGARCH64]
> +  LoongArch64/CpuTimerLib.c
> +
>   [Packages]
>     MdePkg/MdePkg.dec
>     UefiCpuPkg/UefiCpuPkg.dec
>   
>   [LibraryClasses]
>     BaseLib
> -  PcdLib
>     DebugLib
> +  PcdLib
> +  SafeIntLib
>   
>   [Pcd]
>     gUefiCpuPkgTokenSpaceGuid.PcdCpuCoreCrystalClockFrequency  ## CONSUMES
> diff --git a/UefiCpuPkg/Library/CpuTimerLib/LoongArch64/CpuTimerLib.c b/UefiCpuPkg/Library/CpuTimerLib/LoongArch64/CpuTimerLib.c
> new file mode 100644
> index 0000000000..a5ae8d0185
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuTimerLib/LoongArch64/CpuTimerLib.c
> @@ -0,0 +1,251 @@
> +/** @file
> +  CPUCFG 0x4 and 0x5 for Stable Counter frequency instance of Timer Library.
> +
> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#include <Base.h>
> +#include <Library/BaseLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/SafeIntLib.h>
> +#include <Library/TimerLib.h>
> +#include <Register/LoongArch64/Cpucfg.h>
> +
> +/**
> +  Calculate clock frequency using CPUCFG 0x4 and 0x5 registers.
> +
> +  @param  VOID.
> +
> +  @return The frequency in Hz.
> +
> +**/
> +STATIC
> +UINT64
> +CalcConstFreq (
> +  VOID
> +  )
> +{
> +  UINT32                 BaseFreq;
> +  UINT64                 ClockMultiplier;
> +  UINT32                 ClockDivide;
> +  CPUCFG_REG4_INFO_DATA  CcFreq;
> +  CPUCFG_REG5_INFO_DATA  CpucfgReg5Data;
> +  UINT64                 StableTimerFreq;
> +
> +  //
> +  // Get the the crystal frequency corresponding to the constant
> +  // frequency timer and the clock used by the timer.
> +  //
> +  AsmCpucfg (CPUCFG_REG4_INFO, &CcFreq.Uint32);
> +
> +  //
> +  // Get the multiplication factor and frequency division factor
> +  // corresponding to the constant frequency timer and the clock
> +  // used by the timer.
> +  //
> +  AsmCpucfg (CPUCFG_REG5_INFO, &CpucfgReg5Data.Uint32);
> +
> +  BaseFreq        = CcFreq.Bits.CC_FREQ;
> +  ClockMultiplier = CpucfgReg5Data.Bits.CC_MUL & 0xFFFF;
> +  ClockDivide     = CpucfgReg5Data.Bits.CC_DIV & 0xFFFF;
> +
> +  if ((BaseFreq == 0x0) || (ClockMultiplier == 0x0) || (ClockDivide == 0x0)) {
> +    DEBUG ((
> +      DEBUG_ERROR,
> +      "LoongArch Stable Timer is not available in the CPU, hence this library cannot be used.\n"
> +      ));
> +    ASSERT (FALSE);
> +    CpuDeadLoop ();
> +  }
> +
> +  StableTimerFreq = ((ClockMultiplier * BaseFreq) / ClockDivide);
> +
> +  if (StableTimerFreq == 0x0) {
> +    ASSERT (FALSE);
> +  }
> +
> +  return StableTimerFreq;
> +}
> +
> +/**
> +  Stalls the CPU for at least the given number of microseconds.
> +
> +  Stalls the CPU for the number of microseconds specified by MicroSeconds.
> +
> +  @param  MicroSeconds  The minimum number of microseconds to delay.
> +
> +  @return MicroSeconds
> +
> +**/
> +UINTN
> +EFIAPI
> +MicroSecondDelay (
> +  IN UINTN  MicroSeconds
> +  )
> +{
> +  UINT64         CurrentTicks, ExceptedTicks, Remaining;
> +  RETURN_STATUS  Status;
> +
> +  Status = SafeUint64Mult (MicroSeconds, CalcConstFreq (), &Remaining);
> +  ASSERT_RETURN_ERROR (Status);
> +
> +  ExceptedTicks  = DivU64x32 (Remaining, 1000000U);
> +  CurrentTicks   = AsmReadStableCounter ();
> +  ExceptedTicks += CurrentTicks;
> +
> +  do {
> +    CurrentTicks = AsmReadStableCounter ();
> +  } while (CurrentTicks < ExceptedTicks);
> +
> +  return MicroSeconds;
> +}
> +
> +/**
> +  Stalls the CPU for at least the given number of nanoseconds.
> +
> +  Stalls the CPU for the number of nanoseconds specified by NanoSeconds.
> +
> +  @param  NanoSeconds The minimum number of nanoseconds to delay.
> +
> +  @return NanoSeconds
> +
> +**/
> +UINTN
> +EFIAPI
> +NanoSecondDelay (
> +  IN UINTN  NanoSeconds
> +  )
> +{
> +  UINTN  MicroSeconds;
> +
> +  // Round up to 1us Tick Number
> +  MicroSeconds  = NanoSeconds / 1000;
> +  MicroSeconds += ((NanoSeconds % 1000) == 0) ? 0 : 1;
> +
> +  MicroSecondDelay (MicroSeconds);
> +
> +  return NanoSeconds;
> +}
> +
> +/**
> +  Retrieves the current value of a 64-bit free running Stable Counter.
> +
> +  The LoongArch defines a constant frequency timer, whose main body is a
> +  64-bit counter called StableCounter. StableCounter is set to 0 after
> +  reset, and then increments by 1 every counting clock cycle. When the
> +  count reaches all 1s, it automatically wraps around to 0 and continues
> +  to increment.
> +  The properties of the Stable Counter can be retrieved from
> +  GetPerformanceCounterProperties().
> +
> +  @return The current value of the Stable Counter.
> +
> +**/
> +UINT64
> +EFIAPI
> +GetPerformanceCounter (
> +  VOID
> +  )
> +{
> +  //
> +  // Just return the value of Stable Counter.
> +  //
> +  return AsmReadStableCounter ();
> +}
> +
> +/**
> +  Retrieves the 64-bit frequency in Hz and the range of Stable Counter
> +  values.
> +
> +  If StartValue is not NULL, then the value that the stbale counter starts
> +  with immediately after is it rolls over is returned in StartValue. If
> +  EndValue is not NULL, then the value that the stable counter end with
> +  immediately before it rolls over is returned in EndValue. The 64-bit
> +  frequency of the system frequency in Hz is always returned.
> +
> +  @param  StartValue  The value the stable counter starts with when it
> +                      rolls over.
> +  @param  EndValue    The value that the stable counter ends with before
> +                      it rolls over.
> +
> +  @return The frequency in Hz.
> +
> +**/
> +UINT64
> +EFIAPI
> +GetPerformanceCounterProperties (
> +  OUT UINT64  *StartValue   OPTIONAL,
> +  OUT UINT64  *EndValue     OPTIONAL
> +  )
> +{
> +  if (StartValue != NULL) {
> +    *StartValue = 0;
> +  }
> +
> +  if (EndValue != NULL) {
> +    *EndValue = 0xFFFFFFFFFFFFFFFFULL;
> +  }
> +
> +  return CalcConstFreq ();
> +}
> +
> +/**
> +  Converts elapsed ticks of performance counter to time in nanoseconds.
> +
> +  This function converts the elapsed ticks of running performance counter to
> +  time value in unit of nanoseconds.
> +
> +  @param  Ticks     The number of elapsed ticks of running performance counter.
> +
> +  @return The elapsed time in nanoseconds.
> +
> +**/
> +UINT64
> +EFIAPI
> +GetTimeInNanoSecond (
> +  IN UINT64  Ticks
> +  )
> +{
> +  UINT64         Frequency;
> +  UINT64         NanoSeconds;
> +  UINT64         Remainder;
> +  INTN           Shift;
> +  RETURN_STATUS  Status;
> +
> +  Frequency = GetPerformanceCounterProperties (NULL, NULL);
> +
> +  //
> +  //          Ticks
> +  // Time = --------- x 1,000,000,000
> +  //        Frequency
> +  //
> +  Status = SafeUint64Mult (
> +             DivU64x64Remainder (Ticks, Frequency, &Remainder),
> +             1000000000u,
> +             &NanoSeconds
> +             );
> +
> +  //
> +  // Ensure (Remainder * 1,000,000,000) will not overflow 64-bit.
> +  // Since 2^29 < 1,000,000,000 = 0x3B9ACA00 < 2^30, Remainder should < 2^(64-30) = 2^34,
> +  // i.e. highest bit set in Remainder should <= 33.
> +  //
> +  Shift     = MAX (0, HighBitSet64 (Remainder) - 33);
> +  Remainder = RShiftU64 (Remainder, (UINTN)Shift);
> +  Frequency = RShiftU64 (Frequency, (UINTN)Shift);
> +
> +  Status = SafeUint64Add (
> +             NanoSeconds,
> +             DivU64x64Remainder (
> +               MultU64x32 (Remainder, 1000000000u),
> +               Frequency,
> +               NULL
> +               ),
> +             &NanoSeconds
> +             );
> +  ASSERT_RETURN_ERROR (Status);
> +
> +  return NanoSeconds;
> +}


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114848): https://edk2.groups.io/g/devel/message/114848
Mute This Topic: https://groups.io/mt/104068936/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 12/37] UefiCpuPkg: Add CPU exception library for LoongArch
       [not found] ` <17ADD1D7001C37D6.11113@groups.io>
@ 2024-01-31  3:31   ` Chao Li
       [not found]   ` <17AF510933F4B8FA.15701@groups.io>
  1 sibling, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-31  3:31 UTC (permalink / raw)
  To: devel, Ray Ni; +Cc: Eric Dong, Rahul Kumar, Gerd Hoffmann, Baoqi Zhang

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

Hi Ray,

Can you please help to review this patch again?


Thanks,
Chao
On 2024/1/26 14:29, Chao Li wrote:
> Added LoongArch exception handler into CpuExceptionHandlerLib.
>
> Adjust the file order in INF of CpuExceptionHandlerLib with alphabetical
> order.
>
> Adjust files order in CpuExceptionHandlerLib INF in alphabetical order.
>
> BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>
> Cc: Eric Dong<eric.dong@intel.com>
> Cc: Ray Ni<ray.ni@intel.com>
> Cc: Rahul Kumar<rahul1.kumar@intel.com>
> Cc: Gerd Hoffmann<kraxel@redhat.com>
> Signed-off-by: Chao Li<lichao@loongson.cn>
> Co-authored-by: Baoqi Zhang<zhangbaoqi@loongson.cn>
> ---
>   .../DxeCpuExceptionHandlerLib.inf             |  34 +-
>   .../LoongArch/DxeExceptionLib.c               | 198 ++++++++++
>   .../LoongArch/ExceptionCommon.c               | 171 ++++++++
>   .../LoongArch/ExceptionCommon.h               | 131 +++++++
>   .../LoongArch64/ArchExceptionHandler.c        | 268 +++++++++++++
>   .../LoongArch64/ExceptionHandlerAsm.S         | 366 ++++++++++++++++++
>   .../LoongArch/SecPeiExceptionLib.c            | 102 +++++
>   .../SecPeiCpuExceptionHandlerLib.inf          |  29 +-
>   UefiCpuPkg/UefiCpuPkg.dec                     |   5 +
>   9 files changed, 1283 insertions(+), 21 deletions(-)
>   create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/DxeExceptionLib.c
>   create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.c
>   create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.h
>   create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ArchExceptionHandler.c
>   create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ExceptionHandlerAsm.S
>   create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/SecPeiExceptionLib.c
>
> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
> index fdbebadab9..f5bacbe2bc 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
> @@ -18,25 +18,32 @@
>   #
>   # The following information is for reference only and not required by the build tools.
>   #
> -#  VALID_ARCHITECTURES           = IA32 X64
> +#  VALID_ARCHITECTURES           = IA32 X64 LOONGARCH64
>   #
>   
>   [Sources.Ia32]
> -  Ia32/ExceptionHandlerAsm.nasm
> -  Ia32/ExceptionTssEntryAsm.nasm
>     Ia32/ArchExceptionHandler.c
>     Ia32/ArchInterruptDefs.h
> +  Ia32/ExceptionHandlerAsm.nasm
> +  Ia32/ExceptionTssEntryAsm.nasm
>   
>   [Sources.X64]
> -  X64/ExceptionHandlerAsm.nasm
>     X64/ArchExceptionHandler.c
>     X64/ArchInterruptDefs.h
> +  X64/ExceptionHandlerAsm.nasm
>   
> -[Sources.common]
> +[Sources.Ia32, Sources.X64]
>     CpuExceptionCommon.h
>     CpuExceptionCommon.c
> -  PeiDxeSmmCpuException.c
>     DxeException.c
> +  PeiDxeSmmCpuException.c
> +
> +[Sources.LoongArch64]
> +  LoongArch/DxeExceptionLib.c
> +  LoongArch/ExceptionCommon.h
> +  LoongArch/ExceptionCommon.c
> +  LoongArch/LoongArch64/ArchExceptionHandler.c
> +  LoongArch/LoongArch64/ExceptionHandlerAsm.S | GCC
>   
>   [Pcd]
>     gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard
> @@ -51,16 +58,19 @@
>     MdeModulePkg/MdeModulePkg.dec
>     UefiCpuPkg/UefiCpuPkg.dec
>   
> -[LibraryClasses]
> +[LibraryClasses.common]
>     BaseLib
> -  SerialPortLib
> +  CpuLib
> +  DebugLib
> +  MemoryAllocationLib
> +  PeCoffGetEntryPointLib
>     PrintLib
> +  SerialPortLib
>     SynchronizationLib
> -  LocalApicLib
> -  PeCoffGetEntryPointLib
> -  MemoryAllocationLib
> -  DebugLib
> +
> +[LibraryClasses.Ia32, LibraryClasses.X64]
>     CcExitLib
> +  LocalApicLib
>   
>   [BuildOptions]
>     XCODE:*_*_X64_NASM_FLAGS = -D NO_ABSOLUTE_RELOCS_IN_TEXT
> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/DxeExceptionLib.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/DxeExceptionLib.c
> new file mode 100644
> index 0000000000..2c5d202b33
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/DxeExceptionLib.c
> @@ -0,0 +1,198 @@
> +/** @file DxeExceptionLib.c
> +
> +  LoongArch exception library implemenation for DXE modules.
> +
> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#include <Library/BaseLib.h>
> +#include <Library/CpuExceptionHandlerLib.h>
> +#include <Library/CpuLib.h>
> +#include <Library/CacheMaintenanceLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/SerialPortLib.h>
> +#include <Protocol/DebugSupport.h>
> +#include <Register/LoongArch64/Csr.h>
> +
> +#include "ExceptionCommon.h"
> +
> +EFI_EXCEPTION_CALLBACK  ExternalInterruptHandler[MAX_LOONGARCH_INTERRUPT + 1] = { 0 };
> +EFI_EXCEPTION_CALLBACK  ExceptionHandler[MAX_LOONGARCH_EXCEPTION + 1]         = { 0 };
> +
> +/**
> +  Registers a function to be called from the processor interrupt or exception handler.
> +
> +  This function registers and enables the handler specified by InterruptHandler for a processor
> +  interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
> +  handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
> +  The installed handler is called once for each processor interrupt or exception.
> +
> +  @param  InterruptType    A pointer to the processor's current interrupt state. Set to TRUE if interrupts
> +                           are enabled and FALSE if interrupts are disabled.
> +  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
> +                           when a processor interrupt occurs. If this parameter is NULL, then the handler
> +                           will be uninstalled.
> +
> +  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
> +  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was
> +                                previously installed.
> +  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
> +                                previously installed.
> +  @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
> +
> +**/
> +EFI_STATUS
> +RegisterCpuInterruptHandler (
> +  IN EFI_EXCEPTION_TYPE         InterruptType,
> +  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
> +  )
> +{
> +  EFI_EXCEPTION_TYPE  ExceptionType;
> +
> +  ExceptionType = InterruptType & CSR_ESTAT_EXC;
> +
> +  if (ExceptionType != 0) {
> +    //
> +    // Exception
> +    //
> +    if (ExceptionType > EXCEPT_LOONGARCH_FPE) {
> +      return EFI_UNSUPPORTED;
> +    }
> +
> +    ExceptionType >>= CSR_ESTAT_EXC_SHIFT;
> +
> +    if ((InterruptHandler == NULL) && (ExceptionHandler[InterruptType] == NULL)) {
> +      return EFI_INVALID_PARAMETER;
> +    }
> +
> +    if ((InterruptHandler != NULL) && (ExceptionHandler[ExceptionType] != NULL)) {
> +      return EFI_ALREADY_STARTED;
> +    }
> +
> +    ExceptionHandler[ExceptionType] = InterruptHandler;
> +  } else {
> +    //
> +    // Interrupt
> +    //
> +    if (InterruptType > MAX_LOONGARCH_INTERRUPT) {
> +      return EFI_UNSUPPORTED;
> +    }
> +
> +    if ((InterruptHandler == NULL) && (ExternalInterruptHandler[InterruptType] == NULL)) {
> +      return EFI_INVALID_PARAMETER;
> +    }
> +
> +    if ((InterruptHandler != NULL) && (ExternalInterruptHandler[InterruptType] != NULL)) {
> +      return EFI_ALREADY_STARTED;
> +    }
> +
> +    ExternalInterruptHandler[InterruptType] = InterruptHandler;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Common exception handler.
> +
> +  @param ExceptionType  Exception type.
> +  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +
> +**/
> +VOID
> +EFIAPI
> +CommonExceptionHandler (
> +  IN     EFI_EXCEPTION_TYPE  ExceptionType,
> +  IN OUT EFI_SYSTEM_CONTEXT  SystemContext
> +  )
> +{
> +  EFI_EXCEPTION_TYPE  InterruptType;
> +
> +  if (ExceptionType == EXCEPT_LOONGARCH_INT) {
> +    //
> +    // Interrupt
> +    //
> +    InterruptType = GetInterruptType (SystemContext);
> +    if (InterruptType == 0xFF) {
> +      ExceptionType = InterruptType;
> +    } else {
> +      if ((ExternalInterruptHandler != NULL) && (ExternalInterruptHandler[InterruptType] != NULL)) {
> +        ExternalInterruptHandler[InterruptType](InterruptType, SystemContext);
> +        return;
> +      }
> +    }
> +  } else if (ExceptionType == EXCEPT_LOONGARCH_FPD) {
> +    EnableFloatingPointUnits ();
> +    InitializeFloatingPointUnits ();
> +    return;
> +  } else {
> +    //
> +    // Exception
> +    //
> +    ExceptionType >>= CSR_ESTAT_EXC_SHIFT;
> +    if ((ExceptionHandler != NULL) && (ExceptionHandler[ExceptionType] != NULL)) {
> +      ExceptionHandler[ExceptionType](ExceptionType, SystemContext);
> +      return;
> +    }
> +  }
> +
> +  //
> +  // Only the TLB refill exception use the same entry point as normal exceptions.
> +  //
> +  if (CsrRead (LOONGARCH_CSR_TLBRERA) & 0x1) {
> +    ExceptionType = mExceptionKnownNameNum - 1; // Use only to dump the exception context.
> +  }
> +
> +  DefaultExceptionHandler (ExceptionType, SystemContext);
> +}
> +
> +/**
> +  Initializes all CPU exceptions entries and provides the default exception handlers.
> +
> +  Caller should try to get an array of interrupt and/or exception vectors that are in use and need to
> +  persist by EFI_VECTOR_HANDOFF_INFO defined in PI 1.3 specification.
> +  If caller cannot get reserved vector list or it does not exists, set VectorInfo to NULL.
> +  If VectorInfo is not NULL, the exception vectors will be initialized per vector attribute accordingly.
> +
> +  @param[in]  VectorInfo    Pointer to reserved vector list.
> +
> +  @retval EFI_SUCCESS           CPU Exception Entries have been successfully initialized
> +                                with default exception handlers.
> +  @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL.
> +  @retval EFI_UNSUPPORTED       This function is not supported.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +InitializeCpuExceptionHandlers (
> +  IN EFI_VECTOR_HANDOFF_INFO  *VectorInfo OPTIONAL
> +  )
> +{
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Setup separate stacks for certain exception handlers.
> +  If the input Buffer and BufferSize are both NULL, use global variable if possible.
> +
> +  @param[in]       Buffer        Point to buffer used to separate exception stack.
> +  @param[in, out]  BufferSize    On input, it indicates the byte size of Buffer.
> +                                 If the size is not enough, the return status will
> +                                 be EFI_BUFFER_TOO_SMALL, and output BufferSize
> +                                 will be the size it needs.
> +
> +  @retval EFI_SUCCESS             The stacks are assigned successfully.
> +  @retval EFI_UNSUPPORTED         This function is not supported.
> +  @retval EFI_BUFFER_TOO_SMALL    This BufferSize is too small.
> +**/
> +EFI_STATUS
> +EFIAPI
> +InitializeSeparateExceptionStacks (
> +  IN     VOID   *Buffer,
> +  IN OUT UINTN  *BufferSize
> +  )
> +{
> +  return EFI_SUCCESS;
> +}
> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.c
> new file mode 100644
> index 0000000000..801c8393e8
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.c
> @@ -0,0 +1,171 @@
> +/** @file DxeExceptionLib.c
> +
> +  CPU Exception Handler Library common functions.
> +
> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#include <Library/BaseLib.h>
> +#include <Library/PeCoffGetEntryPointLib.h>
> +#include <Library/PrintLib.h>
> +#include <Library/SerialPortLib.h>
> +#include <Register/LoongArch64/Csr.h>
> +#include "ExceptionCommon.h"
> +
> +CONST CHAR8  mExceptionReservedStr[] = "Reserved";
> +CONST CHAR8  *mExceptionNameStr[]    = {
> +  "#INT - Interrupt(CSR.ECFG.VS=0)",
> +  "#PIL - Page invalid exception for Load option",
> +  "#PIS - Page invalid exception for Store operation",
> +  "#PIF - Page invalid exception for Fetch operation",
> +  "#PME - Page modification exception",
> +  "#PNR - Page non-readable exception",
> +  "#PNX - Page non-executable exception",
> +  "#PPI - Page privilege level illegal exception",
> +  "#ADE - Address error exception",
> +  "#ALE - Address alignment fault exception",
> +  "#BCE - Bound check exception",
> +  "#SYS - System call exception",
> +  "#BRK - Beeakpoint exception",
> +  "#INE - Instruction non-defined exception",
> +  "#IPE - Instruction privilege error exception",
> +  "#FPD - Floating-point instruction disable exception",
> +  "#SXD - 128-bit vector (SIMD instructions) expansion instruction disable exception",
> +  "#ASXD - 256-bit vector (Advanced SIMD instructions) expansion instruction disable exception",
> +  "#FPE - Floating-Point error exception",
> +  "#WPE - WatchPoint Exception for Fetch watchpoint or Memory load/store watchpoint",
> +  "#BTD - Binary Translation expansion instruction Disable exception",
> +  "#BTE - Binary Translation related exceptions",
> +  "#GSPR - Guest Sensitive Privileged Resource exception",
> +  "#HVC - HyperVisor Call exception",
> +  "#GCXC - Guest CSR Software/Hardware Change exception",
> +  "#TBR - TLB refill exception" // !!! NOTICE: Because the TLB refill exception is not instructed in ECODE, so the TLB refill exception must be the last one!
> +};
> +
> +INTN  mExceptionKnownNameNum = (sizeof (mExceptionNameStr) / sizeof (CHAR8 *));
> +
> +/**
> +  Get ASCII format string exception name by exception type.
> +
> +  @param ExceptionType  Exception type.
> +
> +  @return  ASCII format string exception name.
> +
> +**/
> +CONST CHAR8 *
> +GetExceptionNameStr (
> +  IN EFI_EXCEPTION_TYPE  ExceptionType
> +  )
> +{
> +  if ((UINTN)ExceptionType < mExceptionKnownNameNum) {
> +    return mExceptionNameStr[ExceptionType];
> +  } else {
> +    return mExceptionReservedStr;
> +  }
> +}
> +
> +/**
> +  Prints a message to the serial port.
> +
> +  @param  Format      Format string for the message to print.
> +  @param  ...         Variable argument list whose contents are accessed
> +                      based on the format string specified by Format.
> +
> +**/
> +VOID
> +EFIAPI
> +InternalPrintMessage (
> +  IN  CONST CHAR8  *Format,
> +  ...
> +  )
> +{
> +  CHAR8    Buffer[MAX_DEBUG_MESSAGE_LENGTH];
> +  VA_LIST  Marker;
> +
> +  //
> +  // Convert the message to an ASCII String
> +  //
> +  VA_START (Marker, Format);
> +  AsciiVSPrint (Buffer, sizeof (Buffer), Format, Marker);
> +  VA_END (Marker);
> +
> +  //
> +  // Send the print string to a Serial Port
> +  //
> +  SerialPortWrite ((UINT8 *)Buffer, AsciiStrLen (Buffer));
> +}
> +
> +/**
> +  Find and display image base address and return image base and its entry point.
> +
> +  @param CurrentEra      Current instruction pointer.
> +
> +**/
> +VOID
> +DumpModuleImageInfo (
> +  IN  UINTN  CurrentEra
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINTN       Pe32Data;
> +  VOID        *PdbPointer;
> +  VOID        *EntryPoint;
> +
> +  Pe32Data = PeCoffSearchImageBase (CurrentEra);
> +  if (Pe32Data == 0) {
> +    InternalPrintMessage ("!!!! Can't find image information. !!!!\n");
> +  } else {
> +    //
> +    // Find Image Base entry point
> +    //
> +    Status = PeCoffLoaderGetEntryPoint ((VOID *)Pe32Data, &EntryPoint);
> +    if (EFI_ERROR (Status)) {
> +      EntryPoint = NULL;
> +    }
> +
> +    InternalPrintMessage ("!!!! Find image based on IP(0x%x) ", CurrentEra);
> +    PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)Pe32Data);
> +    if (PdbPointer != NULL) {
> +      InternalPrintMessage ("%a", PdbPointer);
> +    } else {
> +      InternalPrintMessage ("(No PDB) ");
> +    }
> +
> +    InternalPrintMessage (
> +      " (ImageBase=%016lp, EntryPoint=%016p) !!!!\n",
> +      (VOID *)Pe32Data,
> +      EntryPoint
> +      );
> +  }
> +}
> +
> +/**
> +  Default exception handler.
> +
> +  @param ExceptionType  Exception type.
> +  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +
> +**/
> +VOID
> +EFIAPI
> +DefaultExceptionHandler (
> +  IN     EFI_EXCEPTION_TYPE  ExceptionType,
> +  IN OUT EFI_SYSTEM_CONTEXT  SystemContext
> +  )
> +{
> +  //
> +  // Initialize the serial port before dumping.
> +  //
> +  SerialPortInitialize ();
> +  //
> +  // Display ExceptionType, CPU information and Image information
> +  //
> +  DumpImageAndCpuContent (ExceptionType, SystemContext);
> +
> +  //
> +  // Enter a dead loop.
> +  //
> +  CpuDeadLoop ();
> +}
> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.h b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.h
> new file mode 100644
> index 0000000000..e326b73e3f
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.h
> @@ -0,0 +1,131 @@
> +/** @file DxeExceptionLib.h
> +
> +  Common header file for CPU Exception Handler Library.
> +
> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#ifndef EXCEPTION_COMMON_H_
> +#define EXCEPTION_COMMON_H_
> +
> +#define MAX_DEBUG_MESSAGE_LENGTH  0x100
> +
> +//
> +// For coding convenience, define the maximum valid
> +// LoongArch exception.
> +// Since UEFI V2.11, it will be present in DebugSupport.h.
> +//
> +#define MAX_LOONGARCH_EXCEPTION  64
> +
> +extern INTN  mExceptionKnownNameNum;
> +
> +/**
> +  Get ASCII format string exception name by exception type.
> +
> +  @param[in] ExceptionType  Exception type.
> +
> +  @return    ASCII format string exception name.
> +
> +**/
> +CONST CHAR8 *
> +GetExceptionNameStr (
> +  IN EFI_EXCEPTION_TYPE  ExceptionType
> +  );
> +
> +/**
> +  Prints a message to the serial port.
> +
> +  @param[in]  Format      Format string for the message to print.
> +  @param[in]  ...         Variable argument list whose contents are accessed
> +                      based on the format string specified by Format.
> +
> +**/
> +VOID
> +EFIAPI
> +InternalPrintMessage (
> +  IN  CONST CHAR8  *Format,
> +  ...
> +  );
> +
> +/**
> +  Find and display image base address and return image base and its entry point.
> +
> +  @param[in] CurrentEip      Current instruction pointer.
> +
> +**/
> +VOID
> +DumpModuleImageInfo (
> +  IN UINTN  CurrentEip
> +  );
> +
> +/**
> +  IPI Interrupt Handler.
> +
> +  @param InterruptType    The type of interrupt that occurred
> +  @param SystemContext    A pointer to the system context when the interrupt occurred
> +**/
> +VOID
> +EFIAPI
> +IpiInterruptHandler (
> +  IN EFI_EXCEPTION_TYPE  InterruptType,
> +  IN EFI_SYSTEM_CONTEXT  SystemContext
> +  );
> +
> +/**
> +  Default exception handler.
> +
> +  @param[in] ExceptionType  Exception type.
> +  @param[in] SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +
> +**/
> +VOID
> +EFIAPI
> +DefaultExceptionHandler (
> +  IN     EFI_EXCEPTION_TYPE  ExceptionType,
> +  IN OUT EFI_SYSTEM_CONTEXT  SystemContext
> +  );
> +
> +/**
> +  Display CPU information.
> +
> +  @param[in] ExceptionType  Exception type.
> +  @param[in] SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +
> +**/
> +VOID
> +DumpImageAndCpuContent (
> +  IN EFI_EXCEPTION_TYPE  ExceptionType,
> +  IN EFI_SYSTEM_CONTEXT  SystemContext
> +  );
> +
> +/**
> +  Get exception types
> +
> +  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +
> +  @return     Exception type.
> +
> +**/
> +EFI_EXCEPTION_TYPE
> +EFIAPI
> +GetExceptionType (
> +  IN EFI_SYSTEM_CONTEXT  SystemContext
> +  );
> +
> +/**
> +  Get Common interrupt types
> +
> +  @param[in] SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +
> +  @return    Interrupt type.
> +
> +**/
> +EFI_EXCEPTION_TYPE
> +EFIAPI
> +GetInterruptType (
> +  IN EFI_SYSTEM_CONTEXT  SystemContext
> +  );
> +
> +#endif
> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ArchExceptionHandler.c
> new file mode 100644
> index 0000000000..c0219deba5
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ArchExceptionHandler.c
> @@ -0,0 +1,268 @@
> +/** @file ArchExceptionHandler.c
> +
> +  LoongArch64 CPU Exception Handler.
> +
> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#include <Library/BaseLib.h>
> +#include <Register/LoongArch64/Csr.h>
> +#include "ExceptionCommon.h"
> +
> +/**
> +  Get Exception Type
> +
> +  @param[in] SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +
> +  @return    LoongArch64 exception type.
> +
> +**/
> +EFI_EXCEPTION_TYPE
> +EFIAPI
> +GetExceptionType (
> +  IN EFI_SYSTEM_CONTEXT  SystemContext
> +  )
> +{
> +  EFI_EXCEPTION_TYPE  ExceptionType;
> +
> +  ExceptionType = (SystemContext.SystemContextLoongArch64->ESTAT & CSR_ESTAT_EXC);
> +  return ExceptionType;
> +}
> +
> +/**
> +  Get Interrupt Type
> +
> +  @param[in] SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +
> +  @return    LoongArch64 intrrupt type.
> +
> +**/
> +EFI_EXCEPTION_TYPE
> +EFIAPI
> +GetInterruptType (
> +  IN EFI_SYSTEM_CONTEXT  SystemContext
> +  )
> +{
> +  EFI_EXCEPTION_TYPE  InterruptType;
> +
> +  for (InterruptType = 0; InterruptType <= EXCEPT_LOONGARCH_INT_IPI; InterruptType++) {
> +    if (SystemContext.SystemContextLoongArch64->ESTAT & (1 << InterruptType)) {
> +      //
> +      // 0  - EXCEPT_LOONGARCH_INT_SIP0
> +      // 1  - EXCEPT_LOONGARCH_INT_SIP1
> +      // 2  - EXCEPT_LOONGARCH_INT_IP0
> +      // 3  - EXCEPT_LOONGARCH_INT_IP1
> +      // 4  - EXCEPT_LOONGARCH_INT_IP2
> +      // 5  - EXCEPT_LOONGARCH_INT_IP3
> +      // 6  - EXCEPT_LOONGARCH_INT_IP4
> +      // 7  - EXCEPT_LOONGARCH_INT_IP5
> +      // 8  - EXCEPT_LOONGARCH_INT_IP6
> +      // 9  - EXCEPT_LOONGARCH_INT_IP7
> +      // 10 - EXCEPT_LOONGARCH_INT_PMC
> +      // 11 - EXCEPT_LOONGARCH_INT_TIMER
> +      // 12 - EXCEPT_LOONGARCH_INT_IPI
> +      // Greater than EXCEPT_LOONGARCH_INI_IPI is currently invalid.
> +      //
> +      return InterruptType;
> +    }
> +  }
> +
> +  //
> +  // Invalid IRQ
> +  //
> +  return 0xFF;
> +}
> +
> +/**
> +  Display CPU information.
> +
> +  @param ExceptionType  Exception type.
> +  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +
> +**/
> +VOID
> +EFIAPI
> +DumpCpuContext (
> +  IN EFI_EXCEPTION_TYPE  ExceptionType,
> +  IN EFI_SYSTEM_CONTEXT  SystemContext
> +  )
> +{
> +  InternalPrintMessage (
> +    "\n!!!! LoongArch64 Exception Type - %02x(%a) !!!!\n",
> +    ExceptionType,
> +    GetExceptionNameStr (ExceptionType)
> +    );
> +
> +  //
> +  // Dump TLB refill ERA and BADV
> +  //
> +  if (ExceptionType == (mExceptionKnownNameNum - 1)) {
> +    InternalPrintMessage ("TLB refill ERA  0x%llx\n", (CsrRead (LOONGARCH_CSR_TLBRERA) & (~0x3ULL)));
> +    InternalPrintMessage ("TLB refill BADV  0x%llx\n", CsrRead (LOONGARCH_CSR_TLBRBADV));
> +  }
> +
> +  //
> +  // Dump the general registers
> +  //
> +  InternalPrintMessage (
> +    "Zero  - 0x%016lx, RA  - 0x%016lx, TP - 0x%016lx, SP - 0x%016lx\n",
> +    SystemContext.SystemContextLoongArch64->R0,
> +    SystemContext.SystemContextLoongArch64->R1,
> +    SystemContext.SystemContextLoongArch64->R2,
> +    SystemContext.SystemContextLoongArch64->R3
> +    );
> +  InternalPrintMessage (
> +    "  A0  - 0x%016lx, A1  - 0x%016lx, A2 - 0x%016lx, A3 - 0x%016lx\n",
> +    SystemContext.SystemContextLoongArch64->R4,
> +    SystemContext.SystemContextLoongArch64->R5,
> +    SystemContext.SystemContextLoongArch64->R6,
> +    SystemContext.SystemContextLoongArch64->R7
> +    );
> +  InternalPrintMessage (
> +    "  A4  - 0x%016lx, A5  - 0x%016lx, A6 - 0x%016lx, A7 - 0x%016lx\n",
> +    SystemContext.SystemContextLoongArch64->R8,
> +    SystemContext.SystemContextLoongArch64->R9,
> +    SystemContext.SystemContextLoongArch64->R10,
> +    SystemContext.SystemContextLoongArch64->R11
> +    );
> +  InternalPrintMessage (
> +    "  T0  - 0x%016lx, T1  - 0x%016lx, T2 - 0x%016lx, T3 - 0x%016lx\n",
> +    SystemContext.SystemContextLoongArch64->R12,
> +    SystemContext.SystemContextLoongArch64->R13,
> +    SystemContext.SystemContextLoongArch64->R14,
> +    SystemContext.SystemContextLoongArch64->R15
> +    );
> +  InternalPrintMessage (
> +    "  T4  - 0x%016lx, T5  - 0x%016lx, T6 - 0x%016lx, T7 - 0x%016lx\n",
> +    SystemContext.SystemContextLoongArch64->R16,
> +    SystemContext.SystemContextLoongArch64->R17,
> +    SystemContext.SystemContextLoongArch64->R18,
> +    SystemContext.SystemContextLoongArch64->R19
> +    );
> +  InternalPrintMessage (
> +    "  T8  - 0x%016lx, R21 - 0x%016lx, FP - 0x%016lx, S0 - 0x%016lx\n",
> +    SystemContext.SystemContextLoongArch64->R20,
> +    SystemContext.SystemContextLoongArch64->R21,
> +    SystemContext.SystemContextLoongArch64->R22,
> +    SystemContext.SystemContextLoongArch64->R23
> +    );
> +  InternalPrintMessage (
> +    "  S1  - 0x%016lx, S2  - 0x%016lx, S3 - 0x%016lx, S4 - 0x%016lx\n",
> +    SystemContext.SystemContextLoongArch64->R24,
> +    SystemContext.SystemContextLoongArch64->R25,
> +    SystemContext.SystemContextLoongArch64->R26,
> +    SystemContext.SystemContextLoongArch64->R27
> +    );
> +  InternalPrintMessage (
> +    "  S5  - 0x%016lx, S6  - 0x%016lx, S7 - 0x%016lx, S8 - 0x%016lx\n",
> +    SystemContext.SystemContextLoongArch64->R28,
> +    SystemContext.SystemContextLoongArch64->R29,
> +    SystemContext.SystemContextLoongArch64->R30,
> +    SystemContext.SystemContextLoongArch64->R31
> +    );
> +  InternalPrintMessage ("\n");
> +
> +  //
> +  // Dump the CSR registers
> +  //
> +  InternalPrintMessage (
> +    "CRMD  - 0x%016lx, PRMD  - 0x%016lx, EUEN - 0x%016lx, MISC - 0x%016lx\n",
> +    SystemContext.SystemContextLoongArch64->CRMD,
> +    SystemContext.SystemContextLoongArch64->PRMD,
> +    SystemContext.SystemContextLoongArch64->EUEN,
> +    SystemContext.SystemContextLoongArch64->MISC
> +    );
> +  InternalPrintMessage (
> +    "ECFG  - 0x%016lx, ESTAT - 0x%016lx, ERA  - 0x%016lx, BADV - 0x%016lx\n",
> +    SystemContext.SystemContextLoongArch64->ECFG,
> +    SystemContext.SystemContextLoongArch64->ESTAT,
> +    SystemContext.SystemContextLoongArch64->ERA,
> +    SystemContext.SystemContextLoongArch64->BADV
> +    );
> +  InternalPrintMessage (
> +    "BADI  - 0x%016lx\n",
> +    SystemContext.SystemContextLoongArch64->BADI
> +    );
> +}
> +
> +/**
> +  Display CPU information.
> +
> +  @param ExceptionType  Exception type.
> +  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +
> +**/
> +VOID
> +DumpImageAndCpuContent (
> +  IN EFI_EXCEPTION_TYPE  ExceptionType,
> +  IN EFI_SYSTEM_CONTEXT  SystemContext
> +  )
> +{
> +  DumpCpuContext (ExceptionType, SystemContext);
> +
> +  if (ExceptionType == (mExceptionKnownNameNum - 1)) {
> +    //
> +    // Dump TLB refill image info
> +    //
> +    DumpModuleImageInfo ((CsrRead (LOONGARCH_CSR_TLBRERA) & (~0x3ULL)));
> +  } else {
> +    DumpModuleImageInfo (SystemContext.SystemContextLoongArch64->ERA);
> +  }
> +}
> +
> +/**
> +  IPI Interrupt Handler.
> +
> +  @param InterruptType    The type of interrupt that occurred
> +  @param SystemContext    A pointer to the system context when the interrupt occurred
> +**/
> +VOID
> +EFIAPI
> +IpiInterruptHandler (
> +  IN EFI_EXCEPTION_TYPE  InterruptType,
> +  IN EFI_SYSTEM_CONTEXT  SystemContext
> +  )
> +{
> +  UINTN  ResumeVector;
> +  UINTN  Parameter;
> +
> +  //
> +  // Clear interrupt.
> +  //
> +  IoCsrWrite32 (LOONGARCH_IOCSR_IPI_CLEAR, IoCsrRead32 (LOONGARCH_IOCSR_IPI_STATUS));
> +
> +  //
> +  // Get the resume vector and parameter if populated.
> +  //
> +  ResumeVector = IoCsrRead64 (LOONGARCH_IOCSR_MBUF0);
> +  Parameter    = IoCsrRead64 (LOONGARCH_IOCSR_MBUF3);
> +
> +  //
> +  // Clean up current processor mailbox 0 and mailbox 3.
> +  //
> +  IoCsrWrite64 (LOONGARCH_IOCSR_MBUF0, 0x0);
> +  IoCsrWrite64 (LOONGARCH_IOCSR_MBUF3, 0x0);
> +
> +  //
> +  // If mailbox 0 is non-NULL, it means that the BSP or other cores called the IPI to wake
> +  // up the current core and let it use the resume vector stored in mailbox 0.
> +  //
> +  // If both the resume vector and parameter are non-NULL, it means that the IPI was
> +  // called in the BIOS.
> +  //
> +  // The situation where the resume vector is non-NULL and the parameter is NULL has been
> +  // processed after the exception entry is pushed onto the stack.
> +  //
> +  if ((ResumeVector != 0) && (Parameter != 0)) {
> +    SystemContext.SystemContextLoongArch64->ERA = ResumeVector;
> +    //
> +    // Set $a0 as APIC ID and $a1 as parameter value.
> +    //
> +    SystemContext.SystemContextLoongArch64->R4 = CsrRead (LOONGARCH_CSR_CPUNUM);
> +    SystemContext.SystemContextLoongArch64->R5 = Parameter;
> +  }
> +
> +  MemoryFence ();
> +}
> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ExceptionHandlerAsm.S b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ExceptionHandlerAsm.S
> new file mode 100644
> index 0000000000..7c692e01c1
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ExceptionHandlerAsm.S
> @@ -0,0 +1,366 @@
> +#------------------------------------------------------------------------------
> +#
> +# LoongArch64 ASM exception handler
> +#
> +# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +#------------------------------------------------------------------------------
> +
> +#include <Library/BaseLib.h>
> +#include <Library/CpuLib.h>
> +#include <Register/LoongArch64/Csr.h>
> +
> +#define RSIZE                 8           // 64 bit mode register size
> +#define GP_REG_CONTEXT_SIZE   32 * RSIZE  // General-purpose registers size
> +#define FP_REG_CONTEXT_SIZE   34 * RSIZE  // Floating-point registers size
> +#define CSR_REG_CONTEXT_SIZE  9  * RSIZE  // CSR registers size
> +
> +ASM_GLOBAL ASM_PFX(ExceptionEntry)
> +ASM_GLOBAL ASM_PFX(ExceptionEntryStart)
> +ASM_GLOBAL ASM_PFX(ExceptionEntryEnd)
> +
> +ASM_PFX(ExceptionEntry):
> +  move    $s0, $a0
> +  bl      GetExceptionType        // Exception type stored in register a0
> +  move    $a1, $s0                // SystemContxt
> +  bl      CommonExceptionHandler
> +
> +PopContext:
> +  //
> +  // Not sure if interrupts are turned on during the exception handler, anyway disable interrupts here.
> +  // It will be turned on when the instruction 'ertn' is executed.
> +  //
> +  bl      DisableInterrupts
> +
> +  bl      GetExceptionType        // Get current exception type, and stored in register a0
> +
> +  // Check whether the FPE is changed during interrupt handler, if ture restore it.
> +  ld.d    $t1, $sp, (LOONGARCH_CSR_EUEN * RSIZE + GP_REG_CONTEXT_SIZE)
> +  csrrd   $t0, LOONGARCH_CSR_EUEN        // Current EUEN
> +  andi    $t0, $t0, CSR_EUEN_FPEN
> +  andi    $t1, $t1, CSR_EUEN_FPEN
> +  li.d    $t2, EXCEPT_LOONGARCH_INT
> +  bne     $a0, $t2, PopRegs
> +  beq     $t0, $t1, PopRegs
> +  beqz    $t1, CloseFP
> +  bl      EnableFloatingPointUnits
> +  b       PopRegs
> +
> +CloseFP:
> +  bl      DisableFloatingPointUnits
> +
> +PopRegs:
> +  //
> +  // Pop CSR reigsters
> +  //
> +  addi.d  $sp, $sp, GP_REG_CONTEXT_SIZE
> +
> +  ld.d    $t0, $sp, LOONGARCH_CSR_CRMD * RSIZE
> +  csrwr   $t0, LOONGARCH_CSR_CRMD
> +  ld.d    $t0, $sp, LOONGARCH_CSR_PRMD * RSIZE
> +  csrwr   $t0, LOONGARCH_CSR_PRMD
> +  ld.d    $t0, $sp, LOONGARCH_CSR_ECFG * RSIZE
> +  csrwr   $t0, LOONGARCH_CSR_ECFG
> +  ld.d    $t0, $sp, LOONGARCH_CSR_ERA * RSIZE
> +  csrwr   $t0, LOONGARCH_CSR_ERA
> +
> +  addi.d  $sp, $sp, CSR_REG_CONTEXT_SIZE  // Fource change the stack pointer befor pop the FP registers.
> +
> +  beqz    $t1, PopGP                      // If the FPE not set, only pop the GP registers.
> +
> +  //
> +  // Pop FP registers
> +  //
> +  fld.d  $fa0, $sp, 0 * RSIZE
> +  fld.d  $fa1, $sp, 1 * RSIZE
> +  fld.d  $fa2, $sp, 2 * RSIZE
> +  fld.d  $fa3, $sp, 3 * RSIZE
> +  fld.d  $fa4, $sp, 4 * RSIZE
> +  fld.d  $fa5, $sp, 5 * RSIZE
> +  fld.d  $fa6, $sp, 6 * RSIZE
> +  fld.d  $fa7, $sp, 7 * RSIZE
> +  fld.d  $ft0, $sp, 8 * RSIZE
> +  fld.d  $ft1, $sp, 9 * RSIZE
> +  fld.d  $ft2, $sp, 10 * RSIZE
> +  fld.d  $ft3, $sp, 11 * RSIZE
> +  fld.d  $ft4, $sp, 12 * RSIZE
> +  fld.d  $ft5, $sp, 13 * RSIZE
> +  fld.d  $ft6, $sp, 14 * RSIZE
> +  fld.d  $ft7, $sp, 15 * RSIZE
> +  fld.d  $ft8, $sp, 16 * RSIZE
> +  fld.d  $ft9, $sp, 17 * RSIZE
> +  fld.d  $ft10, $sp, 18 * RSIZE
> +  fld.d  $ft11, $sp, 19 * RSIZE
> +  fld.d  $ft12, $sp, 20 * RSIZE
> +  fld.d  $ft13, $sp, 21 * RSIZE
> +  fld.d  $ft14, $sp, 22 * RSIZE
> +  fld.d  $ft15, $sp, 23 * RSIZE
> +  fld.d  $fs0, $sp, 24 * RSIZE
> +  fld.d  $fs1, $sp, 25 * RSIZE
> +  fld.d  $fs2, $sp, 26 * RSIZE
> +  fld.d  $fs3, $sp, 27 * RSIZE
> +  fld.d  $fs4, $sp, 28 * RSIZE
> +  fld.d  $fs5, $sp, 29 * RSIZE
> +  fld.d  $fs6, $sp, 30 * RSIZE
> +  fld.d  $fs7, $sp, 31 * RSIZE
> +
> +  ld.d        $t0, $sp, 32 * RSIZE
> +  movgr2fcsr  $r0, $t0             // Pop the fcsr0 register.
> +
> +  //
> +  // Pop the fcc0-fcc7 registers.
> +  //
> +  ld.d        $t0, $sp, 33 * RSIZE
> +  bstrpick.d  $t1, $t0, 7, 0
> +  movgr2cf    $fcc0, $t1
> +  bstrpick.d  $t1, $t0, 15, 8
> +  movgr2cf    $fcc1, $t1
> +  bstrpick.d  $t1, $t0, 23, 16
> +  movgr2cf    $fcc2, $t1
> +  bstrpick.d  $t1, $t0, 31, 24
> +  movgr2cf    $fcc3, $t1
> +  bstrpick.d  $t1, $t0, 39, 32
> +  movgr2cf    $fcc4, $t1
> +  bstrpick.d  $t1, $t0, 47, 40
> +  movgr2cf    $fcc5, $t1
> +  bstrpick.d  $t1, $t0, 55, 48
> +  movgr2cf    $fcc6, $t1
> +  bstrpick.d  $t1, $t0, 63, 56
> +  movgr2cf    $fcc7, $t1
> +
> +PopGP:
> +  //
> +  // Pop GP registers
> +  //
> +  addi.d  $sp, $sp, -(GP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE)
> +  ld.d    $ra, $sp, 1 * RSIZE
> +  ld.d    $tp, $sp, 2 * RSIZE
> +  ld.d    $a0, $sp, 4 * RSIZE
> +  ld.d    $a1, $sp, 5 * RSIZE
> +  ld.d    $a2, $sp, 6 * RSIZE
> +  ld.d    $a3, $sp, 7 * RSIZE
> +  ld.d    $a4, $sp, 8 * RSIZE
> +  ld.d    $a5, $sp, 9 * RSIZE
> +  ld.d    $a6, $sp, 10 * RSIZE
> +  ld.d    $a7, $sp, 11 * RSIZE
> +  ld.d    $t0, $sp, 12 * RSIZE
> +  ld.d    $t1, $sp, 13 * RSIZE
> +  ld.d    $t2, $sp, 14 * RSIZE
> +  ld.d    $t3, $sp, 15 * RSIZE
> +  ld.d    $t4, $sp, 16 * RSIZE
> +  ld.d    $t5, $sp, 17 * RSIZE
> +  ld.d    $t6, $sp, 18 * RSIZE
> +  ld.d    $t7, $sp, 19 * RSIZE
> +  ld.d    $t8, $sp, 20 * RSIZE
> +  ld.d    $r21, $sp, 21 * RSIZE
> +  ld.d    $fp, $sp, 22 * RSIZE
> +  ld.d    $s0, $sp, 23 * RSIZE
> +  ld.d    $s1, $sp, 24 * RSIZE
> +  ld.d    $s2, $sp, 25 * RSIZE
> +  ld.d    $s3, $sp, 26 * RSIZE
> +  ld.d    $s4, $sp, 27 * RSIZE
> +  ld.d    $s5, $sp, 28 * RSIZE
> +  ld.d    $s6, $sp, 29 * RSIZE
> +  ld.d    $s7, $sp, 30 * RSIZE
> +  ld.d    $s8, $sp, 31 * RSIZE
> +  ld.d    $sp, $sp, 3 * RSIZE
> +
> +  ertn // Returen from exception.
> +//
> +// End of ExceptionEntry
> +//
> +
> +ASM_PFX(ExceptionEntryStart):
> +  //
> +  // Store the old stack pointer in preparation for pushing the exception context onto the new stack.
> +  //
> +  csrwr   $sp, LOONGARCH_CSR_KS0
> +
> +  csrrd   $sp, LOONGARCH_CSR_KS0
> +
> +  //
> +  // Push GP registers
> +  //
> +  addi.d  $sp, $sp, -(GP_REG_CONTEXT_SIZE + FP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE)
> +  st.d    $zero, $sp, 0 * RSIZE
> +  st.d    $ra, $sp, 1 * RSIZE
> +  st.d    $tp, $sp, 2 * RSIZE
> +  st.d    $a0, $sp, 4 * RSIZE
> +  st.d    $a1, $sp, 5 * RSIZE
> +  st.d    $a2, $sp, 6 * RSIZE
> +  st.d    $a3, $sp, 7 * RSIZE
> +  st.d    $a4, $sp, 8 * RSIZE
> +  st.d    $a5, $sp, 9 * RSIZE
> +  st.d    $a6, $sp, 10 * RSIZE
> +  st.d    $a7, $sp, 11 * RSIZE
> +  st.d    $t0, $sp, 12 * RSIZE
> +  st.d    $t1, $sp, 13 * RSIZE
> +  st.d    $t2, $sp, 14 * RSIZE
> +  st.d    $t3, $sp, 15 * RSIZE
> +  st.d    $t4, $sp, 16 * RSIZE
> +  st.d    $t5, $sp, 17 * RSIZE
> +  st.d    $t6, $sp, 18 * RSIZE
> +  st.d    $t7, $sp, 19 * RSIZE
> +  st.d    $t8, $sp, 20 * RSIZE
> +  st.d    $r21, $sp, 21 * RSIZE
> +  st.d    $fp, $sp, 22 * RSIZE
> +  st.d    $s0, $sp, 23 * RSIZE
> +  st.d    $s1, $sp, 24 * RSIZE
> +  st.d    $s2, $sp, 25 * RSIZE
> +  st.d    $s3, $sp, 26 * RSIZE
> +  st.d    $s4, $sp, 27 * RSIZE
> +  st.d    $s5, $sp, 28 * RSIZE
> +  st.d    $s6, $sp, 29 * RSIZE
> +  st.d    $s7, $sp, 30 * RSIZE
> +  st.d    $s8, $sp, 31 * RSIZE
> +  csrrd   $t0, LOONGARCH_CSR_KS0  // Read the old stack pointer.
> +  st.d    $t0, $sp, 3 * RSIZE
> +
> +  //
> +  // Push CSR registers
> +  //
> +  addi.d  $sp, $sp, GP_REG_CONTEXT_SIZE
> +
> +  csrrd   $t0, LOONGARCH_CSR_CRMD
> +  st.d    $t0, $sp, LOONGARCH_CSR_CRMD * RSIZE
> +  csrrd   $t0, LOONGARCH_CSR_PRMD
> +  st.d    $t0, $sp, LOONGARCH_CSR_PRMD * RSIZE
> +  csrrd   $t0, LOONGARCH_CSR_EUEN
> +  st.d    $t0, $sp, LOONGARCH_CSR_EUEN * RSIZE
> +  csrrd   $t0, LOONGARCH_CSR_MISC
> +  st.d    $t0, $sp, LOONGARCH_CSR_MISC * RSIZE
> +  csrrd   $t0, LOONGARCH_CSR_ECFG
> +  st.d    $t0, $sp, LOONGARCH_CSR_ECFG * RSIZE
> +  csrrd   $t0, LOONGARCH_CSR_ESTAT
> +  st.d    $t0, $sp, LOONGARCH_CSR_ESTAT * RSIZE
> +  csrrd   $t0, LOONGARCH_CSR_ERA
> +  st.d    $t0, $sp, LOONGARCH_CSR_ERA * RSIZE
> +  csrrd   $t0, LOONGARCH_CSR_BADV
> +  st.d    $t0, $sp, LOONGARCH_CSR_BADV * RSIZE
> +  csrrd   $t0, LOONGARCH_CSR_BADI
> +  st.d    $t0, $sp, LOONGARCH_CSR_BADI * RSIZE
> +
> +  //
> +  // Push FP registers
> +  //
> +  addi.d  $sp, $sp, CSR_REG_CONTEXT_SIZE
> +
> +  csrrd   $t0, LOONGARCH_CSR_EUEN
> +  andi    $t0, $t0, CSR_EUEN_FPEN
> +  beqz    $t0, PushRegDone
> +
> +  fst.d  $fa0, $sp, 0 * RSIZE
> +  fst.d  $fa1, $sp, 1 * RSIZE
> +  fst.d  $fa2, $sp, 2 * RSIZE
> +  fst.d  $fa3, $sp, 3 * RSIZE
> +  fst.d  $fa4, $sp, 4 * RSIZE
> +  fst.d  $fa5, $sp, 5 * RSIZE
> +  fst.d  $fa6, $sp, 6 * RSIZE
> +  fst.d  $fa7, $sp, 7 * RSIZE
> +  fst.d  $ft0, $sp, 8 * RSIZE
> +  fst.d  $ft1, $sp, 9 * RSIZE
> +  fst.d  $ft2, $sp, 10 * RSIZE
> +  fst.d  $ft3, $sp, 11 * RSIZE
> +  fst.d  $ft4, $sp, 12 * RSIZE
> +  fst.d  $ft5, $sp, 13 * RSIZE
> +  fst.d  $ft6, $sp, 14 * RSIZE
> +  fst.d  $ft7, $sp, 15 * RSIZE
> +  fst.d  $ft8, $sp, 16 * RSIZE
> +  fst.d  $ft9, $sp, 17 * RSIZE
> +  fst.d  $ft10, $sp, 18 * RSIZE
> +  fst.d  $ft11, $sp, 19 * RSIZE
> +  fst.d  $ft12, $sp, 20 * RSIZE
> +  fst.d  $ft13, $sp, 21 * RSIZE
> +  fst.d  $ft14, $sp, 22 * RSIZE
> +  fst.d  $ft15, $sp, 23 * RSIZE
> +  fst.d  $fs0, $sp, 24 * RSIZE
> +  fst.d  $fs1, $sp, 25 * RSIZE
> +  fst.d  $fs2, $sp, 26 * RSIZE
> +  fst.d  $fs3, $sp, 27 * RSIZE
> +  fst.d  $fs4, $sp, 28 * RSIZE
> +  fst.d  $fs5, $sp, 29 * RSIZE
> +  fst.d  $fs6, $sp, 30 * RSIZE
> +  fst.d  $fs7, $sp, 31 * RSIZE
> +
> +  movfcsr2gr  $t3, $r0
> +  st.d        $t3, $sp, 32 * RSIZE  // Push the FCSR0 register.
> +
> +  //
> +  // Push the fcc0-fcc7 registers.
> +  //
> +  movcf2gr    $t3, $fcc0
> +  or          $t2, $t3, $zero
> +  movcf2gr    $t3, $fcc1
> +  bstrins.d   $t2, $t3, 0xf, 0x8
> +  movcf2gr    $t3, $fcc2
> +  bstrins.d   $t2, $t3, 0x17, 0x10
> +  movcf2gr    $t3, $fcc3
> +  bstrins.d   $t2, $t3, 0x1f, 0x18
> +  movcf2gr    $t3, $fcc4
> +  bstrins.d   $t2, $t3, 0x27, 0x20
> +  movcf2gr    $t3, $fcc5
> +  bstrins.d   $t2, $t3, 0x2f, 0x28
> +  movcf2gr    $t3, $fcc6
> +  bstrins.d   $t2, $t3, 0x37, 0x30
> +  movcf2gr    $t3, $fcc7
> +  bstrins.d   $t2, $t3, 0x3f, 0x38
> +  st.d        $t2, $sp, 33 * RSIZE
> +  //
> +  // Push exception context down
> +  //
> +
> +PushRegDone:
> +  //
> +  // Process IPI only when mailbox3 is NULL and mailbox0 is no-NULL.
> +  //
> +  li.d      $t0, LOONGARCH_IOCSR_MBUF0
> +  iocsrrd.d $a0, $t0
> +  beqz      $a0, EntryConmmonHanlder
> +
> +  li.d      $t0, LOONGARCH_IOCSR_MBUF3
> +  iocsrrd.d $t1, $t0
> +  bnez      $t1, EntryConmmonHanlder
> +
> +  csrrd     $t0, LOONGARCH_CSR_ESTAT
> +  srli.d    $t0, $t0, 12
> +  andi      $t0, $t0, 0x1
> +  beqz      $t0, EntryConmmonHanlder
> +
> +  //
> +  // Clean up current processor mailbox 0 and mailbox 3.
> +  //
> +  li.d      $t0, LOONGARCH_IOCSR_MBUF0
> +  iocsrwr.d $zero, $t0
> +  li.d      $t0, LOONGARCH_IOCSR_MBUF3
> +  iocsrwr.d $zero, $t0
> +
> +  //
> +  // Clear IPI interrupt.
> +  //
> +  li.d      $t0, LOONGARCH_IOCSR_IPI_STATUS
> +  iocsrrd.w $t1, $t0
> +  li.d      $t0, LOONGARCH_IOCSR_IPI_CLEAR
> +  iocsrwr.w $t1, $t0
> +
> +  //
> +  // Only kernel stage BSP calls IPI without parameters. Clean up the PIE and make sure
> +  // global interrupts are turned off for the current processor when jumping to the kernel.
> +  //
> +  csrwr     $a0, LOONGARCH_CSR_ERA         // Update ERA
> +  li.w      $t0, BIT2                      // IE
> +  csrxchg   $zero, $t0, LOONGARCH_CSR_PRMD // Clean PIE
> +
> +  //
> +  // Return this exception and jump to kernel using ERA.
> +  //
> +  ertn
> +
> +EntryConmmonHanlder:
> +  addi.d  $sp, $sp, -(GP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE)
> +  move    $a0, $sp
> +  la.abs  $ra, ExceptionEntry
> +  jirl    $zero, $ra, 0
> +ASM_PFX(ExceptionEntryEnd):
> +.end
> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/SecPeiExceptionLib.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/SecPeiExceptionLib.c
> new file mode 100644
> index 0000000000..7588d2050b
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/SecPeiExceptionLib.c
> @@ -0,0 +1,102 @@
> +/** @file SecPeiExceptionLib.c
> +
> +  LoongArch exception library implemenation for PEI and SEC modules.
> +
> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#include <Library/BaseLib.h>
> +#include <Library/CpuLib.h>
> +#include <Library/CpuExceptionHandlerLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/SerialPortLib.h>
> +#include <Protocol/DebugSupport.h>
> +#include <Register/LoongArch64/Csr.h>
> +
> +#include "ExceptionCommon.h"
> +
> +/**
> +  Registers a function to be called from the processor interrupt or exception handler.
> +
> +  Always return EFI_UNSUPPORTED in the SEC exception initialization module.
> +
> +  @param  InterruptType    A pointer to the processor's current interrupt state. Set to TRUE if interrupts
> +                           are enabled and FALSE if interrupts are disabled.
> +  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
> +                           when a processor interrupt occurs. If this parameter is NULL, then the handler
> +                           will be uninstalled.
> +
> +  @retval EFI_UNSUPPORTED  The interrupt specified by InterruptType is not supported.
> +
> +**/
> +EFI_STATUS
> +RegisterCpuInterruptHandler (
> +  IN EFI_EXCEPTION_TYPE         InterruptType,
> +  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
> +  )
> +{
> +  return EFI_UNSUPPORTED;
> +}
> +
> +/**
> +  Common exception handler.
> +
> +  @param ExceptionType  Exception type.
> +  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +
> +**/
> +VOID
> +EFIAPI
> +CommonExceptionHandler (
> +  IN     EFI_EXCEPTION_TYPE  ExceptionType,
> +  IN OUT EFI_SYSTEM_CONTEXT  SystemContext
> +  )
> +{
> +  EFI_EXCEPTION_TYPE  InterruptType;
> +
> +  if (ExceptionType == EXCEPT_LOONGARCH_INT) {
> +    //
> +    // Interrupt
> +    //
> +    InterruptType = GetInterruptType (SystemContext);
> +    if (InterruptType == EXCEPT_LOONGARCH_INT_IPI) {
> +      //
> +      // APs may wake up via IPI IRQ during the SEC or PEI phase, clear the IPI interrupt and
> +      // perform the remaining work.
> +      //
> +      IpiInterruptHandler (InterruptType, SystemContext);
> +      return;
> +    } else {
> +      ExceptionType = InterruptType;
> +    }
> +  } else {
> +    //
> +    // Exception
> +    //
> +    ExceptionType >>= CSR_ESTAT_EXC_SHIFT;
> +  }
> +
> +  DefaultExceptionHandler (ExceptionType, SystemContext);
> +}
> +
> +/**
> +  Initializes all CPU exceptions entries and provides the default exception handlers.
> +
> +  Always return EFI_SUCCESS in the SEC exception initialization module.
> +
> +  @param[in]  VectorInfo    Pointer to reserved vector list.
> +
> +  @retval EFI_SUCCESS       CPU Exception Entries have been successfully initialized
> +                            with default exception handlers.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +InitializeCpuExceptionHandlers (
> +  IN EFI_VECTOR_HANDOFF_INFO  *VectorInfo OPTIONAL
> +  )
> +{
> +  return EFI_SUCCESS;
> +}
> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
> index e7b1144f69..6bb194ea77 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
> @@ -2,6 +2,7 @@
>   #  CPU Exception Handler library instance for SEC/PEI modules.
>   #
>   #  Copyright (c) 2012 - 2022, Intel Corporation. All rights reserved.<BR>
> +#  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>   #  SPDX-License-Identifier: BSD-2-Clause-Patent
>   #
>   ##
> @@ -18,37 +19,47 @@
>   #
>   # The following information is for reference only and not required by the build tools.
>   #
> -#  VALID_ARCHITECTURES           = IA32 X64
> +#  VALID_ARCHITECTURES           = IA32 X64 LOONGARCH64
>   #
>   
>   [Sources.Ia32]
> -  Ia32/ExceptionHandlerAsm.nasm
> -  Ia32/ExceptionTssEntryAsm.nasm
>     Ia32/ArchExceptionHandler.c
>     Ia32/ArchInterruptDefs.h
> +  Ia32/ExceptionHandlerAsm.nasm
> +  Ia32/ExceptionTssEntryAsm.nasm
>   
>   [Sources.X64]
> -  X64/SecPeiExceptionHandlerAsm.nasm
>     X64/ArchExceptionHandler.c
>     X64/ArchInterruptDefs.h
> +  X64/SecPeiExceptionHandlerAsm.nasm
>   
> -[Sources.common]
> +[Sources.Ia32, Sources.X64]
>     CpuExceptionCommon.h
>     CpuExceptionCommon.c
>     SecPeiCpuException.c
>   
> +[Sources.LoongArch64]
> +  LoongArch/ExceptionCommon.h
> +  LoongArch/ExceptionCommon.c
> +  LoongArch/SecPeiExceptionLib.c
> +  LoongArch/LoongArch64/ArchExceptionHandler.c
> +  LoongArch/LoongArch64/ExceptionHandlerAsm.S | GCC
> +
>   [Packages]
>     MdePkg/MdePkg.dec
>     MdeModulePkg/MdeModulePkg.dec
>     UefiCpuPkg/UefiCpuPkg.dec
>   
> -[LibraryClasses]
> +[LibraryClasses.common]
>     BaseLib
> -  SerialPortLib
> -  PrintLib
> -  LocalApicLib
> +  CpuLib
>     PeCoffGetEntryPointLib
> +  PrintLib
> +  SerialPortLib
> +
> +[LibraryClasses.Ia32, LibraryClasses.X64]
>     CcExitLib
> +  LocalApicLib
>   
>   [Pcd]
>     gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard
> diff --git a/UefiCpuPkg/UefiCpuPkg.dec b/UefiCpuPkg/UefiCpuPkg.dec
> index 571b59b36f..f5febe46ba 100644
> --- a/UefiCpuPkg/UefiCpuPkg.dec
> +++ b/UefiCpuPkg/UefiCpuPkg.dec
> @@ -3,6 +3,7 @@
>   #
>   # Copyright (c) 2007 - 2023, Intel Corporation. All rights reserved.<BR>
>   # Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.<BR>
> +# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>   #
>   # SPDX-License-Identifier: BSD-2-Clause-Patent
>   #
> @@ -385,6 +386,10 @@
>     # @Prompt Enable performance collecting when processor trace is enabled.
>     gUefiCpuPkgTokenSpaceGuid.PcdCpuProcTracePerformanceCollecting|FALSE|BOOLEAN|0x60000020
>   
> +  ## This PCD Contains the pointer to a CPU exception vector base address.
> +  # @Prompt The pointer to a CPU exception vector base address.
> +  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress|0x0|UINT64|0x60000022
> +
>   [PcdsFixedAtBuild.X64, PcdsPatchableInModule.X64, PcdsDynamic.X64, PcdsDynamicEx.X64]
>     ## Indicate access to non-SMRAM memory is restricted to reserved, runtime and ACPI NVS type after SmmReadyToLock.
>     #  MMIO access is always allowed regardless of the value of this PCD.


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114849): https://edk2.groups.io/g/devel/message/114849
Mute This Topic: https://groups.io/mt/104068938/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
       [not found] ` <17ADD1D9CA04F352.11113@groups.io>
@ 2024-01-31  3:31   ` Chao Li
       [not found]   ` <17AF511188DE2475.15701@groups.io>
  1 sibling, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-31  3:31 UTC (permalink / raw)
  To: devel, Ray Ni
  Cc: Eric Dong, Laszlo Ersek, Rahul Kumar, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

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

Hi Ray,

Can you please help to review this patch again?


Thanks,
Chao
On 2024/1/26 14:29, Chao Li wrote:
> Add a new library named CpuMmuLib and add a LoongArch64 instance with in
> the library.
> It provides two-stage MMU libraryinstances, PEI and DXE.
>
> BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>
> Cc: Eric Dong<eric.dong@intel.com>
> Cc: Ray Ni<ray.ni@intel.com>
> Cc: Laszlo Ersek<lersek@redhat.com>
> Cc: Rahul Kumar<rahul1.kumar@intel.com>
> Cc: Gerd Hoffmann<kraxel@redhat.com>
> Signed-off-by: Chao Li<lichao@loongson.cn>
> Co-authored-by: Baoqi Zhang<zhangbaoqi@loongson.cn>
> Co-authored-by: Dongyan Qian<qiandongyan@loongson.cn>
> Co-authored-by: Xianglai Li<lixianglai@loongson.cn>
> Co-authored-by: Bibo Mao<maobibo@loongson.cn>
> ---
>   UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf |  36 +
>   UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni |  14 +
>   .../CpuMmuLib/LoongArch64/CommonMmuLib.c      | 988 ++++++++++++++++++
>   .../CpuMmuLib/LoongArch64/CommonMmuLib.h      |  43 +
>   .../Library/CpuMmuLib/LoongArch64/Page.h      | 279 +++++
>   .../CpuMmuLib/LoongArch64/PeiCpuMmuLib.c      | 178 ++++
>   .../Library/CpuMmuLib/LoongArch64/Tlb.h       |  48 +
>   .../CpuMmuLib/LoongArch64/TlbOperation.S      |  44 +
>   UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf |  44 +
>   UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni |  14 +
>   UefiCpuPkg/UefiCpuPkg.dsc                     |   4 +
>   11 files changed, 1692 insertions(+)
>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
>
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
> new file mode 100644
> index 0000000000..bfce3ce96d
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
> @@ -0,0 +1,36 @@
> +## @file
> +#  CPU Memory Map Unit DXE phase driver.
> +#
> +#  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                      = DxeCpuMmuLib
> +  MODULE_UNI_FILE                = DxeCpuMmuLib.uni
> +  FILE_GUID                      = DA8F0232-FB14-42F0-922C-63104D2C70BE
> +  MODULE_TYPE                    = DXE_DRIVER
> +  VERSION_STRING                 = 1.0
> +  LIBRARY_CLASS                  = CpuMmuLib | DXE_DRIVER
> +  CONSTRUCTOR                    = MmuInitialize
> +
> +#
> +#  VALID_ARCHITECTURES           = LOONGARCH64
> +#
> +
> +[Sources.LoongArch64]
> +  LoongArch64/TlbOperation.S   | GCC
> +  LoongArch64/CommonMmuLib.c
> +  LoongArch64/Page.h
> +  LoongArch64/Tlb.h
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  UefiCpuPkg/UefiCpuPkg.dec
> +
> +[LibraryClasses]
> +  DebugLib
> +  MemoryAllocationLib
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
> new file mode 100644
> index 0000000000..7342249516
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
> @@ -0,0 +1,14 @@
> +// /** @file
> +// CPU Memory Manager Unit library instance for DXE modules.
> +//
> +// CPU Memory Manager Unit library instance for DXE modules.
> +//
> +// 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 "CPU Memory Manager Unit library instance for DXE modules."
> +
> +#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for DXE modules."
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
> new file mode 100644
> index 0000000000..2e852c3371
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
> @@ -0,0 +1,988 @@
> +/** @file
> +
> +  CPU Memory Map Unit Handler Library common functions.
> +
> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +  @par Glossary:
> +    - Pgd or Pgd or PGD    - Page Global Directory
> +    - Pud or Pud or PUD    - Page Upper Directory
> +    - Pmd or Pmd or PMD    - Page Middle Directory
> +    - Pte or pte or PTE    - Page Table Entry
> +    - Val or VAL or val    - Value
> +    - Dir    - Directory
> +**/
> +#include <Uefi.h>
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/CpuMmuLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Register/LoongArch64/Csr.h>
> +#include "Tlb.h"
> +#include "Page.h"
> +
> +#define SWAP_PAGE_DIR              CsrRead(LOONGARCH_CSR_PGDL)
> +#define EFI_MEMORY_CACHETYPE_MASK  (EFI_MEMORY_UC  | \
> +                                    EFI_MEMORY_WC  | \
> +                                    EFI_MEMORY_WT  | \
> +                                    EFI_MEMORY_WB  | \
> +                                    EFI_MEMORY_UCE   \
> +                                    )
> +
> +BOOLEAN  mMmuInited = FALSE;
> +
> +/**
> +  Check to see if mmu successfully initializes.
> +
> +  @param  VOID.
> +
> +  @retval  TRUE  Initialization has been completed.
> +           FALSE Initialization did not complete.
> +**/
> +STATIC
> +BOOLEAN
> +MmuIsInit (
> +  VOID
> +  )
> +{
> +  if (mMmuInited || (SWAP_PAGE_DIR != 0)) {
> +    return TRUE;
> +  }
> +
> +  return FALSE;
> +}
> +
> +/**
> +  Iterates through the page directory to initialize it.
> +
> +  @param  Dst  A pointer to the directory of the page to initialize.
> +  @param  Num  The number of page directories to initialize.
> +  @param  Src  A pointer to the data used to initialize the page directory.
> +
> +  @return VOID.
> +**/
> +STATIC
> +VOID
> +PageDirInit (
> +  IN VOID   *Dst,
> +  IN UINTN  Num,
> +  IN VOID   *Src
> +  )
> +{
> +  UINTN  *Ptr;
> +  UINTN  *End;
> +  UINTN  Entry;
> +
> +  Entry = (UINTN)Src;
> +  Ptr   = (UINTN *)Dst;
> +  End   = Ptr + Num;
> +
> +  for ( ; Ptr < End; Ptr++) {
> +    *Ptr = Entry;
> +  }
> +
> +  return;
> +}
> +
> +/**
> +  Gets the virtual address corresponding to the page global directory table entry.
> +
> +  @param  Address  the virtual address for the table entry.
> +
> +  @retval PGD A pointer to get the table item.
> +**/
> +STATIC
> +PGD *
> +PgdOffset (
> +  IN UINTN  Address
> +  )
> +{
> +  return (PGD *)(SWAP_PAGE_DIR) + PGD_INDEX (Address);
> +}
> +
> +/**
> +  Gets the virtual address corresponding to the page upper directory table entry.
> +
> +  @param  Pgd  A pointer to a page global directory table entry.
> +  @param  Address  the virtual address for the table entry.
> +
> +  @retval PUD A pointer to get the table item.
> +**/
> +STATIC
> +PUD *
> +PudOffset (
> +  IN PGD    *Pgd,
> +  IN UINTN  Address
> +  )
> +{
> +  UINTN  PgdVal;
> +
> +  PgdVal = (UINTN)PGD_VAL (*Pgd);
> +
> +  return (PUD *)PgdVal + PUD_INDEX (Address);
> +}
> +
> +/**
> +  Gets the virtual address corresponding to the page middle directory table entry.
> +
> +  @param  Pud  A pointer to a page upper directory table entry.
> +  @param  Address  the virtual address for the table entry.
> +
> +  @retval PMD A pointer to get the table item.
> +**/
> +STATIC
> +PMD *
> +PmdOffset (
> +  IN PUD    *Pud,
> +  IN UINTN  Address
> +  )
> +{
> +  UINTN  PudVal;
> +
> +  PudVal = PUD_VAL (*Pud);
> +
> +  return (PMD *)PudVal + PMD_INDEX (Address);
> +}
> +
> +/**
> +  Gets the virtual address corresponding to the page table entry.
> +
> +  @param  Pmd  A pointer to a page middle directory table entry.
> +  @param  Address  the virtual address for the table entry.
> +
> +  @retval PTE A pointer to get the table item.
> +**/
> +STATIC
> +PTE *
> +PteOffset (
> +  IN PMD    *Pmd,
> +  IN UINTN  Address
> +  )
> +{
> +  UINTN  PmdVal;
> +
> +  PmdVal = (UINTN)PMD_VAL (*Pmd);
> +
> +  return (PTE *)PmdVal + PTE_INDEX (Address);
> +}
> +
> +/**
> +  Sets the value of the page table entry.
> +
> +  @param  Pte  A pointer to a page table entry.
> +  @param  PteVal  The value of the page table entry to set.
> +
> +**/
> +STATIC
> +VOID
> +SetPte (
> +  IN PTE  *Pte,
> +  IN PTE  PteVal
> +  )
> +{
> +  *Pte = PteVal;
> +}
> +
> +/**
> +  Sets the value of the page global directory.
> +
> +  @param  Pgd  A pointer to a page global directory.
> +  @param  Pud  The value of the page global directory to set.
> +
> +**/
> +STATIC
> +VOID
> +SetPgd (
> +  IN PGD  *Pgd,
> +  IN PUD  *Pud
> +  )
> +{
> +  *Pgd = (PGD) {
> +    ((UINTN)Pud)
> +  };
> +}
> +
> +/**
> +  Sets the value of the page upper directory.
> +
> +  @param  Pud  A pointer to a page upper directory.
> +  @param  Pmd  The value of the page upper directory to set.
> +
> +**/
> +STATIC
> +VOID
> +SetPud (
> +  IN PUD  *Pud,
> +  IN PMD  *Pmd
> +  )
> +{
> +  *Pud = (PUD) {
> +    ((UINTN)Pmd)
> +  };
> +}
> +
> +/**
> +  Sets the value of the page middle directory.
> +
> +  @param  Pmd  A pointer to a page middle directory.
> +  @param  Pte  The value of the page middle directory to set.
> +
> +**/
> +STATIC
> +VOID
> +SetPmd (
> +  IN PMD  *Pmd,
> +  IN PTE  *Pte
> +  )
> +{
> +  *Pmd = (PMD) {
> +    ((UINTN)Pte)
> +  };
> +}
> +
> +/**
> +  Free up memory space occupied by page tables.
> +
> +  @param  Pte  A pointer to the page table.
> +
> +**/
> +VOID
> +PteFree (
> +  IN PTE  *Pte
> +  )
> +{
> +  FreePages ((VOID *)Pte, 1);
> +}
> +
> +/**
> +  Free up memory space occupied by page middle directory.
> +
> +  @param  Pmd  A pointer to the page middle directory.
> +
> +**/
> +VOID
> +PmdFree (
> +  IN PMD  *Pmd
> +  )
> +{
> +  FreePages ((VOID *)Pmd, 1);
> +}
> +
> +/**
> +  Free up memory space occupied by page upper directory.
> +
> +  @param  Pud  A pointer to the page upper directory.
> +
> +**/
> +VOID
> +PudFree (
> +  IN PUD  *Pud
> +  )
> +{
> +  FreePages ((VOID *)Pud, 1);
> +}
> +
> +/**
> +  Requests the memory space required for the page upper directory,
> +  initializes it, and places it in the specified page global directory
> +
> +  @param  Pgd  A pointer to the page global directory.
> +
> +  @retval  EFI_SUCCESS  Memory request successful.
> +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
> +**/
> +STATIC
> +EFI_STATUS
> +PudAlloc (
> +  IN PGD  *Pgd
> +  )
> +{
> +  PUD  *Pud;
> +
> +  Pud = (PUD *)AllocatePages (1);
> +  if (Pud == NULL) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  PageDirInit ((VOID *)Pud, ENTRYS_PER_PUD, (VOID *)INVALID_PAGE);
> +
> +  SetPgd (Pgd, Pud);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Requests the memory space required for the page middle directory,
> +  initializes it, and places it in the specified page upper directory
> +
> +  @param  Pud  A pointer to the page upper directory.
> +
> +  @retval  EFI_SUCCESS  Memory request successful.
> +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
> +**/
> +STATIC
> +EFI_STATUS
> +PmdAlloc (
> +  IN PUD  *Pud
> +  )
> +{
> +  PMD  *Pmd;
> +
> +  Pmd = (PMD *)AllocatePages (1);
> +  if (!Pmd) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  PageDirInit ((VOID *)Pmd, ENTRYS_PER_PMD, (VOID *)INVALID_PAGE);
> +
> +  SetPud (Pud, Pmd);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Requests the memory space required for the page table,
> +  initializes it, and places it in the specified page middle directory
> +
> +  @param  Pmd  A pointer to the page middle directory.
> +
> +  @retval  EFI_SUCCESS  Memory request successful.
> +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
> +**/
> +STATIC
> +EFI_STATUS
> +PteAlloc (
> +  IN PMD  *Pmd
> +  )
> +{
> +  PTE  *Pte;
> +
> +  Pte = (PTE *)AllocatePages (1);
> +  if (!Pte) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  Pte = ZeroMem (Pte, EFI_PAGE_SIZE);
> +
> +  SetPmd (Pmd, Pte);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Requests the memory space required for the page upper directory,
> +  initializes it, and places it in the specified page global directory,
> +  and get the page upper directory entry corresponding to the virtual address.
> +
> +  @param  Pgd      A pointer to the page global directory.
> +  @param  Address  The corresponding virtual address of the page table entry.
> +
> +  @retval          A pointer to the page upper directory entry. Return NULL, if
> +                   allocate the memory buffer is fail.
> +**/
> +STATIC
> +PUD *
> +PudAllocGet (
> +  IN PGD    *Pgd,
> +  IN UINTN  Address
> +  )
> +{
> +  EFI_STATUS  Status;
> +
> +  if (PGD_IS_EMPTY (*Pgd)) {
> +    Status = PudAlloc (Pgd);
> +    ASSERT_EFI_ERROR (Status);
> +    if (EFI_ERROR (Status)) {
> +      return NULL;
> +    }
> +  }
> +
> +  return PudOffset (Pgd, Address);
> +}
> +
> +/**
> +  Requests the memory space required for the page middle directory,
> +  initializes it, and places it in the specified page upper directory,
> +  and get the page middle directory entry corresponding to the virtual address.
> +
> +  @param  Pud      A pointer to the page upper directory.
> +  @param  Address  The corresponding virtual address of the page table entry.
> +
> +  @retval          A pointer to the page middle directory entry. Return NULL, if
> +                   allocate the memory buffer is fail.
> +**/
> +STATIC
> +PMD *
> +PmdAllocGet (
> +  IN PUD    *Pud,
> +  IN UINTN  Address
> +  )
> +{
> +  EFI_STATUS  Status;
> +
> +  if (PUD_IS_EMPTY (*Pud)) {
> +    Status = PmdAlloc (Pud);
> +    ASSERT_EFI_ERROR (Status);
> +    if (EFI_ERROR (Status)) {
> +      return NULL;
> +    }
> +  }
> +
> +  return PmdOffset (Pud, Address);
> +}
> +
> +/**
> +  Requests the memory space required for the page table,
> +  initializes it, and places it in the specified page middle directory,
> +  and get the page table entry corresponding to the virtual address.
> +
> +  @param  Pmd      A pointer to the page upper directory.
> +  @param  Address  The corresponding virtual address of the page table entry.
> +
> +  @retval          A pointer to the page table entry. Return NULL, if allocate
> +                   the memory buffer is fail.
> +**/
> +STATIC
> +PTE *
> +PteAllocGet (
> +  IN PMD    *Pmd,
> +  IN UINTN  Address
> +  )
> +{
> +  EFI_STATUS  Status;
> +
> +  if (PMD_IS_EMPTY (*Pmd)) {
> +    Status = PteAlloc (Pmd);
> +    ASSERT_EFI_ERROR (Status);
> +    if (EFI_ERROR (Status)) {
> +      return NULL;
> +    }
> +  }
> +
> +  return PteOffset (Pmd, Address);
> +}
> +
> +/**
> +  Gets the physical address of the page table entry corresponding to the specified virtual address.
> +
> +  @param  Address  The corresponding virtual address of the page table entry.
> +
> +  @retval  A pointer to the page table entry.
> +  @retval  NULL
> +**/
> +STATIC
> +PTE *
> +GetPteAddress (
> +  IN UINTN  Address
> +  )
> +{
> +  PGD  *Pgd;
> +  PUD  *Pud;
> +  PMD  *Pmd;
> +
> +  Pgd = PgdOffset (Address);
> +
> +  if (PGD_IS_EMPTY (*Pgd)) {
> +    return NULL;
> +  }
> +
> +  Pud = PudOffset (Pgd, Address);
> +
> +  if (PUD_IS_EMPTY (*Pud)) {
> +    return NULL;
> +  }
> +
> +  Pmd = PmdOffset (Pud, Address);
> +  if (PMD_IS_EMPTY (*Pmd)) {
> +    return NULL;
> +  }
> +
> +  if (IS_HUGE_PAGE (Pmd->PmdVal)) {
> +    return ((PTE *)Pmd);
> +  }
> +
> +  return PteOffset (Pmd, Address);
> +}
> +
> +/**
> +  Gets the Attributes of Huge Page.
> +
> +  @param  Pmd  A pointer to the page middle directory.
> +
> +  @retval     Value of Attributes.
> +**/
> +STATIC
> +UINTN
> +GetHugePageAttributes (
> +  IN  PMD  *Pmd
> +  )
> +{
> +  UINTN  Attributes;
> +  UINTN  GlobalFlag;
> +  UINTN  HugeVal;
> +
> +  HugeVal     = PMD_VAL (*Pmd);
> +  Attributes  = HugeVal & (~HUGEP_PAGE_MASK);
> +  GlobalFlag  = ((Attributes & (1 << PAGE_HGLOBAL_SHIFT)) >> PAGE_HGLOBAL_SHIFT) << PAGE_GLOBAL_SHIFT;
> +  Attributes &= ~(1 << PAGE_HGLOBAL_SHIFT);
> +  Attributes |= GlobalFlag;
> +  return Attributes;
> +}
> +
> +/**
> +  Establishes a page table entry based on the specified memory region.
> +
> +  @param  Pmd  A pointer to the page middle directory.
> +  @param  Address  The memory space start address.
> +  @param  End  The end address of the memory space.
> +  @param  Attributes  Memory space Attributes.
> +
> +  @retval     EFI_SUCCESS   The page table entry was created successfully.
> +  @retval     EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.
> +**/
> +STATIC
> +EFI_STATUS
> +MemoryMapPteRange (
> +  IN PMD    *Pmd,
> +  IN UINTN  Address,
> +  IN UINTN  End,
> +  IN UINTN  Attributes
> +  )
> +{
> +  PTE      *Pte;
> +  PTE      PteVal;
> +  BOOLEAN  UpDate;
> +
> +  Pte = PteAllocGet (Pmd, Address);
> +  if (!Pte) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  DEBUG ((
> +    DEBUG_INFO,
> +    "%a %d Address %p End %p  Attributes %llx\n",
> +    __func__,
> +    __LINE__,
> +    Address,
> +    End,
> +    Attributes
> +    ));
> +
> +  do {
> +    UpDate = FALSE;
> +    PteVal = MAKE_PTE (Address, Attributes);
> +
> +    if ((!PTE_IS_EMPTY (*Pte)) &&
> +        (PTE_VAL (*Pte) != PTE_VAL (PteVal)))
> +    {
> +      UpDate = TRUE;
> +    }
> +
> +    SetPte (Pte, PteVal);
> +    if (UpDate) {
> +      InvalidTlb (Address);
> +    }
> +  } while (Pte++, Address += EFI_PAGE_SIZE, Address != End);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Convert Huge Page to Page.
> +
> +  @param  Pmd  A pointer to the page middle directory.
> +  @param  Address  The memory space start address.
> +  @param  End  The end address of the memory space.
> +  @param  Attributes  Memory space Attributes.
> +
> +  @retval  EFI_SUCCESS   The page table entry was created successfully.
> +  @retval  EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.
> +**/
> +STATIC
> +EFI_STATUS
> +ConvertHugePageToPage (
> +  IN  PMD   *Pmd,
> +  IN UINTN  Address,
> +  IN UINTN  End,
> +  IN UINTN  Attributes
> +  )
> +{
> +  UINTN       OldAttributes;
> +  UINTN       HugePageEnd;
> +  UINTN       HugePageStart;
> +  EFI_STATUS  Status;
> +
> +  Status = EFI_SUCCESS;
> +
> +  if ((PMD_IS_EMPTY (*Pmd)) ||
> +      (!IS_HUGE_PAGE (Pmd->PmdVal)))
> +  {
> +    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
> +  } else {
> +    OldAttributes = GetHugePageAttributes (Pmd);
> +    if (Attributes == OldAttributes) {
> +      return Status;
> +    }
> +
> +    SetPmd (Pmd, (PTE *)(INVALID_PAGE));
> +    HugePageStart = Address & PMD_MASK;
> +    HugePageEnd   = HugePageStart + HUGE_PAGE_SIZE;
> +    ASSERT (HugePageEnd >= End);
> +
> +    if (Address > HugePageStart) {
> +      Status |= MemoryMapPteRange (Pmd, HugePageStart, Address, OldAttributes);
> +    }
> +
> +    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
> +
> +    if (End < HugePageEnd) {
> +      Status |= MemoryMapPteRange (Pmd, End, HugePageEnd, OldAttributes);
> +    }
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  Establishes a page middle directory based on the specified memory region.
> +
> +  @param  Pud  A pointer to the page upper directory.
> +  @param  Address  The memory space start address.
> +  @param  End  The end address of the memory space.
> +  @param  Attributes  Memory space Attributes.
> +
> +  @retval     EFI_SUCCESS   The page middle directory was created successfully.
> +  @retval     EFI_OUT_OF_RESOURCES  Page middle directory establishment failed due to resource exhaustion.
> +**/
> +STATIC
> +EFI_STATUS
> +MemoryMapPmdRange (
> +  IN PUD    *Pud,
> +  IN UINTN  Address,
> +  IN UINTN  End,
> +  IN UINTN  Attributes
> +  )
> +{
> +  PMD      *Pmd;
> +  UINTN    Next;
> +  PTE      PteVal;
> +  BOOLEAN  UpDate;
> +
> +  Pmd = PmdAllocGet (Pud, Address);
> +  if (Pmd == NULL) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  do {
> +    Next = PMD_ADDRESS_END (Address, End);
> +    if (((Address & (~PMD_MASK)) == 0) &&
> +        ((Next &  (~PMD_MASK)) == 0) &&
> +        (PMD_IS_EMPTY (*Pmd) || IS_HUGE_PAGE (Pmd->PmdVal)))
> +    {
> +      UpDate = FALSE;
> +      PteVal = MAKE_HUGE_PTE (Address, Attributes);
> +
> +      if ((!PMD_IS_EMPTY (*Pmd)) &&
> +          (PMD_VAL (*Pmd) != PTE_VAL (PteVal)))
> +      {
> +        UpDate = TRUE;
> +      }
> +
> +      SetPmd (Pmd, (PTE *)PteVal.PteVal);
> +      if (UpDate) {
> +        InvalidTlb (Address);
> +      }
> +    } else {
> +      ConvertHugePageToPage (Pmd, Address, Next, Attributes);
> +    }
> +  } while (Pmd++, Address = Next, Address != End);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Establishes a page upper directory based on the specified memory region.
> +
> +  @param  Pgd  A pointer to the page global directory.
> +  @param  Address  The memory space start address.
> +  @param  End  The end address of the memory space.
> +  @param  Attributes  Memory space Attributes.
> +
> +  @retval     EFI_SUCCESS   The page upper directory was created successfully.
> +  @retval     EFI_OUT_OF_RESOURCES  Page upper directory establishment failed due to resource exhaustion.
> +**/
> +STATIC
> +EFI_STATUS
> +MemoryMapPudRange (
> +  IN PGD    *Pgd,
> +  IN UINTN  Address,
> +  IN UINTN  End,
> +  IN UINTN  Attributes
> +  )
> +{
> +  PUD    *Pud;
> +  UINTN  Next;
> +
> +  Pud = PudAllocGet (Pgd, Address);
> +  if (Pud == NULL) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  do {
> +    Next = PUD_ADDRESS_END (Address, End);
> +    if (EFI_ERROR (MemoryMapPmdRange (Pud, Address, Next, Attributes))) {
> +      return EFI_OUT_OF_RESOURCES;
> +    }
> +  } while (Pud++, Address = Next, Address != End);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Establishes a page global directory based on the specified memory region.
> +
> +  @param  Start  The memory space start address.
> +  @param  End  The end address of the memory space.
> +  @param  Attributes  Memory space Attributes.
> +
> +  @retval     EFI_SUCCESS   The page global directory was created successfully.
> +  @retval     EFI_OUT_OF_RESOURCES  Page global directory establishment failed due to resource exhaustion.
> +**/
> +STATIC
> +EFI_STATUS
> +MemoryMapPageRange (
> +  IN UINTN  Start,
> +  IN UINTN  End,
> +  IN UINTN  Attributes
> +  )
> +{
> +  PGD         *Pgd;
> +  UINTN       Next;
> +  UINTN       Address;
> +  EFI_STATUS  Err;
> +
> +  Address = Start;
> +
> +  /* Get PGD(PTE PMD PUD PGD) in PageTables */
> +  Pgd = PgdOffset (Address);
> +  do {
> +    Next = PGD_ADDRESS_END (Address, End);
> +    /* Get Next Align Page to Map */
> +    Err = MemoryMapPudRange (Pgd, Address, Next, Attributes);
> +    if (Err) {
> +      return Err;
> +    }
> +  } while (Pgd++, Address = Next, Address != End);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Page tables are established from memory-mapped tables.
> +
> +  @param  MemoryRegion   A pointer to a memory-mapped table entry.
> +
> +  @retval     EFI_SUCCESS   The page table was created successfully.
> +  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
> +**/
> +EFI_STATUS
> +FillTranslationTable (
> +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
> +  )
> +{
> +  return MemoryMapPageRange (
> +           MemoryRegion->VirtualBase,
> +           (MemoryRegion->Length + MemoryRegion->VirtualBase),
> +           MemoryRegion->Attributes
> +           );
> +}
> +
> +/**
> +  Convert EFI Attributes to Loongarch Attributes.
> +
> +  @param[in]  EfiAttributes     Efi Attributes.
> +
> +  @retval  Corresponding architecture attributes.
> +**/
> +UINTN
> +EFIAPI
> +EfiAttributeConverse (
> +  IN UINTN  EfiAttributes
> +  )
> +{
> +  UINTN  LoongArchAttributes;
> +
> +  LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | PLV_KERNEL | PAGE_GLOBAL;
> +
> +  switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
> +    case EFI_MEMORY_UC:
> +      LoongArchAttributes |= CACHE_SUC;
> +      break;
> +    case EFI_MEMORY_WC:
> +      LoongArchAttributes |= CACHE_WUC;
> +      break;
> +    case EFI_MEMORY_WT:
> +    case EFI_MEMORY_WB:
> +      LoongArchAttributes |= CACHE_CC;
> +      break;
> +    default:
> +      LoongArchAttributes |= CACHE_CC;
> +      break;
> +  }
> +
> +  // Write protection attributes
> +  if (((EfiAttributes & EFI_MEMORY_RO) != 0) ||
> +      ((EfiAttributes & EFI_MEMORY_WP) != 0))
> +  {
> +    LoongArchAttributes &= ~PAGE_DIRTY;
> +  }
> +
> +  if ((EfiAttributes & EFI_MEMORY_RP) != 0) {
> +    LoongArchAttributes |= PAGE_NO_READ;
> +  }
> +
> +  // eXecute protection attribute
> +  if ((EfiAttributes & EFI_MEMORY_XP) != 0) {
> +    LoongArchAttributes |= PAGE_NO_EXEC;
> +  }
> +
> +  return LoongArchAttributes;
> +}
> +
> +/**
> +  Finds the first of the length and memory properties of the memory region corresponding
> +  to the specified base address.
> +
> +  @param[in]       BaseAddress       To find the base address of the memory region.
> +  @param[in, out]  RegionLength      Pointer holding:
> +                                      - At entry, the length of the memory region
> +                                        expected to be found.
> +                                      - At exit, the length of the memory region found.
> +  @param[out]      RegionAttributes  Properties of the memory region found.
> +
> +  @retval  EFI_SUCCESS           The corresponding memory area was successfully found
> +           EFI_NOT_FOUND         No memory area found
> +           EFI_OUT_OF_RESOURCES  Base address or expected memory region exceeds the maximum
> +                                 address.
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetMemoryRegionAttributes (
> +  IN     UINTN  BaseAddress,
> +  IN OUT UINTN  *RegionLength,
> +  OUT    UINTN  *RegionAttributes
> +  )
> +{
> +  PTE    *Pte;
> +  UINTN  Attributes;
> +  UINTN  AttributesTmp;
> +  UINTN  MaxAddress;
> +  UINTN  EndAddress;
> +  UINTN  AddSize;
> +
> +  if (!MmuIsInit ()) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  EndAddress = BaseAddress + *RegionLength;
> +  MaxAddress = LShiftU64 (1ULL, MAX_VA_BITS) - 1;
> +
> +  // Clean the value to prepare output to find region size.
> +  *RegionLength = 0x0;
> +
> +  if ((BaseAddress >= MaxAddress) || (EndAddress >= MaxAddress)) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  Pte = GetPteAddress (BaseAddress);
> +
> +  if (Pte == NULL) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  Attributes = GET_PAGE_ATTRIBUTES (*Pte);
> +  if (IS_HUGE_PAGE (Pte->PteVal)) {
> +    *RegionAttributes = Attributes & (~(PAGE_HUGE));
> +  } else {
> +    *RegionAttributes = Attributes;
> +  }
> +
> +  do {
> +    Pte = GetPteAddress (BaseAddress);
> +    if (Pte == NULL) {
> +      return EFI_SUCCESS;
> +    }
> +
> +    AttributesTmp = GET_PAGE_ATTRIBUTES (*Pte);
> +    if (AttributesTmp == Attributes) {
> +      if (IS_HUGE_PAGE (Pte->PteVal)) {
> +        AddSize = HUGE_PAGE_SIZE;
> +      } else {
> +        AddSize = EFI_PAGE_SIZE;
> +      }
> +
> +      *RegionLength += AddSize;
> +      BaseAddress   += AddSize;
> +    } else {
> +      return EFI_SUCCESS;
> +    }
> +  } while (BaseAddress <= EndAddress);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Sets the Attributes  of the specified memory region
> +
> +  @param[in]  BaseAddress    The base address of the memory region to set the Attributes.
> +  @param[in]  Length         The length of the memory region to set the Attributes.
> +  @param[in]  Attributes     The Attributes to be set.
> +  @param[in]  AttributeMask  Mask of memory attributes to take into account.
> +
> +  @retval  EFI_SUCCESS    The Attributes was set successfully
> +**/
> +EFI_STATUS
> +EFIAPI
> +SetMemoryRegionAttributes (
> +  IN EFI_PHYSICAL_ADDRESS  BaseAddress,
> +  IN UINTN                 Length,
> +  IN UINTN                 Attributes,
> +  IN UINT64                AttributeMask
> +  )
> +{
> +  EFI_STATUS  Status;
> +
> +  if (!MmuIsInit ()) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  Attributes = EfiAttributeConverse (Attributes);
> +  Status     = MemoryMapPageRange (BaseAddress, BaseAddress + Length, Attributes);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  return Status;
> +}
> +
> +/**
> +  Check to see if mmu successfully initializes and saves the result.
> +
> +  @param[in]  ImageHandle  The firmware allocated handle for the EFI image.
> +  @param[in]  SystemTable  A pointer to the EFI System Table.
> +
> +  @retval  RETURN_SUCCESS    Initialization succeeded.
> +**/
> +RETURN_STATUS
> +MmuInitialize (
> +  IN EFI_HANDLE        ImageHandle,
> +  IN EFI_SYSTEM_TABLE  *SystemTable
> +  )
> +{
> +  if (SWAP_PAGE_DIR != 0) {
> +    mMmuInited = TRUE;
> +  }
> +
> +  return RETURN_SUCCESS;
> +}
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
> new file mode 100644
> index 0000000000..d8c922c8fa
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
> @@ -0,0 +1,43 @@
> +/** @file
> +
> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +  @par Glossary:
> +    - Dir    - Directory
> +**/
> +
> +#ifndef  MMU_LIB_CORE_H_
> +#define  MMU_LIB_CORE_H_
> +
> +/**
> +  Iterates through the page directory to initialize it.
> +
> +  @param  Dst  A pointer to the directory of the page to initialize.
> +  @param  Num  The number of page directories to initialize.
> +  @param  Src  A pointer to the data used to initialize the page directory.
> +
> +  @retval VOID.
> +**/
> +VOID
> +PageDirInit (
> +  IN VOID   *dest,
> +  IN UINTN  Count,
> +  IN VOID   *src
> +  );
> +
> +/**
> +  Page tables are established from memory-mapped tables.
> +
> +  @param  MemoryRegion   A pointer to a memory-mapped table entry.
> +
> +  @retval     EFI_SUCCESS   The page table was created successfully.
> +  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
> +**/
> +EFI_STATUS
> +FillTranslationTable (
> +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
> +  );
> +
> +#endif // MMU_LIB_CORE_H_
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
> new file mode 100644
> index 0000000000..bac4f52327
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
> @@ -0,0 +1,279 @@
> +/** @file
> +
> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +  @par Glossary:
> +    - Pgd or Pgd or PGD    - Page Global Directory
> +    - Pud or Pud or PUD    - Page Upper Directory
> +    - Pmd or Pmd or PMD    - Page Middle Directory
> +    - Pte or pte or PTE    - Page Table Entry
> +    - Val or VAL or val    - Value
> +    - Dir    - Directory
> +**/
> +
> +#ifndef PAGE_H_
> +#define PAGE_H_
> +
> +#include <Library/CpuMmuLib.h>
> +
> +#define MAX_VA_BITS  47
> +#define PGD_WIDE     (8)
> +#define PUD_WIDE     (9)
> +#define PMD_WIDE     (9)
> +#define PTE_WIDE     (9)
> +
> +#define ENTRYS_PER_PGD  (1 << PGD_WIDE)
> +#define ENTRYS_PER_PUD  (1 << PUD_WIDE)
> +#define ENTRYS_PER_PMD  (1 << PMD_WIDE)
> +#define ENTRYS_PER_PTE  (1 << PTE_WIDE)
> +
> +#define PGD_SHIFT  (PUD_SHIFT + PUD_WIDE)
> +#define PUD_SHIFT  (PMD_SHIFT + PMD_WIDE)
> +#define PMD_SHIFT  (EFI_PAGE_SHIFT + PTE_WIDE)
> +#define PTE_SHIFT  (EFI_PAGE_SHIFT)
> +
> +#define PGD_SIZE  (1UL << PGD_SHIFT)
> +#define PUD_SIZE  (1UL << PUD_SHIFT)
> +#define PMD_SIZE  (1UL << PMD_SHIFT)
> +
> +#define PGD_MASK   (~(PGD_SIZE-1))
> +#define PUD_MASK   (~(PUD_SIZE-1))
> +#define PMD_MASK   (~(PMD_SIZE-1))
> +#define PAGE_MASK  (~(EFI_PAGE_SIZE - 1))
> +#define PFN_MASK   (~(((UINTN)(1) << (EFI_PAGE_SHIFT)) - 1) & \
> +                   (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
> +
> +#define HUGEP_PAGE_MASK  (~(((UINTN)(1) << (PMD_SHIFT)) - 1) & \
> +                          (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
> +
> +#define INVALID_PAGE  0
> +
> +typedef struct {
> +  UINTN    PgdVal;
> +} PGD;
> +typedef struct {
> +  UINTN    PudVal;
> +} PUD;
> +typedef struct {
> +  UINTN    PmdVal;
> +} PMD;
> +typedef struct {
> +  UINTN    PteVal;
> +} PTE;
> +
> +/**
> +  Gets the value of the page global directory table entry.
> +
> +  @param  x    Page global directory struct variables.
> +
> +  @retval   the value of the page global directory table entry.
> + **/
> +#define PGD_VAL(x)  ((x).PgdVal)
> +
> +/**
> +  Gets the value of the page upper directory table entry.
> +
> +  @param  x    Page upper directory struct variables.
> +
> +  @retval  the value of the page upper directory table entry.
> + **/
> +#define PUD_VAL(x)  ((x).PudVal)
> +
> +/**
> +  Gets the value of the page middle directory table entry.
> +
> +  @param  x    Page middle directory struct variables.
> +
> +  @retval  the value of the page middle directory table entry.
> + **/
> +#define PMD_VAL(x)  ((x).PmdVal)
> +
> +/**
> +  Gets the value of the page table entry.
> +
> +  @param  x    Page table entry struct variables.
> +
> +  @retval  the value of the page table entry.
> + **/
> +#define PTE_VAL(x)  ((x).PteVal)
> +
> +#define PGD_TABLE_SIZE  (ENTRYS_PER_PGD * sizeof(PGD))
> +#define PUD_TABLE_SIZE  (ENTRYS_PER_PUD * sizeof(PUD))
> +#define PMD_TABLE_SIZE  (ENTRYS_PER_PMD * sizeof(PMD))
> +#define PTE_TABLE_SIZE  (ENTRYS_PER_PTE * sizeof(PTE))
> +
> +/**
> +  Gets the physical address of the record in the page table entry.
> +
> +  @param  x    Page table entry struct variables.
> +
> +  @retval  the value of the physical address.
> + **/
> +#define GET_PAGE_ATTRIBUTES(x)  (UINTN) {(PTE_VAL(x) & ~PFN_MASK)}
> +
> +/**
> +  Gets the virtual address of the next block of the specified virtual address
> +  that is aligned with the size of the global page directory mapping.
> +
> +  @param  Address  Specifies the virtual address.
> +  @param  End    The end address of the memory region.
> +
> +  @retval   the specified virtual address  of the next block.
> + **/
> +#define PGD_ADDRESS_END(Address, End)                  \
> +({                                                     \
> +  UINTN Boundary = ((Address) + PGD_SIZE) & PGD_MASK;  \
> +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
> +})
> +
> +/**
> +  Gets the virtual address of the next block of the specified virtual address
> +  that is aligned with the size of the page upper directory mapping.
> +
> +  @param  Address  Specifies the virtual address.
> +  @param  End    The end address of the memory region.
> +
> +  @retval   the specified virtual address  of the next block.
> + **/
> +#define PUD_ADDRESS_END(Address, End)                  \
> +({                                                     \
> +  UINTN Boundary = ((Address) + PUD_SIZE) & PUD_MASK;  \
> +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
> +})
> +
> +/**
> +  Gets the virtual address of the next block of the specified virtual address
> +  that is aligned with the size of the page middle directory mapping.
> +
> +  @param  Address  Specifies the virtual address.
> +  @param  End    The end address of the memory region.
> +
> +  @retval   the specified virtual address  of the next block.
> + **/
> +#define PMD_ADDRESS_END(Address, End)                  \
> +({                                                     \
> +  UINTN Boundary = ((Address) + PMD_SIZE) & PMD_MASK;  \
> +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
> +})
> +
> +/**
> +  Get Specifies the virtual address corresponding to the index of the page global directory table entry.
> +
> +  @param  Address  Specifies the virtual address.
> +
> +  @retval   the index of the page global directory table entry.
> + **/
> +#define PGD_INDEX(Address)  (((Address) >> PGD_SHIFT) & (ENTRYS_PER_PGD-1))
> +
> +/**
> +  Get Specifies the virtual address corresponding to the index of the page upper directory table entry.
> +
> +  @param  Address  Specifies the virtual address.
> +  @param  End    The end address of the memory region.
> +
> +  @retval   the index of the page upper directory table entry.
> + **/
> +#define PUD_INDEX(Address)  (((Address) >> PUD_SHIFT) & (ENTRYS_PER_PUD - 1))
> +
> +/**
> +  Get Specifies the virtual address corresponding to the index of the page middle directory table entry.
> +
> +  @param  Address  Specifies the virtual address.
> +
> +  @retval   the index of the page middle directory table entry.
> + **/
> +#define PMD_INDEX(Address)  (((Address) >> PMD_SHIFT) & (ENTRYS_PER_PMD - 1))
> +
> +/**
> +  Get Specifies the virtual address corresponding to the index of the page table entry.
> +
> +  @param  Address  Specifies the virtual address.
> +
> +  @retval   the index of the page table entry.
> + **/
> +#define PTE_INDEX(Address)  (((Address) >> EFI_PAGE_SHIFT) & (ENTRYS_PER_PTE - 1))
> +
> +/**
> +  Calculates the value of the page table entry based on the specified virtual address and properties.
> +
> +  @param  Address  Specifies the virtual address.
> +  @param  Attributes  Specifies the Attributes.
> +
> +  @retval    the value of the page table entry.
> + **/
> +#define MAKE_PTE(Address, Attributes)  (PTE){((((Address) >> EFI_PAGE_SHIFT) << 12) | (Attributes))}
> +
> +/**
> +  Get Global bit from Attributes
> +
> +  @param  Attributes  Specifies the Attributes.
> + * */
> +#define GET_GLOBALBIT(Attributes)  ((Attributes & PAGE_GLOBAL) >> PAGE_GLOBAL_SHIFT)
> +
> +/**
> +  Calculates the value of the Huge page table entry based on the specified virtual address and properties.
> +
> +  @param  Address  Specifies the virtual address.
> +  @param  Attributes  Specifies the Attributes.
> +
> +  @retval    the value of the HUGE page table entry.
> + **/
> +#define MAKE_HUGE_PTE(Address, Attributes)  (PTE){(((((Address) >> PMD_SHIFT) << PMD_SHIFT) | \
> +                                             ((Attributes) | (GET_GLOBALBIT(Attributes) << PAGE_HGLOBAL_SHIFT) | \
> +                                             PAGE_HUGE)))}
> +
> +/**
> +  Check whether the large page table entry is.
> +
> +  @param  Val The value of the page table entry.
> +
> +  @retval    1   Is huge page table entry.
> +  @retval    0   Isn't huge page table entry.
> +**/
> +#define IS_HUGE_PAGE(Val)  ((((Val) & PAGE_HUGE) == PAGE_HUGE) && \
> +                            (((Val) & PAGE_HGLOBAL) == PAGE_HGLOBAL))
> +
> +#define HUGE_PAGE_SIZE  (PMD_SIZE)
> +
> +/**
> +  Check that the global page directory table entry is empty.
> +
> +  @param  pgd   the global page directory struct variables.
> +
> +  @retval    1   The page table is invalid.
> +  @retval    0   The page table is valid.
> +**/
> +#define PGD_IS_EMPTY(Val)  (PGD_VAL(Val) == INVALID_PAGE)
> +
> +/**
> +  Check that the page upper directory table entry is empty.
> +
> +  @param  pud   Page upper directory struct variables.
> +
> +  @retval    1   The page table is invalid.
> +  @retval    0   The page table is valid.
> +**/
> +#define PUD_IS_EMPTY(Val)  (PUD_VAL(Val) == INVALID_PAGE)
> +
> +/**
> +  Check that the page middle directory table entry is empty.
> +
> +  @param  pmd   Page middle directory struct variables.
> +
> +  @retval    1   The page table is invalid.
> +  @retval    0   The page table is valid.
> +**/
> +#define PMD_IS_EMPTY(Val)  (PMD_VAL(Val) == INVALID_PAGE)
> +
> +/**
> +  Check that the page the page table entry is empty.
> +
> +  @param  pte   Page table entry struct variables.
> +
> +  @retval    1   The page table is invalid.
> +  @retval    0   The page table is valid.
> +**/
> +#define PTE_IS_EMPTY(Val)  (!(PTE_VAL(Val) & (~PAGE_VALID)))
> +#endif // PAGE_H_
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
> new file mode 100644
> index 0000000000..c214e8d847
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
> @@ -0,0 +1,178 @@
> +/** @file
> +  CPU Memory Map Unit PEI phase driver.
> +
> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +  @par Glossary:
> +    - Tlb      - Translation Lookaside Buffer
> +**/
> +
> +#include <Uefi.h>
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/CacheMaintenanceLib.h>
> +#include <Library/CpuMmuLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/PcdLib.h>
> +#include <Register/LoongArch64/Csr.h>
> +
> +#include "Page.h"
> +#include "Tlb.h"
> +#include "CommonMmuLib.h"
> +
> +//
> +// For coding convenience, define the maximum valid
> +// LoongArch exception.
> +// Since UEFI V2.11, it will be present in DebugSupport.h.
> +//
> +#define MAX_LOONGARCH_EXCEPTION  64
> +
> +/**
> +  Create a page table and initialize the memory management unit(MMU).
> +
> +  @param[in]   MemoryTable           A pointer to a memory ragion table.
> +  @param[out]  TranslationTableBase  A pointer to a translation table base address.
> +  @param[out]  TranslationTableSize  A pointer to a translation table base size.
> +
> +  @retval  EFI_SUCCESS                Configure MMU successfully.
> +           EFI_INVALID_PARAMETER      MemoryTable is NULL.
> +           EFI_UNSUPPORTED            Out of memory space or size not aligned.
> +**/
> +EFI_STATUS
> +EFIAPI
> +ConfigureMemoryManagementUnit (
> +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryTable,
> +  OUT VOID                      **TranslationTableBase OPTIONAL,
> +  OUT UINTN                     *TranslationTableSize  OPTIONAL
> +  )
> +{
> +  PGD            *SwapperPageDir;
> +  UINTN          PgdShift;
> +  UINTN          PgdWide;
> +  UINTN          PudShift;
> +  UINTN          PudWide;
> +  UINTN          PmdShift;
> +  UINTN          PmdWide;
> +  UINTN          PteShift;
> +  UINTN          PteWide;
> +  UINTN          Length;
> +  UINTN          TlbReEntry;
> +  UINTN          TlbReEntryOffset;
> +  UINTN          Remaining;
> +  RETURN_STATUS  Status;
> +
> +  SwapperPageDir = NULL;
> +  PgdShift       = PGD_SHIFT;
> +  PgdWide        = PGD_WIDE;
> +  PudShift       = PUD_SHIFT;
> +  PudWide        = PUD_WIDE;
> +  PmdShift       = PMD_SHIFT;
> +  PmdWide        = PMD_WIDE;
> +  PteShift       = PTE_SHIFT;
> +  PteWide        = PTE_WIDE;
> +
> +  if (MemoryTable == NULL) {
> +    ASSERT (MemoryTable != NULL);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  SwapperPageDir = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
> +  ZeroMem (SwapperPageDir, PGD_TABLE_SIZE);
> +
> +  if (SwapperPageDir == NULL) {
> +    goto FreeTranslationTable;
> +  }
> +
> +  CsrWrite (LOONGARCH_CSR_PGDL, (UINTN)SwapperPageDir);
> +
> +  while (MemoryTable->Length != 0) {
> +    DEBUG ((
> +      DEBUG_INFO,
> +      "%a %d VirtualBase %p VirtualEnd %p Attributes %p .\n",
> +      __func__,
> +      __LINE__,
> +      MemoryTable->VirtualBase,
> +      (MemoryTable->Length + MemoryTable->VirtualBase),
> +      MemoryTable->Attributes
> +      ));
> +
> +    Status = FillTranslationTable (MemoryTable);
> +    if (EFI_ERROR (Status)) {
> +      goto FreeTranslationTable;
> +    }
> +
> +    MemoryTable++;
> +  }
> +
> +  //
> +  // TLB Re-entry address at the end of exception vector, a vector is up to 512 bytes,
> +  // so the starting address is: total exception vector size + total interrupt vector size + base.
> +  // The total size of TLB handler and exception vector size and interrupt vector size should not
> +  // be lager than 64KB.
> +  //
> +  Length           = (UINTN)HandleTlbRefillEnd - (UINTN)HandleTlbRefillStart;
> +  TlbReEntryOffset = (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * 512;
> +  Remaining        = TlbReEntryOffset % SIZE_4KB;
> +  if (Remaining != 0x0) {
> +    TlbReEntryOffset += (SIZE_4KB - Remaining);
> +  }
> +
> +  TlbReEntry = PcdGet64 (PcdCpuExceptionVectorBaseAddress) + TlbReEntryOffset;
> +  if ((TlbReEntryOffset + Length) > SIZE_64KB) {
> +    goto FreeTranslationTable;
> +  }
> +
> +  //
> +  // Ensure that TLB refill exception base address alignment is equals to 4KB and is valid.
> +  //
> +  if (TlbReEntry & (SIZE_4KB - 1)) {
> +    goto FreeTranslationTable;
> +  }
> +
> +  CopyMem ((VOID *)TlbReEntry, HandleTlbRefillStart, Length);
> +  InvalidateInstructionCacheRange ((VOID *)(UINTN)HandleTlbRefillStart, Length);
> +
> +  DEBUG ((
> +    DEBUG_INFO,
> +    "%a  %d PteShift %d PteWide %d PmdShift %d PmdWide %d PudShift %d PudWide %d PgdShift %d PgdWide %d.\n",
> +    __func__,
> +    __LINE__,
> +    PteShift,
> +    PteWide,
> +    PmdShift,
> +    PmdWide,
> +    PudShift,
> +    PudWide,
> +    PgdShift,
> +    PgdWide
> +    ));
> +
> +  //
> +  // Set the address of TLB refill exception handler
> +  //
> +  SetTlbRebaseAddress ((UINTN)TlbReEntry);
> +
> +  //
> +  // Set page size
> +  //
> +  CsrXChg (LOONGARCH_CSR_TLBIDX, (DEFAULT_PAGE_SIZE << CSR_TLBIDX_SIZE), CSR_TLBIDX_SIZE_MASK);
> +  CsrWrite (LOONGARCH_CSR_STLBPGSIZE, DEFAULT_PAGE_SIZE);
> +  CsrXChg (LOONGARCH_CSR_TLBREHI, (DEFAULT_PAGE_SIZE << CSR_TLBREHI_PS_SHIFT), CSR_TLBREHI_PS);
> +
> +  CsrWrite (LOONGARCH_CSR_PWCTL0, (PteShift | PteWide << 5 | PmdShift << 10 | PmdWide << 15 | PudShift << 20 | PudWide << 25));
> +  CsrWrite (LOONGARCH_CSR_PWCTL1, (PgdShift | PgdWide << 6));
> +
> +  DEBUG ((DEBUG_INFO, "%a %d Enable Mmu Start PageBassAddress %p.\n", __func__, __LINE__, SwapperPageDir));
> +
> +  return EFI_SUCCESS;
> +
> +FreeTranslationTable:
> +  if (SwapperPageDir != NULL) {
> +    FreePages (SwapperPageDir, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
> +  }
> +
> +  return EFI_UNSUPPORTED;
> +}
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
> new file mode 100644
> index 0000000000..9a681ce8e1
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
> @@ -0,0 +1,48 @@
> +/** @file
> +
> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef TLB_H_
> +#define TLB_H_
> +
> +/**
> +  Invalid corresponding TLB entries are based on the address given
> +
> +  @param Address The address corresponding to the invalid page table entry
> +
> +  @retval  none
> +**/
> +VOID
> +InvalidTlb (
> +  UINTN  Address
> +  );
> +
> +/**
> +  TLB refill handler start.
> +
> +  @param  none
> +
> +  @retval none
> +**/
> +VOID
> +HandleTlbRefillStart (
> +  VOID
> +  );
> +
> +/**
> +  TLB refill handler end.
> +
> +  @param  none
> +
> +  @retval none
> +**/
> +VOID
> +HandleTlbRefillEnd (
> +  VOID
> +  );
> +
> +#endif // TLB_H_
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
> new file mode 100644
> index 0000000000..c9a8c16336
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
> @@ -0,0 +1,44 @@
> +#------------------------------------------------------------------------------
> +#
> +# TLB operation functions
> +#
> +# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +#-----------------------------------------------------------------------------
> +
> +#include <Register/LoongArch64/Csr.h>
> +
> +ASM_GLOBAL ASM_PFX(HandleTlbRefillStart)
> +ASM_GLOBAL ASM_PFX(HandleTlbRefillEnd)
> +ASM_GLOBAL ASM_PFX(InvalidTlb)
> +
> +#
> +#  Refill the page table.
> +#  @param  VOID
> +#  @retval  VOID
> +#
> +ASM_PFX(HandleTlbRefillStart):
> +  csrwr   $t0, LOONGARCH_CSR_TLBRSAVE
> +  csrrd   $t0, LOONGARCH_CSR_PGD
> +  lddir   $t0, $t0, 3   #Put pud BaseAddress into T0
> +  lddir   $t0, $t0, 2   #Put pmd BaseAddress into T0
> +  lddir   $t0, $t0, 1   #Put pte BaseAddress into T0
> +  ldpte   $t0, 0
> +  ldpte   $t0, 1
> +  tlbfill   // refill hi,lo0,lo1
> +  csrrd   $t0, LOONGARCH_CSR_TLBRSAVE
> +  ertn
> +ASM_PFX(HandleTlbRefillEnd):
> +
> +#
> +# Invalid corresponding TLB entries are based on the address given
> +# @param a0 The address corresponding to the invalid page table entry
> +# @retval  none
> +#
> +ASM_PFX(InvalidTlb):
> +    invtlb  INVTLB_ADDR_GTRUE_OR_ASID, $zero, $a0
> +    jirl    $zero, $ra, 0
> +
> +    .end
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
> new file mode 100644
> index 0000000000..45b15db4c9
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
> @@ -0,0 +1,44 @@
> +## @file
> +#  CPU Memory Map Unit PEI phase driver.
> +#
> +#  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                      = PeiCpuMmuLib
> +  MODULE_UNI_FILE                = PeiCpuMmuLib.uni
> +  FILE_GUID                      = F67EB983-AC2A-7550-AB69-3BC51A1C895B
> +  MODULE_TYPE                    = PEIM
> +  VERSION_STRING                 = 1.0
> +  LIBRARY_CLASS                  = CpuMmuLib | SEC PEIM
> +
> +#
> +#  VALID_ARCHITECTURES           = LOONGARCH64
> +#
> +
> +[Sources.LoongArch64]
> +  LoongArch64/TlbOperation.S   | GCC
> +  LoongArch64/CommonMmuLib.c
> +  LoongArch64/PeiCpuMmuLib.c
> +  LoongArch64/CommonMmuLib.h
> +  LoongArch64/Tlb.h
> +  LoongArch64/Page.h
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  UefiCpuPkg/UefiCpuPkg.dec
> +
> +[PCD]
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask
> +  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress
> +
> +[LibraryClasses]
> +  CacheMaintenanceLib
> +  DebugLib
> +  MemoryAllocationLib
> +  PcdLib
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
> new file mode 100644
> index 0000000000..3e21334f3e
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
> @@ -0,0 +1,14 @@
> +// /** @file
> +// CPU Memory Manager Unit library instance for PEI modules.
> +//
> +// CPU Memory Manager Unit library instance for PEI modules.
> +//
> +// 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 "CPU Memory Manager Unit library instance for PEI modules."
> +
> +#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for PEI modules."
> diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
> index 28eed85bce..178dc3c0f9 100644
> --- a/UefiCpuPkg/UefiCpuPkg.dsc
> +++ b/UefiCpuPkg/UefiCpuPkg.dsc
> @@ -207,5 +207,9 @@
>     UefiCpuPkg/CpuTimerDxeRiscV64/CpuTimerDxeRiscV64.inf
>     UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf
>   
> +[Components.LOONGARCH64]
> +  UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
> +  UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
> +
>   [BuildOptions]
>     *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114850): https://edk2.groups.io/g/devel/message/114850
Mute This Topic: https://groups.io/mt/104068947/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 15/37] UefiCpuPkg: Add multiprocessor library for LoongArch64
       [not found] ` <17ADD1DB56FC4702.24595@groups.io>
@ 2024-01-31  3:32   ` Chao Li
       [not found]   ` <17AF511741BD9C8B.15701@groups.io>
  1 sibling, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-31  3:32 UTC (permalink / raw)
  To: devel, Ray Ni; +Cc: Eric Dong, Rahul Kumar, Gerd Hoffmann

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

Hi Ray,

Can you please help to review this patch again?


Thanks,
Chao
On 2024/1/26 14:29, Chao Li wrote:
> Added LoongArch multiprocessor initialization instance into MpInitLib.
>
> BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>
> Cc: Eric Dong<eric.dong@intel.com>
> Cc: Ray Ni<ray.ni@intel.com>
> Cc: Rahul Kumar<rahul1.kumar@intel.com>
> Cc: Gerd Hoffmann<kraxel@redhat.com>
> Signed-off-by: Chao Li<lichao@loongson.cn>
> ---
>   UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf |   27 +-
>   .../Library/MpInitLib/LoongArch64/DxeMpLib.c  |  480 +++++
>   .../Library/MpInitLib/LoongArch64/MpLib.c     | 1621 +++++++++++++++++
>   .../Library/MpInitLib/LoongArch64/MpLib.h     |  361 ++++
>   .../Library/MpInitLib/LoongArch64/PeiMpLib.c  |  404 ++++
>   UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf |   27 +-
>   6 files changed, 2902 insertions(+), 18 deletions(-)
>   create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/DxeMpLib.c
>   create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.c
>   create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.h
>   create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/PeiMpLib.c
>
> diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
> index 55e46d4a1f..6db26f5fec 100644
> --- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
> +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
> @@ -2,6 +2,7 @@
>   #  MP Initialize Library instance for DXE driver.
>   #
>   #  Copyright (c) 2016 - 2023, Intel Corporation. All rights reserved.<BR>
> +#  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>   #  SPDX-License-Identifier: BSD-2-Clause-Patent
>   #
>   ##
> @@ -18,7 +19,7 @@
>   #
>   # The following information is for reference only and not required by the build tools.
>   #
> -#  VALID_ARCHITECTURES           = IA32 X64
> +#  VALID_ARCHITECTURES           = IA32 X64 LOONGARCH64
>   #
>   
>   [Sources.IA32]
> @@ -31,7 +32,7 @@
>     X64/MpFuncs.nasm
>     X64/CreatePageTable.c
>   
> -[Sources.common]
> +[Sources.IA32, Sources.X64]
>     AmdSev.c
>     MpEqu.inc
>     DxeMpLib.c
> @@ -40,24 +41,32 @@
>     Microcode.c
>     MpHandOff.h
>   
> +[Sources.LoongArch64]
> +  LoongArch64/DxeMpLib.c
> +  LoongArch64/MpLib.c
> +  LoongArch64/MpLib.h
> +
>   [Packages]
>     MdePkg/MdePkg.dec
>     MdeModulePkg/MdeModulePkg.dec
>     UefiCpuPkg/UefiCpuPkg.dec
>   
> -[LibraryClasses]
> +[LibraryClasses.common]
>     BaseLib
> -  LocalApicLib
> -  MemoryAllocationLib
> -  HobLib
> -  MtrrLib
>     CpuLib
> -  UefiBootServicesTableLib
>     DebugAgentLib
> -  SynchronizationLib
> +  HobLib
> +  MemoryAllocationLib
>     PcdLib
> +  UefiBootServicesTableLib
> +  SynchronizationLib
> +
> +[LibraryClasses.IA32, LibraryClasses.X64]
>     CcExitLib
> +  LocalApicLib
>     MicrocodeLib
> +  MtrrLib
> +
>   [LibraryClasses.X64]
>     CpuPageTableLib
>   
> diff --git a/UefiCpuPkg/Library/MpInitLib/LoongArch64/DxeMpLib.c b/UefiCpuPkg/Library/MpInitLib/LoongArch64/DxeMpLib.c
> new file mode 100644
> index 0000000000..739da77e32
> --- /dev/null
> +++ b/UefiCpuPkg/Library/MpInitLib/LoongArch64/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/MpInitLib/LoongArch64/MpLib.c b/UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.c
> new file mode 100644
> index 0000000000..930d34aa3d
> --- /dev/null
> +++ b/UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.c
> @@ -0,0 +1,1621 @@
> +/** @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>
> +
> +#define INVALID_APIC_ID  0xFFFFFFFF
> +
> +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) {
> +    Index2 = 0;
> +    for (Index1 = (PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1); Index1 > 0; Index1--) {
> +      if (CpuInfoInHob[Index1].ApicId != INVALID_APIC_ID) {
> +        if (Index1 == ApCount) {
> +          break;
> +        } else {
> +          for ( ; Index2 <= ApCount; Index2++) {
> +            if (CpuInfoInHob[Index2].ApicId == INVALID_APIC_ID) {
> +              CopyMem (&CpuInfoInHob[Index2], &CpuInfoInHob[Index1], sizeof (CPU_INFO_IN_HOB));
> +              CpuMpData->CpuData[Index2]  = CpuMpData->CpuData[Index1];
> +              CpuInfoInHob[Index1].ApicId = INVALID_APIC_ID;
> +              break;
> +            }
> +          }
> +        }
> +      } else {
> +        continue;
> +      }
> +    }
> +
> +    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 and initialization APs ApicId status.
> +  //
> +  for (Index = 0; Index < MaxLogicalProcessorNumber; Index++) {
> +    CpuMpData->CpuData[Index].StartupApSignal =
> +      (UINT32 *)((MpBuffer + BufferSize - MonitorBufferSize) + (sizeof (WAKEUP_AP_SIGNAL) * Index));
> +    if ((OldCpuMpData == NULL) && (Index != CpuMpData->BspNumber)) {
> +      ((CPU_INFO_IN_HOB  *)CpuMpData->CpuInfoInHob)[Index].ApicId = INVALID_APIC_ID;
> +    }
> +  }
> +
> +  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/MpInitLib/LoongArch64/MpLib.h b/UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.h
> new file mode 100644
> index 0000000000..b9c6c55b41
> --- /dev/null
> +++ b/UefiCpuPkg/Library/MpInitLib/LoongArch64/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 MP_CPU_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 MP_CPU_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/MpInitLib/LoongArch64/PeiMpLib.c b/UefiCpuPkg/Library/MpInitLib/LoongArch64/PeiMpLib.c
> new file mode 100644
> index 0000000000..d1c5e55b57
> --- /dev/null
> +++ b/UefiCpuPkg/Library/MpInitLib/LoongArch64/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/Library/MpInitLib/PeiMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
> index bc3d716aa9..36ee6b9c29 100644
> --- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
> +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
> @@ -2,6 +2,7 @@
>   #  MP Initialize Library instance for PEI driver.
>   #
>   #  Copyright (c) 2016 - 2021, Intel Corporation. All rights reserved.<BR>
> +#  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>   #  SPDX-License-Identifier: BSD-2-Clause-Patent
>   #
>   ##
> @@ -18,7 +19,7 @@
>   #
>   # The following information is for reference only and not required by the build tools.
>   #
> -#  VALID_ARCHITECTURES           = IA32 X64
> +#  VALID_ARCHITECTURES           = IA32 X64 LOONGARCH64
>   #
>   
>   [Sources.IA32]
> @@ -29,7 +30,7 @@
>     X64/AmdSev.c
>     X64/MpFuncs.nasm
>   
> -[Sources.common]
> +[Sources.IA32, Sources.X64]
>     AmdSev.c
>     MpEqu.inc
>     PeiMpLib.c
> @@ -37,23 +38,31 @@
>     MpLib.h
>     Microcode.c
>     MpHandOff.h
> +
> +[Sources.LoongArch64]
> +  LoongArch64/PeiMpLib.c
> +  LoongArch64/MpLib.c
> +  LoongArch64/MpLib.h
> +
>   [Packages]
>     MdePkg/MdePkg.dec
>     UefiCpuPkg/UefiCpuPkg.dec
>     MdeModulePkg/MdeModulePkg.dec
>   
> -[LibraryClasses]
> +[LibraryClasses.common]
>     BaseLib
> -  LocalApicLib
> -  MemoryAllocationLib
> -  HobLib
> -  MtrrLib
>     CpuLib
> -  SynchronizationLib
> -  PeiServicesLib
> +  HobLib
> +  MemoryAllocationLib
>     PcdLib
> +  PeiServicesLib
> +  SynchronizationLib
> +
> +[LibraryClasses.IA32, LibraryClasses.X64]
>     CcExitLib
> +  LocalApicLib
>     MicrocodeLib
> +  MtrrLib
>   
>   [Pcd]
>     gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber        ## CONSUMES


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114851): https://edk2.groups.io/g/devel/message/114851
Mute This Topic: https://groups.io/mt/104068953/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 16/37] UefiCpuPkg: Add CpuDxe driver for LoongArch64
       [not found] ` <17ADD1DCBDD4B7FE.11113@groups.io>
@ 2024-01-31  3:32   ` Chao Li
       [not found]   ` <17AF511F29808828.16460@groups.io>
  1 sibling, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-31  3:32 UTC (permalink / raw)
  To: devel, Ray Ni
  Cc: Eric Dong, Rahul Kumar, Gerd Hoffmann, Baoqi Zhang, Dongyan Qian

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

Hi Ray,

Can you please help to review this patch again?


Thanks,
Chao
On 2024/1/26 14:29, Chao Li wrote:
> Added LoongArch64 CPU driver into CpuDxe.
>
> BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>
> Cc: Eric Dong<eric.dong@intel.com>
> Cc: Ray Ni<ray.ni@intel.com>
> Cc: Rahul Kumar<rahul1.kumar@intel.com>
> Cc: Gerd Hoffmann<kraxel@redhat.com>
> Signed-off-by: Chao Li<lichao@loongson.cn>
> Co-authored-by: Baoqi Zhang<zhangbaoqi@loongson.cn>
> Co-authored-by: Dongyan Qian<qiandongyan@loongson.cn>
> ---
>   UefiCpuPkg/CpuDxe/CpuDxe.inf              |  23 +-
>   UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.c    | 454 ++++++++++++++++++
>   UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.h    | 288 ++++++++++++
>   UefiCpuPkg/CpuDxe/LoongArch64/CpuMp.c     | 544 ++++++++++++++++++++++
>   UefiCpuPkg/CpuDxe/LoongArch64/Exception.c | 159 +++++++
>   5 files changed, 1464 insertions(+), 4 deletions(-)
>   create mode 100644 UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.c
>   create mode 100644 UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.h
>   create mode 100644 UefiCpuPkg/CpuDxe/LoongArch64/CpuMp.c
>   create mode 100644 UefiCpuPkg/CpuDxe/LoongArch64/Exception.c
>
> diff --git a/UefiCpuPkg/CpuDxe/CpuDxe.inf b/UefiCpuPkg/CpuDxe/CpuDxe.inf
> index 1d3e9f8cdb..18ebd2eb2c 100644
> --- a/UefiCpuPkg/CpuDxe/CpuDxe.inf
> +++ b/UefiCpuPkg/CpuDxe/CpuDxe.inf
> @@ -3,6 +3,7 @@
>   #
>   #  Copyright (c) 2008 - 2019, Intel Corporation. All rights reserved.<BR>
>   #  Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
> +#  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>   #
>   #  SPDX-License-Identifier: BSD-2-Clause-Patent
>   #
> @@ -22,17 +23,16 @@
>     MdeModulePkg/MdeModulePkg.dec
>     UefiCpuPkg/UefiCpuPkg.dec
>   
> -[LibraryClasses]
> +[LibraryClasses.common]
>     BaseLib
>     BaseMemoryLib
>     CpuLib
> +  CacheMaintenanceLib
>     DebugLib
>     DxeServicesTableLib
>     MemoryAllocationLib
> -  MtrrLib
>     UefiBootServicesTableLib
>     UefiDriverEntryPoint
> -  LocalApicLib
>     UefiLib
>     CpuExceptionHandlerLib
>     HobLib
> @@ -41,7 +41,14 @@
>     TimerLib
>     PeCoffGetEntryPointLib
>   
> -[Sources]
> +[LibraryClasses.IA32, LibraryClasses.X64]
> +  LocalApicLib
> +  MtrrLib
> +
> +[LibraryClasses.LoongArch64]
> +  CpuMmuLib
> +
> +[Sources.IA32, Sources.X64]
>     CpuDxe.c
>     CpuDxe.h
>     CpuGdt.c
> @@ -59,6 +66,13 @@
>     X64/CpuAsm.nasm
>     X64/PagingAttribute.c
>   
> +[Sources.LoongArch64]
> +  CpuMp.h
> +  LoongArch64/CpuDxe.c
> +  LoongArch64/CpuMp.c
> +  LoongArch64/Exception.c
> +  LoongArch64/CpuDxe.h
> +
>   [Protocols]
>     gEfiCpuArchProtocolGuid                       ## PRODUCES
>     gEfiMpServiceProtocolGuid                     ## PRODUCES
> @@ -77,6 +91,7 @@
>     gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard                       ## CONSUMES
>     gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPropertyMask               ## CONSUMES
>     gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask    ## CONSUMES
> +  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress            ## CONSUMES
>     gUefiCpuPkgTokenSpaceGuid.PcdCpuStackSwitchExceptionList              ## CONSUMES
>     gUefiCpuPkgTokenSpaceGuid.PcdCpuKnownGoodStackSize                    ## CONSUMES
>     gEfiMdeModulePkgTokenSpaceGuid.PcdTdxSharedBitMask                    ## CONSUMES
> diff --git a/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.c b/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.c
> new file mode 100644
> index 0000000000..65ed0b3913
> --- /dev/null
> +++ b/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.c
> @@ -0,0 +1,454 @@
> +/** @file CpuDxe.c
> +
> +  CPU DXE Module to produce CPU ARCH Protocol.
> +
> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#include "CpuDxe.h"
> +#include "CpuMp.h"
> +#include <Guid/IdleLoopEvent.h>
> +#include <Library/CpuMmuLib.h>
> +#include <Library/TimerLib.h>
> +#include <Register/LoongArch64/Csr.h>
> +
> +UINT64  mTimerPeriod = 0;
> +
> +/**
> +  IPI Interrupt Handler.
> +
> +  @param InterruptType    The type of interrupt that occurred
> +  @param SystemContext    A pointer to the system context when the interrupt occurred
> +**/
> +VOID
> +EFIAPI
> +IpiInterruptHandler (
> +  IN EFI_EXCEPTION_TYPE  InterruptType,
> +  IN EFI_SYSTEM_CONTEXT  SystemContext
> +  );
> +
> +//
> +// Globals used to initialize the protocol
> +//
> +EFI_HANDLE             mCpuHandle = NULL;
> +EFI_CPU_ARCH_PROTOCOL  gCpu       = {
> +  CpuFlushCpuDataCache,
> +  CpuEnableInterrupt,
> +  CpuDisableInterrupt,
> +  CpuGetInterruptState,
> +  CpuInit,
> +  CpuRegisterInterruptHandler,
> +  CpuGetTimerValue,
> +  CpuSetMemoryAttributes,
> +  0,          // NumberOfTimers
> +  4,          // DmaBufferAlignment
> +};
> +
> +/**
> +  This function flushes the range of addresses from Start to Start+Length
> +  from the processor's data cache. If Start is not aligned to a cache line
> +  boundary, then the bytes before Start to the preceding cache line boundary
> +  are also flushed. If Start+Length is not aligned to a cache line boundary,
> +  then the bytes past Start+Length to the end of the next cache line boundary
> +  are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be
> +  supported. If the data cache is fully coherent with all DMA operations, then
> +  this function can just return EFI_SUCCESS. If the processor does not support
> +  flushing a range of the data cache, then the entire data cache can be flushed.
> +
> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
> +  @param  Start            The beginning physical address to flush from the processor's data
> +                           cache.
> +  @param  Length           The number of bytes to flush from the processor's data cache. This
> +                           function may flush more bytes than Length specifies depending upon
> +                           the granularity of the flush operation that the processor supports.
> +  @param  FlushType        Specifies the type of flush operation to perform.
> +
> +  @retval EFI_SUCCESS           The address range from Start to Start+Length was flushed from
> +                                the processor's data cache.
> +  @retval EFI_INVALID_PARAMETER The processor does not support the cache flush type specified
> +                                by FlushType.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuFlushCpuDataCache (
> +  IN EFI_CPU_ARCH_PROTOCOL  *This,
> +  IN EFI_PHYSICAL_ADDRESS   Start,
> +  IN UINT64                 Length,
> +  IN EFI_CPU_FLUSH_TYPE     FlushType
> +  )
> +{
> +  switch (FlushType) {
> +    case EfiCpuFlushTypeWriteBack:
> +      WriteBackDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length);
> +      break;
> +    case EfiCpuFlushTypeInvalidate:
> +      InvalidateDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length);
> +      break;
> +    case EfiCpuFlushTypeWriteBackInvalidate:
> +      WriteBackInvalidateDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length);
> +      break;
> +    default:
> +      return EFI_INVALID_PARAMETER;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This function enables interrupt processing by the processor.
> +
> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
> +
> +  @retval EFI_SUCCESS           Interrupts are enabled on the processor.
> +  @retval EFI_DEVICE_ERROR      Interrupts could not be enabled on the processor.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuEnableInterrupt (
> +  IN EFI_CPU_ARCH_PROTOCOL  *This
> +  )
> +{
> +  EnableInterrupts ();
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This function disables interrupt processing by the processor.
> +
> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
> +
> +  @retval EFI_SUCCESS           Interrupts are disabled on the processor.
> +  @retval EFI_DEVICE_ERROR      Interrupts could not be disabled on the processor.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuDisableInterrupt (
> +  IN EFI_CPU_ARCH_PROTOCOL  *This
> +  )
> +{
> +  DisableInterrupts ();
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This function retrieves the processor's current interrupt state a returns it in
> +  State. If interrupts are currently enabled, then TRUE is returned. If interrupts
> +  are currently disabled, then FALSE is returned.
> +
> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
> +  @param  State            A pointer to the processor's current interrupt state. Set to TRUE if
> +                           interrupts are enabled and FALSE if interrupts are disabled.
> +
> +  @retval EFI_SUCCESS           The processor's current interrupt state was returned in State.
> +  @retval EFI_INVALID_PARAMETER State is NULL.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuGetInterruptState (
> +  IN  EFI_CPU_ARCH_PROTOCOL  *This,
> +  OUT BOOLEAN                *State
> +  )
> +{
> +  if (State == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  *State = GetInterruptState ();
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This function generates an INIT on the processor. If this function succeeds, then the
> +  processor will be reset, and control will not be returned to the caller. If InitType is
> +  not supported by this processor, or the processor cannot programmatically generate an
> +  INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error
> +  occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned.
> +
> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
> +  @param  InitType         The type of processor INIT to perform.
> +
> +  @retval EFI_SUCCESS           The processor INIT was performed. This return code should never be seen.
> +  @retval EFI_UNSUPPORTED       The processor INIT operation specified by InitType is not supported
> +                                by this processor.
> +  @retval EFI_DEVICE_ERROR      The processor INIT failed.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuInit (
> +  IN EFI_CPU_ARCH_PROTOCOL  *This,
> +  IN EFI_CPU_INIT_TYPE      InitType
> +  )
> +{
> +  return EFI_UNSUPPORTED;
> +}
> +
> +/**
> +  Registers a function to be called from the CPU interrupt handler.
> +
> +  @param  This                   Protocol instance structure
> +  @param  InterruptType          Defines which interrupt to hook. IA-32
> +                                 valid range is 0x00 through 0xFF
> +  @param  InterruptHandler       A pointer to a function of type
> +                                 EFI_CPU_INTERRUPT_HANDLER that is called
> +                                 when a processor interrupt occurs.  A null
> +                                 pointer is an error condition.
> +
> +  @retval EFI_SUCCESS            If handler installed or uninstalled.
> +  @retval EFI_ALREADY_STARTED    InterruptHandler is not NULL, and a handler
> +                                 for InterruptType was previously installed.
> +  @retval EFI_INVALID_PARAMETER  InterruptHandler is NULL, and a handler for
> +                                 InterruptType was not previously installed.
> +  @retval EFI_UNSUPPORTED        The interrupt specified by InterruptType
> +                                 is not supported.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuRegisterInterruptHandler (
> +  IN EFI_CPU_ARCH_PROTOCOL      *This,
> +  IN EFI_EXCEPTION_TYPE         InterruptType,
> +  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
> +  )
> +{
> +  return RegisterInterruptHandler (InterruptType, InterruptHandler);
> +}
> +
> +/**
> +  Returns a timer value from one of the CPU's internal timers. There is no
> +  inherent time interval between ticks but is a function of the CPU frequency.
> +
> +  @param  This                - Protocol instance structure.
> +  @param  TimerIndex          - Specifies which CPU timer is requested.
> +  @param  TimerValue          - Pointer to the returned timer value.
> +  @param  TimerPeriod         - A pointer to the amount of time that passes
> +                                in femtoseconds (10-15) for each increment
> +                                of TimerValue. If TimerValue does not
> +                                increment at a predictable rate, then 0 is
> +                                returned.  The amount of time that has
> +                                passed between two calls to GetTimerValue()
> +                                can be calculated with the formula
> +                                (TimerValue2 - TimerValue1) * TimerPeriod.
> +                                This parameter is optional and may be NULL.
> +
> +  @retval EFI_SUCCESS           - If the CPU timer count was returned.
> +  @retval EFI_UNSUPPORTED       - If the CPU does not have any readable timers.
> +  @retval EFI_DEVICE_ERROR      - If an error occurred while reading the timer.
> +  @retval EFI_INVALID_PARAMETER - TimerIndex is not valid or TimerValue is NULL.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuGetTimerValue (
> +  IN  EFI_CPU_ARCH_PROTOCOL  *This,
> +  IN  UINT32                 TimerIndex,
> +  OUT UINT64                 *TimerValue,
> +  OUT UINT64                 *TimerPeriod   OPTIONAL
> +  )
> +{
> +  UINT64  BeginValue;
> +  UINT64  EndValue;
> +
> +  if (TimerValue == NULL) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (TimerIndex != 0) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  *TimerValue = AsmReadStableCounter ();
> +
> +  if (TimerPeriod != NULL) {
> +    if (mTimerPeriod == 0) {
> +      //
> +      // Read time stamp counter before and after delay of 100 microseconds
> +      //
> +      BeginValue = AsmReadStableCounter ();
> +      MicroSecondDelay (100);
> +      EndValue = AsmReadStableCounter ();
> +      //
> +      // Calculate the actual frequency
> +      //
> +      mTimerPeriod = DivU64x64Remainder (
> +                       MultU64x32 (
> +                         1000 * 1000 * 1000,
> +                         100
> +                         ),
> +                       EndValue - BeginValue,
> +                       NULL
> +                       );
> +    }
> +
> +    *TimerPeriod = mTimerPeriod;
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  This function modifies the attributes for the memory region specified by BaseAddress and
> +  Length from their current attributes to the attributes specified by Attributes.
> +
> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
> +  @param  BaseAddress      The physical address that is the start address of a memory region.
> +  @param  Length           The size in bytes of the memory region.
> +  @param  EfiAttributes    The bit mask of attributes to set for the memory region.
> +
> +  @retval EFI_SUCCESS           The attributes were set for the memory region.
> +  @retval EFI_ACCESS_DENIED     The attributes for the memory resource range specified by
> +                                BaseAddress and Length cannot be modified.
> +  @retval EFI_INVALID_PARAMETER Length is zero.
> +  @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of
> +                                the memory resource range.
> +  @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory
> +                                resource range specified by BaseAddress and Length.
> +                                The bit mask of attributes is not support for the memory resource
> +                                range specified by BaseAddress and Length.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuSetMemoryAttributes (
> +  IN EFI_CPU_ARCH_PROTOCOL  *This,
> +  IN EFI_PHYSICAL_ADDRESS   BaseAddress,
> +  IN UINT64                 Length,
> +  IN UINT64                 EfiAttributes
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINTN       LoongArchAttributes;
> +  UINTN       RegionBaseAddress;
> +  UINTN       RegionLength;
> +  UINTN       RegionLoongArchAttributes;
> +
> +  RegionLength = Length;
> +  Status       = EFI_SUCCESS;
> +
> +  if ((BaseAddress & (EFI_PAGE_SIZE - 1)) != 0) {
> +    //
> +    // Minimum granularity is SIZE_4KB.
> +    //
> +    DEBUG ((
> +      DEBUG_INFO,
> +      "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum granularity is SIZE_4KB\n",
> +      BaseAddress,
> +      Length,
> +      EfiAttributes
> +      ));
> +
> +    Status = EFI_UNSUPPORTED;
> +
> +    return Status;
> +  }
> +
> +  //
> +  // Convert the 'Attribute' into LoongArch Attribute
> +  //
> +  LoongArchAttributes = EfiAttributeConverse (EfiAttributes);
> +
> +  //
> +  // Get the region starting from 'BaseAddress' and its 'Attribute'
> +  //
> +  RegionBaseAddress = BaseAddress;
> +  Status            = GetMemoryRegionAttributes (
> +                        RegionBaseAddress,
> +                        &RegionLength,
> +                        &RegionLoongArchAttributes
> +                        );
> +
> +  //
> +  // Data & Instruction Caches are flushed when we set new memory attributes.
> +  // So, we only set the attributes if the new region is different.
> +  //
> +  if ((Status == EFI_NOT_FOUND) || (RegionLoongArchAttributes != LoongArchAttributes) ||
> +      ((BaseAddress + Length) > (RegionBaseAddress + RegionLength)))
> +  {
> +    Status = SetMemoryRegionAttributes (BaseAddress, Length, EfiAttributes, 0x0);
> +  }
> +
> +  ASSERT_EFI_ERROR (Status);
> +
> +  return Status;
> +}
> +
> +/**
> +  Callback function for idle events.
> +
> +  @param  Event                 Event whose notification function is being invoked.
> +  @param  Context               The pointer to the notification function's context,
> +                                which is implementation-dependent.
> +
> +**/
> +VOID
> +EFIAPI
> +IdleLoopEventCallback (
> +  IN EFI_EVENT  Event,
> +  IN VOID       *Context
> +  )
> +{
> +  CpuSleep ();
> +}
> +
> +/**
> +  Initialize the state information for the CPU Architectural Protocol.
> +
> +  @param ImageHandle     Image handle this driver.
> +  @param SystemTable     Pointer to the System Table.
> +
> +  @retval EFI_SUCCESS           Thread can be successfully created
> +  @retval EFI_OUT_OF_RESOURCES  Cannot allocate protocol data structure
> +  @retval EFI_DEVICE_ERROR      Cannot create the thread
> +
> +**/
> +EFI_STATUS
> +InitializeCpu (
> +  IN EFI_HANDLE        ImageHandle,
> +  IN EFI_SYSTEM_TABLE  *SystemTable
> +  )
> +{
> +  EFI_STATUS  Status;
> +  EFI_EVENT   IdleLoopEvent;
> +
> +  InitializeExceptions (&gCpu);
> +
> +  Status = gBS->InstallMultipleProtocolInterfaces (
> +                  &mCpuHandle,
> +                  &gEfiCpuArchProtocolGuid,
> +                  &gCpu,
> +                  NULL
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  Status = gCpu.RegisterInterruptHandler (
> +                  &gCpu,
> +                  EXCEPT_LOONGARCH_INT_IPI,
> +                  IpiInterruptHandler
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  //
> +  // Setup a callback for idle events
> +  //
> +  Status = gBS->CreateEventEx (
> +                  EVT_NOTIFY_SIGNAL,
> +                  TPL_NOTIFY,
> +                  IdleLoopEventCallback,
> +                  NULL,
> +                  &gIdleLoopEventGuid,
> +                  &IdleLoopEvent
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  InitializeMpSupport ();
> +
> +  return Status;
> +}
> diff --git a/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.h b/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.h
> new file mode 100644
> index 0000000000..8bfbfa3442
> --- /dev/null
> +++ b/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.h
> @@ -0,0 +1,288 @@
> +/** @file CpuDxe.c
> +
> +  CPU DXE Module to produce CPU ARCH Protocol.
> +
> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#ifndef CPU_DXE_H_
> +#define CPU_DXE_H_
> +
> +#include <Uefi.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/CacheMaintenanceLib.h>
> +#include <Library/CpuLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/DxeServicesTableLib.h>
> +#include <Library/MpInitLib.h>
> +#include <Library/PcdLib.h>
> +#include <Library/PeCoffGetEntryPointLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/UefiLib.h>
> +
> +#include <Guid/DebugImageInfoTable.h>
> +#include <Protocol/Cpu.h>
> +#include <Protocol/DebugSupport.h>
> +#include <Protocol/LoadedImage.h>
> +
> +//
> +// For coding convenience, define the maximum valid
> +// LoongArch exception.
> +// Since UEFI V2.11, it will be present in DebugSupport.h.
> +//
> +#define MAX_LOONGARCH_EXCEPTION  64
> +
> +/*
> +  This function flushes the range of addresses from Start to Start+Length
> +  from the processor's data cache. If Start is not aligned to a cache line
> +  boundary, then the bytes before Start to the preceding cache line boundary
> +  are also flushed. If Start+Length is not aligned to a cache line boundary,
> +  then the bytes past Start+Length to the end of the next cache line boundary
> +  are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be
> +  supported. If the data cache is fully coherent with all DMA operations, then
> +  this function can just return EFI_SUCCESS. If the processor does not support
> +  flushing a range of the data cache, then the entire data cache can be flushed.
> +
> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
> +  @param  Start            The beginning physical address to flush from the processor's data
> +                           cache.
> +  @param  Length           The number of bytes to flush from the processor's data cache. This
> +                           function may flush more bytes than Length specifies depending upon
> +                           the granularity of the flush operation that the processor supports.
> +  @param  FlushType        Specifies the type of flush operation to perform.
> +
> +  @retval EFI_SUCCESS           The address range from Start to Start+Length was flushed from
> +                                the processor's data cache.
> +  @retval EFI_UNSUPPORTEDT      The processor does not support the cache flush type specified
> +                                by FlushType.
> +  @retval EFI_DEVICE_ERROR      The address range from Start to Start+Length could not be flushed
> +                                from the processor's data cache.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuFlushCpuDataCache (
> +  IN EFI_CPU_ARCH_PROTOCOL  *This,
> +  IN EFI_PHYSICAL_ADDRESS   Start,
> +  IN UINT64                 Length,
> +  IN EFI_CPU_FLUSH_TYPE     FlushType
> +  );
> +
> +/**
> +  This function enables interrupt processing by the processor.
> +
> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
> +
> +  @retval EFI_SUCCESS           Interrupts are enabled on the processor.
> +  @retval EFI_DEVICE_ERROR      Interrupts could not be enabled on the processor.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuEnableInterrupt (
> +  IN EFI_CPU_ARCH_PROTOCOL  *This
> +  );
> +
> +/**
> +  This function disables interrupt processing by the processor.
> +
> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
> +
> +  @retval EFI_SUCCESS           Interrupts are disabled on the processor.
> +  @retval EFI_DEVICE_ERROR      Interrupts could not be disabled on the processor.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuDisableInterrupt (
> +  IN EFI_CPU_ARCH_PROTOCOL  *This
> +  );
> +
> +/**
> +  This function retrieves the processor's current interrupt state a returns it in
> +  State. If interrupts are currently enabled, then TRUE is returned. If interrupts
> +  are currently disabled, then FALSE is returned.
> +
> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
> +  @param  State            A pointer to the processor's current interrupt state. Set to TRUE if
> +                           interrupts are enabled and FALSE if interrupts are disabled.
> +
> +  @retval EFI_SUCCESS           The processor's current interrupt state was returned in State.
> +  @retval EFI_INVALID_PARAMETER State is NULL.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuGetInterruptState (
> +  IN  EFI_CPU_ARCH_PROTOCOL  *This,
> +  OUT BOOLEAN                *State
> +  );
> +
> +/**
> +  This function generates an INIT on the processor. If this function succeeds, then the
> +  processor will be reset, and control will not be returned to the caller. If InitType is
> +  not supported by this processor, or the processor cannot programmatically generate an
> +  INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error
> +  occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned.
> +
> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
> +  @param  InitType         The type of processor INIT to perform.
> +
> +  @retval EFI_SUCCESS           The processor INIT was performed. This return code should never be seen.
> +  @retval EFI_UNSUPPORTED       The processor INIT operation specified by InitType is not supported
> +                                by this processor.
> +  @retval EFI_DEVICE_ERROR      The processor INIT failed.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuInit (
> +  IN EFI_CPU_ARCH_PROTOCOL  *This,
> +  IN EFI_CPU_INIT_TYPE      InitType
> +  );
> +
> +/**
> +  Registers a function to be called from the CPU interrupt handler.
> +
> +  @param  This                   Protocol instance structure
> +  @param  InterruptType          Defines which interrupt to hook. IA-32
> +                                 valid range is 0x00 through 0xFF
> +  @param  InterruptHandler       A pointer to a function of type
> +                                 EFI_CPU_INTERRUPT_HANDLER that is called
> +                                 when a processor interrupt occurs.  A null
> +                                 pointer is an error condition.
> +
> +  @retval EFI_SUCCESS            If handler installed or uninstalled.
> +  @retval EFI_ALREADY_STARTED    InterruptHandler is not NULL, and a handler
> +                                 for InterruptType was previously installed.
> +  @retval EFI_INVALID_PARAMETER  InterruptHandler is NULL, and a handler for
> +                                 InterruptType was not previously installed.
> +  @retval EFI_UNSUPPORTED        The interrupt specified by InterruptType
> +                                 is not supported.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuRegisterInterruptHandler (
> +  IN EFI_CPU_ARCH_PROTOCOL      *This,
> +  IN EFI_EXCEPTION_TYPE         InterruptType,
> +  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
> +  );
> +
> +/**
> +  Returns a timer value from one of the CPU's internal timers. There is no
> +  inherent time interval between ticks but is a function of the CPU frequency.
> +
> +  @param  This                - Protocol instance structure.
> +  @param  TimerIndex          - Specifies which CPU timer is requested.
> +  @param  TimerValue          - Pointer to the returned timer value.
> +  @param  TimerPeriod         - A pointer to the amount of time that passes
> +                                in femtoseconds (10-15) for each increment
> +                                of TimerValue. If TimerValue does not
> +                                increment at a predictable rate, then 0 is
> +                                returned.  The amount of time that has
> +                                passed between two calls to GetTimerValue()
> +                                can be calculated with the formula
> +                                (TimerValue2 - TimerValue1) * TimerPeriod.
> +                                This parameter is optional and may be NULL.
> +
> +  @retval EFI_SUCCESS           - If the CPU timer count was returned.
> +  @retval EFI_UNSUPPORTED       - If the CPU does not have any readable timers.
> +  @retval EFI_DEVICE_ERROR      - If an error occurred while reading the timer.
> +  @retval EFI_INVALID_PARAMETER - TimerIndex is not valid or TimerValue is NULL.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuGetTimerValue (
> +  IN  EFI_CPU_ARCH_PROTOCOL  *This,
> +  IN  UINT32                 TimerIndex,
> +  OUT UINT64                 *TimerValue,
> +  OUT UINT64                 *TimerPeriod   OPTIONAL
> +  );
> +
> +/**
> +  This function registers and enables the handler specified by InterruptHandler for a processor
> +  interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
> +  handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
> +  The installed handler is called once for each processor interrupt or exception.
> +
> +  @param  InterruptType    A pointer to the processor's current interrupt state. Set to TRUE if interrupts
> +                           are enabled and FALSE if interrupts are disabled.
> +  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
> +                           when a processor interrupt occurs. If this parameter is NULL, then the handler
> +                           will be uninstalled.
> +
> +  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
> +  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was
> +                                previously installed.
> +  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
> +                                previously installed.
> +  @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
> +
> +**/
> +EFI_STATUS
> +RegisterInterruptHandler (
> +  IN EFI_EXCEPTION_TYPE         InterruptType,
> +  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
> +  );
> +
> +/**
> +  This function modifies the attributes for the memory region specified by BaseAddress and
> +  Length from their current attributes to the attributes specified by Attributes.
> +
> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
> +  @param  BaseAddress      The physical address that is the start address of a memory region.
> +  @param  Length           The size in bytes of the memory region.
> +  @param  Attributes       The bit mask of attributes to set for the memory region.
> +
> +  @retval EFI_SUCCESS           The attributes were set for the memory region.
> +  @retval EFI_ACCESS_DENIED     The attributes for the memory resource range specified by
> +                                BaseAddress and Length cannot be modified.
> +  @retval EFI_INVALID_PARAMETER Length is zero.
> +  @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of
> +                                the memory resource range.
> +  @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory
> +                                resource range specified by BaseAddress and Length.
> +                                The bit mask of attributes is not support for the memory resource
> +                                range specified by BaseAddress and Length.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +CpuSetMemoryAttributes (
> +  IN EFI_CPU_ARCH_PROTOCOL  *This,
> +  IN EFI_PHYSICAL_ADDRESS   BaseAddress,
> +  IN UINT64                 Length,
> +  IN UINT64                 Attributes
> +  );
> +
> +/**
> +  Initialize interrupt handling for DXE phase.
> +
> +  @param  Cpu A pointer of EFI_CPU_ARCH_PROTOCOL instance.
> +
> +  @return VOID.
> +
> +**/
> +VOID
> +InitializeExceptions (
> +  IN EFI_CPU_ARCH_PROTOCOL  *gCpu
> +  );
> +
> +/**
> +  Converts EFI Attributes to corresponding architecture Attributes.
> +
> +  @param[in]  EfiAttributes     Efi Attributes.
> +
> +  @retval  Corresponding architecture attributes.
> +**/
> +UINTN
> +EFIAPI
> +EfiAttributeConverse (
> +  IN UINTN  EfiAttributes
> +  );
> +
> +#endif // CPU_DXE_H_
> diff --git a/UefiCpuPkg/CpuDxe/LoongArch64/CpuMp.c b/UefiCpuPkg/CpuDxe/LoongArch64/CpuMp.c
> new file mode 100644
> index 0000000000..3325914e53
> --- /dev/null
> +++ b/UefiCpuPkg/CpuDxe/LoongArch64/CpuMp.c
> @@ -0,0 +1,544 @@
> +/** @file
> +  CPU DXE Module to produce CPU MP Protocol.
> +
> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#include "CpuDxe.h"
> +#include "CpuMp.h"
> +
> +EFI_HANDLE  mMpServiceHandle    = NULL;
> +UINTN       mNumberOfProcessors = 1;
> +
> +EFI_MP_SERVICES_PROTOCOL  mMpServicesTemplate = {
> +  GetNumberOfProcessors,
> +  GetProcessorInfo,
> +  StartupAllAPs,
> +  StartupThisAP,
> +  SwitchBSP,
> +  EnableDisableAP,
> +  WhoAmI
> +};
> +
> +/**
> +  This service retrieves the number of logical processor in the platform
> +  and the number of those logical processors that are enabled on this boot.
> +  This service may only be called from the BSP.
> +
> +  This function is used to retrieve the following information:
> +    - The number of logical processors that are present in the system.
> +    - The number of enabled logical processors in the system at the instant
> +      this call is made.
> +
> +  Because MP Service Protocol provides services to enable and disable processors
> +  dynamically, the number of enabled logical processors may vary during the
> +  course of a boot session.
> +
> +  If this service is called from an AP, then EFI_DEVICE_ERROR is returned.
> +  If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then
> +  EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors
> +  is returned in NumberOfProcessors, the number of currently enabled processor
> +  is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned.
> +
> +  @param[in]  This                        A pointer to the EFI_MP_SERVICES_PROTOCOL
> +                                          instance.
> +  @param[out] NumberOfProcessors          Pointer to the total number of logical
> +                                          processors in the system, including the BSP
> +                                          and disabled APs.
> +  @param[out] NumberOfEnabledProcessors   Pointer to the number of enabled logical
> +                                          processors that exist in system, including
> +                                          the BSP.
> +
> +  @retval EFI_SUCCESS             The number of logical processors and enabled
> +                                  logical processors was retrieved.
> +  @retval EFI_DEVICE_ERROR        The calling processor is an AP.
> +  @retval EFI_INVALID_PARAMETER   NumberOfProcessors is NULL.
> +  @retval EFI_INVALID_PARAMETER   NumberOfEnabledProcessors is NULL.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetNumberOfProcessors (
> +  IN  EFI_MP_SERVICES_PROTOCOL  *This,
> +  OUT UINTN                     *NumberOfProcessors,
> +  OUT UINTN                     *NumberOfEnabledProcessors
> +  )
> +{
> +  if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  return MpInitLibGetNumberOfProcessors (
> +           NumberOfProcessors,
> +           NumberOfEnabledProcessors
> +           );
> +}
> +
> +/**
> +  Gets detailed MP-related information on the requested processor at the
> +  instant this call is made. This service may only be called from the BSP.
> +
> +  This service retrieves detailed MP-related information about any processor
> +  on the platform. Note the following:
> +    - The processor information may change during the course of a boot session.
> +    - The information presented here is entirely MP related.
> +
> +  Information regarding the number of caches and their sizes, frequency of operation,
> +  slot numbers is all considered platform-related information and is not provided
> +  by this service.
> +
> +  @param[in]  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL
> +                                    instance.
> +  @param[in]  ProcessorNumber       The handle number of processor.
> +  @param[out] ProcessorInfoBuffer   A pointer to the buffer where information for
> +                                    the requested processor is deposited.
> +
> +  @retval EFI_SUCCESS             Processor information was returned.
> +  @retval EFI_DEVICE_ERROR        The calling processor is an AP.
> +  @retval EFI_INVALID_PARAMETER   ProcessorInfoBuffer is NULL.
> +  @retval EFI_NOT_FOUND           The processor with the handle specified by
> +                                  ProcessorNumber does not exist in the platform.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetProcessorInfo (
> +  IN  EFI_MP_SERVICES_PROTOCOL   *This,
> +  IN  UINTN                      ProcessorNumber,
> +  OUT EFI_PROCESSOR_INFORMATION  *ProcessorInfoBuffer
> +  )
> +{
> +  return MpInitLibGetProcessorInfo (ProcessorNumber, ProcessorInfoBuffer, NULL);
> +}
> +
> +/**
> +  This service executes a caller provided function on all enabled APs. APs can
> +  run either simultaneously or one at a time in sequence. This service supports
> +  both blocking and non-blocking requests. The non-blocking requests use EFI
> +  events so the BSP can detect when the APs have finished. This service may only
> +  be called from the BSP.
> +
> +  This function is used to dispatch all the enabled APs to the function specified
> +  by Procedure.  If any enabled AP is busy, then EFI_NOT_READY is returned
> +  immediately and Procedure is not started on any AP.
> +
> +  If SingleThread is TRUE, all the enabled APs execute the function specified by
> +  Procedure one by one, in ascending order of processor handle number. Otherwise,
> +  all the enabled APs execute the function specified by Procedure simultaneously.
> +
> +  If WaitEvent is NULL, execution is in blocking mode. The BSP waits until all
> +  APs finish or TimeoutInMicroseconds expires. Otherwise, execution is in non-blocking
> +  mode, and the BSP returns from this service without waiting for APs. If a
> +  non-blocking mode is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
> +  is signaled, then EFI_UNSUPPORTED must be returned.
> +
> +  If the timeout specified by TimeoutInMicroseconds expires before all APs return
> +  from Procedure, then Procedure on the failed APs is terminated. All enabled APs
> +  are always available for further calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
> +  and EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). If FailedCpuList is not NULL, its
> +  content points to the list of processor handle numbers in which Procedure was
> +  terminated.
> +
> +  Note: It is the responsibility of the consumer of the EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
> +  to make sure that the nature of the code that is executed on the BSP and the
> +  dispatched APs is well controlled. The MP Services Protocol does not guarantee
> +  that the Procedure function is MP-safe. Hence, the tasks that can be run in
> +  parallel are limited to certain independent tasks and well-controlled exclusive
> +  code. EFI services and protocols may not be called by APs unless otherwise
> +  specified.
> +
> +  In blocking execution mode, BSP waits until all APs finish or
> +  TimeoutInMicroseconds expires.
> +
> +  In non-blocking execution mode, BSP is freed to return to the caller and then
> +  proceed to the next task without having to wait for APs. The following
> +  sequence needs to occur in a non-blocking execution mode:
> +
> +    -# The caller that intends to use this MP Services Protocol in non-blocking
> +       mode creates WaitEvent by calling the EFI CreateEvent() service.  The caller
> +       invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the parameter WaitEvent
> +       is not NULL, then StartupAllAPs() executes in non-blocking mode. It requests
> +       the function specified by Procedure to be started on all the enabled APs,
> +       and releases the BSP to continue with other tasks.
> +    -# The caller can use the CheckEvent() and WaitForEvent() services to check
> +       the state of the WaitEvent created in step 1.
> +    -# When the APs complete their task or TimeoutInMicroSecondss expires, the MP
> +       Service signals WaitEvent by calling the EFI SignalEvent() function. If
> +       FailedCpuList is not NULL, its content is available when WaitEvent is
> +       signaled. If all APs returned from Procedure prior to the timeout, then
> +       FailedCpuList is set to NULL. If not all APs return from Procedure before
> +       the timeout, then FailedCpuList is filled in with the list of the failed
> +       APs. The buffer is allocated by MP Service Protocol using AllocatePool().
> +       It is the caller's responsibility to free the buffer with FreePool() service.
> +    -# This invocation of SignalEvent() function informs the caller that invoked
> +       EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() that either all the APs completed
> +       the specified task or a timeout occurred. The contents of FailedCpuList
> +       can be examined to determine which APs did not complete the specified task
> +       prior to the timeout.
> +
> +  @param[in]  This                    A pointer to the EFI_MP_SERVICES_PROTOCOL
> +                                      instance.
> +  @param[in]  Procedure               A pointer to the function to be run on
> +                                      enabled APs of the system. See type
> +                                      EFI_AP_PROCEDURE.
> +  @param[in]  SingleThread            If TRUE, then all the enabled APs execute
> +                                      the function specified by Procedure one by
> +                                      one, in ascending order of processor handle
> +                                      number.  If FALSE, then all the enabled APs
> +                                      execute the function specified by Procedure
> +                                      simultaneously.
> +  @param[in]  WaitEvent               The event created by the caller with CreateEvent()
> +                                      service.  If it is NULL, then execute in
> +                                      blocking mode. BSP waits until all APs finish
> +                                      or TimeoutInMicroseconds expires.  If it's
> +                                      not NULL, then execute in non-blocking mode.
> +                                      BSP requests the function specified by
> +                                      Procedure to be started on all the enabled
> +                                      APs, and go on executing immediately. If
> +                                      all return from Procedure, or TimeoutInMicroseconds
> +                                      expires, this event is signaled. The BSP
> +                                      can use the CheckEvent() or WaitForEvent()
> +                                      services to check the state of event.  Type
> +                                      EFI_EVENT is defined in CreateEvent() in
> +                                      the Unified Extensible Firmware Interface
> +                                      Specification.
> +  @param[in]  TimeoutInMicroseconds   Indicates the time limit in microseconds for
> +                                      APs to return from Procedure, either for
> +                                      blocking or non-blocking mode. Zero means
> +                                      infinity.  If the timeout expires before
> +                                      all APs return from Procedure, then Procedure
> +                                      on the failed APs is terminated. All enabled
> +                                      APs are available for next function assigned
> +                                      by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
> +                                      or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
> +                                      If the timeout expires in blocking mode,
> +                                      BSP returns EFI_TIMEOUT.  If the timeout
> +                                      expires in non-blocking mode, WaitEvent
> +                                      is signaled with SignalEvent().
> +  @param[in]  ProcedureArgument       The parameter passed into Procedure for
> +                                      all APs.
> +  @param[out] FailedCpuList           If NULL, this parameter is ignored. Otherwise,
> +                                      if all APs finish successfully, then its
> +                                      content is set to NULL. If not all APs
> +                                      finish before timeout expires, then its
> +                                      content is set to address of the buffer
> +                                      holding handle numbers of the failed APs.
> +                                      The buffer is allocated by MP Service Protocol,
> +                                      and it's the caller's responsibility to
> +                                      free the buffer with FreePool() service.
> +                                      In blocking mode, it is ready for consumption
> +                                      when the call returns. In non-blocking mode,
> +                                      it is ready when WaitEvent is signaled.  The
> +                                      list of failed CPU is terminated by
> +                                      END_OF_CPU_LIST.
> +
> +  @retval EFI_SUCCESS             In blocking mode, all APs have finished before
> +                                  the timeout expired.
> +  @retval EFI_SUCCESS             In non-blocking mode, function has been dispatched
> +                                  to all enabled APs.
> +  @retval EFI_UNSUPPORTED         A non-blocking mode request was made after the
> +                                  UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
> +                                  signaled.
> +  @retval EFI_DEVICE_ERROR        Caller processor is AP.
> +  @retval EFI_NOT_STARTED         No enabled APs exist in the system.
> +  @retval EFI_NOT_READY           Any enabled APs are busy.
> +  @retval EFI_TIMEOUT             In blocking mode, the timeout expired before
> +                                  all enabled APs have finished.
> +  @retval EFI_INVALID_PARAMETER   Procedure is NULL.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +StartupAllAPs (
> +  IN  EFI_MP_SERVICES_PROTOCOL  *This,
> +  IN  EFI_AP_PROCEDURE          Procedure,
> +  IN  BOOLEAN                   SingleThread,
> +  IN  EFI_EVENT                 WaitEvent               OPTIONAL,
> +  IN  UINTN                     TimeoutInMicroseconds,
> +  IN  VOID                      *ProcedureArgument      OPTIONAL,
> +  OUT UINTN                     **FailedCpuList         OPTIONAL
> +  )
> +{
> +  return MpInitLibStartupAllAPs (
> +           Procedure,
> +           SingleThread,
> +           WaitEvent,
> +           TimeoutInMicroseconds,
> +           ProcedureArgument,
> +           FailedCpuList
> +           );
> +}
> +
> +/**
> +  This service lets the caller get one enabled AP to execute a caller-provided
> +  function. The caller can request the BSP to either wait for the completion
> +  of the AP or just proceed with the next task by using the EFI event mechanism.
> +  See EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() for more details on non-blocking
> +  execution support.  This service may only be called from the BSP.
> +
> +  This function is used to dispatch one enabled AP to the function specified by
> +  Procedure passing in the argument specified by ProcedureArgument.  If WaitEvent
> +  is NULL, execution is in blocking mode. The BSP waits until the AP finishes or
> +  TimeoutInMicroSecondss expires. Otherwise, execution is in non-blocking mode.
> +  BSP proceeds to the next task without waiting for the AP. If a non-blocking mode
> +  is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled,
> +  then EFI_UNSUPPORTED must be returned.
> +
> +  If the timeout specified by TimeoutInMicroseconds expires before the AP returns
> +  from Procedure, then execution of Procedure by the AP is terminated. The AP is
> +  available for subsequent calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and
> +  EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
> +
> +  @param[in]  This                    A pointer to the EFI_MP_SERVICES_PROTOCOL
> +                                      instance.
> +  @param[in]  Procedure               A pointer to the function to be run on 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
> +                                      EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
> +  @param[in]  WaitEvent               The event created by the caller with CreateEvent()
> +                                      service.  If it is NULL, then execute in
> +                                      blocking mode. BSP waits until 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 EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
> +                                      or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
> +                                      If the timeout expires in blocking mode,
> +                                      BSP returns EFI_TIMEOUT.  If the timeout
> +                                      expires in non-blocking mode, WaitEvent
> +                                      is signaled with SignalEvent().
> +  @param[in]  ProcedureArgument       The parameter passed into Procedure 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_DEVICE_ERROR        The calling processor is an AP.
> +  @retval EFI_TIMEOUT             In blocking mode, the timeout expired before
> +                                  the specified AP has finished.
> +  @retval EFI_NOT_READY           The specified AP is busy.
> +  @retval EFI_NOT_FOUND           The processor with the handle specified by
> +                                  ProcessorNumber does not exist.
> +  @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the BSP or disabled AP.
> +  @retval EFI_INVALID_PARAMETER   Procedure is NULL.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +StartupThisAP (
> +  IN  EFI_MP_SERVICES_PROTOCOL  *This,
> +  IN  EFI_AP_PROCEDURE          Procedure,
> +  IN  UINTN                     ProcessorNumber,
> +  IN  EFI_EVENT                 WaitEvent               OPTIONAL,
> +  IN  UINTN                     TimeoutInMicroseconds,
> +  IN  VOID                      *ProcedureArgument      OPTIONAL,
> +  OUT BOOLEAN                   *Finished               OPTIONAL
> +  )
> +{
> +  return MpInitLibStartupThisAP (
> +           Procedure,
> +           ProcessorNumber,
> +           WaitEvent,
> +           TimeoutInMicroseconds,
> +           ProcedureArgument,
> +           Finished
> +           );
> +}
> +
> +/**
> +  This service switches the requested AP to be the BSP from that point onward.
> +  This service changes the BSP for all purposes.   This call can only be performed
> +  by the current BSP.
> +
> +  This service switches the requested AP to be the BSP from that point onward.
> +  This service changes the BSP for all purposes. The new BSP can take over the
> +  execution of the old BSP and continue seamlessly from where the old one left
> +  off. This service may not be supported after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
> +  is signaled.
> +
> +  If the BSP cannot be switched prior to the return from this service, then
> +  EFI_UNSUPPORTED must be returned.
> +
> +  @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
> +  @param[in] ProcessorNumber   The handle number of AP that is to become the new
> +                               BSP. The range is from 0 to the total number of
> +                               logical processors minus 1. The total number of
> +                               logical processors can be retrieved by
> +                               EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
> +  @param[in] EnableOldBSP      If TRUE, then the old BSP will be listed as an
> +                               enabled AP. Otherwise, it will be disabled.
> +
> +  @retval EFI_SUCCESS             BSP successfully switched.
> +  @retval EFI_UNSUPPORTED         Switching the BSP cannot be completed prior to
> +                                  this service returning.
> +  @retval EFI_UNSUPPORTED         Switching the BSP is not supported.
> +  @retval EFI_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.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +SwitchBSP (
> +  IN EFI_MP_SERVICES_PROTOCOL  *This,
> +  IN  UINTN                    ProcessorNumber,
> +  IN  BOOLEAN                  EnableOldBSP
> +  )
> +{
> +  return MpInitLibSwitchBSP (ProcessorNumber, EnableOldBSP);
> +}
> +
> +/**
> +  This service lets the caller enable or disable an AP from this point onward.
> +  This service may only be called from the BSP.
> +
> +  This service allows the caller enable or disable an AP from this point onward.
> +  The caller can optionally specify the health status of the AP by Health. If
> +  an AP is being disabled, then the state of the disabled AP is implementation
> +  dependent. If an AP is enabled, then the implementation must guarantee that a
> +  complete initialization sequence is performed on the AP, so the AP is in a state
> +  that is compatible with an MP operating system. This service may not be supported
> +  after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled.
> +
> +  If the enable or disable AP operation cannot be completed prior to the return
> +  from this service, then EFI_UNSUPPORTED must be returned.
> +
> +  @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
> +  @param[in] ProcessorNumber   The handle number of AP.
> +                               The range is from 0 to the total number of
> +                               logical processors minus 1. The total number of
> +                               logical processors can be retrieved by
> +                               EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
> +  @param[in] EnableAP          Specifies the new state for the processor for
> +                               enabled, FALSE for disabled.
> +  @param[in] HealthFlag        If not NULL, a pointer to a value that specifies
> +                               the new health status of the AP. This flag
> +                               corresponds to StatusFlag defined in
> +                               EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only
> +                               the PROCESSOR_HEALTH_STATUS_BIT is used. All other
> +                               bits are ignored.  If it is NULL, this parameter
> +                               is ignored.
> +
> +  @retval EFI_SUCCESS             The specified AP was enabled or disabled successfully.
> +  @retval EFI_UNSUPPORTED         Enabling or disabling an AP cannot be completed
> +                                  prior to this service returning.
> +  @retval EFI_UNSUPPORTED         Enabling or disabling an AP is not supported.
> +  @retval EFI_DEVICE_ERROR        The calling processor is an AP.
> +  @retval EFI_NOT_FOUND           Processor with the handle specified by ProcessorNumber
> +                                  does not exist.
> +  @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the BSP.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +EnableDisableAP (
> +  IN  EFI_MP_SERVICES_PROTOCOL  *This,
> +  IN  UINTN                     ProcessorNumber,
> +  IN  BOOLEAN                   EnableAP,
> +  IN  UINT32                    *HealthFlag OPTIONAL
> +  )
> +{
> +  return MpInitLibEnableDisableAP (ProcessorNumber, EnableAP, HealthFlag);
> +}
> +
> +/**
> +  This return the handle number for the calling processor.  This service may be
> +  called from the BSP and APs.
> +
> +  This service returns the processor handle number for the calling processor.
> +  The returned value is in the range from 0 to the total number of logical
> +  processors minus 1. The total number of logical processors can be retrieved
> +  with EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). This service may be
> +  called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER
> +  is returned. Otherwise, the current processors handle number is returned in
> +  ProcessorNumber, and EFI_SUCCESS is returned.
> +
> +  @param[in]  This             A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
> +  @param[out] ProcessorNumber  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
> +                               EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
> +
> +  @retval EFI_SUCCESS             The current processor handle number was returned
> +                                  in ProcessorNumber.
> +  @retval EFI_INVALID_PARAMETER   ProcessorNumber is NULL.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +WhoAmI (
> +  IN EFI_MP_SERVICES_PROTOCOL  *This,
> +  OUT UINTN                    *ProcessorNumber
> +  )
> +{
> +  return MpInitLibWhoAmI (ProcessorNumber);
> +}
> +
> +/**
> +  Initialize Multi-processor support.
> +**/
> +VOID
> +InitializeMpSupport (
> +  VOID
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINTN       NumberOfProcessors;
> +  UINTN       NumberOfEnabledProcessors;
> +
> +  //
> +  // Wakeup APs to do initialization
> +  //
> +  Status = MpInitLibInitialize ();
> +  ASSERT_EFI_ERROR (Status);
> +
> +  MpInitLibGetNumberOfProcessors (&NumberOfProcessors, &NumberOfEnabledProcessors);
> +  mNumberOfProcessors = NumberOfProcessors;
> +  DEBUG ((DEBUG_INFO, "Detect CPU count: %d\n", mNumberOfProcessors));
> +
> +  Status = gBS->InstallMultipleProtocolInterfaces (
> +                  &mMpServiceHandle,
> +                  &gEfiMpServiceProtocolGuid,
> +                  &mMpServicesTemplate,
> +                  NULL
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +}
> diff --git a/UefiCpuPkg/CpuDxe/LoongArch64/Exception.c b/UefiCpuPkg/CpuDxe/LoongArch64/Exception.c
> new file mode 100644
> index 0000000000..96def89936
> --- /dev/null
> +++ b/UefiCpuPkg/CpuDxe/LoongArch64/Exception.c
> @@ -0,0 +1,159 @@
> +/** @file Exception.c
> +
> +  CPU DXE Module initialization exception instance.
> +
> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#include "CpuDxe.h"
> +#include <Guid/VectorHandoffTable.h>
> +#include <Library/CpuExceptionHandlerLib.h>
> +#include <Register/LoongArch64/Csr.h>
> +
> +VOID
> +ExceptionEntryStart (
> +  VOID
> +  );
> +
> +VOID
> +ExceptionEntryEnd (
> +  VOID
> +  );
> +
> +/**
> +  This function registers and enables the handler specified by InterruptHandler for a processor
> +  interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
> +  handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
> +  The installed handler is called once for each processor interrupt or exception.
> +
> +  @param  InterruptType    A pointer to the processor's current interrupt state. Set to TRUE if interrupts
> +                           are enabled and FALSE if interrupts are disabled.
> +  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
> +                           when a processor interrupt occurs. If this parameter is NULL, then the handler
> +                           will be uninstalled.
> +
> +  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
> +  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was
> +                                previously installed.
> +  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
> +                                previously installed.
> +  @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
> +
> +**/
> +EFI_STATUS
> +RegisterInterruptHandler (
> +  IN EFI_EXCEPTION_TYPE         InterruptType,
> +  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
> +  )
> +{
> +  return (EFI_STATUS)RegisterCpuInterruptHandler (InterruptType, InterruptHandler);
> +}
> +
> +/**
> +  Update the exception start entry code.
> +
> +  @retval EFI_SUCCESS           Update the exception start entry code down.
> +  @retval EFI_OUT_OF_RESOURCES  The start entry code size out of bounds.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +UpdateExceptionStartEntry (
> +  VOID
> +  )
> +{
> +  EFI_PHYSICAL_ADDRESS  ExceptionStartEntry;
> +  UINTN                 VectorLength;
> +  UINTN                 MaxLength;
> +  UINTN                 MaxSizeOfVector;
> +
> +  VectorLength = (UINTN)ExceptionEntryEnd - (UINTN)ExceptionEntryStart;
> +
> +  //
> +  // A vector is up to 512 bytes.
> +  //
> +  MaxSizeOfVector = 512;
> +  MaxLength       = (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * MaxSizeOfVector;
> +
> +  if (VectorLength > MaxLength) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  ExceptionStartEntry = PcdGet64 (PcdCpuExceptionVectorBaseAddress);
> +
> +  InvalidateInstructionCacheRange ((VOID *)ExceptionStartEntry, VectorLength);
> +  CopyMem ((VOID *)ExceptionStartEntry, (VOID *)ExceptionEntryStart, VectorLength);
> +  InvalidateInstructionCacheRange ((VOID *)ExceptionStartEntry, VectorLength);
> +  InvalidateDataCache ();
> +
> +  //
> +  // If PcdCpuExceptionVectorBaseAddress is not used during SEC and PEI stages, the exception
> +  // base addres is set to PcdCpuExceptionVectorBaseAddress.
> +  //
> +  if (CsrRead (LOONGARCH_CSR_EBASE) != ExceptionStartEntry) {
> +    SetExceptionBaseAddress (ExceptionStartEntry);
> +  }
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Initialize interrupt handling for DXE phase.
> +
> +  @param  Cpu A pointer of EFI_CPU_ARCH_PROTOCOL instance.
> +
> +  @return VOID.
> +
> +**/
> +VOID
> +InitializeExceptions (
> +  IN EFI_CPU_ARCH_PROTOCOL  *Cpu
> +  )
> +{
> +  EFI_STATUS               Status;
> +  EFI_VECTOR_HANDOFF_INFO  *VectorInfoList;
> +  EFI_VECTOR_HANDOFF_INFO  *VectorInfo;
> +  BOOLEAN                  IrqEnabled;
> +
> +  VectorInfo = (EFI_VECTOR_HANDOFF_INFO *)NULL;
> +  Status     = EfiGetSystemConfigurationTable (&gEfiVectorHandoffTableGuid, (VOID **)&VectorInfoList);
> +
> +  if ((Status == EFI_SUCCESS) && (VectorInfoList != NULL)) {
> +    VectorInfo = VectorInfoList;
> +  }
> +
> +  //
> +  // Disable interrupts
> +  //
> +  Cpu->GetInterruptState (Cpu, &IrqEnabled);
> +  if (IrqEnabled) {
> +    Cpu->DisableInterrupt (Cpu);
> +  }
> +
> +  //
> +  // Update the Exception Start Entry code to point into CpuDxe.
> +  //
> +  Status = UpdateExceptionStartEntry ();
> +  if (EFI_ERROR (Status)) {
> +    DebugPrint (EFI_D_ERROR, "[%a]: Exception start entry code out of bounds!\n", __func__);
> +    ASSERT_EFI_ERROR (Status);
> +  }
> +
> +  //
> +  // Intialize the CpuExceptionHandlerLib so we take over the exception vector table from the DXE Core
> +  //
> +  Status = InitializeCpuExceptionHandlers (VectorInfo);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  //
> +  // Enable interrupts
> +  //
> +  DebugPrint (EFI_D_INFO, "InitializeExceptions,IrqEnabled = %x\n", IrqEnabled);
> +  if (!IrqEnabled) {
> +    Status = Cpu->EnableInterrupt (Cpu);
> +  }
> +
> +  ASSERT_EFI_ERROR (Status);
> +}


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114852): https://edk2.groups.io/g/devel/message/114852
Mute This Topic: https://groups.io/mt/104068958/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 11/37] UefiCpuPkg: Add LoongArch64 CPU Timer instance
       [not found]   ` <17AF510405DE784C.15701@groups.io>
@ 2024-01-31  5:28     ` Chao Li
  2024-01-31 10:47       ` Laszlo Ersek
  0 siblings, 1 reply; 89+ messages in thread
From: Chao Li @ 2024-01-31  5:28 UTC (permalink / raw)
  To: devel, Ray Ni, Laszlo Ersek; +Cc: Eric Dong, Rahul Kumar, Gerd Hoffmann

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

Hi Ray and Laszlo,

I would very much like to be merged into stable202302, the soft feature 
deadline is 2024-02-05, so could you please hlep to review this patch as 
soon as passable? Please...


Thanks,
Chao
On 2024/1/31 11:30, Chao Li wrote:
>
> Hi Ray,
>
> Can you please help to review this patch again?
>
> On 2024/1/26 14:29, Chao Li wrote:
>> Add the LoongArch64 CPU Timer instance to CpuTimerLib, using CPUCFG 0x4
>> and 0x5 for Stable Counter frequency.
>>
>> BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>>
>> Cc: Eric Dong<eric.dong@intel.com>
>> Cc: Ray Ni<ray.ni@intel.com>
>> Cc: Rahul Kumar<rahul1.kumar@intel.com>
>> Cc: Gerd Hoffmann<kraxel@redhat.com>
>> Signed-off-by: Chao Li<lichao@loongson.cn>
>> ---
>>   .../Library/CpuTimerLib/BaseCpuTimerLib.inf   |   9 +-
>>   .../CpuTimerLib/LoongArch64/CpuTimerLib.c     | 251 ++++++++++++++++++
>>   2 files changed, 258 insertions(+), 2 deletions(-)
>>   create mode 100644 UefiCpuPkg/Library/CpuTimerLib/LoongArch64/CpuTimerLib.c
>>
>> diff --git a/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf b/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf
>> index de0648de91..7e6152ef7e 100644
>> --- a/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf
>> +++ b/UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf
>> @@ -5,6 +5,7 @@
>>   #  counter features are provided by the processors time stamp counter.
>>   #
>>   #  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
>> +#  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>>   #  SPDX-License-Identifier: BSD-2-Clause-Patent
>>   #
>>   ##
>> @@ -18,18 +19,22 @@
>>     LIBRARY_CLASS                  = TimerLib
>>     MODULE_UNI_FILE                = BaseCpuTimerLib.uni
>>   
>> -[Sources]
>> +[Sources.IA32, Sources.X64]
>>     CpuTimerLib.c
>>     BaseCpuTimerLib.c
>>   
>> +[Sources.LOONGARCH64]
>> +  LoongArch64/CpuTimerLib.c
>> +
>>   [Packages]
>>     MdePkg/MdePkg.dec
>>     UefiCpuPkg/UefiCpuPkg.dec
>>   
>>   [LibraryClasses]
>>     BaseLib
>> -  PcdLib
>>     DebugLib
>> +  PcdLib
>> +  SafeIntLib
>>   
>>   [Pcd]
>>     gUefiCpuPkgTokenSpaceGuid.PcdCpuCoreCrystalClockFrequency  ## CONSUMES
>> diff --git a/UefiCpuPkg/Library/CpuTimerLib/LoongArch64/CpuTimerLib.c b/UefiCpuPkg/Library/CpuTimerLib/LoongArch64/CpuTimerLib.c
>> new file mode 100644
>> index 0000000000..a5ae8d0185
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuTimerLib/LoongArch64/CpuTimerLib.c
>> @@ -0,0 +1,251 @@
>> +/** @file
>> +  CPUCFG 0x4 and 0x5 for Stable Counter frequency instance of Timer Library.
>> +
>> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +**/
>> +
>> +#include <Base.h>
>> +#include <Library/BaseLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/SafeIntLib.h>
>> +#include <Library/TimerLib.h>
>> +#include <Register/LoongArch64/Cpucfg.h>
>> +
>> +/**
>> +  Calculate clock frequency using CPUCFG 0x4 and 0x5 registers.
>> +
>> +  @param  VOID.
>> +
>> +  @return The frequency in Hz.
>> +
>> +**/
>> +STATIC
>> +UINT64
>> +CalcConstFreq (
>> +  VOID
>> +  )
>> +{
>> +  UINT32                 BaseFreq;
>> +  UINT64                 ClockMultiplier;
>> +  UINT32                 ClockDivide;
>> +  CPUCFG_REG4_INFO_DATA  CcFreq;
>> +  CPUCFG_REG5_INFO_DATA  CpucfgReg5Data;
>> +  UINT64                 StableTimerFreq;
>> +
>> +  //
>> +  // Get the the crystal frequency corresponding to the constant
>> +  // frequency timer and the clock used by the timer.
>> +  //
>> +  AsmCpucfg (CPUCFG_REG4_INFO, &CcFreq.Uint32);
>> +
>> +  //
>> +  // Get the multiplication factor and frequency division factor
>> +  // corresponding to the constant frequency timer and the clock
>> +  // used by the timer.
>> +  //
>> +  AsmCpucfg (CPUCFG_REG5_INFO, &CpucfgReg5Data.Uint32);
>> +
>> +  BaseFreq        = CcFreq.Bits.CC_FREQ;
>> +  ClockMultiplier = CpucfgReg5Data.Bits.CC_MUL & 0xFFFF;
>> +  ClockDivide     = CpucfgReg5Data.Bits.CC_DIV & 0xFFFF;
>> +
>> +  if ((BaseFreq == 0x0) || (ClockMultiplier == 0x0) || (ClockDivide == 0x0)) {
>> +    DEBUG ((
>> +      DEBUG_ERROR,
>> +      "LoongArch Stable Timer is not available in the CPU, hence this library cannot be used.\n"
>> +      ));
>> +    ASSERT (FALSE);
>> +    CpuDeadLoop ();
>> +  }
>> +
>> +  StableTimerFreq = ((ClockMultiplier * BaseFreq) / ClockDivide);
>> +
>> +  if (StableTimerFreq == 0x0) {
>> +    ASSERT (FALSE);
>> +  }
>> +
>> +  return StableTimerFreq;
>> +}
>> +
>> +/**
>> +  Stalls the CPU for at least the given number of microseconds.
>> +
>> +  Stalls the CPU for the number of microseconds specified by MicroSeconds.
>> +
>> +  @param  MicroSeconds  The minimum number of microseconds to delay.
>> +
>> +  @return MicroSeconds
>> +
>> +**/
>> +UINTN
>> +EFIAPI
>> +MicroSecondDelay (
>> +  IN UINTN  MicroSeconds
>> +  )
>> +{
>> +  UINT64         CurrentTicks, ExceptedTicks, Remaining;
>> +  RETURN_STATUS  Status;
>> +
>> +  Status = SafeUint64Mult (MicroSeconds, CalcConstFreq (), &Remaining);
>> +  ASSERT_RETURN_ERROR (Status);
>> +
>> +  ExceptedTicks  = DivU64x32 (Remaining, 1000000U);
>> +  CurrentTicks   = AsmReadStableCounter ();
>> +  ExceptedTicks += CurrentTicks;
>> +
>> +  do {
>> +    CurrentTicks = AsmReadStableCounter ();
>> +  } while (CurrentTicks < ExceptedTicks);
>> +
>> +  return MicroSeconds;
>> +}
>> +
>> +/**
>> +  Stalls the CPU for at least the given number of nanoseconds.
>> +
>> +  Stalls the CPU for the number of nanoseconds specified by NanoSeconds.
>> +
>> +  @param  NanoSeconds The minimum number of nanoseconds to delay.
>> +
>> +  @return NanoSeconds
>> +
>> +**/
>> +UINTN
>> +EFIAPI
>> +NanoSecondDelay (
>> +  IN UINTN  NanoSeconds
>> +  )
>> +{
>> +  UINTN  MicroSeconds;
>> +
>> +  // Round up to 1us Tick Number
>> +  MicroSeconds  = NanoSeconds / 1000;
>> +  MicroSeconds += ((NanoSeconds % 1000) == 0) ? 0 : 1;
>> +
>> +  MicroSecondDelay (MicroSeconds);
>> +
>> +  return NanoSeconds;
>> +}
>> +
>> +/**
>> +  Retrieves the current value of a 64-bit free running Stable Counter.
>> +
>> +  The LoongArch defines a constant frequency timer, whose main body is a
>> +  64-bit counter called StableCounter. StableCounter is set to 0 after
>> +  reset, and then increments by 1 every counting clock cycle. When the
>> +  count reaches all 1s, it automatically wraps around to 0 and continues
>> +  to increment.
>> +  The properties of the Stable Counter can be retrieved from
>> +  GetPerformanceCounterProperties().
>> +
>> +  @return The current value of the Stable Counter.
>> +
>> +**/
>> +UINT64
>> +EFIAPI
>> +GetPerformanceCounter (
>> +  VOID
>> +  )
>> +{
>> +  //
>> +  // Just return the value of Stable Counter.
>> +  //
>> +  return AsmReadStableCounter ();
>> +}
>> +
>> +/**
>> +  Retrieves the 64-bit frequency in Hz and the range of Stable Counter
>> +  values.
>> +
>> +  If StartValue is not NULL, then the value that the stbale counter starts
>> +  with immediately after is it rolls over is returned in StartValue. If
>> +  EndValue is not NULL, then the value that the stable counter end with
>> +  immediately before it rolls over is returned in EndValue. The 64-bit
>> +  frequency of the system frequency in Hz is always returned.
>> +
>> +  @param  StartValue  The value the stable counter starts with when it
>> +                      rolls over.
>> +  @param  EndValue    The value that the stable counter ends with before
>> +                      it rolls over.
>> +
>> +  @return The frequency in Hz.
>> +
>> +**/
>> +UINT64
>> +EFIAPI
>> +GetPerformanceCounterProperties (
>> +  OUT UINT64  *StartValue   OPTIONAL,
>> +  OUT UINT64  *EndValue     OPTIONAL
>> +  )
>> +{
>> +  if (StartValue != NULL) {
>> +    *StartValue = 0;
>> +  }
>> +
>> +  if (EndValue != NULL) {
>> +    *EndValue = 0xFFFFFFFFFFFFFFFFULL;
>> +  }
>> +
>> +  return CalcConstFreq ();
>> +}
>> +
>> +/**
>> +  Converts elapsed ticks of performance counter to time in nanoseconds.
>> +
>> +  This function converts the elapsed ticks of running performance counter to
>> +  time value in unit of nanoseconds.
>> +
>> +  @param  Ticks     The number of elapsed ticks of running performance counter.
>> +
>> +  @return The elapsed time in nanoseconds.
>> +
>> +**/
>> +UINT64
>> +EFIAPI
>> +GetTimeInNanoSecond (
>> +  IN UINT64  Ticks
>> +  )
>> +{
>> +  UINT64         Frequency;
>> +  UINT64         NanoSeconds;
>> +  UINT64         Remainder;
>> +  INTN           Shift;
>> +  RETURN_STATUS  Status;
>> +
>> +  Frequency = GetPerformanceCounterProperties (NULL, NULL);
>> +
>> +  //
>> +  //          Ticks
>> +  // Time = --------- x 1,000,000,000
>> +  //        Frequency
>> +  //
>> +  Status = SafeUint64Mult (
>> +             DivU64x64Remainder (Ticks, Frequency, &Remainder),
>> +             1000000000u,
>> +             &NanoSeconds
>> +             );
>> +
>> +  //
>> +  // Ensure (Remainder * 1,000,000,000) will not overflow 64-bit.
>> +  // Since 2^29 < 1,000,000,000 = 0x3B9ACA00 < 2^30, Remainder should < 2^(64-30) = 2^34,
>> +  // i.e. highest bit set in Remainder should <= 33.
>> +  //
>> +  Shift     = MAX (0, HighBitSet64 (Remainder) - 33);
>> +  Remainder = RShiftU64 (Remainder, (UINTN)Shift);
>> +  Frequency = RShiftU64 (Frequency, (UINTN)Shift);
>> +
>> +  Status = SafeUint64Add (
>> +             NanoSeconds,
>> +             DivU64x64Remainder (
>> +               MultU64x32 (Remainder, 1000000000u),
>> +               Frequency,
>> +               NULL
>> +               ),
>> +             &NanoSeconds
>> +             );
>> +  ASSERT_RETURN_ERROR (Status);
>> +
>> +  return NanoSeconds;
>> +}
> 


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114860): https://edk2.groups.io/g/devel/message/114860
Mute This Topic: https://groups.io/mt/104070166/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 12/37] UefiCpuPkg: Add CPU exception library for LoongArch
       [not found]   ` <17AF510933F4B8FA.15701@groups.io>
@ 2024-01-31  5:29     ` Chao Li
  0 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-31  5:29 UTC (permalink / raw)
  To: devel, Ray Ni, Laszlo Ersek
  Cc: Eric Dong, Rahul Kumar, Gerd Hoffmann, Baoqi Zhang

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

Hi Ray and Laszlo,

I would very much like to be merged into stable202302, the soft feature 
deadline is 2024-02-05, so could you please hlep to review this patch as 
soon as passable? Please...


Thanks,
Chao
On 2024/1/31 11:31, Chao Li wrote:
>
> Hi Ray,
>
> Can you please help to review this patch again?
>
> On 2024/1/26 14:29, Chao Li wrote:
>> Added LoongArch exception handler into CpuExceptionHandlerLib.
>>
>> Adjust the file order in INF of CpuExceptionHandlerLib with alphabetical
>> order.
>>
>> Adjust files order in CpuExceptionHandlerLib INF in alphabetical order.
>>
>> BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>>
>> Cc: Eric Dong<eric.dong@intel.com>
>> Cc: Ray Ni<ray.ni@intel.com>
>> Cc: Rahul Kumar<rahul1.kumar@intel.com>
>> Cc: Gerd Hoffmann<kraxel@redhat.com>
>> Signed-off-by: Chao Li<lichao@loongson.cn>
>> Co-authored-by: Baoqi Zhang<zhangbaoqi@loongson.cn>
>> ---
>>   .../DxeCpuExceptionHandlerLib.inf             |  34 +-
>>   .../LoongArch/DxeExceptionLib.c               | 198 ++++++++++
>>   .../LoongArch/ExceptionCommon.c               | 171 ++++++++
>>   .../LoongArch/ExceptionCommon.h               | 131 +++++++
>>   .../LoongArch64/ArchExceptionHandler.c        | 268 +++++++++++++
>>   .../LoongArch64/ExceptionHandlerAsm.S         | 366 ++++++++++++++++++
>>   .../LoongArch/SecPeiExceptionLib.c            | 102 +++++
>>   .../SecPeiCpuExceptionHandlerLib.inf          |  29 +-
>>   UefiCpuPkg/UefiCpuPkg.dec                     |   5 +
>>   9 files changed, 1283 insertions(+), 21 deletions(-)
>>   create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/DxeExceptionLib.c
>>   create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.c
>>   create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.h
>>   create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ArchExceptionHandler.c
>>   create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ExceptionHandlerAsm.S
>>   create mode 100644 UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/SecPeiExceptionLib.c
>>
>> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
>> index fdbebadab9..f5bacbe2bc 100644
>> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
>> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
>> @@ -18,25 +18,32 @@
>>   #
>>   # The following information is for reference only and not required by the build tools.
>>   #
>> -#  VALID_ARCHITECTURES           = IA32 X64
>> +#  VALID_ARCHITECTURES           = IA32 X64 LOONGARCH64
>>   #
>>   
>>   [Sources.Ia32]
>> -  Ia32/ExceptionHandlerAsm.nasm
>> -  Ia32/ExceptionTssEntryAsm.nasm
>>     Ia32/ArchExceptionHandler.c
>>     Ia32/ArchInterruptDefs.h
>> +  Ia32/ExceptionHandlerAsm.nasm
>> +  Ia32/ExceptionTssEntryAsm.nasm
>>   
>>   [Sources.X64]
>> -  X64/ExceptionHandlerAsm.nasm
>>     X64/ArchExceptionHandler.c
>>     X64/ArchInterruptDefs.h
>> +  X64/ExceptionHandlerAsm.nasm
>>   
>> -[Sources.common]
>> +[Sources.Ia32, Sources.X64]
>>     CpuExceptionCommon.h
>>     CpuExceptionCommon.c
>> -  PeiDxeSmmCpuException.c
>>     DxeException.c
>> +  PeiDxeSmmCpuException.c
>> +
>> +[Sources.LoongArch64]
>> +  LoongArch/DxeExceptionLib.c
>> +  LoongArch/ExceptionCommon.h
>> +  LoongArch/ExceptionCommon.c
>> +  LoongArch/LoongArch64/ArchExceptionHandler.c
>> +  LoongArch/LoongArch64/ExceptionHandlerAsm.S | GCC
>>   
>>   [Pcd]
>>     gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard
>> @@ -51,16 +58,19 @@
>>     MdeModulePkg/MdeModulePkg.dec
>>     UefiCpuPkg/UefiCpuPkg.dec
>>   
>> -[LibraryClasses]
>> +[LibraryClasses.common]
>>     BaseLib
>> -  SerialPortLib
>> +  CpuLib
>> +  DebugLib
>> +  MemoryAllocationLib
>> +  PeCoffGetEntryPointLib
>>     PrintLib
>> +  SerialPortLib
>>     SynchronizationLib
>> -  LocalApicLib
>> -  PeCoffGetEntryPointLib
>> -  MemoryAllocationLib
>> -  DebugLib
>> +
>> +[LibraryClasses.Ia32, LibraryClasses.X64]
>>     CcExitLib
>> +  LocalApicLib
>>   
>>   [BuildOptions]
>>     XCODE:*_*_X64_NASM_FLAGS = -D NO_ABSOLUTE_RELOCS_IN_TEXT
>> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/DxeExceptionLib.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/DxeExceptionLib.c
>> new file mode 100644
>> index 0000000000..2c5d202b33
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/DxeExceptionLib.c
>> @@ -0,0 +1,198 @@
>> +/** @file DxeExceptionLib.c
>> +
>> +  LoongArch exception library implemenation for DXE modules.
>> +
>> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +**/
>> +
>> +#include <Library/BaseLib.h>
>> +#include <Library/CpuExceptionHandlerLib.h>
>> +#include <Library/CpuLib.h>
>> +#include <Library/CacheMaintenanceLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/SerialPortLib.h>
>> +#include <Protocol/DebugSupport.h>
>> +#include <Register/LoongArch64/Csr.h>
>> +
>> +#include "ExceptionCommon.h"
>> +
>> +EFI_EXCEPTION_CALLBACK  ExternalInterruptHandler[MAX_LOONGARCH_INTERRUPT + 1] = { 0 };
>> +EFI_EXCEPTION_CALLBACK  ExceptionHandler[MAX_LOONGARCH_EXCEPTION + 1]         = { 0 };
>> +
>> +/**
>> +  Registers a function to be called from the processor interrupt or exception handler.
>> +
>> +  This function registers and enables the handler specified by InterruptHandler for a processor
>> +  interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
>> +  handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
>> +  The installed handler is called once for each processor interrupt or exception.
>> +
>> +  @param  InterruptType    A pointer to the processor's current interrupt state. Set to TRUE if interrupts
>> +                           are enabled and FALSE if interrupts are disabled.
>> +  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
>> +                           when a processor interrupt occurs. If this parameter is NULL, then the handler
>> +                           will be uninstalled.
>> +
>> +  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
>> +  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was
>> +                                previously installed.
>> +  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
>> +                                previously installed.
>> +  @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
>> +
>> +**/
>> +EFI_STATUS
>> +RegisterCpuInterruptHandler (
>> +  IN EFI_EXCEPTION_TYPE         InterruptType,
>> +  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
>> +  )
>> +{
>> +  EFI_EXCEPTION_TYPE  ExceptionType;
>> +
>> +  ExceptionType = InterruptType & CSR_ESTAT_EXC;
>> +
>> +  if (ExceptionType != 0) {
>> +    //
>> +    // Exception
>> +    //
>> +    if (ExceptionType > EXCEPT_LOONGARCH_FPE) {
>> +      return EFI_UNSUPPORTED;
>> +    }
>> +
>> +    ExceptionType >>= CSR_ESTAT_EXC_SHIFT;
>> +
>> +    if ((InterruptHandler == NULL) && (ExceptionHandler[InterruptType] == NULL)) {
>> +      return EFI_INVALID_PARAMETER;
>> +    }
>> +
>> +    if ((InterruptHandler != NULL) && (ExceptionHandler[ExceptionType] != NULL)) {
>> +      return EFI_ALREADY_STARTED;
>> +    }
>> +
>> +    ExceptionHandler[ExceptionType] = InterruptHandler;
>> +  } else {
>> +    //
>> +    // Interrupt
>> +    //
>> +    if (InterruptType > MAX_LOONGARCH_INTERRUPT) {
>> +      return EFI_UNSUPPORTED;
>> +    }
>> +
>> +    if ((InterruptHandler == NULL) && (ExternalInterruptHandler[InterruptType] == NULL)) {
>> +      return EFI_INVALID_PARAMETER;
>> +    }
>> +
>> +    if ((InterruptHandler != NULL) && (ExternalInterruptHandler[InterruptType] != NULL)) {
>> +      return EFI_ALREADY_STARTED;
>> +    }
>> +
>> +    ExternalInterruptHandler[InterruptType] = InterruptHandler;
>> +  }
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Common exception handler.
>> +
>> +  @param ExceptionType  Exception type.
>> +  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
>> +
>> +**/
>> +VOID
>> +EFIAPI
>> +CommonExceptionHandler (
>> +  IN     EFI_EXCEPTION_TYPE  ExceptionType,
>> +  IN OUT EFI_SYSTEM_CONTEXT  SystemContext
>> +  )
>> +{
>> +  EFI_EXCEPTION_TYPE  InterruptType;
>> +
>> +  if (ExceptionType == EXCEPT_LOONGARCH_INT) {
>> +    //
>> +    // Interrupt
>> +    //
>> +    InterruptType = GetInterruptType (SystemContext);
>> +    if (InterruptType == 0xFF) {
>> +      ExceptionType = InterruptType;
>> +    } else {
>> +      if ((ExternalInterruptHandler != NULL) && (ExternalInterruptHandler[InterruptType] != NULL)) {
>> +        ExternalInterruptHandler[InterruptType](InterruptType, SystemContext);
>> +        return;
>> +      }
>> +    }
>> +  } else if (ExceptionType == EXCEPT_LOONGARCH_FPD) {
>> +    EnableFloatingPointUnits ();
>> +    InitializeFloatingPointUnits ();
>> +    return;
>> +  } else {
>> +    //
>> +    // Exception
>> +    //
>> +    ExceptionType >>= CSR_ESTAT_EXC_SHIFT;
>> +    if ((ExceptionHandler != NULL) && (ExceptionHandler[ExceptionType] != NULL)) {
>> +      ExceptionHandler[ExceptionType](ExceptionType, SystemContext);
>> +      return;
>> +    }
>> +  }
>> +
>> +  //
>> +  // Only the TLB refill exception use the same entry point as normal exceptions.
>> +  //
>> +  if (CsrRead (LOONGARCH_CSR_TLBRERA) & 0x1) {
>> +    ExceptionType = mExceptionKnownNameNum - 1; // Use only to dump the exception context.
>> +  }
>> +
>> +  DefaultExceptionHandler (ExceptionType, SystemContext);
>> +}
>> +
>> +/**
>> +  Initializes all CPU exceptions entries and provides the default exception handlers.
>> +
>> +  Caller should try to get an array of interrupt and/or exception vectors that are in use and need to
>> +  persist by EFI_VECTOR_HANDOFF_INFO defined in PI 1.3 specification.
>> +  If caller cannot get reserved vector list or it does not exists, set VectorInfo to NULL.
>> +  If VectorInfo is not NULL, the exception vectors will be initialized per vector attribute accordingly.
>> +
>> +  @param[in]  VectorInfo    Pointer to reserved vector list.
>> +
>> +  @retval EFI_SUCCESS           CPU Exception Entries have been successfully initialized
>> +                                with default exception handlers.
>> +  @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL.
>> +  @retval EFI_UNSUPPORTED       This function is not supported.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +InitializeCpuExceptionHandlers (
>> +  IN EFI_VECTOR_HANDOFF_INFO  *VectorInfo OPTIONAL
>> +  )
>> +{
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Setup separate stacks for certain exception handlers.
>> +  If the input Buffer and BufferSize are both NULL, use global variable if possible.
>> +
>> +  @param[in]       Buffer        Point to buffer used to separate exception stack.
>> +  @param[in, out]  BufferSize    On input, it indicates the byte size of Buffer.
>> +                                 If the size is not enough, the return status will
>> +                                 be EFI_BUFFER_TOO_SMALL, and output BufferSize
>> +                                 will be the size it needs.
>> +
>> +  @retval EFI_SUCCESS             The stacks are assigned successfully.
>> +  @retval EFI_UNSUPPORTED         This function is not supported.
>> +  @retval EFI_BUFFER_TOO_SMALL    This BufferSize is too small.
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +InitializeSeparateExceptionStacks (
>> +  IN     VOID   *Buffer,
>> +  IN OUT UINTN  *BufferSize
>> +  )
>> +{
>> +  return EFI_SUCCESS;
>> +}
>> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.c
>> new file mode 100644
>> index 0000000000..801c8393e8
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.c
>> @@ -0,0 +1,171 @@
>> +/** @file DxeExceptionLib.c
>> +
>> +  CPU Exception Handler Library common functions.
>> +
>> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +**/
>> +
>> +#include <Library/BaseLib.h>
>> +#include <Library/PeCoffGetEntryPointLib.h>
>> +#include <Library/PrintLib.h>
>> +#include <Library/SerialPortLib.h>
>> +#include <Register/LoongArch64/Csr.h>
>> +#include "ExceptionCommon.h"
>> +
>> +CONST CHAR8  mExceptionReservedStr[] = "Reserved";
>> +CONST CHAR8  *mExceptionNameStr[]    = {
>> +  "#INT - Interrupt(CSR.ECFG.VS=0)",
>> +  "#PIL - Page invalid exception for Load option",
>> +  "#PIS - Page invalid exception for Store operation",
>> +  "#PIF - Page invalid exception for Fetch operation",
>> +  "#PME - Page modification exception",
>> +  "#PNR - Page non-readable exception",
>> +  "#PNX - Page non-executable exception",
>> +  "#PPI - Page privilege level illegal exception",
>> +  "#ADE - Address error exception",
>> +  "#ALE - Address alignment fault exception",
>> +  "#BCE - Bound check exception",
>> +  "#SYS - System call exception",
>> +  "#BRK - Beeakpoint exception",
>> +  "#INE - Instruction non-defined exception",
>> +  "#IPE - Instruction privilege error exception",
>> +  "#FPD - Floating-point instruction disable exception",
>> +  "#SXD - 128-bit vector (SIMD instructions) expansion instruction disable exception",
>> +  "#ASXD - 256-bit vector (Advanced SIMD instructions) expansion instruction disable exception",
>> +  "#FPE - Floating-Point error exception",
>> +  "#WPE - WatchPoint Exception for Fetch watchpoint or Memory load/store watchpoint",
>> +  "#BTD - Binary Translation expansion instruction Disable exception",
>> +  "#BTE - Binary Translation related exceptions",
>> +  "#GSPR - Guest Sensitive Privileged Resource exception",
>> +  "#HVC - HyperVisor Call exception",
>> +  "#GCXC - Guest CSR Software/Hardware Change exception",
>> +  "#TBR - TLB refill exception" // !!! NOTICE: Because the TLB refill exception is not instructed in ECODE, so the TLB refill exception must be the last one!
>> +};
>> +
>> +INTN  mExceptionKnownNameNum = (sizeof (mExceptionNameStr) / sizeof (CHAR8 *));
>> +
>> +/**
>> +  Get ASCII format string exception name by exception type.
>> +
>> +  @param ExceptionType  Exception type.
>> +
>> +  @return  ASCII format string exception name.
>> +
>> +**/
>> +CONST CHAR8 *
>> +GetExceptionNameStr (
>> +  IN EFI_EXCEPTION_TYPE  ExceptionType
>> +  )
>> +{
>> +  if ((UINTN)ExceptionType < mExceptionKnownNameNum) {
>> +    return mExceptionNameStr[ExceptionType];
>> +  } else {
>> +    return mExceptionReservedStr;
>> +  }
>> +}
>> +
>> +/**
>> +  Prints a message to the serial port.
>> +
>> +  @param  Format      Format string for the message to print.
>> +  @param  ...         Variable argument list whose contents are accessed
>> +                      based on the format string specified by Format.
>> +
>> +**/
>> +VOID
>> +EFIAPI
>> +InternalPrintMessage (
>> +  IN  CONST CHAR8  *Format,
>> +  ...
>> +  )
>> +{
>> +  CHAR8    Buffer[MAX_DEBUG_MESSAGE_LENGTH];
>> +  VA_LIST  Marker;
>> +
>> +  //
>> +  // Convert the message to an ASCII String
>> +  //
>> +  VA_START (Marker, Format);
>> +  AsciiVSPrint (Buffer, sizeof (Buffer), Format, Marker);
>> +  VA_END (Marker);
>> +
>> +  //
>> +  // Send the print string to a Serial Port
>> +  //
>> +  SerialPortWrite ((UINT8 *)Buffer, AsciiStrLen (Buffer));
>> +}
>> +
>> +/**
>> +  Find and display image base address and return image base and its entry point.
>> +
>> +  @param CurrentEra      Current instruction pointer.
>> +
>> +**/
>> +VOID
>> +DumpModuleImageInfo (
>> +  IN  UINTN  CurrentEra
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +  UINTN       Pe32Data;
>> +  VOID        *PdbPointer;
>> +  VOID        *EntryPoint;
>> +
>> +  Pe32Data = PeCoffSearchImageBase (CurrentEra);
>> +  if (Pe32Data == 0) {
>> +    InternalPrintMessage ("!!!! Can't find image information. !!!!\n");
>> +  } else {
>> +    //
>> +    // Find Image Base entry point
>> +    //
>> +    Status = PeCoffLoaderGetEntryPoint ((VOID *)Pe32Data, &EntryPoint);
>> +    if (EFI_ERROR (Status)) {
>> +      EntryPoint = NULL;
>> +    }
>> +
>> +    InternalPrintMessage ("!!!! Find image based on IP(0x%x) ", CurrentEra);
>> +    PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)Pe32Data);
>> +    if (PdbPointer != NULL) {
>> +      InternalPrintMessage ("%a", PdbPointer);
>> +    } else {
>> +      InternalPrintMessage ("(No PDB) ");
>> +    }
>> +
>> +    InternalPrintMessage (
>> +      " (ImageBase=%016lp, EntryPoint=%016p) !!!!\n",
>> +      (VOID *)Pe32Data,
>> +      EntryPoint
>> +      );
>> +  }
>> +}
>> +
>> +/**
>> +  Default exception handler.
>> +
>> +  @param ExceptionType  Exception type.
>> +  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
>> +
>> +**/
>> +VOID
>> +EFIAPI
>> +DefaultExceptionHandler (
>> +  IN     EFI_EXCEPTION_TYPE  ExceptionType,
>> +  IN OUT EFI_SYSTEM_CONTEXT  SystemContext
>> +  )
>> +{
>> +  //
>> +  // Initialize the serial port before dumping.
>> +  //
>> +  SerialPortInitialize ();
>> +  //
>> +  // Display ExceptionType, CPU information and Image information
>> +  //
>> +  DumpImageAndCpuContent (ExceptionType, SystemContext);
>> +
>> +  //
>> +  // Enter a dead loop.
>> +  //
>> +  CpuDeadLoop ();
>> +}
>> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.h b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.h
>> new file mode 100644
>> index 0000000000..e326b73e3f
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/ExceptionCommon.h
>> @@ -0,0 +1,131 @@
>> +/** @file DxeExceptionLib.h
>> +
>> +  Common header file for CPU Exception Handler Library.
>> +
>> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +**/
>> +
>> +#ifndef EXCEPTION_COMMON_H_
>> +#define EXCEPTION_COMMON_H_
>> +
>> +#define MAX_DEBUG_MESSAGE_LENGTH  0x100
>> +
>> +//
>> +// For coding convenience, define the maximum valid
>> +// LoongArch exception.
>> +// Since UEFI V2.11, it will be present in DebugSupport.h.
>> +//
>> +#define MAX_LOONGARCH_EXCEPTION  64
>> +
>> +extern INTN  mExceptionKnownNameNum;
>> +
>> +/**
>> +  Get ASCII format string exception name by exception type.
>> +
>> +  @param[in] ExceptionType  Exception type.
>> +
>> +  @return    ASCII format string exception name.
>> +
>> +**/
>> +CONST CHAR8 *
>> +GetExceptionNameStr (
>> +  IN EFI_EXCEPTION_TYPE  ExceptionType
>> +  );
>> +
>> +/**
>> +  Prints a message to the serial port.
>> +
>> +  @param[in]  Format      Format string for the message to print.
>> +  @param[in]  ...         Variable argument list whose contents are accessed
>> +                      based on the format string specified by Format.
>> +
>> +**/
>> +VOID
>> +EFIAPI
>> +InternalPrintMessage (
>> +  IN  CONST CHAR8  *Format,
>> +  ...
>> +  );
>> +
>> +/**
>> +  Find and display image base address and return image base and its entry point.
>> +
>> +  @param[in] CurrentEip      Current instruction pointer.
>> +
>> +**/
>> +VOID
>> +DumpModuleImageInfo (
>> +  IN UINTN  CurrentEip
>> +  );
>> +
>> +/**
>> +  IPI Interrupt Handler.
>> +
>> +  @param InterruptType    The type of interrupt that occurred
>> +  @param SystemContext    A pointer to the system context when the interrupt occurred
>> +**/
>> +VOID
>> +EFIAPI
>> +IpiInterruptHandler (
>> +  IN EFI_EXCEPTION_TYPE  InterruptType,
>> +  IN EFI_SYSTEM_CONTEXT  SystemContext
>> +  );
>> +
>> +/**
>> +  Default exception handler.
>> +
>> +  @param[in] ExceptionType  Exception type.
>> +  @param[in] SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
>> +
>> +**/
>> +VOID
>> +EFIAPI
>> +DefaultExceptionHandler (
>> +  IN     EFI_EXCEPTION_TYPE  ExceptionType,
>> +  IN OUT EFI_SYSTEM_CONTEXT  SystemContext
>> +  );
>> +
>> +/**
>> +  Display CPU information.
>> +
>> +  @param[in] ExceptionType  Exception type.
>> +  @param[in] SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
>> +
>> +**/
>> +VOID
>> +DumpImageAndCpuContent (
>> +  IN EFI_EXCEPTION_TYPE  ExceptionType,
>> +  IN EFI_SYSTEM_CONTEXT  SystemContext
>> +  );
>> +
>> +/**
>> +  Get exception types
>> +
>> +  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
>> +
>> +  @return     Exception type.
>> +
>> +**/
>> +EFI_EXCEPTION_TYPE
>> +EFIAPI
>> +GetExceptionType (
>> +  IN EFI_SYSTEM_CONTEXT  SystemContext
>> +  );
>> +
>> +/**
>> +  Get Common interrupt types
>> +
>> +  @param[in] SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
>> +
>> +  @return    Interrupt type.
>> +
>> +**/
>> +EFI_EXCEPTION_TYPE
>> +EFIAPI
>> +GetInterruptType (
>> +  IN EFI_SYSTEM_CONTEXT  SystemContext
>> +  );
>> +
>> +#endif
>> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ArchExceptionHandler.c
>> new file mode 100644
>> index 0000000000..c0219deba5
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ArchExceptionHandler.c
>> @@ -0,0 +1,268 @@
>> +/** @file ArchExceptionHandler.c
>> +
>> +  LoongArch64 CPU Exception Handler.
>> +
>> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +**/
>> +
>> +#include <Library/BaseLib.h>
>> +#include <Register/LoongArch64/Csr.h>
>> +#include "ExceptionCommon.h"
>> +
>> +/**
>> +  Get Exception Type
>> +
>> +  @param[in] SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
>> +
>> +  @return    LoongArch64 exception type.
>> +
>> +**/
>> +EFI_EXCEPTION_TYPE
>> +EFIAPI
>> +GetExceptionType (
>> +  IN EFI_SYSTEM_CONTEXT  SystemContext
>> +  )
>> +{
>> +  EFI_EXCEPTION_TYPE  ExceptionType;
>> +
>> +  ExceptionType = (SystemContext.SystemContextLoongArch64->ESTAT & CSR_ESTAT_EXC);
>> +  return ExceptionType;
>> +}
>> +
>> +/**
>> +  Get Interrupt Type
>> +
>> +  @param[in] SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
>> +
>> +  @return    LoongArch64 intrrupt type.
>> +
>> +**/
>> +EFI_EXCEPTION_TYPE
>> +EFIAPI
>> +GetInterruptType (
>> +  IN EFI_SYSTEM_CONTEXT  SystemContext
>> +  )
>> +{
>> +  EFI_EXCEPTION_TYPE  InterruptType;
>> +
>> +  for (InterruptType = 0; InterruptType <= EXCEPT_LOONGARCH_INT_IPI; InterruptType++) {
>> +    if (SystemContext.SystemContextLoongArch64->ESTAT & (1 << InterruptType)) {
>> +      //
>> +      // 0  - EXCEPT_LOONGARCH_INT_SIP0
>> +      // 1  - EXCEPT_LOONGARCH_INT_SIP1
>> +      // 2  - EXCEPT_LOONGARCH_INT_IP0
>> +      // 3  - EXCEPT_LOONGARCH_INT_IP1
>> +      // 4  - EXCEPT_LOONGARCH_INT_IP2
>> +      // 5  - EXCEPT_LOONGARCH_INT_IP3
>> +      // 6  - EXCEPT_LOONGARCH_INT_IP4
>> +      // 7  - EXCEPT_LOONGARCH_INT_IP5
>> +      // 8  - EXCEPT_LOONGARCH_INT_IP6
>> +      // 9  - EXCEPT_LOONGARCH_INT_IP7
>> +      // 10 - EXCEPT_LOONGARCH_INT_PMC
>> +      // 11 - EXCEPT_LOONGARCH_INT_TIMER
>> +      // 12 - EXCEPT_LOONGARCH_INT_IPI
>> +      // Greater than EXCEPT_LOONGARCH_INI_IPI is currently invalid.
>> +      //
>> +      return InterruptType;
>> +    }
>> +  }
>> +
>> +  //
>> +  // Invalid IRQ
>> +  //
>> +  return 0xFF;
>> +}
>> +
>> +/**
>> +  Display CPU information.
>> +
>> +  @param ExceptionType  Exception type.
>> +  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
>> +
>> +**/
>> +VOID
>> +EFIAPI
>> +DumpCpuContext (
>> +  IN EFI_EXCEPTION_TYPE  ExceptionType,
>> +  IN EFI_SYSTEM_CONTEXT  SystemContext
>> +  )
>> +{
>> +  InternalPrintMessage (
>> +    "\n!!!! LoongArch64 Exception Type - %02x(%a) !!!!\n",
>> +    ExceptionType,
>> +    GetExceptionNameStr (ExceptionType)
>> +    );
>> +
>> +  //
>> +  // Dump TLB refill ERA and BADV
>> +  //
>> +  if (ExceptionType == (mExceptionKnownNameNum - 1)) {
>> +    InternalPrintMessage ("TLB refill ERA  0x%llx\n", (CsrRead (LOONGARCH_CSR_TLBRERA) & (~0x3ULL)));
>> +    InternalPrintMessage ("TLB refill BADV  0x%llx\n", CsrRead (LOONGARCH_CSR_TLBRBADV));
>> +  }
>> +
>> +  //
>> +  // Dump the general registers
>> +  //
>> +  InternalPrintMessage (
>> +    "Zero  - 0x%016lx, RA  - 0x%016lx, TP - 0x%016lx, SP - 0x%016lx\n",
>> +    SystemContext.SystemContextLoongArch64->R0,
>> +    SystemContext.SystemContextLoongArch64->R1,
>> +    SystemContext.SystemContextLoongArch64->R2,
>> +    SystemContext.SystemContextLoongArch64->R3
>> +    );
>> +  InternalPrintMessage (
>> +    "  A0  - 0x%016lx, A1  - 0x%016lx, A2 - 0x%016lx, A3 - 0x%016lx\n",
>> +    SystemContext.SystemContextLoongArch64->R4,
>> +    SystemContext.SystemContextLoongArch64->R5,
>> +    SystemContext.SystemContextLoongArch64->R6,
>> +    SystemContext.SystemContextLoongArch64->R7
>> +    );
>> +  InternalPrintMessage (
>> +    "  A4  - 0x%016lx, A5  - 0x%016lx, A6 - 0x%016lx, A7 - 0x%016lx\n",
>> +    SystemContext.SystemContextLoongArch64->R8,
>> +    SystemContext.SystemContextLoongArch64->R9,
>> +    SystemContext.SystemContextLoongArch64->R10,
>> +    SystemContext.SystemContextLoongArch64->R11
>> +    );
>> +  InternalPrintMessage (
>> +    "  T0  - 0x%016lx, T1  - 0x%016lx, T2 - 0x%016lx, T3 - 0x%016lx\n",
>> +    SystemContext.SystemContextLoongArch64->R12,
>> +    SystemContext.SystemContextLoongArch64->R13,
>> +    SystemContext.SystemContextLoongArch64->R14,
>> +    SystemContext.SystemContextLoongArch64->R15
>> +    );
>> +  InternalPrintMessage (
>> +    "  T4  - 0x%016lx, T5  - 0x%016lx, T6 - 0x%016lx, T7 - 0x%016lx\n",
>> +    SystemContext.SystemContextLoongArch64->R16,
>> +    SystemContext.SystemContextLoongArch64->R17,
>> +    SystemContext.SystemContextLoongArch64->R18,
>> +    SystemContext.SystemContextLoongArch64->R19
>> +    );
>> +  InternalPrintMessage (
>> +    "  T8  - 0x%016lx, R21 - 0x%016lx, FP - 0x%016lx, S0 - 0x%016lx\n",
>> +    SystemContext.SystemContextLoongArch64->R20,
>> +    SystemContext.SystemContextLoongArch64->R21,
>> +    SystemContext.SystemContextLoongArch64->R22,
>> +    SystemContext.SystemContextLoongArch64->R23
>> +    );
>> +  InternalPrintMessage (
>> +    "  S1  - 0x%016lx, S2  - 0x%016lx, S3 - 0x%016lx, S4 - 0x%016lx\n",
>> +    SystemContext.SystemContextLoongArch64->R24,
>> +    SystemContext.SystemContextLoongArch64->R25,
>> +    SystemContext.SystemContextLoongArch64->R26,
>> +    SystemContext.SystemContextLoongArch64->R27
>> +    );
>> +  InternalPrintMessage (
>> +    "  S5  - 0x%016lx, S6  - 0x%016lx, S7 - 0x%016lx, S8 - 0x%016lx\n",
>> +    SystemContext.SystemContextLoongArch64->R28,
>> +    SystemContext.SystemContextLoongArch64->R29,
>> +    SystemContext.SystemContextLoongArch64->R30,
>> +    SystemContext.SystemContextLoongArch64->R31
>> +    );
>> +  InternalPrintMessage ("\n");
>> +
>> +  //
>> +  // Dump the CSR registers
>> +  //
>> +  InternalPrintMessage (
>> +    "CRMD  - 0x%016lx, PRMD  - 0x%016lx, EUEN - 0x%016lx, MISC - 0x%016lx\n",
>> +    SystemContext.SystemContextLoongArch64->CRMD,
>> +    SystemContext.SystemContextLoongArch64->PRMD,
>> +    SystemContext.SystemContextLoongArch64->EUEN,
>> +    SystemContext.SystemContextLoongArch64->MISC
>> +    );
>> +  InternalPrintMessage (
>> +    "ECFG  - 0x%016lx, ESTAT - 0x%016lx, ERA  - 0x%016lx, BADV - 0x%016lx\n",
>> +    SystemContext.SystemContextLoongArch64->ECFG,
>> +    SystemContext.SystemContextLoongArch64->ESTAT,
>> +    SystemContext.SystemContextLoongArch64->ERA,
>> +    SystemContext.SystemContextLoongArch64->BADV
>> +    );
>> +  InternalPrintMessage (
>> +    "BADI  - 0x%016lx\n",
>> +    SystemContext.SystemContextLoongArch64->BADI
>> +    );
>> +}
>> +
>> +/**
>> +  Display CPU information.
>> +
>> +  @param ExceptionType  Exception type.
>> +  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
>> +
>> +**/
>> +VOID
>> +DumpImageAndCpuContent (
>> +  IN EFI_EXCEPTION_TYPE  ExceptionType,
>> +  IN EFI_SYSTEM_CONTEXT  SystemContext
>> +  )
>> +{
>> +  DumpCpuContext (ExceptionType, SystemContext);
>> +
>> +  if (ExceptionType == (mExceptionKnownNameNum - 1)) {
>> +    //
>> +    // Dump TLB refill image info
>> +    //
>> +    DumpModuleImageInfo ((CsrRead (LOONGARCH_CSR_TLBRERA) & (~0x3ULL)));
>> +  } else {
>> +    DumpModuleImageInfo (SystemContext.SystemContextLoongArch64->ERA);
>> +  }
>> +}
>> +
>> +/**
>> +  IPI Interrupt Handler.
>> +
>> +  @param InterruptType    The type of interrupt that occurred
>> +  @param SystemContext    A pointer to the system context when the interrupt occurred
>> +**/
>> +VOID
>> +EFIAPI
>> +IpiInterruptHandler (
>> +  IN EFI_EXCEPTION_TYPE  InterruptType,
>> +  IN EFI_SYSTEM_CONTEXT  SystemContext
>> +  )
>> +{
>> +  UINTN  ResumeVector;
>> +  UINTN  Parameter;
>> +
>> +  //
>> +  // Clear interrupt.
>> +  //
>> +  IoCsrWrite32 (LOONGARCH_IOCSR_IPI_CLEAR, IoCsrRead32 (LOONGARCH_IOCSR_IPI_STATUS));
>> +
>> +  //
>> +  // Get the resume vector and parameter if populated.
>> +  //
>> +  ResumeVector = IoCsrRead64 (LOONGARCH_IOCSR_MBUF0);
>> +  Parameter    = IoCsrRead64 (LOONGARCH_IOCSR_MBUF3);
>> +
>> +  //
>> +  // Clean up current processor mailbox 0 and mailbox 3.
>> +  //
>> +  IoCsrWrite64 (LOONGARCH_IOCSR_MBUF0, 0x0);
>> +  IoCsrWrite64 (LOONGARCH_IOCSR_MBUF3, 0x0);
>> +
>> +  //
>> +  // If mailbox 0 is non-NULL, it means that the BSP or other cores called the IPI to wake
>> +  // up the current core and let it use the resume vector stored in mailbox 0.
>> +  //
>> +  // If both the resume vector and parameter are non-NULL, it means that the IPI was
>> +  // called in the BIOS.
>> +  //
>> +  // The situation where the resume vector is non-NULL and the parameter is NULL has been
>> +  // processed after the exception entry is pushed onto the stack.
>> +  //
>> +  if ((ResumeVector != 0) && (Parameter != 0)) {
>> +    SystemContext.SystemContextLoongArch64->ERA = ResumeVector;
>> +    //
>> +    // Set $a0 as APIC ID and $a1 as parameter value.
>> +    //
>> +    SystemContext.SystemContextLoongArch64->R4 = CsrRead (LOONGARCH_CSR_CPUNUM);
>> +    SystemContext.SystemContextLoongArch64->R5 = Parameter;
>> +  }
>> +
>> +  MemoryFence ();
>> +}
>> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ExceptionHandlerAsm.S b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ExceptionHandlerAsm.S
>> new file mode 100644
>> index 0000000000..7c692e01c1
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/LoongArch64/ExceptionHandlerAsm.S
>> @@ -0,0 +1,366 @@
>> +#------------------------------------------------------------------------------
>> +#
>> +# LoongArch64 ASM exception handler
>> +#
>> +# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +#
>> +# SPDX-License-Identifier: BSD-2-Clause-Patent
>> +#
>> +#------------------------------------------------------------------------------
>> +
>> +#include <Library/BaseLib.h>
>> +#include <Library/CpuLib.h>
>> +#include <Register/LoongArch64/Csr.h>
>> +
>> +#define RSIZE                 8           // 64 bit mode register size
>> +#define GP_REG_CONTEXT_SIZE   32 * RSIZE  // General-purpose registers size
>> +#define FP_REG_CONTEXT_SIZE   34 * RSIZE  // Floating-point registers size
>> +#define CSR_REG_CONTEXT_SIZE  9  * RSIZE  // CSR registers size
>> +
>> +ASM_GLOBAL ASM_PFX(ExceptionEntry)
>> +ASM_GLOBAL ASM_PFX(ExceptionEntryStart)
>> +ASM_GLOBAL ASM_PFX(ExceptionEntryEnd)
>> +
>> +ASM_PFX(ExceptionEntry):
>> +  move    $s0, $a0
>> +  bl      GetExceptionType        // Exception type stored in register a0
>> +  move    $a1, $s0                // SystemContxt
>> +  bl      CommonExceptionHandler
>> +
>> +PopContext:
>> +  //
>> +  // Not sure if interrupts are turned on during the exception handler, anyway disable interrupts here.
>> +  // It will be turned on when the instruction 'ertn' is executed.
>> +  //
>> +  bl      DisableInterrupts
>> +
>> +  bl      GetExceptionType        // Get current exception type, and stored in register a0
>> +
>> +  // Check whether the FPE is changed during interrupt handler, if ture restore it.
>> +  ld.d    $t1, $sp, (LOONGARCH_CSR_EUEN * RSIZE + GP_REG_CONTEXT_SIZE)
>> +  csrrd   $t0, LOONGARCH_CSR_EUEN        // Current EUEN
>> +  andi    $t0, $t0, CSR_EUEN_FPEN
>> +  andi    $t1, $t1, CSR_EUEN_FPEN
>> +  li.d    $t2, EXCEPT_LOONGARCH_INT
>> +  bne     $a0, $t2, PopRegs
>> +  beq     $t0, $t1, PopRegs
>> +  beqz    $t1, CloseFP
>> +  bl      EnableFloatingPointUnits
>> +  b       PopRegs
>> +
>> +CloseFP:
>> +  bl      DisableFloatingPointUnits
>> +
>> +PopRegs:
>> +  //
>> +  // Pop CSR reigsters
>> +  //
>> +  addi.d  $sp, $sp, GP_REG_CONTEXT_SIZE
>> +
>> +  ld.d    $t0, $sp, LOONGARCH_CSR_CRMD * RSIZE
>> +  csrwr   $t0, LOONGARCH_CSR_CRMD
>> +  ld.d    $t0, $sp, LOONGARCH_CSR_PRMD * RSIZE
>> +  csrwr   $t0, LOONGARCH_CSR_PRMD
>> +  ld.d    $t0, $sp, LOONGARCH_CSR_ECFG * RSIZE
>> +  csrwr   $t0, LOONGARCH_CSR_ECFG
>> +  ld.d    $t0, $sp, LOONGARCH_CSR_ERA * RSIZE
>> +  csrwr   $t0, LOONGARCH_CSR_ERA
>> +
>> +  addi.d  $sp, $sp, CSR_REG_CONTEXT_SIZE  // Fource change the stack pointer befor pop the FP registers.
>> +
>> +  beqz    $t1, PopGP                      // If the FPE not set, only pop the GP registers.
>> +
>> +  //
>> +  // Pop FP registers
>> +  //
>> +  fld.d  $fa0, $sp, 0 * RSIZE
>> +  fld.d  $fa1, $sp, 1 * RSIZE
>> +  fld.d  $fa2, $sp, 2 * RSIZE
>> +  fld.d  $fa3, $sp, 3 * RSIZE
>> +  fld.d  $fa4, $sp, 4 * RSIZE
>> +  fld.d  $fa5, $sp, 5 * RSIZE
>> +  fld.d  $fa6, $sp, 6 * RSIZE
>> +  fld.d  $fa7, $sp, 7 * RSIZE
>> +  fld.d  $ft0, $sp, 8 * RSIZE
>> +  fld.d  $ft1, $sp, 9 * RSIZE
>> +  fld.d  $ft2, $sp, 10 * RSIZE
>> +  fld.d  $ft3, $sp, 11 * RSIZE
>> +  fld.d  $ft4, $sp, 12 * RSIZE
>> +  fld.d  $ft5, $sp, 13 * RSIZE
>> +  fld.d  $ft6, $sp, 14 * RSIZE
>> +  fld.d  $ft7, $sp, 15 * RSIZE
>> +  fld.d  $ft8, $sp, 16 * RSIZE
>> +  fld.d  $ft9, $sp, 17 * RSIZE
>> +  fld.d  $ft10, $sp, 18 * RSIZE
>> +  fld.d  $ft11, $sp, 19 * RSIZE
>> +  fld.d  $ft12, $sp, 20 * RSIZE
>> +  fld.d  $ft13, $sp, 21 * RSIZE
>> +  fld.d  $ft14, $sp, 22 * RSIZE
>> +  fld.d  $ft15, $sp, 23 * RSIZE
>> +  fld.d  $fs0, $sp, 24 * RSIZE
>> +  fld.d  $fs1, $sp, 25 * RSIZE
>> +  fld.d  $fs2, $sp, 26 * RSIZE
>> +  fld.d  $fs3, $sp, 27 * RSIZE
>> +  fld.d  $fs4, $sp, 28 * RSIZE
>> +  fld.d  $fs5, $sp, 29 * RSIZE
>> +  fld.d  $fs6, $sp, 30 * RSIZE
>> +  fld.d  $fs7, $sp, 31 * RSIZE
>> +
>> +  ld.d        $t0, $sp, 32 * RSIZE
>> +  movgr2fcsr  $r0, $t0             // Pop the fcsr0 register.
>> +
>> +  //
>> +  // Pop the fcc0-fcc7 registers.
>> +  //
>> +  ld.d        $t0, $sp, 33 * RSIZE
>> +  bstrpick.d  $t1, $t0, 7, 0
>> +  movgr2cf    $fcc0, $t1
>> +  bstrpick.d  $t1, $t0, 15, 8
>> +  movgr2cf    $fcc1, $t1
>> +  bstrpick.d  $t1, $t0, 23, 16
>> +  movgr2cf    $fcc2, $t1
>> +  bstrpick.d  $t1, $t0, 31, 24
>> +  movgr2cf    $fcc3, $t1
>> +  bstrpick.d  $t1, $t0, 39, 32
>> +  movgr2cf    $fcc4, $t1
>> +  bstrpick.d  $t1, $t0, 47, 40
>> +  movgr2cf    $fcc5, $t1
>> +  bstrpick.d  $t1, $t0, 55, 48
>> +  movgr2cf    $fcc6, $t1
>> +  bstrpick.d  $t1, $t0, 63, 56
>> +  movgr2cf    $fcc7, $t1
>> +
>> +PopGP:
>> +  //
>> +  // Pop GP registers
>> +  //
>> +  addi.d  $sp, $sp, -(GP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE)
>> +  ld.d    $ra, $sp, 1 * RSIZE
>> +  ld.d    $tp, $sp, 2 * RSIZE
>> +  ld.d    $a0, $sp, 4 * RSIZE
>> +  ld.d    $a1, $sp, 5 * RSIZE
>> +  ld.d    $a2, $sp, 6 * RSIZE
>> +  ld.d    $a3, $sp, 7 * RSIZE
>> +  ld.d    $a4, $sp, 8 * RSIZE
>> +  ld.d    $a5, $sp, 9 * RSIZE
>> +  ld.d    $a6, $sp, 10 * RSIZE
>> +  ld.d    $a7, $sp, 11 * RSIZE
>> +  ld.d    $t0, $sp, 12 * RSIZE
>> +  ld.d    $t1, $sp, 13 * RSIZE
>> +  ld.d    $t2, $sp, 14 * RSIZE
>> +  ld.d    $t3, $sp, 15 * RSIZE
>> +  ld.d    $t4, $sp, 16 * RSIZE
>> +  ld.d    $t5, $sp, 17 * RSIZE
>> +  ld.d    $t6, $sp, 18 * RSIZE
>> +  ld.d    $t7, $sp, 19 * RSIZE
>> +  ld.d    $t8, $sp, 20 * RSIZE
>> +  ld.d    $r21, $sp, 21 * RSIZE
>> +  ld.d    $fp, $sp, 22 * RSIZE
>> +  ld.d    $s0, $sp, 23 * RSIZE
>> +  ld.d    $s1, $sp, 24 * RSIZE
>> +  ld.d    $s2, $sp, 25 * RSIZE
>> +  ld.d    $s3, $sp, 26 * RSIZE
>> +  ld.d    $s4, $sp, 27 * RSIZE
>> +  ld.d    $s5, $sp, 28 * RSIZE
>> +  ld.d    $s6, $sp, 29 * RSIZE
>> +  ld.d    $s7, $sp, 30 * RSIZE
>> +  ld.d    $s8, $sp, 31 * RSIZE
>> +  ld.d    $sp, $sp, 3 * RSIZE
>> +
>> +  ertn // Returen from exception.
>> +//
>> +// End of ExceptionEntry
>> +//
>> +
>> +ASM_PFX(ExceptionEntryStart):
>> +  //
>> +  // Store the old stack pointer in preparation for pushing the exception context onto the new stack.
>> +  //
>> +  csrwr   $sp, LOONGARCH_CSR_KS0
>> +
>> +  csrrd   $sp, LOONGARCH_CSR_KS0
>> +
>> +  //
>> +  // Push GP registers
>> +  //
>> +  addi.d  $sp, $sp, -(GP_REG_CONTEXT_SIZE + FP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE)
>> +  st.d    $zero, $sp, 0 * RSIZE
>> +  st.d    $ra, $sp, 1 * RSIZE
>> +  st.d    $tp, $sp, 2 * RSIZE
>> +  st.d    $a0, $sp, 4 * RSIZE
>> +  st.d    $a1, $sp, 5 * RSIZE
>> +  st.d    $a2, $sp, 6 * RSIZE
>> +  st.d    $a3, $sp, 7 * RSIZE
>> +  st.d    $a4, $sp, 8 * RSIZE
>> +  st.d    $a5, $sp, 9 * RSIZE
>> +  st.d    $a6, $sp, 10 * RSIZE
>> +  st.d    $a7, $sp, 11 * RSIZE
>> +  st.d    $t0, $sp, 12 * RSIZE
>> +  st.d    $t1, $sp, 13 * RSIZE
>> +  st.d    $t2, $sp, 14 * RSIZE
>> +  st.d    $t3, $sp, 15 * RSIZE
>> +  st.d    $t4, $sp, 16 * RSIZE
>> +  st.d    $t5, $sp, 17 * RSIZE
>> +  st.d    $t6, $sp, 18 * RSIZE
>> +  st.d    $t7, $sp, 19 * RSIZE
>> +  st.d    $t8, $sp, 20 * RSIZE
>> +  st.d    $r21, $sp, 21 * RSIZE
>> +  st.d    $fp, $sp, 22 * RSIZE
>> +  st.d    $s0, $sp, 23 * RSIZE
>> +  st.d    $s1, $sp, 24 * RSIZE
>> +  st.d    $s2, $sp, 25 * RSIZE
>> +  st.d    $s3, $sp, 26 * RSIZE
>> +  st.d    $s4, $sp, 27 * RSIZE
>> +  st.d    $s5, $sp, 28 * RSIZE
>> +  st.d    $s6, $sp, 29 * RSIZE
>> +  st.d    $s7, $sp, 30 * RSIZE
>> +  st.d    $s8, $sp, 31 * RSIZE
>> +  csrrd   $t0, LOONGARCH_CSR_KS0  // Read the old stack pointer.
>> +  st.d    $t0, $sp, 3 * RSIZE
>> +
>> +  //
>> +  // Push CSR registers
>> +  //
>> +  addi.d  $sp, $sp, GP_REG_CONTEXT_SIZE
>> +
>> +  csrrd   $t0, LOONGARCH_CSR_CRMD
>> +  st.d    $t0, $sp, LOONGARCH_CSR_CRMD * RSIZE
>> +  csrrd   $t0, LOONGARCH_CSR_PRMD
>> +  st.d    $t0, $sp, LOONGARCH_CSR_PRMD * RSIZE
>> +  csrrd   $t0, LOONGARCH_CSR_EUEN
>> +  st.d    $t0, $sp, LOONGARCH_CSR_EUEN * RSIZE
>> +  csrrd   $t0, LOONGARCH_CSR_MISC
>> +  st.d    $t0, $sp, LOONGARCH_CSR_MISC * RSIZE
>> +  csrrd   $t0, LOONGARCH_CSR_ECFG
>> +  st.d    $t0, $sp, LOONGARCH_CSR_ECFG * RSIZE
>> +  csrrd   $t0, LOONGARCH_CSR_ESTAT
>> +  st.d    $t0, $sp, LOONGARCH_CSR_ESTAT * RSIZE
>> +  csrrd   $t0, LOONGARCH_CSR_ERA
>> +  st.d    $t0, $sp, LOONGARCH_CSR_ERA * RSIZE
>> +  csrrd   $t0, LOONGARCH_CSR_BADV
>> +  st.d    $t0, $sp, LOONGARCH_CSR_BADV * RSIZE
>> +  csrrd   $t0, LOONGARCH_CSR_BADI
>> +  st.d    $t0, $sp, LOONGARCH_CSR_BADI * RSIZE
>> +
>> +  //
>> +  // Push FP registers
>> +  //
>> +  addi.d  $sp, $sp, CSR_REG_CONTEXT_SIZE
>> +
>> +  csrrd   $t0, LOONGARCH_CSR_EUEN
>> +  andi    $t0, $t0, CSR_EUEN_FPEN
>> +  beqz    $t0, PushRegDone
>> +
>> +  fst.d  $fa0, $sp, 0 * RSIZE
>> +  fst.d  $fa1, $sp, 1 * RSIZE
>> +  fst.d  $fa2, $sp, 2 * RSIZE
>> +  fst.d  $fa3, $sp, 3 * RSIZE
>> +  fst.d  $fa4, $sp, 4 * RSIZE
>> +  fst.d  $fa5, $sp, 5 * RSIZE
>> +  fst.d  $fa6, $sp, 6 * RSIZE
>> +  fst.d  $fa7, $sp, 7 * RSIZE
>> +  fst.d  $ft0, $sp, 8 * RSIZE
>> +  fst.d  $ft1, $sp, 9 * RSIZE
>> +  fst.d  $ft2, $sp, 10 * RSIZE
>> +  fst.d  $ft3, $sp, 11 * RSIZE
>> +  fst.d  $ft4, $sp, 12 * RSIZE
>> +  fst.d  $ft5, $sp, 13 * RSIZE
>> +  fst.d  $ft6, $sp, 14 * RSIZE
>> +  fst.d  $ft7, $sp, 15 * RSIZE
>> +  fst.d  $ft8, $sp, 16 * RSIZE
>> +  fst.d  $ft9, $sp, 17 * RSIZE
>> +  fst.d  $ft10, $sp, 18 * RSIZE
>> +  fst.d  $ft11, $sp, 19 * RSIZE
>> +  fst.d  $ft12, $sp, 20 * RSIZE
>> +  fst.d  $ft13, $sp, 21 * RSIZE
>> +  fst.d  $ft14, $sp, 22 * RSIZE
>> +  fst.d  $ft15, $sp, 23 * RSIZE
>> +  fst.d  $fs0, $sp, 24 * RSIZE
>> +  fst.d  $fs1, $sp, 25 * RSIZE
>> +  fst.d  $fs2, $sp, 26 * RSIZE
>> +  fst.d  $fs3, $sp, 27 * RSIZE
>> +  fst.d  $fs4, $sp, 28 * RSIZE
>> +  fst.d  $fs5, $sp, 29 * RSIZE
>> +  fst.d  $fs6, $sp, 30 * RSIZE
>> +  fst.d  $fs7, $sp, 31 * RSIZE
>> +
>> +  movfcsr2gr  $t3, $r0
>> +  st.d        $t3, $sp, 32 * RSIZE  // Push the FCSR0 register.
>> +
>> +  //
>> +  // Push the fcc0-fcc7 registers.
>> +  //
>> +  movcf2gr    $t3, $fcc0
>> +  or          $t2, $t3, $zero
>> +  movcf2gr    $t3, $fcc1
>> +  bstrins.d   $t2, $t3, 0xf, 0x8
>> +  movcf2gr    $t3, $fcc2
>> +  bstrins.d   $t2, $t3, 0x17, 0x10
>> +  movcf2gr    $t3, $fcc3
>> +  bstrins.d   $t2, $t3, 0x1f, 0x18
>> +  movcf2gr    $t3, $fcc4
>> +  bstrins.d   $t2, $t3, 0x27, 0x20
>> +  movcf2gr    $t3, $fcc5
>> +  bstrins.d   $t2, $t3, 0x2f, 0x28
>> +  movcf2gr    $t3, $fcc6
>> +  bstrins.d   $t2, $t3, 0x37, 0x30
>> +  movcf2gr    $t3, $fcc7
>> +  bstrins.d   $t2, $t3, 0x3f, 0x38
>> +  st.d        $t2, $sp, 33 * RSIZE
>> +  //
>> +  // Push exception context down
>> +  //
>> +
>> +PushRegDone:
>> +  //
>> +  // Process IPI only when mailbox3 is NULL and mailbox0 is no-NULL.
>> +  //
>> +  li.d      $t0, LOONGARCH_IOCSR_MBUF0
>> +  iocsrrd.d $a0, $t0
>> +  beqz      $a0, EntryConmmonHanlder
>> +
>> +  li.d      $t0, LOONGARCH_IOCSR_MBUF3
>> +  iocsrrd.d $t1, $t0
>> +  bnez      $t1, EntryConmmonHanlder
>> +
>> +  csrrd     $t0, LOONGARCH_CSR_ESTAT
>> +  srli.d    $t0, $t0, 12
>> +  andi      $t0, $t0, 0x1
>> +  beqz      $t0, EntryConmmonHanlder
>> +
>> +  //
>> +  // Clean up current processor mailbox 0 and mailbox 3.
>> +  //
>> +  li.d      $t0, LOONGARCH_IOCSR_MBUF0
>> +  iocsrwr.d $zero, $t0
>> +  li.d      $t0, LOONGARCH_IOCSR_MBUF3
>> +  iocsrwr.d $zero, $t0
>> +
>> +  //
>> +  // Clear IPI interrupt.
>> +  //
>> +  li.d      $t0, LOONGARCH_IOCSR_IPI_STATUS
>> +  iocsrrd.w $t1, $t0
>> +  li.d      $t0, LOONGARCH_IOCSR_IPI_CLEAR
>> +  iocsrwr.w $t1, $t0
>> +
>> +  //
>> +  // Only kernel stage BSP calls IPI without parameters. Clean up the PIE and make sure
>> +  // global interrupts are turned off for the current processor when jumping to the kernel.
>> +  //
>> +  csrwr     $a0, LOONGARCH_CSR_ERA         // Update ERA
>> +  li.w      $t0, BIT2                      // IE
>> +  csrxchg   $zero, $t0, LOONGARCH_CSR_PRMD // Clean PIE
>> +
>> +  //
>> +  // Return this exception and jump to kernel using ERA.
>> +  //
>> +  ertn
>> +
>> +EntryConmmonHanlder:
>> +  addi.d  $sp, $sp, -(GP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE)
>> +  move    $a0, $sp
>> +  la.abs  $ra, ExceptionEntry
>> +  jirl    $zero, $ra, 0
>> +ASM_PFX(ExceptionEntryEnd):
>> +.end
>> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/SecPeiExceptionLib.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/SecPeiExceptionLib.c
>> new file mode 100644
>> index 0000000000..7588d2050b
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/LoongArch/SecPeiExceptionLib.c
>> @@ -0,0 +1,102 @@
>> +/** @file SecPeiExceptionLib.c
>> +
>> +  LoongArch exception library implemenation for PEI and SEC modules.
>> +
>> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +**/
>> +
>> +#include <Library/BaseLib.h>
>> +#include <Library/CpuLib.h>
>> +#include <Library/CpuExceptionHandlerLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/SerialPortLib.h>
>> +#include <Protocol/DebugSupport.h>
>> +#include <Register/LoongArch64/Csr.h>
>> +
>> +#include "ExceptionCommon.h"
>> +
>> +/**
>> +  Registers a function to be called from the processor interrupt or exception handler.
>> +
>> +  Always return EFI_UNSUPPORTED in the SEC exception initialization module.
>> +
>> +  @param  InterruptType    A pointer to the processor's current interrupt state. Set to TRUE if interrupts
>> +                           are enabled and FALSE if interrupts are disabled.
>> +  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
>> +                           when a processor interrupt occurs. If this parameter is NULL, then the handler
>> +                           will be uninstalled.
>> +
>> +  @retval EFI_UNSUPPORTED  The interrupt specified by InterruptType is not supported.
>> +
>> +**/
>> +EFI_STATUS
>> +RegisterCpuInterruptHandler (
>> +  IN EFI_EXCEPTION_TYPE         InterruptType,
>> +  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
>> +  )
>> +{
>> +  return EFI_UNSUPPORTED;
>> +}
>> +
>> +/**
>> +  Common exception handler.
>> +
>> +  @param ExceptionType  Exception type.
>> +  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
>> +
>> +**/
>> +VOID
>> +EFIAPI
>> +CommonExceptionHandler (
>> +  IN     EFI_EXCEPTION_TYPE  ExceptionType,
>> +  IN OUT EFI_SYSTEM_CONTEXT  SystemContext
>> +  )
>> +{
>> +  EFI_EXCEPTION_TYPE  InterruptType;
>> +
>> +  if (ExceptionType == EXCEPT_LOONGARCH_INT) {
>> +    //
>> +    // Interrupt
>> +    //
>> +    InterruptType = GetInterruptType (SystemContext);
>> +    if (InterruptType == EXCEPT_LOONGARCH_INT_IPI) {
>> +      //
>> +      // APs may wake up via IPI IRQ during the SEC or PEI phase, clear the IPI interrupt and
>> +      // perform the remaining work.
>> +      //
>> +      IpiInterruptHandler (InterruptType, SystemContext);
>> +      return;
>> +    } else {
>> +      ExceptionType = InterruptType;
>> +    }
>> +  } else {
>> +    //
>> +    // Exception
>> +    //
>> +    ExceptionType >>= CSR_ESTAT_EXC_SHIFT;
>> +  }
>> +
>> +  DefaultExceptionHandler (ExceptionType, SystemContext);
>> +}
>> +
>> +/**
>> +  Initializes all CPU exceptions entries and provides the default exception handlers.
>> +
>> +  Always return EFI_SUCCESS in the SEC exception initialization module.
>> +
>> +  @param[in]  VectorInfo    Pointer to reserved vector list.
>> +
>> +  @retval EFI_SUCCESS       CPU Exception Entries have been successfully initialized
>> +                            with default exception handlers.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +InitializeCpuExceptionHandlers (
>> +  IN EFI_VECTOR_HANDOFF_INFO  *VectorInfo OPTIONAL
>> +  )
>> +{
>> +  return EFI_SUCCESS;
>> +}
>> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
>> index e7b1144f69..6bb194ea77 100644
>> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
>> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
>> @@ -2,6 +2,7 @@
>>   #  CPU Exception Handler library instance for SEC/PEI modules.
>>   #
>>   #  Copyright (c) 2012 - 2022, Intel Corporation. All rights reserved.<BR>
>> +#  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>>   #  SPDX-License-Identifier: BSD-2-Clause-Patent
>>   #
>>   ##
>> @@ -18,37 +19,47 @@
>>   #
>>   # The following information is for reference only and not required by the build tools.
>>   #
>> -#  VALID_ARCHITECTURES           = IA32 X64
>> +#  VALID_ARCHITECTURES           = IA32 X64 LOONGARCH64
>>   #
>>   
>>   [Sources.Ia32]
>> -  Ia32/ExceptionHandlerAsm.nasm
>> -  Ia32/ExceptionTssEntryAsm.nasm
>>     Ia32/ArchExceptionHandler.c
>>     Ia32/ArchInterruptDefs.h
>> +  Ia32/ExceptionHandlerAsm.nasm
>> +  Ia32/ExceptionTssEntryAsm.nasm
>>   
>>   [Sources.X64]
>> -  X64/SecPeiExceptionHandlerAsm.nasm
>>     X64/ArchExceptionHandler.c
>>     X64/ArchInterruptDefs.h
>> +  X64/SecPeiExceptionHandlerAsm.nasm
>>   
>> -[Sources.common]
>> +[Sources.Ia32, Sources.X64]
>>     CpuExceptionCommon.h
>>     CpuExceptionCommon.c
>>     SecPeiCpuException.c
>>   
>> +[Sources.LoongArch64]
>> +  LoongArch/ExceptionCommon.h
>> +  LoongArch/ExceptionCommon.c
>> +  LoongArch/SecPeiExceptionLib.c
>> +  LoongArch/LoongArch64/ArchExceptionHandler.c
>> +  LoongArch/LoongArch64/ExceptionHandlerAsm.S | GCC
>> +
>>   [Packages]
>>     MdePkg/MdePkg.dec
>>     MdeModulePkg/MdeModulePkg.dec
>>     UefiCpuPkg/UefiCpuPkg.dec
>>   
>> -[LibraryClasses]
>> +[LibraryClasses.common]
>>     BaseLib
>> -  SerialPortLib
>> -  PrintLib
>> -  LocalApicLib
>> +  CpuLib
>>     PeCoffGetEntryPointLib
>> +  PrintLib
>> +  SerialPortLib
>> +
>> +[LibraryClasses.Ia32, LibraryClasses.X64]
>>     CcExitLib
>> +  LocalApicLib
>>   
>>   [Pcd]
>>     gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard
>> diff --git a/UefiCpuPkg/UefiCpuPkg.dec b/UefiCpuPkg/UefiCpuPkg.dec
>> index 571b59b36f..f5febe46ba 100644
>> --- a/UefiCpuPkg/UefiCpuPkg.dec
>> +++ b/UefiCpuPkg/UefiCpuPkg.dec
>> @@ -3,6 +3,7 @@
>>   #
>>   # Copyright (c) 2007 - 2023, Intel Corporation. All rights reserved.<BR>
>>   # Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.<BR>
>> +# Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>>   #
>>   # SPDX-License-Identifier: BSD-2-Clause-Patent
>>   #
>> @@ -385,6 +386,10 @@
>>     # @Prompt Enable performance collecting when processor trace is enabled.
>>     gUefiCpuPkgTokenSpaceGuid.PcdCpuProcTracePerformanceCollecting|FALSE|BOOLEAN|0x60000020
>>   
>> +  ## This PCD Contains the pointer to a CPU exception vector base address.
>> +  # @Prompt The pointer to a CPU exception vector base address.
>> +  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress|0x0|UINT64|0x60000022
>> +
>>   [PcdsFixedAtBuild.X64, PcdsPatchableInModule.X64, PcdsDynamic.X64, PcdsDynamicEx.X64]
>>     ## Indicate access to non-SMRAM memory is restricted to reserved, runtime and ACPI NVS type after SmmReadyToLock.
>>     #  MMIO access is always allowed regardless of the value of this PCD.
> 


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114859): https://edk2.groups.io/g/devel/message/114859
Mute This Topic: https://groups.io/mt/104070164/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
       [not found]   ` <17AF511188DE2475.15701@groups.io>
@ 2024-01-31  5:32     ` Chao Li
  0 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-31  5:32 UTC (permalink / raw)
  To: devel, Ray Ni, Laszlo Ersek
  Cc: Eric Dong, Rahul Kumar, Gerd Hoffmann, Baoqi Zhang, Dongyan Qian,
	Xianglai Li, Bibo Mao

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

Hi Ray and Laszlo,

I would very much like to be merged into stable202302, the soft feature 
deadline is 2024-02-05, so could you please hlep to review this patch as 
soon as passable? Please...


Thanks,
Chao
On 2024/1/31 11:31, Chao Li wrote:
>
> Hi Ray,
>
> Can you please help to review this patch again?
>
> On 2024/1/26 14:29, Chao Li wrote:
>> Add a new library named CpuMmuLib and add a LoongArch64 instance with in
>> the library.
>> It provides two-stage MMU libraryinstances, PEI and DXE.
>>
>> BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>>
>> Cc: Eric Dong<eric.dong@intel.com>
>> Cc: Ray Ni<ray.ni@intel.com>
>> Cc: Laszlo Ersek<lersek@redhat.com>
>> Cc: Rahul Kumar<rahul1.kumar@intel.com>
>> Cc: Gerd Hoffmann<kraxel@redhat.com>
>> Signed-off-by: Chao Li<lichao@loongson.cn>
>> Co-authored-by: Baoqi Zhang<zhangbaoqi@loongson.cn>
>> Co-authored-by: Dongyan Qian<qiandongyan@loongson.cn>
>> Co-authored-by: Xianglai Li<lixianglai@loongson.cn>
>> Co-authored-by: Bibo Mao<maobibo@loongson.cn>
>> ---
>>   UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf |  36 +
>>   UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni |  14 +
>>   .../CpuMmuLib/LoongArch64/CommonMmuLib.c      | 988 ++++++++++++++++++
>>   .../CpuMmuLib/LoongArch64/CommonMmuLib.h      |  43 +
>>   .../Library/CpuMmuLib/LoongArch64/Page.h      | 279 +++++
>>   .../CpuMmuLib/LoongArch64/PeiCpuMmuLib.c      | 178 ++++
>>   .../Library/CpuMmuLib/LoongArch64/Tlb.h       |  48 +
>>   .../CpuMmuLib/LoongArch64/TlbOperation.S      |  44 +
>>   UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf |  44 +
>>   UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni |  14 +
>>   UefiCpuPkg/UefiCpuPkg.dsc                     |   4 +
>>   11 files changed, 1692 insertions(+)
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
>>
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>> new file mode 100644
>> index 0000000000..bfce3ce96d
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>> @@ -0,0 +1,36 @@
>> +## @file
>> +#  CPU Memory Map Unit DXE phase driver.
>> +#
>> +#  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                      = DxeCpuMmuLib
>> +  MODULE_UNI_FILE                = DxeCpuMmuLib.uni
>> +  FILE_GUID                      = DA8F0232-FB14-42F0-922C-63104D2C70BE
>> +  MODULE_TYPE                    = DXE_DRIVER
>> +  VERSION_STRING                 = 1.0
>> +  LIBRARY_CLASS                  = CpuMmuLib | DXE_DRIVER
>> +  CONSTRUCTOR                    = MmuInitialize
>> +
>> +#
>> +#  VALID_ARCHITECTURES           = LOONGARCH64
>> +#
>> +
>> +[Sources.LoongArch64]
>> +  LoongArch64/TlbOperation.S   | GCC
>> +  LoongArch64/CommonMmuLib.c
>> +  LoongArch64/Page.h
>> +  LoongArch64/Tlb.h
>> +
>> +[Packages]
>> +  MdePkg/MdePkg.dec
>> +  UefiCpuPkg/UefiCpuPkg.dec
>> +
>> +[LibraryClasses]
>> +  DebugLib
>> +  MemoryAllocationLib
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
>> new file mode 100644
>> index 0000000000..7342249516
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
>> @@ -0,0 +1,14 @@
>> +// /** @file
>> +// CPU Memory Manager Unit library instance for DXE modules.
>> +//
>> +// CPU Memory Manager Unit library instance for DXE modules.
>> +//
>> +// 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 "CPU Memory Manager Unit library instance for DXE modules."
>> +
>> +#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for DXE modules."
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>> new file mode 100644
>> index 0000000000..2e852c3371
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>> @@ -0,0 +1,988 @@
>> +/** @file
>> +
>> +  CPU Memory Map Unit Handler Library common functions.
>> +
>> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +  @par Glossary:
>> +    - Pgd or Pgd or PGD    - Page Global Directory
>> +    - Pud or Pud or PUD    - Page Upper Directory
>> +    - Pmd or Pmd or PMD    - Page Middle Directory
>> +    - Pte or pte or PTE    - Page Table Entry
>> +    - Val or VAL or val    - Value
>> +    - Dir    - Directory
>> +**/
>> +#include <Uefi.h>
>> +#include <Library/BaseLib.h>
>> +#include <Library/BaseMemoryLib.h>
>> +#include <Library/CpuMmuLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/MemoryAllocationLib.h>
>> +#include <Register/LoongArch64/Csr.h>
>> +#include "Tlb.h"
>> +#include "Page.h"
>> +
>> +#define SWAP_PAGE_DIR              CsrRead(LOONGARCH_CSR_PGDL)
>> +#define EFI_MEMORY_CACHETYPE_MASK  (EFI_MEMORY_UC  | \
>> +                                    EFI_MEMORY_WC  | \
>> +                                    EFI_MEMORY_WT  | \
>> +                                    EFI_MEMORY_WB  | \
>> +                                    EFI_MEMORY_UCE   \
>> +                                    )
>> +
>> +BOOLEAN  mMmuInited = FALSE;
>> +
>> +/**
>> +  Check to see if mmu successfully initializes.
>> +
>> +  @param  VOID.
>> +
>> +  @retval  TRUE  Initialization has been completed.
>> +           FALSE Initialization did not complete.
>> +**/
>> +STATIC
>> +BOOLEAN
>> +MmuIsInit (
>> +  VOID
>> +  )
>> +{
>> +  if (mMmuInited || (SWAP_PAGE_DIR != 0)) {
>> +    return TRUE;
>> +  }
>> +
>> +  return FALSE;
>> +}
>> +
>> +/**
>> +  Iterates through the page directory to initialize it.
>> +
>> +  @param  Dst  A pointer to the directory of the page to initialize.
>> +  @param  Num  The number of page directories to initialize.
>> +  @param  Src  A pointer to the data used to initialize the page directory.
>> +
>> +  @return VOID.
>> +**/
>> +STATIC
>> +VOID
>> +PageDirInit (
>> +  IN VOID   *Dst,
>> +  IN UINTN  Num,
>> +  IN VOID   *Src
>> +  )
>> +{
>> +  UINTN  *Ptr;
>> +  UINTN  *End;
>> +  UINTN  Entry;
>> +
>> +  Entry = (UINTN)Src;
>> +  Ptr   = (UINTN *)Dst;
>> +  End   = Ptr + Num;
>> +
>> +  for ( ; Ptr < End; Ptr++) {
>> +    *Ptr = Entry;
>> +  }
>> +
>> +  return;
>> +}
>> +
>> +/**
>> +  Gets the virtual address corresponding to the page global directory table entry.
>> +
>> +  @param  Address  the virtual address for the table entry.
>> +
>> +  @retval PGD A pointer to get the table item.
>> +**/
>> +STATIC
>> +PGD *
>> +PgdOffset (
>> +  IN UINTN  Address
>> +  )
>> +{
>> +  return (PGD *)(SWAP_PAGE_DIR) + PGD_INDEX (Address);
>> +}
>> +
>> +/**
>> +  Gets the virtual address corresponding to the page upper directory table entry.
>> +
>> +  @param  Pgd  A pointer to a page global directory table entry.
>> +  @param  Address  the virtual address for the table entry.
>> +
>> +  @retval PUD A pointer to get the table item.
>> +**/
>> +STATIC
>> +PUD *
>> +PudOffset (
>> +  IN PGD    *Pgd,
>> +  IN UINTN  Address
>> +  )
>> +{
>> +  UINTN  PgdVal;
>> +
>> +  PgdVal = (UINTN)PGD_VAL (*Pgd);
>> +
>> +  return (PUD *)PgdVal + PUD_INDEX (Address);
>> +}
>> +
>> +/**
>> +  Gets the virtual address corresponding to the page middle directory table entry.
>> +
>> +  @param  Pud  A pointer to a page upper directory table entry.
>> +  @param  Address  the virtual address for the table entry.
>> +
>> +  @retval PMD A pointer to get the table item.
>> +**/
>> +STATIC
>> +PMD *
>> +PmdOffset (
>> +  IN PUD    *Pud,
>> +  IN UINTN  Address
>> +  )
>> +{
>> +  UINTN  PudVal;
>> +
>> +  PudVal = PUD_VAL (*Pud);
>> +
>> +  return (PMD *)PudVal + PMD_INDEX (Address);
>> +}
>> +
>> +/**
>> +  Gets the virtual address corresponding to the page table entry.
>> +
>> +  @param  Pmd  A pointer to a page middle directory table entry.
>> +  @param  Address  the virtual address for the table entry.
>> +
>> +  @retval PTE A pointer to get the table item.
>> +**/
>> +STATIC
>> +PTE *
>> +PteOffset (
>> +  IN PMD    *Pmd,
>> +  IN UINTN  Address
>> +  )
>> +{
>> +  UINTN  PmdVal;
>> +
>> +  PmdVal = (UINTN)PMD_VAL (*Pmd);
>> +
>> +  return (PTE *)PmdVal + PTE_INDEX (Address);
>> +}
>> +
>> +/**
>> +  Sets the value of the page table entry.
>> +
>> +  @param  Pte  A pointer to a page table entry.
>> +  @param  PteVal  The value of the page table entry to set.
>> +
>> +**/
>> +STATIC
>> +VOID
>> +SetPte (
>> +  IN PTE  *Pte,
>> +  IN PTE  PteVal
>> +  )
>> +{
>> +  *Pte = PteVal;
>> +}
>> +
>> +/**
>> +  Sets the value of the page global directory.
>> +
>> +  @param  Pgd  A pointer to a page global directory.
>> +  @param  Pud  The value of the page global directory to set.
>> +
>> +**/
>> +STATIC
>> +VOID
>> +SetPgd (
>> +  IN PGD  *Pgd,
>> +  IN PUD  *Pud
>> +  )
>> +{
>> +  *Pgd = (PGD) {
>> +    ((UINTN)Pud)
>> +  };
>> +}
>> +
>> +/**
>> +  Sets the value of the page upper directory.
>> +
>> +  @param  Pud  A pointer to a page upper directory.
>> +  @param  Pmd  The value of the page upper directory to set.
>> +
>> +**/
>> +STATIC
>> +VOID
>> +SetPud (
>> +  IN PUD  *Pud,
>> +  IN PMD  *Pmd
>> +  )
>> +{
>> +  *Pud = (PUD) {
>> +    ((UINTN)Pmd)
>> +  };
>> +}
>> +
>> +/**
>> +  Sets the value of the page middle directory.
>> +
>> +  @param  Pmd  A pointer to a page middle directory.
>> +  @param  Pte  The value of the page middle directory to set.
>> +
>> +**/
>> +STATIC
>> +VOID
>> +SetPmd (
>> +  IN PMD  *Pmd,
>> +  IN PTE  *Pte
>> +  )
>> +{
>> +  *Pmd = (PMD) {
>> +    ((UINTN)Pte)
>> +  };
>> +}
>> +
>> +/**
>> +  Free up memory space occupied by page tables.
>> +
>> +  @param  Pte  A pointer to the page table.
>> +
>> +**/
>> +VOID
>> +PteFree (
>> +  IN PTE  *Pte
>> +  )
>> +{
>> +  FreePages ((VOID *)Pte, 1);
>> +}
>> +
>> +/**
>> +  Free up memory space occupied by page middle directory.
>> +
>> +  @param  Pmd  A pointer to the page middle directory.
>> +
>> +**/
>> +VOID
>> +PmdFree (
>> +  IN PMD  *Pmd
>> +  )
>> +{
>> +  FreePages ((VOID *)Pmd, 1);
>> +}
>> +
>> +/**
>> +  Free up memory space occupied by page upper directory.
>> +
>> +  @param  Pud  A pointer to the page upper directory.
>> +
>> +**/
>> +VOID
>> +PudFree (
>> +  IN PUD  *Pud
>> +  )
>> +{
>> +  FreePages ((VOID *)Pud, 1);
>> +}
>> +
>> +/**
>> +  Requests the memory space required for the page upper directory,
>> +  initializes it, and places it in the specified page global directory
>> +
>> +  @param  Pgd  A pointer to the page global directory.
>> +
>> +  @retval  EFI_SUCCESS  Memory request successful.
>> +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +PudAlloc (
>> +  IN PGD  *Pgd
>> +  )
>> +{
>> +  PUD  *Pud;
>> +
>> +  Pud = (PUD *)AllocatePages (1);
>> +  if (Pud == NULL) {
>> +    return EFI_OUT_OF_RESOURCES;
>> +  }
>> +
>> +  PageDirInit ((VOID *)Pud, ENTRYS_PER_PUD, (VOID *)INVALID_PAGE);
>> +
>> +  SetPgd (Pgd, Pud);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Requests the memory space required for the page middle directory,
>> +  initializes it, and places it in the specified page upper directory
>> +
>> +  @param  Pud  A pointer to the page upper directory.
>> +
>> +  @retval  EFI_SUCCESS  Memory request successful.
>> +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +PmdAlloc (
>> +  IN PUD  *Pud
>> +  )
>> +{
>> +  PMD  *Pmd;
>> +
>> +  Pmd = (PMD *)AllocatePages (1);
>> +  if (!Pmd) {
>> +    return EFI_OUT_OF_RESOURCES;
>> +  }
>> +
>> +  PageDirInit ((VOID *)Pmd, ENTRYS_PER_PMD, (VOID *)INVALID_PAGE);
>> +
>> +  SetPud (Pud, Pmd);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Requests the memory space required for the page table,
>> +  initializes it, and places it in the specified page middle directory
>> +
>> +  @param  Pmd  A pointer to the page middle directory.
>> +
>> +  @retval  EFI_SUCCESS  Memory request successful.
>> +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +PteAlloc (
>> +  IN PMD  *Pmd
>> +  )
>> +{
>> +  PTE  *Pte;
>> +
>> +  Pte = (PTE *)AllocatePages (1);
>> +  if (!Pte) {
>> +    return EFI_OUT_OF_RESOURCES;
>> +  }
>> +
>> +  Pte = ZeroMem (Pte, EFI_PAGE_SIZE);
>> +
>> +  SetPmd (Pmd, Pte);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Requests the memory space required for the page upper directory,
>> +  initializes it, and places it in the specified page global directory,
>> +  and get the page upper directory entry corresponding to the virtual address.
>> +
>> +  @param  Pgd      A pointer to the page global directory.
>> +  @param  Address  The corresponding virtual address of the page table entry.
>> +
>> +  @retval          A pointer to the page upper directory entry. Return NULL, if
>> +                   allocate the memory buffer is fail.
>> +**/
>> +STATIC
>> +PUD *
>> +PudAllocGet (
>> +  IN PGD    *Pgd,
>> +  IN UINTN  Address
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +
>> +  if (PGD_IS_EMPTY (*Pgd)) {
>> +    Status = PudAlloc (Pgd);
>> +    ASSERT_EFI_ERROR (Status);
>> +    if (EFI_ERROR (Status)) {
>> +      return NULL;
>> +    }
>> +  }
>> +
>> +  return PudOffset (Pgd, Address);
>> +}
>> +
>> +/**
>> +  Requests the memory space required for the page middle directory,
>> +  initializes it, and places it in the specified page upper directory,
>> +  and get the page middle directory entry corresponding to the virtual address.
>> +
>> +  @param  Pud      A pointer to the page upper directory.
>> +  @param  Address  The corresponding virtual address of the page table entry.
>> +
>> +  @retval          A pointer to the page middle directory entry. Return NULL, if
>> +                   allocate the memory buffer is fail.
>> +**/
>> +STATIC
>> +PMD *
>> +PmdAllocGet (
>> +  IN PUD    *Pud,
>> +  IN UINTN  Address
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +
>> +  if (PUD_IS_EMPTY (*Pud)) {
>> +    Status = PmdAlloc (Pud);
>> +    ASSERT_EFI_ERROR (Status);
>> +    if (EFI_ERROR (Status)) {
>> +      return NULL;
>> +    }
>> +  }
>> +
>> +  return PmdOffset (Pud, Address);
>> +}
>> +
>> +/**
>> +  Requests the memory space required for the page table,
>> +  initializes it, and places it in the specified page middle directory,
>> +  and get the page table entry corresponding to the virtual address.
>> +
>> +  @param  Pmd      A pointer to the page upper directory.
>> +  @param  Address  The corresponding virtual address of the page table entry.
>> +
>> +  @retval          A pointer to the page table entry. Return NULL, if allocate
>> +                   the memory buffer is fail.
>> +**/
>> +STATIC
>> +PTE *
>> +PteAllocGet (
>> +  IN PMD    *Pmd,
>> +  IN UINTN  Address
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +
>> +  if (PMD_IS_EMPTY (*Pmd)) {
>> +    Status = PteAlloc (Pmd);
>> +    ASSERT_EFI_ERROR (Status);
>> +    if (EFI_ERROR (Status)) {
>> +      return NULL;
>> +    }
>> +  }
>> +
>> +  return PteOffset (Pmd, Address);
>> +}
>> +
>> +/**
>> +  Gets the physical address of the page table entry corresponding to the specified virtual address.
>> +
>> +  @param  Address  The corresponding virtual address of the page table entry.
>> +
>> +  @retval  A pointer to the page table entry.
>> +  @retval  NULL
>> +**/
>> +STATIC
>> +PTE *
>> +GetPteAddress (
>> +  IN UINTN  Address
>> +  )
>> +{
>> +  PGD  *Pgd;
>> +  PUD  *Pud;
>> +  PMD  *Pmd;
>> +
>> +  Pgd = PgdOffset (Address);
>> +
>> +  if (PGD_IS_EMPTY (*Pgd)) {
>> +    return NULL;
>> +  }
>> +
>> +  Pud = PudOffset (Pgd, Address);
>> +
>> +  if (PUD_IS_EMPTY (*Pud)) {
>> +    return NULL;
>> +  }
>> +
>> +  Pmd = PmdOffset (Pud, Address);
>> +  if (PMD_IS_EMPTY (*Pmd)) {
>> +    return NULL;
>> +  }
>> +
>> +  if (IS_HUGE_PAGE (Pmd->PmdVal)) {
>> +    return ((PTE *)Pmd);
>> +  }
>> +
>> +  return PteOffset (Pmd, Address);
>> +}
>> +
>> +/**
>> +  Gets the Attributes of Huge Page.
>> +
>> +  @param  Pmd  A pointer to the page middle directory.
>> +
>> +  @retval     Value of Attributes.
>> +**/
>> +STATIC
>> +UINTN
>> +GetHugePageAttributes (
>> +  IN  PMD  *Pmd
>> +  )
>> +{
>> +  UINTN  Attributes;
>> +  UINTN  GlobalFlag;
>> +  UINTN  HugeVal;
>> +
>> +  HugeVal     = PMD_VAL (*Pmd);
>> +  Attributes  = HugeVal & (~HUGEP_PAGE_MASK);
>> +  GlobalFlag  = ((Attributes & (1 << PAGE_HGLOBAL_SHIFT)) >> PAGE_HGLOBAL_SHIFT) << PAGE_GLOBAL_SHIFT;
>> +  Attributes &= ~(1 << PAGE_HGLOBAL_SHIFT);
>> +  Attributes |= GlobalFlag;
>> +  return Attributes;
>> +}
>> +
>> +/**
>> +  Establishes a page table entry based on the specified memory region.
>> +
>> +  @param  Pmd  A pointer to the page middle directory.
>> +  @param  Address  The memory space start address.
>> +  @param  End  The end address of the memory space.
>> +  @param  Attributes  Memory space Attributes.
>> +
>> +  @retval     EFI_SUCCESS   The page table entry was created successfully.
>> +  @retval     EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +MemoryMapPteRange (
>> +  IN PMD    *Pmd,
>> +  IN UINTN  Address,
>> +  IN UINTN  End,
>> +  IN UINTN  Attributes
>> +  )
>> +{
>> +  PTE      *Pte;
>> +  PTE      PteVal;
>> +  BOOLEAN  UpDate;
>> +
>> +  Pte = PteAllocGet (Pmd, Address);
>> +  if (!Pte) {
>> +    return EFI_OUT_OF_RESOURCES;
>> +  }
>> +
>> +  DEBUG ((
>> +    DEBUG_INFO,
>> +    "%a %d Address %p End %p  Attributes %llx\n",
>> +    __func__,
>> +    __LINE__,
>> +    Address,
>> +    End,
>> +    Attributes
>> +    ));
>> +
>> +  do {
>> +    UpDate = FALSE;
>> +    PteVal = MAKE_PTE (Address, Attributes);
>> +
>> +    if ((!PTE_IS_EMPTY (*Pte)) &&
>> +        (PTE_VAL (*Pte) != PTE_VAL (PteVal)))
>> +    {
>> +      UpDate = TRUE;
>> +    }
>> +
>> +    SetPte (Pte, PteVal);
>> +    if (UpDate) {
>> +      InvalidTlb (Address);
>> +    }
>> +  } while (Pte++, Address += EFI_PAGE_SIZE, Address != End);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Convert Huge Page to Page.
>> +
>> +  @param  Pmd  A pointer to the page middle directory.
>> +  @param  Address  The memory space start address.
>> +  @param  End  The end address of the memory space.
>> +  @param  Attributes  Memory space Attributes.
>> +
>> +  @retval  EFI_SUCCESS   The page table entry was created successfully.
>> +  @retval  EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +ConvertHugePageToPage (
>> +  IN  PMD   *Pmd,
>> +  IN UINTN  Address,
>> +  IN UINTN  End,
>> +  IN UINTN  Attributes
>> +  )
>> +{
>> +  UINTN       OldAttributes;
>> +  UINTN       HugePageEnd;
>> +  UINTN       HugePageStart;
>> +  EFI_STATUS  Status;
>> +
>> +  Status = EFI_SUCCESS;
>> +
>> +  if ((PMD_IS_EMPTY (*Pmd)) ||
>> +      (!IS_HUGE_PAGE (Pmd->PmdVal)))
>> +  {
>> +    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
>> +  } else {
>> +    OldAttributes = GetHugePageAttributes (Pmd);
>> +    if (Attributes == OldAttributes) {
>> +      return Status;
>> +    }
>> +
>> +    SetPmd (Pmd, (PTE *)(INVALID_PAGE));
>> +    HugePageStart = Address & PMD_MASK;
>> +    HugePageEnd   = HugePageStart + HUGE_PAGE_SIZE;
>> +    ASSERT (HugePageEnd >= End);
>> +
>> +    if (Address > HugePageStart) {
>> +      Status |= MemoryMapPteRange (Pmd, HugePageStart, Address, OldAttributes);
>> +    }
>> +
>> +    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
>> +
>> +    if (End < HugePageEnd) {
>> +      Status |= MemoryMapPteRange (Pmd, End, HugePageEnd, OldAttributes);
>> +    }
>> +  }
>> +
>> +  return Status;
>> +}
>> +
>> +/**
>> +  Establishes a page middle directory based on the specified memory region.
>> +
>> +  @param  Pud  A pointer to the page upper directory.
>> +  @param  Address  The memory space start address.
>> +  @param  End  The end address of the memory space.
>> +  @param  Attributes  Memory space Attributes.
>> +
>> +  @retval     EFI_SUCCESS   The page middle directory was created successfully.
>> +  @retval     EFI_OUT_OF_RESOURCES  Page middle directory establishment failed due to resource exhaustion.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +MemoryMapPmdRange (
>> +  IN PUD    *Pud,
>> +  IN UINTN  Address,
>> +  IN UINTN  End,
>> +  IN UINTN  Attributes
>> +  )
>> +{
>> +  PMD      *Pmd;
>> +  UINTN    Next;
>> +  PTE      PteVal;
>> +  BOOLEAN  UpDate;
>> +
>> +  Pmd = PmdAllocGet (Pud, Address);
>> +  if (Pmd == NULL) {
>> +    return EFI_OUT_OF_RESOURCES;
>> +  }
>> +
>> +  do {
>> +    Next = PMD_ADDRESS_END (Address, End);
>> +    if (((Address & (~PMD_MASK)) == 0) &&
>> +        ((Next &  (~PMD_MASK)) == 0) &&
>> +        (PMD_IS_EMPTY (*Pmd) || IS_HUGE_PAGE (Pmd->PmdVal)))
>> +    {
>> +      UpDate = FALSE;
>> +      PteVal = MAKE_HUGE_PTE (Address, Attributes);
>> +
>> +      if ((!PMD_IS_EMPTY (*Pmd)) &&
>> +          (PMD_VAL (*Pmd) != PTE_VAL (PteVal)))
>> +      {
>> +        UpDate = TRUE;
>> +      }
>> +
>> +      SetPmd (Pmd, (PTE *)PteVal.PteVal);
>> +      if (UpDate) {
>> +        InvalidTlb (Address);
>> +      }
>> +    } else {
>> +      ConvertHugePageToPage (Pmd, Address, Next, Attributes);
>> +    }
>> +  } while (Pmd++, Address = Next, Address != End);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Establishes a page upper directory based on the specified memory region.
>> +
>> +  @param  Pgd  A pointer to the page global directory.
>> +  @param  Address  The memory space start address.
>> +  @param  End  The end address of the memory space.
>> +  @param  Attributes  Memory space Attributes.
>> +
>> +  @retval     EFI_SUCCESS   The page upper directory was created successfully.
>> +  @retval     EFI_OUT_OF_RESOURCES  Page upper directory establishment failed due to resource exhaustion.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +MemoryMapPudRange (
>> +  IN PGD    *Pgd,
>> +  IN UINTN  Address,
>> +  IN UINTN  End,
>> +  IN UINTN  Attributes
>> +  )
>> +{
>> +  PUD    *Pud;
>> +  UINTN  Next;
>> +
>> +  Pud = PudAllocGet (Pgd, Address);
>> +  if (Pud == NULL) {
>> +    return EFI_OUT_OF_RESOURCES;
>> +  }
>> +
>> +  do {
>> +    Next = PUD_ADDRESS_END (Address, End);
>> +    if (EFI_ERROR (MemoryMapPmdRange (Pud, Address, Next, Attributes))) {
>> +      return EFI_OUT_OF_RESOURCES;
>> +    }
>> +  } while (Pud++, Address = Next, Address != End);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Establishes a page global directory based on the specified memory region.
>> +
>> +  @param  Start  The memory space start address.
>> +  @param  End  The end address of the memory space.
>> +  @param  Attributes  Memory space Attributes.
>> +
>> +  @retval     EFI_SUCCESS   The page global directory was created successfully.
>> +  @retval     EFI_OUT_OF_RESOURCES  Page global directory establishment failed due to resource exhaustion.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +MemoryMapPageRange (
>> +  IN UINTN  Start,
>> +  IN UINTN  End,
>> +  IN UINTN  Attributes
>> +  )
>> +{
>> +  PGD         *Pgd;
>> +  UINTN       Next;
>> +  UINTN       Address;
>> +  EFI_STATUS  Err;
>> +
>> +  Address = Start;
>> +
>> +  /* Get PGD(PTE PMD PUD PGD) in PageTables */
>> +  Pgd = PgdOffset (Address);
>> +  do {
>> +    Next = PGD_ADDRESS_END (Address, End);
>> +    /* Get Next Align Page to Map */
>> +    Err = MemoryMapPudRange (Pgd, Address, Next, Attributes);
>> +    if (Err) {
>> +      return Err;
>> +    }
>> +  } while (Pgd++, Address = Next, Address != End);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Page tables are established from memory-mapped tables.
>> +
>> +  @param  MemoryRegion   A pointer to a memory-mapped table entry.
>> +
>> +  @retval     EFI_SUCCESS   The page table was created successfully.
>> +  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
>> +**/
>> +EFI_STATUS
>> +FillTranslationTable (
>> +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
>> +  )
>> +{
>> +  return MemoryMapPageRange (
>> +           MemoryRegion->VirtualBase,
>> +           (MemoryRegion->Length + MemoryRegion->VirtualBase),
>> +           MemoryRegion->Attributes
>> +           );
>> +}
>> +
>> +/**
>> +  Convert EFI Attributes to Loongarch Attributes.
>> +
>> +  @param[in]  EfiAttributes     Efi Attributes.
>> +
>> +  @retval  Corresponding architecture attributes.
>> +**/
>> +UINTN
>> +EFIAPI
>> +EfiAttributeConverse (
>> +  IN UINTN  EfiAttributes
>> +  )
>> +{
>> +  UINTN  LoongArchAttributes;
>> +
>> +  LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | PLV_KERNEL | PAGE_GLOBAL;
>> +
>> +  switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
>> +    case EFI_MEMORY_UC:
>> +      LoongArchAttributes |= CACHE_SUC;
>> +      break;
>> +    case EFI_MEMORY_WC:
>> +      LoongArchAttributes |= CACHE_WUC;
>> +      break;
>> +    case EFI_MEMORY_WT:
>> +    case EFI_MEMORY_WB:
>> +      LoongArchAttributes |= CACHE_CC;
>> +      break;
>> +    default:
>> +      LoongArchAttributes |= CACHE_CC;
>> +      break;
>> +  }
>> +
>> +  // Write protection attributes
>> +  if (((EfiAttributes & EFI_MEMORY_RO) != 0) ||
>> +      ((EfiAttributes & EFI_MEMORY_WP) != 0))
>> +  {
>> +    LoongArchAttributes &= ~PAGE_DIRTY;
>> +  }
>> +
>> +  if ((EfiAttributes & EFI_MEMORY_RP) != 0) {
>> +    LoongArchAttributes |= PAGE_NO_READ;
>> +  }
>> +
>> +  // eXecute protection attribute
>> +  if ((EfiAttributes & EFI_MEMORY_XP) != 0) {
>> +    LoongArchAttributes |= PAGE_NO_EXEC;
>> +  }
>> +
>> +  return LoongArchAttributes;
>> +}
>> +
>> +/**
>> +  Finds the first of the length and memory properties of the memory region corresponding
>> +  to the specified base address.
>> +
>> +  @param[in]       BaseAddress       To find the base address of the memory region.
>> +  @param[in, out]  RegionLength      Pointer holding:
>> +                                      - At entry, the length of the memory region
>> +                                        expected to be found.
>> +                                      - At exit, the length of the memory region found.
>> +  @param[out]      RegionAttributes  Properties of the memory region found.
>> +
>> +  @retval  EFI_SUCCESS           The corresponding memory area was successfully found
>> +           EFI_NOT_FOUND         No memory area found
>> +           EFI_OUT_OF_RESOURCES  Base address or expected memory region exceeds the maximum
>> +                                 address.
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +GetMemoryRegionAttributes (
>> +  IN     UINTN  BaseAddress,
>> +  IN OUT UINTN  *RegionLength,
>> +  OUT    UINTN  *RegionAttributes
>> +  )
>> +{
>> +  PTE    *Pte;
>> +  UINTN  Attributes;
>> +  UINTN  AttributesTmp;
>> +  UINTN  MaxAddress;
>> +  UINTN  EndAddress;
>> +  UINTN  AddSize;
>> +
>> +  if (!MmuIsInit ()) {
>> +    return EFI_UNSUPPORTED;
>> +  }
>> +
>> +  EndAddress = BaseAddress + *RegionLength;
>> +  MaxAddress = LShiftU64 (1ULL, MAX_VA_BITS) - 1;
>> +
>> +  // Clean the value to prepare output to find region size.
>> +  *RegionLength = 0x0;
>> +
>> +  if ((BaseAddress >= MaxAddress) || (EndAddress >= MaxAddress)) {
>> +    return EFI_OUT_OF_RESOURCES;
>> +  }
>> +
>> +  Pte = GetPteAddress (BaseAddress);
>> +
>> +  if (Pte == NULL) {
>> +    return EFI_NOT_FOUND;
>> +  }
>> +
>> +  Attributes = GET_PAGE_ATTRIBUTES (*Pte);
>> +  if (IS_HUGE_PAGE (Pte->PteVal)) {
>> +    *RegionAttributes = Attributes & (~(PAGE_HUGE));
>> +  } else {
>> +    *RegionAttributes = Attributes;
>> +  }
>> +
>> +  do {
>> +    Pte = GetPteAddress (BaseAddress);
>> +    if (Pte == NULL) {
>> +      return EFI_SUCCESS;
>> +    }
>> +
>> +    AttributesTmp = GET_PAGE_ATTRIBUTES (*Pte);
>> +    if (AttributesTmp == Attributes) {
>> +      if (IS_HUGE_PAGE (Pte->PteVal)) {
>> +        AddSize = HUGE_PAGE_SIZE;
>> +      } else {
>> +        AddSize = EFI_PAGE_SIZE;
>> +      }
>> +
>> +      *RegionLength += AddSize;
>> +      BaseAddress   += AddSize;
>> +    } else {
>> +      return EFI_SUCCESS;
>> +    }
>> +  } while (BaseAddress <= EndAddress);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Sets the Attributes  of the specified memory region
>> +
>> +  @param[in]  BaseAddress    The base address of the memory region to set the Attributes.
>> +  @param[in]  Length         The length of the memory region to set the Attributes.
>> +  @param[in]  Attributes     The Attributes to be set.
>> +  @param[in]  AttributeMask  Mask of memory attributes to take into account.
>> +
>> +  @retval  EFI_SUCCESS    The Attributes was set successfully
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +SetMemoryRegionAttributes (
>> +  IN EFI_PHYSICAL_ADDRESS  BaseAddress,
>> +  IN UINTN                 Length,
>> +  IN UINTN                 Attributes,
>> +  IN UINT64                AttributeMask
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +
>> +  if (!MmuIsInit ()) {
>> +    return EFI_UNSUPPORTED;
>> +  }
>> +
>> +  Attributes = EfiAttributeConverse (Attributes);
>> +  Status     = MemoryMapPageRange (BaseAddress, BaseAddress + Length, Attributes);
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  return Status;
>> +}
>> +
>> +/**
>> +  Check to see if mmu successfully initializes and saves the result.
>> +
>> +  @param[in]  ImageHandle  The firmware allocated handle for the EFI image.
>> +  @param[in]  SystemTable  A pointer to the EFI System Table.
>> +
>> +  @retval  RETURN_SUCCESS    Initialization succeeded.
>> +**/
>> +RETURN_STATUS
>> +MmuInitialize (
>> +  IN EFI_HANDLE        ImageHandle,
>> +  IN EFI_SYSTEM_TABLE  *SystemTable
>> +  )
>> +{
>> +  if (SWAP_PAGE_DIR != 0) {
>> +    mMmuInited = TRUE;
>> +  }
>> +
>> +  return RETURN_SUCCESS;
>> +}
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
>> new file mode 100644
>> index 0000000000..d8c922c8fa
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
>> @@ -0,0 +1,43 @@
>> +/** @file
>> +
>> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +  @par Glossary:
>> +    - Dir    - Directory
>> +**/
>> +
>> +#ifndef  MMU_LIB_CORE_H_
>> +#define  MMU_LIB_CORE_H_
>> +
>> +/**
>> +  Iterates through the page directory to initialize it.
>> +
>> +  @param  Dst  A pointer to the directory of the page to initialize.
>> +  @param  Num  The number of page directories to initialize.
>> +  @param  Src  A pointer to the data used to initialize the page directory.
>> +
>> +  @retval VOID.
>> +**/
>> +VOID
>> +PageDirInit (
>> +  IN VOID   *dest,
>> +  IN UINTN  Count,
>> +  IN VOID   *src
>> +  );
>> +
>> +/**
>> +  Page tables are established from memory-mapped tables.
>> +
>> +  @param  MemoryRegion   A pointer to a memory-mapped table entry.
>> +
>> +  @retval     EFI_SUCCESS   The page table was created successfully.
>> +  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
>> +**/
>> +EFI_STATUS
>> +FillTranslationTable (
>> +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
>> +  );
>> +
>> +#endif // MMU_LIB_CORE_H_
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
>> new file mode 100644
>> index 0000000000..bac4f52327
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
>> @@ -0,0 +1,279 @@
>> +/** @file
>> +
>> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +  @par Glossary:
>> +    - Pgd or Pgd or PGD    - Page Global Directory
>> +    - Pud or Pud or PUD    - Page Upper Directory
>> +    - Pmd or Pmd or PMD    - Page Middle Directory
>> +    - Pte or pte or PTE    - Page Table Entry
>> +    - Val or VAL or val    - Value
>> +    - Dir    - Directory
>> +**/
>> +
>> +#ifndef PAGE_H_
>> +#define PAGE_H_
>> +
>> +#include <Library/CpuMmuLib.h>
>> +
>> +#define MAX_VA_BITS  47
>> +#define PGD_WIDE     (8)
>> +#define PUD_WIDE     (9)
>> +#define PMD_WIDE     (9)
>> +#define PTE_WIDE     (9)
>> +
>> +#define ENTRYS_PER_PGD  (1 << PGD_WIDE)
>> +#define ENTRYS_PER_PUD  (1 << PUD_WIDE)
>> +#define ENTRYS_PER_PMD  (1 << PMD_WIDE)
>> +#define ENTRYS_PER_PTE  (1 << PTE_WIDE)
>> +
>> +#define PGD_SHIFT  (PUD_SHIFT + PUD_WIDE)
>> +#define PUD_SHIFT  (PMD_SHIFT + PMD_WIDE)
>> +#define PMD_SHIFT  (EFI_PAGE_SHIFT + PTE_WIDE)
>> +#define PTE_SHIFT  (EFI_PAGE_SHIFT)
>> +
>> +#define PGD_SIZE  (1UL << PGD_SHIFT)
>> +#define PUD_SIZE  (1UL << PUD_SHIFT)
>> +#define PMD_SIZE  (1UL << PMD_SHIFT)
>> +
>> +#define PGD_MASK   (~(PGD_SIZE-1))
>> +#define PUD_MASK   (~(PUD_SIZE-1))
>> +#define PMD_MASK   (~(PMD_SIZE-1))
>> +#define PAGE_MASK  (~(EFI_PAGE_SIZE - 1))
>> +#define PFN_MASK   (~(((UINTN)(1) << (EFI_PAGE_SHIFT)) - 1) & \
>> +                   (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
>> +
>> +#define HUGEP_PAGE_MASK  (~(((UINTN)(1) << (PMD_SHIFT)) - 1) & \
>> +                          (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
>> +
>> +#define INVALID_PAGE  0
>> +
>> +typedef struct {
>> +  UINTN    PgdVal;
>> +} PGD;
>> +typedef struct {
>> +  UINTN    PudVal;
>> +} PUD;
>> +typedef struct {
>> +  UINTN    PmdVal;
>> +} PMD;
>> +typedef struct {
>> +  UINTN    PteVal;
>> +} PTE;
>> +
>> +/**
>> +  Gets the value of the page global directory table entry.
>> +
>> +  @param  x    Page global directory struct variables.
>> +
>> +  @retval   the value of the page global directory table entry.
>> + **/
>> +#define PGD_VAL(x)  ((x).PgdVal)
>> +
>> +/**
>> +  Gets the value of the page upper directory table entry.
>> +
>> +  @param  x    Page upper directory struct variables.
>> +
>> +  @retval  the value of the page upper directory table entry.
>> + **/
>> +#define PUD_VAL(x)  ((x).PudVal)
>> +
>> +/**
>> +  Gets the value of the page middle directory table entry.
>> +
>> +  @param  x    Page middle directory struct variables.
>> +
>> +  @retval  the value of the page middle directory table entry.
>> + **/
>> +#define PMD_VAL(x)  ((x).PmdVal)
>> +
>> +/**
>> +  Gets the value of the page table entry.
>> +
>> +  @param  x    Page table entry struct variables.
>> +
>> +  @retval  the value of the page table entry.
>> + **/
>> +#define PTE_VAL(x)  ((x).PteVal)
>> +
>> +#define PGD_TABLE_SIZE  (ENTRYS_PER_PGD * sizeof(PGD))
>> +#define PUD_TABLE_SIZE  (ENTRYS_PER_PUD * sizeof(PUD))
>> +#define PMD_TABLE_SIZE  (ENTRYS_PER_PMD * sizeof(PMD))
>> +#define PTE_TABLE_SIZE  (ENTRYS_PER_PTE * sizeof(PTE))
>> +
>> +/**
>> +  Gets the physical address of the record in the page table entry.
>> +
>> +  @param  x    Page table entry struct variables.
>> +
>> +  @retval  the value of the physical address.
>> + **/
>> +#define GET_PAGE_ATTRIBUTES(x)  (UINTN) {(PTE_VAL(x) & ~PFN_MASK)}
>> +
>> +/**
>> +  Gets the virtual address of the next block of the specified virtual address
>> +  that is aligned with the size of the global page directory mapping.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +  @param  End    The end address of the memory region.
>> +
>> +  @retval   the specified virtual address  of the next block.
>> + **/
>> +#define PGD_ADDRESS_END(Address, End)                  \
>> +({                                                     \
>> +  UINTN Boundary = ((Address) + PGD_SIZE) & PGD_MASK;  \
>> +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
>> +})
>> +
>> +/**
>> +  Gets the virtual address of the next block of the specified virtual address
>> +  that is aligned with the size of the page upper directory mapping.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +  @param  End    The end address of the memory region.
>> +
>> +  @retval   the specified virtual address  of the next block.
>> + **/
>> +#define PUD_ADDRESS_END(Address, End)                  \
>> +({                                                     \
>> +  UINTN Boundary = ((Address) + PUD_SIZE) & PUD_MASK;  \
>> +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
>> +})
>> +
>> +/**
>> +  Gets the virtual address of the next block of the specified virtual address
>> +  that is aligned with the size of the page middle directory mapping.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +  @param  End    The end address of the memory region.
>> +
>> +  @retval   the specified virtual address  of the next block.
>> + **/
>> +#define PMD_ADDRESS_END(Address, End)                  \
>> +({                                                     \
>> +  UINTN Boundary = ((Address) + PMD_SIZE) & PMD_MASK;  \
>> +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
>> +})
>> +
>> +/**
>> +  Get Specifies the virtual address corresponding to the index of the page global directory table entry.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +
>> +  @retval   the index of the page global directory table entry.
>> + **/
>> +#define PGD_INDEX(Address)  (((Address) >> PGD_SHIFT) & (ENTRYS_PER_PGD-1))
>> +
>> +/**
>> +  Get Specifies the virtual address corresponding to the index of the page upper directory table entry.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +  @param  End    The end address of the memory region.
>> +
>> +  @retval   the index of the page upper directory table entry.
>> + **/
>> +#define PUD_INDEX(Address)  (((Address) >> PUD_SHIFT) & (ENTRYS_PER_PUD - 1))
>> +
>> +/**
>> +  Get Specifies the virtual address corresponding to the index of the page middle directory table entry.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +
>> +  @retval   the index of the page middle directory table entry.
>> + **/
>> +#define PMD_INDEX(Address)  (((Address) >> PMD_SHIFT) & (ENTRYS_PER_PMD - 1))
>> +
>> +/**
>> +  Get Specifies the virtual address corresponding to the index of the page table entry.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +
>> +  @retval   the index of the page table entry.
>> + **/
>> +#define PTE_INDEX(Address)  (((Address) >> EFI_PAGE_SHIFT) & (ENTRYS_PER_PTE - 1))
>> +
>> +/**
>> +  Calculates the value of the page table entry based on the specified virtual address and properties.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +  @param  Attributes  Specifies the Attributes.
>> +
>> +  @retval    the value of the page table entry.
>> + **/
>> +#define MAKE_PTE(Address, Attributes)  (PTE){((((Address) >> EFI_PAGE_SHIFT) << 12) | (Attributes))}
>> +
>> +/**
>> +  Get Global bit from Attributes
>> +
>> +  @param  Attributes  Specifies the Attributes.
>> + * */
>> +#define GET_GLOBALBIT(Attributes)  ((Attributes & PAGE_GLOBAL) >> PAGE_GLOBAL_SHIFT)
>> +
>> +/**
>> +  Calculates the value of the Huge page table entry based on the specified virtual address and properties.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +  @param  Attributes  Specifies the Attributes.
>> +
>> +  @retval    the value of the HUGE page table entry.
>> + **/
>> +#define MAKE_HUGE_PTE(Address, Attributes)  (PTE){(((((Address) >> PMD_SHIFT) << PMD_SHIFT) | \
>> +                                             ((Attributes) | (GET_GLOBALBIT(Attributes) << PAGE_HGLOBAL_SHIFT) | \
>> +                                             PAGE_HUGE)))}
>> +
>> +/**
>> +  Check whether the large page table entry is.
>> +
>> +  @param  Val The value of the page table entry.
>> +
>> +  @retval    1   Is huge page table entry.
>> +  @retval    0   Isn't huge page table entry.
>> +**/
>> +#define IS_HUGE_PAGE(Val)  ((((Val) & PAGE_HUGE) == PAGE_HUGE) && \
>> +                            (((Val) & PAGE_HGLOBAL) == PAGE_HGLOBAL))
>> +
>> +#define HUGE_PAGE_SIZE  (PMD_SIZE)
>> +
>> +/**
>> +  Check that the global page directory table entry is empty.
>> +
>> +  @param  pgd   the global page directory struct variables.
>> +
>> +  @retval    1   The page table is invalid.
>> +  @retval    0   The page table is valid.
>> +**/
>> +#define PGD_IS_EMPTY(Val)  (PGD_VAL(Val) == INVALID_PAGE)
>> +
>> +/**
>> +  Check that the page upper directory table entry is empty.
>> +
>> +  @param  pud   Page upper directory struct variables.
>> +
>> +  @retval    1   The page table is invalid.
>> +  @retval    0   The page table is valid.
>> +**/
>> +#define PUD_IS_EMPTY(Val)  (PUD_VAL(Val) == INVALID_PAGE)
>> +
>> +/**
>> +  Check that the page middle directory table entry is empty.
>> +
>> +  @param  pmd   Page middle directory struct variables.
>> +
>> +  @retval    1   The page table is invalid.
>> +  @retval    0   The page table is valid.
>> +**/
>> +#define PMD_IS_EMPTY(Val)  (PMD_VAL(Val) == INVALID_PAGE)
>> +
>> +/**
>> +  Check that the page the page table entry is empty.
>> +
>> +  @param  pte   Page table entry struct variables.
>> +
>> +  @retval    1   The page table is invalid.
>> +  @retval    0   The page table is valid.
>> +**/
>> +#define PTE_IS_EMPTY(Val)  (!(PTE_VAL(Val) & (~PAGE_VALID)))
>> +#endif // PAGE_H_
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
>> new file mode 100644
>> index 0000000000..c214e8d847
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
>> @@ -0,0 +1,178 @@
>> +/** @file
>> +  CPU Memory Map Unit PEI phase driver.
>> +
>> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +  @par Glossary:
>> +    - Tlb      - Translation Lookaside Buffer
>> +**/
>> +
>> +#include <Uefi.h>
>> +#include <Library/BaseLib.h>
>> +#include <Library/BaseMemoryLib.h>
>> +#include <Library/CacheMaintenanceLib.h>
>> +#include <Library/CpuMmuLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/MemoryAllocationLib.h>
>> +#include <Library/PcdLib.h>
>> +#include <Register/LoongArch64/Csr.h>
>> +
>> +#include "Page.h"
>> +#include "Tlb.h"
>> +#include "CommonMmuLib.h"
>> +
>> +//
>> +// For coding convenience, define the maximum valid
>> +// LoongArch exception.
>> +// Since UEFI V2.11, it will be present in DebugSupport.h.
>> +//
>> +#define MAX_LOONGARCH_EXCEPTION  64
>> +
>> +/**
>> +  Create a page table and initialize the memory management unit(MMU).
>> +
>> +  @param[in]   MemoryTable           A pointer to a memory ragion table.
>> +  @param[out]  TranslationTableBase  A pointer to a translation table base address.
>> +  @param[out]  TranslationTableSize  A pointer to a translation table base size.
>> +
>> +  @retval  EFI_SUCCESS                Configure MMU successfully.
>> +           EFI_INVALID_PARAMETER      MemoryTable is NULL.
>> +           EFI_UNSUPPORTED            Out of memory space or size not aligned.
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +ConfigureMemoryManagementUnit (
>> +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryTable,
>> +  OUT VOID                      **TranslationTableBase OPTIONAL,
>> +  OUT UINTN                     *TranslationTableSize  OPTIONAL
>> +  )
>> +{
>> +  PGD            *SwapperPageDir;
>> +  UINTN          PgdShift;
>> +  UINTN          PgdWide;
>> +  UINTN          PudShift;
>> +  UINTN          PudWide;
>> +  UINTN          PmdShift;
>> +  UINTN          PmdWide;
>> +  UINTN          PteShift;
>> +  UINTN          PteWide;
>> +  UINTN          Length;
>> +  UINTN          TlbReEntry;
>> +  UINTN          TlbReEntryOffset;
>> +  UINTN          Remaining;
>> +  RETURN_STATUS  Status;
>> +
>> +  SwapperPageDir = NULL;
>> +  PgdShift       = PGD_SHIFT;
>> +  PgdWide        = PGD_WIDE;
>> +  PudShift       = PUD_SHIFT;
>> +  PudWide        = PUD_WIDE;
>> +  PmdShift       = PMD_SHIFT;
>> +  PmdWide        = PMD_WIDE;
>> +  PteShift       = PTE_SHIFT;
>> +  PteWide        = PTE_WIDE;
>> +
>> +  if (MemoryTable == NULL) {
>> +    ASSERT (MemoryTable != NULL);
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  SwapperPageDir = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
>> +  ZeroMem (SwapperPageDir, PGD_TABLE_SIZE);
>> +
>> +  if (SwapperPageDir == NULL) {
>> +    goto FreeTranslationTable;
>> +  }
>> +
>> +  CsrWrite (LOONGARCH_CSR_PGDL, (UINTN)SwapperPageDir);
>> +
>> +  while (MemoryTable->Length != 0) {
>> +    DEBUG ((
>> +      DEBUG_INFO,
>> +      "%a %d VirtualBase %p VirtualEnd %p Attributes %p .\n",
>> +      __func__,
>> +      __LINE__,
>> +      MemoryTable->VirtualBase,
>> +      (MemoryTable->Length + MemoryTable->VirtualBase),
>> +      MemoryTable->Attributes
>> +      ));
>> +
>> +    Status = FillTranslationTable (MemoryTable);
>> +    if (EFI_ERROR (Status)) {
>> +      goto FreeTranslationTable;
>> +    }
>> +
>> +    MemoryTable++;
>> +  }
>> +
>> +  //
>> +  // TLB Re-entry address at the end of exception vector, a vector is up to 512 bytes,
>> +  // so the starting address is: total exception vector size + total interrupt vector size + base.
>> +  // The total size of TLB handler and exception vector size and interrupt vector size should not
>> +  // be lager than 64KB.
>> +  //
>> +  Length           = (UINTN)HandleTlbRefillEnd - (UINTN)HandleTlbRefillStart;
>> +  TlbReEntryOffset = (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * 512;
>> +  Remaining        = TlbReEntryOffset % SIZE_4KB;
>> +  if (Remaining != 0x0) {
>> +    TlbReEntryOffset += (SIZE_4KB - Remaining);
>> +  }
>> +
>> +  TlbReEntry = PcdGet64 (PcdCpuExceptionVectorBaseAddress) + TlbReEntryOffset;
>> +  if ((TlbReEntryOffset + Length) > SIZE_64KB) {
>> +    goto FreeTranslationTable;
>> +  }
>> +
>> +  //
>> +  // Ensure that TLB refill exception base address alignment is equals to 4KB and is valid.
>> +  //
>> +  if (TlbReEntry & (SIZE_4KB - 1)) {
>> +    goto FreeTranslationTable;
>> +  }
>> +
>> +  CopyMem ((VOID *)TlbReEntry, HandleTlbRefillStart, Length);
>> +  InvalidateInstructionCacheRange ((VOID *)(UINTN)HandleTlbRefillStart, Length);
>> +
>> +  DEBUG ((
>> +    DEBUG_INFO,
>> +    "%a  %d PteShift %d PteWide %d PmdShift %d PmdWide %d PudShift %d PudWide %d PgdShift %d PgdWide %d.\n",
>> +    __func__,
>> +    __LINE__,
>> +    PteShift,
>> +    PteWide,
>> +    PmdShift,
>> +    PmdWide,
>> +    PudShift,
>> +    PudWide,
>> +    PgdShift,
>> +    PgdWide
>> +    ));
>> +
>> +  //
>> +  // Set the address of TLB refill exception handler
>> +  //
>> +  SetTlbRebaseAddress ((UINTN)TlbReEntry);
>> +
>> +  //
>> +  // Set page size
>> +  //
>> +  CsrXChg (LOONGARCH_CSR_TLBIDX, (DEFAULT_PAGE_SIZE << CSR_TLBIDX_SIZE), CSR_TLBIDX_SIZE_MASK);
>> +  CsrWrite (LOONGARCH_CSR_STLBPGSIZE, DEFAULT_PAGE_SIZE);
>> +  CsrXChg (LOONGARCH_CSR_TLBREHI, (DEFAULT_PAGE_SIZE << CSR_TLBREHI_PS_SHIFT), CSR_TLBREHI_PS);
>> +
>> +  CsrWrite (LOONGARCH_CSR_PWCTL0, (PteShift | PteWide << 5 | PmdShift << 10 | PmdWide << 15 | PudShift << 20 | PudWide << 25));
>> +  CsrWrite (LOONGARCH_CSR_PWCTL1, (PgdShift | PgdWide << 6));
>> +
>> +  DEBUG ((DEBUG_INFO, "%a %d Enable Mmu Start PageBassAddress %p.\n", __func__, __LINE__, SwapperPageDir));
>> +
>> +  return EFI_SUCCESS;
>> +
>> +FreeTranslationTable:
>> +  if (SwapperPageDir != NULL) {
>> +    FreePages (SwapperPageDir, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
>> +  }
>> +
>> +  return EFI_UNSUPPORTED;
>> +}
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
>> new file mode 100644
>> index 0000000000..9a681ce8e1
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
>> @@ -0,0 +1,48 @@
>> +/** @file
>> +
>> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +#ifndef TLB_H_
>> +#define TLB_H_
>> +
>> +/**
>> +  Invalid corresponding TLB entries are based on the address given
>> +
>> +  @param Address The address corresponding to the invalid page table entry
>> +
>> +  @retval  none
>> +**/
>> +VOID
>> +InvalidTlb (
>> +  UINTN  Address
>> +  );
>> +
>> +/**
>> +  TLB refill handler start.
>> +
>> +  @param  none
>> +
>> +  @retval none
>> +**/
>> +VOID
>> +HandleTlbRefillStart (
>> +  VOID
>> +  );
>> +
>> +/**
>> +  TLB refill handler end.
>> +
>> +  @param  none
>> +
>> +  @retval none
>> +**/
>> +VOID
>> +HandleTlbRefillEnd (
>> +  VOID
>> +  );
>> +
>> +#endif // TLB_H_
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
>> new file mode 100644
>> index 0000000000..c9a8c16336
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
>> @@ -0,0 +1,44 @@
>> +#------------------------------------------------------------------------------
>> +#
>> +# TLB operation functions
>> +#
>> +# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +#
>> +# SPDX-License-Identifier: BSD-2-Clause-Patent
>> +#
>> +#-----------------------------------------------------------------------------
>> +
>> +#include <Register/LoongArch64/Csr.h>
>> +
>> +ASM_GLOBAL ASM_PFX(HandleTlbRefillStart)
>> +ASM_GLOBAL ASM_PFX(HandleTlbRefillEnd)
>> +ASM_GLOBAL ASM_PFX(InvalidTlb)
>> +
>> +#
>> +#  Refill the page table.
>> +#  @param  VOID
>> +#  @retval  VOID
>> +#
>> +ASM_PFX(HandleTlbRefillStart):
>> +  csrwr   $t0, LOONGARCH_CSR_TLBRSAVE
>> +  csrrd   $t0, LOONGARCH_CSR_PGD
>> +  lddir   $t0, $t0, 3   #Put pud BaseAddress into T0
>> +  lddir   $t0, $t0, 2   #Put pmd BaseAddress into T0
>> +  lddir   $t0, $t0, 1   #Put pte BaseAddress into T0
>> +  ldpte   $t0, 0
>> +  ldpte   $t0, 1
>> +  tlbfill   // refill hi,lo0,lo1
>> +  csrrd   $t0, LOONGARCH_CSR_TLBRSAVE
>> +  ertn
>> +ASM_PFX(HandleTlbRefillEnd):
>> +
>> +#
>> +# Invalid corresponding TLB entries are based on the address given
>> +# @param a0 The address corresponding to the invalid page table entry
>> +# @retval  none
>> +#
>> +ASM_PFX(InvalidTlb):
>> +    invtlb  INVTLB_ADDR_GTRUE_OR_ASID, $zero, $a0
>> +    jirl    $zero, $ra, 0
>> +
>> +    .end
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>> new file mode 100644
>> index 0000000000..45b15db4c9
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>> @@ -0,0 +1,44 @@
>> +## @file
>> +#  CPU Memory Map Unit PEI phase driver.
>> +#
>> +#  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                      = PeiCpuMmuLib
>> +  MODULE_UNI_FILE                = PeiCpuMmuLib.uni
>> +  FILE_GUID                      = F67EB983-AC2A-7550-AB69-3BC51A1C895B
>> +  MODULE_TYPE                    = PEIM
>> +  VERSION_STRING                 = 1.0
>> +  LIBRARY_CLASS                  = CpuMmuLib | SEC PEIM
>> +
>> +#
>> +#  VALID_ARCHITECTURES           = LOONGARCH64
>> +#
>> +
>> +[Sources.LoongArch64]
>> +  LoongArch64/TlbOperation.S   | GCC
>> +  LoongArch64/CommonMmuLib.c
>> +  LoongArch64/PeiCpuMmuLib.c
>> +  LoongArch64/CommonMmuLib.h
>> +  LoongArch64/Tlb.h
>> +  LoongArch64/Page.h
>> +
>> +[Packages]
>> +  MdePkg/MdePkg.dec
>> +  MdeModulePkg/MdeModulePkg.dec
>> +  UefiCpuPkg/UefiCpuPkg.dec
>> +
>> +[PCD]
>> +  gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask
>> +  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress
>> +
>> +[LibraryClasses]
>> +  CacheMaintenanceLib
>> +  DebugLib
>> +  MemoryAllocationLib
>> +  PcdLib
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
>> new file mode 100644
>> index 0000000000..3e21334f3e
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
>> @@ -0,0 +1,14 @@
>> +// /** @file
>> +// CPU Memory Manager Unit library instance for PEI modules.
>> +//
>> +// CPU Memory Manager Unit library instance for PEI modules.
>> +//
>> +// 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 "CPU Memory Manager Unit library instance for PEI modules."
>> +
>> +#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for PEI modules."
>> diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
>> index 28eed85bce..178dc3c0f9 100644
>> --- a/UefiCpuPkg/UefiCpuPkg.dsc
>> +++ b/UefiCpuPkg/UefiCpuPkg.dsc
>> @@ -207,5 +207,9 @@
>>     UefiCpuPkg/CpuTimerDxeRiscV64/CpuTimerDxeRiscV64.inf
>>     UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf
>>   
>> +[Components.LOONGARCH64]
>> +  UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>> +  UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>> +
>>   [BuildOptions]
>>     *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
> 


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114861): https://edk2.groups.io/g/devel/message/114861
Mute This Topic: https://groups.io/mt/104070179/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 15/37] UefiCpuPkg: Add multiprocessor library for LoongArch64
       [not found]   ` <17AF511741BD9C8B.15701@groups.io>
@ 2024-01-31  5:33     ` Chao Li
  0 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-31  5:33 UTC (permalink / raw)
  To: devel, Ray Ni, Laszlo Ersek; +Cc: Eric Dong, Rahul Kumar, Gerd Hoffmann

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

Hi Ray and Laszlo,

I would very much like to be merged into stable202302, the soft feature 
deadline is 2024-02-05, so could you please hlep to review this patch as 
soon as passable? Please...


Thanks,
Chao
On 2024/1/31 11:32, Chao Li wrote:
>
> Hi Ray,
>
> Can you please help to review this patch again?
>
> On 2024/1/26 14:29, Chao Li wrote:
>> Added LoongArch multiprocessor initialization instance into MpInitLib.
>>
>> BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>>
>> Cc: Eric Dong<eric.dong@intel.com>
>> Cc: Ray Ni<ray.ni@intel.com>
>> Cc: Rahul Kumar<rahul1.kumar@intel.com>
>> Cc: Gerd Hoffmann<kraxel@redhat.com>
>> Signed-off-by: Chao Li<lichao@loongson.cn>
>> ---
>>   UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf |   27 +-
>>   .../Library/MpInitLib/LoongArch64/DxeMpLib.c  |  480 +++++
>>   .../Library/MpInitLib/LoongArch64/MpLib.c     | 1621 +++++++++++++++++
>>   .../Library/MpInitLib/LoongArch64/MpLib.h     |  361 ++++
>>   .../Library/MpInitLib/LoongArch64/PeiMpLib.c  |  404 ++++
>>   UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf |   27 +-
>>   6 files changed, 2902 insertions(+), 18 deletions(-)
>>   create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/DxeMpLib.c
>>   create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.c
>>   create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.h
>>   create mode 100644 UefiCpuPkg/Library/MpInitLib/LoongArch64/PeiMpLib.c
>>
>> diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>> index 55e46d4a1f..6db26f5fec 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>> +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>> @@ -2,6 +2,7 @@
>>   #  MP Initialize Library instance for DXE driver.
>>   #
>>   #  Copyright (c) 2016 - 2023, Intel Corporation. All rights reserved.<BR>
>> +#  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>>   #  SPDX-License-Identifier: BSD-2-Clause-Patent
>>   #
>>   ##
>> @@ -18,7 +19,7 @@
>>   #
>>   # The following information is for reference only and not required by the build tools.
>>   #
>> -#  VALID_ARCHITECTURES           = IA32 X64
>> +#  VALID_ARCHITECTURES           = IA32 X64 LOONGARCH64
>>   #
>>   
>>   [Sources.IA32]
>> @@ -31,7 +32,7 @@
>>     X64/MpFuncs.nasm
>>     X64/CreatePageTable.c
>>   
>> -[Sources.common]
>> +[Sources.IA32, Sources.X64]
>>     AmdSev.c
>>     MpEqu.inc
>>     DxeMpLib.c
>> @@ -40,24 +41,32 @@
>>     Microcode.c
>>     MpHandOff.h
>>   
>> +[Sources.LoongArch64]
>> +  LoongArch64/DxeMpLib.c
>> +  LoongArch64/MpLib.c
>> +  LoongArch64/MpLib.h
>> +
>>   [Packages]
>>     MdePkg/MdePkg.dec
>>     MdeModulePkg/MdeModulePkg.dec
>>     UefiCpuPkg/UefiCpuPkg.dec
>>   
>> -[LibraryClasses]
>> +[LibraryClasses.common]
>>     BaseLib
>> -  LocalApicLib
>> -  MemoryAllocationLib
>> -  HobLib
>> -  MtrrLib
>>     CpuLib
>> -  UefiBootServicesTableLib
>>     DebugAgentLib
>> -  SynchronizationLib
>> +  HobLib
>> +  MemoryAllocationLib
>>     PcdLib
>> +  UefiBootServicesTableLib
>> +  SynchronizationLib
>> +
>> +[LibraryClasses.IA32, LibraryClasses.X64]
>>     CcExitLib
>> +  LocalApicLib
>>     MicrocodeLib
>> +  MtrrLib
>> +
>>   [LibraryClasses.X64]
>>     CpuPageTableLib
>>   
>> diff --git a/UefiCpuPkg/Library/MpInitLib/LoongArch64/DxeMpLib.c b/UefiCpuPkg/Library/MpInitLib/LoongArch64/DxeMpLib.c
>> new file mode 100644
>> index 0000000000..739da77e32
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/MpInitLib/LoongArch64/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/MpInitLib/LoongArch64/MpLib.c b/UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.c
>> new file mode 100644
>> index 0000000000..930d34aa3d
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.c
>> @@ -0,0 +1,1621 @@
>> +/** @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>
>> +
>> +#define INVALID_APIC_ID  0xFFFFFFFF
>> +
>> +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) {
>> +    Index2 = 0;
>> +    for (Index1 = (PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1); Index1 > 0; Index1--) {
>> +      if (CpuInfoInHob[Index1].ApicId != INVALID_APIC_ID) {
>> +        if (Index1 == ApCount) {
>> +          break;
>> +        } else {
>> +          for ( ; Index2 <= ApCount; Index2++) {
>> +            if (CpuInfoInHob[Index2].ApicId == INVALID_APIC_ID) {
>> +              CopyMem (&CpuInfoInHob[Index2], &CpuInfoInHob[Index1], sizeof (CPU_INFO_IN_HOB));
>> +              CpuMpData->CpuData[Index2]  = CpuMpData->CpuData[Index1];
>> +              CpuInfoInHob[Index1].ApicId = INVALID_APIC_ID;
>> +              break;
>> +            }
>> +          }
>> +        }
>> +      } else {
>> +        continue;
>> +      }
>> +    }
>> +
>> +    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 and initialization APs ApicId status.
>> +  //
>> +  for (Index = 0; Index < MaxLogicalProcessorNumber; Index++) {
>> +    CpuMpData->CpuData[Index].StartupApSignal =
>> +      (UINT32 *)((MpBuffer + BufferSize - MonitorBufferSize) + (sizeof (WAKEUP_AP_SIGNAL) * Index));
>> +    if ((OldCpuMpData == NULL) && (Index != CpuMpData->BspNumber)) {
>> +      ((CPU_INFO_IN_HOB  *)CpuMpData->CpuInfoInHob)[Index].ApicId = INVALID_APIC_ID;
>> +    }
>> +  }
>> +
>> +  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/MpInitLib/LoongArch64/MpLib.h b/UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.h
>> new file mode 100644
>> index 0000000000..b9c6c55b41
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/MpInitLib/LoongArch64/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 MP_CPU_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 MP_CPU_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/MpInitLib/LoongArch64/PeiMpLib.c b/UefiCpuPkg/Library/MpInitLib/LoongArch64/PeiMpLib.c
>> new file mode 100644
>> index 0000000000..d1c5e55b57
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/MpInitLib/LoongArch64/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/Library/MpInitLib/PeiMpInitLib.inf b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
>> index bc3d716aa9..36ee6b9c29 100644
>> --- a/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
>> +++ b/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf
>> @@ -2,6 +2,7 @@
>>   #  MP Initialize Library instance for PEI driver.
>>   #
>>   #  Copyright (c) 2016 - 2021, Intel Corporation. All rights reserved.<BR>
>> +#  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>>   #  SPDX-License-Identifier: BSD-2-Clause-Patent
>>   #
>>   ##
>> @@ -18,7 +19,7 @@
>>   #
>>   # The following information is for reference only and not required by the build tools.
>>   #
>> -#  VALID_ARCHITECTURES           = IA32 X64
>> +#  VALID_ARCHITECTURES           = IA32 X64 LOONGARCH64
>>   #
>>   
>>   [Sources.IA32]
>> @@ -29,7 +30,7 @@
>>     X64/AmdSev.c
>>     X64/MpFuncs.nasm
>>   
>> -[Sources.common]
>> +[Sources.IA32, Sources.X64]
>>     AmdSev.c
>>     MpEqu.inc
>>     PeiMpLib.c
>> @@ -37,23 +38,31 @@
>>     MpLib.h
>>     Microcode.c
>>     MpHandOff.h
>> +
>> +[Sources.LoongArch64]
>> +  LoongArch64/PeiMpLib.c
>> +  LoongArch64/MpLib.c
>> +  LoongArch64/MpLib.h
>> +
>>   [Packages]
>>     MdePkg/MdePkg.dec
>>     UefiCpuPkg/UefiCpuPkg.dec
>>     MdeModulePkg/MdeModulePkg.dec
>>   
>> -[LibraryClasses]
>> +[LibraryClasses.common]
>>     BaseLib
>> -  LocalApicLib
>> -  MemoryAllocationLib
>> -  HobLib
>> -  MtrrLib
>>     CpuLib
>> -  SynchronizationLib
>> -  PeiServicesLib
>> +  HobLib
>> +  MemoryAllocationLib
>>     PcdLib
>> +  PeiServicesLib
>> +  SynchronizationLib
>> +
>> +[LibraryClasses.IA32, LibraryClasses.X64]
>>     CcExitLib
>> +  LocalApicLib
>>     MicrocodeLib
>> +  MtrrLib
>>   
>>   [Pcd]
>>     gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber        ## CONSUMES
> 


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114862): https://edk2.groups.io/g/devel/message/114862
Mute This Topic: https://groups.io/mt/104070182/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 16/37] UefiCpuPkg: Add CpuDxe driver for LoongArch64
       [not found]   ` <17AF511F29808828.16460@groups.io>
@ 2024-01-31  5:33     ` Chao Li
  0 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-01-31  5:33 UTC (permalink / raw)
  To: devel, Ray Ni, Laszlo Ersek
  Cc: Eric Dong, Rahul Kumar, Gerd Hoffmann, Baoqi Zhang, Dongyan Qian

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

Hi Ray and Laszlo,

I would very much like to be merged into stable202302, the soft feature 
deadline is 2024-02-05, so could you please hlep to review this patch as 
soon as passable? Please...


Thanks,
Chao
On 2024/1/31 11:32, Chao Li wrote:
>
> Hi Ray,
>
> Can you please help to review this patch again?
>
> On 2024/1/26 14:29, Chao Li wrote:
>> Added LoongArch64 CPU driver into CpuDxe.
>>
>> BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>>
>> Cc: Eric Dong<eric.dong@intel.com>
>> Cc: Ray Ni<ray.ni@intel.com>
>> Cc: Rahul Kumar<rahul1.kumar@intel.com>
>> Cc: Gerd Hoffmann<kraxel@redhat.com>
>> Signed-off-by: Chao Li<lichao@loongson.cn>
>> Co-authored-by: Baoqi Zhang<zhangbaoqi@loongson.cn>
>> Co-authored-by: Dongyan Qian<qiandongyan@loongson.cn>
>> ---
>>   UefiCpuPkg/CpuDxe/CpuDxe.inf              |  23 +-
>>   UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.c    | 454 ++++++++++++++++++
>>   UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.h    | 288 ++++++++++++
>>   UefiCpuPkg/CpuDxe/LoongArch64/CpuMp.c     | 544 ++++++++++++++++++++++
>>   UefiCpuPkg/CpuDxe/LoongArch64/Exception.c | 159 +++++++
>>   5 files changed, 1464 insertions(+), 4 deletions(-)
>>   create mode 100644 UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.c
>>   create mode 100644 UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.h
>>   create mode 100644 UefiCpuPkg/CpuDxe/LoongArch64/CpuMp.c
>>   create mode 100644 UefiCpuPkg/CpuDxe/LoongArch64/Exception.c
>>
>> diff --git a/UefiCpuPkg/CpuDxe/CpuDxe.inf b/UefiCpuPkg/CpuDxe/CpuDxe.inf
>> index 1d3e9f8cdb..18ebd2eb2c 100644
>> --- a/UefiCpuPkg/CpuDxe/CpuDxe.inf
>> +++ b/UefiCpuPkg/CpuDxe/CpuDxe.inf
>> @@ -3,6 +3,7 @@
>>   #
>>   #  Copyright (c) 2008 - 2019, Intel Corporation. All rights reserved.<BR>
>>   #  Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
>> +#  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>>   #
>>   #  SPDX-License-Identifier: BSD-2-Clause-Patent
>>   #
>> @@ -22,17 +23,16 @@
>>     MdeModulePkg/MdeModulePkg.dec
>>     UefiCpuPkg/UefiCpuPkg.dec
>>   
>> -[LibraryClasses]
>> +[LibraryClasses.common]
>>     BaseLib
>>     BaseMemoryLib
>>     CpuLib
>> +  CacheMaintenanceLib
>>     DebugLib
>>     DxeServicesTableLib
>>     MemoryAllocationLib
>> -  MtrrLib
>>     UefiBootServicesTableLib
>>     UefiDriverEntryPoint
>> -  LocalApicLib
>>     UefiLib
>>     CpuExceptionHandlerLib
>>     HobLib
>> @@ -41,7 +41,14 @@
>>     TimerLib
>>     PeCoffGetEntryPointLib
>>   
>> -[Sources]
>> +[LibraryClasses.IA32, LibraryClasses.X64]
>> +  LocalApicLib
>> +  MtrrLib
>> +
>> +[LibraryClasses.LoongArch64]
>> +  CpuMmuLib
>> +
>> +[Sources.IA32, Sources.X64]
>>     CpuDxe.c
>>     CpuDxe.h
>>     CpuGdt.c
>> @@ -59,6 +66,13 @@
>>     X64/CpuAsm.nasm
>>     X64/PagingAttribute.c
>>   
>> +[Sources.LoongArch64]
>> +  CpuMp.h
>> +  LoongArch64/CpuDxe.c
>> +  LoongArch64/CpuMp.c
>> +  LoongArch64/Exception.c
>> +  LoongArch64/CpuDxe.h
>> +
>>   [Protocols]
>>     gEfiCpuArchProtocolGuid                       ## PRODUCES
>>     gEfiMpServiceProtocolGuid                     ## PRODUCES
>> @@ -77,6 +91,7 @@
>>     gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard                       ## CONSUMES
>>     gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPropertyMask               ## CONSUMES
>>     gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask    ## CONSUMES
>> +  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress            ## CONSUMES
>>     gUefiCpuPkgTokenSpaceGuid.PcdCpuStackSwitchExceptionList              ## CONSUMES
>>     gUefiCpuPkgTokenSpaceGuid.PcdCpuKnownGoodStackSize                    ## CONSUMES
>>     gEfiMdeModulePkgTokenSpaceGuid.PcdTdxSharedBitMask                    ## CONSUMES
>> diff --git a/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.c b/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.c
>> new file mode 100644
>> index 0000000000..65ed0b3913
>> --- /dev/null
>> +++ b/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.c
>> @@ -0,0 +1,454 @@
>> +/** @file CpuDxe.c
>> +
>> +  CPU DXE Module to produce CPU ARCH Protocol.
>> +
>> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +**/
>> +
>> +#include "CpuDxe.h"
>> +#include "CpuMp.h"
>> +#include <Guid/IdleLoopEvent.h>
>> +#include <Library/CpuMmuLib.h>
>> +#include <Library/TimerLib.h>
>> +#include <Register/LoongArch64/Csr.h>
>> +
>> +UINT64  mTimerPeriod = 0;
>> +
>> +/**
>> +  IPI Interrupt Handler.
>> +
>> +  @param InterruptType    The type of interrupt that occurred
>> +  @param SystemContext    A pointer to the system context when the interrupt occurred
>> +**/
>> +VOID
>> +EFIAPI
>> +IpiInterruptHandler (
>> +  IN EFI_EXCEPTION_TYPE  InterruptType,
>> +  IN EFI_SYSTEM_CONTEXT  SystemContext
>> +  );
>> +
>> +//
>> +// Globals used to initialize the protocol
>> +//
>> +EFI_HANDLE             mCpuHandle = NULL;
>> +EFI_CPU_ARCH_PROTOCOL  gCpu       = {
>> +  CpuFlushCpuDataCache,
>> +  CpuEnableInterrupt,
>> +  CpuDisableInterrupt,
>> +  CpuGetInterruptState,
>> +  CpuInit,
>> +  CpuRegisterInterruptHandler,
>> +  CpuGetTimerValue,
>> +  CpuSetMemoryAttributes,
>> +  0,          // NumberOfTimers
>> +  4,          // DmaBufferAlignment
>> +};
>> +
>> +/**
>> +  This function flushes the range of addresses from Start to Start+Length
>> +  from the processor's data cache. If Start is not aligned to a cache line
>> +  boundary, then the bytes before Start to the preceding cache line boundary
>> +  are also flushed. If Start+Length is not aligned to a cache line boundary,
>> +  then the bytes past Start+Length to the end of the next cache line boundary
>> +  are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be
>> +  supported. If the data cache is fully coherent with all DMA operations, then
>> +  this function can just return EFI_SUCCESS. If the processor does not support
>> +  flushing a range of the data cache, then the entire data cache can be flushed.
>> +
>> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
>> +  @param  Start            The beginning physical address to flush from the processor's data
>> +                           cache.
>> +  @param  Length           The number of bytes to flush from the processor's data cache. This
>> +                           function may flush more bytes than Length specifies depending upon
>> +                           the granularity of the flush operation that the processor supports.
>> +  @param  FlushType        Specifies the type of flush operation to perform.
>> +
>> +  @retval EFI_SUCCESS           The address range from Start to Start+Length was flushed from
>> +                                the processor's data cache.
>> +  @retval EFI_INVALID_PARAMETER The processor does not support the cache flush type specified
>> +                                by FlushType.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +CpuFlushCpuDataCache (
>> +  IN EFI_CPU_ARCH_PROTOCOL  *This,
>> +  IN EFI_PHYSICAL_ADDRESS   Start,
>> +  IN UINT64                 Length,
>> +  IN EFI_CPU_FLUSH_TYPE     FlushType
>> +  )
>> +{
>> +  switch (FlushType) {
>> +    case EfiCpuFlushTypeWriteBack:
>> +      WriteBackDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length);
>> +      break;
>> +    case EfiCpuFlushTypeInvalidate:
>> +      InvalidateDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length);
>> +      break;
>> +    case EfiCpuFlushTypeWriteBackInvalidate:
>> +      WriteBackInvalidateDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length);
>> +      break;
>> +    default:
>> +      return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  This function enables interrupt processing by the processor.
>> +
>> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
>> +
>> +  @retval EFI_SUCCESS           Interrupts are enabled on the processor.
>> +  @retval EFI_DEVICE_ERROR      Interrupts could not be enabled on the processor.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +CpuEnableInterrupt (
>> +  IN EFI_CPU_ARCH_PROTOCOL  *This
>> +  )
>> +{
>> +  EnableInterrupts ();
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  This function disables interrupt processing by the processor.
>> +
>> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
>> +
>> +  @retval EFI_SUCCESS           Interrupts are disabled on the processor.
>> +  @retval EFI_DEVICE_ERROR      Interrupts could not be disabled on the processor.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +CpuDisableInterrupt (
>> +  IN EFI_CPU_ARCH_PROTOCOL  *This
>> +  )
>> +{
>> +  DisableInterrupts ();
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  This function retrieves the processor's current interrupt state a returns it in
>> +  State. If interrupts are currently enabled, then TRUE is returned. If interrupts
>> +  are currently disabled, then FALSE is returned.
>> +
>> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
>> +  @param  State            A pointer to the processor's current interrupt state. Set to TRUE if
>> +                           interrupts are enabled and FALSE if interrupts are disabled.
>> +
>> +  @retval EFI_SUCCESS           The processor's current interrupt state was returned in State.
>> +  @retval EFI_INVALID_PARAMETER State is NULL.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +CpuGetInterruptState (
>> +  IN  EFI_CPU_ARCH_PROTOCOL  *This,
>> +  OUT BOOLEAN                *State
>> +  )
>> +{
>> +  if (State == NULL) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  *State = GetInterruptState ();
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  This function generates an INIT on the processor. If this function succeeds, then the
>> +  processor will be reset, and control will not be returned to the caller. If InitType is
>> +  not supported by this processor, or the processor cannot programmatically generate an
>> +  INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error
>> +  occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned.
>> +
>> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
>> +  @param  InitType         The type of processor INIT to perform.
>> +
>> +  @retval EFI_SUCCESS           The processor INIT was performed. This return code should never be seen.
>> +  @retval EFI_UNSUPPORTED       The processor INIT operation specified by InitType is not supported
>> +                                by this processor.
>> +  @retval EFI_DEVICE_ERROR      The processor INIT failed.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +CpuInit (
>> +  IN EFI_CPU_ARCH_PROTOCOL  *This,
>> +  IN EFI_CPU_INIT_TYPE      InitType
>> +  )
>> +{
>> +  return EFI_UNSUPPORTED;
>> +}
>> +
>> +/**
>> +  Registers a function to be called from the CPU interrupt handler.
>> +
>> +  @param  This                   Protocol instance structure
>> +  @param  InterruptType          Defines which interrupt to hook. IA-32
>> +                                 valid range is 0x00 through 0xFF
>> +  @param  InterruptHandler       A pointer to a function of type
>> +                                 EFI_CPU_INTERRUPT_HANDLER that is called
>> +                                 when a processor interrupt occurs.  A null
>> +                                 pointer is an error condition.
>> +
>> +  @retval EFI_SUCCESS            If handler installed or uninstalled.
>> +  @retval EFI_ALREADY_STARTED    InterruptHandler is not NULL, and a handler
>> +                                 for InterruptType was previously installed.
>> +  @retval EFI_INVALID_PARAMETER  InterruptHandler is NULL, and a handler for
>> +                                 InterruptType was not previously installed.
>> +  @retval EFI_UNSUPPORTED        The interrupt specified by InterruptType
>> +                                 is not supported.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +CpuRegisterInterruptHandler (
>> +  IN EFI_CPU_ARCH_PROTOCOL      *This,
>> +  IN EFI_EXCEPTION_TYPE         InterruptType,
>> +  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
>> +  )
>> +{
>> +  return RegisterInterruptHandler (InterruptType, InterruptHandler);
>> +}
>> +
>> +/**
>> +  Returns a timer value from one of the CPU's internal timers. There is no
>> +  inherent time interval between ticks but is a function of the CPU frequency.
>> +
>> +  @param  This                - Protocol instance structure.
>> +  @param  TimerIndex          - Specifies which CPU timer is requested.
>> +  @param  TimerValue          - Pointer to the returned timer value.
>> +  @param  TimerPeriod         - A pointer to the amount of time that passes
>> +                                in femtoseconds (10-15) for each increment
>> +                                of TimerValue. If TimerValue does not
>> +                                increment at a predictable rate, then 0 is
>> +                                returned.  The amount of time that has
>> +                                passed between two calls to GetTimerValue()
>> +                                can be calculated with the formula
>> +                                (TimerValue2 - TimerValue1) * TimerPeriod.
>> +                                This parameter is optional and may be NULL.
>> +
>> +  @retval EFI_SUCCESS           - If the CPU timer count was returned.
>> +  @retval EFI_UNSUPPORTED       - If the CPU does not have any readable timers.
>> +  @retval EFI_DEVICE_ERROR      - If an error occurred while reading the timer.
>> +  @retval EFI_INVALID_PARAMETER - TimerIndex is not valid or TimerValue is NULL.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +CpuGetTimerValue (
>> +  IN  EFI_CPU_ARCH_PROTOCOL  *This,
>> +  IN  UINT32                 TimerIndex,
>> +  OUT UINT64                 *TimerValue,
>> +  OUT UINT64                 *TimerPeriod   OPTIONAL
>> +  )
>> +{
>> +  UINT64  BeginValue;
>> +  UINT64  EndValue;
>> +
>> +  if (TimerValue == NULL) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  if (TimerIndex != 0) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  *TimerValue = AsmReadStableCounter ();
>> +
>> +  if (TimerPeriod != NULL) {
>> +    if (mTimerPeriod == 0) {
>> +      //
>> +      // Read time stamp counter before and after delay of 100 microseconds
>> +      //
>> +      BeginValue = AsmReadStableCounter ();
>> +      MicroSecondDelay (100);
>> +      EndValue = AsmReadStableCounter ();
>> +      //
>> +      // Calculate the actual frequency
>> +      //
>> +      mTimerPeriod = DivU64x64Remainder (
>> +                       MultU64x32 (
>> +                         1000 * 1000 * 1000,
>> +                         100
>> +                         ),
>> +                       EndValue - BeginValue,
>> +                       NULL
>> +                       );
>> +    }
>> +
>> +    *TimerPeriod = mTimerPeriod;
>> +  }
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  This function modifies the attributes for the memory region specified by BaseAddress and
>> +  Length from their current attributes to the attributes specified by Attributes.
>> +
>> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
>> +  @param  BaseAddress      The physical address that is the start address of a memory region.
>> +  @param  Length           The size in bytes of the memory region.
>> +  @param  EfiAttributes    The bit mask of attributes to set for the memory region.
>> +
>> +  @retval EFI_SUCCESS           The attributes were set for the memory region.
>> +  @retval EFI_ACCESS_DENIED     The attributes for the memory resource range specified by
>> +                                BaseAddress and Length cannot be modified.
>> +  @retval EFI_INVALID_PARAMETER Length is zero.
>> +  @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of
>> +                                the memory resource range.
>> +  @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory
>> +                                resource range specified by BaseAddress and Length.
>> +                                The bit mask of attributes is not support for the memory resource
>> +                                range specified by BaseAddress and Length.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +CpuSetMemoryAttributes (
>> +  IN EFI_CPU_ARCH_PROTOCOL  *This,
>> +  IN EFI_PHYSICAL_ADDRESS   BaseAddress,
>> +  IN UINT64                 Length,
>> +  IN UINT64                 EfiAttributes
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +  UINTN       LoongArchAttributes;
>> +  UINTN       RegionBaseAddress;
>> +  UINTN       RegionLength;
>> +  UINTN       RegionLoongArchAttributes;
>> +
>> +  RegionLength = Length;
>> +  Status       = EFI_SUCCESS;
>> +
>> +  if ((BaseAddress & (EFI_PAGE_SIZE - 1)) != 0) {
>> +    //
>> +    // Minimum granularity is SIZE_4KB.
>> +    //
>> +    DEBUG ((
>> +      DEBUG_INFO,
>> +      "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum granularity is SIZE_4KB\n",
>> +      BaseAddress,
>> +      Length,
>> +      EfiAttributes
>> +      ));
>> +
>> +    Status = EFI_UNSUPPORTED;
>> +
>> +    return Status;
>> +  }
>> +
>> +  //
>> +  // Convert the 'Attribute' into LoongArch Attribute
>> +  //
>> +  LoongArchAttributes = EfiAttributeConverse (EfiAttributes);
>> +
>> +  //
>> +  // Get the region starting from 'BaseAddress' and its 'Attribute'
>> +  //
>> +  RegionBaseAddress = BaseAddress;
>> +  Status            = GetMemoryRegionAttributes (
>> +                        RegionBaseAddress,
>> +                        &RegionLength,
>> +                        &RegionLoongArchAttributes
>> +                        );
>> +
>> +  //
>> +  // Data & Instruction Caches are flushed when we set new memory attributes.
>> +  // So, we only set the attributes if the new region is different.
>> +  //
>> +  if ((Status == EFI_NOT_FOUND) || (RegionLoongArchAttributes != LoongArchAttributes) ||
>> +      ((BaseAddress + Length) > (RegionBaseAddress + RegionLength)))
>> +  {
>> +    Status = SetMemoryRegionAttributes (BaseAddress, Length, EfiAttributes, 0x0);
>> +  }
>> +
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  return Status;
>> +}
>> +
>> +/**
>> +  Callback function for idle events.
>> +
>> +  @param  Event                 Event whose notification function is being invoked.
>> +  @param  Context               The pointer to the notification function's context,
>> +                                which is implementation-dependent.
>> +
>> +**/
>> +VOID
>> +EFIAPI
>> +IdleLoopEventCallback (
>> +  IN EFI_EVENT  Event,
>> +  IN VOID       *Context
>> +  )
>> +{
>> +  CpuSleep ();
>> +}
>> +
>> +/**
>> +  Initialize the state information for the CPU Architectural Protocol.
>> +
>> +  @param ImageHandle     Image handle this driver.
>> +  @param SystemTable     Pointer to the System Table.
>> +
>> +  @retval EFI_SUCCESS           Thread can be successfully created
>> +  @retval EFI_OUT_OF_RESOURCES  Cannot allocate protocol data structure
>> +  @retval EFI_DEVICE_ERROR      Cannot create the thread
>> +
>> +**/
>> +EFI_STATUS
>> +InitializeCpu (
>> +  IN EFI_HANDLE        ImageHandle,
>> +  IN EFI_SYSTEM_TABLE  *SystemTable
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +  EFI_EVENT   IdleLoopEvent;
>> +
>> +  InitializeExceptions (&gCpu);
>> +
>> +  Status = gBS->InstallMultipleProtocolInterfaces (
>> +                  &mCpuHandle,
>> +                  &gEfiCpuArchProtocolGuid,
>> +                  &gCpu,
>> +                  NULL
>> +                  );
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  Status = gCpu.RegisterInterruptHandler (
>> +                  &gCpu,
>> +                  EXCEPT_LOONGARCH_INT_IPI,
>> +                  IpiInterruptHandler
>> +                  );
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  //
>> +  // Setup a callback for idle events
>> +  //
>> +  Status = gBS->CreateEventEx (
>> +                  EVT_NOTIFY_SIGNAL,
>> +                  TPL_NOTIFY,
>> +                  IdleLoopEventCallback,
>> +                  NULL,
>> +                  &gIdleLoopEventGuid,
>> +                  &IdleLoopEvent
>> +                  );
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  InitializeMpSupport ();
>> +
>> +  return Status;
>> +}
>> diff --git a/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.h b/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.h
>> new file mode 100644
>> index 0000000000..8bfbfa3442
>> --- /dev/null
>> +++ b/UefiCpuPkg/CpuDxe/LoongArch64/CpuDxe.h
>> @@ -0,0 +1,288 @@
>> +/** @file CpuDxe.c
>> +
>> +  CPU DXE Module to produce CPU ARCH Protocol.
>> +
>> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +**/
>> +
>> +#ifndef CPU_DXE_H_
>> +#define CPU_DXE_H_
>> +
>> +#include <Uefi.h>
>> +#include <Library/BaseMemoryLib.h>
>> +#include <Library/CacheMaintenanceLib.h>
>> +#include <Library/CpuLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/DxeServicesTableLib.h>
>> +#include <Library/MpInitLib.h>
>> +#include <Library/PcdLib.h>
>> +#include <Library/PeCoffGetEntryPointLib.h>
>> +#include <Library/UefiBootServicesTableLib.h>
>> +#include <Library/UefiLib.h>
>> +
>> +#include <Guid/DebugImageInfoTable.h>
>> +#include <Protocol/Cpu.h>
>> +#include <Protocol/DebugSupport.h>
>> +#include <Protocol/LoadedImage.h>
>> +
>> +//
>> +// For coding convenience, define the maximum valid
>> +// LoongArch exception.
>> +// Since UEFI V2.11, it will be present in DebugSupport.h.
>> +//
>> +#define MAX_LOONGARCH_EXCEPTION  64
>> +
>> +/*
>> +  This function flushes the range of addresses from Start to Start+Length
>> +  from the processor's data cache. If Start is not aligned to a cache line
>> +  boundary, then the bytes before Start to the preceding cache line boundary
>> +  are also flushed. If Start+Length is not aligned to a cache line boundary,
>> +  then the bytes past Start+Length to the end of the next cache line boundary
>> +  are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be
>> +  supported. If the data cache is fully coherent with all DMA operations, then
>> +  this function can just return EFI_SUCCESS. If the processor does not support
>> +  flushing a range of the data cache, then the entire data cache can be flushed.
>> +
>> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
>> +  @param  Start            The beginning physical address to flush from the processor's data
>> +                           cache.
>> +  @param  Length           The number of bytes to flush from the processor's data cache. This
>> +                           function may flush more bytes than Length specifies depending upon
>> +                           the granularity of the flush operation that the processor supports.
>> +  @param  FlushType        Specifies the type of flush operation to perform.
>> +
>> +  @retval EFI_SUCCESS           The address range from Start to Start+Length was flushed from
>> +                                the processor's data cache.
>> +  @retval EFI_UNSUPPORTEDT      The processor does not support the cache flush type specified
>> +                                by FlushType.
>> +  @retval EFI_DEVICE_ERROR      The address range from Start to Start+Length could not be flushed
>> +                                from the processor's data cache.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +CpuFlushCpuDataCache (
>> +  IN EFI_CPU_ARCH_PROTOCOL  *This,
>> +  IN EFI_PHYSICAL_ADDRESS   Start,
>> +  IN UINT64                 Length,
>> +  IN EFI_CPU_FLUSH_TYPE     FlushType
>> +  );
>> +
>> +/**
>> +  This function enables interrupt processing by the processor.
>> +
>> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
>> +
>> +  @retval EFI_SUCCESS           Interrupts are enabled on the processor.
>> +  @retval EFI_DEVICE_ERROR      Interrupts could not be enabled on the processor.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +CpuEnableInterrupt (
>> +  IN EFI_CPU_ARCH_PROTOCOL  *This
>> +  );
>> +
>> +/**
>> +  This function disables interrupt processing by the processor.
>> +
>> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
>> +
>> +  @retval EFI_SUCCESS           Interrupts are disabled on the processor.
>> +  @retval EFI_DEVICE_ERROR      Interrupts could not be disabled on the processor.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +CpuDisableInterrupt (
>> +  IN EFI_CPU_ARCH_PROTOCOL  *This
>> +  );
>> +
>> +/**
>> +  This function retrieves the processor's current interrupt state a returns it in
>> +  State. If interrupts are currently enabled, then TRUE is returned. If interrupts
>> +  are currently disabled, then FALSE is returned.
>> +
>> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
>> +  @param  State            A pointer to the processor's current interrupt state. Set to TRUE if
>> +                           interrupts are enabled and FALSE if interrupts are disabled.
>> +
>> +  @retval EFI_SUCCESS           The processor's current interrupt state was returned in State.
>> +  @retval EFI_INVALID_PARAMETER State is NULL.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +CpuGetInterruptState (
>> +  IN  EFI_CPU_ARCH_PROTOCOL  *This,
>> +  OUT BOOLEAN                *State
>> +  );
>> +
>> +/**
>> +  This function generates an INIT on the processor. If this function succeeds, then the
>> +  processor will be reset, and control will not be returned to the caller. If InitType is
>> +  not supported by this processor, or the processor cannot programmatically generate an
>> +  INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error
>> +  occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned.
>> +
>> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
>> +  @param  InitType         The type of processor INIT to perform.
>> +
>> +  @retval EFI_SUCCESS           The processor INIT was performed. This return code should never be seen.
>> +  @retval EFI_UNSUPPORTED       The processor INIT operation specified by InitType is not supported
>> +                                by this processor.
>> +  @retval EFI_DEVICE_ERROR      The processor INIT failed.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +CpuInit (
>> +  IN EFI_CPU_ARCH_PROTOCOL  *This,
>> +  IN EFI_CPU_INIT_TYPE      InitType
>> +  );
>> +
>> +/**
>> +  Registers a function to be called from the CPU interrupt handler.
>> +
>> +  @param  This                   Protocol instance structure
>> +  @param  InterruptType          Defines which interrupt to hook. IA-32
>> +                                 valid range is 0x00 through 0xFF
>> +  @param  InterruptHandler       A pointer to a function of type
>> +                                 EFI_CPU_INTERRUPT_HANDLER that is called
>> +                                 when a processor interrupt occurs.  A null
>> +                                 pointer is an error condition.
>> +
>> +  @retval EFI_SUCCESS            If handler installed or uninstalled.
>> +  @retval EFI_ALREADY_STARTED    InterruptHandler is not NULL, and a handler
>> +                                 for InterruptType was previously installed.
>> +  @retval EFI_INVALID_PARAMETER  InterruptHandler is NULL, and a handler for
>> +                                 InterruptType was not previously installed.
>> +  @retval EFI_UNSUPPORTED        The interrupt specified by InterruptType
>> +                                 is not supported.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +CpuRegisterInterruptHandler (
>> +  IN EFI_CPU_ARCH_PROTOCOL      *This,
>> +  IN EFI_EXCEPTION_TYPE         InterruptType,
>> +  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
>> +  );
>> +
>> +/**
>> +  Returns a timer value from one of the CPU's internal timers. There is no
>> +  inherent time interval between ticks but is a function of the CPU frequency.
>> +
>> +  @param  This                - Protocol instance structure.
>> +  @param  TimerIndex          - Specifies which CPU timer is requested.
>> +  @param  TimerValue          - Pointer to the returned timer value.
>> +  @param  TimerPeriod         - A pointer to the amount of time that passes
>> +                                in femtoseconds (10-15) for each increment
>> +                                of TimerValue. If TimerValue does not
>> +                                increment at a predictable rate, then 0 is
>> +                                returned.  The amount of time that has
>> +                                passed between two calls to GetTimerValue()
>> +                                can be calculated with the formula
>> +                                (TimerValue2 - TimerValue1) * TimerPeriod.
>> +                                This parameter is optional and may be NULL.
>> +
>> +  @retval EFI_SUCCESS           - If the CPU timer count was returned.
>> +  @retval EFI_UNSUPPORTED       - If the CPU does not have any readable timers.
>> +  @retval EFI_DEVICE_ERROR      - If an error occurred while reading the timer.
>> +  @retval EFI_INVALID_PARAMETER - TimerIndex is not valid or TimerValue is NULL.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +CpuGetTimerValue (
>> +  IN  EFI_CPU_ARCH_PROTOCOL  *This,
>> +  IN  UINT32                 TimerIndex,
>> +  OUT UINT64                 *TimerValue,
>> +  OUT UINT64                 *TimerPeriod   OPTIONAL
>> +  );
>> +
>> +/**
>> +  This function registers and enables the handler specified by InterruptHandler for a processor
>> +  interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
>> +  handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
>> +  The installed handler is called once for each processor interrupt or exception.
>> +
>> +  @param  InterruptType    A pointer to the processor's current interrupt state. Set to TRUE if interrupts
>> +                           are enabled and FALSE if interrupts are disabled.
>> +  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
>> +                           when a processor interrupt occurs. If this parameter is NULL, then the handler
>> +                           will be uninstalled.
>> +
>> +  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
>> +  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was
>> +                                previously installed.
>> +  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
>> +                                previously installed.
>> +  @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
>> +
>> +**/
>> +EFI_STATUS
>> +RegisterInterruptHandler (
>> +  IN EFI_EXCEPTION_TYPE         InterruptType,
>> +  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
>> +  );
>> +
>> +/**
>> +  This function modifies the attributes for the memory region specified by BaseAddress and
>> +  Length from their current attributes to the attributes specified by Attributes.
>> +
>> +  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
>> +  @param  BaseAddress      The physical address that is the start address of a memory region.
>> +  @param  Length           The size in bytes of the memory region.
>> +  @param  Attributes       The bit mask of attributes to set for the memory region.
>> +
>> +  @retval EFI_SUCCESS           The attributes were set for the memory region.
>> +  @retval EFI_ACCESS_DENIED     The attributes for the memory resource range specified by
>> +                                BaseAddress and Length cannot be modified.
>> +  @retval EFI_INVALID_PARAMETER Length is zero.
>> +  @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of
>> +                                the memory resource range.
>> +  @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory
>> +                                resource range specified by BaseAddress and Length.
>> +                                The bit mask of attributes is not support for the memory resource
>> +                                range specified by BaseAddress and Length.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +CpuSetMemoryAttributes (
>> +  IN EFI_CPU_ARCH_PROTOCOL  *This,
>> +  IN EFI_PHYSICAL_ADDRESS   BaseAddress,
>> +  IN UINT64                 Length,
>> +  IN UINT64                 Attributes
>> +  );
>> +
>> +/**
>> +  Initialize interrupt handling for DXE phase.
>> +
>> +  @param  Cpu A pointer of EFI_CPU_ARCH_PROTOCOL instance.
>> +
>> +  @return VOID.
>> +
>> +**/
>> +VOID
>> +InitializeExceptions (
>> +  IN EFI_CPU_ARCH_PROTOCOL  *gCpu
>> +  );
>> +
>> +/**
>> +  Converts EFI Attributes to corresponding architecture Attributes.
>> +
>> +  @param[in]  EfiAttributes     Efi Attributes.
>> +
>> +  @retval  Corresponding architecture attributes.
>> +**/
>> +UINTN
>> +EFIAPI
>> +EfiAttributeConverse (
>> +  IN UINTN  EfiAttributes
>> +  );
>> +
>> +#endif // CPU_DXE_H_
>> diff --git a/UefiCpuPkg/CpuDxe/LoongArch64/CpuMp.c b/UefiCpuPkg/CpuDxe/LoongArch64/CpuMp.c
>> new file mode 100644
>> index 0000000000..3325914e53
>> --- /dev/null
>> +++ b/UefiCpuPkg/CpuDxe/LoongArch64/CpuMp.c
>> @@ -0,0 +1,544 @@
>> +/** @file
>> +  CPU DXE Module to produce CPU MP Protocol.
>> +
>> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +**/
>> +
>> +#include "CpuDxe.h"
>> +#include "CpuMp.h"
>> +
>> +EFI_HANDLE  mMpServiceHandle    = NULL;
>> +UINTN       mNumberOfProcessors = 1;
>> +
>> +EFI_MP_SERVICES_PROTOCOL  mMpServicesTemplate = {
>> +  GetNumberOfProcessors,
>> +  GetProcessorInfo,
>> +  StartupAllAPs,
>> +  StartupThisAP,
>> +  SwitchBSP,
>> +  EnableDisableAP,
>> +  WhoAmI
>> +};
>> +
>> +/**
>> +  This service retrieves the number of logical processor in the platform
>> +  and the number of those logical processors that are enabled on this boot.
>> +  This service may only be called from the BSP.
>> +
>> +  This function is used to retrieve the following information:
>> +    - The number of logical processors that are present in the system.
>> +    - The number of enabled logical processors in the system at the instant
>> +      this call is made.
>> +
>> +  Because MP Service Protocol provides services to enable and disable processors
>> +  dynamically, the number of enabled logical processors may vary during the
>> +  course of a boot session.
>> +
>> +  If this service is called from an AP, then EFI_DEVICE_ERROR is returned.
>> +  If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then
>> +  EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors
>> +  is returned in NumberOfProcessors, the number of currently enabled processor
>> +  is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned.
>> +
>> +  @param[in]  This                        A pointer to the EFI_MP_SERVICES_PROTOCOL
>> +                                          instance.
>> +  @param[out] NumberOfProcessors          Pointer to the total number of logical
>> +                                          processors in the system, including the BSP
>> +                                          and disabled APs.
>> +  @param[out] NumberOfEnabledProcessors   Pointer to the number of enabled logical
>> +                                          processors that exist in system, including
>> +                                          the BSP.
>> +
>> +  @retval EFI_SUCCESS             The number of logical processors and enabled
>> +                                  logical processors was retrieved.
>> +  @retval EFI_DEVICE_ERROR        The calling processor is an AP.
>> +  @retval EFI_INVALID_PARAMETER   NumberOfProcessors is NULL.
>> +  @retval EFI_INVALID_PARAMETER   NumberOfEnabledProcessors is NULL.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +GetNumberOfProcessors (
>> +  IN  EFI_MP_SERVICES_PROTOCOL  *This,
>> +  OUT UINTN                     *NumberOfProcessors,
>> +  OUT UINTN                     *NumberOfEnabledProcessors
>> +  )
>> +{
>> +  if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) {
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  return MpInitLibGetNumberOfProcessors (
>> +           NumberOfProcessors,
>> +           NumberOfEnabledProcessors
>> +           );
>> +}
>> +
>> +/**
>> +  Gets detailed MP-related information on the requested processor at the
>> +  instant this call is made. This service may only be called from the BSP.
>> +
>> +  This service retrieves detailed MP-related information about any processor
>> +  on the platform. Note the following:
>> +    - The processor information may change during the course of a boot session.
>> +    - The information presented here is entirely MP related.
>> +
>> +  Information regarding the number of caches and their sizes, frequency of operation,
>> +  slot numbers is all considered platform-related information and is not provided
>> +  by this service.
>> +
>> +  @param[in]  This                  A pointer to the EFI_MP_SERVICES_PROTOCOL
>> +                                    instance.
>> +  @param[in]  ProcessorNumber       The handle number of processor.
>> +  @param[out] ProcessorInfoBuffer   A pointer to the buffer where information for
>> +                                    the requested processor is deposited.
>> +
>> +  @retval EFI_SUCCESS             Processor information was returned.
>> +  @retval EFI_DEVICE_ERROR        The calling processor is an AP.
>> +  @retval EFI_INVALID_PARAMETER   ProcessorInfoBuffer is NULL.
>> +  @retval EFI_NOT_FOUND           The processor with the handle specified by
>> +                                  ProcessorNumber does not exist in the platform.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +GetProcessorInfo (
>> +  IN  EFI_MP_SERVICES_PROTOCOL   *This,
>> +  IN  UINTN                      ProcessorNumber,
>> +  OUT EFI_PROCESSOR_INFORMATION  *ProcessorInfoBuffer
>> +  )
>> +{
>> +  return MpInitLibGetProcessorInfo (ProcessorNumber, ProcessorInfoBuffer, NULL);
>> +}
>> +
>> +/**
>> +  This service executes a caller provided function on all enabled APs. APs can
>> +  run either simultaneously or one at a time in sequence. This service supports
>> +  both blocking and non-blocking requests. The non-blocking requests use EFI
>> +  events so the BSP can detect when the APs have finished. This service may only
>> +  be called from the BSP.
>> +
>> +  This function is used to dispatch all the enabled APs to the function specified
>> +  by Procedure.  If any enabled AP is busy, then EFI_NOT_READY is returned
>> +  immediately and Procedure is not started on any AP.
>> +
>> +  If SingleThread is TRUE, all the enabled APs execute the function specified by
>> +  Procedure one by one, in ascending order of processor handle number. Otherwise,
>> +  all the enabled APs execute the function specified by Procedure simultaneously.
>> +
>> +  If WaitEvent is NULL, execution is in blocking mode. The BSP waits until all
>> +  APs finish or TimeoutInMicroseconds expires. Otherwise, execution is in non-blocking
>> +  mode, and the BSP returns from this service without waiting for APs. If a
>> +  non-blocking mode is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
>> +  is signaled, then EFI_UNSUPPORTED must be returned.
>> +
>> +  If the timeout specified by TimeoutInMicroseconds expires before all APs return
>> +  from Procedure, then Procedure on the failed APs is terminated. All enabled APs
>> +  are always available for further calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
>> +  and EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). If FailedCpuList is not NULL, its
>> +  content points to the list of processor handle numbers in which Procedure was
>> +  terminated.
>> +
>> +  Note: It is the responsibility of the consumer of the EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
>> +  to make sure that the nature of the code that is executed on the BSP and the
>> +  dispatched APs is well controlled. The MP Services Protocol does not guarantee
>> +  that the Procedure function is MP-safe. Hence, the tasks that can be run in
>> +  parallel are limited to certain independent tasks and well-controlled exclusive
>> +  code. EFI services and protocols may not be called by APs unless otherwise
>> +  specified.
>> +
>> +  In blocking execution mode, BSP waits until all APs finish or
>> +  TimeoutInMicroseconds expires.
>> +
>> +  In non-blocking execution mode, BSP is freed to return to the caller and then
>> +  proceed to the next task without having to wait for APs. The following
>> +  sequence needs to occur in a non-blocking execution mode:
>> +
>> +    -# The caller that intends to use this MP Services Protocol in non-blocking
>> +       mode creates WaitEvent by calling the EFI CreateEvent() service.  The caller
>> +       invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the parameter WaitEvent
>> +       is not NULL, then StartupAllAPs() executes in non-blocking mode. It requests
>> +       the function specified by Procedure to be started on all the enabled APs,
>> +       and releases the BSP to continue with other tasks.
>> +    -# The caller can use the CheckEvent() and WaitForEvent() services to check
>> +       the state of the WaitEvent created in step 1.
>> +    -# When the APs complete their task or TimeoutInMicroSecondss expires, the MP
>> +       Service signals WaitEvent by calling the EFI SignalEvent() function. If
>> +       FailedCpuList is not NULL, its content is available when WaitEvent is
>> +       signaled. If all APs returned from Procedure prior to the timeout, then
>> +       FailedCpuList is set to NULL. If not all APs return from Procedure before
>> +       the timeout, then FailedCpuList is filled in with the list of the failed
>> +       APs. The buffer is allocated by MP Service Protocol using AllocatePool().
>> +       It is the caller's responsibility to free the buffer with FreePool() service.
>> +    -# This invocation of SignalEvent() function informs the caller that invoked
>> +       EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() that either all the APs completed
>> +       the specified task or a timeout occurred. The contents of FailedCpuList
>> +       can be examined to determine which APs did not complete the specified task
>> +       prior to the timeout.
>> +
>> +  @param[in]  This                    A pointer to the EFI_MP_SERVICES_PROTOCOL
>> +                                      instance.
>> +  @param[in]  Procedure               A pointer to the function to be run on
>> +                                      enabled APs of the system. See type
>> +                                      EFI_AP_PROCEDURE.
>> +  @param[in]  SingleThread            If TRUE, then all the enabled APs execute
>> +                                      the function specified by Procedure one by
>> +                                      one, in ascending order of processor handle
>> +                                      number.  If FALSE, then all the enabled APs
>> +                                      execute the function specified by Procedure
>> +                                      simultaneously.
>> +  @param[in]  WaitEvent               The event created by the caller with CreateEvent()
>> +                                      service.  If it is NULL, then execute in
>> +                                      blocking mode. BSP waits until all APs finish
>> +                                      or TimeoutInMicroseconds expires.  If it's
>> +                                      not NULL, then execute in non-blocking mode.
>> +                                      BSP requests the function specified by
>> +                                      Procedure to be started on all the enabled
>> +                                      APs, and go on executing immediately. If
>> +                                      all return from Procedure, or TimeoutInMicroseconds
>> +                                      expires, this event is signaled. The BSP
>> +                                      can use the CheckEvent() or WaitForEvent()
>> +                                      services to check the state of event.  Type
>> +                                      EFI_EVENT is defined in CreateEvent() in
>> +                                      the Unified Extensible Firmware Interface
>> +                                      Specification.
>> +  @param[in]  TimeoutInMicroseconds   Indicates the time limit in microseconds for
>> +                                      APs to return from Procedure, either for
>> +                                      blocking or non-blocking mode. Zero means
>> +                                      infinity.  If the timeout expires before
>> +                                      all APs return from Procedure, then Procedure
>> +                                      on the failed APs is terminated. All enabled
>> +                                      APs are available for next function assigned
>> +                                      by EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
>> +                                      or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
>> +                                      If the timeout expires in blocking mode,
>> +                                      BSP returns EFI_TIMEOUT.  If the timeout
>> +                                      expires in non-blocking mode, WaitEvent
>> +                                      is signaled with SignalEvent().
>> +  @param[in]  ProcedureArgument       The parameter passed into Procedure for
>> +                                      all APs.
>> +  @param[out] FailedCpuList           If NULL, this parameter is ignored. Otherwise,
>> +                                      if all APs finish successfully, then its
>> +                                      content is set to NULL. If not all APs
>> +                                      finish before timeout expires, then its
>> +                                      content is set to address of the buffer
>> +                                      holding handle numbers of the failed APs.
>> +                                      The buffer is allocated by MP Service Protocol,
>> +                                      and it's the caller's responsibility to
>> +                                      free the buffer with FreePool() service.
>> +                                      In blocking mode, it is ready for consumption
>> +                                      when the call returns. In non-blocking mode,
>> +                                      it is ready when WaitEvent is signaled.  The
>> +                                      list of failed CPU is terminated by
>> +                                      END_OF_CPU_LIST.
>> +
>> +  @retval EFI_SUCCESS             In blocking mode, all APs have finished before
>> +                                  the timeout expired.
>> +  @retval EFI_SUCCESS             In non-blocking mode, function has been dispatched
>> +                                  to all enabled APs.
>> +  @retval EFI_UNSUPPORTED         A non-blocking mode request was made after the
>> +                                  UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
>> +                                  signaled.
>> +  @retval EFI_DEVICE_ERROR        Caller processor is AP.
>> +  @retval EFI_NOT_STARTED         No enabled APs exist in the system.
>> +  @retval EFI_NOT_READY           Any enabled APs are busy.
>> +  @retval EFI_TIMEOUT             In blocking mode, the timeout expired before
>> +                                  all enabled APs have finished.
>> +  @retval EFI_INVALID_PARAMETER   Procedure is NULL.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +StartupAllAPs (
>> +  IN  EFI_MP_SERVICES_PROTOCOL  *This,
>> +  IN  EFI_AP_PROCEDURE          Procedure,
>> +  IN  BOOLEAN                   SingleThread,
>> +  IN  EFI_EVENT                 WaitEvent               OPTIONAL,
>> +  IN  UINTN                     TimeoutInMicroseconds,
>> +  IN  VOID                      *ProcedureArgument      OPTIONAL,
>> +  OUT UINTN                     **FailedCpuList         OPTIONAL
>> +  )
>> +{
>> +  return MpInitLibStartupAllAPs (
>> +           Procedure,
>> +           SingleThread,
>> +           WaitEvent,
>> +           TimeoutInMicroseconds,
>> +           ProcedureArgument,
>> +           FailedCpuList
>> +           );
>> +}
>> +
>> +/**
>> +  This service lets the caller get one enabled AP to execute a caller-provided
>> +  function. The caller can request the BSP to either wait for the completion
>> +  of the AP or just proceed with the next task by using the EFI event mechanism.
>> +  See EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() for more details on non-blocking
>> +  execution support.  This service may only be called from the BSP.
>> +
>> +  This function is used to dispatch one enabled AP to the function specified by
>> +  Procedure passing in the argument specified by ProcedureArgument.  If WaitEvent
>> +  is NULL, execution is in blocking mode. The BSP waits until the AP finishes or
>> +  TimeoutInMicroSecondss expires. Otherwise, execution is in non-blocking mode.
>> +  BSP proceeds to the next task without waiting for the AP. If a non-blocking mode
>> +  is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled,
>> +  then EFI_UNSUPPORTED must be returned.
>> +
>> +  If the timeout specified by TimeoutInMicroseconds expires before the AP returns
>> +  from Procedure, then execution of Procedure by the AP is terminated. The AP is
>> +  available for subsequent calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and
>> +  EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
>> +
>> +  @param[in]  This                    A pointer to the EFI_MP_SERVICES_PROTOCOL
>> +                                      instance.
>> +  @param[in]  Procedure               A pointer to the function to be run on 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
>> +                                      EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
>> +  @param[in]  WaitEvent               The event created by the caller with CreateEvent()
>> +                                      service.  If it is NULL, then execute in
>> +                                      blocking mode. BSP waits until 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 EFI_MP_SERVICES_PROTOCOL.StartupAllAPs()
>> +                                      or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
>> +                                      If the timeout expires in blocking mode,
>> +                                      BSP returns EFI_TIMEOUT.  If the timeout
>> +                                      expires in non-blocking mode, WaitEvent
>> +                                      is signaled with SignalEvent().
>> +  @param[in]  ProcedureArgument       The parameter passed into Procedure 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_DEVICE_ERROR        The calling processor is an AP.
>> +  @retval EFI_TIMEOUT             In blocking mode, the timeout expired before
>> +                                  the specified AP has finished.
>> +  @retval EFI_NOT_READY           The specified AP is busy.
>> +  @retval EFI_NOT_FOUND           The processor with the handle specified by
>> +                                  ProcessorNumber does not exist.
>> +  @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the BSP or disabled AP.
>> +  @retval EFI_INVALID_PARAMETER   Procedure is NULL.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +StartupThisAP (
>> +  IN  EFI_MP_SERVICES_PROTOCOL  *This,
>> +  IN  EFI_AP_PROCEDURE          Procedure,
>> +  IN  UINTN                     ProcessorNumber,
>> +  IN  EFI_EVENT                 WaitEvent               OPTIONAL,
>> +  IN  UINTN                     TimeoutInMicroseconds,
>> +  IN  VOID                      *ProcedureArgument      OPTIONAL,
>> +  OUT BOOLEAN                   *Finished               OPTIONAL
>> +  )
>> +{
>> +  return MpInitLibStartupThisAP (
>> +           Procedure,
>> +           ProcessorNumber,
>> +           WaitEvent,
>> +           TimeoutInMicroseconds,
>> +           ProcedureArgument,
>> +           Finished
>> +           );
>> +}
>> +
>> +/**
>> +  This service switches the requested AP to be the BSP from that point onward.
>> +  This service changes the BSP for all purposes.   This call can only be performed
>> +  by the current BSP.
>> +
>> +  This service switches the requested AP to be the BSP from that point onward.
>> +  This service changes the BSP for all purposes. The new BSP can take over the
>> +  execution of the old BSP and continue seamlessly from where the old one left
>> +  off. This service may not be supported after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT
>> +  is signaled.
>> +
>> +  If the BSP cannot be switched prior to the return from this service, then
>> +  EFI_UNSUPPORTED must be returned.
>> +
>> +  @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
>> +  @param[in] ProcessorNumber   The handle number of AP that is to become the new
>> +                               BSP. The range is from 0 to the total number of
>> +                               logical processors minus 1. The total number of
>> +                               logical processors can be retrieved by
>> +                               EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
>> +  @param[in] EnableOldBSP      If TRUE, then the old BSP will be listed as an
>> +                               enabled AP. Otherwise, it will be disabled.
>> +
>> +  @retval EFI_SUCCESS             BSP successfully switched.
>> +  @retval EFI_UNSUPPORTED         Switching the BSP cannot be completed prior to
>> +                                  this service returning.
>> +  @retval EFI_UNSUPPORTED         Switching the BSP is not supported.
>> +  @retval EFI_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.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +SwitchBSP (
>> +  IN EFI_MP_SERVICES_PROTOCOL  *This,
>> +  IN  UINTN                    ProcessorNumber,
>> +  IN  BOOLEAN                  EnableOldBSP
>> +  )
>> +{
>> +  return MpInitLibSwitchBSP (ProcessorNumber, EnableOldBSP);
>> +}
>> +
>> +/**
>> +  This service lets the caller enable or disable an AP from this point onward.
>> +  This service may only be called from the BSP.
>> +
>> +  This service allows the caller enable or disable an AP from this point onward.
>> +  The caller can optionally specify the health status of the AP by Health. If
>> +  an AP is being disabled, then the state of the disabled AP is implementation
>> +  dependent. If an AP is enabled, then the implementation must guarantee that a
>> +  complete initialization sequence is performed on the AP, so the AP is in a state
>> +  that is compatible with an MP operating system. This service may not be supported
>> +  after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled.
>> +
>> +  If the enable or disable AP operation cannot be completed prior to the return
>> +  from this service, then EFI_UNSUPPORTED must be returned.
>> +
>> +  @param[in] This              A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
>> +  @param[in] ProcessorNumber   The handle number of AP.
>> +                               The range is from 0 to the total number of
>> +                               logical processors minus 1. The total number of
>> +                               logical processors can be retrieved by
>> +                               EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
>> +  @param[in] EnableAP          Specifies the new state for the processor for
>> +                               enabled, FALSE for disabled.
>> +  @param[in] HealthFlag        If not NULL, a pointer to a value that specifies
>> +                               the new health status of the AP. This flag
>> +                               corresponds to StatusFlag defined in
>> +                               EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo(). Only
>> +                               the PROCESSOR_HEALTH_STATUS_BIT is used. All other
>> +                               bits are ignored.  If it is NULL, this parameter
>> +                               is ignored.
>> +
>> +  @retval EFI_SUCCESS             The specified AP was enabled or disabled successfully.
>> +  @retval EFI_UNSUPPORTED         Enabling or disabling an AP cannot be completed
>> +                                  prior to this service returning.
>> +  @retval EFI_UNSUPPORTED         Enabling or disabling an AP is not supported.
>> +  @retval EFI_DEVICE_ERROR        The calling processor is an AP.
>> +  @retval EFI_NOT_FOUND           Processor with the handle specified by ProcessorNumber
>> +                                  does not exist.
>> +  @retval EFI_INVALID_PARAMETER   ProcessorNumber specifies the BSP.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +EnableDisableAP (
>> +  IN  EFI_MP_SERVICES_PROTOCOL  *This,
>> +  IN  UINTN                     ProcessorNumber,
>> +  IN  BOOLEAN                   EnableAP,
>> +  IN  UINT32                    *HealthFlag OPTIONAL
>> +  )
>> +{
>> +  return MpInitLibEnableDisableAP (ProcessorNumber, EnableAP, HealthFlag);
>> +}
>> +
>> +/**
>> +  This return the handle number for the calling processor.  This service may be
>> +  called from the BSP and APs.
>> +
>> +  This service returns the processor handle number for the calling processor.
>> +  The returned value is in the range from 0 to the total number of logical
>> +  processors minus 1. The total number of logical processors can be retrieved
>> +  with EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). This service may be
>> +  called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER
>> +  is returned. Otherwise, the current processors handle number is returned in
>> +  ProcessorNumber, and EFI_SUCCESS is returned.
>> +
>> +  @param[in]  This             A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
>> +  @param[out] ProcessorNumber  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
>> +                               EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors().
>> +
>> +  @retval EFI_SUCCESS             The current processor handle number was returned
>> +                                  in ProcessorNumber.
>> +  @retval EFI_INVALID_PARAMETER   ProcessorNumber is NULL.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +WhoAmI (
>> +  IN EFI_MP_SERVICES_PROTOCOL  *This,
>> +  OUT UINTN                    *ProcessorNumber
>> +  )
>> +{
>> +  return MpInitLibWhoAmI (ProcessorNumber);
>> +}
>> +
>> +/**
>> +  Initialize Multi-processor support.
>> +**/
>> +VOID
>> +InitializeMpSupport (
>> +  VOID
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +  UINTN       NumberOfProcessors;
>> +  UINTN       NumberOfEnabledProcessors;
>> +
>> +  //
>> +  // Wakeup APs to do initialization
>> +  //
>> +  Status = MpInitLibInitialize ();
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  MpInitLibGetNumberOfProcessors (&NumberOfProcessors, &NumberOfEnabledProcessors);
>> +  mNumberOfProcessors = NumberOfProcessors;
>> +  DEBUG ((DEBUG_INFO, "Detect CPU count: %d\n", mNumberOfProcessors));
>> +
>> +  Status = gBS->InstallMultipleProtocolInterfaces (
>> +                  &mMpServiceHandle,
>> +                  &gEfiMpServiceProtocolGuid,
>> +                  &mMpServicesTemplate,
>> +                  NULL
>> +                  );
>> +  ASSERT_EFI_ERROR (Status);
>> +}
>> diff --git a/UefiCpuPkg/CpuDxe/LoongArch64/Exception.c b/UefiCpuPkg/CpuDxe/LoongArch64/Exception.c
>> new file mode 100644
>> index 0000000000..96def89936
>> --- /dev/null
>> +++ b/UefiCpuPkg/CpuDxe/LoongArch64/Exception.c
>> @@ -0,0 +1,159 @@
>> +/** @file Exception.c
>> +
>> +  CPU DXE Module initialization exception instance.
>> +
>> +  Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +**/
>> +
>> +#include "CpuDxe.h"
>> +#include <Guid/VectorHandoffTable.h>
>> +#include <Library/CpuExceptionHandlerLib.h>
>> +#include <Register/LoongArch64/Csr.h>
>> +
>> +VOID
>> +ExceptionEntryStart (
>> +  VOID
>> +  );
>> +
>> +VOID
>> +ExceptionEntryEnd (
>> +  VOID
>> +  );
>> +
>> +/**
>> +  This function registers and enables the handler specified by InterruptHandler for a processor
>> +  interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
>> +  handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
>> +  The installed handler is called once for each processor interrupt or exception.
>> +
>> +  @param  InterruptType    A pointer to the processor's current interrupt state. Set to TRUE if interrupts
>> +                           are enabled and FALSE if interrupts are disabled.
>> +  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
>> +                           when a processor interrupt occurs. If this parameter is NULL, then the handler
>> +                           will be uninstalled.
>> +
>> +  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
>> +  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was
>> +                                previously installed.
>> +  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
>> +                                previously installed.
>> +  @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
>> +
>> +**/
>> +EFI_STATUS
>> +RegisterInterruptHandler (
>> +  IN EFI_EXCEPTION_TYPE         InterruptType,
>> +  IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
>> +  )
>> +{
>> +  return (EFI_STATUS)RegisterCpuInterruptHandler (InterruptType, InterruptHandler);
>> +}
>> +
>> +/**
>> +  Update the exception start entry code.
>> +
>> +  @retval EFI_SUCCESS           Update the exception start entry code down.
>> +  @retval EFI_OUT_OF_RESOURCES  The start entry code size out of bounds.
>> +
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +UpdateExceptionStartEntry (
>> +  VOID
>> +  )
>> +{
>> +  EFI_PHYSICAL_ADDRESS  ExceptionStartEntry;
>> +  UINTN                 VectorLength;
>> +  UINTN                 MaxLength;
>> +  UINTN                 MaxSizeOfVector;
>> +
>> +  VectorLength = (UINTN)ExceptionEntryEnd - (UINTN)ExceptionEntryStart;
>> +
>> +  //
>> +  // A vector is up to 512 bytes.
>> +  //
>> +  MaxSizeOfVector = 512;
>> +  MaxLength       = (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * MaxSizeOfVector;
>> +
>> +  if (VectorLength > MaxLength) {
>> +    return EFI_OUT_OF_RESOURCES;
>> +  }
>> +
>> +  ExceptionStartEntry = PcdGet64 (PcdCpuExceptionVectorBaseAddress);
>> +
>> +  InvalidateInstructionCacheRange ((VOID *)ExceptionStartEntry, VectorLength);
>> +  CopyMem ((VOID *)ExceptionStartEntry, (VOID *)ExceptionEntryStart, VectorLength);
>> +  InvalidateInstructionCacheRange ((VOID *)ExceptionStartEntry, VectorLength);
>> +  InvalidateDataCache ();
>> +
>> +  //
>> +  // If PcdCpuExceptionVectorBaseAddress is not used during SEC and PEI stages, the exception
>> +  // base addres is set to PcdCpuExceptionVectorBaseAddress.
>> +  //
>> +  if (CsrRead (LOONGARCH_CSR_EBASE) != ExceptionStartEntry) {
>> +    SetExceptionBaseAddress (ExceptionStartEntry);
>> +  }
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Initialize interrupt handling for DXE phase.
>> +
>> +  @param  Cpu A pointer of EFI_CPU_ARCH_PROTOCOL instance.
>> +
>> +  @return VOID.
>> +
>> +**/
>> +VOID
>> +InitializeExceptions (
>> +  IN EFI_CPU_ARCH_PROTOCOL  *Cpu
>> +  )
>> +{
>> +  EFI_STATUS               Status;
>> +  EFI_VECTOR_HANDOFF_INFO  *VectorInfoList;
>> +  EFI_VECTOR_HANDOFF_INFO  *VectorInfo;
>> +  BOOLEAN                  IrqEnabled;
>> +
>> +  VectorInfo = (EFI_VECTOR_HANDOFF_INFO *)NULL;
>> +  Status     = EfiGetSystemConfigurationTable (&gEfiVectorHandoffTableGuid, (VOID **)&VectorInfoList);
>> +
>> +  if ((Status == EFI_SUCCESS) && (VectorInfoList != NULL)) {
>> +    VectorInfo = VectorInfoList;
>> +  }
>> +
>> +  //
>> +  // Disable interrupts
>> +  //
>> +  Cpu->GetInterruptState (Cpu, &IrqEnabled);
>> +  if (IrqEnabled) {
>> +    Cpu->DisableInterrupt (Cpu);
>> +  }
>> +
>> +  //
>> +  // Update the Exception Start Entry code to point into CpuDxe.
>> +  //
>> +  Status = UpdateExceptionStartEntry ();
>> +  if (EFI_ERROR (Status)) {
>> +    DebugPrint (EFI_D_ERROR, "[%a]: Exception start entry code out of bounds!\n", __func__);
>> +    ASSERT_EFI_ERROR (Status);
>> +  }
>> +
>> +  //
>> +  // Intialize the CpuExceptionHandlerLib so we take over the exception vector table from the DXE Core
>> +  //
>> +  Status = InitializeCpuExceptionHandlers (VectorInfo);
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  //
>> +  // Enable interrupts
>> +  //
>> +  DebugPrint (EFI_D_INFO, "InitializeExceptions,IrqEnabled = %x\n", IrqEnabled);
>> +  if (!IrqEnabled) {
>> +    Status = Cpu->EnableInterrupt (Cpu);
>> +  }
>> +
>> +  ASSERT_EFI_ERROR (Status);
>> +}
> 


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114863): https://edk2.groups.io/g/devel/message/114863
Mute This Topic: https://groups.io/mt/104070187/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib " Chao Li
@ 2024-01-31  9:47   ` Laszlo Ersek
  2024-02-01  7:57     ` Chao Li
  2024-01-31 10:33   ` Pedro Falcato
  1 sibling, 1 reply; 89+ messages in thread
From: Laszlo Ersek @ 2024-01-31  9:47 UTC (permalink / raw)
  To: Chao Li, devel
  Cc: Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

On 1/26/24 07:29, Chao Li wrote:
> Add a new library named CpuMmuLib and add a LoongArch64 instance with in
> the library.
> It provides two-stage MMU libraryinstances, PEI and DXE.
> 
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584
> 
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Ray Ni <ray.ni@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Rahul Kumar <rahul1.kumar@intel.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Signed-off-by: Chao Li <lichao@loongson.cn>
> Co-authored-by: Baoqi Zhang <zhangbaoqi@loongson.cn>
> Co-authored-by: Dongyan Qian <qiandongyan@loongson.cn>
> Co-authored-by: Xianglai Li <lixianglai@loongson.cn>
> Co-authored-by: Bibo Mao <maobibo@loongson.cn>
> ---
>  UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf |  36 +
>  UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni |  14 +
>  .../CpuMmuLib/LoongArch64/CommonMmuLib.c      | 988 ++++++++++++++++++
>  .../CpuMmuLib/LoongArch64/CommonMmuLib.h      |  43 +
>  .../Library/CpuMmuLib/LoongArch64/Page.h      | 279 +++++
>  .../CpuMmuLib/LoongArch64/PeiCpuMmuLib.c      | 178 ++++
>  .../Library/CpuMmuLib/LoongArch64/Tlb.h       |  48 +
>  .../CpuMmuLib/LoongArch64/TlbOperation.S      |  44 +
>  UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf |  44 +
>  UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni |  14 +
>  UefiCpuPkg/UefiCpuPkg.dsc                     |   4 +
>  11 files changed, 1692 insertions(+)
>  create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>  create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
>  create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>  create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
>  create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
>  create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
>  create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
>  create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
>  create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>  create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
> 
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
> new file mode 100644
> index 0000000000..bfce3ce96d
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
> @@ -0,0 +1,36 @@
> +## @file
> +#  CPU Memory Map Unit DXE phase driver.
> +#
> +#  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                      = DxeCpuMmuLib
> +  MODULE_UNI_FILE                = DxeCpuMmuLib.uni
> +  FILE_GUID                      = DA8F0232-FB14-42F0-922C-63104D2C70BE

(1) This FILE_GUID was created from the FILE_GUID of
"ArmPkg/Library/ArmMmuLib/ArmMmuBaseLib.inf" by adding 1. That's not an
acceptable method for GUID generation.

A method is only acceptable for GUID generation if multiple (= an
unlimited number of) parties can execute the method at any time, and the
output remains conflict-free.

Taking an existent (known) FILE_GUID and incrementing it by 1 is not
such a method.

Please regenerate the GUID with "uuidgen".

Please also review the rest of your new GUIDs over this series (not only
FILE_GUIDs in INF files, but any other GUIDs, too).


> +  MODULE_TYPE                    = DXE_DRIVER
> +  VERSION_STRING                 = 1.0
> +  LIBRARY_CLASS                  = CpuMmuLib | DXE_DRIVER
> +  CONSTRUCTOR                    = MmuInitialize
> +
> +#
> +#  VALID_ARCHITECTURES           = LOONGARCH64
> +#
> +
> +[Sources.LoongArch64]
> +  LoongArch64/TlbOperation.S   | GCC
> +  LoongArch64/CommonMmuLib.c
> +  LoongArch64/Page.h
> +  LoongArch64/Tlb.h
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  UefiCpuPkg/UefiCpuPkg.dec
> +
> +[LibraryClasses]
> +  DebugLib
> +  MemoryAllocationLib
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
> new file mode 100644
> index 0000000000..7342249516
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
> @@ -0,0 +1,14 @@
> +// /** @file
> +// CPU Memory Manager Unit library instance for DXE modules.
> +//
> +// CPU Memory Manager Unit library instance for DXE modules.
> +//
> +// 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 "CPU Memory Manager Unit library instance for DXE modules."
> +
> +#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for DXE modules."
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
> new file mode 100644
> index 0000000000..2e852c3371
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
> @@ -0,0 +1,988 @@
> +/** @file
> +
> +  CPU Memory Map Unit Handler Library common functions.
> +
> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +  @par Glossary:
> +    - Pgd or Pgd or PGD    - Page Global Directory
> +    - Pud or Pud or PUD    - Page Upper Directory
> +    - Pmd or Pmd or PMD    - Page Middle Directory
> +    - Pte or pte or PTE    - Page Table Entry
> +    - Val or VAL or val    - Value
> +    - Dir    - Directory
> +**/
> +#include <Uefi.h>
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/CpuMmuLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Register/LoongArch64/Csr.h>
> +#include "Tlb.h"
> +#include "Page.h"
> +
> +#define SWAP_PAGE_DIR              CsrRead(LOONGARCH_CSR_PGDL)

(2) Missing space after "CsrRead".

> +#define EFI_MEMORY_CACHETYPE_MASK  (EFI_MEMORY_UC  | \
> +                                    EFI_MEMORY_WC  | \
> +                                    EFI_MEMORY_WT  | \
> +                                    EFI_MEMORY_WB  | \
> +                                    EFI_MEMORY_UCE   \
> +                                    )

(3) This seems to come from "ArmPkg/Include/Library/ArmLib.h"; it's not
great regardless: we shouldn't use the EFI_ prefix for symbols that are
not standard. (Put differently, EFI_ is a reserved namespace prefix for
the UEFI and PI specs.)

> +
> +BOOLEAN  mMmuInited = FALSE;

(4) This should be STATIC, I believe.

(5) So this is the point where I realize that the library design makes
no sense to me.

Normally you create SEC/PEI vs. DXE phase instances of a library
because, using writable global variables, you can gain performance (you
can remember the initialization) in DXE, but in SEC/PEI, you don't have
writeable global variables.

This pattern does not seem to apply here, or at least it doesn't seem to
work. Here's why:

- the variable mMmuInited and the function MmuInitialize (which contains
an assignment to the variable) are in "CommonMmuLib.c", which gets built
into both library instances. This makes no sense; that assignment cannot
work in SEC/PEI (I presume you're not going to have writeable globals --
executing from flash).

- I think you may be trying to make up for that problem by checking
SWAP_PAGE_DIR (the LOONGARCH_CSR_PGDL register) in MmuInitialize() and
MmuIsInit(). That doesn't seem right.

- The PEI instance of the library contains an EFIAPI function called
ConfigureMemoryManagementUnit(). This function is never called in this
patch, which makes me think it's supposed to be called from driver or
application code (i.e., not from within the library itself, but from
client code). However, ConfigureMemoryManagementUnit() is also not
declared in the previous patch (in
"UefiCpuPkg/Include/Library/CpuMmuLib.h"); therefore client code cannot
reach it at all -- so that function doesn't even belong in this library.

- MmuInitialize() doesn't actually do anything, it just checks (via the
CSR) whether some other component has already initialized the MMU
(likely with ConfigureMemoryManagementUnit()). And the sole purpose of
MmuIsInit() appears to be to return from the public library APIs
SetMemoryRegionAttributes() and GetMemoryRegionAttributes() early, if
that "other" (unknown) component has not called
ConfigureMemoryManagementUnit() yet.

So, I don't understand what you are trying to do. I could explain how to
keep the initialization logic *differences* minimal between the SEC/PEI
and the DXE library instances, but I don't understand how / when the MMU
initialization is supposed to occur in the first place.

(6) The patch is too large in general. You should construct these
library instances over multiple patches. The first patch could add some
declarations / macro definitions, such as "Page.h" and "Tlb.h". Another
patch could add the assembly language helper functions. Another patch
could add the PEI phase library instance (including the common code). A
final patch could add the DXE-phase bits.

The idea is to proceed in layers, logically building one on top of the
other. It's fine if the library doesn't build initially; there is no
attempt to build it anyway until you actually reference the INF files in
some DSC files. So please split this at least in 4 patches.

(7) The commit message should explain the expected usage model in detail.

Best regards,
Laszlo


> +
> +/**
> +  Check to see if mmu successfully initializes.
> +
> +  @param  VOID.
> +
> +  @retval  TRUE  Initialization has been completed.
> +           FALSE Initialization did not complete.
> +**/
> +STATIC
> +BOOLEAN
> +MmuIsInit (
> +  VOID
> +  )
> +{
> +  if (mMmuInited || (SWAP_PAGE_DIR != 0)) {
> +    return TRUE;
> +  }
> +
> +  return FALSE;
> +}
> +
> +/**
> +  Iterates through the page directory to initialize it.
> +
> +  @param  Dst  A pointer to the directory of the page to initialize.
> +  @param  Num  The number of page directories to initialize.
> +  @param  Src  A pointer to the data used to initialize the page directory.
> +
> +  @return VOID.
> +**/
> +STATIC
> +VOID
> +PageDirInit (
> +  IN VOID   *Dst,
> +  IN UINTN  Num,
> +  IN VOID   *Src
> +  )
> +{
> +  UINTN  *Ptr;
> +  UINTN  *End;
> +  UINTN  Entry;
> +
> +  Entry = (UINTN)Src;
> +  Ptr   = (UINTN *)Dst;
> +  End   = Ptr + Num;
> +
> +  for ( ; Ptr < End; Ptr++) {
> +    *Ptr = Entry;
> +  }
> +
> +  return;
> +}
> +
> +/**
> +  Gets the virtual address corresponding to the page global directory table entry.
> +
> +  @param  Address  the virtual address for the table entry.
> +
> +  @retval PGD A pointer to get the table item.
> +**/
> +STATIC
> +PGD *
> +PgdOffset (
> +  IN UINTN  Address
> +  )
> +{
> +  return (PGD *)(SWAP_PAGE_DIR) + PGD_INDEX (Address);
> +}
> +
> +/**
> +  Gets the virtual address corresponding to the page upper directory table entry.
> +
> +  @param  Pgd  A pointer to a page global directory table entry.
> +  @param  Address  the virtual address for the table entry.
> +
> +  @retval PUD A pointer to get the table item.
> +**/
> +STATIC
> +PUD *
> +PudOffset (
> +  IN PGD    *Pgd,
> +  IN UINTN  Address
> +  )
> +{
> +  UINTN  PgdVal;
> +
> +  PgdVal = (UINTN)PGD_VAL (*Pgd);
> +
> +  return (PUD *)PgdVal + PUD_INDEX (Address);
> +}
> +
> +/**
> +  Gets the virtual address corresponding to the page middle directory table entry.
> +
> +  @param  Pud  A pointer to a page upper directory table entry.
> +  @param  Address  the virtual address for the table entry.
> +
> +  @retval PMD A pointer to get the table item.
> +**/
> +STATIC
> +PMD *
> +PmdOffset (
> +  IN PUD    *Pud,
> +  IN UINTN  Address
> +  )
> +{
> +  UINTN  PudVal;
> +
> +  PudVal = PUD_VAL (*Pud);
> +
> +  return (PMD *)PudVal + PMD_INDEX (Address);
> +}
> +
> +/**
> +  Gets the virtual address corresponding to the page table entry.
> +
> +  @param  Pmd  A pointer to a page middle directory table entry.
> +  @param  Address  the virtual address for the table entry.
> +
> +  @retval PTE A pointer to get the table item.
> +**/
> +STATIC
> +PTE *
> +PteOffset (
> +  IN PMD    *Pmd,
> +  IN UINTN  Address
> +  )
> +{
> +  UINTN  PmdVal;
> +
> +  PmdVal = (UINTN)PMD_VAL (*Pmd);
> +
> +  return (PTE *)PmdVal + PTE_INDEX (Address);
> +}
> +
> +/**
> +  Sets the value of the page table entry.
> +
> +  @param  Pte  A pointer to a page table entry.
> +  @param  PteVal  The value of the page table entry to set.
> +
> +**/
> +STATIC
> +VOID
> +SetPte (
> +  IN PTE  *Pte,
> +  IN PTE  PteVal
> +  )
> +{
> +  *Pte = PteVal;
> +}
> +
> +/**
> +  Sets the value of the page global directory.
> +
> +  @param  Pgd  A pointer to a page global directory.
> +  @param  Pud  The value of the page global directory to set.
> +
> +**/
> +STATIC
> +VOID
> +SetPgd (
> +  IN PGD  *Pgd,
> +  IN PUD  *Pud
> +  )
> +{
> +  *Pgd = (PGD) {
> +    ((UINTN)Pud)
> +  };
> +}
> +
> +/**
> +  Sets the value of the page upper directory.
> +
> +  @param  Pud  A pointer to a page upper directory.
> +  @param  Pmd  The value of the page upper directory to set.
> +
> +**/
> +STATIC
> +VOID
> +SetPud (
> +  IN PUD  *Pud,
> +  IN PMD  *Pmd
> +  )
> +{
> +  *Pud = (PUD) {
> +    ((UINTN)Pmd)
> +  };
> +}
> +
> +/**
> +  Sets the value of the page middle directory.
> +
> +  @param  Pmd  A pointer to a page middle directory.
> +  @param  Pte  The value of the page middle directory to set.
> +
> +**/
> +STATIC
> +VOID
> +SetPmd (
> +  IN PMD  *Pmd,
> +  IN PTE  *Pte
> +  )
> +{
> +  *Pmd = (PMD) {
> +    ((UINTN)Pte)
> +  };
> +}
> +
> +/**
> +  Free up memory space occupied by page tables.
> +
> +  @param  Pte  A pointer to the page table.
> +
> +**/
> +VOID
> +PteFree (
> +  IN PTE  *Pte
> +  )
> +{
> +  FreePages ((VOID *)Pte, 1);
> +}
> +
> +/**
> +  Free up memory space occupied by page middle directory.
> +
> +  @param  Pmd  A pointer to the page middle directory.
> +
> +**/
> +VOID
> +PmdFree (
> +  IN PMD  *Pmd
> +  )
> +{
> +  FreePages ((VOID *)Pmd, 1);
> +}
> +
> +/**
> +  Free up memory space occupied by page upper directory.
> +
> +  @param  Pud  A pointer to the page upper directory.
> +
> +**/
> +VOID
> +PudFree (
> +  IN PUD  *Pud
> +  )
> +{
> +  FreePages ((VOID *)Pud, 1);
> +}
> +
> +/**
> +  Requests the memory space required for the page upper directory,
> +  initializes it, and places it in the specified page global directory
> +
> +  @param  Pgd  A pointer to the page global directory.
> +
> +  @retval  EFI_SUCCESS  Memory request successful.
> +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
> +**/
> +STATIC
> +EFI_STATUS
> +PudAlloc (
> +  IN PGD  *Pgd
> +  )
> +{
> +  PUD  *Pud;
> +
> +  Pud = (PUD *)AllocatePages (1);
> +  if (Pud == NULL) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  PageDirInit ((VOID *)Pud, ENTRYS_PER_PUD, (VOID *)INVALID_PAGE);
> +
> +  SetPgd (Pgd, Pud);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Requests the memory space required for the page middle directory,
> +  initializes it, and places it in the specified page upper directory
> +
> +  @param  Pud  A pointer to the page upper directory.
> +
> +  @retval  EFI_SUCCESS  Memory request successful.
> +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
> +**/
> +STATIC
> +EFI_STATUS
> +PmdAlloc (
> +  IN PUD  *Pud
> +  )
> +{
> +  PMD  *Pmd;
> +
> +  Pmd = (PMD *)AllocatePages (1);
> +  if (!Pmd) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  PageDirInit ((VOID *)Pmd, ENTRYS_PER_PMD, (VOID *)INVALID_PAGE);
> +
> +  SetPud (Pud, Pmd);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Requests the memory space required for the page table,
> +  initializes it, and places it in the specified page middle directory
> +
> +  @param  Pmd  A pointer to the page middle directory.
> +
> +  @retval  EFI_SUCCESS  Memory request successful.
> +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
> +**/
> +STATIC
> +EFI_STATUS
> +PteAlloc (
> +  IN PMD  *Pmd
> +  )
> +{
> +  PTE  *Pte;
> +
> +  Pte = (PTE *)AllocatePages (1);
> +  if (!Pte) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  Pte = ZeroMem (Pte, EFI_PAGE_SIZE);
> +
> +  SetPmd (Pmd, Pte);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Requests the memory space required for the page upper directory,
> +  initializes it, and places it in the specified page global directory,
> +  and get the page upper directory entry corresponding to the virtual address.
> +
> +  @param  Pgd      A pointer to the page global directory.
> +  @param  Address  The corresponding virtual address of the page table entry.
> +
> +  @retval          A pointer to the page upper directory entry. Return NULL, if
> +                   allocate the memory buffer is fail.
> +**/
> +STATIC
> +PUD *
> +PudAllocGet (
> +  IN PGD    *Pgd,
> +  IN UINTN  Address
> +  )
> +{
> +  EFI_STATUS  Status;
> +
> +  if (PGD_IS_EMPTY (*Pgd)) {
> +    Status = PudAlloc (Pgd);
> +    ASSERT_EFI_ERROR (Status);
> +    if (EFI_ERROR (Status)) {
> +      return NULL;
> +    }
> +  }
> +
> +  return PudOffset (Pgd, Address);
> +}
> +
> +/**
> +  Requests the memory space required for the page middle directory,
> +  initializes it, and places it in the specified page upper directory,
> +  and get the page middle directory entry corresponding to the virtual address.
> +
> +  @param  Pud      A pointer to the page upper directory.
> +  @param  Address  The corresponding virtual address of the page table entry.
> +
> +  @retval          A pointer to the page middle directory entry. Return NULL, if
> +                   allocate the memory buffer is fail.
> +**/
> +STATIC
> +PMD *
> +PmdAllocGet (
> +  IN PUD    *Pud,
> +  IN UINTN  Address
> +  )
> +{
> +  EFI_STATUS  Status;
> +
> +  if (PUD_IS_EMPTY (*Pud)) {
> +    Status = PmdAlloc (Pud);
> +    ASSERT_EFI_ERROR (Status);
> +    if (EFI_ERROR (Status)) {
> +      return NULL;
> +    }
> +  }
> +
> +  return PmdOffset (Pud, Address);
> +}
> +
> +/**
> +  Requests the memory space required for the page table,
> +  initializes it, and places it in the specified page middle directory,
> +  and get the page table entry corresponding to the virtual address.
> +
> +  @param  Pmd      A pointer to the page upper directory.
> +  @param  Address  The corresponding virtual address of the page table entry.
> +
> +  @retval          A pointer to the page table entry. Return NULL, if allocate
> +                   the memory buffer is fail.
> +**/
> +STATIC
> +PTE *
> +PteAllocGet (
> +  IN PMD    *Pmd,
> +  IN UINTN  Address
> +  )
> +{
> +  EFI_STATUS  Status;
> +
> +  if (PMD_IS_EMPTY (*Pmd)) {
> +    Status = PteAlloc (Pmd);
> +    ASSERT_EFI_ERROR (Status);
> +    if (EFI_ERROR (Status)) {
> +      return NULL;
> +    }
> +  }
> +
> +  return PteOffset (Pmd, Address);
> +}
> +
> +/**
> +  Gets the physical address of the page table entry corresponding to the specified virtual address.
> +
> +  @param  Address  The corresponding virtual address of the page table entry.
> +
> +  @retval  A pointer to the page table entry.
> +  @retval  NULL
> +**/
> +STATIC
> +PTE *
> +GetPteAddress (
> +  IN UINTN  Address
> +  )
> +{
> +  PGD  *Pgd;
> +  PUD  *Pud;
> +  PMD  *Pmd;
> +
> +  Pgd = PgdOffset (Address);
> +
> +  if (PGD_IS_EMPTY (*Pgd)) {
> +    return NULL;
> +  }
> +
> +  Pud = PudOffset (Pgd, Address);
> +
> +  if (PUD_IS_EMPTY (*Pud)) {
> +    return NULL;
> +  }
> +
> +  Pmd = PmdOffset (Pud, Address);
> +  if (PMD_IS_EMPTY (*Pmd)) {
> +    return NULL;
> +  }
> +
> +  if (IS_HUGE_PAGE (Pmd->PmdVal)) {
> +    return ((PTE *)Pmd);
> +  }
> +
> +  return PteOffset (Pmd, Address);
> +}
> +
> +/**
> +  Gets the Attributes of Huge Page.
> +
> +  @param  Pmd  A pointer to the page middle directory.
> +
> +  @retval     Value of Attributes.
> +**/
> +STATIC
> +UINTN
> +GetHugePageAttributes (
> +  IN  PMD  *Pmd
> +  )
> +{
> +  UINTN  Attributes;
> +  UINTN  GlobalFlag;
> +  UINTN  HugeVal;
> +
> +  HugeVal     = PMD_VAL (*Pmd);
> +  Attributes  = HugeVal & (~HUGEP_PAGE_MASK);
> +  GlobalFlag  = ((Attributes & (1 << PAGE_HGLOBAL_SHIFT)) >> PAGE_HGLOBAL_SHIFT) << PAGE_GLOBAL_SHIFT;
> +  Attributes &= ~(1 << PAGE_HGLOBAL_SHIFT);
> +  Attributes |= GlobalFlag;
> +  return Attributes;
> +}
> +
> +/**
> +  Establishes a page table entry based on the specified memory region.
> +
> +  @param  Pmd  A pointer to the page middle directory.
> +  @param  Address  The memory space start address.
> +  @param  End  The end address of the memory space.
> +  @param  Attributes  Memory space Attributes.
> +
> +  @retval     EFI_SUCCESS   The page table entry was created successfully.
> +  @retval     EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.
> +**/
> +STATIC
> +EFI_STATUS
> +MemoryMapPteRange (
> +  IN PMD    *Pmd,
> +  IN UINTN  Address,
> +  IN UINTN  End,
> +  IN UINTN  Attributes
> +  )
> +{
> +  PTE      *Pte;
> +  PTE      PteVal;
> +  BOOLEAN  UpDate;
> +
> +  Pte = PteAllocGet (Pmd, Address);
> +  if (!Pte) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  DEBUG ((
> +    DEBUG_INFO,
> +    "%a %d Address %p End %p  Attributes %llx\n",
> +    __func__,
> +    __LINE__,
> +    Address,
> +    End,
> +    Attributes
> +    ));
> +
> +  do {
> +    UpDate = FALSE;
> +    PteVal = MAKE_PTE (Address, Attributes);
> +
> +    if ((!PTE_IS_EMPTY (*Pte)) &&
> +        (PTE_VAL (*Pte) != PTE_VAL (PteVal)))
> +    {
> +      UpDate = TRUE;
> +    }
> +
> +    SetPte (Pte, PteVal);
> +    if (UpDate) {
> +      InvalidTlb (Address);
> +    }
> +  } while (Pte++, Address += EFI_PAGE_SIZE, Address != End);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Convert Huge Page to Page.
> +
> +  @param  Pmd  A pointer to the page middle directory.
> +  @param  Address  The memory space start address.
> +  @param  End  The end address of the memory space.
> +  @param  Attributes  Memory space Attributes.
> +
> +  @retval  EFI_SUCCESS   The page table entry was created successfully.
> +  @retval  EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.
> +**/
> +STATIC
> +EFI_STATUS
> +ConvertHugePageToPage (
> +  IN  PMD   *Pmd,
> +  IN UINTN  Address,
> +  IN UINTN  End,
> +  IN UINTN  Attributes
> +  )
> +{
> +  UINTN       OldAttributes;
> +  UINTN       HugePageEnd;
> +  UINTN       HugePageStart;
> +  EFI_STATUS  Status;
> +
> +  Status = EFI_SUCCESS;
> +
> +  if ((PMD_IS_EMPTY (*Pmd)) ||
> +      (!IS_HUGE_PAGE (Pmd->PmdVal)))
> +  {
> +    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
> +  } else {
> +    OldAttributes = GetHugePageAttributes (Pmd);
> +    if (Attributes == OldAttributes) {
> +      return Status;
> +    }
> +
> +    SetPmd (Pmd, (PTE *)(INVALID_PAGE));
> +    HugePageStart = Address & PMD_MASK;
> +    HugePageEnd   = HugePageStart + HUGE_PAGE_SIZE;
> +    ASSERT (HugePageEnd >= End);
> +
> +    if (Address > HugePageStart) {
> +      Status |= MemoryMapPteRange (Pmd, HugePageStart, Address, OldAttributes);
> +    }
> +
> +    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
> +
> +    if (End < HugePageEnd) {
> +      Status |= MemoryMapPteRange (Pmd, End, HugePageEnd, OldAttributes);
> +    }
> +  }
> +
> +  return Status;
> +}
> +
> +/**
> +  Establishes a page middle directory based on the specified memory region.
> +
> +  @param  Pud  A pointer to the page upper directory.
> +  @param  Address  The memory space start address.
> +  @param  End  The end address of the memory space.
> +  @param  Attributes  Memory space Attributes.
> +
> +  @retval     EFI_SUCCESS   The page middle directory was created successfully.
> +  @retval     EFI_OUT_OF_RESOURCES  Page middle directory establishment failed due to resource exhaustion.
> +**/
> +STATIC
> +EFI_STATUS
> +MemoryMapPmdRange (
> +  IN PUD    *Pud,
> +  IN UINTN  Address,
> +  IN UINTN  End,
> +  IN UINTN  Attributes
> +  )
> +{
> +  PMD      *Pmd;
> +  UINTN    Next;
> +  PTE      PteVal;
> +  BOOLEAN  UpDate;
> +
> +  Pmd = PmdAllocGet (Pud, Address);
> +  if (Pmd == NULL) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  do {
> +    Next = PMD_ADDRESS_END (Address, End);
> +    if (((Address & (~PMD_MASK)) == 0) &&
> +        ((Next &  (~PMD_MASK)) == 0) &&
> +        (PMD_IS_EMPTY (*Pmd) || IS_HUGE_PAGE (Pmd->PmdVal)))
> +    {
> +      UpDate = FALSE;
> +      PteVal = MAKE_HUGE_PTE (Address, Attributes);
> +
> +      if ((!PMD_IS_EMPTY (*Pmd)) &&
> +          (PMD_VAL (*Pmd) != PTE_VAL (PteVal)))
> +      {
> +        UpDate = TRUE;
> +      }
> +
> +      SetPmd (Pmd, (PTE *)PteVal.PteVal);
> +      if (UpDate) {
> +        InvalidTlb (Address);
> +      }
> +    } else {
> +      ConvertHugePageToPage (Pmd, Address, Next, Attributes);
> +    }
> +  } while (Pmd++, Address = Next, Address != End);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Establishes a page upper directory based on the specified memory region.
> +
> +  @param  Pgd  A pointer to the page global directory.
> +  @param  Address  The memory space start address.
> +  @param  End  The end address of the memory space.
> +  @param  Attributes  Memory space Attributes.
> +
> +  @retval     EFI_SUCCESS   The page upper directory was created successfully.
> +  @retval     EFI_OUT_OF_RESOURCES  Page upper directory establishment failed due to resource exhaustion.
> +**/
> +STATIC
> +EFI_STATUS
> +MemoryMapPudRange (
> +  IN PGD    *Pgd,
> +  IN UINTN  Address,
> +  IN UINTN  End,
> +  IN UINTN  Attributes
> +  )
> +{
> +  PUD    *Pud;
> +  UINTN  Next;
> +
> +  Pud = PudAllocGet (Pgd, Address);
> +  if (Pud == NULL) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  do {
> +    Next = PUD_ADDRESS_END (Address, End);
> +    if (EFI_ERROR (MemoryMapPmdRange (Pud, Address, Next, Attributes))) {
> +      return EFI_OUT_OF_RESOURCES;
> +    }
> +  } while (Pud++, Address = Next, Address != End);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Establishes a page global directory based on the specified memory region.
> +
> +  @param  Start  The memory space start address.
> +  @param  End  The end address of the memory space.
> +  @param  Attributes  Memory space Attributes.
> +
> +  @retval     EFI_SUCCESS   The page global directory was created successfully.
> +  @retval     EFI_OUT_OF_RESOURCES  Page global directory establishment failed due to resource exhaustion.
> +**/
> +STATIC
> +EFI_STATUS
> +MemoryMapPageRange (
> +  IN UINTN  Start,
> +  IN UINTN  End,
> +  IN UINTN  Attributes
> +  )
> +{
> +  PGD         *Pgd;
> +  UINTN       Next;
> +  UINTN       Address;
> +  EFI_STATUS  Err;
> +
> +  Address = Start;
> +
> +  /* Get PGD(PTE PMD PUD PGD) in PageTables */
> +  Pgd = PgdOffset (Address);
> +  do {
> +    Next = PGD_ADDRESS_END (Address, End);
> +    /* Get Next Align Page to Map */
> +    Err = MemoryMapPudRange (Pgd, Address, Next, Attributes);
> +    if (Err) {
> +      return Err;
> +    }
> +  } while (Pgd++, Address = Next, Address != End);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Page tables are established from memory-mapped tables.
> +
> +  @param  MemoryRegion   A pointer to a memory-mapped table entry.
> +
> +  @retval     EFI_SUCCESS   The page table was created successfully.
> +  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
> +**/
> +EFI_STATUS
> +FillTranslationTable (
> +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
> +  )
> +{
> +  return MemoryMapPageRange (
> +           MemoryRegion->VirtualBase,
> +           (MemoryRegion->Length + MemoryRegion->VirtualBase),
> +           MemoryRegion->Attributes
> +           );
> +}
> +
> +/**
> +  Convert EFI Attributes to Loongarch Attributes.
> +
> +  @param[in]  EfiAttributes     Efi Attributes.
> +
> +  @retval  Corresponding architecture attributes.
> +**/
> +UINTN
> +EFIAPI
> +EfiAttributeConverse (
> +  IN UINTN  EfiAttributes
> +  )
> +{
> +  UINTN  LoongArchAttributes;
> +
> +  LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | PLV_KERNEL | PAGE_GLOBAL;
> +
> +  switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
> +    case EFI_MEMORY_UC:
> +      LoongArchAttributes |= CACHE_SUC;
> +      break;
> +    case EFI_MEMORY_WC:
> +      LoongArchAttributes |= CACHE_WUC;
> +      break;
> +    case EFI_MEMORY_WT:
> +    case EFI_MEMORY_WB:
> +      LoongArchAttributes |= CACHE_CC;
> +      break;
> +    default:
> +      LoongArchAttributes |= CACHE_CC;
> +      break;
> +  }
> +
> +  // Write protection attributes
> +  if (((EfiAttributes & EFI_MEMORY_RO) != 0) ||
> +      ((EfiAttributes & EFI_MEMORY_WP) != 0))
> +  {
> +    LoongArchAttributes &= ~PAGE_DIRTY;
> +  }
> +
> +  if ((EfiAttributes & EFI_MEMORY_RP) != 0) {
> +    LoongArchAttributes |= PAGE_NO_READ;
> +  }
> +
> +  // eXecute protection attribute
> +  if ((EfiAttributes & EFI_MEMORY_XP) != 0) {
> +    LoongArchAttributes |= PAGE_NO_EXEC;
> +  }
> +
> +  return LoongArchAttributes;
> +}
> +
> +/**
> +  Finds the first of the length and memory properties of the memory region corresponding
> +  to the specified base address.
> +
> +  @param[in]       BaseAddress       To find the base address of the memory region.
> +  @param[in, out]  RegionLength      Pointer holding:
> +                                      - At entry, the length of the memory region
> +                                        expected to be found.
> +                                      - At exit, the length of the memory region found.
> +  @param[out]      RegionAttributes  Properties of the memory region found.
> +
> +  @retval  EFI_SUCCESS           The corresponding memory area was successfully found
> +           EFI_NOT_FOUND         No memory area found
> +           EFI_OUT_OF_RESOURCES  Base address or expected memory region exceeds the maximum
> +                                 address.
> +**/
> +EFI_STATUS
> +EFIAPI
> +GetMemoryRegionAttributes (
> +  IN     UINTN  BaseAddress,
> +  IN OUT UINTN  *RegionLength,
> +  OUT    UINTN  *RegionAttributes
> +  )
> +{
> +  PTE    *Pte;
> +  UINTN  Attributes;
> +  UINTN  AttributesTmp;
> +  UINTN  MaxAddress;
> +  UINTN  EndAddress;
> +  UINTN  AddSize;
> +
> +  if (!MmuIsInit ()) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  EndAddress = BaseAddress + *RegionLength;
> +  MaxAddress = LShiftU64 (1ULL, MAX_VA_BITS) - 1;
> +
> +  // Clean the value to prepare output to find region size.
> +  *RegionLength = 0x0;
> +
> +  if ((BaseAddress >= MaxAddress) || (EndAddress >= MaxAddress)) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  Pte = GetPteAddress (BaseAddress);
> +
> +  if (Pte == NULL) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  Attributes = GET_PAGE_ATTRIBUTES (*Pte);
> +  if (IS_HUGE_PAGE (Pte->PteVal)) {
> +    *RegionAttributes = Attributes & (~(PAGE_HUGE));
> +  } else {
> +    *RegionAttributes = Attributes;
> +  }
> +
> +  do {
> +    Pte = GetPteAddress (BaseAddress);
> +    if (Pte == NULL) {
> +      return EFI_SUCCESS;
> +    }
> +
> +    AttributesTmp = GET_PAGE_ATTRIBUTES (*Pte);
> +    if (AttributesTmp == Attributes) {
> +      if (IS_HUGE_PAGE (Pte->PteVal)) {
> +        AddSize = HUGE_PAGE_SIZE;
> +      } else {
> +        AddSize = EFI_PAGE_SIZE;
> +      }
> +
> +      *RegionLength += AddSize;
> +      BaseAddress   += AddSize;
> +    } else {
> +      return EFI_SUCCESS;
> +    }
> +  } while (BaseAddress <= EndAddress);
> +
> +  return EFI_SUCCESS;
> +}
> +
> +/**
> +  Sets the Attributes  of the specified memory region
> +
> +  @param[in]  BaseAddress    The base address of the memory region to set the Attributes.
> +  @param[in]  Length         The length of the memory region to set the Attributes.
> +  @param[in]  Attributes     The Attributes to be set.
> +  @param[in]  AttributeMask  Mask of memory attributes to take into account.
> +
> +  @retval  EFI_SUCCESS    The Attributes was set successfully
> +**/
> +EFI_STATUS
> +EFIAPI
> +SetMemoryRegionAttributes (
> +  IN EFI_PHYSICAL_ADDRESS  BaseAddress,
> +  IN UINTN                 Length,
> +  IN UINTN                 Attributes,
> +  IN UINT64                AttributeMask
> +  )
> +{
> +  EFI_STATUS  Status;
> +
> +  if (!MmuIsInit ()) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  Attributes = EfiAttributeConverse (Attributes);
> +  Status     = MemoryMapPageRange (BaseAddress, BaseAddress + Length, Attributes);
> +  ASSERT_EFI_ERROR (Status);
> +
> +  return Status;
> +}
> +
> +/**
> +  Check to see if mmu successfully initializes and saves the result.
> +
> +  @param[in]  ImageHandle  The firmware allocated handle for the EFI image.
> +  @param[in]  SystemTable  A pointer to the EFI System Table.
> +
> +  @retval  RETURN_SUCCESS    Initialization succeeded.
> +**/
> +RETURN_STATUS
> +MmuInitialize (
> +  IN EFI_HANDLE        ImageHandle,
> +  IN EFI_SYSTEM_TABLE  *SystemTable
> +  )
> +{
> +  if (SWAP_PAGE_DIR != 0) {
> +    mMmuInited = TRUE;
> +  }
> +
> +  return RETURN_SUCCESS;
> +}
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
> new file mode 100644
> index 0000000000..d8c922c8fa
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
> @@ -0,0 +1,43 @@
> +/** @file
> +
> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +  @par Glossary:
> +    - Dir    - Directory
> +**/
> +
> +#ifndef  MMU_LIB_CORE_H_
> +#define  MMU_LIB_CORE_H_
> +
> +/**
> +  Iterates through the page directory to initialize it.
> +
> +  @param  Dst  A pointer to the directory of the page to initialize.
> +  @param  Num  The number of page directories to initialize.
> +  @param  Src  A pointer to the data used to initialize the page directory.
> +
> +  @retval VOID.
> +**/
> +VOID
> +PageDirInit (
> +  IN VOID   *dest,
> +  IN UINTN  Count,
> +  IN VOID   *src
> +  );
> +
> +/**
> +  Page tables are established from memory-mapped tables.
> +
> +  @param  MemoryRegion   A pointer to a memory-mapped table entry.
> +
> +  @retval     EFI_SUCCESS   The page table was created successfully.
> +  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
> +**/
> +EFI_STATUS
> +FillTranslationTable (
> +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
> +  );
> +
> +#endif // MMU_LIB_CORE_H_
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
> new file mode 100644
> index 0000000000..bac4f52327
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
> @@ -0,0 +1,279 @@
> +/** @file
> +
> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +  @par Glossary:
> +    - Pgd or Pgd or PGD    - Page Global Directory
> +    - Pud or Pud or PUD    - Page Upper Directory
> +    - Pmd or Pmd or PMD    - Page Middle Directory
> +    - Pte or pte or PTE    - Page Table Entry
> +    - Val or VAL or val    - Value
> +    - Dir    - Directory
> +**/
> +
> +#ifndef PAGE_H_
> +#define PAGE_H_
> +
> +#include <Library/CpuMmuLib.h>
> +
> +#define MAX_VA_BITS  47
> +#define PGD_WIDE     (8)
> +#define PUD_WIDE     (9)
> +#define PMD_WIDE     (9)
> +#define PTE_WIDE     (9)
> +
> +#define ENTRYS_PER_PGD  (1 << PGD_WIDE)
> +#define ENTRYS_PER_PUD  (1 << PUD_WIDE)
> +#define ENTRYS_PER_PMD  (1 << PMD_WIDE)
> +#define ENTRYS_PER_PTE  (1 << PTE_WIDE)
> +
> +#define PGD_SHIFT  (PUD_SHIFT + PUD_WIDE)
> +#define PUD_SHIFT  (PMD_SHIFT + PMD_WIDE)
> +#define PMD_SHIFT  (EFI_PAGE_SHIFT + PTE_WIDE)
> +#define PTE_SHIFT  (EFI_PAGE_SHIFT)
> +
> +#define PGD_SIZE  (1UL << PGD_SHIFT)
> +#define PUD_SIZE  (1UL << PUD_SHIFT)
> +#define PMD_SIZE  (1UL << PMD_SHIFT)
> +
> +#define PGD_MASK   (~(PGD_SIZE-1))
> +#define PUD_MASK   (~(PUD_SIZE-1))
> +#define PMD_MASK   (~(PMD_SIZE-1))
> +#define PAGE_MASK  (~(EFI_PAGE_SIZE - 1))
> +#define PFN_MASK   (~(((UINTN)(1) << (EFI_PAGE_SHIFT)) - 1) & \
> +                   (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
> +
> +#define HUGEP_PAGE_MASK  (~(((UINTN)(1) << (PMD_SHIFT)) - 1) & \
> +                          (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
> +
> +#define INVALID_PAGE  0
> +
> +typedef struct {
> +  UINTN    PgdVal;
> +} PGD;
> +typedef struct {
> +  UINTN    PudVal;
> +} PUD;
> +typedef struct {
> +  UINTN    PmdVal;
> +} PMD;
> +typedef struct {
> +  UINTN    PteVal;
> +} PTE;
> +
> +/**
> +  Gets the value of the page global directory table entry.
> +
> +  @param  x    Page global directory struct variables.
> +
> +  @retval   the value of the page global directory table entry.
> + **/
> +#define PGD_VAL(x)  ((x).PgdVal)
> +
> +/**
> +  Gets the value of the page upper directory table entry.
> +
> +  @param  x    Page upper directory struct variables.
> +
> +  @retval  the value of the page upper directory table entry.
> + **/
> +#define PUD_VAL(x)  ((x).PudVal)
> +
> +/**
> +  Gets the value of the page middle directory table entry.
> +
> +  @param  x    Page middle directory struct variables.
> +
> +  @retval  the value of the page middle directory table entry.
> + **/
> +#define PMD_VAL(x)  ((x).PmdVal)
> +
> +/**
> +  Gets the value of the page table entry.
> +
> +  @param  x    Page table entry struct variables.
> +
> +  @retval  the value of the page table entry.
> + **/
> +#define PTE_VAL(x)  ((x).PteVal)
> +
> +#define PGD_TABLE_SIZE  (ENTRYS_PER_PGD * sizeof(PGD))
> +#define PUD_TABLE_SIZE  (ENTRYS_PER_PUD * sizeof(PUD))
> +#define PMD_TABLE_SIZE  (ENTRYS_PER_PMD * sizeof(PMD))
> +#define PTE_TABLE_SIZE  (ENTRYS_PER_PTE * sizeof(PTE))
> +
> +/**
> +  Gets the physical address of the record in the page table entry.
> +
> +  @param  x    Page table entry struct variables.
> +
> +  @retval  the value of the physical address.
> + **/
> +#define GET_PAGE_ATTRIBUTES(x)  (UINTN) {(PTE_VAL(x) & ~PFN_MASK)}
> +
> +/**
> +  Gets the virtual address of the next block of the specified virtual address
> +  that is aligned with the size of the global page directory mapping.
> +
> +  @param  Address  Specifies the virtual address.
> +  @param  End    The end address of the memory region.
> +
> +  @retval   the specified virtual address  of the next block.
> + **/
> +#define PGD_ADDRESS_END(Address, End)                  \
> +({                                                     \
> +  UINTN Boundary = ((Address) + PGD_SIZE) & PGD_MASK;  \
> +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
> +})
> +
> +/**
> +  Gets the virtual address of the next block of the specified virtual address
> +  that is aligned with the size of the page upper directory mapping.
> +
> +  @param  Address  Specifies the virtual address.
> +  @param  End    The end address of the memory region.
> +
> +  @retval   the specified virtual address  of the next block.
> + **/
> +#define PUD_ADDRESS_END(Address, End)                  \
> +({                                                     \
> +  UINTN Boundary = ((Address) + PUD_SIZE) & PUD_MASK;  \
> +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
> +})
> +
> +/**
> +  Gets the virtual address of the next block of the specified virtual address
> +  that is aligned with the size of the page middle directory mapping.
> +
> +  @param  Address  Specifies the virtual address.
> +  @param  End    The end address of the memory region.
> +
> +  @retval   the specified virtual address  of the next block.
> + **/
> +#define PMD_ADDRESS_END(Address, End)                  \
> +({                                                     \
> +  UINTN Boundary = ((Address) + PMD_SIZE) & PMD_MASK;  \
> +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
> +})
> +
> +/**
> +  Get Specifies the virtual address corresponding to the index of the page global directory table entry.
> +
> +  @param  Address  Specifies the virtual address.
> +
> +  @retval   the index of the page global directory table entry.
> + **/
> +#define PGD_INDEX(Address)  (((Address) >> PGD_SHIFT) & (ENTRYS_PER_PGD-1))
> +
> +/**
> +  Get Specifies the virtual address corresponding to the index of the page upper directory table entry.
> +
> +  @param  Address  Specifies the virtual address.
> +  @param  End    The end address of the memory region.
> +
> +  @retval   the index of the page upper directory table entry.
> + **/
> +#define PUD_INDEX(Address)  (((Address) >> PUD_SHIFT) & (ENTRYS_PER_PUD - 1))
> +
> +/**
> +  Get Specifies the virtual address corresponding to the index of the page middle directory table entry.
> +
> +  @param  Address  Specifies the virtual address.
> +
> +  @retval   the index of the page middle directory table entry.
> + **/
> +#define PMD_INDEX(Address)  (((Address) >> PMD_SHIFT) & (ENTRYS_PER_PMD - 1))
> +
> +/**
> +  Get Specifies the virtual address corresponding to the index of the page table entry.
> +
> +  @param  Address  Specifies the virtual address.
> +
> +  @retval   the index of the page table entry.
> + **/
> +#define PTE_INDEX(Address)  (((Address) >> EFI_PAGE_SHIFT) & (ENTRYS_PER_PTE - 1))
> +
> +/**
> +  Calculates the value of the page table entry based on the specified virtual address and properties.
> +
> +  @param  Address  Specifies the virtual address.
> +  @param  Attributes  Specifies the Attributes.
> +
> +  @retval    the value of the page table entry.
> + **/
> +#define MAKE_PTE(Address, Attributes)  (PTE){((((Address) >> EFI_PAGE_SHIFT) << 12) | (Attributes))}
> +
> +/**
> +  Get Global bit from Attributes
> +
> +  @param  Attributes  Specifies the Attributes.
> + * */
> +#define GET_GLOBALBIT(Attributes)  ((Attributes & PAGE_GLOBAL) >> PAGE_GLOBAL_SHIFT)
> +
> +/**
> +  Calculates the value of the Huge page table entry based on the specified virtual address and properties.
> +
> +  @param  Address  Specifies the virtual address.
> +  @param  Attributes  Specifies the Attributes.
> +
> +  @retval    the value of the HUGE page table entry.
> + **/
> +#define MAKE_HUGE_PTE(Address, Attributes)  (PTE){(((((Address) >> PMD_SHIFT) << PMD_SHIFT) | \
> +                                             ((Attributes) | (GET_GLOBALBIT(Attributes) << PAGE_HGLOBAL_SHIFT) | \
> +                                             PAGE_HUGE)))}
> +
> +/**
> +  Check whether the large page table entry is.
> +
> +  @param  Val The value of the page table entry.
> +
> +  @retval    1   Is huge page table entry.
> +  @retval    0   Isn't huge page table entry.
> +**/
> +#define IS_HUGE_PAGE(Val)  ((((Val) & PAGE_HUGE) == PAGE_HUGE) && \
> +                            (((Val) & PAGE_HGLOBAL) == PAGE_HGLOBAL))
> +
> +#define HUGE_PAGE_SIZE  (PMD_SIZE)
> +
> +/**
> +  Check that the global page directory table entry is empty.
> +
> +  @param  pgd   the global page directory struct variables.
> +
> +  @retval    1   The page table is invalid.
> +  @retval    0   The page table is valid.
> +**/
> +#define PGD_IS_EMPTY(Val)  (PGD_VAL(Val) == INVALID_PAGE)
> +
> +/**
> +  Check that the page upper directory table entry is empty.
> +
> +  @param  pud   Page upper directory struct variables.
> +
> +  @retval    1   The page table is invalid.
> +  @retval    0   The page table is valid.
> +**/
> +#define PUD_IS_EMPTY(Val)  (PUD_VAL(Val) == INVALID_PAGE)
> +
> +/**
> +  Check that the page middle directory table entry is empty.
> +
> +  @param  pmd   Page middle directory struct variables.
> +
> +  @retval    1   The page table is invalid.
> +  @retval    0   The page table is valid.
> +**/
> +#define PMD_IS_EMPTY(Val)  (PMD_VAL(Val) == INVALID_PAGE)
> +
> +/**
> +  Check that the page the page table entry is empty.
> +
> +  @param  pte   Page table entry struct variables.
> +
> +  @retval    1   The page table is invalid.
> +  @retval    0   The page table is valid.
> +**/
> +#define PTE_IS_EMPTY(Val)  (!(PTE_VAL(Val) & (~PAGE_VALID)))
> +#endif // PAGE_H_
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
> new file mode 100644
> index 0000000000..c214e8d847
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
> @@ -0,0 +1,178 @@
> +/** @file
> +  CPU Memory Map Unit PEI phase driver.
> +
> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +  @par Glossary:
> +    - Tlb      - Translation Lookaside Buffer
> +**/
> +
> +#include <Uefi.h>
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/CacheMaintenanceLib.h>
> +#include <Library/CpuMmuLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/PcdLib.h>
> +#include <Register/LoongArch64/Csr.h>
> +
> +#include "Page.h"
> +#include "Tlb.h"
> +#include "CommonMmuLib.h"
> +
> +//
> +// For coding convenience, define the maximum valid
> +// LoongArch exception.
> +// Since UEFI V2.11, it will be present in DebugSupport.h.
> +//
> +#define MAX_LOONGARCH_EXCEPTION  64
> +
> +/**
> +  Create a page table and initialize the memory management unit(MMU).
> +
> +  @param[in]   MemoryTable           A pointer to a memory ragion table.
> +  @param[out]  TranslationTableBase  A pointer to a translation table base address.
> +  @param[out]  TranslationTableSize  A pointer to a translation table base size.
> +
> +  @retval  EFI_SUCCESS                Configure MMU successfully.
> +           EFI_INVALID_PARAMETER      MemoryTable is NULL.
> +           EFI_UNSUPPORTED            Out of memory space or size not aligned.
> +**/
> +EFI_STATUS
> +EFIAPI
> +ConfigureMemoryManagementUnit (
> +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryTable,
> +  OUT VOID                      **TranslationTableBase OPTIONAL,
> +  OUT UINTN                     *TranslationTableSize  OPTIONAL
> +  )
> +{
> +  PGD            *SwapperPageDir;
> +  UINTN          PgdShift;
> +  UINTN          PgdWide;
> +  UINTN          PudShift;
> +  UINTN          PudWide;
> +  UINTN          PmdShift;
> +  UINTN          PmdWide;
> +  UINTN          PteShift;
> +  UINTN          PteWide;
> +  UINTN          Length;
> +  UINTN          TlbReEntry;
> +  UINTN          TlbReEntryOffset;
> +  UINTN          Remaining;
> +  RETURN_STATUS  Status;
> +
> +  SwapperPageDir = NULL;
> +  PgdShift       = PGD_SHIFT;
> +  PgdWide        = PGD_WIDE;
> +  PudShift       = PUD_SHIFT;
> +  PudWide        = PUD_WIDE;
> +  PmdShift       = PMD_SHIFT;
> +  PmdWide        = PMD_WIDE;
> +  PteShift       = PTE_SHIFT;
> +  PteWide        = PTE_WIDE;
> +
> +  if (MemoryTable == NULL) {
> +    ASSERT (MemoryTable != NULL);
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  SwapperPageDir = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
> +  ZeroMem (SwapperPageDir, PGD_TABLE_SIZE);
> +
> +  if (SwapperPageDir == NULL) {
> +    goto FreeTranslationTable;
> +  }
> +
> +  CsrWrite (LOONGARCH_CSR_PGDL, (UINTN)SwapperPageDir);
> +
> +  while (MemoryTable->Length != 0) {
> +    DEBUG ((
> +      DEBUG_INFO,
> +      "%a %d VirtualBase %p VirtualEnd %p Attributes %p .\n",
> +      __func__,
> +      __LINE__,
> +      MemoryTable->VirtualBase,
> +      (MemoryTable->Length + MemoryTable->VirtualBase),
> +      MemoryTable->Attributes
> +      ));
> +
> +    Status = FillTranslationTable (MemoryTable);
> +    if (EFI_ERROR (Status)) {
> +      goto FreeTranslationTable;
> +    }
> +
> +    MemoryTable++;
> +  }
> +
> +  //
> +  // TLB Re-entry address at the end of exception vector, a vector is up to 512 bytes,
> +  // so the starting address is: total exception vector size + total interrupt vector size + base.
> +  // The total size of TLB handler and exception vector size and interrupt vector size should not
> +  // be lager than 64KB.
> +  //
> +  Length           = (UINTN)HandleTlbRefillEnd - (UINTN)HandleTlbRefillStart;
> +  TlbReEntryOffset = (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * 512;
> +  Remaining        = TlbReEntryOffset % SIZE_4KB;
> +  if (Remaining != 0x0) {
> +    TlbReEntryOffset += (SIZE_4KB - Remaining);
> +  }
> +
> +  TlbReEntry = PcdGet64 (PcdCpuExceptionVectorBaseAddress) + TlbReEntryOffset;
> +  if ((TlbReEntryOffset + Length) > SIZE_64KB) {
> +    goto FreeTranslationTable;
> +  }
> +
> +  //
> +  // Ensure that TLB refill exception base address alignment is equals to 4KB and is valid.
> +  //
> +  if (TlbReEntry & (SIZE_4KB - 1)) {
> +    goto FreeTranslationTable;
> +  }
> +
> +  CopyMem ((VOID *)TlbReEntry, HandleTlbRefillStart, Length);
> +  InvalidateInstructionCacheRange ((VOID *)(UINTN)HandleTlbRefillStart, Length);
> +
> +  DEBUG ((
> +    DEBUG_INFO,
> +    "%a  %d PteShift %d PteWide %d PmdShift %d PmdWide %d PudShift %d PudWide %d PgdShift %d PgdWide %d.\n",
> +    __func__,
> +    __LINE__,
> +    PteShift,
> +    PteWide,
> +    PmdShift,
> +    PmdWide,
> +    PudShift,
> +    PudWide,
> +    PgdShift,
> +    PgdWide
> +    ));
> +
> +  //
> +  // Set the address of TLB refill exception handler
> +  //
> +  SetTlbRebaseAddress ((UINTN)TlbReEntry);
> +
> +  //
> +  // Set page size
> +  //
> +  CsrXChg (LOONGARCH_CSR_TLBIDX, (DEFAULT_PAGE_SIZE << CSR_TLBIDX_SIZE), CSR_TLBIDX_SIZE_MASK);
> +  CsrWrite (LOONGARCH_CSR_STLBPGSIZE, DEFAULT_PAGE_SIZE);
> +  CsrXChg (LOONGARCH_CSR_TLBREHI, (DEFAULT_PAGE_SIZE << CSR_TLBREHI_PS_SHIFT), CSR_TLBREHI_PS);
> +
> +  CsrWrite (LOONGARCH_CSR_PWCTL0, (PteShift | PteWide << 5 | PmdShift << 10 | PmdWide << 15 | PudShift << 20 | PudWide << 25));
> +  CsrWrite (LOONGARCH_CSR_PWCTL1, (PgdShift | PgdWide << 6));
> +
> +  DEBUG ((DEBUG_INFO, "%a %d Enable Mmu Start PageBassAddress %p.\n", __func__, __LINE__, SwapperPageDir));
> +
> +  return EFI_SUCCESS;
> +
> +FreeTranslationTable:
> +  if (SwapperPageDir != NULL) {
> +    FreePages (SwapperPageDir, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
> +  }
> +
> +  return EFI_UNSUPPORTED;
> +}
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
> new file mode 100644
> index 0000000000..9a681ce8e1
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
> @@ -0,0 +1,48 @@
> +/** @file
> +
> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#ifndef TLB_H_
> +#define TLB_H_
> +
> +/**
> +  Invalid corresponding TLB entries are based on the address given
> +
> +  @param Address The address corresponding to the invalid page table entry
> +
> +  @retval  none
> +**/
> +VOID
> +InvalidTlb (
> +  UINTN  Address
> +  );
> +
> +/**
> +  TLB refill handler start.
> +
> +  @param  none
> +
> +  @retval none
> +**/
> +VOID
> +HandleTlbRefillStart (
> +  VOID
> +  );
> +
> +/**
> +  TLB refill handler end.
> +
> +  @param  none
> +
> +  @retval none
> +**/
> +VOID
> +HandleTlbRefillEnd (
> +  VOID
> +  );
> +
> +#endif // TLB_H_
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
> new file mode 100644
> index 0000000000..c9a8c16336
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
> @@ -0,0 +1,44 @@
> +#------------------------------------------------------------------------------
> +#
> +# TLB operation functions
> +#
> +# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +#
> +# SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +#-----------------------------------------------------------------------------
> +
> +#include <Register/LoongArch64/Csr.h>
> +
> +ASM_GLOBAL ASM_PFX(HandleTlbRefillStart)
> +ASM_GLOBAL ASM_PFX(HandleTlbRefillEnd)
> +ASM_GLOBAL ASM_PFX(InvalidTlb)
> +
> +#
> +#  Refill the page table.
> +#  @param  VOID
> +#  @retval  VOID
> +#
> +ASM_PFX(HandleTlbRefillStart):
> +  csrwr   $t0, LOONGARCH_CSR_TLBRSAVE
> +  csrrd   $t0, LOONGARCH_CSR_PGD
> +  lddir   $t0, $t0, 3   #Put pud BaseAddress into T0
> +  lddir   $t0, $t0, 2   #Put pmd BaseAddress into T0
> +  lddir   $t0, $t0, 1   #Put pte BaseAddress into T0
> +  ldpte   $t0, 0
> +  ldpte   $t0, 1
> +  tlbfill   // refill hi,lo0,lo1
> +  csrrd   $t0, LOONGARCH_CSR_TLBRSAVE
> +  ertn
> +ASM_PFX(HandleTlbRefillEnd):
> +
> +#
> +# Invalid corresponding TLB entries are based on the address given
> +# @param a0 The address corresponding to the invalid page table entry
> +# @retval  none
> +#
> +ASM_PFX(InvalidTlb):
> +    invtlb  INVTLB_ADDR_GTRUE_OR_ASID, $zero, $a0
> +    jirl    $zero, $ra, 0
> +
> +    .end
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
> new file mode 100644
> index 0000000000..45b15db4c9
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
> @@ -0,0 +1,44 @@
> +## @file
> +#  CPU Memory Map Unit PEI phase driver.
> +#
> +#  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                      = PeiCpuMmuLib
> +  MODULE_UNI_FILE                = PeiCpuMmuLib.uni
> +  FILE_GUID                      = F67EB983-AC2A-7550-AB69-3BC51A1C895B
> +  MODULE_TYPE                    = PEIM
> +  VERSION_STRING                 = 1.0
> +  LIBRARY_CLASS                  = CpuMmuLib | SEC PEIM
> +
> +#
> +#  VALID_ARCHITECTURES           = LOONGARCH64
> +#
> +
> +[Sources.LoongArch64]
> +  LoongArch64/TlbOperation.S   | GCC
> +  LoongArch64/CommonMmuLib.c
> +  LoongArch64/PeiCpuMmuLib.c
> +  LoongArch64/CommonMmuLib.h
> +  LoongArch64/Tlb.h
> +  LoongArch64/Page.h
> +
> +[Packages]
> +  MdePkg/MdePkg.dec
> +  MdeModulePkg/MdeModulePkg.dec
> +  UefiCpuPkg/UefiCpuPkg.dec
> +
> +[PCD]
> +  gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask
> +  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress
> +
> +[LibraryClasses]
> +  CacheMaintenanceLib
> +  DebugLib
> +  MemoryAllocationLib
> +  PcdLib
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
> new file mode 100644
> index 0000000000..3e21334f3e
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
> @@ -0,0 +1,14 @@
> +// /** @file
> +// CPU Memory Manager Unit library instance for PEI modules.
> +//
> +// CPU Memory Manager Unit library instance for PEI modules.
> +//
> +// 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 "CPU Memory Manager Unit library instance for PEI modules."
> +
> +#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for PEI modules."
> diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
> index 28eed85bce..178dc3c0f9 100644
> --- a/UefiCpuPkg/UefiCpuPkg.dsc
> +++ b/UefiCpuPkg/UefiCpuPkg.dsc
> @@ -207,5 +207,9 @@
>    UefiCpuPkg/CpuTimerDxeRiscV64/CpuTimerDxeRiscV64.inf
>    UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf
>  
> +[Components.LOONGARCH64]
> +  UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
> +  UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
> +
>  [BuildOptions]
>    *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114868): https://edk2.groups.io/g/devel/message/114868
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/leave/12367111/7686176/1913456212/xyzzy [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib " Chao Li
  2024-01-31  9:47   ` Laszlo Ersek
@ 2024-01-31 10:33   ` Pedro Falcato
  2024-01-31 13:41     ` Laszlo Ersek
  1 sibling, 1 reply; 89+ messages in thread
From: Pedro Falcato @ 2024-01-31 10:33 UTC (permalink / raw)
  To: devel, lichao
  Cc: Eric Dong, Ray Ni, Laszlo Ersek, Rahul Kumar, Gerd Hoffmann,
	Baoqi Zhang, Dongyan Qian, Xianglai Li, Bibo Mao

On Fri, Jan 26, 2024 at 6:29 AM Chao Li <lichao@loongson.cn> wrote:
>
> Add a new library named CpuMmuLib and add a LoongArch64 instance with in
> the library.
> It provides two-stage MMU libraryinstances, PEI and DXE.
>
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>[...]
> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
> new file mode 100644
> index 0000000000..2e852c3371
> --- /dev/null
> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
> @@ -0,0 +1,988 @@
> +/** @file
> +
> +  CPU Memory Map Unit Handler Library common functions.
> +
> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +  @par Glossary:
> +    - Pgd or Pgd or PGD    - Page Global Directory
> +    - Pud or Pud or PUD    - Page Upper Directory
> +    - Pmd or Pmd or PMD    - Page Middle Directory
> +    - Pte or pte or PTE    - Page Table Entry
> +    - Val or VAL or val    - Value
> +    - Dir    - Directory
> +**/

Wow, this is one of the most bizarre patches I've seen in EDK2. You
took essentially Linux code (and the Linux way of doing paging) and
EDK2-ify'd it. Congrats, this is cursed.
You even include the way they botched what a "PTE" means back in Linux 1.0.

> +#include <Uefi.h>
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/CpuMmuLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Register/LoongArch64/Csr.h>
> +#include "Tlb.h"
> +#include "Page.h"
> +
> +#define SWAP_PAGE_DIR              CsrRead(LOONGARCH_CSR_PGDL)

swapper_pg_dir? :P

You should seriously unscrew this patch's cursedness: disambiguate
pte_t and pteval_t (I would suggest having a PTABLE (for the lower
level page table) and a PTE for their entries), and... rename
swapper_pd_dir which makes no sense in this EFI firmware context,
where swapper is a really old, traditional UNIX name.

> +BOOLEAN  mMmuInited = FALSE;

Should probably be a STATIC, you also don't need to explicitly init it
to FALSE, as it's a static duration variable.

-- 
Pedro


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114877): https://edk2.groups.io/g/devel/message/114877
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 11/37] UefiCpuPkg: Add LoongArch64 CPU Timer instance
  2024-01-31  5:28     ` Chao Li
@ 2024-01-31 10:47       ` Laszlo Ersek
  0 siblings, 0 replies; 89+ messages in thread
From: Laszlo Ersek @ 2024-01-31 10:47 UTC (permalink / raw)
  To: devel, lichao, Ray Ni; +Cc: Eric Dong, Rahul Kumar, Gerd Hoffmann

On 1/31/24 06:28, Chao Li wrote:
> Hi Ray and Laszlo,
> 
> I would very much like to be merged into stable202302, the soft feature
> deadline is 2024-02-05, so could you please hlep to review this patch as
> soon as passable? Please...

Sorry, that's unrealistic, from my perspective. This is a large patch
set, and it's far from needing only "finishing touches".

I'm sorry that it's been taking so long. I can (at best) review 1-2
patches from this series per day. Plus, I've learned that, as long as I
perceive high-level problems in a patch, it makes no sense to dig down
into the details.

I wonder if splitting this series into several sub-series might allow
you to advance more quickly (also splitting the large patches into
smaller ones). I don't think we absolutely need to merge this series in
one go; you might be able to upstream the sub-series one by one.

Laszlo



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114878): https://edk2.groups.io/g/devel/message/114878
Mute This Topic: https://groups.io/mt/104070166/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/leave/12367111/7686176/1913456212/xyzzy [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-01-31 10:33   ` Pedro Falcato
@ 2024-01-31 13:41     ` Laszlo Ersek
  2024-01-31 17:46       ` Pedro Falcato
  0 siblings, 1 reply; 89+ messages in thread
From: Laszlo Ersek @ 2024-01-31 13:41 UTC (permalink / raw)
  To: devel, pedro.falcato, lichao
  Cc: Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

Hi Pedro,

On 1/31/24 11:33, Pedro Falcato wrote:
> On Fri, Jan 26, 2024 at 6:29 AM Chao Li <lichao@loongson.cn> wrote:
>>
>> Add a new library named CpuMmuLib and add a LoongArch64 instance with in
>> the library.
>> It provides two-stage MMU libraryinstances, PEI and DXE.
>>
>> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>> [...]
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>> new file mode 100644
>> index 0000000000..2e852c3371
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>> @@ -0,0 +1,988 @@
>> +/** @file
>> +
>> +  CPU Memory Map Unit Handler Library common functions.
>> +
>> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +  @par Glossary:
>> +    - Pgd or Pgd or PGD    - Page Global Directory
>> +    - Pud or Pud or PUD    - Page Upper Directory
>> +    - Pmd or Pmd or PMD    - Page Middle Directory
>> +    - Pte or pte or PTE    - Page Table Entry
>> +    - Val or VAL or val    - Value
>> +    - Dir    - Directory
>> +**/
> 
> You took essentially Linux code [...] and EDK2-ify'd it. Congrats,
> this is cursed.

are you saying this patch is effectively a port of GPL'd Linux code
(potentially *old* code, but still), with the GPL simply replaced with
BSD-2-Clause-Patent?

That sounds... shocking. If such a patch were merged into edk2, it would
violate both the GPL (the GPL cannot just be "relaxed") and the edk2
rules (edk2 does not want GPL code).

The only exception would be if Loongson Technology Corporation Limited
were the Copyright holder of the Linux code, and this patch were a
relicensing. But then that too would have to be documented, IMO.

Thank you for watching out for this (and for recognizing the source code
patterns).

Laszlo



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114900): https://edk2.groups.io/g/devel/message/114900
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/leave/12367111/7686176/1913456212/xyzzy [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-01-31 13:41     ` Laszlo Ersek
@ 2024-01-31 17:46       ` Pedro Falcato
  2024-02-01  3:05         ` Chao Li
  0 siblings, 1 reply; 89+ messages in thread
From: Pedro Falcato @ 2024-01-31 17:46 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: devel, lichao, Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann,
	Baoqi Zhang, Dongyan Qian, Xianglai Li, Bibo Mao

On Wed, Jan 31, 2024 at 1:41 PM Laszlo Ersek <lersek@redhat.com> wrote:
>
> Hi Pedro,
>
> On 1/31/24 11:33, Pedro Falcato wrote:
> > On Fri, Jan 26, 2024 at 6:29 AM Chao Li <lichao@loongson.cn> wrote:
> >>
> >> Add a new library named CpuMmuLib and add a LoongArch64 instance with in
> >> the library.
> >> It provides two-stage MMU libraryinstances, PEI and DXE.
> >>
> >> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584
> >> [...]
> >> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
> >> new file mode 100644
> >> index 0000000000..2e852c3371
> >> --- /dev/null
> >> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
> >> @@ -0,0 +1,988 @@
> >> +/** @file
> >> +
> >> +  CPU Memory Map Unit Handler Library common functions.
> >> +
> >> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
> >> +
> >> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> >> +
> >> +  @par Glossary:
> >> +    - Pgd or Pgd or PGD    - Page Global Directory
> >> +    - Pud or Pud or PUD    - Page Upper Directory
> >> +    - Pmd or Pmd or PMD    - Page Middle Directory
> >> +    - Pte or pte or PTE    - Page Table Entry
> >> +    - Val or VAL or val    - Value
> >> +    - Dir    - Directory
> >> +**/
> >
> > You took essentially Linux code [...] and EDK2-ify'd it. Congrats,
> > this is cursed.
>
> are you saying this patch is effectively a port of GPL'd Linux code
> (potentially *old* code, but still), with the GPL simply replaced with
> BSD-2-Clause-Patent?

Yes. With a bit of patience, you can get a good idea of the
similarities by kernel-ifying identifiers and searching them on
https://elixir.bootlin.com/linux/latest/source.
The smoking gun being the types and strategy used for the MMU code
(PGD, P4D, PUD, PMD, PTE and VAL/PTEVAL being a very unique linux
strategy that is not emulated elsewhere AFAIK) and SWAP_PAGE_DIR (->
swapper_pg_dir, the kernel's pgd; swap has no meaning whatsoever in
UEFI land).

>
> That sounds... shocking. If such a patch were merged into edk2, it would
> violate both the GPL (the GPL cannot just be "relaxed") and the edk2
> rules (edk2 does not want GPL code).
>
> The only exception would be if Loongson Technology Corporation Limited
> were the Copyright holder of the Linux code, and this patch were a
> relicensing. But then that too would have to be documented, IMO.

FWIW, I wasn't objecting to the patch on legal grounds, but mainly
technical ones, since I was assuming Loongson had written most of this
code.
However (arch/loongarch/include/asm/pgtable.h):
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
 *
 * Derived from MIPS:
 * Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 2003 Ralf Baechle
 * Copyright (C) 1999, 2000, 2001 Silicon Graphics, Inc.
 */

and after a closer look, other functions such as P*AllocGet()
mirroring p4d_alloc, pud_alloc and pmd_alloc (from include/linux/mm.h,
definitely not loongson's copyright) very closely, it seems like this
patch is a walking copyright violation.
Not that implementing something similar to Linux's is impossible in a
clean-room instance, but the code used and the patterns used imitate
Linux's down to a t.

-- 
Pedro


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114911): https://edk2.groups.io/g/devel/message/114911
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-01-31 17:46       ` Pedro Falcato
@ 2024-02-01  3:05         ` Chao Li
  2024-02-01 19:36           ` Pedro Falcato
  0 siblings, 1 reply; 89+ messages in thread
From: Chao Li @ 2024-02-01  3:05 UTC (permalink / raw)
  To: devel, pedro.falcato, Laszlo Ersek
  Cc: Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

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

Hi Pedro and Laszlo,

Part of the code in this patch is indeed quoted from the Linux kernel, 
and do you think it is inapproparate? If so, we need to refactor this 
module, what are you suggests with the refactoring? Just remove the 
unused logic from the Kernel code and keep the logic good or refactor 
from scratch?


Thanks,
Chao
On 2024/2/1 01:46, Pedro Falcato wrote:
> On Wed, Jan 31, 2024 at 1:41 PM Laszlo Ersek<lersek@redhat.com>  wrote:
>> Hi Pedro,
>>
>> On 1/31/24 11:33, Pedro Falcato wrote:
>>> On Fri, Jan 26, 2024 at 6:29 AM Chao Li<lichao@loongson.cn>  wrote:
>>>> Add a new library named CpuMmuLib and add a LoongArch64 instance with in
>>>> the library.
>>>> It provides two-stage MMU libraryinstances, PEI and DXE.
>>>>
>>>> BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>>>> [...]
>>>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>>>> new file mode 100644
>>>> index 0000000000..2e852c3371
>>>> --- /dev/null
>>>> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>>>> @@ -0,0 +1,988 @@
>>>> +/** @file
>>>> +
>>>> +  CPU Memory Map Unit Handler Library common functions.
>>>> +
>>>> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>>>> +
>>>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>>>> +
>>>> +  @par Glossary:
>>>> +    - Pgd or Pgd or PGD    - Page Global Directory
>>>> +    - Pud or Pud or PUD    - Page Upper Directory
>>>> +    - Pmd or Pmd or PMD    - Page Middle Directory
>>>> +    - Pte or pte or PTE    - Page Table Entry
>>>> +    - Val or VAL or val    - Value
>>>> +    - Dir    - Directory
>>>> +**/
>>> You took essentially Linux code [...] and EDK2-ify'd it. Congrats,
>>> this is cursed.
>> are you saying this patch is effectively a port of GPL'd Linux code
>> (potentially *old* code, but still), with the GPL simply replaced with
>> BSD-2-Clause-Patent?
> Yes. With a bit of patience, you can get a good idea of the
> similarities by kernel-ifying identifiers and searching them on
> https://elixir.bootlin.com/linux/latest/source.
> The smoking gun being the types and strategy used for the MMU code
> (PGD, P4D, PUD, PMD, PTE and VAL/PTEVAL being a very unique linux
> strategy that is not emulated elsewhere AFAIK) and SWAP_PAGE_DIR (->
> swapper_pg_dir, the kernel's pgd; swap has no meaning whatsoever in
> UEFI land).
>
>> That sounds... shocking. If such a patch were merged into edk2, it would
>> violate both the GPL (the GPL cannot just be "relaxed") and the edk2
>> rules (edk2 does not want GPL code).
>>
>> The only exception would be if Loongson Technology Corporation Limited
>> were the Copyright holder of the Linux code, and this patch were a
>> relicensing. But then that too would have to be documented, IMO.
> FWIW, I wasn't objecting to the patch on legal grounds, but mainly
> technical ones, since I was assuming Loongson had written most of this
> code.
> However (arch/loongarch/include/asm/pgtable.h):
> /* SPDX-License-Identifier: GPL-2.0 */
> /*
>   * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
>   *
>   * Derived from MIPS:
>   * Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 2003 Ralf Baechle
>   * Copyright (C) 1999, 2000, 2001 Silicon Graphics, Inc.
>   */
>
> and after a closer look, other functions such as P*AllocGet()
> mirroring p4d_alloc, pud_alloc and pmd_alloc (from include/linux/mm.h,
> definitely not loongson's copyright) very closely, it seems like this
> patch is a walking copyright violation.
> Not that implementing something similar to Linux's is impossible in a
> clean-room instance, but the code used and the patterns used imitate
> Linux's down to a t.
>


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114928): https://edk2.groups.io/g/devel/message/114928
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-01-31  9:47   ` Laszlo Ersek
@ 2024-02-01  7:57     ` Chao Li
  2024-02-01 22:46       ` Laszlo Ersek
  2024-02-02  3:33       ` Ni, Ray
  0 siblings, 2 replies; 89+ messages in thread
From: Chao Li @ 2024-02-01  7:57 UTC (permalink / raw)
  To: devel, lersek
  Cc: Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

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

Hi Lazslo,


Thanks,
Chao
On 2024/1/31 17:47, Laszlo Ersek wrote:
> On 1/26/24 07:29, Chao Li wrote:
>> Add a new library named CpuMmuLib and add a LoongArch64 instance with in
>> the library.
>> It provides two-stage MMU libraryinstances, PEI and DXE.
>>
>> BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>>
>> Cc: Eric Dong<eric.dong@intel.com>
>> Cc: Ray Ni<ray.ni@intel.com>
>> Cc: Laszlo Ersek<lersek@redhat.com>
>> Cc: Rahul Kumar<rahul1.kumar@intel.com>
>> Cc: Gerd Hoffmann<kraxel@redhat.com>
>> Signed-off-by: Chao Li<lichao@loongson.cn>
>> Co-authored-by: Baoqi Zhang<zhangbaoqi@loongson.cn>
>> Co-authored-by: Dongyan Qian<qiandongyan@loongson.cn>
>> Co-authored-by: Xianglai Li<lixianglai@loongson.cn>
>> Co-authored-by: Bibo Mao<maobibo@loongson.cn>
>> ---
>>   UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf |  36 +
>>   UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni |  14 +
>>   .../CpuMmuLib/LoongArch64/CommonMmuLib.c      | 988 ++++++++++++++++++
>>   .../CpuMmuLib/LoongArch64/CommonMmuLib.h      |  43 +
>>   .../Library/CpuMmuLib/LoongArch64/Page.h      | 279 +++++
>>   .../CpuMmuLib/LoongArch64/PeiCpuMmuLib.c      | 178 ++++
>>   .../Library/CpuMmuLib/LoongArch64/Tlb.h       |  48 +
>>   .../CpuMmuLib/LoongArch64/TlbOperation.S      |  44 +
>>   UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf |  44 +
>>   UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni |  14 +
>>   UefiCpuPkg/UefiCpuPkg.dsc                     |   4 +
>>   11 files changed, 1692 insertions(+)
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>>   create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
>>
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>> new file mode 100644
>> index 0000000000..bfce3ce96d
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>> @@ -0,0 +1,36 @@
>> +## @file
>> +#  CPU Memory Map Unit DXE phase driver.
>> +#
>> +#  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                      = DxeCpuMmuLib
>> +  MODULE_UNI_FILE                = DxeCpuMmuLib.uni
>> +  FILE_GUID                      = DA8F0232-FB14-42F0-922C-63104D2C70BE
> (1) This FILE_GUID was created from the FILE_GUID of
> "ArmPkg/Library/ArmMmuLib/ArmMmuBaseLib.inf" by adding 1. That's not an
> acceptable method for GUID generation.
>
> A method is only acceptable for GUID generation if multiple (= an
> unlimited number of) parties can execute the method at any time, and the
> output remains conflict-free.
>
> Taking an existent (known) FILE_GUID and incrementing it by 1 is not
> such a method.
>
> Please regenerate the GUID with "uuidgen".
>
> Please also review the rest of your new GUIDs over this series (not only
> FILE_GUIDs in INF files, but any other GUIDs, too).
OK, I will regenerate the GUID in next commit.
>
>
>> +  MODULE_TYPE                    = DXE_DRIVER
>> +  VERSION_STRING                 = 1.0
>> +  LIBRARY_CLASS                  = CpuMmuLib | DXE_DRIVER
>> +  CONSTRUCTOR                    = MmuInitialize
>> +
>> +#
>> +#  VALID_ARCHITECTURES           = LOONGARCH64
>> +#
>> +
>> +[Sources.LoongArch64]
>> +  LoongArch64/TlbOperation.S   | GCC
>> +  LoongArch64/CommonMmuLib.c
>> +  LoongArch64/Page.h
>> +  LoongArch64/Tlb.h
>> +
>> +[Packages]
>> +  MdePkg/MdePkg.dec
>> +  UefiCpuPkg/UefiCpuPkg.dec
>> +
>> +[LibraryClasses]
>> +  DebugLib
>> +  MemoryAllocationLib
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
>> new file mode 100644
>> index 0000000000..7342249516
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
>> @@ -0,0 +1,14 @@
>> +// /** @file
>> +// CPU Memory Manager Unit library instance for DXE modules.
>> +//
>> +// CPU Memory Manager Unit library instance for DXE modules.
>> +//
>> +// 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 "CPU Memory Manager Unit library instance for DXE modules."
>> +
>> +#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for DXE modules."
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>> new file mode 100644
>> index 0000000000..2e852c3371
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>> @@ -0,0 +1,988 @@
>> +/** @file
>> +
>> +  CPU Memory Map Unit Handler Library common functions.
>> +
>> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +  @par Glossary:
>> +    - Pgd or Pgd or PGD    - Page Global Directory
>> +    - Pud or Pud or PUD    - Page Upper Directory
>> +    - Pmd or Pmd or PMD    - Page Middle Directory
>> +    - Pte or pte or PTE    - Page Table Entry
>> +    - Val or VAL or val    - Value
>> +    - Dir    - Directory
>> +**/
>> +#include <Uefi.h>
>> +#include <Library/BaseLib.h>
>> +#include <Library/BaseMemoryLib.h>
>> +#include <Library/CpuMmuLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/MemoryAllocationLib.h>
>> +#include <Register/LoongArch64/Csr.h>
>> +#include "Tlb.h"
>> +#include "Page.h"
>> +
>> +#define SWAP_PAGE_DIR              CsrRead(LOONGARCH_CSR_PGDL)
> (2) Missing space after "CsrRead".
OK.
>
>> +#define EFI_MEMORY_CACHETYPE_MASK  (EFI_MEMORY_UC  | \
>> +                                    EFI_MEMORY_WC  | \
>> +                                    EFI_MEMORY_WT  | \
>> +                                    EFI_MEMORY_WB  | \
>> +                                    EFI_MEMORY_UCE   \
>> +                                    )
> (3) This seems to come from "ArmPkg/Include/Library/ArmLib.h"; it's not
> great regardless: we shouldn't use the EFI_ prefix for symbols that are
> not standard. (Put differently, EFI_ is a reserved namespace prefix for
> the UEFI and PI specs.)
OK, I will rename with out EFI_perfix next time.
>
>> +
>> +BOOLEAN  mMmuInited = FALSE;
> (4) This should be STATIC, I believe.
>
> (5) So this is the point where I realize that the library design makes
> no sense to me.
>
> Normally you create SEC/PEI vs. DXE phase instances of a library
> because, using writable global variables, you can gain performance (you
> can remember the initialization) in DXE, but in SEC/PEI, you don't have
> writeable global variables.
>
> This pattern does not seem to apply here, or at least it doesn't seem to
> work. Here's why:
>
> - the variable mMmuInited and the function MmuInitialize (which contains
> an assignment to the variable) are in "CommonMmuLib.c", which gets built
> into both library instances. This makes no sense; that assignment cannot
> work in SEC/PEI (I presume you're not going to have writeable globals --
> executing from flash).
>
> - I think you may be trying to make up for that problem by checking
> SWAP_PAGE_DIR (the LOONGARCH_CSR_PGDL register) in MmuInitialize() and
> MmuIsInit(). That doesn't seem right.

Yes, you are right, the PEI stage may be executed from flash, in this 
case, we have no way to write global variables, so we can only check 
whether CSR_PGDL is NULL in the DXE stage to get whether the MMU has 
been initialized.

>
> - The PEI instance of the library contains an EFIAPI function called
> ConfigureMemoryManagementUnit(). This function is never called in this
> patch, which makes me think it's supposed to be called from driver or
> application code (i.e., not from within the library itself, but from
> client code). However, ConfigureMemoryManagementUnit() is also not
> declared in the previous patch (in
> "UefiCpuPkg/Include/Library/CpuMmuLib.h"); therefore client code cannot
> reach it at all -- so that function doesn't even belong in this library.

This API was discussed with Ray in the V3 patch 13. Actually, the 
CpuMmuLib.h include more APIs before, Ray believed that other APIs 
should not be open to users or some APIs can be done by the base APIs, 
and in my opinion, the ConfigureMemoryManagementUnit() is a private API, 
so in V4, it has been removed from CpuMmuLib.h.

So what do you think? the ConfigureMemoryManagementUnit should be added 
back?

>
> - MmuInitialize() doesn't actually do anything, it just checks (via the
> CSR) whether some other component has already initialized the MMU
> (likely with ConfigureMemoryManagementUnit()). And the sole purpose of
> MmuIsInit() appears to be to return from the public library APIs
> SetMemoryRegionAttributes() and GetMemoryRegionAttributes() early, if
> that "other" (unknown) component has not called
> ConfigureMemoryManagementUnit() yet.
>
> So, I don't understand what you are trying to do. I could explain how to
> keep the initialization logic *differences* minimal between the SEC/PEI
> and the DXE library instances, but I don't understand how / when the MMU
> initialization is supposed to occur in the first place.

Let's me tell you this library how to work:

In PEI stage, in addition to ensuring that the MMU is not used, the user 
must call the ConfigureMemoryManagementUnit toinitialization the MMU, 
such as filling the static page tables, set the TLB refill exception 
entry point, set the page size etc. the ConfigureMemoryManagementUnit is 
a private API but related to ARCH.

During DXE stage, this library will provide services for CpuDxe and 
other drivers, but almost changes to memory page attributes use the 
protocols provided by CpuDxe. That is why the CpuMmuLib.h only contains 
two APIs, it only can get/set the attribute.

In short, the PEI stage needs to initialize and set up TLB refill entry 
point and method(dynamically populate the TLB, keep the PA == VA), and 
DXE stage is provides services for changing the memory attributes.

>
> (6) The patch is too large in general. You should construct these
> library instances over multiple patches. The first patch could add some
> declarations / macro definitions, such as "Page.h" and "Tlb.h". Another
> patch could add the assembly language helper functions. Another patch
> could add the PEI phase library instance (including the common code). A
> final patch could add the DXE-phase bits.
>
> The idea is to proceed in layers, logically building one on top of the
> other. It's fine if the library doesn't build initially; there is no
> attempt to build it anyway until you actually reference the INF files in
> some DSC files. So please split this at least in 4 patches.
OK, I will try it.
>
> (7) The commit message should explain the expected usage model in detail.
OK.
>
> Best regards,
> Laszlo
>
>
>> +
>> +/**
>> +  Check to see if mmu successfully initializes.
>> +
>> +  @param  VOID.
>> +
>> +  @retval  TRUE  Initialization has been completed.
>> +           FALSE Initialization did not complete.
>> +**/
>> +STATIC
>> +BOOLEAN
>> +MmuIsInit (
>> +  VOID
>> +  )
>> +{
>> +  if (mMmuInited || (SWAP_PAGE_DIR != 0)) {
>> +    return TRUE;
>> +  }
>> +
>> +  return FALSE;
>> +}
>> +
>> +/**
>> +  Iterates through the page directory to initialize it.
>> +
>> +  @param  Dst  A pointer to the directory of the page to initialize.
>> +  @param  Num  The number of page directories to initialize.
>> +  @param  Src  A pointer to the data used to initialize the page directory.
>> +
>> +  @return VOID.
>> +**/
>> +STATIC
>> +VOID
>> +PageDirInit (
>> +  IN VOID   *Dst,
>> +  IN UINTN  Num,
>> +  IN VOID   *Src
>> +  )
>> +{
>> +  UINTN  *Ptr;
>> +  UINTN  *End;
>> +  UINTN  Entry;
>> +
>> +  Entry = (UINTN)Src;
>> +  Ptr   = (UINTN *)Dst;
>> +  End   = Ptr + Num;
>> +
>> +  for ( ; Ptr < End; Ptr++) {
>> +    *Ptr = Entry;
>> +  }
>> +
>> +  return;
>> +}
>> +
>> +/**
>> +  Gets the virtual address corresponding to the page global directory table entry.
>> +
>> +  @param  Address  the virtual address for the table entry.
>> +
>> +  @retval PGD A pointer to get the table item.
>> +**/
>> +STATIC
>> +PGD *
>> +PgdOffset (
>> +  IN UINTN  Address
>> +  )
>> +{
>> +  return (PGD *)(SWAP_PAGE_DIR) + PGD_INDEX (Address);
>> +}
>> +
>> +/**
>> +  Gets the virtual address corresponding to the page upper directory table entry.
>> +
>> +  @param  Pgd  A pointer to a page global directory table entry.
>> +  @param  Address  the virtual address for the table entry.
>> +
>> +  @retval PUD A pointer to get the table item.
>> +**/
>> +STATIC
>> +PUD *
>> +PudOffset (
>> +  IN PGD    *Pgd,
>> +  IN UINTN  Address
>> +  )
>> +{
>> +  UINTN  PgdVal;
>> +
>> +  PgdVal = (UINTN)PGD_VAL (*Pgd);
>> +
>> +  return (PUD *)PgdVal + PUD_INDEX (Address);
>> +}
>> +
>> +/**
>> +  Gets the virtual address corresponding to the page middle directory table entry.
>> +
>> +  @param  Pud  A pointer to a page upper directory table entry.
>> +  @param  Address  the virtual address for the table entry.
>> +
>> +  @retval PMD A pointer to get the table item.
>> +**/
>> +STATIC
>> +PMD *
>> +PmdOffset (
>> +  IN PUD    *Pud,
>> +  IN UINTN  Address
>> +  )
>> +{
>> +  UINTN  PudVal;
>> +
>> +  PudVal = PUD_VAL (*Pud);
>> +
>> +  return (PMD *)PudVal + PMD_INDEX (Address);
>> +}
>> +
>> +/**
>> +  Gets the virtual address corresponding to the page table entry.
>> +
>> +  @param  Pmd  A pointer to a page middle directory table entry.
>> +  @param  Address  the virtual address for the table entry.
>> +
>> +  @retval PTE A pointer to get the table item.
>> +**/
>> +STATIC
>> +PTE *
>> +PteOffset (
>> +  IN PMD    *Pmd,
>> +  IN UINTN  Address
>> +  )
>> +{
>> +  UINTN  PmdVal;
>> +
>> +  PmdVal = (UINTN)PMD_VAL (*Pmd);
>> +
>> +  return (PTE *)PmdVal + PTE_INDEX (Address);
>> +}
>> +
>> +/**
>> +  Sets the value of the page table entry.
>> +
>> +  @param  Pte  A pointer to a page table entry.
>> +  @param  PteVal  The value of the page table entry to set.
>> +
>> +**/
>> +STATIC
>> +VOID
>> +SetPte (
>> +  IN PTE  *Pte,
>> +  IN PTE  PteVal
>> +  )
>> +{
>> +  *Pte = PteVal;
>> +}
>> +
>> +/**
>> +  Sets the value of the page global directory.
>> +
>> +  @param  Pgd  A pointer to a page global directory.
>> +  @param  Pud  The value of the page global directory to set.
>> +
>> +**/
>> +STATIC
>> +VOID
>> +SetPgd (
>> +  IN PGD  *Pgd,
>> +  IN PUD  *Pud
>> +  )
>> +{
>> +  *Pgd = (PGD) {
>> +    ((UINTN)Pud)
>> +  };
>> +}
>> +
>> +/**
>> +  Sets the value of the page upper directory.
>> +
>> +  @param  Pud  A pointer to a page upper directory.
>> +  @param  Pmd  The value of the page upper directory to set.
>> +
>> +**/
>> +STATIC
>> +VOID
>> +SetPud (
>> +  IN PUD  *Pud,
>> +  IN PMD  *Pmd
>> +  )
>> +{
>> +  *Pud = (PUD) {
>> +    ((UINTN)Pmd)
>> +  };
>> +}
>> +
>> +/**
>> +  Sets the value of the page middle directory.
>> +
>> +  @param  Pmd  A pointer to a page middle directory.
>> +  @param  Pte  The value of the page middle directory to set.
>> +
>> +**/
>> +STATIC
>> +VOID
>> +SetPmd (
>> +  IN PMD  *Pmd,
>> +  IN PTE  *Pte
>> +  )
>> +{
>> +  *Pmd = (PMD) {
>> +    ((UINTN)Pte)
>> +  };
>> +}
>> +
>> +/**
>> +  Free up memory space occupied by page tables.
>> +
>> +  @param  Pte  A pointer to the page table.
>> +
>> +**/
>> +VOID
>> +PteFree (
>> +  IN PTE  *Pte
>> +  )
>> +{
>> +  FreePages ((VOID *)Pte, 1);
>> +}
>> +
>> +/**
>> +  Free up memory space occupied by page middle directory.
>> +
>> +  @param  Pmd  A pointer to the page middle directory.
>> +
>> +**/
>> +VOID
>> +PmdFree (
>> +  IN PMD  *Pmd
>> +  )
>> +{
>> +  FreePages ((VOID *)Pmd, 1);
>> +}
>> +
>> +/**
>> +  Free up memory space occupied by page upper directory.
>> +
>> +  @param  Pud  A pointer to the page upper directory.
>> +
>> +**/
>> +VOID
>> +PudFree (
>> +  IN PUD  *Pud
>> +  )
>> +{
>> +  FreePages ((VOID *)Pud, 1);
>> +}
>> +
>> +/**
>> +  Requests the memory space required for the page upper directory,
>> +  initializes it, and places it in the specified page global directory
>> +
>> +  @param  Pgd  A pointer to the page global directory.
>> +
>> +  @retval  EFI_SUCCESS  Memory request successful.
>> +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +PudAlloc (
>> +  IN PGD  *Pgd
>> +  )
>> +{
>> +  PUD  *Pud;
>> +
>> +  Pud = (PUD *)AllocatePages (1);
>> +  if (Pud == NULL) {
>> +    return EFI_OUT_OF_RESOURCES;
>> +  }
>> +
>> +  PageDirInit ((VOID *)Pud, ENTRYS_PER_PUD, (VOID *)INVALID_PAGE);
>> +
>> +  SetPgd (Pgd, Pud);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Requests the memory space required for the page middle directory,
>> +  initializes it, and places it in the specified page upper directory
>> +
>> +  @param  Pud  A pointer to the page upper directory.
>> +
>> +  @retval  EFI_SUCCESS  Memory request successful.
>> +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +PmdAlloc (
>> +  IN PUD  *Pud
>> +  )
>> +{
>> +  PMD  *Pmd;
>> +
>> +  Pmd = (PMD *)AllocatePages (1);
>> +  if (!Pmd) {
>> +    return EFI_OUT_OF_RESOURCES;
>> +  }
>> +
>> +  PageDirInit ((VOID *)Pmd, ENTRYS_PER_PMD, (VOID *)INVALID_PAGE);
>> +
>> +  SetPud (Pud, Pmd);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Requests the memory space required for the page table,
>> +  initializes it, and places it in the specified page middle directory
>> +
>> +  @param  Pmd  A pointer to the page middle directory.
>> +
>> +  @retval  EFI_SUCCESS  Memory request successful.
>> +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +PteAlloc (
>> +  IN PMD  *Pmd
>> +  )
>> +{
>> +  PTE  *Pte;
>> +
>> +  Pte = (PTE *)AllocatePages (1);
>> +  if (!Pte) {
>> +    return EFI_OUT_OF_RESOURCES;
>> +  }
>> +
>> +  Pte = ZeroMem (Pte, EFI_PAGE_SIZE);
>> +
>> +  SetPmd (Pmd, Pte);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Requests the memory space required for the page upper directory,
>> +  initializes it, and places it in the specified page global directory,
>> +  and get the page upper directory entry corresponding to the virtual address.
>> +
>> +  @param  Pgd      A pointer to the page global directory.
>> +  @param  Address  The corresponding virtual address of the page table entry.
>> +
>> +  @retval          A pointer to the page upper directory entry. Return NULL, if
>> +                   allocate the memory buffer is fail.
>> +**/
>> +STATIC
>> +PUD *
>> +PudAllocGet (
>> +  IN PGD    *Pgd,
>> +  IN UINTN  Address
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +
>> +  if (PGD_IS_EMPTY (*Pgd)) {
>> +    Status = PudAlloc (Pgd);
>> +    ASSERT_EFI_ERROR (Status);
>> +    if (EFI_ERROR (Status)) {
>> +      return NULL;
>> +    }
>> +  }
>> +
>> +  return PudOffset (Pgd, Address);
>> +}
>> +
>> +/**
>> +  Requests the memory space required for the page middle directory,
>> +  initializes it, and places it in the specified page upper directory,
>> +  and get the page middle directory entry corresponding to the virtual address.
>> +
>> +  @param  Pud      A pointer to the page upper directory.
>> +  @param  Address  The corresponding virtual address of the page table entry.
>> +
>> +  @retval          A pointer to the page middle directory entry. Return NULL, if
>> +                   allocate the memory buffer is fail.
>> +**/
>> +STATIC
>> +PMD *
>> +PmdAllocGet (
>> +  IN PUD    *Pud,
>> +  IN UINTN  Address
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +
>> +  if (PUD_IS_EMPTY (*Pud)) {
>> +    Status = PmdAlloc (Pud);
>> +    ASSERT_EFI_ERROR (Status);
>> +    if (EFI_ERROR (Status)) {
>> +      return NULL;
>> +    }
>> +  }
>> +
>> +  return PmdOffset (Pud, Address);
>> +}
>> +
>> +/**
>> +  Requests the memory space required for the page table,
>> +  initializes it, and places it in the specified page middle directory,
>> +  and get the page table entry corresponding to the virtual address.
>> +
>> +  @param  Pmd      A pointer to the page upper directory.
>> +  @param  Address  The corresponding virtual address of the page table entry.
>> +
>> +  @retval          A pointer to the page table entry. Return NULL, if allocate
>> +                   the memory buffer is fail.
>> +**/
>> +STATIC
>> +PTE *
>> +PteAllocGet (
>> +  IN PMD    *Pmd,
>> +  IN UINTN  Address
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +
>> +  if (PMD_IS_EMPTY (*Pmd)) {
>> +    Status = PteAlloc (Pmd);
>> +    ASSERT_EFI_ERROR (Status);
>> +    if (EFI_ERROR (Status)) {
>> +      return NULL;
>> +    }
>> +  }
>> +
>> +  return PteOffset (Pmd, Address);
>> +}
>> +
>> +/**
>> +  Gets the physical address of the page table entry corresponding to the specified virtual address.
>> +
>> +  @param  Address  The corresponding virtual address of the page table entry.
>> +
>> +  @retval  A pointer to the page table entry.
>> +  @retval  NULL
>> +**/
>> +STATIC
>> +PTE *
>> +GetPteAddress (
>> +  IN UINTN  Address
>> +  )
>> +{
>> +  PGD  *Pgd;
>> +  PUD  *Pud;
>> +  PMD  *Pmd;
>> +
>> +  Pgd = PgdOffset (Address);
>> +
>> +  if (PGD_IS_EMPTY (*Pgd)) {
>> +    return NULL;
>> +  }
>> +
>> +  Pud = PudOffset (Pgd, Address);
>> +
>> +  if (PUD_IS_EMPTY (*Pud)) {
>> +    return NULL;
>> +  }
>> +
>> +  Pmd = PmdOffset (Pud, Address);
>> +  if (PMD_IS_EMPTY (*Pmd)) {
>> +    return NULL;
>> +  }
>> +
>> +  if (IS_HUGE_PAGE (Pmd->PmdVal)) {
>> +    return ((PTE *)Pmd);
>> +  }
>> +
>> +  return PteOffset (Pmd, Address);
>> +}
>> +
>> +/**
>> +  Gets the Attributes of Huge Page.
>> +
>> +  @param  Pmd  A pointer to the page middle directory.
>> +
>> +  @retval     Value of Attributes.
>> +**/
>> +STATIC
>> +UINTN
>> +GetHugePageAttributes (
>> +  IN  PMD  *Pmd
>> +  )
>> +{
>> +  UINTN  Attributes;
>> +  UINTN  GlobalFlag;
>> +  UINTN  HugeVal;
>> +
>> +  HugeVal     = PMD_VAL (*Pmd);
>> +  Attributes  = HugeVal & (~HUGEP_PAGE_MASK);
>> +  GlobalFlag  = ((Attributes & (1 << PAGE_HGLOBAL_SHIFT)) >> PAGE_HGLOBAL_SHIFT) << PAGE_GLOBAL_SHIFT;
>> +  Attributes &= ~(1 << PAGE_HGLOBAL_SHIFT);
>> +  Attributes |= GlobalFlag;
>> +  return Attributes;
>> +}
>> +
>> +/**
>> +  Establishes a page table entry based on the specified memory region.
>> +
>> +  @param  Pmd  A pointer to the page middle directory.
>> +  @param  Address  The memory space start address.
>> +  @param  End  The end address of the memory space.
>> +  @param  Attributes  Memory space Attributes.
>> +
>> +  @retval     EFI_SUCCESS   The page table entry was created successfully.
>> +  @retval     EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +MemoryMapPteRange (
>> +  IN PMD    *Pmd,
>> +  IN UINTN  Address,
>> +  IN UINTN  End,
>> +  IN UINTN  Attributes
>> +  )
>> +{
>> +  PTE      *Pte;
>> +  PTE      PteVal;
>> +  BOOLEAN  UpDate;
>> +
>> +  Pte = PteAllocGet (Pmd, Address);
>> +  if (!Pte) {
>> +    return EFI_OUT_OF_RESOURCES;
>> +  }
>> +
>> +  DEBUG ((
>> +    DEBUG_INFO,
>> +    "%a %d Address %p End %p  Attributes %llx\n",
>> +    __func__,
>> +    __LINE__,
>> +    Address,
>> +    End,
>> +    Attributes
>> +    ));
>> +
>> +  do {
>> +    UpDate = FALSE;
>> +    PteVal = MAKE_PTE (Address, Attributes);
>> +
>> +    if ((!PTE_IS_EMPTY (*Pte)) &&
>> +        (PTE_VAL (*Pte) != PTE_VAL (PteVal)))
>> +    {
>> +      UpDate = TRUE;
>> +    }
>> +
>> +    SetPte (Pte, PteVal);
>> +    if (UpDate) {
>> +      InvalidTlb (Address);
>> +    }
>> +  } while (Pte++, Address += EFI_PAGE_SIZE, Address != End);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Convert Huge Page to Page.
>> +
>> +  @param  Pmd  A pointer to the page middle directory.
>> +  @param  Address  The memory space start address.
>> +  @param  End  The end address of the memory space.
>> +  @param  Attributes  Memory space Attributes.
>> +
>> +  @retval  EFI_SUCCESS   The page table entry was created successfully.
>> +  @retval  EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +ConvertHugePageToPage (
>> +  IN  PMD   *Pmd,
>> +  IN UINTN  Address,
>> +  IN UINTN  End,
>> +  IN UINTN  Attributes
>> +  )
>> +{
>> +  UINTN       OldAttributes;
>> +  UINTN       HugePageEnd;
>> +  UINTN       HugePageStart;
>> +  EFI_STATUS  Status;
>> +
>> +  Status = EFI_SUCCESS;
>> +
>> +  if ((PMD_IS_EMPTY (*Pmd)) ||
>> +      (!IS_HUGE_PAGE (Pmd->PmdVal)))
>> +  {
>> +    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
>> +  } else {
>> +    OldAttributes = GetHugePageAttributes (Pmd);
>> +    if (Attributes == OldAttributes) {
>> +      return Status;
>> +    }
>> +
>> +    SetPmd (Pmd, (PTE *)(INVALID_PAGE));
>> +    HugePageStart = Address & PMD_MASK;
>> +    HugePageEnd   = HugePageStart + HUGE_PAGE_SIZE;
>> +    ASSERT (HugePageEnd >= End);
>> +
>> +    if (Address > HugePageStart) {
>> +      Status |= MemoryMapPteRange (Pmd, HugePageStart, Address, OldAttributes);
>> +    }
>> +
>> +    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
>> +
>> +    if (End < HugePageEnd) {
>> +      Status |= MemoryMapPteRange (Pmd, End, HugePageEnd, OldAttributes);
>> +    }
>> +  }
>> +
>> +  return Status;
>> +}
>> +
>> +/**
>> +  Establishes a page middle directory based on the specified memory region.
>> +
>> +  @param  Pud  A pointer to the page upper directory.
>> +  @param  Address  The memory space start address.
>> +  @param  End  The end address of the memory space.
>> +  @param  Attributes  Memory space Attributes.
>> +
>> +  @retval     EFI_SUCCESS   The page middle directory was created successfully.
>> +  @retval     EFI_OUT_OF_RESOURCES  Page middle directory establishment failed due to resource exhaustion.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +MemoryMapPmdRange (
>> +  IN PUD    *Pud,
>> +  IN UINTN  Address,
>> +  IN UINTN  End,
>> +  IN UINTN  Attributes
>> +  )
>> +{
>> +  PMD      *Pmd;
>> +  UINTN    Next;
>> +  PTE      PteVal;
>> +  BOOLEAN  UpDate;
>> +
>> +  Pmd = PmdAllocGet (Pud, Address);
>> +  if (Pmd == NULL) {
>> +    return EFI_OUT_OF_RESOURCES;
>> +  }
>> +
>> +  do {
>> +    Next = PMD_ADDRESS_END (Address, End);
>> +    if (((Address & (~PMD_MASK)) == 0) &&
>> +        ((Next &  (~PMD_MASK)) == 0) &&
>> +        (PMD_IS_EMPTY (*Pmd) || IS_HUGE_PAGE (Pmd->PmdVal)))
>> +    {
>> +      UpDate = FALSE;
>> +      PteVal = MAKE_HUGE_PTE (Address, Attributes);
>> +
>> +      if ((!PMD_IS_EMPTY (*Pmd)) &&
>> +          (PMD_VAL (*Pmd) != PTE_VAL (PteVal)))
>> +      {
>> +        UpDate = TRUE;
>> +      }
>> +
>> +      SetPmd (Pmd, (PTE *)PteVal.PteVal);
>> +      if (UpDate) {
>> +        InvalidTlb (Address);
>> +      }
>> +    } else {
>> +      ConvertHugePageToPage (Pmd, Address, Next, Attributes);
>> +    }
>> +  } while (Pmd++, Address = Next, Address != End);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Establishes a page upper directory based on the specified memory region.
>> +
>> +  @param  Pgd  A pointer to the page global directory.
>> +  @param  Address  The memory space start address.
>> +  @param  End  The end address of the memory space.
>> +  @param  Attributes  Memory space Attributes.
>> +
>> +  @retval     EFI_SUCCESS   The page upper directory was created successfully.
>> +  @retval     EFI_OUT_OF_RESOURCES  Page upper directory establishment failed due to resource exhaustion.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +MemoryMapPudRange (
>> +  IN PGD    *Pgd,
>> +  IN UINTN  Address,
>> +  IN UINTN  End,
>> +  IN UINTN  Attributes
>> +  )
>> +{
>> +  PUD    *Pud;
>> +  UINTN  Next;
>> +
>> +  Pud = PudAllocGet (Pgd, Address);
>> +  if (Pud == NULL) {
>> +    return EFI_OUT_OF_RESOURCES;
>> +  }
>> +
>> +  do {
>> +    Next = PUD_ADDRESS_END (Address, End);
>> +    if (EFI_ERROR (MemoryMapPmdRange (Pud, Address, Next, Attributes))) {
>> +      return EFI_OUT_OF_RESOURCES;
>> +    }
>> +  } while (Pud++, Address = Next, Address != End);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Establishes a page global directory based on the specified memory region.
>> +
>> +  @param  Start  The memory space start address.
>> +  @param  End  The end address of the memory space.
>> +  @param  Attributes  Memory space Attributes.
>> +
>> +  @retval     EFI_SUCCESS   The page global directory was created successfully.
>> +  @retval     EFI_OUT_OF_RESOURCES  Page global directory establishment failed due to resource exhaustion.
>> +**/
>> +STATIC
>> +EFI_STATUS
>> +MemoryMapPageRange (
>> +  IN UINTN  Start,
>> +  IN UINTN  End,
>> +  IN UINTN  Attributes
>> +  )
>> +{
>> +  PGD         *Pgd;
>> +  UINTN       Next;
>> +  UINTN       Address;
>> +  EFI_STATUS  Err;
>> +
>> +  Address = Start;
>> +
>> +  /* Get PGD(PTE PMD PUD PGD) in PageTables */
>> +  Pgd = PgdOffset (Address);
>> +  do {
>> +    Next = PGD_ADDRESS_END (Address, End);
>> +    /* Get Next Align Page to Map */
>> +    Err = MemoryMapPudRange (Pgd, Address, Next, Attributes);
>> +    if (Err) {
>> +      return Err;
>> +    }
>> +  } while (Pgd++, Address = Next, Address != End);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Page tables are established from memory-mapped tables.
>> +
>> +  @param  MemoryRegion   A pointer to a memory-mapped table entry.
>> +
>> +  @retval     EFI_SUCCESS   The page table was created successfully.
>> +  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
>> +**/
>> +EFI_STATUS
>> +FillTranslationTable (
>> +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
>> +  )
>> +{
>> +  return MemoryMapPageRange (
>> +           MemoryRegion->VirtualBase,
>> +           (MemoryRegion->Length + MemoryRegion->VirtualBase),
>> +           MemoryRegion->Attributes
>> +           );
>> +}
>> +
>> +/**
>> +  Convert EFI Attributes to Loongarch Attributes.
>> +
>> +  @param[in]  EfiAttributes     Efi Attributes.
>> +
>> +  @retval  Corresponding architecture attributes.
>> +**/
>> +UINTN
>> +EFIAPI
>> +EfiAttributeConverse (
>> +  IN UINTN  EfiAttributes
>> +  )
>> +{
>> +  UINTN  LoongArchAttributes;
>> +
>> +  LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | PLV_KERNEL | PAGE_GLOBAL;
>> +
>> +  switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
>> +    case EFI_MEMORY_UC:
>> +      LoongArchAttributes |= CACHE_SUC;
>> +      break;
>> +    case EFI_MEMORY_WC:
>> +      LoongArchAttributes |= CACHE_WUC;
>> +      break;
>> +    case EFI_MEMORY_WT:
>> +    case EFI_MEMORY_WB:
>> +      LoongArchAttributes |= CACHE_CC;
>> +      break;
>> +    default:
>> +      LoongArchAttributes |= CACHE_CC;
>> +      break;
>> +  }
>> +
>> +  // Write protection attributes
>> +  if (((EfiAttributes & EFI_MEMORY_RO) != 0) ||
>> +      ((EfiAttributes & EFI_MEMORY_WP) != 0))
>> +  {
>> +    LoongArchAttributes &= ~PAGE_DIRTY;
>> +  }
>> +
>> +  if ((EfiAttributes & EFI_MEMORY_RP) != 0) {
>> +    LoongArchAttributes |= PAGE_NO_READ;
>> +  }
>> +
>> +  // eXecute protection attribute
>> +  if ((EfiAttributes & EFI_MEMORY_XP) != 0) {
>> +    LoongArchAttributes |= PAGE_NO_EXEC;
>> +  }
>> +
>> +  return LoongArchAttributes;
>> +}
>> +
>> +/**
>> +  Finds the first of the length and memory properties of the memory region corresponding
>> +  to the specified base address.
>> +
>> +  @param[in]       BaseAddress       To find the base address of the memory region.
>> +  @param[in, out]  RegionLength      Pointer holding:
>> +                                      - At entry, the length of the memory region
>> +                                        expected to be found.
>> +                                      - At exit, the length of the memory region found.
>> +  @param[out]      RegionAttributes  Properties of the memory region found.
>> +
>> +  @retval  EFI_SUCCESS           The corresponding memory area was successfully found
>> +           EFI_NOT_FOUND         No memory area found
>> +           EFI_OUT_OF_RESOURCES  Base address or expected memory region exceeds the maximum
>> +                                 address.
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +GetMemoryRegionAttributes (
>> +  IN     UINTN  BaseAddress,
>> +  IN OUT UINTN  *RegionLength,
>> +  OUT    UINTN  *RegionAttributes
>> +  )
>> +{
>> +  PTE    *Pte;
>> +  UINTN  Attributes;
>> +  UINTN  AttributesTmp;
>> +  UINTN  MaxAddress;
>> +  UINTN  EndAddress;
>> +  UINTN  AddSize;
>> +
>> +  if (!MmuIsInit ()) {
>> +    return EFI_UNSUPPORTED;
>> +  }
>> +
>> +  EndAddress = BaseAddress + *RegionLength;
>> +  MaxAddress = LShiftU64 (1ULL, MAX_VA_BITS) - 1;
>> +
>> +  // Clean the value to prepare output to find region size.
>> +  *RegionLength = 0x0;
>> +
>> +  if ((BaseAddress >= MaxAddress) || (EndAddress >= MaxAddress)) {
>> +    return EFI_OUT_OF_RESOURCES;
>> +  }
>> +
>> +  Pte = GetPteAddress (BaseAddress);
>> +
>> +  if (Pte == NULL) {
>> +    return EFI_NOT_FOUND;
>> +  }
>> +
>> +  Attributes = GET_PAGE_ATTRIBUTES (*Pte);
>> +  if (IS_HUGE_PAGE (Pte->PteVal)) {
>> +    *RegionAttributes = Attributes & (~(PAGE_HUGE));
>> +  } else {
>> +    *RegionAttributes = Attributes;
>> +  }
>> +
>> +  do {
>> +    Pte = GetPteAddress (BaseAddress);
>> +    if (Pte == NULL) {
>> +      return EFI_SUCCESS;
>> +    }
>> +
>> +    AttributesTmp = GET_PAGE_ATTRIBUTES (*Pte);
>> +    if (AttributesTmp == Attributes) {
>> +      if (IS_HUGE_PAGE (Pte->PteVal)) {
>> +        AddSize = HUGE_PAGE_SIZE;
>> +      } else {
>> +        AddSize = EFI_PAGE_SIZE;
>> +      }
>> +
>> +      *RegionLength += AddSize;
>> +      BaseAddress   += AddSize;
>> +    } else {
>> +      return EFI_SUCCESS;
>> +    }
>> +  } while (BaseAddress <= EndAddress);
>> +
>> +  return EFI_SUCCESS;
>> +}
>> +
>> +/**
>> +  Sets the Attributes  of the specified memory region
>> +
>> +  @param[in]  BaseAddress    The base address of the memory region to set the Attributes.
>> +  @param[in]  Length         The length of the memory region to set the Attributes.
>> +  @param[in]  Attributes     The Attributes to be set.
>> +  @param[in]  AttributeMask  Mask of memory attributes to take into account.
>> +
>> +  @retval  EFI_SUCCESS    The Attributes was set successfully
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +SetMemoryRegionAttributes (
>> +  IN EFI_PHYSICAL_ADDRESS  BaseAddress,
>> +  IN UINTN                 Length,
>> +  IN UINTN                 Attributes,
>> +  IN UINT64                AttributeMask
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +
>> +  if (!MmuIsInit ()) {
>> +    return EFI_UNSUPPORTED;
>> +  }
>> +
>> +  Attributes = EfiAttributeConverse (Attributes);
>> +  Status     = MemoryMapPageRange (BaseAddress, BaseAddress + Length, Attributes);
>> +  ASSERT_EFI_ERROR (Status);
>> +
>> +  return Status;
>> +}
>> +
>> +/**
>> +  Check to see if mmu successfully initializes and saves the result.
>> +
>> +  @param[in]  ImageHandle  The firmware allocated handle for the EFI image.
>> +  @param[in]  SystemTable  A pointer to the EFI System Table.
>> +
>> +  @retval  RETURN_SUCCESS    Initialization succeeded.
>> +**/
>> +RETURN_STATUS
>> +MmuInitialize (
>> +  IN EFI_HANDLE        ImageHandle,
>> +  IN EFI_SYSTEM_TABLE  *SystemTable
>> +  )
>> +{
>> +  if (SWAP_PAGE_DIR != 0) {
>> +    mMmuInited = TRUE;
>> +  }
>> +
>> +  return RETURN_SUCCESS;
>> +}
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
>> new file mode 100644
>> index 0000000000..d8c922c8fa
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
>> @@ -0,0 +1,43 @@
>> +/** @file
>> +
>> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +  @par Glossary:
>> +    - Dir    - Directory
>> +**/
>> +
>> +#ifndef  MMU_LIB_CORE_H_
>> +#define  MMU_LIB_CORE_H_
>> +
>> +/**
>> +  Iterates through the page directory to initialize it.
>> +
>> +  @param  Dst  A pointer to the directory of the page to initialize.
>> +  @param  Num  The number of page directories to initialize.
>> +  @param  Src  A pointer to the data used to initialize the page directory.
>> +
>> +  @retval VOID.
>> +**/
>> +VOID
>> +PageDirInit (
>> +  IN VOID   *dest,
>> +  IN UINTN  Count,
>> +  IN VOID   *src
>> +  );
>> +
>> +/**
>> +  Page tables are established from memory-mapped tables.
>> +
>> +  @param  MemoryRegion   A pointer to a memory-mapped table entry.
>> +
>> +  @retval     EFI_SUCCESS   The page table was created successfully.
>> +  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
>> +**/
>> +EFI_STATUS
>> +FillTranslationTable (
>> +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
>> +  );
>> +
>> +#endif // MMU_LIB_CORE_H_
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
>> new file mode 100644
>> index 0000000000..bac4f52327
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
>> @@ -0,0 +1,279 @@
>> +/** @file
>> +
>> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +  @par Glossary:
>> +    - Pgd or Pgd or PGD    - Page Global Directory
>> +    - Pud or Pud or PUD    - Page Upper Directory
>> +    - Pmd or Pmd or PMD    - Page Middle Directory
>> +    - Pte or pte or PTE    - Page Table Entry
>> +    - Val or VAL or val    - Value
>> +    - Dir    - Directory
>> +**/
>> +
>> +#ifndef PAGE_H_
>> +#define PAGE_H_
>> +
>> +#include <Library/CpuMmuLib.h>
>> +
>> +#define MAX_VA_BITS  47
>> +#define PGD_WIDE     (8)
>> +#define PUD_WIDE     (9)
>> +#define PMD_WIDE     (9)
>> +#define PTE_WIDE     (9)
>> +
>> +#define ENTRYS_PER_PGD  (1 << PGD_WIDE)
>> +#define ENTRYS_PER_PUD  (1 << PUD_WIDE)
>> +#define ENTRYS_PER_PMD  (1 << PMD_WIDE)
>> +#define ENTRYS_PER_PTE  (1 << PTE_WIDE)
>> +
>> +#define PGD_SHIFT  (PUD_SHIFT + PUD_WIDE)
>> +#define PUD_SHIFT  (PMD_SHIFT + PMD_WIDE)
>> +#define PMD_SHIFT  (EFI_PAGE_SHIFT + PTE_WIDE)
>> +#define PTE_SHIFT  (EFI_PAGE_SHIFT)
>> +
>> +#define PGD_SIZE  (1UL << PGD_SHIFT)
>> +#define PUD_SIZE  (1UL << PUD_SHIFT)
>> +#define PMD_SIZE  (1UL << PMD_SHIFT)
>> +
>> +#define PGD_MASK   (~(PGD_SIZE-1))
>> +#define PUD_MASK   (~(PUD_SIZE-1))
>> +#define PMD_MASK   (~(PMD_SIZE-1))
>> +#define PAGE_MASK  (~(EFI_PAGE_SIZE - 1))
>> +#define PFN_MASK   (~(((UINTN)(1) << (EFI_PAGE_SHIFT)) - 1) & \
>> +                   (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
>> +
>> +#define HUGEP_PAGE_MASK  (~(((UINTN)(1) << (PMD_SHIFT)) - 1) & \
>> +                          (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
>> +
>> +#define INVALID_PAGE  0
>> +
>> +typedef struct {
>> +  UINTN    PgdVal;
>> +} PGD;
>> +typedef struct {
>> +  UINTN    PudVal;
>> +} PUD;
>> +typedef struct {
>> +  UINTN    PmdVal;
>> +} PMD;
>> +typedef struct {
>> +  UINTN    PteVal;
>> +} PTE;
>> +
>> +/**
>> +  Gets the value of the page global directory table entry.
>> +
>> +  @param  x    Page global directory struct variables.
>> +
>> +  @retval   the value of the page global directory table entry.
>> + **/
>> +#define PGD_VAL(x)  ((x).PgdVal)
>> +
>> +/**
>> +  Gets the value of the page upper directory table entry.
>> +
>> +  @param  x    Page upper directory struct variables.
>> +
>> +  @retval  the value of the page upper directory table entry.
>> + **/
>> +#define PUD_VAL(x)  ((x).PudVal)
>> +
>> +/**
>> +  Gets the value of the page middle directory table entry.
>> +
>> +  @param  x    Page middle directory struct variables.
>> +
>> +  @retval  the value of the page middle directory table entry.
>> + **/
>> +#define PMD_VAL(x)  ((x).PmdVal)
>> +
>> +/**
>> +  Gets the value of the page table entry.
>> +
>> +  @param  x    Page table entry struct variables.
>> +
>> +  @retval  the value of the page table entry.
>> + **/
>> +#define PTE_VAL(x)  ((x).PteVal)
>> +
>> +#define PGD_TABLE_SIZE  (ENTRYS_PER_PGD * sizeof(PGD))
>> +#define PUD_TABLE_SIZE  (ENTRYS_PER_PUD * sizeof(PUD))
>> +#define PMD_TABLE_SIZE  (ENTRYS_PER_PMD * sizeof(PMD))
>> +#define PTE_TABLE_SIZE  (ENTRYS_PER_PTE * sizeof(PTE))
>> +
>> +/**
>> +  Gets the physical address of the record in the page table entry.
>> +
>> +  @param  x    Page table entry struct variables.
>> +
>> +  @retval  the value of the physical address.
>> + **/
>> +#define GET_PAGE_ATTRIBUTES(x)  (UINTN) {(PTE_VAL(x) & ~PFN_MASK)}
>> +
>> +/**
>> +  Gets the virtual address of the next block of the specified virtual address
>> +  that is aligned with the size of the global page directory mapping.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +  @param  End    The end address of the memory region.
>> +
>> +  @retval   the specified virtual address  of the next block.
>> + **/
>> +#define PGD_ADDRESS_END(Address, End)                  \
>> +({                                                     \
>> +  UINTN Boundary = ((Address) + PGD_SIZE) & PGD_MASK;  \
>> +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
>> +})
>> +
>> +/**
>> +  Gets the virtual address of the next block of the specified virtual address
>> +  that is aligned with the size of the page upper directory mapping.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +  @param  End    The end address of the memory region.
>> +
>> +  @retval   the specified virtual address  of the next block.
>> + **/
>> +#define PUD_ADDRESS_END(Address, End)                  \
>> +({                                                     \
>> +  UINTN Boundary = ((Address) + PUD_SIZE) & PUD_MASK;  \
>> +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
>> +})
>> +
>> +/**
>> +  Gets the virtual address of the next block of the specified virtual address
>> +  that is aligned with the size of the page middle directory mapping.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +  @param  End    The end address of the memory region.
>> +
>> +  @retval   the specified virtual address  of the next block.
>> + **/
>> +#define PMD_ADDRESS_END(Address, End)                  \
>> +({                                                     \
>> +  UINTN Boundary = ((Address) + PMD_SIZE) & PMD_MASK;  \
>> +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
>> +})
>> +
>> +/**
>> +  Get Specifies the virtual address corresponding to the index of the page global directory table entry.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +
>> +  @retval   the index of the page global directory table entry.
>> + **/
>> +#define PGD_INDEX(Address)  (((Address) >> PGD_SHIFT) & (ENTRYS_PER_PGD-1))
>> +
>> +/**
>> +  Get Specifies the virtual address corresponding to the index of the page upper directory table entry.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +  @param  End    The end address of the memory region.
>> +
>> +  @retval   the index of the page upper directory table entry.
>> + **/
>> +#define PUD_INDEX(Address)  (((Address) >> PUD_SHIFT) & (ENTRYS_PER_PUD - 1))
>> +
>> +/**
>> +  Get Specifies the virtual address corresponding to the index of the page middle directory table entry.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +
>> +  @retval   the index of the page middle directory table entry.
>> + **/
>> +#define PMD_INDEX(Address)  (((Address) >> PMD_SHIFT) & (ENTRYS_PER_PMD - 1))
>> +
>> +/**
>> +  Get Specifies the virtual address corresponding to the index of the page table entry.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +
>> +  @retval   the index of the page table entry.
>> + **/
>> +#define PTE_INDEX(Address)  (((Address) >> EFI_PAGE_SHIFT) & (ENTRYS_PER_PTE - 1))
>> +
>> +/**
>> +  Calculates the value of the page table entry based on the specified virtual address and properties.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +  @param  Attributes  Specifies the Attributes.
>> +
>> +  @retval    the value of the page table entry.
>> + **/
>> +#define MAKE_PTE(Address, Attributes)  (PTE){((((Address) >> EFI_PAGE_SHIFT) << 12) | (Attributes))}
>> +
>> +/**
>> +  Get Global bit from Attributes
>> +
>> +  @param  Attributes  Specifies the Attributes.
>> + * */
>> +#define GET_GLOBALBIT(Attributes)  ((Attributes & PAGE_GLOBAL) >> PAGE_GLOBAL_SHIFT)
>> +
>> +/**
>> +  Calculates the value of the Huge page table entry based on the specified virtual address and properties.
>> +
>> +  @param  Address  Specifies the virtual address.
>> +  @param  Attributes  Specifies the Attributes.
>> +
>> +  @retval    the value of the HUGE page table entry.
>> + **/
>> +#define MAKE_HUGE_PTE(Address, Attributes)  (PTE){(((((Address) >> PMD_SHIFT) << PMD_SHIFT) | \
>> +                                             ((Attributes) | (GET_GLOBALBIT(Attributes) << PAGE_HGLOBAL_SHIFT) | \
>> +                                             PAGE_HUGE)))}
>> +
>> +/**
>> +  Check whether the large page table entry is.
>> +
>> +  @param  Val The value of the page table entry.
>> +
>> +  @retval    1   Is huge page table entry.
>> +  @retval    0   Isn't huge page table entry.
>> +**/
>> +#define IS_HUGE_PAGE(Val)  ((((Val) & PAGE_HUGE) == PAGE_HUGE) && \
>> +                            (((Val) & PAGE_HGLOBAL) == PAGE_HGLOBAL))
>> +
>> +#define HUGE_PAGE_SIZE  (PMD_SIZE)
>> +
>> +/**
>> +  Check that the global page directory table entry is empty.
>> +
>> +  @param  pgd   the global page directory struct variables.
>> +
>> +  @retval    1   The page table is invalid.
>> +  @retval    0   The page table is valid.
>> +**/
>> +#define PGD_IS_EMPTY(Val)  (PGD_VAL(Val) == INVALID_PAGE)
>> +
>> +/**
>> +  Check that the page upper directory table entry is empty.
>> +
>> +  @param  pud   Page upper directory struct variables.
>> +
>> +  @retval    1   The page table is invalid.
>> +  @retval    0   The page table is valid.
>> +**/
>> +#define PUD_IS_EMPTY(Val)  (PUD_VAL(Val) == INVALID_PAGE)
>> +
>> +/**
>> +  Check that the page middle directory table entry is empty.
>> +
>> +  @param  pmd   Page middle directory struct variables.
>> +
>> +  @retval    1   The page table is invalid.
>> +  @retval    0   The page table is valid.
>> +**/
>> +#define PMD_IS_EMPTY(Val)  (PMD_VAL(Val) == INVALID_PAGE)
>> +
>> +/**
>> +  Check that the page the page table entry is empty.
>> +
>> +  @param  pte   Page table entry struct variables.
>> +
>> +  @retval    1   The page table is invalid.
>> +  @retval    0   The page table is valid.
>> +**/
>> +#define PTE_IS_EMPTY(Val)  (!(PTE_VAL(Val) & (~PAGE_VALID)))
>> +#endif // PAGE_H_
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
>> new file mode 100644
>> index 0000000000..c214e8d847
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
>> @@ -0,0 +1,178 @@
>> +/** @file
>> +  CPU Memory Map Unit PEI phase driver.
>> +
>> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +  @par Glossary:
>> +    - Tlb      - Translation Lookaside Buffer
>> +**/
>> +
>> +#include <Uefi.h>
>> +#include <Library/BaseLib.h>
>> +#include <Library/BaseMemoryLib.h>
>> +#include <Library/CacheMaintenanceLib.h>
>> +#include <Library/CpuMmuLib.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/MemoryAllocationLib.h>
>> +#include <Library/PcdLib.h>
>> +#include <Register/LoongArch64/Csr.h>
>> +
>> +#include "Page.h"
>> +#include "Tlb.h"
>> +#include "CommonMmuLib.h"
>> +
>> +//
>> +// For coding convenience, define the maximum valid
>> +// LoongArch exception.
>> +// Since UEFI V2.11, it will be present in DebugSupport.h.
>> +//
>> +#define MAX_LOONGARCH_EXCEPTION  64
>> +
>> +/**
>> +  Create a page table and initialize the memory management unit(MMU).
>> +
>> +  @param[in]   MemoryTable           A pointer to a memory ragion table.
>> +  @param[out]  TranslationTableBase  A pointer to a translation table base address.
>> +  @param[out]  TranslationTableSize  A pointer to a translation table base size.
>> +
>> +  @retval  EFI_SUCCESS                Configure MMU successfully.
>> +           EFI_INVALID_PARAMETER      MemoryTable is NULL.
>> +           EFI_UNSUPPORTED            Out of memory space or size not aligned.
>> +**/
>> +EFI_STATUS
>> +EFIAPI
>> +ConfigureMemoryManagementUnit (
>> +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryTable,
>> +  OUT VOID                      **TranslationTableBase OPTIONAL,
>> +  OUT UINTN                     *TranslationTableSize  OPTIONAL
>> +  )
>> +{
>> +  PGD            *SwapperPageDir;
>> +  UINTN          PgdShift;
>> +  UINTN          PgdWide;
>> +  UINTN          PudShift;
>> +  UINTN          PudWide;
>> +  UINTN          PmdShift;
>> +  UINTN          PmdWide;
>> +  UINTN          PteShift;
>> +  UINTN          PteWide;
>> +  UINTN          Length;
>> +  UINTN          TlbReEntry;
>> +  UINTN          TlbReEntryOffset;
>> +  UINTN          Remaining;
>> +  RETURN_STATUS  Status;
>> +
>> +  SwapperPageDir = NULL;
>> +  PgdShift       = PGD_SHIFT;
>> +  PgdWide        = PGD_WIDE;
>> +  PudShift       = PUD_SHIFT;
>> +  PudWide        = PUD_WIDE;
>> +  PmdShift       = PMD_SHIFT;
>> +  PmdWide        = PMD_WIDE;
>> +  PteShift       = PTE_SHIFT;
>> +  PteWide        = PTE_WIDE;
>> +
>> +  if (MemoryTable == NULL) {
>> +    ASSERT (MemoryTable != NULL);
>> +    return EFI_INVALID_PARAMETER;
>> +  }
>> +
>> +  SwapperPageDir = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
>> +  ZeroMem (SwapperPageDir, PGD_TABLE_SIZE);
>> +
>> +  if (SwapperPageDir == NULL) {
>> +    goto FreeTranslationTable;
>> +  }
>> +
>> +  CsrWrite (LOONGARCH_CSR_PGDL, (UINTN)SwapperPageDir);
>> +
>> +  while (MemoryTable->Length != 0) {
>> +    DEBUG ((
>> +      DEBUG_INFO,
>> +      "%a %d VirtualBase %p VirtualEnd %p Attributes %p .\n",
>> +      __func__,
>> +      __LINE__,
>> +      MemoryTable->VirtualBase,
>> +      (MemoryTable->Length + MemoryTable->VirtualBase),
>> +      MemoryTable->Attributes
>> +      ));
>> +
>> +    Status = FillTranslationTable (MemoryTable);
>> +    if (EFI_ERROR (Status)) {
>> +      goto FreeTranslationTable;
>> +    }
>> +
>> +    MemoryTable++;
>> +  }
>> +
>> +  //
>> +  // TLB Re-entry address at the end of exception vector, a vector is up to 512 bytes,
>> +  // so the starting address is: total exception vector size + total interrupt vector size + base.
>> +  // The total size of TLB handler and exception vector size and interrupt vector size should not
>> +  // be lager than 64KB.
>> +  //
>> +  Length           = (UINTN)HandleTlbRefillEnd - (UINTN)HandleTlbRefillStart;
>> +  TlbReEntryOffset = (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * 512;
>> +  Remaining        = TlbReEntryOffset % SIZE_4KB;
>> +  if (Remaining != 0x0) {
>> +    TlbReEntryOffset += (SIZE_4KB - Remaining);
>> +  }
>> +
>> +  TlbReEntry = PcdGet64 (PcdCpuExceptionVectorBaseAddress) + TlbReEntryOffset;
>> +  if ((TlbReEntryOffset + Length) > SIZE_64KB) {
>> +    goto FreeTranslationTable;
>> +  }
>> +
>> +  //
>> +  // Ensure that TLB refill exception base address alignment is equals to 4KB and is valid.
>> +  //
>> +  if (TlbReEntry & (SIZE_4KB - 1)) {
>> +    goto FreeTranslationTable;
>> +  }
>> +
>> +  CopyMem ((VOID *)TlbReEntry, HandleTlbRefillStart, Length);
>> +  InvalidateInstructionCacheRange ((VOID *)(UINTN)HandleTlbRefillStart, Length);
>> +
>> +  DEBUG ((
>> +    DEBUG_INFO,
>> +    "%a  %d PteShift %d PteWide %d PmdShift %d PmdWide %d PudShift %d PudWide %d PgdShift %d PgdWide %d.\n",
>> +    __func__,
>> +    __LINE__,
>> +    PteShift,
>> +    PteWide,
>> +    PmdShift,
>> +    PmdWide,
>> +    PudShift,
>> +    PudWide,
>> +    PgdShift,
>> +    PgdWide
>> +    ));
>> +
>> +  //
>> +  // Set the address of TLB refill exception handler
>> +  //
>> +  SetTlbRebaseAddress ((UINTN)TlbReEntry);
>> +
>> +  //
>> +  // Set page size
>> +  //
>> +  CsrXChg (LOONGARCH_CSR_TLBIDX, (DEFAULT_PAGE_SIZE << CSR_TLBIDX_SIZE), CSR_TLBIDX_SIZE_MASK);
>> +  CsrWrite (LOONGARCH_CSR_STLBPGSIZE, DEFAULT_PAGE_SIZE);
>> +  CsrXChg (LOONGARCH_CSR_TLBREHI, (DEFAULT_PAGE_SIZE << CSR_TLBREHI_PS_SHIFT), CSR_TLBREHI_PS);
>> +
>> +  CsrWrite (LOONGARCH_CSR_PWCTL0, (PteShift | PteWide << 5 | PmdShift << 10 | PmdWide << 15 | PudShift << 20 | PudWide << 25));
>> +  CsrWrite (LOONGARCH_CSR_PWCTL1, (PgdShift | PgdWide << 6));
>> +
>> +  DEBUG ((DEBUG_INFO, "%a %d Enable Mmu Start PageBassAddress %p.\n", __func__, __LINE__, SwapperPageDir));
>> +
>> +  return EFI_SUCCESS;
>> +
>> +FreeTranslationTable:
>> +  if (SwapperPageDir != NULL) {
>> +    FreePages (SwapperPageDir, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
>> +  }
>> +
>> +  return EFI_UNSUPPORTED;
>> +}
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
>> new file mode 100644
>> index 0000000000..9a681ce8e1
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
>> @@ -0,0 +1,48 @@
>> +/** @file
>> +
>> +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +
>> +**/
>> +
>> +#ifndef TLB_H_
>> +#define TLB_H_
>> +
>> +/**
>> +  Invalid corresponding TLB entries are based on the address given
>> +
>> +  @param Address The address corresponding to the invalid page table entry
>> +
>> +  @retval  none
>> +**/
>> +VOID
>> +InvalidTlb (
>> +  UINTN  Address
>> +  );
>> +
>> +/**
>> +  TLB refill handler start.
>> +
>> +  @param  none
>> +
>> +  @retval none
>> +**/
>> +VOID
>> +HandleTlbRefillStart (
>> +  VOID
>> +  );
>> +
>> +/**
>> +  TLB refill handler end.
>> +
>> +  @param  none
>> +
>> +  @retval none
>> +**/
>> +VOID
>> +HandleTlbRefillEnd (
>> +  VOID
>> +  );
>> +
>> +#endif // TLB_H_
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
>> new file mode 100644
>> index 0000000000..c9a8c16336
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
>> @@ -0,0 +1,44 @@
>> +#------------------------------------------------------------------------------
>> +#
>> +# TLB operation functions
>> +#
>> +# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>> +#
>> +# SPDX-License-Identifier: BSD-2-Clause-Patent
>> +#
>> +#-----------------------------------------------------------------------------
>> +
>> +#include <Register/LoongArch64/Csr.h>
>> +
>> +ASM_GLOBAL ASM_PFX(HandleTlbRefillStart)
>> +ASM_GLOBAL ASM_PFX(HandleTlbRefillEnd)
>> +ASM_GLOBAL ASM_PFX(InvalidTlb)
>> +
>> +#
>> +#  Refill the page table.
>> +#  @param  VOID
>> +#  @retval  VOID
>> +#
>> +ASM_PFX(HandleTlbRefillStart):
>> +  csrwr   $t0, LOONGARCH_CSR_TLBRSAVE
>> +  csrrd   $t0, LOONGARCH_CSR_PGD
>> +  lddir   $t0, $t0, 3   #Put pud BaseAddress into T0
>> +  lddir   $t0, $t0, 2   #Put pmd BaseAddress into T0
>> +  lddir   $t0, $t0, 1   #Put pte BaseAddress into T0
>> +  ldpte   $t0, 0
>> +  ldpte   $t0, 1
>> +  tlbfill   // refill hi,lo0,lo1
>> +  csrrd   $t0, LOONGARCH_CSR_TLBRSAVE
>> +  ertn
>> +ASM_PFX(HandleTlbRefillEnd):
>> +
>> +#
>> +# Invalid corresponding TLB entries are based on the address given
>> +# @param a0 The address corresponding to the invalid page table entry
>> +# @retval  none
>> +#
>> +ASM_PFX(InvalidTlb):
>> +    invtlb  INVTLB_ADDR_GTRUE_OR_ASID, $zero, $a0
>> +    jirl    $zero, $ra, 0
>> +
>> +    .end
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>> new file mode 100644
>> index 0000000000..45b15db4c9
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>> @@ -0,0 +1,44 @@
>> +## @file
>> +#  CPU Memory Map Unit PEI phase driver.
>> +#
>> +#  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                      = PeiCpuMmuLib
>> +  MODULE_UNI_FILE                = PeiCpuMmuLib.uni
>> +  FILE_GUID                      = F67EB983-AC2A-7550-AB69-3BC51A1C895B
>> +  MODULE_TYPE                    = PEIM
>> +  VERSION_STRING                 = 1.0
>> +  LIBRARY_CLASS                  = CpuMmuLib | SEC PEIM
>> +
>> +#
>> +#  VALID_ARCHITECTURES           = LOONGARCH64
>> +#
>> +
>> +[Sources.LoongArch64]
>> +  LoongArch64/TlbOperation.S   | GCC
>> +  LoongArch64/CommonMmuLib.c
>> +  LoongArch64/PeiCpuMmuLib.c
>> +  LoongArch64/CommonMmuLib.h
>> +  LoongArch64/Tlb.h
>> +  LoongArch64/Page.h
>> +
>> +[Packages]
>> +  MdePkg/MdePkg.dec
>> +  MdeModulePkg/MdeModulePkg.dec
>> +  UefiCpuPkg/UefiCpuPkg.dec
>> +
>> +[PCD]
>> +  gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask
>> +  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress
>> +
>> +[LibraryClasses]
>> +  CacheMaintenanceLib
>> +  DebugLib
>> +  MemoryAllocationLib
>> +  PcdLib
>> diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
>> new file mode 100644
>> index 0000000000..3e21334f3e
>> --- /dev/null
>> +++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
>> @@ -0,0 +1,14 @@
>> +// /** @file
>> +// CPU Memory Manager Unit library instance for PEI modules.
>> +//
>> +// CPU Memory Manager Unit library instance for PEI modules.
>> +//
>> +// 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 "CPU Memory Manager Unit library instance for PEI modules."
>> +
>> +#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for PEI modules."
>> diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
>> index 28eed85bce..178dc3c0f9 100644
>> --- a/UefiCpuPkg/UefiCpuPkg.dsc
>> +++ b/UefiCpuPkg/UefiCpuPkg.dsc
>> @@ -207,5 +207,9 @@
>>     UefiCpuPkg/CpuTimerDxeRiscV64/CpuTimerDxeRiscV64.inf
>>     UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf
>>   
>> +[Components.LOONGARCH64]
>> +  UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>> +  UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>> +
>>   [BuildOptions]
>>     *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
>
>
> 
>


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114935): https://edk2.groups.io/g/devel/message/114935
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-02-01  3:05         ` Chao Li
@ 2024-02-01 19:36           ` Pedro Falcato
  2024-02-01 23:02             ` Laszlo Ersek
  2024-02-02 15:14             ` Leif Lindholm
  0 siblings, 2 replies; 89+ messages in thread
From: Pedro Falcato @ 2024-02-01 19:36 UTC (permalink / raw)
  To: Chao Li
  Cc: devel, Laszlo Ersek, Eric Dong, Ray Ni, Rahul Kumar,
	Gerd Hoffmann, Baoqi Zhang, Dongyan Qian, Xianglai Li, Bibo Mao,
	Andrew Fish, Leif Lindholm, Kinney, Michael D

On Thu, Feb 1, 2024 at 3:05 AM Chao Li <lichao@loongson.cn> wrote:
>
> Hi Pedro and Laszlo,
>
> Part of the code in this patch is indeed quoted from the Linux kernel, and do you think it is inapproparate? If so, we need to refactor this module, what are you suggests with the refactoring? Just remove the unused logic from the Kernel code and keep the logic good or refactor from scratch?

+CC stewards

Disclaimer: I'm not a lawyer

It is wildly inappropriate. All of the code was clearly inspired by
GPL and derives from the Linux GPL code, it's not just unused logic.
You should triple check *every other patch* you've sent out for these
kinds of GPL violations.

There's another way of writing this sort of code (that doesn't involve
all the Linux mm craziness) but I don't know if changing strategies
would be considered getting rid of any shadow of GPL/IP violation.

(As a side note, I don't really understand IP in the software world.
If you work, say, on GPL software for a moment in time, are you always
going to be "GPL-tainted"? Surely not? Most people in the industry
I've talked to about this say that, yeah, no, corps don't expect that.
But no one really seems to have drawn a line between OK and not-OK,
but rather "please please don't sue us". And in this case I don't know
(but I suspect it'd be uncomfortable) for someone to redesign a
solution right away, after being "tainted". Anyway, tough problem, and
IANAL :/)

-- 
Pedro


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114956): https://edk2.groups.io/g/devel/message/114956
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 20/37] ArmVirtPkg: Enable CpuMmio2Dxe
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 20/37] ArmVirtPkg: Enable CpuMmio2Dxe Chao Li
@ 2024-02-01 22:19   ` Laszlo Ersek
  0 siblings, 0 replies; 89+ messages in thread
From: Laszlo Ersek @ 2024-02-01 22:19 UTC (permalink / raw)
  To: devel, lichao; +Cc: Ard Biesheuvel, Leif Lindholm, Sami Mujawar, Gerd Hoffmann

On 1/26/24 07:29, Chao Li wrote:
> CpuMmio2Dxe is supports MMIO, enable it.
>
> Build-tested only (with "ArmVirtQemu.dsc").
>
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Leif Lindholm <quic_llindhol@quicinc.com>
> Cc: Sami Mujawar <sami.mujawar@arm.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Signed-off-by: Chao Li <lichao@loongson.cn>
> ---
>  ArmVirtPkg/ArmVirtCloudHv.dsc        | 2 +-
>  ArmVirtPkg/ArmVirtCloudHv.fdf        | 2 +-
>  ArmVirtPkg/ArmVirtKvmTool.dsc        | 2 +-
>  ArmVirtPkg/ArmVirtKvmTool.fdf        | 2 +-
>  ArmVirtPkg/ArmVirtQemu.dsc           | 2 +-
>  ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc | 2 +-
>  ArmVirtPkg/ArmVirtQemuKernel.dsc     | 2 +-
>  7 files changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/ArmVirtPkg/ArmVirtCloudHv.dsc b/ArmVirtPkg/ArmVirtCloudHv.dsc
> index 76c0d28544..5cb2a609b1 100644
> --- a/ArmVirtPkg/ArmVirtCloudHv.dsc
> +++ b/ArmVirtPkg/ArmVirtCloudHv.dsc
> @@ -341,7 +341,7 @@
>    #
>    # PCI support
>    #
> -  ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf {
> +  UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf {
>      <LibraryClasses>
>        NULL|OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
>    }
> diff --git a/ArmVirtPkg/ArmVirtCloudHv.fdf b/ArmVirtPkg/ArmVirtCloudHv.fdf
> index 56d1ea6e8c..8554efc294 100644
> --- a/ArmVirtPkg/ArmVirtCloudHv.fdf
> +++ b/ArmVirtPkg/ArmVirtCloudHv.fdf
> @@ -201,7 +201,7 @@ READ_LOCK_STATUS   = TRUE
>    #
>    # PCI support
>    #
> -  INF ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf
> +  INF UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf
>    INF MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf
>    INF MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
>    INF OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf
> diff --git a/ArmVirtPkg/ArmVirtKvmTool.dsc b/ArmVirtPkg/ArmVirtKvmTool.dsc
> index cac4fe06d3..20da331966 100644
> --- a/ArmVirtPkg/ArmVirtKvmTool.dsc
> +++ b/ArmVirtPkg/ArmVirtKvmTool.dsc
> @@ -372,7 +372,7 @@
>    #
>    # PCI support
>    #
> -  ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf {
> +  UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf {
>      <LibraryClasses>
>        NULL|OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
>        NULL|OvmfPkg/Library/BaseCachingPciExpressLib/BaseCachingPciExpressLib.inf
> diff --git a/ArmVirtPkg/ArmVirtKvmTool.fdf b/ArmVirtPkg/ArmVirtKvmTool.fdf
> index 82aff47673..cdf756c112 100644
> --- a/ArmVirtPkg/ArmVirtKvmTool.fdf
> +++ b/ArmVirtPkg/ArmVirtKvmTool.fdf
> @@ -195,7 +195,7 @@ READ_LOCK_STATUS   = TRUE
>    #
>    # PCI support
>    #
> -  INF ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf
> +  INF UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf
>    INF MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf
>    INF MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
>    INF OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
> diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc
> index cf306cac08..dbd2396c78 100644
> --- a/ArmVirtPkg/ArmVirtQemu.dsc
> +++ b/ArmVirtPkg/ArmVirtQemu.dsc
> @@ -526,7 +526,7 @@
>    #
>    # PCI support
>    #
> -  ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf {
> +  UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf {
>      <LibraryClasses>
>        NULL|OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
>    }
> diff --git a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
> index 9b3e37d5c9..c5d097ffb9 100644
> --- a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
> +++ b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
> @@ -153,7 +153,7 @@ READ_LOCK_STATUS   = TRUE
>    #
>    # PCI support
>    #
> -  INF ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf
> +  INF UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf
>    INF MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf
>    INF MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
>    INF OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf
> diff --git a/ArmVirtPkg/ArmVirtQemuKernel.dsc b/ArmVirtPkg/ArmVirtQemuKernel.dsc
> index c0d079e28d..6a6ecfc12a 100644
> --- a/ArmVirtPkg/ArmVirtQemuKernel.dsc
> +++ b/ArmVirtPkg/ArmVirtQemuKernel.dsc
> @@ -431,7 +431,7 @@
>    #
>    # PCI support
>    #
> -  ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf {
> +  UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf {
>      <LibraryClasses>
>        NULL|OvmfPkg/Fdt/FdtPciPcdProducerLib/FdtPciPcdProducerLib.inf
>    }

I have verified this patch as follows:

* diffed "UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf" against
"ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf" -- good:

>| --- ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf  2023-11-21 17:30:08.878750682 +0100
>| +++ UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf      2024-02-01 22:56:42.146986301 +0100
>| @@ -3,30 +3,31 @@
>|  #
>|  # Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
>|  # Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
>| +# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>|  #
>|  # SPDX-License-Identifier: BSD-2-Clause-Patent
>|  #
>|  ##
>|
>|  [Defines]
>| -  INF_VERSION                    = 0x00010005
>| -  BASE_NAME                      = ArmPciCpuIo2Dxe
>| -  FILE_GUID                      = 168D1A6E-F4A5-448A-9E95-795661BB3067
>| +  INF_VERSION                    = 1.29
>| +  BASE_NAME                      = CpuMmio2Dxe
>| +  MODULE_UNI_FILE                = CpuMmio2Dxe.uni
>| +  FILE_GUID                      = FBC36D76-CF22-2584-DBD8-85FF765BAEF1
>|    MODULE_TYPE                    = DXE_DRIVER
>|    VERSION_STRING                 = 1.0
>| -  ENTRY_POINT                    = ArmPciCpuIo2Initialize
>| +  ENTRY_POINT                    = CpuMmio2Initialize
>|
>|  #
>|  # The following information is for reference only and not required by the build tools.
>|  #
>| -#  VALID_ARCHITECTURES           = ARM AARCH64
>| +#  VALID_ARCHITECTURES           = ARM AARCH64 LOONGARCH64 RISCV64
>|  #
>|
>|  [Sources]
>| -  ArmPciCpuIo2Dxe.c
>| +  CpuMmio2Dxe.c
>|
>|  [Packages]
>| -  ArmPkg/ArmPkg.dec
>|    MdePkg/MdePkg.dec
>|
>|  [LibraryClasses]

* diffed "UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.c" against
"ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.c" -- good:

>| --- ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.c    2023-11-21 17:30:08.878750682 +0100
>| +++ UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.c        2024-02-01 22:56:42.146986301 +0100
>| @@ -3,6 +3,7 @@
>|
>|  Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>
>|  Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
>| +Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>|
>|  SPDX-License-Identifier: BSD-2-Clause-Patent
>|
>| @@ -513,7 +514,7 @@
>|  //
>|  // CPU I/O 2 Protocol instance
>|  //
>| -STATIC EFI_CPU_IO2_PROTOCOL  mCpuIo2 = {
>| +STATIC EFI_CPU_IO2_PROTOCOL  mCpuMmio2 = {
>|    {
>|      CpuMemoryServiceRead,
>|      CpuMemoryServiceWrite
>| @@ -536,7 +537,7 @@
>|  **/
>|  EFI_STATUS
>|  EFIAPI
>| -ArmPciCpuIo2Initialize (
>| +CpuMmio2Initialize (
>|    IN EFI_HANDLE        ImageHandle,
>|    IN EFI_SYSTEM_TABLE  *SystemTable
>|    )
>| @@ -547,7 +548,7 @@
>|    Status = gBS->InstallMultipleProtocolInterfaces (
>|                    &mHandle,
>|                    &gEfiCpuIo2ProtocolGuid,
>| -                  &mCpuIo2,
>| +                  &mCpuMmio2,
>|                    NULL
>|                    );
>|    ASSERT_EFI_ERROR (Status);

Therefore, no changes in functionality are expected.

- Furthermore, dug a bit down in git history, to see where the NULL
class resolution for FdtPciPcdProducerLib comes from. It is commit
53ee81bb686c ("ArmVirtPkg/ArmVirtQemu: switch to generic
PciHostBridgeDxe", 2016-09-02). Good.

Therefore:

Reviewed-by: Laszlo Ersek <lersek@redhat.com>



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114970): https://edk2.groups.io/g/devel/message/114970
Mute This Topic: https://groups.io/mt/103971664/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-02-01  7:57     ` Chao Li
@ 2024-02-01 22:46       ` Laszlo Ersek
  2024-02-02  3:30         ` Chao Li
  2024-02-02  3:33       ` Ni, Ray
  1 sibling, 1 reply; 89+ messages in thread
From: Laszlo Ersek @ 2024-02-01 22:46 UTC (permalink / raw)
  To: Chao Li, devel
  Cc: Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

On 2/1/24 08:57, Chao Li wrote:

> Let's me tell you this library how to work:
> 
> In PEI stage, in addition to ensuring that the MMU is not used, the user
> must call the ConfigureMemoryManagementUnit toinitialization the MMU,
> such as filling the static page tables, set the TLB refill exception
> entry point, set the page size etc. the ConfigureMemoryManagementUnit is
> a private API but related to ARCH.
> 
> During DXE stage, this library will provide services for CpuDxe and
> other drivers, but almost changes to memory page attributes use the
> protocols provided by CpuDxe. That is why the CpuMmuLib.h only contains
> two APIs, it only can get/set the attribute.
> 
> In short, the PEI stage needs to initialize and set up TLB refill entry
> point and method(dynamically populate the TLB, keep the PA == VA), and
> DXE stage is provides services for changing the memory attributes.

My understanding is the following then:

(1) You should provide a very low level library, implementing and also
publicly exposing primitives for manipulating page tables. This library
could / should expose ConfigureMemoryManagementUnit(). This library
should not use global variables at all, I think -- the user of the
library would be responsible for tracking state. API names in this
library should have a good, distinctive prefix.

(2) Introduce a higher level lib class that contains very few APIs --
APIs that are possible to implement by multiple arches.

(3) Provide a library instance of class (2) by way of consuming (1).

(4) In your platform PEIM, for setting up paging, use (1) directly, or
(if you can do it) only (2)+(3).

(5) In DXE phase code, use (2)+(3).

Basically I both agree and disagree with Ray's review for your

  [PATCH v3 13/39] UefiCpuPkg: Add CpuMmuLib.h to UefiCpuPkg

I think that Ray is right in that we want good high-level paging APIs
that can be implemented by multiple arches. On the other hand,
architectural differences do exist, they are idiosyncratic, they need to
exist *somewhere*, and that idiosyncratic logic may still be necessary
to use from *multiple* places -- so that indicates that we need a
library (low-level) that is not "well designed" but actually a "grab bag
of utilities" that just does the work.

To satisfy both requirements, insert a new layer of indirection :)

I'm not a big fan of top-down library design. I rather like to look at
existent usage patterns, and then factoring out common stuff to a
library. At that point, unification / cleanup possibilities may emerge.
But getting the API design right immediately -- that requires seeing the
future, or *extreme* amounts of experience. So an "ugly" interface
library may be unavoidable. But, we can always wrap it into nicer
interfaces, for those clients that don't really need the low-level
primitives.

Laszlo



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114972): https://edk2.groups.io/g/devel/message/114972
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-02-01 19:36           ` Pedro Falcato
@ 2024-02-01 23:02             ` Laszlo Ersek
  2024-02-02 15:14             ` Leif Lindholm
  1 sibling, 0 replies; 89+ messages in thread
From: Laszlo Ersek @ 2024-02-01 23:02 UTC (permalink / raw)
  To: Pedro Falcato, Chao Li
  Cc: devel, Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao, Andrew Fish, Leif Lindholm,
	Kinney, Michael D

On 2/1/24 20:36, Pedro Falcato wrote:
> On Thu, Feb 1, 2024 at 3:05 AM Chao Li <lichao@loongson.cn> wrote:
>>
>> Hi Pedro and Laszlo,
>>
>> Part of the code in this patch is indeed quoted from the Linux kernel, and do you think it is inapproparate? If so, we need to refactor this module, what are you suggests with the refactoring? Just remove the unused logic from the Kernel code and keep the logic good or refactor from scratch?
> 
> +CC stewards
> 
> Disclaimer: I'm not a lawyer
> 
> It is wildly inappropriate. All of the code was clearly inspired by
> GPL and derives from the Linux GPL code, it's not just unused logic.
> You should triple check *every other patch* you've sent out for these
> kinds of GPL violations.
> 
> There's another way of writing this sort of code (that doesn't involve
> all the Linux mm craziness) but I don't know if changing strategies
> would be considered getting rid of any shadow of GPL/IP violation.
> 
> (As a side note, I don't really understand IP in the software world.
> If you work, say, on GPL software for a moment in time, are you always
> going to be "GPL-tainted"? Surely not? Most people in the industry
> I've talked to about this say that, yeah, no, corps don't expect that.
> But no one really seems to have drawn a line between OK and not-OK,
> but rather "please please don't sue us". And in this case I don't know
> (but I suspect it'd be uncomfortable) for someone to redesign a
> solution right away, after being "tainted". Anyway, tough problem, and
> IANAL :/)
> 

If Chao can find someone at loongson.cn who can write this code *from
zero* and has never looked at the Linux kernel, IMO that would be
preferable.

If not, then I think Chao can rewrite this code too, but it must be from
zero (starting with an empty C file in the editor). Only architecture
manuals and such should be used -- I assume all that's needed for this
paging stuff is described in sufficient detail in architecture manuals.
And yes, that implies Chao needs to manually #define new macros from
zero, if the manuals provide constants, bitmasks, etc.

Then, Pedro, you'd be invited to please check whether the new code
struck you as "foreign"! :)

Thanks!
Laszlo



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114975): https://edk2.groups.io/g/devel/message/114975
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 18/37] ArmVirtPkg: Move PCD of FDT base address and FDT padding to OvmfPkg
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 18/37] ArmVirtPkg: Move PCD of FDT base address and FDT padding to OvmfPkg Chao Li
@ 2024-02-01 23:20   ` Laszlo Ersek
  0 siblings, 0 replies; 89+ messages in thread
From: Laszlo Ersek @ 2024-02-01 23:20 UTC (permalink / raw)
  To: Chao Li, devel
  Cc: Ard Biesheuvel, Leif Lindholm, Sami Mujawar, Gerd Hoffmann,
	Jiewen Yao, Jordan Justen

On 1/26/24 07:29, Chao Li wrote:
> Moved PcdDeviceTreeInitialBaseAddress and PcdDeviceTreeAllocationPadding
> to OvmfPkg for easier use by other architectures.
> 
> Build-tested only (with "ArmVirtQemu.dsc").
> 
> BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584
> 
> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Leif Lindholm <quic_llindhol@quicinc.com>
> Cc: Sami Mujawar <sami.mujawar@arm.com>
> Cc: Gerd Hoffmann <kraxel@redhat.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Jordan Justen <jordan.l.justen@intel.com>
> Signed-off-by: Chao Li <lichao@loongson.cn>
> Acked-by: Gerd Hoffmann <kraxel@redhat.com>
> Reviewed-by: Sami Mujawar <sami.mujawar@arm.com>
> ---
>  ArmVirtPkg/ArmVirtCloudHv.dsc                     |  2 +-
>  ArmVirtPkg/ArmVirtKvmTool.dsc                     |  2 +-
>  ArmVirtPkg/ArmVirtPkg.dec                         | 14 --------------
>  ArmVirtPkg/ArmVirtQemu.dsc                        |  2 +-
>  ArmVirtPkg/ArmVirtQemuKernel.dsc                  |  2 +-
>  ArmVirtPkg/ArmVirtXen.dsc                         |  2 +-
>  .../ArmVirtPsciResetSystemPeiLib.inf              |  3 ++-
>  .../CloudHvVirtMemInfoPeiLib.inf                  |  3 ++-
>  .../DebugLibFdtPL011UartFlash.inf                 |  3 ++-
>  .../EarlyFdt16550SerialPortHookLib.inf            |  3 ++-
>  .../EarlyFdtPL011SerialPortLib.inf                |  3 ++-
>  .../KvmtoolPlatformPeiLib.inf                     |  5 +++--
>  .../Library/PlatformPeiLib/PlatformPeiLib.inf     | 10 +++++-----
>  .../QemuVirtMemInfoLib/QemuVirtMemInfoPeiLib.inf  |  3 ++-
>  .../PrePi/ArmVirtPrePiUniCoreRelocatable.inf      |  3 ++-
>  OvmfPkg/OvmfPkg.dec                               | 15 +++++++++++++++
>  16 files changed, 42 insertions(+), 33 deletions(-)

Reviewed-by: Laszlo Ersek <lersek@redhat.com>




-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#114981): https://edk2.groups.io/g/devel/message/114981
Mute This Topic: https://groups.io/mt/103971659/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 11/37] UefiCpuPkg: Add LoongArch64 CPU Timer instance
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 11/37] UefiCpuPkg: Add LoongArch64 CPU Timer instance Chao Li
@ 2024-02-02  3:24   ` Ni, Ray
  2024-02-02  3:38     ` Chao Li
  0 siblings, 1 reply; 89+ messages in thread
From: Ni, Ray @ 2024-02-02  3:24 UTC (permalink / raw)
  To: devel@edk2.groups.io, lichao@loongson.cn
  Cc: Dong, Eric, Kumar, Rahul R, Gerd Hoffmann


> 
>  [LibraryClasses]
>    BaseLib
> -  PcdLib
>    DebugLib
> +  PcdLib

1. This PcdLib location change is unnecessary.


> +  SafeIntLib

2. x86 Lib instance does not depend on SafeIntLib.
Adding it in common section may break some x86 platforms if SafeIntLib is not added the platform DSC.
Can you move it under LoongArch section?



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115007): https://edk2.groups.io/g/devel/message/115007
Mute This Topic: https://groups.io/mt/103971650/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-02-01 22:46       ` Laszlo Ersek
@ 2024-02-02  3:30         ` Chao Li
  0 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-02-02  3:30 UTC (permalink / raw)
  To: Laszlo Ersek, devel
  Cc: Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

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

Hi Laszlo,


Thanks,
Chao
On 2024/2/2 06:46, Laszlo Ersek wrote:
> On 2/1/24 08:57, Chao Li wrote:
>
>> Let's me tell you this library how to work:
>>
>> In PEI stage, in addition to ensuring that the MMU is not used, the user
>> must call the ConfigureMemoryManagementUnit toinitialization the MMU,
>> such as filling the static page tables, set the TLB refill exception
>> entry point, set the page size etc. the ConfigureMemoryManagementUnit is
>> a private API but related to ARCH.
>>
>> During DXE stage, this library will provide services for CpuDxe and
>> other drivers, but almost changes to memory page attributes use the
>> protocols provided by CpuDxe. That is why the CpuMmuLib.h only contains
>> two APIs, it only can get/set the attribute.
>>
>> In short, the PEI stage needs to initialize and set up TLB refill entry
>> point and method(dynamically populate the TLB, keep the PA == VA), and
>> DXE stage is provides services for changing the memory attributes.
> My understanding is the following then:
>
> (1) You should provide a very low level library, implementing and also
> publicly exposing primitives for manipulating page tables. This library
> could / should expose ConfigureMemoryManagementUnit(). This library
> should not use global variables at all, I think -- the user of the
> library would be responsible for tracking state. API names in this
> library should have a good, distinctive prefix.
>
> (2) Introduce a higher level lib class that contains very few APIs --
> APIs that are possible to implement by multiple arches.
>
> (3) Provide a library instance of class (2) by way of consuming (1).
>
> (4) In your platform PEIM, for setting up paging, use (1) directly, or
> (if you can do it) only (2)+(3).
>
> (5) In DXE phase code, use (2)+(3).
>
> Basically I both agree and disagree with Ray's review for your
>
>    [PATCH v3 13/39] UefiCpuPkg: Add CpuMmuLib.h to UefiCpuPkg
>
> I think that Ray is right in that we want good high-level paging APIs
> that can be implemented by multiple arches. On the other hand,
> architectural differences do exist, they are idiosyncratic, they need to
> exist *somewhere*, and that idiosyncratic logic may still be necessary
> to use from *multiple* places -- so that indicates that we need a
> library (low-level) that is not "well designed" but actually a "grab bag
> of utilities" that just does the work.
>
> To satisfy both requirements, insert a new layer of indirection :)
>
> I'm not a big fan of top-down library design. I rather like to look at
> existent usage patterns, and then factoring out common stuff to a
> library. At that point, unification / cleanup possibilities may emerge.
> But getting the API design right immediately -- that requires seeing the
> future, or *extreme* amounts of experience. So an "ugly" interface
> library may be unavoidable. But, we can always wrap it into nicer
> interfaces, for those clients that don't really need the low-level
> primitives.
When I redesign this module, I will try to add a new layer, what you 
call a low-level library.
>
> Laszlo


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115008): https://edk2.groups.io/g/devel/message/115008
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 12/37] UefiCpuPkg: Add CPU exception library for LoongArch
  2024-01-26  6:29 ` [edk2-devel] [PATCH v8 12/37] UefiCpuPkg: Add CPU exception library for LoongArch Chao Li
@ 2024-02-02  3:30   ` Ni, Ray
  2024-02-02  3:44     ` Chao Li
  2024-03-08  8:02     ` Chao Li
  0 siblings, 2 replies; 89+ messages in thread
From: Ni, Ray @ 2024-02-02  3:30 UTC (permalink / raw)
  To: Chao Li, devel@edk2.groups.io
  Cc: Dong, Eric, Kumar, Rahul R, Gerd Hoffmann, Baoqi Zhang


1. Most of INF changes are not necessary for x86. Can you avoid them?

> +  ## This PCD Contains the pointer to a CPU exception vector base address.
> +  # @Prompt The pointer to a CPU exception vector base address.
> +
> gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress|0x0|UINT
> 64|0x60000022
> +

2. I do not see any reference to the above PCD in source/INF files.
Any mistake here?



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115009): https://edk2.groups.io/g/devel/message/115009
Mute This Topic: https://groups.io/mt/103971651/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-02-01  7:57     ` Chao Li
  2024-02-01 22:46       ` Laszlo Ersek
@ 2024-02-02  3:33       ` Ni, Ray
  2024-02-02  3:50         ` Chao Li
  1 sibling, 1 reply; 89+ messages in thread
From: Ni, Ray @ 2024-02-02  3:33 UTC (permalink / raw)
  To: Chao Li, devel@edk2.groups.io, lersek@redhat.com
  Cc: Dong, Eric, Kumar, Rahul R, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

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

Chao,
ConfigureMemoryManagementUnit() is not a library API anymore.
Who will call it? I don’t see any reference to this function inside the lib source.

Thanks,
Ray
From: Chao Li <lichao@loongson.cn>
Sent: Thursday, February 1, 2024 3:58 PM
To: devel@edk2.groups.io; lersek@redhat.com
Cc: Dong, Eric <eric.dong@intel.com>; Ni, Ray <ray.ni@intel.com>; Kumar, Rahul R <rahul.r.kumar@intel.com>; Gerd Hoffmann <kraxel@redhat.com>; Baoqi Zhang <zhangbaoqi@loongson.cn>; Dongyan Qian <qiandongyan@loongson.cn>; Xianglai Li <lixianglai@loongson.cn>; Bibo Mao <maobibo@loongson.cn>
Subject: Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg


Hi Lazslo,

Thanks,
Chao
On 2024/1/31 17:47, Laszlo Ersek wrote:

On 1/26/24 07:29, Chao Li wrote:

Add a new library named CpuMmuLib and add a LoongArch64 instance with in

the library.

It provides two-stage MMU libraryinstances, PEI and DXE.



BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584



Cc: Eric Dong <eric.dong@intel.com><mailto:eric.dong@intel.com>

Cc: Ray Ni <ray.ni@intel.com><mailto:ray.ni@intel.com>

Cc: Laszlo Ersek <lersek@redhat.com><mailto:lersek@redhat.com>

Cc: Rahul Kumar <rahul1.kumar@intel.com><mailto:rahul1.kumar@intel.com>

Cc: Gerd Hoffmann <kraxel@redhat.com><mailto:kraxel@redhat.com>

Signed-off-by: Chao Li <lichao@loongson.cn><mailto:lichao@loongson.cn>

Co-authored-by: Baoqi Zhang <zhangbaoqi@loongson.cn><mailto:zhangbaoqi@loongson.cn>

Co-authored-by: Dongyan Qian <qiandongyan@loongson.cn><mailto:qiandongyan@loongson.cn>

Co-authored-by: Xianglai Li <lixianglai@loongson.cn><mailto:lixianglai@loongson.cn>

Co-authored-by: Bibo Mao <maobibo@loongson.cn><mailto:maobibo@loongson.cn>

---

 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf |  36 +

 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni |  14 +

 .../CpuMmuLib/LoongArch64/CommonMmuLib.c      | 988 ++++++++++++++++++

 .../CpuMmuLib/LoongArch64/CommonMmuLib.h      |  43 +

 .../Library/CpuMmuLib/LoongArch64/Page.h      | 279 +++++

 .../CpuMmuLib/LoongArch64/PeiCpuMmuLib.c      | 178 ++++

 .../Library/CpuMmuLib/LoongArch64/Tlb.h       |  48 +

 .../CpuMmuLib/LoongArch64/TlbOperation.S      |  44 +

 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf |  44 +

 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni |  14 +

 UefiCpuPkg/UefiCpuPkg.dsc                     |   4 +

 11 files changed, 1692 insertions(+)

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni



diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf

new file mode 100644

index 0000000000..bfce3ce96d

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf

@@ -0,0 +1,36 @@

+## @file

+#  CPU Memory Map Unit DXE phase driver.

+#

+#  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                      = DxeCpuMmuLib

+  MODULE_UNI_FILE                = DxeCpuMmuLib.uni

+  FILE_GUID                      = DA8F0232-FB14-42F0-922C-63104D2C70BE



(1) This FILE_GUID was created from the FILE_GUID of

"ArmPkg/Library/ArmMmuLib/ArmMmuBaseLib.inf" by adding 1. That's not an

acceptable method for GUID generation.



A method is only acceptable for GUID generation if multiple (= an

unlimited number of) parties can execute the method at any time, and the

output remains conflict-free.



Taking an existent (known) FILE_GUID and incrementing it by 1 is not

such a method.



Please regenerate the GUID with "uuidgen".



Please also review the rest of your new GUIDs over this series (not only

FILE_GUIDs in INF files, but any other GUIDs, too).
OK, I will regenerate the GUID in next commit.








+  MODULE_TYPE                    = DXE_DRIVER

+  VERSION_STRING                 = 1.0

+  LIBRARY_CLASS                  = CpuMmuLib | DXE_DRIVER

+  CONSTRUCTOR                    = MmuInitialize

+

+#

+#  VALID_ARCHITECTURES           = LOONGARCH64

+#

+

+[Sources.LoongArch64]

+  LoongArch64/TlbOperation.S   | GCC

+  LoongArch64/CommonMmuLib.c

+  LoongArch64/Page.h

+  LoongArch64/Tlb.h

+

+[Packages]

+  MdePkg/MdePkg.dec

+  UefiCpuPkg/UefiCpuPkg.dec

+

+[LibraryClasses]

+  DebugLib

+  MemoryAllocationLib

diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni

new file mode 100644

index 0000000000..7342249516

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni

@@ -0,0 +1,14 @@

+// /** @file

+// CPU Memory Manager Unit library instance for DXE modules.

+//

+// CPU Memory Manager Unit library instance for DXE modules.

+//

+// 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 "CPU Memory Manager Unit library instance for DXE modules."

+

+#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for DXE modules."

diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c

new file mode 100644

index 0000000000..2e852c3371

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c

@@ -0,0 +1,988 @@

+/** @file

+

+  CPU Memory Map Unit Handler Library common functions.

+

+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>

+

+  SPDX-License-Identifier: BSD-2-Clause-Patent

+

+  @par Glossary:

+    - Pgd or Pgd or PGD    - Page Global Directory

+    - Pud or Pud or PUD    - Page Upper Directory

+    - Pmd or Pmd or PMD    - Page Middle Directory

+    - Pte or pte or PTE    - Page Table Entry

+    - Val or VAL or val    - Value

+    - Dir    - Directory

+**/

+#include <Uefi.h>

+#include <Library/BaseLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/CpuMmuLib.h>

+#include <Library/DebugLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Register/LoongArch64/Csr.h>

+#include "Tlb.h"

+#include "Page.h"

+

+#define SWAP_PAGE_DIR              CsrRead(LOONGARCH_CSR_PGDL)



(2) Missing space after "CsrRead".
OK.






+#define EFI_MEMORY_CACHETYPE_MASK  (EFI_MEMORY_UC  | \

+                                    EFI_MEMORY_WC  | \

+                                    EFI_MEMORY_WT  | \

+                                    EFI_MEMORY_WB  | \

+                                    EFI_MEMORY_UCE   \

+                                    )



(3) This seems to come from "ArmPkg/Include/Library/ArmLib.h"; it's not

great regardless: we shouldn't use the EFI_ prefix for symbols that are

not standard. (Put differently, EFI_ is a reserved namespace prefix for

the UEFI and PI specs.)
OK, I will rename with out EFI_perfix next time.






+

+BOOLEAN  mMmuInited = FALSE;



(4) This should be STATIC, I believe.



(5) So this is the point where I realize that the library design makes

no sense to me.



Normally you create SEC/PEI vs. DXE phase instances of a library

because, using writable global variables, you can gain performance (you

can remember the initialization) in DXE, but in SEC/PEI, you don't have

writeable global variables.



This pattern does not seem to apply here, or at least it doesn't seem to

work. Here's why:



- the variable mMmuInited and the function MmuInitialize (which contains

an assignment to the variable) are in "CommonMmuLib.c", which gets built

into both library instances. This makes no sense; that assignment cannot

work in SEC/PEI (I presume you're not going to have writeable globals --

executing from flash).



- I think you may be trying to make up for that problem by checking

SWAP_PAGE_DIR (the LOONGARCH_CSR_PGDL register) in MmuInitialize() and

MmuIsInit(). That doesn't seem right.

Yes, you are right, the PEI stage may be executed from flash, in this case, we have no way to write global variables, so we can only check whether CSR_PGDL is NULL in the DXE stage to get whether the MMU has been initialized.





- The PEI instance of the library contains an EFIAPI function called

ConfigureMemoryManagementUnit(). This function is never called in this

patch, which makes me think it's supposed to be called from driver or

application code (i.e., not from within the library itself, but from

client code). However, ConfigureMemoryManagementUnit() is also not

declared in the previous patch (in

"UefiCpuPkg/Include/Library/CpuMmuLib.h"); therefore client code cannot

reach it at all -- so that function doesn't even belong in this library.

This API was discussed with Ray in the V3 patch 13. Actually, the CpuMmuLib.h include more APIs before, Ray believed that other APIs should not be open to users or some APIs can be done by the base APIs, and in my opinion, the ConfigureMemoryManagementUnit() is a private API, so in V4, it has been removed from CpuMmuLib.h.

So what do you think? the ConfigureMemoryManagementUnit should be added back?





- MmuInitialize() doesn't actually do anything, it just checks (via the

CSR) whether some other component has already initialized the MMU

(likely with ConfigureMemoryManagementUnit()). And the sole purpose of

MmuIsInit() appears to be to return from the public library APIs

SetMemoryRegionAttributes() and GetMemoryRegionAttributes() early, if

that "other" (unknown) component has not called

ConfigureMemoryManagementUnit() yet.



So, I don't understand what you are trying to do. I could explain how to

keep the initialization logic *differences* minimal between the SEC/PEI

and the DXE library instances, but I don't understand how / when the MMU

initialization is supposed to occur in the first place.

Let's me tell you this library how to work:

In PEI stage, in addition to ensuring that the MMU is not used, the user must call the ConfigureMemoryManagementUnit toinitialization the MMU, such as filling the static page tables, set the TLB refill exception entry point, set the page size etc. the ConfigureMemoryManagementUnit is a private API but related to ARCH.

During DXE stage, this library will provide services for CpuDxe and other drivers, but almost changes to memory page attributes use the protocols provided by CpuDxe. That is why the CpuMmuLib.h only contains two APIs, it only can get/set the attribute.

In short, the PEI stage needs to initialize and set up TLB refill entry point and method(dynamically populate the TLB, keep the PA == VA), and DXE stage is provides services for changing the memory attributes.





(6) The patch is too large in general. You should construct these

library instances over multiple patches. The first patch could add some

declarations / macro definitions, such as "Page.h" and "Tlb.h". Another

patch could add the assembly language helper functions. Another patch

could add the PEI phase library instance (including the common code). A

final patch could add the DXE-phase bits.



The idea is to proceed in layers, logically building one on top of the

other. It's fine if the library doesn't build initially; there is no

attempt to build it anyway until you actually reference the INF files in

some DSC files. So please split this at least in 4 patches.
OK, I will try it.






(7) The commit message should explain the expected usage model in detail.
OK.






Best regards,

Laszlo





+

+/**

+  Check to see if mmu successfully initializes.

+

+  @param  VOID.

+

+  @retval  TRUE  Initialization has been completed.

+           FALSE Initialization did not complete.

+**/

+STATIC

+BOOLEAN

+MmuIsInit (

+  VOID

+  )

+{

+  if (mMmuInited || (SWAP_PAGE_DIR != 0)) {

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+/**

+  Iterates through the page directory to initialize it.

+

+  @param  Dst  A pointer to the directory of the page to initialize.

+  @param  Num  The number of page directories to initialize.

+  @param  Src  A pointer to the data used to initialize the page directory.

+

+  @return VOID.

+**/

+STATIC

+VOID

+PageDirInit (

+  IN VOID   *Dst,

+  IN UINTN  Num,

+  IN VOID   *Src

+  )

+{

+  UINTN  *Ptr;

+  UINTN  *End;

+  UINTN  Entry;

+

+  Entry = (UINTN)Src;

+  Ptr   = (UINTN *)Dst;

+  End   = Ptr + Num;

+

+  for ( ; Ptr < End; Ptr++) {

+    *Ptr = Entry;

+  }

+

+  return;

+}

+

+/**

+  Gets the virtual address corresponding to the page global directory table entry.

+

+  @param  Address  the virtual address for the table entry.

+

+  @retval PGD A pointer to get the table item.

+**/

+STATIC

+PGD *

+PgdOffset (

+  IN UINTN  Address

+  )

+{

+  return (PGD *)(SWAP_PAGE_DIR) + PGD_INDEX (Address);

+}

+

+/**

+  Gets the virtual address corresponding to the page upper directory table entry.

+

+  @param  Pgd  A pointer to a page global directory table entry.

+  @param  Address  the virtual address for the table entry.

+

+  @retval PUD A pointer to get the table item.

+**/

+STATIC

+PUD *

+PudOffset (

+  IN PGD    *Pgd,

+  IN UINTN  Address

+  )

+{

+  UINTN  PgdVal;

+

+  PgdVal = (UINTN)PGD_VAL (*Pgd);

+

+  return (PUD *)PgdVal + PUD_INDEX (Address);

+}

+

+/**

+  Gets the virtual address corresponding to the page middle directory table entry.

+

+  @param  Pud  A pointer to a page upper directory table entry.

+  @param  Address  the virtual address for the table entry.

+

+  @retval PMD A pointer to get the table item.

+**/

+STATIC

+PMD *

+PmdOffset (

+  IN PUD    *Pud,

+  IN UINTN  Address

+  )

+{

+  UINTN  PudVal;

+

+  PudVal = PUD_VAL (*Pud);

+

+  return (PMD *)PudVal + PMD_INDEX (Address);

+}

+

+/**

+  Gets the virtual address corresponding to the page table entry.

+

+  @param  Pmd  A pointer to a page middle directory table entry.

+  @param  Address  the virtual address for the table entry.

+

+  @retval PTE A pointer to get the table item.

+**/

+STATIC

+PTE *

+PteOffset (

+  IN PMD    *Pmd,

+  IN UINTN  Address

+  )

+{

+  UINTN  PmdVal;

+

+  PmdVal = (UINTN)PMD_VAL (*Pmd);

+

+  return (PTE *)PmdVal + PTE_INDEX (Address);

+}

+

+/**

+  Sets the value of the page table entry.

+

+  @param  Pte  A pointer to a page table entry.

+  @param  PteVal  The value of the page table entry to set.

+

+**/

+STATIC

+VOID

+SetPte (

+  IN PTE  *Pte,

+  IN PTE  PteVal

+  )

+{

+  *Pte = PteVal;

+}

+

+/**

+  Sets the value of the page global directory.

+

+  @param  Pgd  A pointer to a page global directory.

+  @param  Pud  The value of the page global directory to set.

+

+**/

+STATIC

+VOID

+SetPgd (

+  IN PGD  *Pgd,

+  IN PUD  *Pud

+  )

+{

+  *Pgd = (PGD) {

+    ((UINTN)Pud)

+  };

+}

+

+/**

+  Sets the value of the page upper directory.

+

+  @param  Pud  A pointer to a page upper directory.

+  @param  Pmd  The value of the page upper directory to set.

+

+**/

+STATIC

+VOID

+SetPud (

+  IN PUD  *Pud,

+  IN PMD  *Pmd

+  )

+{

+  *Pud = (PUD) {

+    ((UINTN)Pmd)

+  };

+}

+

+/**

+  Sets the value of the page middle directory.

+

+  @param  Pmd  A pointer to a page middle directory.

+  @param  Pte  The value of the page middle directory to set.

+

+**/

+STATIC

+VOID

+SetPmd (

+  IN PMD  *Pmd,

+  IN PTE  *Pte

+  )

+{

+  *Pmd = (PMD) {

+    ((UINTN)Pte)

+  };

+}

+

+/**

+  Free up memory space occupied by page tables.

+

+  @param  Pte  A pointer to the page table.

+

+**/

+VOID

+PteFree (

+  IN PTE  *Pte

+  )

+{

+  FreePages ((VOID *)Pte, 1);

+}

+

+/**

+  Free up memory space occupied by page middle directory.

+

+  @param  Pmd  A pointer to the page middle directory.

+

+**/

+VOID

+PmdFree (

+  IN PMD  *Pmd

+  )

+{

+  FreePages ((VOID *)Pmd, 1);

+}

+

+/**

+  Free up memory space occupied by page upper directory.

+

+  @param  Pud  A pointer to the page upper directory.

+

+**/

+VOID

+PudFree (

+  IN PUD  *Pud

+  )

+{

+  FreePages ((VOID *)Pud, 1);

+}

+

+/**

+  Requests the memory space required for the page upper directory,

+  initializes it, and places it in the specified page global directory

+

+  @param  Pgd  A pointer to the page global directory.

+

+  @retval  EFI_SUCCESS  Memory request successful.

+  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.

+**/

+STATIC

+EFI_STATUS

+PudAlloc (

+  IN PGD  *Pgd

+  )

+{

+  PUD  *Pud;

+

+  Pud = (PUD *)AllocatePages (1);

+  if (Pud == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  PageDirInit ((VOID *)Pud, ENTRYS_PER_PUD, (VOID *)INVALID_PAGE);

+

+  SetPgd (Pgd, Pud);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Requests the memory space required for the page middle directory,

+  initializes it, and places it in the specified page upper directory

+

+  @param  Pud  A pointer to the page upper directory.

+

+  @retval  EFI_SUCCESS  Memory request successful.

+  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.

+**/

+STATIC

+EFI_STATUS

+PmdAlloc (

+  IN PUD  *Pud

+  )

+{

+  PMD  *Pmd;

+

+  Pmd = (PMD *)AllocatePages (1);

+  if (!Pmd) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  PageDirInit ((VOID *)Pmd, ENTRYS_PER_PMD, (VOID *)INVALID_PAGE);

+

+  SetPud (Pud, Pmd);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Requests the memory space required for the page table,

+  initializes it, and places it in the specified page middle directory

+

+  @param  Pmd  A pointer to the page middle directory.

+

+  @retval  EFI_SUCCESS  Memory request successful.

+  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.

+**/

+STATIC

+EFI_STATUS

+PteAlloc (

+  IN PMD  *Pmd

+  )

+{

+  PTE  *Pte;

+

+  Pte = (PTE *)AllocatePages (1);

+  if (!Pte) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Pte = ZeroMem (Pte, EFI_PAGE_SIZE);

+

+  SetPmd (Pmd, Pte);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Requests the memory space required for the page upper directory,

+  initializes it, and places it in the specified page global directory,

+  and get the page upper directory entry corresponding to the virtual address.

+

+  @param  Pgd      A pointer to the page global directory.

+  @param  Address  The corresponding virtual address of the page table entry.

+

+  @retval          A pointer to the page upper directory entry. Return NULL, if

+                   allocate the memory buffer is fail.

+**/

+STATIC

+PUD *

+PudAllocGet (

+  IN PGD    *Pgd,

+  IN UINTN  Address

+  )

+{

+  EFI_STATUS  Status;

+

+  if (PGD_IS_EMPTY (*Pgd)) {

+    Status = PudAlloc (Pgd);

+    ASSERT_EFI_ERROR (Status);

+    if (EFI_ERROR (Status)) {

+      return NULL;

+    }

+  }

+

+  return PudOffset (Pgd, Address);

+}

+

+/**

+  Requests the memory space required for the page middle directory,

+  initializes it, and places it in the specified page upper directory,

+  and get the page middle directory entry corresponding to the virtual address.

+

+  @param  Pud      A pointer to the page upper directory.

+  @param  Address  The corresponding virtual address of the page table entry.

+

+  @retval          A pointer to the page middle directory entry. Return NULL, if

+                   allocate the memory buffer is fail.

+**/

+STATIC

+PMD *

+PmdAllocGet (

+  IN PUD    *Pud,

+  IN UINTN  Address

+  )

+{

+  EFI_STATUS  Status;

+

+  if (PUD_IS_EMPTY (*Pud)) {

+    Status = PmdAlloc (Pud);

+    ASSERT_EFI_ERROR (Status);

+    if (EFI_ERROR (Status)) {

+      return NULL;

+    }

+  }

+

+  return PmdOffset (Pud, Address);

+}

+

+/**

+  Requests the memory space required for the page table,

+  initializes it, and places it in the specified page middle directory,

+  and get the page table entry corresponding to the virtual address.

+

+  @param  Pmd      A pointer to the page upper directory.

+  @param  Address  The corresponding virtual address of the page table entry.

+

+  @retval          A pointer to the page table entry. Return NULL, if allocate

+                   the memory buffer is fail.

+**/

+STATIC

+PTE *

+PteAllocGet (

+  IN PMD    *Pmd,

+  IN UINTN  Address

+  )

+{

+  EFI_STATUS  Status;

+

+  if (PMD_IS_EMPTY (*Pmd)) {

+    Status = PteAlloc (Pmd);

+    ASSERT_EFI_ERROR (Status);

+    if (EFI_ERROR (Status)) {

+      return NULL;

+    }

+  }

+

+  return PteOffset (Pmd, Address);

+}

+

+/**

+  Gets the physical address of the page table entry corresponding to the specified virtual address.

+

+  @param  Address  The corresponding virtual address of the page table entry.

+

+  @retval  A pointer to the page table entry.

+  @retval  NULL

+**/

+STATIC

+PTE *

+GetPteAddress (

+  IN UINTN  Address

+  )

+{

+  PGD  *Pgd;

+  PUD  *Pud;

+  PMD  *Pmd;

+

+  Pgd = PgdOffset (Address);

+

+  if (PGD_IS_EMPTY (*Pgd)) {

+    return NULL;

+  }

+

+  Pud = PudOffset (Pgd, Address);

+

+  if (PUD_IS_EMPTY (*Pud)) {

+    return NULL;

+  }

+

+  Pmd = PmdOffset (Pud, Address);

+  if (PMD_IS_EMPTY (*Pmd)) {

+    return NULL;

+  }

+

+  if (IS_HUGE_PAGE (Pmd->PmdVal)) {

+    return ((PTE *)Pmd);

+  }

+

+  return PteOffset (Pmd, Address);

+}

+

+/**

+  Gets the Attributes of Huge Page.

+

+  @param  Pmd  A pointer to the page middle directory.

+

+  @retval     Value of Attributes.

+**/

+STATIC

+UINTN

+GetHugePageAttributes (

+  IN  PMD  *Pmd

+  )

+{

+  UINTN  Attributes;

+  UINTN  GlobalFlag;

+  UINTN  HugeVal;

+

+  HugeVal     = PMD_VAL (*Pmd);

+  Attributes  = HugeVal & (~HUGEP_PAGE_MASK);

+  GlobalFlag  = ((Attributes & (1 << PAGE_HGLOBAL_SHIFT)) >> PAGE_HGLOBAL_SHIFT) << PAGE_GLOBAL_SHIFT;

+  Attributes &= ~(1 << PAGE_HGLOBAL_SHIFT);

+  Attributes |= GlobalFlag;

+  return Attributes;

+}

+

+/**

+  Establishes a page table entry based on the specified memory region.

+

+  @param  Pmd  A pointer to the page middle directory.

+  @param  Address  The memory space start address.

+  @param  End  The end address of the memory space.

+  @param  Attributes  Memory space Attributes.

+

+  @retval     EFI_SUCCESS   The page table entry was created successfully.

+  @retval     EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.

+**/

+STATIC

+EFI_STATUS

+MemoryMapPteRange (

+  IN PMD    *Pmd,

+  IN UINTN  Address,

+  IN UINTN  End,

+  IN UINTN  Attributes

+  )

+{

+  PTE      *Pte;

+  PTE      PteVal;

+  BOOLEAN  UpDate;

+

+  Pte = PteAllocGet (Pmd, Address);

+  if (!Pte) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  DEBUG ((

+    DEBUG_INFO,

+    "%a %d Address %p End %p  Attributes %llx\n",

+    __func__,

+    __LINE__,

+    Address,

+    End,

+    Attributes

+    ));

+

+  do {

+    UpDate = FALSE;

+    PteVal = MAKE_PTE (Address, Attributes);

+

+    if ((!PTE_IS_EMPTY (*Pte)) &&

+        (PTE_VAL (*Pte) != PTE_VAL (PteVal)))

+    {

+      UpDate = TRUE;

+    }

+

+    SetPte (Pte, PteVal);

+    if (UpDate) {

+      InvalidTlb (Address);

+    }

+  } while (Pte++, Address += EFI_PAGE_SIZE, Address != End);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Convert Huge Page to Page.

+

+  @param  Pmd  A pointer to the page middle directory.

+  @param  Address  The memory space start address.

+  @param  End  The end address of the memory space.

+  @param  Attributes  Memory space Attributes.

+

+  @retval  EFI_SUCCESS   The page table entry was created successfully.

+  @retval  EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.

+**/

+STATIC

+EFI_STATUS

+ConvertHugePageToPage (

+  IN  PMD   *Pmd,

+  IN UINTN  Address,

+  IN UINTN  End,

+  IN UINTN  Attributes

+  )

+{

+  UINTN       OldAttributes;

+  UINTN       HugePageEnd;

+  UINTN       HugePageStart;

+  EFI_STATUS  Status;

+

+  Status = EFI_SUCCESS;

+

+  if ((PMD_IS_EMPTY (*Pmd)) ||

+      (!IS_HUGE_PAGE (Pmd->PmdVal)))

+  {

+    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);

+  } else {

+    OldAttributes = GetHugePageAttributes (Pmd);

+    if (Attributes == OldAttributes) {

+      return Status;

+    }

+

+    SetPmd (Pmd, (PTE *)(INVALID_PAGE));

+    HugePageStart = Address & PMD_MASK;

+    HugePageEnd   = HugePageStart + HUGE_PAGE_SIZE;

+    ASSERT (HugePageEnd >= End);

+

+    if (Address > HugePageStart) {

+      Status |= MemoryMapPteRange (Pmd, HugePageStart, Address, OldAttributes);

+    }

+

+    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);

+

+    if (End < HugePageEnd) {

+      Status |= MemoryMapPteRange (Pmd, End, HugePageEnd, OldAttributes);

+    }

+  }

+

+  return Status;

+}

+

+/**

+  Establishes a page middle directory based on the specified memory region.

+

+  @param  Pud  A pointer to the page upper directory.

+  @param  Address  The memory space start address.

+  @param  End  The end address of the memory space.

+  @param  Attributes  Memory space Attributes.

+

+  @retval     EFI_SUCCESS   The page middle directory was created successfully.

+  @retval     EFI_OUT_OF_RESOURCES  Page middle directory establishment failed due to resource exhaustion.

+**/

+STATIC

+EFI_STATUS

+MemoryMapPmdRange (

+  IN PUD    *Pud,

+  IN UINTN  Address,

+  IN UINTN  End,

+  IN UINTN  Attributes

+  )

+{

+  PMD      *Pmd;

+  UINTN    Next;

+  PTE      PteVal;

+  BOOLEAN  UpDate;

+

+  Pmd = PmdAllocGet (Pud, Address);

+  if (Pmd == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  do {

+    Next = PMD_ADDRESS_END (Address, End);

+    if (((Address & (~PMD_MASK)) == 0) &&

+        ((Next &  (~PMD_MASK)) == 0) &&

+        (PMD_IS_EMPTY (*Pmd) || IS_HUGE_PAGE (Pmd->PmdVal)))

+    {

+      UpDate = FALSE;

+      PteVal = MAKE_HUGE_PTE (Address, Attributes);

+

+      if ((!PMD_IS_EMPTY (*Pmd)) &&

+          (PMD_VAL (*Pmd) != PTE_VAL (PteVal)))

+      {

+        UpDate = TRUE;

+      }

+

+      SetPmd (Pmd, (PTE *)PteVal.PteVal);

+      if (UpDate) {

+        InvalidTlb (Address);

+      }

+    } else {

+      ConvertHugePageToPage (Pmd, Address, Next, Attributes);

+    }

+  } while (Pmd++, Address = Next, Address != End);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Establishes a page upper directory based on the specified memory region.

+

+  @param  Pgd  A pointer to the page global directory.

+  @param  Address  The memory space start address.

+  @param  End  The end address of the memory space.

+  @param  Attributes  Memory space Attributes.

+

+  @retval     EFI_SUCCESS   The page upper directory was created successfully.

+  @retval     EFI_OUT_OF_RESOURCES  Page upper directory establishment failed due to resource exhaustion.

+**/

+STATIC

+EFI_STATUS

+MemoryMapPudRange (

+  IN PGD    *Pgd,

+  IN UINTN  Address,

+  IN UINTN  End,

+  IN UINTN  Attributes

+  )

+{

+  PUD    *Pud;

+  UINTN  Next;

+

+  Pud = PudAllocGet (Pgd, Address);

+  if (Pud == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  do {

+    Next = PUD_ADDRESS_END (Address, End);

+    if (EFI_ERROR (MemoryMapPmdRange (Pud, Address, Next, Attributes))) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+  } while (Pud++, Address = Next, Address != End);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Establishes a page global directory based on the specified memory region.

+

+  @param  Start  The memory space start address.

+  @param  End  The end address of the memory space.

+  @param  Attributes  Memory space Attributes.

+

+  @retval     EFI_SUCCESS   The page global directory was created successfully.

+  @retval     EFI_OUT_OF_RESOURCES  Page global directory establishment failed due to resource exhaustion.

+**/

+STATIC

+EFI_STATUS

+MemoryMapPageRange (

+  IN UINTN  Start,

+  IN UINTN  End,

+  IN UINTN  Attributes

+  )

+{

+  PGD         *Pgd;

+  UINTN       Next;

+  UINTN       Address;

+  EFI_STATUS  Err;

+

+  Address = Start;

+

+  /* Get PGD(PTE PMD PUD PGD) in PageTables */

+  Pgd = PgdOffset (Address);

+  do {

+    Next = PGD_ADDRESS_END (Address, End);

+    /* Get Next Align Page to Map */

+    Err = MemoryMapPudRange (Pgd, Address, Next, Attributes);

+    if (Err) {

+      return Err;

+    }

+  } while (Pgd++, Address = Next, Address != End);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Page tables are established from memory-mapped tables.

+

+  @param  MemoryRegion   A pointer to a memory-mapped table entry.

+

+  @retval     EFI_SUCCESS   The page table was created successfully.

+  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.

+**/

+EFI_STATUS

+FillTranslationTable (

+  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion

+  )

+{

+  return MemoryMapPageRange (

+           MemoryRegion->VirtualBase,

+           (MemoryRegion->Length + MemoryRegion->VirtualBase),

+           MemoryRegion->Attributes

+           );

+}

+

+/**

+  Convert EFI Attributes to Loongarch Attributes.

+

+  @param[in]  EfiAttributes     Efi Attributes.

+

+  @retval  Corresponding architecture attributes.

+**/

+UINTN

+EFIAPI

+EfiAttributeConverse (

+  IN UINTN  EfiAttributes

+  )

+{

+  UINTN  LoongArchAttributes;

+

+  LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | PLV_KERNEL | PAGE_GLOBAL;

+

+  switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {

+    case EFI_MEMORY_UC:

+      LoongArchAttributes |= CACHE_SUC;

+      break;

+    case EFI_MEMORY_WC:

+      LoongArchAttributes |= CACHE_WUC;

+      break;

+    case EFI_MEMORY_WT:

+    case EFI_MEMORY_WB:

+      LoongArchAttributes |= CACHE_CC;

+      break;

+    default:

+      LoongArchAttributes |= CACHE_CC;

+      break;

+  }

+

+  // Write protection attributes

+  if (((EfiAttributes & EFI_MEMORY_RO) != 0) ||

+      ((EfiAttributes & EFI_MEMORY_WP) != 0))

+  {

+    LoongArchAttributes &= ~PAGE_DIRTY;

+  }

+

+  if ((EfiAttributes & EFI_MEMORY_RP) != 0) {

+    LoongArchAttributes |= PAGE_NO_READ;

+  }

+

+  // eXecute protection attribute

+  if ((EfiAttributes & EFI_MEMORY_XP) != 0) {

+    LoongArchAttributes |= PAGE_NO_EXEC;

+  }

+

+  return LoongArchAttributes;

+}

+

+/**

+  Finds the first of the length and memory properties of the memory region corresponding

+  to the specified base address.

+

+  @param[in]       BaseAddress       To find the base address of the memory region.

+  @param[in, out]  RegionLength      Pointer holding:

+                                      - At entry, the length of the memory region

+                                        expected to be found.

+                                      - At exit, the length of the memory region found.

+  @param[out]      RegionAttributes  Properties of the memory region found.

+

+  @retval  EFI_SUCCESS           The corresponding memory area was successfully found

+           EFI_NOT_FOUND         No memory area found

+           EFI_OUT_OF_RESOURCES  Base address or expected memory region exceeds the maximum

+                                 address.

+**/

+EFI_STATUS

+EFIAPI

+GetMemoryRegionAttributes (

+  IN     UINTN  BaseAddress,

+  IN OUT UINTN  *RegionLength,

+  OUT    UINTN  *RegionAttributes

+  )

+{

+  PTE    *Pte;

+  UINTN  Attributes;

+  UINTN  AttributesTmp;

+  UINTN  MaxAddress;

+  UINTN  EndAddress;

+  UINTN  AddSize;

+

+  if (!MmuIsInit ()) {

+    return EFI_UNSUPPORTED;

+  }

+

+  EndAddress = BaseAddress + *RegionLength;

+  MaxAddress = LShiftU64 (1ULL, MAX_VA_BITS) - 1;

+

+  // Clean the value to prepare output to find region size.

+  *RegionLength = 0x0;

+

+  if ((BaseAddress >= MaxAddress) || (EndAddress >= MaxAddress)) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Pte = GetPteAddress (BaseAddress);

+

+  if (Pte == NULL) {

+    return EFI_NOT_FOUND;

+  }

+

+  Attributes = GET_PAGE_ATTRIBUTES (*Pte);

+  if (IS_HUGE_PAGE (Pte->PteVal)) {

+    *RegionAttributes = Attributes & (~(PAGE_HUGE));

+  } else {

+    *RegionAttributes = Attributes;

+  }

+

+  do {

+    Pte = GetPteAddress (BaseAddress);

+    if (Pte == NULL) {

+      return EFI_SUCCESS;

+    }

+

+    AttributesTmp = GET_PAGE_ATTRIBUTES (*Pte);

+    if (AttributesTmp == Attributes) {

+      if (IS_HUGE_PAGE (Pte->PteVal)) {

+        AddSize = HUGE_PAGE_SIZE;

+      } else {

+        AddSize = EFI_PAGE_SIZE;

+      }

+

+      *RegionLength += AddSize;

+      BaseAddress   += AddSize;

+    } else {

+      return EFI_SUCCESS;

+    }

+  } while (BaseAddress <= EndAddress);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Sets the Attributes  of the specified memory region

+

+  @param[in]  BaseAddress    The base address of the memory region to set the Attributes.

+  @param[in]  Length         The length of the memory region to set the Attributes.

+  @param[in]  Attributes     The Attributes to be set.

+  @param[in]  AttributeMask  Mask of memory attributes to take into account.

+

+  @retval  EFI_SUCCESS    The Attributes was set successfully

+**/

+EFI_STATUS

+EFIAPI

+SetMemoryRegionAttributes (

+  IN EFI_PHYSICAL_ADDRESS  BaseAddress,

+  IN UINTN                 Length,

+  IN UINTN                 Attributes,

+  IN UINT64                AttributeMask

+  )

+{

+  EFI_STATUS  Status;

+

+  if (!MmuIsInit ()) {

+    return EFI_UNSUPPORTED;

+  }

+

+  Attributes = EfiAttributeConverse (Attributes);

+  Status     = MemoryMapPageRange (BaseAddress, BaseAddress + Length, Attributes);

+  ASSERT_EFI_ERROR (Status);

+

+  return Status;

+}

+

+/**

+  Check to see if mmu successfully initializes and saves the result.

+

+  @param[in]  ImageHandle  The firmware allocated handle for the EFI image.

+  @param[in]  SystemTable  A pointer to the EFI System Table.

+

+  @retval  RETURN_SUCCESS    Initialization succeeded.

+**/

+RETURN_STATUS

+MmuInitialize (

+  IN EFI_HANDLE        ImageHandle,

+  IN EFI_SYSTEM_TABLE  *SystemTable

+  )

+{

+  if (SWAP_PAGE_DIR != 0) {

+    mMmuInited = TRUE;

+  }

+

+  return RETURN_SUCCESS;

+}

diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h

new file mode 100644

index 0000000000..d8c922c8fa

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h

@@ -0,0 +1,43 @@

+/** @file

+

+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>

+

+  SPDX-License-Identifier: BSD-2-Clause-Patent

+

+  @par Glossary:

+    - Dir    - Directory

+**/

+

+#ifndef  MMU_LIB_CORE_H_

+#define  MMU_LIB_CORE_H_

+

+/**

+  Iterates through the page directory to initialize it.

+

+  @param  Dst  A pointer to the directory of the page to initialize.

+  @param  Num  The number of page directories to initialize.

+  @param  Src  A pointer to the data used to initialize the page directory.

+

+  @retval VOID.

+**/

+VOID

+PageDirInit (

+  IN VOID   *dest,

+  IN UINTN  Count,

+  IN VOID   *src

+  );

+

+/**

+  Page tables are established from memory-mapped tables.

+

+  @param  MemoryRegion   A pointer to a memory-mapped table entry.

+

+  @retval     EFI_SUCCESS   The page table was created successfully.

+  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.

+**/

+EFI_STATUS

+FillTranslationTable (

+  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion

+  );

+

+#endif // MMU_LIB_CORE_H_

diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h

new file mode 100644

index 0000000000..bac4f52327

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h

@@ -0,0 +1,279 @@

+/** @file

+

+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>

+

+  SPDX-License-Identifier: BSD-2-Clause-Patent

+

+  @par Glossary:

+    - Pgd or Pgd or PGD    - Page Global Directory

+    - Pud or Pud or PUD    - Page Upper Directory

+    - Pmd or Pmd or PMD    - Page Middle Directory

+    - Pte or pte or PTE    - Page Table Entry

+    - Val or VAL or val    - Value

+    - Dir    - Directory

+**/

+

+#ifndef PAGE_H_

+#define PAGE_H_

+

+#include <Library/CpuMmuLib.h>

+

+#define MAX_VA_BITS  47

+#define PGD_WIDE     (8)

+#define PUD_WIDE     (9)

+#define PMD_WIDE     (9)

+#define PTE_WIDE     (9)

+

+#define ENTRYS_PER_PGD  (1 << PGD_WIDE)

+#define ENTRYS_PER_PUD  (1 << PUD_WIDE)

+#define ENTRYS_PER_PMD  (1 << PMD_WIDE)

+#define ENTRYS_PER_PTE  (1 << PTE_WIDE)

+

+#define PGD_SHIFT  (PUD_SHIFT + PUD_WIDE)

+#define PUD_SHIFT  (PMD_SHIFT + PMD_WIDE)

+#define PMD_SHIFT  (EFI_PAGE_SHIFT + PTE_WIDE)

+#define PTE_SHIFT  (EFI_PAGE_SHIFT)

+

+#define PGD_SIZE  (1UL << PGD_SHIFT)

+#define PUD_SIZE  (1UL << PUD_SHIFT)

+#define PMD_SIZE  (1UL << PMD_SHIFT)

+

+#define PGD_MASK   (~(PGD_SIZE-1))

+#define PUD_MASK   (~(PUD_SIZE-1))

+#define PMD_MASK   (~(PMD_SIZE-1))

+#define PAGE_MASK  (~(EFI_PAGE_SIZE - 1))

+#define PFN_MASK   (~(((UINTN)(1) << (EFI_PAGE_SHIFT)) - 1) & \

+                   (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))

+

+#define HUGEP_PAGE_MASK  (~(((UINTN)(1) << (PMD_SHIFT)) - 1) & \

+                          (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))

+

+#define INVALID_PAGE  0

+

+typedef struct {

+  UINTN    PgdVal;

+} PGD;

+typedef struct {

+  UINTN    PudVal;

+} PUD;

+typedef struct {

+  UINTN    PmdVal;

+} PMD;

+typedef struct {

+  UINTN    PteVal;

+} PTE;

+

+/**

+  Gets the value of the page global directory table entry.

+

+  @param  x    Page global directory struct variables.

+

+  @retval   the value of the page global directory table entry.

+ **/

+#define PGD_VAL(x)  ((x).PgdVal)

+

+/**

+  Gets the value of the page upper directory table entry.

+

+  @param  x    Page upper directory struct variables.

+

+  @retval  the value of the page upper directory table entry.

+ **/

+#define PUD_VAL(x)  ((x).PudVal)

+

+/**

+  Gets the value of the page middle directory table entry.

+

+  @param  x    Page middle directory struct variables.

+

+  @retval  the value of the page middle directory table entry.

+ **/

+#define PMD_VAL(x)  ((x).PmdVal)

+

+/**

+  Gets the value of the page table entry.

+

+  @param  x    Page table entry struct variables.

+

+  @retval  the value of the page table entry.

+ **/

+#define PTE_VAL(x)  ((x).PteVal)

+

+#define PGD_TABLE_SIZE  (ENTRYS_PER_PGD * sizeof(PGD))

+#define PUD_TABLE_SIZE  (ENTRYS_PER_PUD * sizeof(PUD))

+#define PMD_TABLE_SIZE  (ENTRYS_PER_PMD * sizeof(PMD))

+#define PTE_TABLE_SIZE  (ENTRYS_PER_PTE * sizeof(PTE))

+

+/**

+  Gets the physical address of the record in the page table entry.

+

+  @param  x    Page table entry struct variables.

+

+  @retval  the value of the physical address.

+ **/

+#define GET_PAGE_ATTRIBUTES(x)  (UINTN) {(PTE_VAL(x) & ~PFN_MASK)}

+

+/**

+  Gets the virtual address of the next block of the specified virtual address

+  that is aligned with the size of the global page directory mapping.

+

+  @param  Address  Specifies the virtual address.

+  @param  End    The end address of the memory region.

+

+  @retval   the specified virtual address  of the next block.

+ **/

+#define PGD_ADDRESS_END(Address, End)                  \

+({                                                     \

+  UINTN Boundary = ((Address) + PGD_SIZE) & PGD_MASK;  \

+  (Boundary - 1 < (End) - 1)? Boundary: (End);         \

+})

+

+/**

+  Gets the virtual address of the next block of the specified virtual address

+  that is aligned with the size of the page upper directory mapping.

+

+  @param  Address  Specifies the virtual address.

+  @param  End    The end address of the memory region.

+

+  @retval   the specified virtual address  of the next block.

+ **/

+#define PUD_ADDRESS_END(Address, End)                  \

+({                                                     \

+  UINTN Boundary = ((Address) + PUD_SIZE) & PUD_MASK;  \

+  (Boundary - 1 < (End) - 1)? Boundary: (End);         \

+})

+

+/**

+  Gets the virtual address of the next block of the specified virtual address

+  that is aligned with the size of the page middle directory mapping.

+

+  @param  Address  Specifies the virtual address.

+  @param  End    The end address of the memory region.

+

+  @retval   the specified virtual address  of the next block.

+ **/

+#define PMD_ADDRESS_END(Address, End)                  \

+({                                                     \

+  UINTN Boundary = ((Address) + PMD_SIZE) & PMD_MASK;  \

+  (Boundary - 1 < (End) - 1)? Boundary: (End);         \

+})

+

+/**

+  Get Specifies the virtual address corresponding to the index of the page global directory table entry.

+

+  @param  Address  Specifies the virtual address.

+

+  @retval   the index of the page global directory table entry.

+ **/

+#define PGD_INDEX(Address)  (((Address) >> PGD_SHIFT) & (ENTRYS_PER_PGD-1))

+

+/**

+  Get Specifies the virtual address corresponding to the index of the page upper directory table entry.

+

+  @param  Address  Specifies the virtual address.

+  @param  End    The end address of the memory region.

+

+  @retval   the index of the page upper directory table entry.

+ **/

+#define PUD_INDEX(Address)  (((Address) >> PUD_SHIFT) & (ENTRYS_PER_PUD - 1))

+

+/**

+  Get Specifies the virtual address corresponding to the index of the page middle directory table entry.

+

+  @param  Address  Specifies the virtual address.

+

+  @retval   the index of the page middle directory table entry.

+ **/

+#define PMD_INDEX(Address)  (((Address) >> PMD_SHIFT) & (ENTRYS_PER_PMD - 1))

+

+/**

+  Get Specifies the virtual address corresponding to the index of the page table entry.

+

+  @param  Address  Specifies the virtual address.

+

+  @retval   the index of the page table entry.

+ **/

+#define PTE_INDEX(Address)  (((Address) >> EFI_PAGE_SHIFT) & (ENTRYS_PER_PTE - 1))

+

+/**

+  Calculates the value of the page table entry based on the specified virtual address and properties.

+

+  @param  Address  Specifies the virtual address.

+  @param  Attributes  Specifies the Attributes.

+

+  @retval    the value of the page table entry.

+ **/

+#define MAKE_PTE(Address, Attributes)  (PTE){((((Address) >> EFI_PAGE_SHIFT) << 12) | (Attributes))}

+

+/**

+  Get Global bit from Attributes

+

+  @param  Attributes  Specifies the Attributes.

+ * */

+#define GET_GLOBALBIT(Attributes)  ((Attributes & PAGE_GLOBAL) >> PAGE_GLOBAL_SHIFT)

+

+/**

+  Calculates the value of the Huge page table entry based on the specified virtual address and properties.

+

+  @param  Address  Specifies the virtual address.

+  @param  Attributes  Specifies the Attributes.

+

+  @retval    the value of the HUGE page table entry.

+ **/

+#define MAKE_HUGE_PTE(Address, Attributes)  (PTE){(((((Address) >> PMD_SHIFT) << PMD_SHIFT) | \

+                                             ((Attributes) | (GET_GLOBALBIT(Attributes) << PAGE_HGLOBAL_SHIFT) | \

+                                             PAGE_HUGE)))}

+

+/**

+  Check whether the large page table entry is.

+

+  @param  Val The value of the page table entry.

+

+  @retval    1   Is huge page table entry.

+  @retval    0   Isn't huge page table entry.

+**/

+#define IS_HUGE_PAGE(Val)  ((((Val) & PAGE_HUGE) == PAGE_HUGE) && \

+                            (((Val) & PAGE_HGLOBAL) == PAGE_HGLOBAL))

+

+#define HUGE_PAGE_SIZE  (PMD_SIZE)

+

+/**

+  Check that the global page directory table entry is empty.

+

+  @param  pgd   the global page directory struct variables.

+

+  @retval    1   The page table is invalid.

+  @retval    0   The page table is valid.

+**/

+#define PGD_IS_EMPTY(Val)  (PGD_VAL(Val) == INVALID_PAGE)

+

+/**

+  Check that the page upper directory table entry is empty.

+

+  @param  pud   Page upper directory struct variables.

+

+  @retval    1   The page table is invalid.

+  @retval    0   The page table is valid.

+**/

+#define PUD_IS_EMPTY(Val)  (PUD_VAL(Val) == INVALID_PAGE)

+

+/**

+  Check that the page middle directory table entry is empty.

+

+  @param  pmd   Page middle directory struct variables.

+

+  @retval    1   The page table is invalid.

+  @retval    0   The page table is valid.

+**/

+#define PMD_IS_EMPTY(Val)  (PMD_VAL(Val) == INVALID_PAGE)

+

+/**

+  Check that the page the page table entry is empty.

+

+  @param  pte   Page table entry struct variables.

+

+  @retval    1   The page table is invalid.

+  @retval    0   The page table is valid.

+**/

+#define PTE_IS_EMPTY(Val)  (!(PTE_VAL(Val) & (~PAGE_VALID)))

+#endif // PAGE_H_

diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c

new file mode 100644

index 0000000000..c214e8d847

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c

@@ -0,0 +1,178 @@

+/** @file

+  CPU Memory Map Unit PEI phase driver.

+

+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>

+

+  SPDX-License-Identifier: BSD-2-Clause-Patent

+

+  @par Glossary:

+    - Tlb      - Translation Lookaside Buffer

+**/

+

+#include <Uefi.h>

+#include <Library/BaseLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/CacheMaintenanceLib.h>

+#include <Library/CpuMmuLib.h>

+#include <Library/DebugLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/PcdLib.h>

+#include <Register/LoongArch64/Csr.h>

+

+#include "Page.h"

+#include "Tlb.h"

+#include "CommonMmuLib.h"

+

+//

+// For coding convenience, define the maximum valid

+// LoongArch exception.

+// Since UEFI V2.11, it will be present in DebugSupport.h.

+//

+#define MAX_LOONGARCH_EXCEPTION  64

+

+/**

+  Create a page table and initialize the memory management unit(MMU).

+

+  @param[in]   MemoryTable           A pointer to a memory ragion table.

+  @param[out]  TranslationTableBase  A pointer to a translation table base address.

+  @param[out]  TranslationTableSize  A pointer to a translation table base size.

+

+  @retval  EFI_SUCCESS                Configure MMU successfully.

+           EFI_INVALID_PARAMETER      MemoryTable is NULL.

+           EFI_UNSUPPORTED            Out of memory space or size not aligned.

+**/

+EFI_STATUS

+EFIAPI

+ConfigureMemoryManagementUnit (

+  IN  MEMORY_REGION_DESCRIPTOR  *MemoryTable,

+  OUT VOID                      **TranslationTableBase OPTIONAL,

+  OUT UINTN                     *TranslationTableSize  OPTIONAL

+  )

+{

+  PGD            *SwapperPageDir;

+  UINTN          PgdShift;

+  UINTN          PgdWide;

+  UINTN          PudShift;

+  UINTN          PudWide;

+  UINTN          PmdShift;

+  UINTN          PmdWide;

+  UINTN          PteShift;

+  UINTN          PteWide;

+  UINTN          Length;

+  UINTN          TlbReEntry;

+  UINTN          TlbReEntryOffset;

+  UINTN          Remaining;

+  RETURN_STATUS  Status;

+

+  SwapperPageDir = NULL;

+  PgdShift       = PGD_SHIFT;

+  PgdWide        = PGD_WIDE;

+  PudShift       = PUD_SHIFT;

+  PudWide        = PUD_WIDE;

+  PmdShift       = PMD_SHIFT;

+  PmdWide        = PMD_WIDE;

+  PteShift       = PTE_SHIFT;

+  PteWide        = PTE_WIDE;

+

+  if (MemoryTable == NULL) {

+    ASSERT (MemoryTable != NULL);

+    return EFI_INVALID_PARAMETER;

+  }

+

+  SwapperPageDir = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));

+  ZeroMem (SwapperPageDir, PGD_TABLE_SIZE);

+

+  if (SwapperPageDir == NULL) {

+    goto FreeTranslationTable;

+  }

+

+  CsrWrite (LOONGARCH_CSR_PGDL, (UINTN)SwapperPageDir);

+

+  while (MemoryTable->Length != 0) {

+    DEBUG ((

+      DEBUG_INFO,

+      "%a %d VirtualBase %p VirtualEnd %p Attributes %p .\n",

+      __func__,

+      __LINE__,

+      MemoryTable->VirtualBase,

+      (MemoryTable->Length + MemoryTable->VirtualBase),

+      MemoryTable->Attributes

+      ));

+

+    Status = FillTranslationTable (MemoryTable);

+    if (EFI_ERROR (Status)) {

+      goto FreeTranslationTable;

+    }

+

+    MemoryTable++;

+  }

+

+  //

+  // TLB Re-entry address at the end of exception vector, a vector is up to 512 bytes,

+  // so the starting address is: total exception vector size + total interrupt vector size + base.

+  // The total size of TLB handler and exception vector size and interrupt vector size should not

+  // be lager than 64KB.

+  //

+  Length           = (UINTN)HandleTlbRefillEnd - (UINTN)HandleTlbRefillStart;

+  TlbReEntryOffset = (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * 512;

+  Remaining        = TlbReEntryOffset % SIZE_4KB;

+  if (Remaining != 0x0) {

+    TlbReEntryOffset += (SIZE_4KB - Remaining);

+  }

+

+  TlbReEntry = PcdGet64 (PcdCpuExceptionVectorBaseAddress) + TlbReEntryOffset;

+  if ((TlbReEntryOffset + Length) > SIZE_64KB) {

+    goto FreeTranslationTable;

+  }

+

+  //

+  // Ensure that TLB refill exception base address alignment is equals to 4KB and is valid.

+  //

+  if (TlbReEntry & (SIZE_4KB - 1)) {

+    goto FreeTranslationTable;

+  }

+

+  CopyMem ((VOID *)TlbReEntry, HandleTlbRefillStart, Length);

+  InvalidateInstructionCacheRange ((VOID *)(UINTN)HandleTlbRefillStart, Length);

+

+  DEBUG ((

+    DEBUG_INFO,

+    "%a  %d PteShift %d PteWide %d PmdShift %d PmdWide %d PudShift %d PudWide %d PgdShift %d PgdWide %d.\n",

+    __func__,

+    __LINE__,

+    PteShift,

+    PteWide,

+    PmdShift,

+    PmdWide,

+    PudShift,

+    PudWide,

+    PgdShift,

+    PgdWide

+    ));

+

+  //

+  // Set the address of TLB refill exception handler

+  //

+  SetTlbRebaseAddress ((UINTN)TlbReEntry);

+

+  //

+  // Set page size

+  //

+  CsrXChg (LOONGARCH_CSR_TLBIDX, (DEFAULT_PAGE_SIZE << CSR_TLBIDX_SIZE), CSR_TLBIDX_SIZE_MASK);

+  CsrWrite (LOONGARCH_CSR_STLBPGSIZE, DEFAULT_PAGE_SIZE);

+  CsrXChg (LOONGARCH_CSR_TLBREHI, (DEFAULT_PAGE_SIZE << CSR_TLBREHI_PS_SHIFT), CSR_TLBREHI_PS);

+

+  CsrWrite (LOONGARCH_CSR_PWCTL0, (PteShift | PteWide << 5 | PmdShift << 10 | PmdWide << 15 | PudShift << 20 | PudWide << 25));

+  CsrWrite (LOONGARCH_CSR_PWCTL1, (PgdShift | PgdWide << 6));

+

+  DEBUG ((DEBUG_INFO, "%a %d Enable Mmu Start PageBassAddress %p.\n", __func__, __LINE__, SwapperPageDir));

+

+  return EFI_SUCCESS;

+

+FreeTranslationTable:

+  if (SwapperPageDir != NULL) {

+    FreePages (SwapperPageDir, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));

+  }

+

+  return EFI_UNSUPPORTED;

+}

diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h

new file mode 100644

index 0000000000..9a681ce8e1

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h

@@ -0,0 +1,48 @@

+/** @file

+

+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>

+

+  SPDX-License-Identifier: BSD-2-Clause-Patent

+

+**/

+

+#ifndef TLB_H_

+#define TLB_H_

+

+/**

+  Invalid corresponding TLB entries are based on the address given

+

+  @param Address The address corresponding to the invalid page table entry

+

+  @retval  none

+**/

+VOID

+InvalidTlb (

+  UINTN  Address

+  );

+

+/**

+  TLB refill handler start.

+

+  @param  none

+

+  @retval none

+**/

+VOID

+HandleTlbRefillStart (

+  VOID

+  );

+

+/**

+  TLB refill handler end.

+

+  @param  none

+

+  @retval none

+**/

+VOID

+HandleTlbRefillEnd (

+  VOID

+  );

+

+#endif // TLB_H_

diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S

new file mode 100644

index 0000000000..c9a8c16336

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S

@@ -0,0 +1,44 @@

+#------------------------------------------------------------------------------

+#

+# TLB operation functions

+#

+# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>

+#

+# SPDX-License-Identifier: BSD-2-Clause-Patent

+#

+#-----------------------------------------------------------------------------

+

+#include <Register/LoongArch64/Csr.h>

+

+ASM_GLOBAL ASM_PFX(HandleTlbRefillStart)

+ASM_GLOBAL ASM_PFX(HandleTlbRefillEnd)

+ASM_GLOBAL ASM_PFX(InvalidTlb)

+

+#

+#  Refill the page table.

+#  @param  VOID

+#  @retval  VOID

+#

+ASM_PFX(HandleTlbRefillStart):

+  csrwr   $t0, LOONGARCH_CSR_TLBRSAVE

+  csrrd   $t0, LOONGARCH_CSR_PGD

+  lddir   $t0, $t0, 3   #Put pud BaseAddress into T0

+  lddir   $t0, $t0, 2   #Put pmd BaseAddress into T0

+  lddir   $t0, $t0, 1   #Put pte BaseAddress into T0

+  ldpte   $t0, 0

+  ldpte   $t0, 1

+  tlbfill   // refill hi,lo0,lo1

+  csrrd   $t0, LOONGARCH_CSR_TLBRSAVE

+  ertn

+ASM_PFX(HandleTlbRefillEnd):

+

+#

+# Invalid corresponding TLB entries are based on the address given

+# @param a0 The address corresponding to the invalid page table entry

+# @retval  none

+#

+ASM_PFX(InvalidTlb):

+    invtlb  INVTLB_ADDR_GTRUE_OR_ASID, $zero, $a0

+    jirl    $zero, $ra, 0

+

+    .end

diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf

new file mode 100644

index 0000000000..45b15db4c9

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf

@@ -0,0 +1,44 @@

+## @file

+#  CPU Memory Map Unit PEI phase driver.

+#

+#  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                      = PeiCpuMmuLib

+  MODULE_UNI_FILE                = PeiCpuMmuLib.uni

+  FILE_GUID                      = F67EB983-AC2A-7550-AB69-3BC51A1C895B

+  MODULE_TYPE                    = PEIM

+  VERSION_STRING                 = 1.0

+  LIBRARY_CLASS                  = CpuMmuLib | SEC PEIM

+

+#

+#  VALID_ARCHITECTURES           = LOONGARCH64

+#

+

+[Sources.LoongArch64]

+  LoongArch64/TlbOperation.S   | GCC

+  LoongArch64/CommonMmuLib.c

+  LoongArch64/PeiCpuMmuLib.c

+  LoongArch64/CommonMmuLib.h

+  LoongArch64/Tlb.h

+  LoongArch64/Page.h

+

+[Packages]

+  MdePkg/MdePkg.dec

+  MdeModulePkg/MdeModulePkg.dec

+  UefiCpuPkg/UefiCpuPkg.dec

+

+[PCD]

+  gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask

+  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress

+

+[LibraryClasses]

+  CacheMaintenanceLib

+  DebugLib

+  MemoryAllocationLib

+  PcdLib

diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni

new file mode 100644

index 0000000000..3e21334f3e

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni

@@ -0,0 +1,14 @@

+// /** @file

+// CPU Memory Manager Unit library instance for PEI modules.

+//

+// CPU Memory Manager Unit library instance for PEI modules.

+//

+// 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 "CPU Memory Manager Unit library instance for PEI modules."

+

+#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for PEI modules."

diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc

index 28eed85bce..178dc3c0f9 100644

--- a/UefiCpuPkg/UefiCpuPkg.dsc

+++ b/UefiCpuPkg/UefiCpuPkg.dsc

@@ -207,5 +207,9 @@

   UefiCpuPkg/CpuTimerDxeRiscV64/CpuTimerDxeRiscV64.inf

   UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf



+[Components.LOONGARCH64]

+  UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf

+  UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf

+

 [BuildOptions]

   *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES












-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115010): https://edk2.groups.io/g/devel/message/115010
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 11/37] UefiCpuPkg: Add LoongArch64 CPU Timer instance
  2024-02-02  3:24   ` Ni, Ray
@ 2024-02-02  3:38     ` Chao Li
  0 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-02-02  3:38 UTC (permalink / raw)
  To: devel, ray.ni; +Cc: Dong, Eric, Kumar, Rahul R, Gerd Hoffmann

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

Hi Ray,


Thanks,
Chao
On 2024/2/2 11:24, Ni, Ray wrote:
>>   [LibraryClasses]
>>     BaseLib
>> -  PcdLib
>>     DebugLib
>> +  PcdLib
> 1. This PcdLib location change is unnecessary.
OK, I will keep them with original appearance next time.
>
>
>> +  SafeIntLib
> 2. x86 Lib instance does not depend on SafeIntLib.
> Adding it in common section may break some x86 platforms if SafeIntLib is not added the platform DSC.
> Can you move it under LoongArch section?
OK.
>
>
>
> 
>


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115012): https://edk2.groups.io/g/devel/message/115012
Mute This Topic: https://groups.io/mt/103971650/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 12/37] UefiCpuPkg: Add CPU exception library for LoongArch
  2024-02-02  3:30   ` Ni, Ray
@ 2024-02-02  3:44     ` Chao Li
  2024-02-02  4:30       ` Ni, Ray
  2024-03-08  8:02     ` Chao Li
  1 sibling, 1 reply; 89+ messages in thread
From: Chao Li @ 2024-02-02  3:44 UTC (permalink / raw)
  To: devel, ray.ni; +Cc: Dong, Eric, Kumar, Rahul R, Gerd Hoffmann, Baoqi Zhang

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

Hi Ray,


Thanks,
Chao
On 2024/2/2 11:30, Ni, Ray wrote:
> 1. Most of INF changes are not necessary for x86. Can you avoid them?
I just sorted them alphabetically, do you think it is unnecessary? If 
so, I will remove the changes in other patches in next time.
>
>> +  ## This PCD Contains the pointer to a CPU exception vector base address.
>> +  # @Prompt The pointer to a CPU exception vector base address.
>> +
>> gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress|0x0|UINT
>> 64|0x60000022
>> +
> 2. I do not see any reference to the above PCD in source/INF files.
> Any mistake here?
This PCD is used in CpuDxe and MmuLib, among others. Do you think this 
change should be made as a separate patch?
>
>
>
> 
>


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115013): https://edk2.groups.io/g/devel/message/115013
Mute This Topic: https://groups.io/mt/103971651/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-02-02  3:33       ` Ni, Ray
@ 2024-02-02  3:50         ` Chao Li
  2024-02-02  4:30           ` Ni, Ray
  0 siblings, 1 reply; 89+ messages in thread
From: Chao Li @ 2024-02-02  3:50 UTC (permalink / raw)
  To: devel, ray.ni, lersek@redhat.com
  Cc: Dong, Eric, Kumar, Rahul R, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

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

Hi Ray,


Thanks,
Chao
On 2024/2/2 11:33, Ni, Ray wrote:
>
> Chao,
>
> ConfigureMemoryManagementUnit() is not a library API anymore.
>
> Who will call it? I don’t see any reference to this function inside 
> the lib source.
>
It become a private API, called at the LoongArch virtual/physical 
machine PEIM, and when called it, the C file should extern this symbol.

Please refer incoming and outgoing mails, this library probably need to 
be refactored, sothe APIs may change, so PLS focus on this module in 
future and help me more, please...

> Thanks,
>
> Ray
>
> *From:* Chao Li <lichao@loongson.cn>
> *Sent:* Thursday, February 1, 2024 3:58 PM
> *To:* devel@edk2.groups.io; lersek@redhat.com
> *Cc:* Dong, Eric <eric.dong@intel.com>; Ni, Ray <ray.ni@intel.com>; 
> Kumar, Rahul R <rahul.r.kumar@intel.com>; Gerd Hoffmann 
> <kraxel@redhat.com>; Baoqi Zhang <zhangbaoqi@loongson.cn>; Dongyan 
> Qian <qiandongyan@loongson.cn>; Xianglai Li <lixianglai@loongson.cn>; 
> Bibo Mao <maobibo@loongson.cn>
> *Subject:* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib 
> to UefiCpuPkg
>
> Hi Lazslo,
>
> Thanks,
> Chao
>
> On 2024/1/31 17:47, Laszlo Ersek wrote:
>
>     On 1/26/24 07:29, Chao Li wrote:
>
>         Add a new library named CpuMmuLib and add a LoongArch64 instance with in
>
>         the library.
>
>         It provides two-stage MMU libraryinstances, PEI and DXE.
>
>         BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>
>         Cc: Eric Dong<eric.dong@intel.com>  <mailto:eric.dong@intel.com>
>
>         Cc: Ray Ni<ray.ni@intel.com>  <mailto:ray.ni@intel.com>
>
>         Cc: Laszlo Ersek<lersek@redhat.com>  <mailto:lersek@redhat.com>
>
>         Cc: Rahul Kumar<rahul1.kumar@intel.com>  <mailto:rahul1.kumar@intel.com>
>
>         Cc: Gerd Hoffmann<kraxel@redhat.com>  <mailto:kraxel@redhat.com>
>
>         Signed-off-by: Chao Li<lichao@loongson.cn>  <mailto:lichao@loongson.cn>
>
>         Co-authored-by: Baoqi Zhang<zhangbaoqi@loongson.cn>  <mailto:zhangbaoqi@loongson.cn>
>
>         Co-authored-by: Dongyan Qian<qiandongyan@loongson.cn>  <mailto:qiandongyan@loongson.cn>
>
>         Co-authored-by: Xianglai Li<lixianglai@loongson.cn>  <mailto:lixianglai@loongson.cn>
>
>         Co-authored-by: Bibo Mao<maobibo@loongson.cn>  <mailto:maobibo@loongson.cn>
>
>         ---
>
>           UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf |  36 +
>
>           UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni |  14 +
>
>           .../CpuMmuLib/LoongArch64/CommonMmuLib.c      | 988 ++++++++++++++++++
>
>           .../CpuMmuLib/LoongArch64/CommonMmuLib.h      |  43 +
>
>           .../Library/CpuMmuLib/LoongArch64/Page.h      | 279 +++++
>
>           .../CpuMmuLib/LoongArch64/PeiCpuMmuLib.c      | 178 ++++
>
>           .../Library/CpuMmuLib/LoongArch64/Tlb.h       |  48 +
>
>           .../CpuMmuLib/LoongArch64/TlbOperation.S      |  44 +
>
>           UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf |  44 +
>
>           UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni |  14 +
>
>           UefiCpuPkg/UefiCpuPkg.dsc                     |   4 +
>
>           11 files changed, 1692 insertions(+)
>
>           create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>
>           create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
>
>           create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>
>           create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
>
>           create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
>
>           create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
>
>           create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
>
>           create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
>
>           create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>
>           create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
>
>         diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>
>         new file mode 100644
>
>         index 0000000000..bfce3ce96d
>
>         --- /dev/null
>
>         +++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>
>         @@ -0,0 +1,36 @@
>
>         +## @file
>
>         +#  CPU Memory Map Unit DXE phase driver.
>
>         +#
>
>         +#  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                      = DxeCpuMmuLib
>
>         +  MODULE_UNI_FILE                = DxeCpuMmuLib.uni
>
>         +  FILE_GUID                      = DA8F0232-FB14-42F0-922C-63104D2C70BE
>
>     (1) This FILE_GUID was created from the FILE_GUID of
>
>     "ArmPkg/Library/ArmMmuLib/ArmMmuBaseLib.inf" by adding 1. That's not an
>
>     acceptable method for GUID generation.
>
>     A method is only acceptable for GUID generation if multiple (= an
>
>     unlimited number of) parties can execute the method at any time, and the
>
>     output remains conflict-free.
>
>     Taking an existent (known) FILE_GUID and incrementing it by 1 is not
>
>     such a method.
>
>     Please regenerate the GUID with "uuidgen".
>
>     Please also review the rest of your new GUIDs over this series (not only
>
>     FILE_GUIDs in INF files, but any other GUIDs, too).
>
> OK, I will regenerate the GUID in next commit.
>
>         +  MODULE_TYPE                    = DXE_DRIVER
>
>         +  VERSION_STRING                 = 1.0
>
>         +  LIBRARY_CLASS                  = CpuMmuLib | DXE_DRIVER
>
>         +  CONSTRUCTOR                    = MmuInitialize
>
>         +
>
>         +#
>
>         +#  VALID_ARCHITECTURES           = LOONGARCH64
>
>         +#
>
>         +
>
>         +[Sources.LoongArch64]
>
>         +  LoongArch64/TlbOperation.S   | GCC
>
>         +  LoongArch64/CommonMmuLib.c
>
>         +  LoongArch64/Page.h
>
>         +  LoongArch64/Tlb.h
>
>         +
>
>         +[Packages]
>
>         +  MdePkg/MdePkg.dec
>
>         +  UefiCpuPkg/UefiCpuPkg.dec
>
>         +
>
>         +[LibraryClasses]
>
>         +  DebugLib
>
>         +  MemoryAllocationLib
>
>         diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
>
>         new file mode 100644
>
>         index 0000000000..7342249516
>
>         --- /dev/null
>
>         +++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
>
>         @@ -0,0 +1,14 @@
>
>         +// /** @file
>
>         +// CPU Memory Manager Unit library instance for DXE modules.
>
>         +//
>
>         +// CPU Memory Manager Unit library instance for DXE modules.
>
>         +//
>
>         +// 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 "CPU Memory Manager Unit library instance for DXE modules."
>
>         +
>
>         +#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for DXE modules."
>
>         diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>
>         new file mode 100644
>
>         index 0000000000..2e852c3371
>
>         --- /dev/null
>
>         +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>
>         @@ -0,0 +1,988 @@
>
>         +/** @file
>
>         +
>
>         +  CPU Memory Map Unit Handler Library common functions.
>
>         +
>
>         +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>
>         +
>
>         +  SPDX-License-Identifier: BSD-2-Clause-Patent
>
>         +
>
>         +  @par Glossary:
>
>         +    - Pgd or Pgd or PGD    - Page Global Directory
>
>         +    - Pud or Pud or PUD    - Page Upper Directory
>
>         +    - Pmd or Pmd or PMD    - Page Middle Directory
>
>         +    - Pte or pte or PTE    - Page Table Entry
>
>         +    - Val or VAL or val    - Value
>
>         +    - Dir    - Directory
>
>         +**/
>
>         +#include <Uefi.h>
>
>         +#include <Library/BaseLib.h>
>
>         +#include <Library/BaseMemoryLib.h>
>
>         +#include <Library/CpuMmuLib.h>
>
>         +#include <Library/DebugLib.h>
>
>         +#include <Library/MemoryAllocationLib.h>
>
>         +#include <Register/LoongArch64/Csr.h>
>
>         +#include "Tlb.h"
>
>         +#include "Page.h"
>
>         +
>
>         +#define SWAP_PAGE_DIR              CsrRead(LOONGARCH_CSR_PGDL)
>
>     (2) Missing space after "CsrRead".
>
> OK.
>
>         +#define EFI_MEMORY_CACHETYPE_MASK  (EFI_MEMORY_UC  | \
>
>         +                                    EFI_MEMORY_WC  | \
>
>         +                                    EFI_MEMORY_WT  | \
>
>         +                                    EFI_MEMORY_WB  | \
>
>         +                                    EFI_MEMORY_UCE   \
>
>         +                                    )
>
>     (3) This seems to come from "ArmPkg/Include/Library/ArmLib.h"; it's not
>
>     great regardless: we shouldn't use the EFI_ prefix for symbols that are
>
>     not standard. (Put differently, EFI_ is a reserved namespace prefix for
>
>     the UEFI and PI specs.)
>
> OK, I will rename with out EFI_perfix next time.
>
>         +
>
>         +BOOLEAN  mMmuInited = FALSE;
>
>     (4) This should be STATIC, I believe.
>
>     (5) So this is the point where I realize that the library design makes
>
>     no sense to me.
>
>     Normally you create SEC/PEI vs. DXE phase instances of a library
>
>     because, using writable global variables, you can gain performance (you
>
>     can remember the initialization) in DXE, but in SEC/PEI, you don't have
>
>     writeable global variables.
>
>     This pattern does not seem to apply here, or at least it doesn't seem to
>
>     work. Here's why:
>
>     - the variable mMmuInited and the function MmuInitialize (which contains
>
>     an assignment to the variable) are in "CommonMmuLib.c", which gets built
>
>     into both library instances. This makes no sense; that assignment cannot
>
>     work in SEC/PEI (I presume you're not going to have writeable globals --
>
>     executing from flash).
>
>     - I think you may be trying to make up for that problem by checking
>
>     SWAP_PAGE_DIR (the LOONGARCH_CSR_PGDL register) in MmuInitialize() and
>
>     MmuIsInit(). That doesn't seem right.
>
> Yes, you are right, the PEI stage may be executed from flash, in this 
> case, we have no way to write global variables, so we can only check 
> whether CSR_PGDL is NULL in the DXE stage to get whether the MMU has 
> been initialized.
>
>     - The PEI instance of the library contains an EFIAPI function called
>
>     ConfigureMemoryManagementUnit(). This function is never called in this
>
>     patch, which makes me think it's supposed to be called from driver or
>
>     application code (i.e., not from within the library itself, but from
>
>     client code). However, ConfigureMemoryManagementUnit() is also not
>
>     declared in the previous patch (in
>
>     "UefiCpuPkg/Include/Library/CpuMmuLib.h"); therefore client code cannot
>
>     reach it at all -- so that function doesn't even belong in this library.
>
> This API was discussed with Ray in the V3 patch 13. Actually, the 
> CpuMmuLib.h include more APIs before, Ray believed that other APIs 
> should not be open to users or some APIs can be done by the base APIs, 
> and in my opinion, the ConfigureMemoryManagementUnit() is a private 
> API, so in V4, it has been removed from CpuMmuLib.h.
>
> So what do you think? the ConfigureMemoryManagementUnit should be 
> added back?
>
>     - MmuInitialize() doesn't actually do anything, it just checks (via the
>
>     CSR) whether some other component has already initialized the MMU
>
>     (likely with ConfigureMemoryManagementUnit()). And the sole purpose of
>
>     MmuIsInit() appears to be to return from the public library APIs
>
>     SetMemoryRegionAttributes() and GetMemoryRegionAttributes() early, if
>
>     that "other" (unknown) component has not called
>
>     ConfigureMemoryManagementUnit() yet.
>
>     So, I don't understand what you are trying to do. I could explain how to
>
>     keep the initialization logic *differences* minimal between the SEC/PEI
>
>     and the DXE library instances, but I don't understand how / when the MMU
>
>     initialization is supposed to occur in the first place.
>
> Let's me tell you this library how to work:
>
> In PEI stage, in addition to ensuring that the MMU is not used, the 
> user must call the ConfigureMemoryManagementUnit toinitialization the 
> MMU, such as filling the static page tables, set the TLB refill 
> exception entry point, set the page size etc. the 
> ConfigureMemoryManagementUnit is a private API but related to ARCH.
>
> During DXE stage, this library will provide services for CpuDxe and 
> other drivers, but almost changes to memory page attributes use the 
> protocols provided by CpuDxe. That is why the CpuMmuLib.h only 
> contains two APIs, it only can get/set the attribute.
>
> In short, the PEI stage needs to initialize and set up TLB refill 
> entry point and method(dynamically populate the TLB, keep the PA == 
> VA), and DXE stage is provides services for changing the memory 
> attributes.
>
>     (6) The patch is too large in general. You should construct these
>
>     library instances over multiple patches. The first patch could add some
>
>     declarations / macro definitions, such as "Page.h" and "Tlb.h". Another
>
>     patch could add the assembly language helper functions. Another patch
>
>     could add the PEI phase library instance (including the common code). A
>
>     final patch could add the DXE-phase bits.
>
>     The idea is to proceed in layers, logically building one on top of the
>
>     other. It's fine if the library doesn't build initially; there is no
>
>     attempt to build it anyway until you actually reference the INF files in
>
>     some DSC files. So please split this at least in 4 patches.
>
> OK, I will try it.
>
>     (7) The commit message should explain the expected usage model in detail.
>
> OK.
>
>     Best regards,
>
>     Laszlo
>
>         +
>
>         +/**
>
>         +  Check to see if mmu successfully initializes.
>
>         +
>
>         +  @param  VOID.
>
>         +
>
>         +  @retval  TRUE  Initialization has been completed.
>
>         +           FALSE Initialization did not complete.
>
>         +**/
>
>         +STATIC
>
>         +BOOLEAN
>
>         +MmuIsInit (
>
>         +  VOID
>
>         +  )
>
>         +{
>
>         +  if (mMmuInited || (SWAP_PAGE_DIR != 0)) {
>
>         +    return TRUE;
>
>         +  }
>
>         +
>
>         +  return FALSE;
>
>         +}
>
>         +
>
>         +/**
>
>         +  Iterates through the page directory to initialize it.
>
>         +
>
>         +  @param  Dst  A pointer to the directory of the page to initialize.
>
>         +  @param  Num  The number of page directories to initialize.
>
>         +  @param  Src  A pointer to the data used to initialize the page directory.
>
>         +
>
>         +  @return VOID.
>
>         +**/
>
>         +STATIC
>
>         +VOID
>
>         +PageDirInit (
>
>         +  IN VOID   *Dst,
>
>         +  IN UINTN  Num,
>
>         +  IN VOID   *Src
>
>         +  )
>
>         +{
>
>         +  UINTN  *Ptr;
>
>         +  UINTN  *End;
>
>         +  UINTN  Entry;
>
>         +
>
>         +  Entry = (UINTN)Src;
>
>         +  Ptr   = (UINTN *)Dst;
>
>         +  End   = Ptr + Num;
>
>         +
>
>         +  for ( ; Ptr < End; Ptr++) {
>
>         +    *Ptr = Entry;
>
>         +  }
>
>         +
>
>         +  return;
>
>         +}
>
>         +
>
>         +/**
>
>         +  Gets the virtual address corresponding to the page global directory table entry.
>
>         +
>
>         +  @param  Address  the virtual address for the table entry.
>
>         +
>
>         +  @retval PGD A pointer to get the table item.
>
>         +**/
>
>         +STATIC
>
>         +PGD *
>
>         +PgdOffset (
>
>         +  IN UINTN  Address
>
>         +  )
>
>         +{
>
>         +  return (PGD *)(SWAP_PAGE_DIR) + PGD_INDEX (Address);
>
>         +}
>
>         +
>
>         +/**
>
>         +  Gets the virtual address corresponding to the page upper directory table entry.
>
>         +
>
>         +  @param  Pgd  A pointer to a page global directory table entry.
>
>         +  @param  Address  the virtual address for the table entry.
>
>         +
>
>         +  @retval PUD A pointer to get the table item.
>
>         +**/
>
>         +STATIC
>
>         +PUD *
>
>         +PudOffset (
>
>         +  IN PGD    *Pgd,
>
>         +  IN UINTN  Address
>
>         +  )
>
>         +{
>
>         +  UINTN  PgdVal;
>
>         +
>
>         +  PgdVal = (UINTN)PGD_VAL (*Pgd);
>
>         +
>
>         +  return (PUD *)PgdVal + PUD_INDEX (Address);
>
>         +}
>
>         +
>
>         +/**
>
>         +  Gets the virtual address corresponding to the page middle directory table entry.
>
>         +
>
>         +  @param  Pud  A pointer to a page upper directory table entry.
>
>         +  @param  Address  the virtual address for the table entry.
>
>         +
>
>         +  @retval PMD A pointer to get the table item.
>
>         +**/
>
>         +STATIC
>
>         +PMD *
>
>         +PmdOffset (
>
>         +  IN PUD    *Pud,
>
>         +  IN UINTN  Address
>
>         +  )
>
>         +{
>
>         +  UINTN  PudVal;
>
>         +
>
>         +  PudVal = PUD_VAL (*Pud);
>
>         +
>
>         +  return (PMD *)PudVal + PMD_INDEX (Address);
>
>         +}
>
>         +
>
>         +/**
>
>         +  Gets the virtual address corresponding to the page table entry.
>
>         +
>
>         +  @param  Pmd  A pointer to a page middle directory table entry.
>
>         +  @param  Address  the virtual address for the table entry.
>
>         +
>
>         +  @retval PTE A pointer to get the table item.
>
>         +**/
>
>         +STATIC
>
>         +PTE *
>
>         +PteOffset (
>
>         +  IN PMD    *Pmd,
>
>         +  IN UINTN  Address
>
>         +  )
>
>         +{
>
>         +  UINTN  PmdVal;
>
>         +
>
>         +  PmdVal = (UINTN)PMD_VAL (*Pmd);
>
>         +
>
>         +  return (PTE *)PmdVal + PTE_INDEX (Address);
>
>         +}
>
>         +
>
>         +/**
>
>         +  Sets the value of the page table entry.
>
>         +
>
>         +  @param  Pte  A pointer to a page table entry.
>
>         +  @param  PteVal  The value of the page table entry to set.
>
>         +
>
>         +**/
>
>         +STATIC
>
>         +VOID
>
>         +SetPte (
>
>         +  IN PTE  *Pte,
>
>         +  IN PTE  PteVal
>
>         +  )
>
>         +{
>
>         +  *Pte = PteVal;
>
>         +}
>
>         +
>
>         +/**
>
>         +  Sets the value of the page global directory.
>
>         +
>
>         +  @param  Pgd  A pointer to a page global directory.
>
>         +  @param  Pud  The value of the page global directory to set.
>
>         +
>
>         +**/
>
>         +STATIC
>
>         +VOID
>
>         +SetPgd (
>
>         +  IN PGD  *Pgd,
>
>         +  IN PUD  *Pud
>
>         +  )
>
>         +{
>
>         +  *Pgd = (PGD) {
>
>         +    ((UINTN)Pud)
>
>         +  };
>
>         +}
>
>         +
>
>         +/**
>
>         +  Sets the value of the page upper directory.
>
>         +
>
>         +  @param  Pud  A pointer to a page upper directory.
>
>         +  @param  Pmd  The value of the page upper directory to set.
>
>         +
>
>         +**/
>
>         +STATIC
>
>         +VOID
>
>         +SetPud (
>
>         +  IN PUD  *Pud,
>
>         +  IN PMD  *Pmd
>
>         +  )
>
>         +{
>
>         +  *Pud = (PUD) {
>
>         +    ((UINTN)Pmd)
>
>         +  };
>
>         +}
>
>         +
>
>         +/**
>
>         +  Sets the value of the page middle directory.
>
>         +
>
>         +  @param  Pmd  A pointer to a page middle directory.
>
>         +  @param  Pte  The value of the page middle directory to set.
>
>         +
>
>         +**/
>
>         +STATIC
>
>         +VOID
>
>         +SetPmd (
>
>         +  IN PMD  *Pmd,
>
>         +  IN PTE  *Pte
>
>         +  )
>
>         +{
>
>         +  *Pmd = (PMD) {
>
>         +    ((UINTN)Pte)
>
>         +  };
>
>         +}
>
>         +
>
>         +/**
>
>         +  Free up memory space occupied by page tables.
>
>         +
>
>         +  @param  Pte  A pointer to the page table.
>
>         +
>
>         +**/
>
>         +VOID
>
>         +PteFree (
>
>         +  IN PTE  *Pte
>
>         +  )
>
>         +{
>
>         +  FreePages ((VOID *)Pte, 1);
>
>         +}
>
>         +
>
>         +/**
>
>         +  Free up memory space occupied by page middle directory.
>
>         +
>
>         +  @param  Pmd  A pointer to the page middle directory.
>
>         +
>
>         +**/
>
>         +VOID
>
>         +PmdFree (
>
>         +  IN PMD  *Pmd
>
>         +  )
>
>         +{
>
>         +  FreePages ((VOID *)Pmd, 1);
>
>         +}
>
>         +
>
>         +/**
>
>         +  Free up memory space occupied by page upper directory.
>
>         +
>
>         +  @param  Pud  A pointer to the page upper directory.
>
>         +
>
>         +**/
>
>         +VOID
>
>         +PudFree (
>
>         +  IN PUD  *Pud
>
>         +  )
>
>         +{
>
>         +  FreePages ((VOID *)Pud, 1);
>
>         +}
>
>         +
>
>         +/**
>
>         +  Requests the memory space required for the page upper directory,
>
>         +  initializes it, and places it in the specified page global directory
>
>         +
>
>         +  @param  Pgd  A pointer to the page global directory.
>
>         +
>
>         +  @retval  EFI_SUCCESS  Memory request successful.
>
>         +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
>
>         +**/
>
>         +STATIC
>
>         +EFI_STATUS
>
>         +PudAlloc (
>
>         +  IN PGD  *Pgd
>
>         +  )
>
>         +{
>
>         +  PUD  *Pud;
>
>         +
>
>         +  Pud = (PUD *)AllocatePages (1);
>
>         +  if (Pud == NULL) {
>
>         +    return EFI_OUT_OF_RESOURCES;
>
>         +  }
>
>         +
>
>         +  PageDirInit ((VOID *)Pud, ENTRYS_PER_PUD, (VOID *)INVALID_PAGE);
>
>         +
>
>         +  SetPgd (Pgd, Pud);
>
>         +
>
>         +  return EFI_SUCCESS;
>
>         +}
>
>         +
>
>         +/**
>
>         +  Requests the memory space required for the page middle directory,
>
>         +  initializes it, and places it in the specified page upper directory
>
>         +
>
>         +  @param  Pud  A pointer to the page upper directory.
>
>         +
>
>         +  @retval  EFI_SUCCESS  Memory request successful.
>
>         +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
>
>         +**/
>
>         +STATIC
>
>         +EFI_STATUS
>
>         +PmdAlloc (
>
>         +  IN PUD  *Pud
>
>         +  )
>
>         +{
>
>         +  PMD  *Pmd;
>
>         +
>
>         +  Pmd = (PMD *)AllocatePages (1);
>
>         +  if (!Pmd) {
>
>         +    return EFI_OUT_OF_RESOURCES;
>
>         +  }
>
>         +
>
>         +  PageDirInit ((VOID *)Pmd, ENTRYS_PER_PMD, (VOID *)INVALID_PAGE);
>
>         +
>
>         +  SetPud (Pud, Pmd);
>
>         +
>
>         +  return EFI_SUCCESS;
>
>         +}
>
>         +
>
>         +/**
>
>         +  Requests the memory space required for the page table,
>
>         +  initializes it, and places it in the specified page middle directory
>
>         +
>
>         +  @param  Pmd  A pointer to the page middle directory.
>
>         +
>
>         +  @retval  EFI_SUCCESS  Memory request successful.
>
>         +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
>
>         +**/
>
>         +STATIC
>
>         +EFI_STATUS
>
>         +PteAlloc (
>
>         +  IN PMD  *Pmd
>
>         +  )
>
>         +{
>
>         +  PTE  *Pte;
>
>         +
>
>         +  Pte = (PTE *)AllocatePages (1);
>
>         +  if (!Pte) {
>
>         +    return EFI_OUT_OF_RESOURCES;
>
>         +  }
>
>         +
>
>         +  Pte = ZeroMem (Pte, EFI_PAGE_SIZE);
>
>         +
>
>         +  SetPmd (Pmd, Pte);
>
>         +
>
>         +  return EFI_SUCCESS;
>
>         +}
>
>         +
>
>         +/**
>
>         +  Requests the memory space required for the page upper directory,
>
>         +  initializes it, and places it in the specified page global directory,
>
>         +  and get the page upper directory entry corresponding to the virtual address.
>
>         +
>
>         +  @param  Pgd      A pointer to the page global directory.
>
>         +  @param  Address  The corresponding virtual address of the page table entry.
>
>         +
>
>         +  @retval          A pointer to the page upper directory entry. Return NULL, if
>
>         +                   allocate the memory buffer is fail.
>
>         +**/
>
>         +STATIC
>
>         +PUD *
>
>         +PudAllocGet (
>
>         +  IN PGD    *Pgd,
>
>         +  IN UINTN  Address
>
>         +  )
>
>         +{
>
>         +  EFI_STATUS  Status;
>
>         +
>
>         +  if (PGD_IS_EMPTY (*Pgd)) {
>
>         +    Status = PudAlloc (Pgd);
>
>         +    ASSERT_EFI_ERROR (Status);
>
>         +    if (EFI_ERROR (Status)) {
>
>         +      return NULL;
>
>         +    }
>
>         +  }
>
>         +
>
>         +  return PudOffset (Pgd, Address);
>
>         +}
>
>         +
>
>         +/**
>
>         +  Requests the memory space required for the page middle directory,
>
>         +  initializes it, and places it in the specified page upper directory,
>
>         +  and get the page middle directory entry corresponding to the virtual address.
>
>         +
>
>         +  @param  Pud      A pointer to the page upper directory.
>
>         +  @param  Address  The corresponding virtual address of the page table entry.
>
>         +
>
>         +  @retval          A pointer to the page middle directory entry. Return NULL, if
>
>         +                   allocate the memory buffer is fail.
>
>         +**/
>
>         +STATIC
>
>         +PMD *
>
>         +PmdAllocGet (
>
>         +  IN PUD    *Pud,
>
>         +  IN UINTN  Address
>
>         +  )
>
>         +{
>
>         +  EFI_STATUS  Status;
>
>         +
>
>         +  if (PUD_IS_EMPTY (*Pud)) {
>
>         +    Status = PmdAlloc (Pud);
>
>         +    ASSERT_EFI_ERROR (Status);
>
>         +    if (EFI_ERROR (Status)) {
>
>         +      return NULL;
>
>         +    }
>
>         +  }
>
>         +
>
>         +  return PmdOffset (Pud, Address);
>
>         +}
>
>         +
>
>         +/**
>
>         +  Requests the memory space required for the page table,
>
>         +  initializes it, and places it in the specified page middle directory,
>
>         +  and get the page table entry corresponding to the virtual address.
>
>         +
>
>         +  @param  Pmd      A pointer to the page upper directory.
>
>         +  @param  Address  The corresponding virtual address of the page table entry.
>
>         +
>
>         +  @retval          A pointer to the page table entry. Return NULL, if allocate
>
>         +                   the memory buffer is fail.
>
>         +**/
>
>         +STATIC
>
>         +PTE *
>
>         +PteAllocGet (
>
>         +  IN PMD    *Pmd,
>
>         +  IN UINTN  Address
>
>         +  )
>
>         +{
>
>         +  EFI_STATUS  Status;
>
>         +
>
>         +  if (PMD_IS_EMPTY (*Pmd)) {
>
>         +    Status = PteAlloc (Pmd);
>
>         +    ASSERT_EFI_ERROR (Status);
>
>         +    if (EFI_ERROR (Status)) {
>
>         +      return NULL;
>
>         +    }
>
>         +  }
>
>         +
>
>         +  return PteOffset (Pmd, Address);
>
>         +}
>
>         +
>
>         +/**
>
>         +  Gets the physical address of the page table entry corresponding to the specified virtual address.
>
>         +
>
>         +  @param  Address  The corresponding virtual address of the page table entry.
>
>         +
>
>         +  @retval  A pointer to the page table entry.
>
>         +  @retval  NULL
>
>         +**/
>
>         +STATIC
>
>         +PTE *
>
>         +GetPteAddress (
>
>         +  IN UINTN  Address
>
>         +  )
>
>         +{
>
>         +  PGD  *Pgd;
>
>         +  PUD  *Pud;
>
>         +  PMD  *Pmd;
>
>         +
>
>         +  Pgd = PgdOffset (Address);
>
>         +
>
>         +  if (PGD_IS_EMPTY (*Pgd)) {
>
>         +    return NULL;
>
>         +  }
>
>         +
>
>         +  Pud = PudOffset (Pgd, Address);
>
>         +
>
>         +  if (PUD_IS_EMPTY (*Pud)) {
>
>         +    return NULL;
>
>         +  }
>
>         +
>
>         +  Pmd = PmdOffset (Pud, Address);
>
>         +  if (PMD_IS_EMPTY (*Pmd)) {
>
>         +    return NULL;
>
>         +  }
>
>         +
>
>         +  if (IS_HUGE_PAGE (Pmd->PmdVal)) {
>
>         +    return ((PTE *)Pmd);
>
>         +  }
>
>         +
>
>         +  return PteOffset (Pmd, Address);
>
>         +}
>
>         +
>
>         +/**
>
>         +  Gets the Attributes of Huge Page.
>
>         +
>
>         +  @param  Pmd  A pointer to the page middle directory.
>
>         +
>
>         +  @retval     Value of Attributes.
>
>         +**/
>
>         +STATIC
>
>         +UINTN
>
>         +GetHugePageAttributes (
>
>         +  IN  PMD  *Pmd
>
>         +  )
>
>         +{
>
>         +  UINTN  Attributes;
>
>         +  UINTN  GlobalFlag;
>
>         +  UINTN  HugeVal;
>
>         +
>
>         +  HugeVal     = PMD_VAL (*Pmd);
>
>         +  Attributes  = HugeVal & (~HUGEP_PAGE_MASK);
>
>         +  GlobalFlag  = ((Attributes & (1 << PAGE_HGLOBAL_SHIFT)) >> PAGE_HGLOBAL_SHIFT) << PAGE_GLOBAL_SHIFT;
>
>         +  Attributes &= ~(1 << PAGE_HGLOBAL_SHIFT);
>
>         +  Attributes |= GlobalFlag;
>
>         +  return Attributes;
>
>         +}
>
>         +
>
>         +/**
>
>         +  Establishes a page table entry based on the specified memory region.
>
>         +
>
>         +  @param  Pmd  A pointer to the page middle directory.
>
>         +  @param  Address  The memory space start address.
>
>         +  @param  End  The end address of the memory space.
>
>         +  @param  Attributes  Memory space Attributes.
>
>         +
>
>         +  @retval     EFI_SUCCESS   The page table entry was created successfully.
>
>         +  @retval     EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.
>
>         +**/
>
>         +STATIC
>
>         +EFI_STATUS
>
>         +MemoryMapPteRange (
>
>         +  IN PMD    *Pmd,
>
>         +  IN UINTN  Address,
>
>         +  IN UINTN  End,
>
>         +  IN UINTN  Attributes
>
>         +  )
>
>         +{
>
>         +  PTE      *Pte;
>
>         +  PTE      PteVal;
>
>         +  BOOLEAN  UpDate;
>
>         +
>
>         +  Pte = PteAllocGet (Pmd, Address);
>
>         +  if (!Pte) {
>
>         +    return EFI_OUT_OF_RESOURCES;
>
>         +  }
>
>         +
>
>         +  DEBUG ((
>
>         +    DEBUG_INFO,
>
>         +    "%a %d Address %p End %p  Attributes %llx\n",
>
>         +    __func__,
>
>         +    __LINE__,
>
>         +    Address,
>
>         +    End,
>
>         +    Attributes
>
>         +    ));
>
>         +
>
>         +  do {
>
>         +    UpDate = FALSE;
>
>         +    PteVal = MAKE_PTE (Address, Attributes);
>
>         +
>
>         +    if ((!PTE_IS_EMPTY (*Pte)) &&
>
>         +        (PTE_VAL (*Pte) != PTE_VAL (PteVal)))
>
>         +    {
>
>         +      UpDate = TRUE;
>
>         +    }
>
>         +
>
>         +    SetPte (Pte, PteVal);
>
>         +    if (UpDate) {
>
>         +      InvalidTlb (Address);
>
>         +    }
>
>         +  } while (Pte++, Address += EFI_PAGE_SIZE, Address != End);
>
>         +
>
>         +  return EFI_SUCCESS;
>
>         +}
>
>         +
>
>         +/**
>
>         +  Convert Huge Page to Page.
>
>         +
>
>         +  @param  Pmd  A pointer to the page middle directory.
>
>         +  @param  Address  The memory space start address.
>
>         +  @param  End  The end address of the memory space.
>
>         +  @param  Attributes  Memory space Attributes.
>
>         +
>
>         +  @retval  EFI_SUCCESS   The page table entry was created successfully.
>
>         +  @retval  EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.
>
>         +**/
>
>         +STATIC
>
>         +EFI_STATUS
>
>         +ConvertHugePageToPage (
>
>         +  IN  PMD   *Pmd,
>
>         +  IN UINTN  Address,
>
>         +  IN UINTN  End,
>
>         +  IN UINTN  Attributes
>
>         +  )
>
>         +{
>
>         +  UINTN       OldAttributes;
>
>         +  UINTN       HugePageEnd;
>
>         +  UINTN       HugePageStart;
>
>         +  EFI_STATUS  Status;
>
>         +
>
>         +  Status = EFI_SUCCESS;
>
>         +
>
>         +  if ((PMD_IS_EMPTY (*Pmd)) ||
>
>         +      (!IS_HUGE_PAGE (Pmd->PmdVal)))
>
>         +  {
>
>         +    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
>
>         +  } else {
>
>         +    OldAttributes = GetHugePageAttributes (Pmd);
>
>         +    if (Attributes == OldAttributes) {
>
>         +      return Status;
>
>         +    }
>
>         +
>
>         +    SetPmd (Pmd, (PTE *)(INVALID_PAGE));
>
>         +    HugePageStart = Address & PMD_MASK;
>
>         +    HugePageEnd   = HugePageStart + HUGE_PAGE_SIZE;
>
>         +    ASSERT (HugePageEnd >= End);
>
>         +
>
>         +    if (Address > HugePageStart) {
>
>         +      Status |= MemoryMapPteRange (Pmd, HugePageStart, Address, OldAttributes);
>
>         +    }
>
>         +
>
>         +    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
>
>         +
>
>         +    if (End < HugePageEnd) {
>
>         +      Status |= MemoryMapPteRange (Pmd, End, HugePageEnd, OldAttributes);
>
>         +    }
>
>         +  }
>
>         +
>
>         +  return Status;
>
>         +}
>
>         +
>
>         +/**
>
>         +  Establishes a page middle directory based on the specified memory region.
>
>         +
>
>         +  @param  Pud  A pointer to the page upper directory.
>
>         +  @param  Address  The memory space start address.
>
>         +  @param  End  The end address of the memory space.
>
>         +  @param  Attributes  Memory space Attributes.
>
>         +
>
>         +  @retval     EFI_SUCCESS   The page middle directory was created successfully.
>
>         +  @retval     EFI_OUT_OF_RESOURCES  Page middle directory establishment failed due to resource exhaustion.
>
>         +**/
>
>         +STATIC
>
>         +EFI_STATUS
>
>         +MemoryMapPmdRange (
>
>         +  IN PUD    *Pud,
>
>         +  IN UINTN  Address,
>
>         +  IN UINTN  End,
>
>         +  IN UINTN  Attributes
>
>         +  )
>
>         +{
>
>         +  PMD      *Pmd;
>
>         +  UINTN    Next;
>
>         +  PTE      PteVal;
>
>         +  BOOLEAN  UpDate;
>
>         +
>
>         +  Pmd = PmdAllocGet (Pud, Address);
>
>         +  if (Pmd == NULL) {
>
>         +    return EFI_OUT_OF_RESOURCES;
>
>         +  }
>
>         +
>
>         +  do {
>
>         +    Next = PMD_ADDRESS_END (Address, End);
>
>         +    if (((Address & (~PMD_MASK)) == 0) &&
>
>         +        ((Next &  (~PMD_MASK)) == 0) &&
>
>         +        (PMD_IS_EMPTY (*Pmd) || IS_HUGE_PAGE (Pmd->PmdVal)))
>
>         +    {
>
>         +      UpDate = FALSE;
>
>         +      PteVal = MAKE_HUGE_PTE (Address, Attributes);
>
>         +
>
>         +      if ((!PMD_IS_EMPTY (*Pmd)) &&
>
>         +          (PMD_VAL (*Pmd) != PTE_VAL (PteVal)))
>
>         +      {
>
>         +        UpDate = TRUE;
>
>         +      }
>
>         +
>
>         +      SetPmd (Pmd, (PTE *)PteVal.PteVal);
>
>         +      if (UpDate) {
>
>         +        InvalidTlb (Address);
>
>         +      }
>
>         +    } else {
>
>         +      ConvertHugePageToPage (Pmd, Address, Next, Attributes);
>
>         +    }
>
>         +  } while (Pmd++, Address = Next, Address != End);
>
>         +
>
>         +  return EFI_SUCCESS;
>
>         +}
>
>         +
>
>         +/**
>
>         +  Establishes a page upper directory based on the specified memory region.
>
>         +
>
>         +  @param  Pgd  A pointer to the page global directory.
>
>         +  @param  Address  The memory space start address.
>
>         +  @param  End  The end address of the memory space.
>
>         +  @param  Attributes  Memory space Attributes.
>
>         +
>
>         +  @retval     EFI_SUCCESS   The page upper directory was created successfully.
>
>         +  @retval     EFI_OUT_OF_RESOURCES  Page upper directory establishment failed due to resource exhaustion.
>
>         +**/
>
>         +STATIC
>
>         +EFI_STATUS
>
>         +MemoryMapPudRange (
>
>         +  IN PGD    *Pgd,
>
>         +  IN UINTN  Address,
>
>         +  IN UINTN  End,
>
>         +  IN UINTN  Attributes
>
>         +  )
>
>         +{
>
>         +  PUD    *Pud;
>
>         +  UINTN  Next;
>
>         +
>
>         +  Pud = PudAllocGet (Pgd, Address);
>
>         +  if (Pud == NULL) {
>
>         +    return EFI_OUT_OF_RESOURCES;
>
>         +  }
>
>         +
>
>         +  do {
>
>         +    Next = PUD_ADDRESS_END (Address, End);
>
>         +    if (EFI_ERROR (MemoryMapPmdRange (Pud, Address, Next, Attributes))) {
>
>         +      return EFI_OUT_OF_RESOURCES;
>
>         +    }
>
>         +  } while (Pud++, Address = Next, Address != End);
>
>         +
>
>         +  return EFI_SUCCESS;
>
>         +}
>
>         +
>
>         +/**
>
>         +  Establishes a page global directory based on the specified memory region.
>
>         +
>
>         +  @param  Start  The memory space start address.
>
>         +  @param  End  The end address of the memory space.
>
>         +  @param  Attributes  Memory space Attributes.
>
>         +
>
>         +  @retval     EFI_SUCCESS   The page global directory was created successfully.
>
>         +  @retval     EFI_OUT_OF_RESOURCES  Page global directory establishment failed due to resource exhaustion.
>
>         +**/
>
>         +STATIC
>
>         +EFI_STATUS
>
>         +MemoryMapPageRange (
>
>         +  IN UINTN  Start,
>
>         +  IN UINTN  End,
>
>         +  IN UINTN  Attributes
>
>         +  )
>
>         +{
>
>         +  PGD         *Pgd;
>
>         +  UINTN       Next;
>
>         +  UINTN       Address;
>
>         +  EFI_STATUS  Err;
>
>         +
>
>         +  Address = Start;
>
>         +
>
>         +  /* Get PGD(PTE PMD PUD PGD) in PageTables */
>
>         +  Pgd = PgdOffset (Address);
>
>         +  do {
>
>         +    Next = PGD_ADDRESS_END (Address, End);
>
>         +    /* Get Next Align Page to Map */
>
>         +    Err = MemoryMapPudRange (Pgd, Address, Next, Attributes);
>
>         +    if (Err) {
>
>         +      return Err;
>
>         +    }
>
>         +  } while (Pgd++, Address = Next, Address != End);
>
>         +
>
>         +  return EFI_SUCCESS;
>
>         +}
>
>         +
>
>         +/**
>
>         +  Page tables are established from memory-mapped tables.
>
>         +
>
>         +  @param  MemoryRegion   A pointer to a memory-mapped table entry.
>
>         +
>
>         +  @retval     EFI_SUCCESS   The page table was created successfully.
>
>         +  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
>
>         +**/
>
>         +EFI_STATUS
>
>         +FillTranslationTable (
>
>         +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
>
>         +  )
>
>         +{
>
>         +  return MemoryMapPageRange (
>
>         +           MemoryRegion->VirtualBase,
>
>         +           (MemoryRegion->Length + MemoryRegion->VirtualBase),
>
>         +           MemoryRegion->Attributes
>
>         +           );
>
>         +}
>
>         +
>
>         +/**
>
>         +  Convert EFI Attributes to Loongarch Attributes.
>
>         +
>
>         +  @param[in]  EfiAttributes     Efi Attributes.
>
>         +
>
>         +  @retval  Corresponding architecture attributes.
>
>         +**/
>
>         +UINTN
>
>         +EFIAPI
>
>         +EfiAttributeConverse (
>
>         +  IN UINTN  EfiAttributes
>
>         +  )
>
>         +{
>
>         +  UINTN  LoongArchAttributes;
>
>         +
>
>         +  LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | PLV_KERNEL | PAGE_GLOBAL;
>
>         +
>
>         +  switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
>
>         +    case EFI_MEMORY_UC:
>
>         +      LoongArchAttributes |= CACHE_SUC;
>
>         +      break;
>
>         +    case EFI_MEMORY_WC:
>
>         +      LoongArchAttributes |= CACHE_WUC;
>
>         +      break;
>
>         +    case EFI_MEMORY_WT:
>
>         +    case EFI_MEMORY_WB:
>
>         +      LoongArchAttributes |= CACHE_CC;
>
>         +      break;
>
>         +    default:
>
>         +      LoongArchAttributes |= CACHE_CC;
>
>         +      break;
>
>         +  }
>
>         +
>
>         +  // Write protection attributes
>
>         +  if (((EfiAttributes & EFI_MEMORY_RO) != 0) ||
>
>         +      ((EfiAttributes & EFI_MEMORY_WP) != 0))
>
>         +  {
>
>         +    LoongArchAttributes &= ~PAGE_DIRTY;
>
>         +  }
>
>         +
>
>         +  if ((EfiAttributes & EFI_MEMORY_RP) != 0) {
>
>         +    LoongArchAttributes |= PAGE_NO_READ;
>
>         +  }
>
>         +
>
>         +  // eXecute protection attribute
>
>         +  if ((EfiAttributes & EFI_MEMORY_XP) != 0) {
>
>         +    LoongArchAttributes |= PAGE_NO_EXEC;
>
>         +  }
>
>         +
>
>         +  return LoongArchAttributes;
>
>         +}
>
>         +
>
>         +/**
>
>         +  Finds the first of the length and memory properties of the memory region corresponding
>
>         +  to the specified base address.
>
>         +
>
>         +  @param[in]       BaseAddress       To find the base address of the memory region.
>
>         +  @param[in, out]  RegionLength      Pointer holding:
>
>         +                                      - At entry, the length of the memory region
>
>         +                                        expected to be found.
>
>         +                                      - At exit, the length of the memory region found.
>
>         +  @param[out]      RegionAttributes  Properties of the memory region found.
>
>         +
>
>         +  @retval  EFI_SUCCESS           The corresponding memory area was successfully found
>
>         +           EFI_NOT_FOUND         No memory area found
>
>         +           EFI_OUT_OF_RESOURCES  Base address or expected memory region exceeds the maximum
>
>         +                                 address.
>
>         +**/
>
>         +EFI_STATUS
>
>         +EFIAPI
>
>         +GetMemoryRegionAttributes (
>
>         +  IN     UINTN  BaseAddress,
>
>         +  IN OUT UINTN  *RegionLength,
>
>         +  OUT    UINTN  *RegionAttributes
>
>         +  )
>
>         +{
>
>         +  PTE    *Pte;
>
>         +  UINTN  Attributes;
>
>         +  UINTN  AttributesTmp;
>
>         +  UINTN  MaxAddress;
>
>         +  UINTN  EndAddress;
>
>         +  UINTN  AddSize;
>
>         +
>
>         +  if (!MmuIsInit ()) {
>
>         +    return EFI_UNSUPPORTED;
>
>         +  }
>
>         +
>
>         +  EndAddress = BaseAddress + *RegionLength;
>
>         +  MaxAddress = LShiftU64 (1ULL, MAX_VA_BITS) - 1;
>
>         +
>
>         +  // Clean the value to prepare output to find region size.
>
>         +  *RegionLength = 0x0;
>
>         +
>
>         +  if ((BaseAddress >= MaxAddress) || (EndAddress >= MaxAddress)) {
>
>         +    return EFI_OUT_OF_RESOURCES;
>
>         +  }
>
>         +
>
>         +  Pte = GetPteAddress (BaseAddress);
>
>         +
>
>         +  if (Pte == NULL) {
>
>         +    return EFI_NOT_FOUND;
>
>         +  }
>
>         +
>
>         +  Attributes = GET_PAGE_ATTRIBUTES (*Pte);
>
>         +  if (IS_HUGE_PAGE (Pte->PteVal)) {
>
>         +    *RegionAttributes = Attributes & (~(PAGE_HUGE));
>
>         +  } else {
>
>         +    *RegionAttributes = Attributes;
>
>         +  }
>
>         +
>
>         +  do {
>
>         +    Pte = GetPteAddress (BaseAddress);
>
>         +    if (Pte == NULL) {
>
>         +      return EFI_SUCCESS;
>
>         +    }
>
>         +
>
>         +    AttributesTmp = GET_PAGE_ATTRIBUTES (*Pte);
>
>         +    if (AttributesTmp == Attributes) {
>
>         +      if (IS_HUGE_PAGE (Pte->PteVal)) {
>
>         +        AddSize = HUGE_PAGE_SIZE;
>
>         +      } else {
>
>         +        AddSize = EFI_PAGE_SIZE;
>
>         +      }
>
>         +
>
>         +      *RegionLength += AddSize;
>
>         +      BaseAddress   += AddSize;
>
>         +    } else {
>
>         +      return EFI_SUCCESS;
>
>         +    }
>
>         +  } while (BaseAddress <= EndAddress);
>
>         +
>
>         +  return EFI_SUCCESS;
>
>         +}
>
>         +
>
>         +/**
>
>         +  Sets the Attributes  of the specified memory region
>
>         +
>
>         +  @param[in]  BaseAddress    The base address of the memory region to set the Attributes.
>
>         +  @param[in]  Length         The length of the memory region to set the Attributes.
>
>         +  @param[in]  Attributes     The Attributes to be set.
>
>         +  @param[in]  AttributeMask  Mask of memory attributes to take into account.
>
>         +
>
>         +  @retval  EFI_SUCCESS    The Attributes was set successfully
>
>         +**/
>
>         +EFI_STATUS
>
>         +EFIAPI
>
>         +SetMemoryRegionAttributes (
>
>         +  IN EFI_PHYSICAL_ADDRESS  BaseAddress,
>
>         +  IN UINTN                 Length,
>
>         +  IN UINTN                 Attributes,
>
>         +  IN UINT64                AttributeMask
>
>         +  )
>
>         +{
>
>         +  EFI_STATUS  Status;
>
>         +
>
>         +  if (!MmuIsInit ()) {
>
>         +    return EFI_UNSUPPORTED;
>
>         +  }
>
>         +
>
>         +  Attributes = EfiAttributeConverse (Attributes);
>
>         +  Status     = MemoryMapPageRange (BaseAddress, BaseAddress + Length, Attributes);
>
>         +  ASSERT_EFI_ERROR (Status);
>
>         +
>
>         +  return Status;
>
>         +}
>
>         +
>
>         +/**
>
>         +  Check to see if mmu successfully initializes and saves the result.
>
>         +
>
>         +  @param[in]  ImageHandle  The firmware allocated handle for the EFI image.
>
>         +  @param[in]  SystemTable  A pointer to the EFI System Table.
>
>         +
>
>         +  @retval  RETURN_SUCCESS    Initialization succeeded.
>
>         +**/
>
>         +RETURN_STATUS
>
>         +MmuInitialize (
>
>         +  IN EFI_HANDLE        ImageHandle,
>
>         +  IN EFI_SYSTEM_TABLE  *SystemTable
>
>         +  )
>
>         +{
>
>         +  if (SWAP_PAGE_DIR != 0) {
>
>         +    mMmuInited = TRUE;
>
>         +  }
>
>         +
>
>         +  return RETURN_SUCCESS;
>
>         +}
>
>         diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
>
>         new file mode 100644
>
>         index 0000000000..d8c922c8fa
>
>         --- /dev/null
>
>         +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
>
>         @@ -0,0 +1,43 @@
>
>         +/** @file
>
>         +
>
>         +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>
>         +
>
>         +  SPDX-License-Identifier: BSD-2-Clause-Patent
>
>         +
>
>         +  @par Glossary:
>
>         +    - Dir    - Directory
>
>         +**/
>
>         +
>
>         +#ifndef  MMU_LIB_CORE_H_
>
>         +#define  MMU_LIB_CORE_H_
>
>         +
>
>         +/**
>
>         +  Iterates through the page directory to initialize it.
>
>         +
>
>         +  @param  Dst  A pointer to the directory of the page to initialize.
>
>         +  @param  Num  The number of page directories to initialize.
>
>         +  @param  Src  A pointer to the data used to initialize the page directory.
>
>         +
>
>         +  @retval VOID.
>
>         +**/
>
>         +VOID
>
>         +PageDirInit (
>
>         +  IN VOID   *dest,
>
>         +  IN UINTN  Count,
>
>         +  IN VOID   *src
>
>         +  );
>
>         +
>
>         +/**
>
>         +  Page tables are established from memory-mapped tables.
>
>         +
>
>         +  @param  MemoryRegion   A pointer to a memory-mapped table entry.
>
>         +
>
>         +  @retval     EFI_SUCCESS   The page table was created successfully.
>
>         +  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
>
>         +**/
>
>         +EFI_STATUS
>
>         +FillTranslationTable (
>
>         +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
>
>         +  );
>
>         +
>
>         +#endif // MMU_LIB_CORE_H_
>
>         diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
>
>         new file mode 100644
>
>         index 0000000000..bac4f52327
>
>         --- /dev/null
>
>         +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
>
>         @@ -0,0 +1,279 @@
>
>         +/** @file
>
>         +
>
>         +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>
>         +
>
>         +  SPDX-License-Identifier: BSD-2-Clause-Patent
>
>         +
>
>         +  @par Glossary:
>
>         +    - Pgd or Pgd or PGD    - Page Global Directory
>
>         +    - Pud or Pud or PUD    - Page Upper Directory
>
>         +    - Pmd or Pmd or PMD    - Page Middle Directory
>
>         +    - Pte or pte or PTE    - Page Table Entry
>
>         +    - Val or VAL or val    - Value
>
>         +    - Dir    - Directory
>
>         +**/
>
>         +
>
>         +#ifndef PAGE_H_
>
>         +#define PAGE_H_
>
>         +
>
>         +#include <Library/CpuMmuLib.h>
>
>         +
>
>         +#define MAX_VA_BITS  47
>
>         +#define PGD_WIDE     (8)
>
>         +#define PUD_WIDE     (9)
>
>         +#define PMD_WIDE     (9)
>
>         +#define PTE_WIDE     (9)
>
>         +
>
>         +#define ENTRYS_PER_PGD  (1 << PGD_WIDE)
>
>         +#define ENTRYS_PER_PUD  (1 << PUD_WIDE)
>
>         +#define ENTRYS_PER_PMD  (1 << PMD_WIDE)
>
>         +#define ENTRYS_PER_PTE  (1 << PTE_WIDE)
>
>         +
>
>         +#define PGD_SHIFT  (PUD_SHIFT + PUD_WIDE)
>
>         +#define PUD_SHIFT  (PMD_SHIFT + PMD_WIDE)
>
>         +#define PMD_SHIFT  (EFI_PAGE_SHIFT + PTE_WIDE)
>
>         +#define PTE_SHIFT  (EFI_PAGE_SHIFT)
>
>         +
>
>         +#define PGD_SIZE  (1UL << PGD_SHIFT)
>
>         +#define PUD_SIZE  (1UL << PUD_SHIFT)
>
>         +#define PMD_SIZE  (1UL << PMD_SHIFT)
>
>         +
>
>         +#define PGD_MASK   (~(PGD_SIZE-1))
>
>         +#define PUD_MASK   (~(PUD_SIZE-1))
>
>         +#define PMD_MASK   (~(PMD_SIZE-1))
>
>         +#define PAGE_MASK  (~(EFI_PAGE_SIZE - 1))
>
>         +#define PFN_MASK   (~(((UINTN)(1) << (EFI_PAGE_SHIFT)) - 1) & \
>
>         +                   (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
>
>         +
>
>         +#define HUGEP_PAGE_MASK  (~(((UINTN)(1) << (PMD_SHIFT)) - 1) & \
>
>         +                          (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
>
>         +
>
>         +#define INVALID_PAGE  0
>
>         +
>
>         +typedef struct {
>
>         +  UINTN    PgdVal;
>
>         +} PGD;
>
>         +typedef struct {
>
>         +  UINTN    PudVal;
>
>         +} PUD;
>
>         +typedef struct {
>
>         +  UINTN    PmdVal;
>
>         +} PMD;
>
>         +typedef struct {
>
>         +  UINTN    PteVal;
>
>         +} PTE;
>
>         +
>
>         +/**
>
>         +  Gets the value of the page global directory table entry.
>
>         +
>
>         +  @param  x    Page global directory struct variables.
>
>         +
>
>         +  @retval   the value of the page global directory table entry.
>
>         + **/
>
>         +#define PGD_VAL(x)  ((x).PgdVal)
>
>         +
>
>         +/**
>
>         +  Gets the value of the page upper directory table entry.
>
>         +
>
>         +  @param  x    Page upper directory struct variables.
>
>         +
>
>         +  @retval  the value of the page upper directory table entry.
>
>         + **/
>
>         +#define PUD_VAL(x)  ((x).PudVal)
>
>         +
>
>         +/**
>
>         +  Gets the value of the page middle directory table entry.
>
>         +
>
>         +  @param  x    Page middle directory struct variables.
>
>         +
>
>         +  @retval  the value of the page middle directory table entry.
>
>         + **/
>
>         +#define PMD_VAL(x)  ((x).PmdVal)
>
>         +
>
>         +/**
>
>         +  Gets the value of the page table entry.
>
>         +
>
>         +  @param  x    Page table entry struct variables.
>
>         +
>
>         +  @retval  the value of the page table entry.
>
>         + **/
>
>         +#define PTE_VAL(x)  ((x).PteVal)
>
>         +
>
>         +#define PGD_TABLE_SIZE  (ENTRYS_PER_PGD * sizeof(PGD))
>
>         +#define PUD_TABLE_SIZE  (ENTRYS_PER_PUD * sizeof(PUD))
>
>         +#define PMD_TABLE_SIZE  (ENTRYS_PER_PMD * sizeof(PMD))
>
>         +#define PTE_TABLE_SIZE  (ENTRYS_PER_PTE * sizeof(PTE))
>
>         +
>
>         +/**
>
>         +  Gets the physical address of the record in the page table entry.
>
>         +
>
>         +  @param  x    Page table entry struct variables.
>
>         +
>
>         +  @retval  the value of the physical address.
>
>         + **/
>
>         +#define GET_PAGE_ATTRIBUTES(x)  (UINTN) {(PTE_VAL(x) & ~PFN_MASK)}
>
>         +
>
>         +/**
>
>         +  Gets the virtual address of the next block of the specified virtual address
>
>         +  that is aligned with the size of the global page directory mapping.
>
>         +
>
>         +  @param  Address  Specifies the virtual address.
>
>         +  @param  End    The end address of the memory region.
>
>         +
>
>         +  @retval   the specified virtual address  of the next block.
>
>         + **/
>
>         +#define PGD_ADDRESS_END(Address, End)                  \
>
>         +({                                                     \
>
>         +  UINTN Boundary = ((Address) + PGD_SIZE) & PGD_MASK;  \
>
>         +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
>
>         +})
>
>         +
>
>         +/**
>
>         +  Gets the virtual address of the next block of the specified virtual address
>
>         +  that is aligned with the size of the page upper directory mapping.
>
>         +
>
>         +  @param  Address  Specifies the virtual address.
>
>         +  @param  End    The end address of the memory region.
>
>         +
>
>         +  @retval   the specified virtual address  of the next block.
>
>         + **/
>
>         +#define PUD_ADDRESS_END(Address, End)                  \
>
>         +({                                                     \
>
>         +  UINTN Boundary = ((Address) + PUD_SIZE) & PUD_MASK;  \
>
>         +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
>
>         +})
>
>         +
>
>         +/**
>
>         +  Gets the virtual address of the next block of the specified virtual address
>
>         +  that is aligned with the size of the page middle directory mapping.
>
>         +
>
>         +  @param  Address  Specifies the virtual address.
>
>         +  @param  End    The end address of the memory region.
>
>         +
>
>         +  @retval   the specified virtual address  of the next block.
>
>         + **/
>
>         +#define PMD_ADDRESS_END(Address, End)                  \
>
>         +({                                                     \
>
>         +  UINTN Boundary = ((Address) + PMD_SIZE) & PMD_MASK;  \
>
>         +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
>
>         +})
>
>         +
>
>         +/**
>
>         +  Get Specifies the virtual address corresponding to the index of the page global directory table entry.
>
>         +
>
>         +  @param  Address  Specifies the virtual address.
>
>         +
>
>         +  @retval   the index of the page global directory table entry.
>
>         + **/
>
>         +#define PGD_INDEX(Address)  (((Address) >> PGD_SHIFT) & (ENTRYS_PER_PGD-1))
>
>         +
>
>         +/**
>
>         +  Get Specifies the virtual address corresponding to the index of the page upper directory table entry.
>
>         +
>
>         +  @param  Address  Specifies the virtual address.
>
>         +  @param  End    The end address of the memory region.
>
>         +
>
>         +  @retval   the index of the page upper directory table entry.
>
>         + **/
>
>         +#define PUD_INDEX(Address)  (((Address) >> PUD_SHIFT) & (ENTRYS_PER_PUD - 1))
>
>         +
>
>         +/**
>
>         +  Get Specifies the virtual address corresponding to the index of the page middle directory table entry.
>
>         +
>
>         +  @param  Address  Specifies the virtual address.
>
>         +
>
>         +  @retval   the index of the page middle directory table entry.
>
>         + **/
>
>         +#define PMD_INDEX(Address)  (((Address) >> PMD_SHIFT) & (ENTRYS_PER_PMD - 1))
>
>         +
>
>         +/**
>
>         +  Get Specifies the virtual address corresponding to the index of the page table entry.
>
>         +
>
>         +  @param  Address  Specifies the virtual address.
>
>         +
>
>         +  @retval   the index of the page table entry.
>
>         + **/
>
>         +#define PTE_INDEX(Address)  (((Address) >> EFI_PAGE_SHIFT) & (ENTRYS_PER_PTE - 1))
>
>         +
>
>         +/**
>
>         +  Calculates the value of the page table entry based on the specified virtual address and properties.
>
>         +
>
>         +  @param  Address  Specifies the virtual address.
>
>         +  @param  Attributes  Specifies the Attributes.
>
>         +
>
>         +  @retval    the value of the page table entry.
>
>         + **/
>
>         +#define MAKE_PTE(Address, Attributes)  (PTE){((((Address) >> EFI_PAGE_SHIFT) << 12) | (Attributes))}
>
>         +
>
>         +/**
>
>         +  Get Global bit from Attributes
>
>         +
>
>         +  @param  Attributes  Specifies the Attributes.
>
>         + * */
>
>         +#define GET_GLOBALBIT(Attributes)  ((Attributes & PAGE_GLOBAL) >> PAGE_GLOBAL_SHIFT)
>
>         +
>
>         +/**
>
>         +  Calculates the value of the Huge page table entry based on the specified virtual address and properties.
>
>         +
>
>         +  @param  Address  Specifies the virtual address.
>
>         +  @param  Attributes  Specifies the Attributes.
>
>         +
>
>         +  @retval    the value of the HUGE page table entry.
>
>         + **/
>
>         +#define MAKE_HUGE_PTE(Address, Attributes)  (PTE){(((((Address) >> PMD_SHIFT) << PMD_SHIFT) | \
>
>         +                                             ((Attributes) | (GET_GLOBALBIT(Attributes) << PAGE_HGLOBAL_SHIFT) | \
>
>         +                                             PAGE_HUGE)))}
>
>         +
>
>         +/**
>
>         +  Check whether the large page table entry is.
>
>         +
>
>         +  @param  Val The value of the page table entry.
>
>         +
>
>         +  @retval    1   Is huge page table entry.
>
>         +  @retval    0   Isn't huge page table entry.
>
>         +**/
>
>         +#define IS_HUGE_PAGE(Val)  ((((Val) & PAGE_HUGE) == PAGE_HUGE) && \
>
>         +                            (((Val) & PAGE_HGLOBAL) == PAGE_HGLOBAL))
>
>         +
>
>         +#define HUGE_PAGE_SIZE  (PMD_SIZE)
>
>         +
>
>         +/**
>
>         +  Check that the global page directory table entry is empty.
>
>         +
>
>         +  @param  pgd   the global page directory struct variables.
>
>         +
>
>         +  @retval    1   The page table is invalid.
>
>         +  @retval    0   The page table is valid.
>
>         +**/
>
>         +#define PGD_IS_EMPTY(Val)  (PGD_VAL(Val) == INVALID_PAGE)
>
>         +
>
>         +/**
>
>         +  Check that the page upper directory table entry is empty.
>
>         +
>
>         +  @param  pud   Page upper directory struct variables.
>
>         +
>
>         +  @retval    1   The page table is invalid.
>
>         +  @retval    0   The page table is valid.
>
>         +**/
>
>         +#define PUD_IS_EMPTY(Val)  (PUD_VAL(Val) == INVALID_PAGE)
>
>         +
>
>         +/**
>
>         +  Check that the page middle directory table entry is empty.
>
>         +
>
>         +  @param  pmd   Page middle directory struct variables.
>
>         +
>
>         +  @retval    1   The page table is invalid.
>
>         +  @retval    0   The page table is valid.
>
>         +**/
>
>         +#define PMD_IS_EMPTY(Val)  (PMD_VAL(Val) == INVALID_PAGE)
>
>         +
>
>         +/**
>
>         +  Check that the page the page table entry is empty.
>
>         +
>
>         +  @param  pte   Page table entry struct variables.
>
>         +
>
>         +  @retval    1   The page table is invalid.
>
>         +  @retval    0   The page table is valid.
>
>         +**/
>
>         +#define PTE_IS_EMPTY(Val)  (!(PTE_VAL(Val) & (~PAGE_VALID)))
>
>         +#endif // PAGE_H_
>
>         diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
>
>         new file mode 100644
>
>         index 0000000000..c214e8d847
>
>         --- /dev/null
>
>         +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
>
>         @@ -0,0 +1,178 @@
>
>         +/** @file
>
>         +  CPU Memory Map Unit PEI phase driver.
>
>         +
>
>         +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>
>         +
>
>         +  SPDX-License-Identifier: BSD-2-Clause-Patent
>
>         +
>
>         +  @par Glossary:
>
>         +    - Tlb      - Translation Lookaside Buffer
>
>         +**/
>
>         +
>
>         +#include <Uefi.h>
>
>         +#include <Library/BaseLib.h>
>
>         +#include <Library/BaseMemoryLib.h>
>
>         +#include <Library/CacheMaintenanceLib.h>
>
>         +#include <Library/CpuMmuLib.h>
>
>         +#include <Library/DebugLib.h>
>
>         +#include <Library/MemoryAllocationLib.h>
>
>         +#include <Library/PcdLib.h>
>
>         +#include <Register/LoongArch64/Csr.h>
>
>         +
>
>         +#include "Page.h"
>
>         +#include "Tlb.h"
>
>         +#include "CommonMmuLib.h"
>
>         +
>
>         +//
>
>         +// For coding convenience, define the maximum valid
>
>         +// LoongArch exception.
>
>         +// Since UEFI V2.11, it will be present in DebugSupport.h.
>
>         +//
>
>         +#define MAX_LOONGARCH_EXCEPTION  64
>
>         +
>
>         +/**
>
>         +  Create a page table and initialize the memory management unit(MMU).
>
>         +
>
>         +  @param[in]   MemoryTable           A pointer to a memory ragion table.
>
>         +  @param[out]  TranslationTableBase  A pointer to a translation table base address.
>
>         +  @param[out]  TranslationTableSize  A pointer to a translation table base size.
>
>         +
>
>         +  @retval  EFI_SUCCESS                Configure MMU successfully.
>
>         +           EFI_INVALID_PARAMETER      MemoryTable is NULL.
>
>         +           EFI_UNSUPPORTED            Out of memory space or size not aligned.
>
>         +**/
>
>         +EFI_STATUS
>
>         +EFIAPI
>
>         +ConfigureMemoryManagementUnit (
>
>         +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryTable,
>
>         +  OUT VOID                      **TranslationTableBase OPTIONAL,
>
>         +  OUT UINTN                     *TranslationTableSize  OPTIONAL
>
>         +  )
>
>         +{
>
>         +  PGD            *SwapperPageDir;
>
>         +  UINTN          PgdShift;
>
>         +  UINTN          PgdWide;
>
>         +  UINTN          PudShift;
>
>         +  UINTN          PudWide;
>
>         +  UINTN          PmdShift;
>
>         +  UINTN          PmdWide;
>
>         +  UINTN          PteShift;
>
>         +  UINTN          PteWide;
>
>         +  UINTN          Length;
>
>         +  UINTN          TlbReEntry;
>
>         +  UINTN          TlbReEntryOffset;
>
>         +  UINTN          Remaining;
>
>         +  RETURN_STATUS  Status;
>
>         +
>
>         +  SwapperPageDir = NULL;
>
>         +  PgdShift       = PGD_SHIFT;
>
>         +  PgdWide        = PGD_WIDE;
>
>         +  PudShift       = PUD_SHIFT;
>
>         +  PudWide        = PUD_WIDE;
>
>         +  PmdShift       = PMD_SHIFT;
>
>         +  PmdWide        = PMD_WIDE;
>
>         +  PteShift       = PTE_SHIFT;
>
>         +  PteWide        = PTE_WIDE;
>
>         +
>
>         +  if (MemoryTable == NULL) {
>
>         +    ASSERT (MemoryTable != NULL);
>
>         +    return EFI_INVALID_PARAMETER;
>
>         +  }
>
>         +
>
>         +  SwapperPageDir = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
>
>         +  ZeroMem (SwapperPageDir, PGD_TABLE_SIZE);
>
>         +
>
>         +  if (SwapperPageDir == NULL) {
>
>         +    goto FreeTranslationTable;
>
>         +  }
>
>         +
>
>         +  CsrWrite (LOONGARCH_CSR_PGDL, (UINTN)SwapperPageDir);
>
>         +
>
>         +  while (MemoryTable->Length != 0) {
>
>         +    DEBUG ((
>
>         +      DEBUG_INFO,
>
>         +      "%a %d VirtualBase %p VirtualEnd %p Attributes %p .\n",
>
>         +      __func__,
>
>         +      __LINE__,
>
>         +      MemoryTable->VirtualBase,
>
>         +      (MemoryTable->Length + MemoryTable->VirtualBase),
>
>         +      MemoryTable->Attributes
>
>         +      ));
>
>         +
>
>         +    Status = FillTranslationTable (MemoryTable);
>
>         +    if (EFI_ERROR (Status)) {
>
>         +      goto FreeTranslationTable;
>
>         +    }
>
>         +
>
>         +    MemoryTable++;
>
>         +  }
>
>         +
>
>         +  //
>
>         +  // TLB Re-entry address at the end of exception vector, a vector is up to 512 bytes,
>
>         +  // so the starting address is: total exception vector size + total interrupt vector size + base.
>
>         +  // The total size of TLB handler and exception vector size and interrupt vector size should not
>
>         +  // be lager than 64KB.
>
>         +  //
>
>         +  Length           = (UINTN)HandleTlbRefillEnd - (UINTN)HandleTlbRefillStart;
>
>         +  TlbReEntryOffset = (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * 512;
>
>         +  Remaining        = TlbReEntryOffset % SIZE_4KB;
>
>         +  if (Remaining != 0x0) {
>
>         +    TlbReEntryOffset += (SIZE_4KB - Remaining);
>
>         +  }
>
>         +
>
>         +  TlbReEntry = PcdGet64 (PcdCpuExceptionVectorBaseAddress) + TlbReEntryOffset;
>
>         +  if ((TlbReEntryOffset + Length) > SIZE_64KB) {
>
>         +    goto FreeTranslationTable;
>
>         +  }
>
>         +
>
>         +  //
>
>         +  // Ensure that TLB refill exception base address alignment is equals to 4KB and is valid.
>
>         +  //
>
>         +  if (TlbReEntry & (SIZE_4KB - 1)) {
>
>         +    goto FreeTranslationTable;
>
>         +  }
>
>         +
>
>         +  CopyMem ((VOID *)TlbReEntry, HandleTlbRefillStart, Length);
>
>         +  InvalidateInstructionCacheRange ((VOID *)(UINTN)HandleTlbRefillStart, Length);
>
>         +
>
>         +  DEBUG ((
>
>         +    DEBUG_INFO,
>
>         +    "%a  %d PteShift %d PteWide %d PmdShift %d PmdWide %d PudShift %d PudWide %d PgdShift %d PgdWide %d.\n",
>
>         +    __func__,
>
>         +    __LINE__,
>
>         +    PteShift,
>
>         +    PteWide,
>
>         +    PmdShift,
>
>         +    PmdWide,
>
>         +    PudShift,
>
>         +    PudWide,
>
>         +    PgdShift,
>
>         +    PgdWide
>
>         +    ));
>
>         +
>
>         +  //
>
>         +  // Set the address of TLB refill exception handler
>
>         +  //
>
>         +  SetTlbRebaseAddress ((UINTN)TlbReEntry);
>
>         +
>
>         +  //
>
>         +  // Set page size
>
>         +  //
>
>         +  CsrXChg (LOONGARCH_CSR_TLBIDX, (DEFAULT_PAGE_SIZE << CSR_TLBIDX_SIZE), CSR_TLBIDX_SIZE_MASK);
>
>         +  CsrWrite (LOONGARCH_CSR_STLBPGSIZE, DEFAULT_PAGE_SIZE);
>
>         +  CsrXChg (LOONGARCH_CSR_TLBREHI, (DEFAULT_PAGE_SIZE << CSR_TLBREHI_PS_SHIFT), CSR_TLBREHI_PS);
>
>         +
>
>         +  CsrWrite (LOONGARCH_CSR_PWCTL0, (PteShift | PteWide << 5 | PmdShift << 10 | PmdWide << 15 | PudShift << 20 | PudWide << 25));
>
>         +  CsrWrite (LOONGARCH_CSR_PWCTL1, (PgdShift | PgdWide << 6));
>
>         +
>
>         +  DEBUG ((DEBUG_INFO, "%a %d Enable Mmu Start PageBassAddress %p.\n", __func__, __LINE__, SwapperPageDir));
>
>         +
>
>         +  return EFI_SUCCESS;
>
>         +
>
>         +FreeTranslationTable:
>
>         +  if (SwapperPageDir != NULL) {
>
>         +    FreePages (SwapperPageDir, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
>
>         +  }
>
>         +
>
>         +  return EFI_UNSUPPORTED;
>
>         +}
>
>         diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
>
>         new file mode 100644
>
>         index 0000000000..9a681ce8e1
>
>         --- /dev/null
>
>         +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
>
>         @@ -0,0 +1,48 @@
>
>         +/** @file
>
>         +
>
>         +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>
>         +
>
>         +  SPDX-License-Identifier: BSD-2-Clause-Patent
>
>         +
>
>         +**/
>
>         +
>
>         +#ifndef TLB_H_
>
>         +#define TLB_H_
>
>         +
>
>         +/**
>
>         +  Invalid corresponding TLB entries are based on the address given
>
>         +
>
>         +  @param Address The address corresponding to the invalid page table entry
>
>         +
>
>         +  @retval  none
>
>         +**/
>
>         +VOID
>
>         +InvalidTlb (
>
>         +  UINTN  Address
>
>         +  );
>
>         +
>
>         +/**
>
>         +  TLB refill handler start.
>
>         +
>
>         +  @param  none
>
>         +
>
>         +  @retval none
>
>         +**/
>
>         +VOID
>
>         +HandleTlbRefillStart (
>
>         +  VOID
>
>         +  );
>
>         +
>
>         +/**
>
>         +  TLB refill handler end.
>
>         +
>
>         +  @param  none
>
>         +
>
>         +  @retval none
>
>         +**/
>
>         +VOID
>
>         +HandleTlbRefillEnd (
>
>         +  VOID
>
>         +  );
>
>         +
>
>         +#endif // TLB_H_
>
>         diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
>
>         new file mode 100644
>
>         index 0000000000..c9a8c16336
>
>         --- /dev/null
>
>         +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
>
>         @@ -0,0 +1,44 @@
>
>         +#------------------------------------------------------------------------------
>
>         +#
>
>         +# TLB operation functions
>
>         +#
>
>         +# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>
>         +#
>
>         +# SPDX-License-Identifier: BSD-2-Clause-Patent
>
>         +#
>
>         +#-----------------------------------------------------------------------------
>
>         +
>
>         +#include <Register/LoongArch64/Csr.h>
>
>         +
>
>         +ASM_GLOBAL ASM_PFX(HandleTlbRefillStart)
>
>         +ASM_GLOBAL ASM_PFX(HandleTlbRefillEnd)
>
>         +ASM_GLOBAL ASM_PFX(InvalidTlb)
>
>         +
>
>         +#
>
>         +#  Refill the page table.
>
>         +#  @param  VOID
>
>         +#  @retval  VOID
>
>         +#
>
>         +ASM_PFX(HandleTlbRefillStart):
>
>         +  csrwr   $t0, LOONGARCH_CSR_TLBRSAVE
>
>         +  csrrd   $t0, LOONGARCH_CSR_PGD
>
>         +  lddir   $t0, $t0, 3   #Put pud BaseAddress into T0
>
>         +  lddir   $t0, $t0, 2   #Put pmd BaseAddress into T0
>
>         +  lddir   $t0, $t0, 1   #Put pte BaseAddress into T0
>
>         +  ldpte   $t0, 0
>
>         +  ldpte   $t0, 1
>
>         +  tlbfill   // refill hi,lo0,lo1
>
>         +  csrrd   $t0, LOONGARCH_CSR_TLBRSAVE
>
>         +  ertn
>
>         +ASM_PFX(HandleTlbRefillEnd):
>
>         +
>
>         +#
>
>         +# Invalid corresponding TLB entries are based on the address given
>
>         +# @param a0 The address corresponding to the invalid page table entry
>
>         +# @retval  none
>
>         +#
>
>         +ASM_PFX(InvalidTlb):
>
>         +    invtlb  INVTLB_ADDR_GTRUE_OR_ASID, $zero, $a0
>
>         +    jirl    $zero, $ra, 0
>
>         +
>
>         +    .end
>
>         diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>
>         new file mode 100644
>
>         index 0000000000..45b15db4c9
>
>         --- /dev/null
>
>         +++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>
>         @@ -0,0 +1,44 @@
>
>         +## @file
>
>         +#  CPU Memory Map Unit PEI phase driver.
>
>         +#
>
>         +#  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                      = PeiCpuMmuLib
>
>         +  MODULE_UNI_FILE                = PeiCpuMmuLib.uni
>
>         +  FILE_GUID                      = F67EB983-AC2A-7550-AB69-3BC51A1C895B
>
>         +  MODULE_TYPE                    = PEIM
>
>         +  VERSION_STRING                 = 1.0
>
>         +  LIBRARY_CLASS                  = CpuMmuLib | SEC PEIM
>
>         +
>
>         +#
>
>         +#  VALID_ARCHITECTURES           = LOONGARCH64
>
>         +#
>
>         +
>
>         +[Sources.LoongArch64]
>
>         +  LoongArch64/TlbOperation.S   | GCC
>
>         +  LoongArch64/CommonMmuLib.c
>
>         +  LoongArch64/PeiCpuMmuLib.c
>
>         +  LoongArch64/CommonMmuLib.h
>
>         +  LoongArch64/Tlb.h
>
>         +  LoongArch64/Page.h
>
>         +
>
>         +[Packages]
>
>         +  MdePkg/MdePkg.dec
>
>         +  MdeModulePkg/MdeModulePkg.dec
>
>         +  UefiCpuPkg/UefiCpuPkg.dec
>
>         +
>
>         +[PCD]
>
>         +  gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask
>
>         +  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress
>
>         +
>
>         +[LibraryClasses]
>
>         +  CacheMaintenanceLib
>
>         +  DebugLib
>
>         +  MemoryAllocationLib
>
>         +  PcdLib
>
>         diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
>
>         new file mode 100644
>
>         index 0000000000..3e21334f3e
>
>         --- /dev/null
>
>         +++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
>
>         @@ -0,0 +1,14 @@
>
>         +// /** @file
>
>         +// CPU Memory Manager Unit library instance for PEI modules.
>
>         +//
>
>         +// CPU Memory Manager Unit library instance for PEI modules.
>
>         +//
>
>         +// 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 "CPU Memory Manager Unit library instance for PEI modules."
>
>         +
>
>         +#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for PEI modules."
>
>         diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
>
>         index 28eed85bce..178dc3c0f9 100644
>
>         --- a/UefiCpuPkg/UefiCpuPkg.dsc
>
>         +++ b/UefiCpuPkg/UefiCpuPkg.dsc
>
>         @@ -207,5 +207,9 @@
>
>             UefiCpuPkg/CpuTimerDxeRiscV64/CpuTimerDxeRiscV64.inf
>
>             UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf
>
>           
>
>         +[Components.LOONGARCH64]
>
>         +  UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>
>         +  UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>
>         +
>
>           [BuildOptions]
>
>             *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
>
> 


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115014): https://edk2.groups.io/g/devel/message/115014
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-02-02  3:50         ` Chao Li
@ 2024-02-02  4:30           ` Ni, Ray
  2024-03-01  1:26             ` Chao Li
       [not found]             ` <17B87F9FA8D0E543.14067@groups.io>
  0 siblings, 2 replies; 89+ messages in thread
From: Ni, Ray @ 2024-02-02  4:30 UTC (permalink / raw)
  To: Chao Li, devel@edk2.groups.io, lersek@redhat.com
  Cc: Dong, Eric, Kumar, Rahul R, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

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

Sure.
A local function externed by another PEIM and called from that PEIM is not a good practice.

Thanks,
Ray
From: Chao Li <lichao@loongson.cn>
Sent: Friday, February 2, 2024 11:51 AM
To: devel@edk2.groups.io; Ni, Ray <ray.ni@intel.com>; lersek@redhat.com
Cc: Dong, Eric <eric.dong@intel.com>; Kumar, Rahul R <rahul.r.kumar@intel.com>; Gerd Hoffmann <kraxel@redhat.com>; Baoqi Zhang <zhangbaoqi@loongson.cn>; Dongyan Qian <qiandongyan@loongson.cn>; Xianglai Li <lixianglai@loongson.cn>; Bibo Mao <maobibo@loongson.cn>
Subject: Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg


Hi Ray,

Thanks,
Chao
On 2024/2/2 11:33, Ni, Ray wrote:
Chao,
ConfigureMemoryManagementUnit() is not a library API anymore.
Who will call it? I don’t see any reference to this function inside the lib source.

It become a private API, called at the LoongArch virtual/physical machine PEIM, and when called it, the C file should extern this symbol.

Please refer incoming and outgoing mails, this library probably need to be refactored, sothe APIs may change, so PLS focus on this module in future and help me more, please...

Thanks,
Ray
From: Chao Li <lichao@loongson.cn><mailto:lichao@loongson.cn>
Sent: Thursday, February 1, 2024 3:58 PM
To: devel@edk2.groups.io<mailto:devel@edk2.groups.io>; lersek@redhat.com<mailto:lersek@redhat.com>
Cc: Dong, Eric <eric.dong@intel.com><mailto:eric.dong@intel.com>; Ni, Ray <ray.ni@intel.com><mailto:ray.ni@intel.com>; Kumar, Rahul R <rahul.r.kumar@intel.com><mailto:rahul.r.kumar@intel.com>; Gerd Hoffmann <kraxel@redhat.com><mailto:kraxel@redhat.com>; Baoqi Zhang <zhangbaoqi@loongson.cn><mailto:zhangbaoqi@loongson.cn>; Dongyan Qian <qiandongyan@loongson.cn><mailto:qiandongyan@loongson.cn>; Xianglai Li <lixianglai@loongson.cn><mailto:lixianglai@loongson.cn>; Bibo Mao <maobibo@loongson.cn><mailto:maobibo@loongson.cn>
Subject: Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg


Hi Lazslo,

Thanks,
Chao
On 2024/1/31 17:47, Laszlo Ersek wrote:

On 1/26/24 07:29, Chao Li wrote:

Add a new library named CpuMmuLib and add a LoongArch64 instance with in

the library.

It provides two-stage MMU libraryinstances, PEI and DXE.



BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584



Cc: Eric Dong <eric.dong@intel.com><mailto:eric.dong@intel.com>

Cc: Ray Ni <ray.ni@intel.com><mailto:ray.ni@intel.com>

Cc: Laszlo Ersek <lersek@redhat.com><mailto:lersek@redhat.com>

Cc: Rahul Kumar <rahul1.kumar@intel.com><mailto:rahul1.kumar@intel.com>

Cc: Gerd Hoffmann <kraxel@redhat.com><mailto:kraxel@redhat.com>

Signed-off-by: Chao Li <lichao@loongson.cn><mailto:lichao@loongson.cn>

Co-authored-by: Baoqi Zhang <zhangbaoqi@loongson.cn><mailto:zhangbaoqi@loongson.cn>

Co-authored-by: Dongyan Qian <qiandongyan@loongson.cn><mailto:qiandongyan@loongson.cn>

Co-authored-by: Xianglai Li <lixianglai@loongson.cn><mailto:lixianglai@loongson.cn>

Co-authored-by: Bibo Mao <maobibo@loongson.cn><mailto:maobibo@loongson.cn>

---

 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf |  36 +

 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni |  14 +

 .../CpuMmuLib/LoongArch64/CommonMmuLib.c      | 988 ++++++++++++++++++

 .../CpuMmuLib/LoongArch64/CommonMmuLib.h      |  43 +

 .../Library/CpuMmuLib/LoongArch64/Page.h      | 279 +++++

 .../CpuMmuLib/LoongArch64/PeiCpuMmuLib.c      | 178 ++++

 .../Library/CpuMmuLib/LoongArch64/Tlb.h       |  48 +

 .../CpuMmuLib/LoongArch64/TlbOperation.S      |  44 +

 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf |  44 +

 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni |  14 +

 UefiCpuPkg/UefiCpuPkg.dsc                     |   4 +

 11 files changed, 1692 insertions(+)

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf

 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni



diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf

new file mode 100644

index 0000000000..bfce3ce96d

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf

@@ -0,0 +1,36 @@

+## @file

+#  CPU Memory Map Unit DXE phase driver.

+#

+#  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                      = DxeCpuMmuLib

+  MODULE_UNI_FILE                = DxeCpuMmuLib.uni

+  FILE_GUID                      = DA8F0232-FB14-42F0-922C-63104D2C70BE



(1) This FILE_GUID was created from the FILE_GUID of

"ArmPkg/Library/ArmMmuLib/ArmMmuBaseLib.inf" by adding 1. That's not an

acceptable method for GUID generation.



A method is only acceptable for GUID generation if multiple (= an

unlimited number of) parties can execute the method at any time, and the

output remains conflict-free.



Taking an existent (known) FILE_GUID and incrementing it by 1 is not

such a method.



Please regenerate the GUID with "uuidgen".



Please also review the rest of your new GUIDs over this series (not only

FILE_GUIDs in INF files, but any other GUIDs, too).
OK, I will regenerate the GUID in next commit.









+  MODULE_TYPE                    = DXE_DRIVER

+  VERSION_STRING                 = 1.0

+  LIBRARY_CLASS                  = CpuMmuLib | DXE_DRIVER

+  CONSTRUCTOR                    = MmuInitialize

+

+#

+#  VALID_ARCHITECTURES           = LOONGARCH64

+#

+

+[Sources.LoongArch64]

+  LoongArch64/TlbOperation.S   | GCC

+  LoongArch64/CommonMmuLib.c

+  LoongArch64/Page.h

+  LoongArch64/Tlb.h

+

+[Packages]

+  MdePkg/MdePkg.dec

+  UefiCpuPkg/UefiCpuPkg.dec

+

+[LibraryClasses]

+  DebugLib

+  MemoryAllocationLib

diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni

new file mode 100644

index 0000000000..7342249516

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni

@@ -0,0 +1,14 @@

+// /** @file

+// CPU Memory Manager Unit library instance for DXE modules.

+//

+// CPU Memory Manager Unit library instance for DXE modules.

+//

+// 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 "CPU Memory Manager Unit library instance for DXE modules."

+

+#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for DXE modules."

diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c

new file mode 100644

index 0000000000..2e852c3371

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c

@@ -0,0 +1,988 @@

+/** @file

+

+  CPU Memory Map Unit Handler Library common functions.

+

+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>

+

+  SPDX-License-Identifier: BSD-2-Clause-Patent

+

+  @par Glossary:

+    - Pgd or Pgd or PGD    - Page Global Directory

+    - Pud or Pud or PUD    - Page Upper Directory

+    - Pmd or Pmd or PMD    - Page Middle Directory

+    - Pte or pte or PTE    - Page Table Entry

+    - Val or VAL or val    - Value

+    - Dir    - Directory

+**/

+#include <Uefi.h>

+#include <Library/BaseLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/CpuMmuLib.h>

+#include <Library/DebugLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Register/LoongArch64/Csr.h>

+#include "Tlb.h"

+#include "Page.h"

+

+#define SWAP_PAGE_DIR              CsrRead(LOONGARCH_CSR_PGDL)



(2) Missing space after "CsrRead".
OK.







+#define EFI_MEMORY_CACHETYPE_MASK  (EFI_MEMORY_UC  | \

+                                    EFI_MEMORY_WC  | \

+                                    EFI_MEMORY_WT  | \

+                                    EFI_MEMORY_WB  | \

+                                    EFI_MEMORY_UCE   \

+                                    )



(3) This seems to come from "ArmPkg/Include/Library/ArmLib.h"; it's not

great regardless: we shouldn't use the EFI_ prefix for symbols that are

not standard. (Put differently, EFI_ is a reserved namespace prefix for

the UEFI and PI specs.)
OK, I will rename with out EFI_perfix next time.







+

+BOOLEAN  mMmuInited = FALSE;



(4) This should be STATIC, I believe.



(5) So this is the point where I realize that the library design makes

no sense to me.



Normally you create SEC/PEI vs. DXE phase instances of a library

because, using writable global variables, you can gain performance (you

can remember the initialization) in DXE, but in SEC/PEI, you don't have

writeable global variables.



This pattern does not seem to apply here, or at least it doesn't seem to

work. Here's why:



- the variable mMmuInited and the function MmuInitialize (which contains

an assignment to the variable) are in "CommonMmuLib.c", which gets built

into both library instances. This makes no sense; that assignment cannot

work in SEC/PEI (I presume you're not going to have writeable globals --

executing from flash).



- I think you may be trying to make up for that problem by checking

SWAP_PAGE_DIR (the LOONGARCH_CSR_PGDL register) in MmuInitialize() and

MmuIsInit(). That doesn't seem right.

Yes, you are right, the PEI stage may be executed from flash, in this case, we have no way to write global variables, so we can only check whether CSR_PGDL is NULL in the DXE stage to get whether the MMU has been initialized.





- The PEI instance of the library contains an EFIAPI function called

ConfigureMemoryManagementUnit(). This function is never called in this

patch, which makes me think it's supposed to be called from driver or

application code (i.e., not from within the library itself, but from

client code). However, ConfigureMemoryManagementUnit() is also not

declared in the previous patch (in

"UefiCpuPkg/Include/Library/CpuMmuLib.h"); therefore client code cannot

reach it at all -- so that function doesn't even belong in this library.

This API was discussed with Ray in the V3 patch 13. Actually, the CpuMmuLib.h include more APIs before, Ray believed that other APIs should not be open to users or some APIs can be done by the base APIs, and in my opinion, the ConfigureMemoryManagementUnit() is a private API, so in V4, it has been removed from CpuMmuLib.h.

So what do you think? the ConfigureMemoryManagementUnit should be added back?





- MmuInitialize() doesn't actually do anything, it just checks (via the

CSR) whether some other component has already initialized the MMU

(likely with ConfigureMemoryManagementUnit()). And the sole purpose of

MmuIsInit() appears to be to return from the public library APIs

SetMemoryRegionAttributes() and GetMemoryRegionAttributes() early, if

that "other" (unknown) component has not called

ConfigureMemoryManagementUnit() yet.



So, I don't understand what you are trying to do. I could explain how to

keep the initialization logic *differences* minimal between the SEC/PEI

and the DXE library instances, but I don't understand how / when the MMU

initialization is supposed to occur in the first place.

Let's me tell you this library how to work:

In PEI stage, in addition to ensuring that the MMU is not used, the user must call the ConfigureMemoryManagementUnit toinitialization the MMU, such as filling the static page tables, set the TLB refill exception entry point, set the page size etc. the ConfigureMemoryManagementUnit is a private API but related to ARCH.

During DXE stage, this library will provide services for CpuDxe and other drivers, but almost changes to memory page attributes use the protocols provided by CpuDxe. That is why the CpuMmuLib.h only contains two APIs, it only can get/set the attribute.

In short, the PEI stage needs to initialize and set up TLB refill entry point and method(dynamically populate the TLB, keep the PA == VA), and DXE stage is provides services for changing the memory attributes.





(6) The patch is too large in general. You should construct these

library instances over multiple patches. The first patch could add some

declarations / macro definitions, such as "Page.h" and "Tlb.h". Another

patch could add the assembly language helper functions. Another patch

could add the PEI phase library instance (including the common code). A

final patch could add the DXE-phase bits.



The idea is to proceed in layers, logically building one on top of the

other. It's fine if the library doesn't build initially; there is no

attempt to build it anyway until you actually reference the INF files in

some DSC files. So please split this at least in 4 patches.
OK, I will try it.







(7) The commit message should explain the expected usage model in detail.
OK.







Best regards,

Laszlo





+

+/**

+  Check to see if mmu successfully initializes.

+

+  @param  VOID.

+

+  @retval  TRUE  Initialization has been completed.

+           FALSE Initialization did not complete.

+**/

+STATIC

+BOOLEAN

+MmuIsInit (

+  VOID

+  )

+{

+  if (mMmuInited || (SWAP_PAGE_DIR != 0)) {

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+/**

+  Iterates through the page directory to initialize it.

+

+  @param  Dst  A pointer to the directory of the page to initialize.

+  @param  Num  The number of page directories to initialize.

+  @param  Src  A pointer to the data used to initialize the page directory.

+

+  @return VOID.

+**/

+STATIC

+VOID

+PageDirInit (

+  IN VOID   *Dst,

+  IN UINTN  Num,

+  IN VOID   *Src

+  )

+{

+  UINTN  *Ptr;

+  UINTN  *End;

+  UINTN  Entry;

+

+  Entry = (UINTN)Src;

+  Ptr   = (UINTN *)Dst;

+  End   = Ptr + Num;

+

+  for ( ; Ptr < End; Ptr++) {

+    *Ptr = Entry;

+  }

+

+  return;

+}

+

+/**

+  Gets the virtual address corresponding to the page global directory table entry.

+

+  @param  Address  the virtual address for the table entry.

+

+  @retval PGD A pointer to get the table item.

+**/

+STATIC

+PGD *

+PgdOffset (

+  IN UINTN  Address

+  )

+{

+  return (PGD *)(SWAP_PAGE_DIR) + PGD_INDEX (Address);

+}

+

+/**

+  Gets the virtual address corresponding to the page upper directory table entry.

+

+  @param  Pgd  A pointer to a page global directory table entry.

+  @param  Address  the virtual address for the table entry.

+

+  @retval PUD A pointer to get the table item.

+**/

+STATIC

+PUD *

+PudOffset (

+  IN PGD    *Pgd,

+  IN UINTN  Address

+  )

+{

+  UINTN  PgdVal;

+

+  PgdVal = (UINTN)PGD_VAL (*Pgd);

+

+  return (PUD *)PgdVal + PUD_INDEX (Address);

+}

+

+/**

+  Gets the virtual address corresponding to the page middle directory table entry.

+

+  @param  Pud  A pointer to a page upper directory table entry.

+  @param  Address  the virtual address for the table entry.

+

+  @retval PMD A pointer to get the table item.

+**/

+STATIC

+PMD *

+PmdOffset (

+  IN PUD    *Pud,

+  IN UINTN  Address

+  )

+{

+  UINTN  PudVal;

+

+  PudVal = PUD_VAL (*Pud);

+

+  return (PMD *)PudVal + PMD_INDEX (Address);

+}

+

+/**

+  Gets the virtual address corresponding to the page table entry.

+

+  @param  Pmd  A pointer to a page middle directory table entry.

+  @param  Address  the virtual address for the table entry.

+

+  @retval PTE A pointer to get the table item.

+**/

+STATIC

+PTE *

+PteOffset (

+  IN PMD    *Pmd,

+  IN UINTN  Address

+  )

+{

+  UINTN  PmdVal;

+

+  PmdVal = (UINTN)PMD_VAL (*Pmd);

+

+  return (PTE *)PmdVal + PTE_INDEX (Address);

+}

+

+/**

+  Sets the value of the page table entry.

+

+  @param  Pte  A pointer to a page table entry.

+  @param  PteVal  The value of the page table entry to set.

+

+**/

+STATIC

+VOID

+SetPte (

+  IN PTE  *Pte,

+  IN PTE  PteVal

+  )

+{

+  *Pte = PteVal;

+}

+

+/**

+  Sets the value of the page global directory.

+

+  @param  Pgd  A pointer to a page global directory.

+  @param  Pud  The value of the page global directory to set.

+

+**/

+STATIC

+VOID

+SetPgd (

+  IN PGD  *Pgd,

+  IN PUD  *Pud

+  )

+{

+  *Pgd = (PGD) {

+    ((UINTN)Pud)

+  };

+}

+

+/**

+  Sets the value of the page upper directory.

+

+  @param  Pud  A pointer to a page upper directory.

+  @param  Pmd  The value of the page upper directory to set.

+

+**/

+STATIC

+VOID

+SetPud (

+  IN PUD  *Pud,

+  IN PMD  *Pmd

+  )

+{

+  *Pud = (PUD) {

+    ((UINTN)Pmd)

+  };

+}

+

+/**

+  Sets the value of the page middle directory.

+

+  @param  Pmd  A pointer to a page middle directory.

+  @param  Pte  The value of the page middle directory to set.

+

+**/

+STATIC

+VOID

+SetPmd (

+  IN PMD  *Pmd,

+  IN PTE  *Pte

+  )

+{

+  *Pmd = (PMD) {

+    ((UINTN)Pte)

+  };

+}

+

+/**

+  Free up memory space occupied by page tables.

+

+  @param  Pte  A pointer to the page table.

+

+**/

+VOID

+PteFree (

+  IN PTE  *Pte

+  )

+{

+  FreePages ((VOID *)Pte, 1);

+}

+

+/**

+  Free up memory space occupied by page middle directory.

+

+  @param  Pmd  A pointer to the page middle directory.

+

+**/

+VOID

+PmdFree (

+  IN PMD  *Pmd

+  )

+{

+  FreePages ((VOID *)Pmd, 1);

+}

+

+/**

+  Free up memory space occupied by page upper directory.

+

+  @param  Pud  A pointer to the page upper directory.

+

+**/

+VOID

+PudFree (

+  IN PUD  *Pud

+  )

+{

+  FreePages ((VOID *)Pud, 1);

+}

+

+/**

+  Requests the memory space required for the page upper directory,

+  initializes it, and places it in the specified page global directory

+

+  @param  Pgd  A pointer to the page global directory.

+

+  @retval  EFI_SUCCESS  Memory request successful.

+  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.

+**/

+STATIC

+EFI_STATUS

+PudAlloc (

+  IN PGD  *Pgd

+  )

+{

+  PUD  *Pud;

+

+  Pud = (PUD *)AllocatePages (1);

+  if (Pud == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  PageDirInit ((VOID *)Pud, ENTRYS_PER_PUD, (VOID *)INVALID_PAGE);

+

+  SetPgd (Pgd, Pud);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Requests the memory space required for the page middle directory,

+  initializes it, and places it in the specified page upper directory

+

+  @param  Pud  A pointer to the page upper directory.

+

+  @retval  EFI_SUCCESS  Memory request successful.

+  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.

+**/

+STATIC

+EFI_STATUS

+PmdAlloc (

+  IN PUD  *Pud

+  )

+{

+  PMD  *Pmd;

+

+  Pmd = (PMD *)AllocatePages (1);

+  if (!Pmd) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  PageDirInit ((VOID *)Pmd, ENTRYS_PER_PMD, (VOID *)INVALID_PAGE);

+

+  SetPud (Pud, Pmd);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Requests the memory space required for the page table,

+  initializes it, and places it in the specified page middle directory

+

+  @param  Pmd  A pointer to the page middle directory.

+

+  @retval  EFI_SUCCESS  Memory request successful.

+  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.

+**/

+STATIC

+EFI_STATUS

+PteAlloc (

+  IN PMD  *Pmd

+  )

+{

+  PTE  *Pte;

+

+  Pte = (PTE *)AllocatePages (1);

+  if (!Pte) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Pte = ZeroMem (Pte, EFI_PAGE_SIZE);

+

+  SetPmd (Pmd, Pte);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Requests the memory space required for the page upper directory,

+  initializes it, and places it in the specified page global directory,

+  and get the page upper directory entry corresponding to the virtual address.

+

+  @param  Pgd      A pointer to the page global directory.

+  @param  Address  The corresponding virtual address of the page table entry.

+

+  @retval          A pointer to the page upper directory entry. Return NULL, if

+                   allocate the memory buffer is fail.

+**/

+STATIC

+PUD *

+PudAllocGet (

+  IN PGD    *Pgd,

+  IN UINTN  Address

+  )

+{

+  EFI_STATUS  Status;

+

+  if (PGD_IS_EMPTY (*Pgd)) {

+    Status = PudAlloc (Pgd);

+    ASSERT_EFI_ERROR (Status);

+    if (EFI_ERROR (Status)) {

+      return NULL;

+    }

+  }

+

+  return PudOffset (Pgd, Address);

+}

+

+/**

+  Requests the memory space required for the page middle directory,

+  initializes it, and places it in the specified page upper directory,

+  and get the page middle directory entry corresponding to the virtual address.

+

+  @param  Pud      A pointer to the page upper directory.

+  @param  Address  The corresponding virtual address of the page table entry.

+

+  @retval          A pointer to the page middle directory entry. Return NULL, if

+                   allocate the memory buffer is fail.

+**/

+STATIC

+PMD *

+PmdAllocGet (

+  IN PUD    *Pud,

+  IN UINTN  Address

+  )

+{

+  EFI_STATUS  Status;

+

+  if (PUD_IS_EMPTY (*Pud)) {

+    Status = PmdAlloc (Pud);

+    ASSERT_EFI_ERROR (Status);

+    if (EFI_ERROR (Status)) {

+      return NULL;

+    }

+  }

+

+  return PmdOffset (Pud, Address);

+}

+

+/**

+  Requests the memory space required for the page table,

+  initializes it, and places it in the specified page middle directory,

+  and get the page table entry corresponding to the virtual address.

+

+  @param  Pmd      A pointer to the page upper directory.

+  @param  Address  The corresponding virtual address of the page table entry.

+

+  @retval          A pointer to the page table entry. Return NULL, if allocate

+                   the memory buffer is fail.

+**/

+STATIC

+PTE *

+PteAllocGet (

+  IN PMD    *Pmd,

+  IN UINTN  Address

+  )

+{

+  EFI_STATUS  Status;

+

+  if (PMD_IS_EMPTY (*Pmd)) {

+    Status = PteAlloc (Pmd);

+    ASSERT_EFI_ERROR (Status);

+    if (EFI_ERROR (Status)) {

+      return NULL;

+    }

+  }

+

+  return PteOffset (Pmd, Address);

+}

+

+/**

+  Gets the physical address of the page table entry corresponding to the specified virtual address.

+

+  @param  Address  The corresponding virtual address of the page table entry.

+

+  @retval  A pointer to the page table entry.

+  @retval  NULL

+**/

+STATIC

+PTE *

+GetPteAddress (

+  IN UINTN  Address

+  )

+{

+  PGD  *Pgd;

+  PUD  *Pud;

+  PMD  *Pmd;

+

+  Pgd = PgdOffset (Address);

+

+  if (PGD_IS_EMPTY (*Pgd)) {

+    return NULL;

+  }

+

+  Pud = PudOffset (Pgd, Address);

+

+  if (PUD_IS_EMPTY (*Pud)) {

+    return NULL;

+  }

+

+  Pmd = PmdOffset (Pud, Address);

+  if (PMD_IS_EMPTY (*Pmd)) {

+    return NULL;

+  }

+

+  if (IS_HUGE_PAGE (Pmd->PmdVal)) {

+    return ((PTE *)Pmd);

+  }

+

+  return PteOffset (Pmd, Address);

+}

+

+/**

+  Gets the Attributes of Huge Page.

+

+  @param  Pmd  A pointer to the page middle directory.

+

+  @retval     Value of Attributes.

+**/

+STATIC

+UINTN

+GetHugePageAttributes (

+  IN  PMD  *Pmd

+  )

+{

+  UINTN  Attributes;

+  UINTN  GlobalFlag;

+  UINTN  HugeVal;

+

+  HugeVal     = PMD_VAL (*Pmd);

+  Attributes  = HugeVal & (~HUGEP_PAGE_MASK);

+  GlobalFlag  = ((Attributes & (1 << PAGE_HGLOBAL_SHIFT)) >> PAGE_HGLOBAL_SHIFT) << PAGE_GLOBAL_SHIFT;

+  Attributes &= ~(1 << PAGE_HGLOBAL_SHIFT);

+  Attributes |= GlobalFlag;

+  return Attributes;

+}

+

+/**

+  Establishes a page table entry based on the specified memory region.

+

+  @param  Pmd  A pointer to the page middle directory.

+  @param  Address  The memory space start address.

+  @param  End  The end address of the memory space.

+  @param  Attributes  Memory space Attributes.

+

+  @retval     EFI_SUCCESS   The page table entry was created successfully.

+  @retval     EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.

+**/

+STATIC

+EFI_STATUS

+MemoryMapPteRange (

+  IN PMD    *Pmd,

+  IN UINTN  Address,

+  IN UINTN  End,

+  IN UINTN  Attributes

+  )

+{

+  PTE      *Pte;

+  PTE      PteVal;

+  BOOLEAN  UpDate;

+

+  Pte = PteAllocGet (Pmd, Address);

+  if (!Pte) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  DEBUG ((

+    DEBUG_INFO,

+    "%a %d Address %p End %p  Attributes %llx\n",

+    __func__,

+    __LINE__,

+    Address,

+    End,

+    Attributes

+    ));

+

+  do {

+    UpDate = FALSE;

+    PteVal = MAKE_PTE (Address, Attributes);

+

+    if ((!PTE_IS_EMPTY (*Pte)) &&

+        (PTE_VAL (*Pte) != PTE_VAL (PteVal)))

+    {

+      UpDate = TRUE;

+    }

+

+    SetPte (Pte, PteVal);

+    if (UpDate) {

+      InvalidTlb (Address);

+    }

+  } while (Pte++, Address += EFI_PAGE_SIZE, Address != End);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Convert Huge Page to Page.

+

+  @param  Pmd  A pointer to the page middle directory.

+  @param  Address  The memory space start address.

+  @param  End  The end address of the memory space.

+  @param  Attributes  Memory space Attributes.

+

+  @retval  EFI_SUCCESS   The page table entry was created successfully.

+  @retval  EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.

+**/

+STATIC

+EFI_STATUS

+ConvertHugePageToPage (

+  IN  PMD   *Pmd,

+  IN UINTN  Address,

+  IN UINTN  End,

+  IN UINTN  Attributes

+  )

+{

+  UINTN       OldAttributes;

+  UINTN       HugePageEnd;

+  UINTN       HugePageStart;

+  EFI_STATUS  Status;

+

+  Status = EFI_SUCCESS;

+

+  if ((PMD_IS_EMPTY (*Pmd)) ||

+      (!IS_HUGE_PAGE (Pmd->PmdVal)))

+  {

+    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);

+  } else {

+    OldAttributes = GetHugePageAttributes (Pmd);

+    if (Attributes == OldAttributes) {

+      return Status;

+    }

+

+    SetPmd (Pmd, (PTE *)(INVALID_PAGE));

+    HugePageStart = Address & PMD_MASK;

+    HugePageEnd   = HugePageStart + HUGE_PAGE_SIZE;

+    ASSERT (HugePageEnd >= End);

+

+    if (Address > HugePageStart) {

+      Status |= MemoryMapPteRange (Pmd, HugePageStart, Address, OldAttributes);

+    }

+

+    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);

+

+    if (End < HugePageEnd) {

+      Status |= MemoryMapPteRange (Pmd, End, HugePageEnd, OldAttributes);

+    }

+  }

+

+  return Status;

+}

+

+/**

+  Establishes a page middle directory based on the specified memory region.

+

+  @param  Pud  A pointer to the page upper directory.

+  @param  Address  The memory space start address.

+  @param  End  The end address of the memory space.

+  @param  Attributes  Memory space Attributes.

+

+  @retval     EFI_SUCCESS   The page middle directory was created successfully.

+  @retval     EFI_OUT_OF_RESOURCES  Page middle directory establishment failed due to resource exhaustion.

+**/

+STATIC

+EFI_STATUS

+MemoryMapPmdRange (

+  IN PUD    *Pud,

+  IN UINTN  Address,

+  IN UINTN  End,

+  IN UINTN  Attributes

+  )

+{

+  PMD      *Pmd;

+  UINTN    Next;

+  PTE      PteVal;

+  BOOLEAN  UpDate;

+

+  Pmd = PmdAllocGet (Pud, Address);

+  if (Pmd == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  do {

+    Next = PMD_ADDRESS_END (Address, End);

+    if (((Address & (~PMD_MASK)) == 0) &&

+        ((Next &  (~PMD_MASK)) == 0) &&

+        (PMD_IS_EMPTY (*Pmd) || IS_HUGE_PAGE (Pmd->PmdVal)))

+    {

+      UpDate = FALSE;

+      PteVal = MAKE_HUGE_PTE (Address, Attributes);

+

+      if ((!PMD_IS_EMPTY (*Pmd)) &&

+          (PMD_VAL (*Pmd) != PTE_VAL (PteVal)))

+      {

+        UpDate = TRUE;

+      }

+

+      SetPmd (Pmd, (PTE *)PteVal.PteVal);

+      if (UpDate) {

+        InvalidTlb (Address);

+      }

+    } else {

+      ConvertHugePageToPage (Pmd, Address, Next, Attributes);

+    }

+  } while (Pmd++, Address = Next, Address != End);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Establishes a page upper directory based on the specified memory region.

+

+  @param  Pgd  A pointer to the page global directory.

+  @param  Address  The memory space start address.

+  @param  End  The end address of the memory space.

+  @param  Attributes  Memory space Attributes.

+

+  @retval     EFI_SUCCESS   The page upper directory was created successfully.

+  @retval     EFI_OUT_OF_RESOURCES  Page upper directory establishment failed due to resource exhaustion.

+**/

+STATIC

+EFI_STATUS

+MemoryMapPudRange (

+  IN PGD    *Pgd,

+  IN UINTN  Address,

+  IN UINTN  End,

+  IN UINTN  Attributes

+  )

+{

+  PUD    *Pud;

+  UINTN  Next;

+

+  Pud = PudAllocGet (Pgd, Address);

+  if (Pud == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  do {

+    Next = PUD_ADDRESS_END (Address, End);

+    if (EFI_ERROR (MemoryMapPmdRange (Pud, Address, Next, Attributes))) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+  } while (Pud++, Address = Next, Address != End);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Establishes a page global directory based on the specified memory region.

+

+  @param  Start  The memory space start address.

+  @param  End  The end address of the memory space.

+  @param  Attributes  Memory space Attributes.

+

+  @retval     EFI_SUCCESS   The page global directory was created successfully.

+  @retval     EFI_OUT_OF_RESOURCES  Page global directory establishment failed due to resource exhaustion.

+**/

+STATIC

+EFI_STATUS

+MemoryMapPageRange (

+  IN UINTN  Start,

+  IN UINTN  End,

+  IN UINTN  Attributes

+  )

+{

+  PGD         *Pgd;

+  UINTN       Next;

+  UINTN       Address;

+  EFI_STATUS  Err;

+

+  Address = Start;

+

+  /* Get PGD(PTE PMD PUD PGD) in PageTables */

+  Pgd = PgdOffset (Address);

+  do {

+    Next = PGD_ADDRESS_END (Address, End);

+    /* Get Next Align Page to Map */

+    Err = MemoryMapPudRange (Pgd, Address, Next, Attributes);

+    if (Err) {

+      return Err;

+    }

+  } while (Pgd++, Address = Next, Address != End);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Page tables are established from memory-mapped tables.

+

+  @param  MemoryRegion   A pointer to a memory-mapped table entry.

+

+  @retval     EFI_SUCCESS   The page table was created successfully.

+  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.

+**/

+EFI_STATUS

+FillTranslationTable (

+  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion

+  )

+{

+  return MemoryMapPageRange (

+           MemoryRegion->VirtualBase,

+           (MemoryRegion->Length + MemoryRegion->VirtualBase),

+           MemoryRegion->Attributes

+           );

+}

+

+/**

+  Convert EFI Attributes to Loongarch Attributes.

+

+  @param[in]  EfiAttributes     Efi Attributes.

+

+  @retval  Corresponding architecture attributes.

+**/

+UINTN

+EFIAPI

+EfiAttributeConverse (

+  IN UINTN  EfiAttributes

+  )

+{

+  UINTN  LoongArchAttributes;

+

+  LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | PLV_KERNEL | PAGE_GLOBAL;

+

+  switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {

+    case EFI_MEMORY_UC:

+      LoongArchAttributes |= CACHE_SUC;

+      break;

+    case EFI_MEMORY_WC:

+      LoongArchAttributes |= CACHE_WUC;

+      break;

+    case EFI_MEMORY_WT:

+    case EFI_MEMORY_WB:

+      LoongArchAttributes |= CACHE_CC;

+      break;

+    default:

+      LoongArchAttributes |= CACHE_CC;

+      break;

+  }

+

+  // Write protection attributes

+  if (((EfiAttributes & EFI_MEMORY_RO) != 0) ||

+      ((EfiAttributes & EFI_MEMORY_WP) != 0))

+  {

+    LoongArchAttributes &= ~PAGE_DIRTY;

+  }

+

+  if ((EfiAttributes & EFI_MEMORY_RP) != 0) {

+    LoongArchAttributes |= PAGE_NO_READ;

+  }

+

+  // eXecute protection attribute

+  if ((EfiAttributes & EFI_MEMORY_XP) != 0) {

+    LoongArchAttributes |= PAGE_NO_EXEC;

+  }

+

+  return LoongArchAttributes;

+}

+

+/**

+  Finds the first of the length and memory properties of the memory region corresponding

+  to the specified base address.

+

+  @param[in]       BaseAddress       To find the base address of the memory region.

+  @param[in, out]  RegionLength      Pointer holding:

+                                      - At entry, the length of the memory region

+                                        expected to be found.

+                                      - At exit, the length of the memory region found.

+  @param[out]      RegionAttributes  Properties of the memory region found.

+

+  @retval  EFI_SUCCESS           The corresponding memory area was successfully found

+           EFI_NOT_FOUND         No memory area found

+           EFI_OUT_OF_RESOURCES  Base address or expected memory region exceeds the maximum

+                                 address.

+**/

+EFI_STATUS

+EFIAPI

+GetMemoryRegionAttributes (

+  IN     UINTN  BaseAddress,

+  IN OUT UINTN  *RegionLength,

+  OUT    UINTN  *RegionAttributes

+  )

+{

+  PTE    *Pte;

+  UINTN  Attributes;

+  UINTN  AttributesTmp;

+  UINTN  MaxAddress;

+  UINTN  EndAddress;

+  UINTN  AddSize;

+

+  if (!MmuIsInit ()) {

+    return EFI_UNSUPPORTED;

+  }

+

+  EndAddress = BaseAddress + *RegionLength;

+  MaxAddress = LShiftU64 (1ULL, MAX_VA_BITS) - 1;

+

+  // Clean the value to prepare output to find region size.

+  *RegionLength = 0x0;

+

+  if ((BaseAddress >= MaxAddress) || (EndAddress >= MaxAddress)) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Pte = GetPteAddress (BaseAddress);

+

+  if (Pte == NULL) {

+    return EFI_NOT_FOUND;

+  }

+

+  Attributes = GET_PAGE_ATTRIBUTES (*Pte);

+  if (IS_HUGE_PAGE (Pte->PteVal)) {

+    *RegionAttributes = Attributes & (~(PAGE_HUGE));

+  } else {

+    *RegionAttributes = Attributes;

+  }

+

+  do {

+    Pte = GetPteAddress (BaseAddress);

+    if (Pte == NULL) {

+      return EFI_SUCCESS;

+    }

+

+    AttributesTmp = GET_PAGE_ATTRIBUTES (*Pte);

+    if (AttributesTmp == Attributes) {

+      if (IS_HUGE_PAGE (Pte->PteVal)) {

+        AddSize = HUGE_PAGE_SIZE;

+      } else {

+        AddSize = EFI_PAGE_SIZE;

+      }

+

+      *RegionLength += AddSize;

+      BaseAddress   += AddSize;

+    } else {

+      return EFI_SUCCESS;

+    }

+  } while (BaseAddress <= EndAddress);

+

+  return EFI_SUCCESS;

+}

+

+/**

+  Sets the Attributes  of the specified memory region

+

+  @param[in]  BaseAddress    The base address of the memory region to set the Attributes.

+  @param[in]  Length         The length of the memory region to set the Attributes.

+  @param[in]  Attributes     The Attributes to be set.

+  @param[in]  AttributeMask  Mask of memory attributes to take into account.

+

+  @retval  EFI_SUCCESS    The Attributes was set successfully

+**/

+EFI_STATUS

+EFIAPI

+SetMemoryRegionAttributes (

+  IN EFI_PHYSICAL_ADDRESS  BaseAddress,

+  IN UINTN                 Length,

+  IN UINTN                 Attributes,

+  IN UINT64                AttributeMask

+  )

+{

+  EFI_STATUS  Status;

+

+  if (!MmuIsInit ()) {

+    return EFI_UNSUPPORTED;

+  }

+

+  Attributes = EfiAttributeConverse (Attributes);

+  Status     = MemoryMapPageRange (BaseAddress, BaseAddress + Length, Attributes);

+  ASSERT_EFI_ERROR (Status);

+

+  return Status;

+}

+

+/**

+  Check to see if mmu successfully initializes and saves the result.

+

+  @param[in]  ImageHandle  The firmware allocated handle for the EFI image.

+  @param[in]  SystemTable  A pointer to the EFI System Table.

+

+  @retval  RETURN_SUCCESS    Initialization succeeded.

+**/

+RETURN_STATUS

+MmuInitialize (

+  IN EFI_HANDLE        ImageHandle,

+  IN EFI_SYSTEM_TABLE  *SystemTable

+  )

+{

+  if (SWAP_PAGE_DIR != 0) {

+    mMmuInited = TRUE;

+  }

+

+  return RETURN_SUCCESS;

+}

diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h

new file mode 100644

index 0000000000..d8c922c8fa

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h

@@ -0,0 +1,43 @@

+/** @file

+

+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>

+

+  SPDX-License-Identifier: BSD-2-Clause-Patent

+

+  @par Glossary:

+    - Dir    - Directory

+**/

+

+#ifndef  MMU_LIB_CORE_H_

+#define  MMU_LIB_CORE_H_

+

+/**

+  Iterates through the page directory to initialize it.

+

+  @param  Dst  A pointer to the directory of the page to initialize.

+  @param  Num  The number of page directories to initialize.

+  @param  Src  A pointer to the data used to initialize the page directory.

+

+  @retval VOID.

+**/

+VOID

+PageDirInit (

+  IN VOID   *dest,

+  IN UINTN  Count,

+  IN VOID   *src

+  );

+

+/**

+  Page tables are established from memory-mapped tables.

+

+  @param  MemoryRegion   A pointer to a memory-mapped table entry.

+

+  @retval     EFI_SUCCESS   The page table was created successfully.

+  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.

+**/

+EFI_STATUS

+FillTranslationTable (

+  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion

+  );

+

+#endif // MMU_LIB_CORE_H_

diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h

new file mode 100644

index 0000000000..bac4f52327

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h

@@ -0,0 +1,279 @@

+/** @file

+

+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>

+

+  SPDX-License-Identifier: BSD-2-Clause-Patent

+

+  @par Glossary:

+    - Pgd or Pgd or PGD    - Page Global Directory

+    - Pud or Pud or PUD    - Page Upper Directory

+    - Pmd or Pmd or PMD    - Page Middle Directory

+    - Pte or pte or PTE    - Page Table Entry

+    - Val or VAL or val    - Value

+    - Dir    - Directory

+**/

+

+#ifndef PAGE_H_

+#define PAGE_H_

+

+#include <Library/CpuMmuLib.h>

+

+#define MAX_VA_BITS  47

+#define PGD_WIDE     (8)

+#define PUD_WIDE     (9)

+#define PMD_WIDE     (9)

+#define PTE_WIDE     (9)

+

+#define ENTRYS_PER_PGD  (1 << PGD_WIDE)

+#define ENTRYS_PER_PUD  (1 << PUD_WIDE)

+#define ENTRYS_PER_PMD  (1 << PMD_WIDE)

+#define ENTRYS_PER_PTE  (1 << PTE_WIDE)

+

+#define PGD_SHIFT  (PUD_SHIFT + PUD_WIDE)

+#define PUD_SHIFT  (PMD_SHIFT + PMD_WIDE)

+#define PMD_SHIFT  (EFI_PAGE_SHIFT + PTE_WIDE)

+#define PTE_SHIFT  (EFI_PAGE_SHIFT)

+

+#define PGD_SIZE  (1UL << PGD_SHIFT)

+#define PUD_SIZE  (1UL << PUD_SHIFT)

+#define PMD_SIZE  (1UL << PMD_SHIFT)

+

+#define PGD_MASK   (~(PGD_SIZE-1))

+#define PUD_MASK   (~(PUD_SIZE-1))

+#define PMD_MASK   (~(PMD_SIZE-1))

+#define PAGE_MASK  (~(EFI_PAGE_SIZE - 1))

+#define PFN_MASK   (~(((UINTN)(1) << (EFI_PAGE_SHIFT)) - 1) & \

+                   (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))

+

+#define HUGEP_PAGE_MASK  (~(((UINTN)(1) << (PMD_SHIFT)) - 1) & \

+                          (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))

+

+#define INVALID_PAGE  0

+

+typedef struct {

+  UINTN    PgdVal;

+} PGD;

+typedef struct {

+  UINTN    PudVal;

+} PUD;

+typedef struct {

+  UINTN    PmdVal;

+} PMD;

+typedef struct {

+  UINTN    PteVal;

+} PTE;

+

+/**

+  Gets the value of the page global directory table entry.

+

+  @param  x    Page global directory struct variables.

+

+  @retval   the value of the page global directory table entry.

+ **/

+#define PGD_VAL(x)  ((x).PgdVal)

+

+/**

+  Gets the value of the page upper directory table entry.

+

+  @param  x    Page upper directory struct variables.

+

+  @retval  the value of the page upper directory table entry.

+ **/

+#define PUD_VAL(x)  ((x).PudVal)

+

+/**

+  Gets the value of the page middle directory table entry.

+

+  @param  x    Page middle directory struct variables.

+

+  @retval  the value of the page middle directory table entry.

+ **/

+#define PMD_VAL(x)  ((x).PmdVal)

+

+/**

+  Gets the value of the page table entry.

+

+  @param  x    Page table entry struct variables.

+

+  @retval  the value of the page table entry.

+ **/

+#define PTE_VAL(x)  ((x).PteVal)

+

+#define PGD_TABLE_SIZE  (ENTRYS_PER_PGD * sizeof(PGD))

+#define PUD_TABLE_SIZE  (ENTRYS_PER_PUD * sizeof(PUD))

+#define PMD_TABLE_SIZE  (ENTRYS_PER_PMD * sizeof(PMD))

+#define PTE_TABLE_SIZE  (ENTRYS_PER_PTE * sizeof(PTE))

+

+/**

+  Gets the physical address of the record in the page table entry.

+

+  @param  x    Page table entry struct variables.

+

+  @retval  the value of the physical address.

+ **/

+#define GET_PAGE_ATTRIBUTES(x)  (UINTN) {(PTE_VAL(x) & ~PFN_MASK)}

+

+/**

+  Gets the virtual address of the next block of the specified virtual address

+  that is aligned with the size of the global page directory mapping.

+

+  @param  Address  Specifies the virtual address.

+  @param  End    The end address of the memory region.

+

+  @retval   the specified virtual address  of the next block.

+ **/

+#define PGD_ADDRESS_END(Address, End)                  \

+({                                                     \

+  UINTN Boundary = ((Address) + PGD_SIZE) & PGD_MASK;  \

+  (Boundary - 1 < (End) - 1)? Boundary: (End);         \

+})

+

+/**

+  Gets the virtual address of the next block of the specified virtual address

+  that is aligned with the size of the page upper directory mapping.

+

+  @param  Address  Specifies the virtual address.

+  @param  End    The end address of the memory region.

+

+  @retval   the specified virtual address  of the next block.

+ **/

+#define PUD_ADDRESS_END(Address, End)                  \

+({                                                     \

+  UINTN Boundary = ((Address) + PUD_SIZE) & PUD_MASK;  \

+  (Boundary - 1 < (End) - 1)? Boundary: (End);         \

+})

+

+/**

+  Gets the virtual address of the next block of the specified virtual address

+  that is aligned with the size of the page middle directory mapping.

+

+  @param  Address  Specifies the virtual address.

+  @param  End    The end address of the memory region.

+

+  @retval   the specified virtual address  of the next block.

+ **/

+#define PMD_ADDRESS_END(Address, End)                  \

+({                                                     \

+  UINTN Boundary = ((Address) + PMD_SIZE) & PMD_MASK;  \

+  (Boundary - 1 < (End) - 1)? Boundary: (End);         \

+})

+

+/**

+  Get Specifies the virtual address corresponding to the index of the page global directory table entry.

+

+  @param  Address  Specifies the virtual address.

+

+  @retval   the index of the page global directory table entry.

+ **/

+#define PGD_INDEX(Address)  (((Address) >> PGD_SHIFT) & (ENTRYS_PER_PGD-1))

+

+/**

+  Get Specifies the virtual address corresponding to the index of the page upper directory table entry.

+

+  @param  Address  Specifies the virtual address.

+  @param  End    The end address of the memory region.

+

+  @retval   the index of the page upper directory table entry.

+ **/

+#define PUD_INDEX(Address)  (((Address) >> PUD_SHIFT) & (ENTRYS_PER_PUD - 1))

+

+/**

+  Get Specifies the virtual address corresponding to the index of the page middle directory table entry.

+

+  @param  Address  Specifies the virtual address.

+

+  @retval   the index of the page middle directory table entry.

+ **/

+#define PMD_INDEX(Address)  (((Address) >> PMD_SHIFT) & (ENTRYS_PER_PMD - 1))

+

+/**

+  Get Specifies the virtual address corresponding to the index of the page table entry.

+

+  @param  Address  Specifies the virtual address.

+

+  @retval   the index of the page table entry.

+ **/

+#define PTE_INDEX(Address)  (((Address) >> EFI_PAGE_SHIFT) & (ENTRYS_PER_PTE - 1))

+

+/**

+  Calculates the value of the page table entry based on the specified virtual address and properties.

+

+  @param  Address  Specifies the virtual address.

+  @param  Attributes  Specifies the Attributes.

+

+  @retval    the value of the page table entry.

+ **/

+#define MAKE_PTE(Address, Attributes)  (PTE){((((Address) >> EFI_PAGE_SHIFT) << 12) | (Attributes))}

+

+/**

+  Get Global bit from Attributes

+

+  @param  Attributes  Specifies the Attributes.

+ * */

+#define GET_GLOBALBIT(Attributes)  ((Attributes & PAGE_GLOBAL) >> PAGE_GLOBAL_SHIFT)

+

+/**

+  Calculates the value of the Huge page table entry based on the specified virtual address and properties.

+

+  @param  Address  Specifies the virtual address.

+  @param  Attributes  Specifies the Attributes.

+

+  @retval    the value of the HUGE page table entry.

+ **/

+#define MAKE_HUGE_PTE(Address, Attributes)  (PTE){(((((Address) >> PMD_SHIFT) << PMD_SHIFT) | \

+                                             ((Attributes) | (GET_GLOBALBIT(Attributes) << PAGE_HGLOBAL_SHIFT) | \

+                                             PAGE_HUGE)))}

+

+/**

+  Check whether the large page table entry is.

+

+  @param  Val The value of the page table entry.

+

+  @retval    1   Is huge page table entry.

+  @retval    0   Isn't huge page table entry.

+**/

+#define IS_HUGE_PAGE(Val)  ((((Val) & PAGE_HUGE) == PAGE_HUGE) && \

+                            (((Val) & PAGE_HGLOBAL) == PAGE_HGLOBAL))

+

+#define HUGE_PAGE_SIZE  (PMD_SIZE)

+

+/**

+  Check that the global page directory table entry is empty.

+

+  @param  pgd   the global page directory struct variables.

+

+  @retval    1   The page table is invalid.

+  @retval    0   The page table is valid.

+**/

+#define PGD_IS_EMPTY(Val)  (PGD_VAL(Val) == INVALID_PAGE)

+

+/**

+  Check that the page upper directory table entry is empty.

+

+  @param  pud   Page upper directory struct variables.

+

+  @retval    1   The page table is invalid.

+  @retval    0   The page table is valid.

+**/

+#define PUD_IS_EMPTY(Val)  (PUD_VAL(Val) == INVALID_PAGE)

+

+/**

+  Check that the page middle directory table entry is empty.

+

+  @param  pmd   Page middle directory struct variables.

+

+  @retval    1   The page table is invalid.

+  @retval    0   The page table is valid.

+**/

+#define PMD_IS_EMPTY(Val)  (PMD_VAL(Val) == INVALID_PAGE)

+

+/**

+  Check that the page the page table entry is empty.

+

+  @param  pte   Page table entry struct variables.

+

+  @retval    1   The page table is invalid.

+  @retval    0   The page table is valid.

+**/

+#define PTE_IS_EMPTY(Val)  (!(PTE_VAL(Val) & (~PAGE_VALID)))

+#endif // PAGE_H_

diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c

new file mode 100644

index 0000000000..c214e8d847

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c

@@ -0,0 +1,178 @@

+/** @file

+  CPU Memory Map Unit PEI phase driver.

+

+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>

+

+  SPDX-License-Identifier: BSD-2-Clause-Patent

+

+  @par Glossary:

+    - Tlb      - Translation Lookaside Buffer

+**/

+

+#include <Uefi.h>

+#include <Library/BaseLib.h>

+#include <Library/BaseMemoryLib.h>

+#include <Library/CacheMaintenanceLib.h>

+#include <Library/CpuMmuLib.h>

+#include <Library/DebugLib.h>

+#include <Library/MemoryAllocationLib.h>

+#include <Library/PcdLib.h>

+#include <Register/LoongArch64/Csr.h>

+

+#include "Page.h"

+#include "Tlb.h"

+#include "CommonMmuLib.h"

+

+//

+// For coding convenience, define the maximum valid

+// LoongArch exception.

+// Since UEFI V2.11, it will be present in DebugSupport.h.

+//

+#define MAX_LOONGARCH_EXCEPTION  64

+

+/**

+  Create a page table and initialize the memory management unit(MMU).

+

+  @param[in]   MemoryTable           A pointer to a memory ragion table.

+  @param[out]  TranslationTableBase  A pointer to a translation table base address.

+  @param[out]  TranslationTableSize  A pointer to a translation table base size.

+

+  @retval  EFI_SUCCESS                Configure MMU successfully.

+           EFI_INVALID_PARAMETER      MemoryTable is NULL.

+           EFI_UNSUPPORTED            Out of memory space or size not aligned.

+**/

+EFI_STATUS

+EFIAPI

+ConfigureMemoryManagementUnit (

+  IN  MEMORY_REGION_DESCRIPTOR  *MemoryTable,

+  OUT VOID                      **TranslationTableBase OPTIONAL,

+  OUT UINTN                     *TranslationTableSize  OPTIONAL

+  )

+{

+  PGD            *SwapperPageDir;

+  UINTN          PgdShift;

+  UINTN          PgdWide;

+  UINTN          PudShift;

+  UINTN          PudWide;

+  UINTN          PmdShift;

+  UINTN          PmdWide;

+  UINTN          PteShift;

+  UINTN          PteWide;

+  UINTN          Length;

+  UINTN          TlbReEntry;

+  UINTN          TlbReEntryOffset;

+  UINTN          Remaining;

+  RETURN_STATUS  Status;

+

+  SwapperPageDir = NULL;

+  PgdShift       = PGD_SHIFT;

+  PgdWide        = PGD_WIDE;

+  PudShift       = PUD_SHIFT;

+  PudWide        = PUD_WIDE;

+  PmdShift       = PMD_SHIFT;

+  PmdWide        = PMD_WIDE;

+  PteShift       = PTE_SHIFT;

+  PteWide        = PTE_WIDE;

+

+  if (MemoryTable == NULL) {

+    ASSERT (MemoryTable != NULL);

+    return EFI_INVALID_PARAMETER;

+  }

+

+  SwapperPageDir = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));

+  ZeroMem (SwapperPageDir, PGD_TABLE_SIZE);

+

+  if (SwapperPageDir == NULL) {

+    goto FreeTranslationTable;

+  }

+

+  CsrWrite (LOONGARCH_CSR_PGDL, (UINTN)SwapperPageDir);

+

+  while (MemoryTable->Length != 0) {

+    DEBUG ((

+      DEBUG_INFO,

+      "%a %d VirtualBase %p VirtualEnd %p Attributes %p .\n",

+      __func__,

+      __LINE__,

+      MemoryTable->VirtualBase,

+      (MemoryTable->Length + MemoryTable->VirtualBase),

+      MemoryTable->Attributes

+      ));

+

+    Status = FillTranslationTable (MemoryTable);

+    if (EFI_ERROR (Status)) {

+      goto FreeTranslationTable;

+    }

+

+    MemoryTable++;

+  }

+

+  //

+  // TLB Re-entry address at the end of exception vector, a vector is up to 512 bytes,

+  // so the starting address is: total exception vector size + total interrupt vector size + base.

+  // The total size of TLB handler and exception vector size and interrupt vector size should not

+  // be lager than 64KB.

+  //

+  Length           = (UINTN)HandleTlbRefillEnd - (UINTN)HandleTlbRefillStart;

+  TlbReEntryOffset = (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * 512;

+  Remaining        = TlbReEntryOffset % SIZE_4KB;

+  if (Remaining != 0x0) {

+    TlbReEntryOffset += (SIZE_4KB - Remaining);

+  }

+

+  TlbReEntry = PcdGet64 (PcdCpuExceptionVectorBaseAddress) + TlbReEntryOffset;

+  if ((TlbReEntryOffset + Length) > SIZE_64KB) {

+    goto FreeTranslationTable;

+  }

+

+  //

+  // Ensure that TLB refill exception base address alignment is equals to 4KB and is valid.

+  //

+  if (TlbReEntry & (SIZE_4KB - 1)) {

+    goto FreeTranslationTable;

+  }

+

+  CopyMem ((VOID *)TlbReEntry, HandleTlbRefillStart, Length);

+  InvalidateInstructionCacheRange ((VOID *)(UINTN)HandleTlbRefillStart, Length);

+

+  DEBUG ((

+    DEBUG_INFO,

+    "%a  %d PteShift %d PteWide %d PmdShift %d PmdWide %d PudShift %d PudWide %d PgdShift %d PgdWide %d.\n",

+    __func__,

+    __LINE__,

+    PteShift,

+    PteWide,

+    PmdShift,

+    PmdWide,

+    PudShift,

+    PudWide,

+    PgdShift,

+    PgdWide

+    ));

+

+  //

+  // Set the address of TLB refill exception handler

+  //

+  SetTlbRebaseAddress ((UINTN)TlbReEntry);

+

+  //

+  // Set page size

+  //

+  CsrXChg (LOONGARCH_CSR_TLBIDX, (DEFAULT_PAGE_SIZE << CSR_TLBIDX_SIZE), CSR_TLBIDX_SIZE_MASK);

+  CsrWrite (LOONGARCH_CSR_STLBPGSIZE, DEFAULT_PAGE_SIZE);

+  CsrXChg (LOONGARCH_CSR_TLBREHI, (DEFAULT_PAGE_SIZE << CSR_TLBREHI_PS_SHIFT), CSR_TLBREHI_PS);

+

+  CsrWrite (LOONGARCH_CSR_PWCTL0, (PteShift | PteWide << 5 | PmdShift << 10 | PmdWide << 15 | PudShift << 20 | PudWide << 25));

+  CsrWrite (LOONGARCH_CSR_PWCTL1, (PgdShift | PgdWide << 6));

+

+  DEBUG ((DEBUG_INFO, "%a %d Enable Mmu Start PageBassAddress %p.\n", __func__, __LINE__, SwapperPageDir));

+

+  return EFI_SUCCESS;

+

+FreeTranslationTable:

+  if (SwapperPageDir != NULL) {

+    FreePages (SwapperPageDir, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));

+  }

+

+  return EFI_UNSUPPORTED;

+}

diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h

new file mode 100644

index 0000000000..9a681ce8e1

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h

@@ -0,0 +1,48 @@

+/** @file

+

+  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>

+

+  SPDX-License-Identifier: BSD-2-Clause-Patent

+

+**/

+

+#ifndef TLB_H_

+#define TLB_H_

+

+/**

+  Invalid corresponding TLB entries are based on the address given

+

+  @param Address The address corresponding to the invalid page table entry

+

+  @retval  none

+**/

+VOID

+InvalidTlb (

+  UINTN  Address

+  );

+

+/**

+  TLB refill handler start.

+

+  @param  none

+

+  @retval none

+**/

+VOID

+HandleTlbRefillStart (

+  VOID

+  );

+

+/**

+  TLB refill handler end.

+

+  @param  none

+

+  @retval none

+**/

+VOID

+HandleTlbRefillEnd (

+  VOID

+  );

+

+#endif // TLB_H_

diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S

new file mode 100644

index 0000000000..c9a8c16336

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S

@@ -0,0 +1,44 @@

+#------------------------------------------------------------------------------

+#

+# TLB operation functions

+#

+# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>

+#

+# SPDX-License-Identifier: BSD-2-Clause-Patent

+#

+#-----------------------------------------------------------------------------

+

+#include <Register/LoongArch64/Csr.h>

+

+ASM_GLOBAL ASM_PFX(HandleTlbRefillStart)

+ASM_GLOBAL ASM_PFX(HandleTlbRefillEnd)

+ASM_GLOBAL ASM_PFX(InvalidTlb)

+

+#

+#  Refill the page table.

+#  @param  VOID

+#  @retval  VOID

+#

+ASM_PFX(HandleTlbRefillStart):

+  csrwr   $t0, LOONGARCH_CSR_TLBRSAVE

+  csrrd   $t0, LOONGARCH_CSR_PGD

+  lddir   $t0, $t0, 3   #Put pud BaseAddress into T0

+  lddir   $t0, $t0, 2   #Put pmd BaseAddress into T0

+  lddir   $t0, $t0, 1   #Put pte BaseAddress into T0

+  ldpte   $t0, 0

+  ldpte   $t0, 1

+  tlbfill   // refill hi,lo0,lo1

+  csrrd   $t0, LOONGARCH_CSR_TLBRSAVE

+  ertn

+ASM_PFX(HandleTlbRefillEnd):

+

+#

+# Invalid corresponding TLB entries are based on the address given

+# @param a0 The address corresponding to the invalid page table entry

+# @retval  none

+#

+ASM_PFX(InvalidTlb):

+    invtlb  INVTLB_ADDR_GTRUE_OR_ASID, $zero, $a0

+    jirl    $zero, $ra, 0

+

+    .end

diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf

new file mode 100644

index 0000000000..45b15db4c9

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf

@@ -0,0 +1,44 @@

+## @file

+#  CPU Memory Map Unit PEI phase driver.

+#

+#  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                      = PeiCpuMmuLib

+  MODULE_UNI_FILE                = PeiCpuMmuLib.uni

+  FILE_GUID                      = F67EB983-AC2A-7550-AB69-3BC51A1C895B

+  MODULE_TYPE                    = PEIM

+  VERSION_STRING                 = 1.0

+  LIBRARY_CLASS                  = CpuMmuLib | SEC PEIM

+

+#

+#  VALID_ARCHITECTURES           = LOONGARCH64

+#

+

+[Sources.LoongArch64]

+  LoongArch64/TlbOperation.S   | GCC

+  LoongArch64/CommonMmuLib.c

+  LoongArch64/PeiCpuMmuLib.c

+  LoongArch64/CommonMmuLib.h

+  LoongArch64/Tlb.h

+  LoongArch64/Page.h

+

+[Packages]

+  MdePkg/MdePkg.dec

+  MdeModulePkg/MdeModulePkg.dec

+  UefiCpuPkg/UefiCpuPkg.dec

+

+[PCD]

+  gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask

+  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress

+

+[LibraryClasses]

+  CacheMaintenanceLib

+  DebugLib

+  MemoryAllocationLib

+  PcdLib

diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni

new file mode 100644

index 0000000000..3e21334f3e

--- /dev/null

+++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni

@@ -0,0 +1,14 @@

+// /** @file

+// CPU Memory Manager Unit library instance for PEI modules.

+//

+// CPU Memory Manager Unit library instance for PEI modules.

+//

+// 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 "CPU Memory Manager Unit library instance for PEI modules."

+

+#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for PEI modules."

diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc

index 28eed85bce..178dc3c0f9 100644

--- a/UefiCpuPkg/UefiCpuPkg.dsc

+++ b/UefiCpuPkg/UefiCpuPkg.dsc

@@ -207,5 +207,9 @@

   UefiCpuPkg/CpuTimerDxeRiscV64/CpuTimerDxeRiscV64.inf

   UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf



+[Components.LOONGARCH64]

+  UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf

+  UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf

+

 [BuildOptions]

   *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES











-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115015): https://edk2.groups.io/g/devel/message/115015
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 12/37] UefiCpuPkg: Add CPU exception library for LoongArch
  2024-02-02  3:44     ` Chao Li
@ 2024-02-02  4:30       ` Ni, Ray
  0 siblings, 0 replies; 89+ messages in thread
From: Ni, Ray @ 2024-02-02  4:30 UTC (permalink / raw)
  To: Chao Li, devel@edk2.groups.io
  Cc: Dong, Eric, Kumar, Rahul R, Gerd Hoffmann, Baoqi Zhang

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

The change can be either a separate patch, or be included in another patch that uses the PCD.

Thanks,
Ray
From: Chao Li <lichao@loongson.cn>
Sent: Friday, February 2, 2024 11:44 AM
To: devel@edk2.groups.io; Ni, Ray <ray.ni@intel.com>
Cc: Dong, Eric <eric.dong@intel.com>; Kumar, Rahul R <rahul.r.kumar@intel.com>; Gerd Hoffmann <kraxel@redhat.com>; Baoqi Zhang <zhangbaoqi@loongson.cn>
Subject: Re: [edk2-devel] [PATCH v8 12/37] UefiCpuPkg: Add CPU exception library for LoongArch


Hi Ray,

Thanks,
Chao
On 2024/2/2 11:30, Ni, Ray wrote:



1. Most of INF changes are not necessary for x86. Can you avoid them?
I just sorted them alphabetically, do you think it is unnecessary? If so, I will remove the changes in other patches in next time.






+  ## This PCD Contains the pointer to a CPU exception vector base address.

+  # @Prompt The pointer to a CPU exception vector base address.

+

gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress|0x0|UINT

64|0x60000022

+



2. I do not see any reference to the above PCD in source/INF files.

Any mistake here?
This PCD is used in CpuDxe and MmuLib, among others. Do you think this change should be made as a separate patch?















-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115016): https://edk2.groups.io/g/devel/message/115016
Mute This Topic: https://groups.io/mt/103971651/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-02-01 19:36           ` Pedro Falcato
  2024-02-01 23:02             ` Laszlo Ersek
@ 2024-02-02 15:14             ` Leif Lindholm
  2024-02-04  2:58               ` Chao Li
       [not found]               ` <17B0898B4883051D.13964@groups.io>
  1 sibling, 2 replies; 89+ messages in thread
From: Leif Lindholm @ 2024-02-02 15:14 UTC (permalink / raw)
  To: Pedro Falcato, Chao Li
  Cc: devel, Laszlo Ersek, Eric Dong, Ray Ni, Rahul Kumar,
	Gerd Hoffmann, Baoqi Zhang, Dongyan Qian, Xianglai Li, Bibo Mao,
	Andrew Fish, Kinney, Michael D

On 2024-02-01 19:36, Pedro Falcato wrote:
> On Thu, Feb 1, 2024 at 3:05 AM Chao Li <lichao@loongson.cn> wrote:
>>
>> Hi Pedro and Laszlo,
>>
>> Part of the code in this patch is indeed quoted from the Linux kernel, and do you think it is inapproparate? If so, we need to refactor this module, what are you suggests with the refactoring? Just remove the unused logic from the Kernel code and keep the logic good or refactor from scratch?
> 
> +CC stewards
> 
> Disclaimer: I'm not a lawyer
> 
> It is wildly inappropriate. All of the code was clearly inspired by
> GPL and derives from the Linux GPL code, it's not just unused logic.
> You should triple check *every other patch* you've sent out for these
> kinds of GPL violations.

I want to highlight
https://github.com/tianocore/edk2/blob/master/ReadMe.rst?plain=1#L177

Chao, by adding your Signed-off-by to any patch and sending it out, you 
certify that:

(a) The contribution was created in whole or in part by me and I
     have the right to submit it under the open source license
     indicated in the file; or

(b) The contribution is based upon previous work that, to the best
     of my knowledge, is covered under an appropriate open source
     license and I have the right under that license to submit that
     work with modifications, whether created in whole or in part
     by me, under the same open source license (unless I am
     permitted to submit under a different license), as indicated
     in the file; or

(c) The contribution was provided directly to me by some other
     person who certified (a), (b) or (c) and I have not modified
     it.

(d) I understand and agree that this project and the contribution
     are public and that a record of the contribution (including all
     personal information I submit with it, including my sign-off) is
     maintained indefinitely and may be redistributed consistent with
     this project or the open source license(s) involved.

Now, that's a bunch of legalese, but it matters.
Mistakes happen, but it would have been a massive headache if this had 
been merged and *then* we found out about this. The *best case* scenario 
would have been that we would have been forced to revert the whole set.

I wouldn't say you need to "triple check every patch", but I would say 
you need to re-evaluate the existing patches based on this new 
information you have learned. So that once you resubmit a version as per 
Laszlos comments in separate email, you are comfortable that the whole 
submission conforms with the DCO.
And if you are unsure - ask. That's never wrong.

Pedro - many thanks for this. Owe you one.

/
     Leif

> There's another way of writing this sort of code (that doesn't involve
> all the Linux mm craziness) but I don't know if changing strategies
> would be considered getting rid of any shadow of GPL/IP violation.
> 
> (As a side note, I don't really understand IP in the software world.
> If you work, say, on GPL software for a moment in time, are you always
> going to be "GPL-tainted"? Surely not? Most people in the industry
> I've talked to about this say that, yeah, no, corps don't expect that.
> But no one really seems to have drawn a line between OK and not-OK,
> but rather "please please don't sue us". And in this case I don't know
> (but I suspect it'd be uncomfortable) for someone to redesign a
> solution right away, after being "tainted". Anyway, tough problem, and
> IANAL :/)
> 



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115059): https://edk2.groups.io/g/devel/message/115059
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-02-02 15:14             ` Leif Lindholm
@ 2024-02-04  2:58               ` Chao Li
       [not found]               ` <17B0898B4883051D.13964@groups.io>
  1 sibling, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-02-04  2:58 UTC (permalink / raw)
  To: Leif Lindholm, Pedro Falcato
  Cc: devel, Laszlo Ersek, Eric Dong, Ray Ni, Rahul Kumar,
	Gerd Hoffmann, Baoqi Zhang, Dongyan Qian, Xianglai Li, Bibo Mao,
	Andrew Fish, Kinney, Michael D

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

Hi Leif and Pedro,


Thanks,
Chao
On 2024/2/2 23:14, Leif Lindholm wrote:
> On 2024-02-01 19:36, Pedro Falcato wrote:
>> On Thu, Feb 1, 2024 at 3:05 AM Chao Li <lichao@loongson.cn> wrote:
>>>
>>> Hi Pedro and Laszlo,
>>>
>>> Part of the code in this patch is indeed quoted from the Linux 
>>> kernel, and do you think it is inapproparate? If so, we need to 
>>> refactor this module, what are you suggests with the refactoring? 
>>> Just remove the unused logic from the Kernel code and keep the logic 
>>> good or refactor from scratch?
>>
>> +CC stewards
>>
>> Disclaimer: I'm not a lawyer
>>
>> It is wildly inappropriate. All of the code was clearly inspired by
>> GPL and derives from the Linux GPL code, it's not just unused logic.
>> You should triple check *every other patch* you've sent out for these
>> kinds of GPL violations.
>
> I want to highlight
> https://github.com/tianocore/edk2/blob/master/ReadMe.rst?plain=1#L177
>
> Chao, by adding your Signed-off-by to any patch and sending it out, 
> you certify that:
>
> (a) The contribution was created in whole or in part by me and I
>     have the right to submit it under the open source license
>     indicated in the file; or
>
> (b) The contribution is based upon previous work that, to the best
>     of my knowledge, is covered under an appropriate open source
>     license and I have the right under that license to submit that
>     work with modifications, whether created in whole or in part
>     by me, under the same open source license (unless I am
>     permitted to submit under a different license), as indicated
>     in the file; or
>
> (c) The contribution was provided directly to me by some other
>     person who certified (a), (b) or (c) and I have not modified
>     it.
>
> (d) I understand and agree that this project and the contribution
>     are public and that a record of the contribution (including all
>     personal information I submit with it, including my sign-off) is
>     maintained indefinitely and may be redistributed consistent with
>     this project or the open source license(s) involved.
>
> Now, that's a bunch of legalese, but it matters.
> Mistakes happen, but it would have been a massive headache if this had 
> been merged and *then* we found out about this. The *best case* 
> scenario would have been that we would have been forced to revert the 
> whole set.
>
> I wouldn't say you need to "triple check every patch", but I would say 
> you need to re-evaluate the existing patches based on this new 
> information you have learned. So that once you resubmit a version as 
> per Laszlos comments in separate email, you are comfortable that the 
> whole submission conforms with the DCO.
> And if you are unsure - ask. That's never wrong.
>
> Pedro - many thanks for this. Owe you one.
Of couse, I will study the entries you are referring to and I'm sure 
that the Part 1 series is full compliance with terms, and I will 
double/triple check them when I sumbit the Part 2, thank you for 
spotting this and preventing me form making a mistake.
>
> /
>     Leif
>
>> There's another way of writing this sort of code (that doesn't involve
>> all the Linux mm craziness) but I don't know if changing strategies
>> would be considered getting rid of any shadow of GPL/IP violation.
>>
>> (As a side note, I don't really understand IP in the software world.
>> If you work, say, on GPL software for a moment in time, are you always
>> going to be "GPL-tainted"? Surely not? Most people in the industry
>> I've talked to about this say that, yeah, no, corps don't expect that.
>> But no one really seems to have drawn a line between OK and not-OK,
>> but rather "please please don't sue us". And in this case I don't know
>> (but I suspect it'd be uncomfortable) for someone to redesign a
>> solution right away, after being "tainted". Anyway, tough problem, and
>> IANAL :/)
>>


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115077): https://edk2.groups.io/g/devel/message/115077
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
       [not found]               ` <17B0898B4883051D.13964@groups.io>
@ 2024-02-06  2:57                 ` Chao Li
  2024-02-06 14:32                   ` Laszlo Ersek
  0 siblings, 1 reply; 89+ messages in thread
From: Chao Li @ 2024-02-06  2:57 UTC (permalink / raw)
  To: Pedro Falcato, Laszlo Ersek
  Cc: devel, Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao, Andrew Fish,
	Kinney, Michael D, Leif Lindholm

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

Hi Pedro, Laszlo,

I have double-check this patch and I'd like to point out the following:

1. Some of the code in this patch actually refers to the Linux kernel. 
But only used the name of kernel code , such as PUD, PMD, PTE, Swapper 
Page Dir, etc.

2. Refer to the LoongArch TLB rules, if we use the multi-level page 
table to search and filling, the usual logic is as follows:

     a. Get the first level page table, called PGD, in reigster PGDL or 
PGDH.

     b. PGD will index the 2st Dir, and the 2st Dir will index the 3st 
Dir, and so on.

     c. We would like to use the four-level page table, because if we 
use two- or three-level page tables, the page tables will larger and 
must be continuous, so we think the four-level is better(Linux kernel 
also uses the four-level page tables).

     d. LoongArch paging logic is shown in the image below(URL: 
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#memory-management-of-page-table-mapping):

Referring to the above, should I use the old logic, rewrite the Linux 
kernel-like code to EFI style and clean up some unused code and redesign 
the PEI library, it that OK?


Thanks,
Chao
On 2024/2/4 10:58, Chao Li wrote:
>
> Hi Leif and Pedro,
>
> On 2024/2/2 23:14, Leif Lindholm wrote:
>> On 2024-02-01 19:36, Pedro Falcato wrote:
>>> On Thu, Feb 1, 2024 at 3:05 AM Chao Li <lichao@loongson.cn> wrote:
>>>>
>>>> Hi Pedro and Laszlo,
>>>>
>>>> Part of the code in this patch is indeed quoted from the Linux 
>>>> kernel, and do you think it is inapproparate? If so, we need to 
>>>> refactor this module, what are you suggests with the refactoring? 
>>>> Just remove the unused logic from the Kernel code and keep the 
>>>> logic good or refactor from scratch?
>>>
>>> +CC stewards
>>>
>>> Disclaimer: I'm not a lawyer
>>>
>>> It is wildly inappropriate. All of the code was clearly inspired by
>>> GPL and derives from the Linux GPL code, it's not just unused logic.
>>> You should triple check *every other patch* you've sent out for these
>>> kinds of GPL violations.
>>
>> I want to highlight
>> https://github.com/tianocore/edk2/blob/master/ReadMe.rst?plain=1#L177
>>
>> Chao, by adding your Signed-off-by to any patch and sending it out, 
>> you certify that:
>>
>> (a) The contribution was created in whole or in part by me and I
>>     have the right to submit it under the open source license
>>     indicated in the file; or
>>
>> (b) The contribution is based upon previous work that, to the best
>>     of my knowledge, is covered under an appropriate open source
>>     license and I have the right under that license to submit that
>>     work with modifications, whether created in whole or in part
>>     by me, under the same open source license (unless I am
>>     permitted to submit under a different license), as indicated
>>     in the file; or
>>
>> (c) The contribution was provided directly to me by some other
>>     person who certified (a), (b) or (c) and I have not modified
>>     it.
>>
>> (d) I understand and agree that this project and the contribution
>>     are public and that a record of the contribution (including all
>>     personal information I submit with it, including my sign-off) is
>>     maintained indefinitely and may be redistributed consistent with
>>     this project or the open source license(s) involved.
>>
>> Now, that's a bunch of legalese, but it matters.
>> Mistakes happen, but it would have been a massive headache if this 
>> had been merged and *then* we found out about this. The *best case* 
>> scenario would have been that we would have been forced to revert the 
>> whole set.
>>
>> I wouldn't say you need to "triple check every patch", but I would 
>> say you need to re-evaluate the existing patches based on this new 
>> information you have learned. So that once you resubmit a version as 
>> per Laszlos comments in separate email, you are comfortable that the 
>> whole submission conforms with the DCO.
>> And if you are unsure - ask. That's never wrong.
>>
>> Pedro - many thanks for this. Owe you one.
> Of couse, I will study the entries you are referring to and I'm sure 
> that the Part 1 series is full compliance with terms, and I will 
> double/triple check them when I sumbit the Part 2, thank you for 
> spotting this and preventing me form making a mistake.
>>
>> /
>>     Leif
>>
>>> There's another way of writing this sort of code (that doesn't involve
>>> all the Linux mm craziness) but I don't know if changing strategies
>>> would be considered getting rid of any shadow of GPL/IP violation.
>>>
>>> (As a side note, I don't really understand IP in the software world.
>>> If you work, say, on GPL software for a moment in time, are you always
>>> going to be "GPL-tainted"? Surely not? Most people in the industry
>>> I've talked to about this say that, yeah, no, corps don't expect that.
>>> But no one really seems to have drawn a line between OK and not-OK,
>>> but rather "please please don't sue us". And in this case I don't know
>>> (but I suspect it'd be uncomfortable) for someone to redesign a
>>> solution right away, after being "tainted". Anyway, tough problem, and
>>> IANAL :/)
>>>
> 


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115141): https://edk2.groups.io/g/devel/message/115141
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



[-- Attachment #2.1: Type: text/html, Size: 9047 bytes --]

[-- Attachment #2.2: 18PR0Yn00tzXf3Dn.png --]
[-- Type: image/png, Size: 153048 bytes --]

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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-02-06  2:57                 ` Chao Li
@ 2024-02-06 14:32                   ` Laszlo Ersek
  2024-02-06 16:45                     ` Pedro Falcato
  0 siblings, 1 reply; 89+ messages in thread
From: Laszlo Ersek @ 2024-02-06 14:32 UTC (permalink / raw)
  To: devel, lichao, Pedro Falcato
  Cc: Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao, Andrew Fish,
	Kinney, Michael D, Leif Lindholm

On 2/6/24 03:57, Chao Li wrote:
> Hi Pedro, Laszlo,
> 
> I have double-check this patch and I'd like to point out the following:
> 
> 1. Some of the code in this patch actually refers to the Linux kernel.
> But only used the name of kernel code , such as PUD, PMD, PTE, Swapper
> Page Dir, etc.
> 
> 2. Refer to the LoongArch TLB rules, if we use the multi-level page
> table to search and filling, the usual logic is as follows:
> 
>     a. Get the first level page table, called PGD, in reigster PGDL or PGDH.
> 
>     b. PGD will index the 2st Dir, and the 2st Dir will index the 3st
> Dir, and so on.
> 
>     c. We would like to use the four-level page table, because if we use
> two- or three-level page tables, the page tables will larger and must be
> continuous, so we think the four-level is better(Linux kernel also uses
> the four-level page tables).
> 
>     d. LoongArch paging logic is shown in the image below(URL:
> https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#memory-management-of-page-table-mapping):
> 
> Referring to the above, should I use the old logic, rewrite the Linux
> kernel-like code to EFI style and clean up some unused code and redesign
> the PEI library, it that OK?

I don't like the expression "rewrite the Linux kernel-like code to EFI
style".

You should not start with, or incorporate, anything that comes from
Linux. Whatever you do, make sure it is not a derivative of Linux. Don't
*derive* your edk2 contribution from Linux code.

I think you are fine to implement any paging logic you prefer, as long
as you start with an empty source file, and consult only the public
LoongArch architecture specs, plus the rest of edk2 while writing the code.

Consider the current source files from this patch "tainted". I don't
encourage a piecemeal approach on this, "replace this", "rewrite that".
The files look like they are a derivative of Linux code, so any further
modifications in these files will just result in more derived code.
Let's not play "where does this line come from" games. Scrap it all;
implement it again from the ground up, using only the public arch specs.

Just my opinion.
Laszlo


> 
> 
> Thanks,
> Chao
> On 2024/2/4 10:58, Chao Li wrote:
>>
>> Hi Leif and Pedro,
>>
>> On 2024/2/2 23:14, Leif Lindholm wrote:
>>> On 2024-02-01 19:36, Pedro Falcato wrote:
>>>> On Thu, Feb 1, 2024 at 3:05 AM Chao Li <lichao@loongson.cn> wrote:
>>>>>
>>>>> Hi Pedro and Laszlo,
>>>>>
>>>>> Part of the code in this patch is indeed quoted from the Linux
>>>>> kernel, and do you think it is inapproparate? If so, we need to
>>>>> refactor this module, what are you suggests with the refactoring?
>>>>> Just remove the unused logic from the Kernel code and keep the
>>>>> logic good or refactor from scratch?
>>>>
>>>> +CC stewards
>>>>
>>>> Disclaimer: I'm not a lawyer
>>>>
>>>> It is wildly inappropriate. All of the code was clearly inspired by
>>>> GPL and derives from the Linux GPL code, it's not just unused logic.
>>>> You should triple check *every other patch* you've sent out for these
>>>> kinds of GPL violations.
>>>
>>> I want to highlight
>>> https://github.com/tianocore/edk2/blob/master/ReadMe.rst?plain=1#L177
>>>
>>> Chao, by adding your Signed-off-by to any patch and sending it out,
>>> you certify that:
>>>
>>> (a) The contribution was created in whole or in part by me and I
>>>     have the right to submit it under the open source license
>>>     indicated in the file; or
>>>
>>> (b) The contribution is based upon previous work that, to the best
>>>     of my knowledge, is covered under an appropriate open source
>>>     license and I have the right under that license to submit that
>>>     work with modifications, whether created in whole or in part
>>>     by me, under the same open source license (unless I am
>>>     permitted to submit under a different license), as indicated
>>>     in the file; or
>>>
>>> (c) The contribution was provided directly to me by some other
>>>     person who certified (a), (b) or (c) and I have not modified
>>>     it.
>>>
>>> (d) I understand and agree that this project and the contribution
>>>     are public and that a record of the contribution (including all
>>>     personal information I submit with it, including my sign-off) is
>>>     maintained indefinitely and may be redistributed consistent with
>>>     this project or the open source license(s) involved.
>>>
>>> Now, that's a bunch of legalese, but it matters.
>>> Mistakes happen, but it would have been a massive headache if this
>>> had been merged and *then* we found out about this. The *best case*
>>> scenario would have been that we would have been forced to revert the
>>> whole set.
>>>
>>> I wouldn't say you need to "triple check every patch", but I would
>>> say you need to re-evaluate the existing patches based on this new
>>> information you have learned. So that once you resubmit a version as
>>> per Laszlos comments in separate email, you are comfortable that the
>>> whole submission conforms with the DCO.
>>> And if you are unsure - ask. That's never wrong.
>>>
>>> Pedro - many thanks for this. Owe you one.
>> Of couse, I will study the entries you are referring to and I'm sure
>> that the Part 1 series is full compliance with terms, and I will
>> double/triple check them when I sumbit the Part 2, thank you for
>> spotting this and preventing me form making a mistake.
>>>
>>> /
>>>     Leif
>>>
>>>> There's another way of writing this sort of code (that doesn't involve
>>>> all the Linux mm craziness) but I don't know if changing strategies
>>>> would be considered getting rid of any shadow of GPL/IP violation.
>>>>
>>>> (As a side note, I don't really understand IP in the software world.
>>>> If you work, say, on GPL software for a moment in time, are you always
>>>> going to be "GPL-tainted"? Surely not? Most people in the industry
>>>> I've talked to about this say that, yeah, no, corps don't expect that.
>>>> But no one really seems to have drawn a line between OK and not-OK,
>>>> but rather "please please don't sue us". And in this case I don't know
>>>> (but I suspect it'd be uncomfortable) for someone to redesign a
>>>> solution right away, after being "tainted". Anyway, tough problem, and
>>>> IANAL :/)
>>>>
>> 



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115164): https://edk2.groups.io/g/devel/message/115164
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-02-06 14:32                   ` Laszlo Ersek
@ 2024-02-06 16:45                     ` Pedro Falcato
  0 siblings, 0 replies; 89+ messages in thread
From: Pedro Falcato @ 2024-02-06 16:45 UTC (permalink / raw)
  To: Laszlo Ersek
  Cc: devel, lichao, Eric Dong, Ray Ni, Rahul Kumar, Gerd Hoffmann,
	Baoqi Zhang, Dongyan Qian, Xianglai Li, Bibo Mao, Andrew Fish,
	Kinney, Michael D, Leif Lindholm

On Tue, Feb 6, 2024 at 2:32 PM Laszlo Ersek <lersek@redhat.com> wrote:
>
> On 2/6/24 03:57, Chao Li wrote:
> > Hi Pedro, Laszlo,
> >
> > I have double-check this patch and I'd like to point out the following:
> >
> > 1. Some of the code in this patch actually refers to the Linux kernel.
> > But only used the name of kernel code , such as PUD, PMD, PTE, Swapper
> > Page Dir, etc.
> >
> > 2. Refer to the LoongArch TLB rules, if we use the multi-level page
> > table to search and filling, the usual logic is as follows:
> >
> >     a. Get the first level page table, called PGD, in reigster PGDL or PGDH.
> >
> >     b. PGD will index the 2st Dir, and the 2st Dir will index the 3st
> > Dir, and so on.
> >
> >     c. We would like to use the four-level page table, because if we use
> > two- or three-level page tables, the page tables will larger and must be
> > continuous, so we think the four-level is better(Linux kernel also uses
> > the four-level page tables).
> >
> >     d. LoongArch paging logic is shown in the image below(URL:
> > https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#memory-management-of-page-table-mapping):
> >
> > Referring to the above, should I use the old logic, rewrite the Linux
> > kernel-like code to EFI style and clean up some unused code and redesign
> > the PEI library, it that OK?
>
> I don't like the expression "rewrite the Linux kernel-like code to EFI
> style".
>
> You should not start with, or incorporate, anything that comes from
> Linux. Whatever you do, make sure it is not a derivative of Linux. Don't
> *derive* your edk2 contribution from Linux code.
>
> I think you are fine to implement any paging logic you prefer, as long
> as you start with an empty source file, and consult only the public
> LoongArch architecture specs, plus the rest of edk2 while writing the code.
>
> Consider the current source files from this patch "tainted". I don't
> encourage a piecemeal approach on this, "replace this", "rewrite that".
> The files look like they are a derivative of Linux code, so any further
> modifications in these files will just result in more derived code.
> Let's not play "where does this line come from" games. Scrap it all;
> implement it again from the ground up, using only the public arch specs.
>
> Just my opinion.
> Laszlo

I completely agree. Note that (AFAIK) you can also consult other
*license-compatible* code (BSD licenses, MIT, ISC, whatever).

-- 
Pedro


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#115183): https://edk2.groups.io/g/devel/message/115183
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-02-02  4:30           ` Ni, Ray
@ 2024-03-01  1:26             ` Chao Li
  2024-03-01 11:27               ` Laszlo Ersek
       [not found]             ` <17B87F9FA8D0E543.14067@groups.io>
  1 sibling, 1 reply; 89+ messages in thread
From: Chao Li @ 2024-03-01  1:26 UTC (permalink / raw)
  To: Ni, Ray, devel@edk2.groups.io, lersek@redhat.com
  Cc: Dong, Eric, Kumar, Rahul R, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

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

Hi Ray, Lazslo,

This library is almost complete to refactored, it refer to ARM and 
RISC-V version, the API include set/get memory region attribute.

I have one last question, in ARM and RISC-V version, even LoongArch old 
and current version, they all request a configure interface, which may 
be called in PEI or DXE stage, so should we open configure API? If so, 
it is possible for ARM RISC-V and LongArch's MMU libraries to be meged 
into the same instance if they wished.


Thanks,
Chao
On 2024/2/2 12:30, Ni, Ray wrote:
>
> Sure.
>
> A local function externed by another PEIM and called from that PEIM is 
> not a good practice.
>
> Thanks,
>
> Ray
>
> *From:* Chao Li <lichao@loongson.cn>
> *Sent:* Friday, February 2, 2024 11:51 AM
> *To:* devel@edk2.groups.io; Ni, Ray <ray.ni@intel.com>; lersek@redhat.com
> *Cc:* Dong, Eric <eric.dong@intel.com>; Kumar, Rahul R 
> <rahul.r.kumar@intel.com>; Gerd Hoffmann <kraxel@redhat.com>; Baoqi 
> Zhang <zhangbaoqi@loongson.cn>; Dongyan Qian 
> <qiandongyan@loongson.cn>; Xianglai Li <lixianglai@loongson.cn>; Bibo 
> Mao <maobibo@loongson.cn>
> *Subject:* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib 
> to UefiCpuPkg
>
> Hi Ray,
>
> Thanks,
> Chao
>
> On 2024/2/2 11:33, Ni, Ray wrote:
>
>     Chao,
>
>     ConfigureMemoryManagementUnit() is not a library API anymore.
>
>     Who will call it? I don’t see any reference to this function
>     inside the lib source.
>
> It become a private API, called at the LoongArch virtual/physical 
> machine PEIM, and when called it, the C file should extern this symbol.
>
> Please refer incoming and outgoing mails, this library probably need 
> to be refactored, sothe APIs may change, so PLS focus on this module 
> in future and help me more, please...
>
>     Thanks,
>
>     Ray
>
>     *From:* Chao Li <lichao@loongson.cn> <mailto:lichao@loongson.cn>
>     *Sent:* Thursday, February 1, 2024 3:58 PM
>     *To:* devel@edk2.groups.io; lersek@redhat.com
>     *Cc:* Dong, Eric <eric.dong@intel.com>
>     <mailto:eric.dong@intel.com>; Ni, Ray <ray.ni@intel.com>
>     <mailto:ray.ni@intel.com>; Kumar, Rahul R
>     <rahul.r.kumar@intel.com> <mailto:rahul.r.kumar@intel.com>; Gerd
>     Hoffmann <kraxel@redhat.com> <mailto:kraxel@redhat.com>; Baoqi
>     Zhang <zhangbaoqi@loongson.cn> <mailto:zhangbaoqi@loongson.cn>;
>     Dongyan Qian <qiandongyan@loongson.cn>
>     <mailto:qiandongyan@loongson.cn>; Xianglai Li
>     <lixianglai@loongson.cn> <mailto:lixianglai@loongson.cn>; Bibo Mao
>     <maobibo@loongson.cn> <mailto:maobibo@loongson.cn>
>     *Subject:* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add
>     CpuMmuLib to UefiCpuPkg
>
>     Hi Lazslo,
>
>     Thanks,
>     Chao
>
>     On 2024/1/31 17:47, Laszlo Ersek wrote:
>
>         On 1/26/24 07:29, Chao Li wrote:
>
>             Add a new library named CpuMmuLib and add a LoongArch64 instance with in
>
>             the library.
>
>             It provides two-stage MMU libraryinstances, PEI and DXE.
>
>               
>
>             BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>
>               
>
>             Cc: Eric Dong<eric.dong@intel.com>  <mailto:eric.dong@intel.com>
>
>             Cc: Ray Ni<ray.ni@intel.com>  <mailto:ray.ni@intel.com>
>
>             Cc: Laszlo Ersek<lersek@redhat.com>  <mailto:lersek@redhat.com>
>
>             Cc: Rahul Kumar<rahul1.kumar@intel.com>  <mailto:rahul1.kumar@intel.com>
>
>             Cc: Gerd Hoffmann<kraxel@redhat.com>  <mailto:kraxel@redhat.com>
>
>             Signed-off-by: Chao Li<lichao@loongson.cn>  <mailto:lichao@loongson.cn>
>
>             Co-authored-by: Baoqi Zhang<zhangbaoqi@loongson.cn>  <mailto:zhangbaoqi@loongson.cn>
>
>             Co-authored-by: Dongyan Qian<qiandongyan@loongson.cn>  <mailto:qiandongyan@loongson.cn>
>
>             Co-authored-by: Xianglai Li<lixianglai@loongson.cn>  <mailto:lixianglai@loongson.cn>
>
>             Co-authored-by: Bibo Mao<maobibo@loongson.cn>  <mailto:maobibo@loongson.cn>
>
>             ---
>
>               UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf |  36 +
>
>               UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni |  14 +
>
>               .../CpuMmuLib/LoongArch64/CommonMmuLib.c      | 988 ++++++++++++++++++
>
>               .../CpuMmuLib/LoongArch64/CommonMmuLib.h      |  43 +
>
>               .../Library/CpuMmuLib/LoongArch64/Page.h      | 279 +++++
>
>               .../CpuMmuLib/LoongArch64/PeiCpuMmuLib.c      | 178 ++++
>
>               .../Library/CpuMmuLib/LoongArch64/Tlb.h       |  48 +
>
>               .../CpuMmuLib/LoongArch64/TlbOperation.S      |  44 +
>
>               UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf |  44 +
>
>               UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni |  14 +
>
>               UefiCpuPkg/UefiCpuPkg.dsc                     |   4 +
>
>               11 files changed, 1692 insertions(+)
>
>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>
>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
>
>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>
>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
>
>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
>
>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
>
>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
>
>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
>
>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>
>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
>
>               
>
>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>
>             new file mode 100644
>
>             index 0000000000..bfce3ce96d
>
>             --- /dev/null
>
>             +++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>
>             @@ -0,0 +1,36 @@
>
>             +## @file
>
>             +#  CPU Memory Map Unit DXE phase driver.
>
>             +#
>
>             +#  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                      = DxeCpuMmuLib
>
>             +  MODULE_UNI_FILE                = DxeCpuMmuLib.uni
>
>             +  FILE_GUID                      = DA8F0232-FB14-42F0-922C-63104D2C70BE
>
>           
>
>         (1) This FILE_GUID was created from the FILE_GUID of
>
>         "ArmPkg/Library/ArmMmuLib/ArmMmuBaseLib.inf" by adding 1. That's not an
>
>         acceptable method for GUID generation.
>
>           
>
>         A method is only acceptable for GUID generation if multiple (= an
>
>         unlimited number of) parties can execute the method at any time, and the
>
>         output remains conflict-free.
>
>           
>
>         Taking an existent (known) FILE_GUID and incrementing it by 1 is not
>
>         such a method.
>
>           
>
>         Please regenerate the GUID with "uuidgen".
>
>           
>
>         Please also review the rest of your new GUIDs over this series (not only
>
>         FILE_GUIDs in INF files, but any other GUIDs, too).
>
>     OK, I will regenerate the GUID in next commit.
>
>
>           
>
>           
>
>           
>
>             +  MODULE_TYPE                    = DXE_DRIVER
>
>             +  VERSION_STRING                 = 1.0
>
>             +  LIBRARY_CLASS                  = CpuMmuLib | DXE_DRIVER
>
>             +  CONSTRUCTOR                    = MmuInitialize
>
>             +
>
>             +#
>
>             +#  VALID_ARCHITECTURES           = LOONGARCH64
>
>             +#
>
>             +
>
>             +[Sources.LoongArch64]
>
>             +  LoongArch64/TlbOperation.S   | GCC
>
>             +  LoongArch64/CommonMmuLib.c
>
>             +  LoongArch64/Page.h
>
>             +  LoongArch64/Tlb.h
>
>             +
>
>             +[Packages]
>
>             +  MdePkg/MdePkg.dec
>
>             +  UefiCpuPkg/UefiCpuPkg.dec
>
>             +
>
>             +[LibraryClasses]
>
>             +  DebugLib
>
>             +  MemoryAllocationLib
>
>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
>
>             new file mode 100644
>
>             index 0000000000..7342249516
>
>             --- /dev/null
>
>             +++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
>
>             @@ -0,0 +1,14 @@
>
>             +// /** @file
>
>             +// CPU Memory Manager Unit library instance for DXE modules.
>
>             +//
>
>             +// CPU Memory Manager Unit library instance for DXE modules.
>
>             +//
>
>             +// 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 "CPU Memory Manager Unit library instance for DXE modules."
>
>             +
>
>             +#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for DXE modules."
>
>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>
>             new file mode 100644
>
>             index 0000000000..2e852c3371
>
>             --- /dev/null
>
>             +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>
>             @@ -0,0 +1,988 @@
>
>             +/** @file
>
>             +
>
>             +  CPU Memory Map Unit Handler Library common functions.
>
>             +
>
>             +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>
>             +
>
>             +  SPDX-License-Identifier: BSD-2-Clause-Patent
>
>             +
>
>             +  @par Glossary:
>
>             +    - Pgd or Pgd or PGD    - Page Global Directory
>
>             +    - Pud or Pud or PUD    - Page Upper Directory
>
>             +    - Pmd or Pmd or PMD    - Page Middle Directory
>
>             +    - Pte or pte or PTE    - Page Table Entry
>
>             +    - Val or VAL or val    - Value
>
>             +    - Dir    - Directory
>
>             +**/
>
>             +#include <Uefi.h>
>
>             +#include <Library/BaseLib.h>
>
>             +#include <Library/BaseMemoryLib.h>
>
>             +#include <Library/CpuMmuLib.h>
>
>             +#include <Library/DebugLib.h>
>
>             +#include <Library/MemoryAllocationLib.h>
>
>             +#include <Register/LoongArch64/Csr.h>
>
>             +#include "Tlb.h"
>
>             +#include "Page.h"
>
>             +
>
>             +#define SWAP_PAGE_DIR              CsrRead(LOONGARCH_CSR_PGDL)
>
>           
>
>         (2) Missing space after "CsrRead".
>
>     OK.
>
>
>           
>
>           
>
>             +#define EFI_MEMORY_CACHETYPE_MASK  (EFI_MEMORY_UC  | \
>
>             +                                    EFI_MEMORY_WC  | \
>
>             +                                    EFI_MEMORY_WT  | \
>
>             +                                    EFI_MEMORY_WB  | \
>
>             +                                    EFI_MEMORY_UCE   \
>
>             +                                    )
>
>           
>
>         (3) This seems to come from "ArmPkg/Include/Library/ArmLib.h"; it's not
>
>         great regardless: we shouldn't use the EFI_ prefix for symbols that are
>
>         not standard. (Put differently, EFI_ is a reserved namespace prefix for
>
>         the UEFI and PI specs.)
>
>     OK, I will rename with out EFI_perfix next time.
>
>
>           
>
>           
>
>             +
>
>             +BOOLEAN  mMmuInited = FALSE;
>
>           
>
>         (4) This should be STATIC, I believe.
>
>           
>
>         (5) So this is the point where I realize that the library design makes
>
>         no sense to me.
>
>           
>
>         Normally you create SEC/PEI vs. DXE phase instances of a library
>
>         because, using writable global variables, you can gain performance (you
>
>         can remember the initialization) in DXE, but in SEC/PEI, you don't have
>
>         writeable global variables.
>
>           
>
>         This pattern does not seem to apply here, or at least it doesn't seem to
>
>         work. Here's why:
>
>           
>
>         - the variable mMmuInited and the function MmuInitialize (which contains
>
>         an assignment to the variable) are in "CommonMmuLib.c", which gets built
>
>         into both library instances. This makes no sense; that assignment cannot
>
>         work in SEC/PEI (I presume you're not going to have writeable globals --
>
>         executing from flash).
>
>           
>
>         - I think you may be trying to make up for that problem by checking
>
>         SWAP_PAGE_DIR (the LOONGARCH_CSR_PGDL register) in MmuInitialize() and
>
>         MmuIsInit(). That doesn't seem right.
>
>     Yes, you are right, the PEI stage may be executed from flash, in
>     this case, we have no way to write global variables, so we can
>     only check whether CSR_PGDL is NULL in the DXE stage to get
>     whether the MMU has been initialized.
>
>           
>
>           
>
>         - The PEI instance of the library contains an EFIAPI function called
>
>         ConfigureMemoryManagementUnit(). This function is never called in this
>
>         patch, which makes me think it's supposed to be called from driver or
>
>         application code (i.e., not from within the library itself, but from
>
>         client code). However, ConfigureMemoryManagementUnit() is also not
>
>         declared in the previous patch (in
>
>         "UefiCpuPkg/Include/Library/CpuMmuLib.h"); therefore client code cannot
>
>         reach it at all -- so that function doesn't even belong in this library.
>
>     This API was discussed with Ray in the V3 patch 13. Actually, the
>     CpuMmuLib.h include more APIs before, Ray believed that other APIs
>     should not be open to users or some APIs can be done by the base
>     APIs, and in my opinion, the ConfigureMemoryManagementUnit() is a
>     private API, so in V4, it has been removed from CpuMmuLib.h.
>
>     So what do you think? the ConfigureMemoryManagementUnit should be
>     added back?
>
>           
>
>           
>
>         - MmuInitialize() doesn't actually do anything, it just checks (via the
>
>         CSR) whether some other component has already initialized the MMU
>
>         (likely with ConfigureMemoryManagementUnit()). And the sole purpose of
>
>         MmuIsInit() appears to be to return from the public library APIs
>
>         SetMemoryRegionAttributes() and GetMemoryRegionAttributes() early, if
>
>         that "other" (unknown) component has not called
>
>         ConfigureMemoryManagementUnit() yet.
>
>           
>
>         So, I don't understand what you are trying to do. I could explain how to
>
>         keep the initialization logic *differences* minimal between the SEC/PEI
>
>         and the DXE library instances, but I don't understand how / when the MMU
>
>         initialization is supposed to occur in the first place.
>
>     Let's me tell you this library how to work:
>
>     In PEI stage, in addition to ensuring that the MMU is not used,
>     the user must call the ConfigureMemoryManagementUnit
>     toinitialization the MMU, such as filling the static page tables,
>     set the TLB refill exception entry point, set the page size etc.
>     the ConfigureMemoryManagementUnit is a private API but related to
>     ARCH.
>
>     During DXE stage, this library will provide services for CpuDxe
>     and other drivers, but almost changes to memory page attributes
>     use the protocols provided by CpuDxe. That is why the CpuMmuLib.h
>     only contains two APIs, it only can get/set the attribute.
>
>     In short, the PEI stage needs to initialize and set up TLB refill
>     entry point and method(dynamically populate the TLB, keep the PA
>     == VA), and DXE stage is provides services for changing the memory
>     attributes.
>
>           
>
>           
>
>         (6) The patch is too large in general. You should construct these
>
>         library instances over multiple patches. The first patch could add some
>
>         declarations / macro definitions, such as "Page.h" and "Tlb.h". Another
>
>         patch could add the assembly language helper functions. Another patch
>
>         could add the PEI phase library instance (including the common code). A
>
>         final patch could add the DXE-phase bits.
>
>           
>
>         The idea is to proceed in layers, logically building one on top of the
>
>         other. It's fine if the library doesn't build initially; there is no
>
>         attempt to build it anyway until you actually reference the INF files in
>
>         some DSC files. So please split this at least in 4 patches.
>
>     OK, I will try it.
>
>
>           
>
>           
>
>         (7) The commit message should explain the expected usage model in detail.
>
>     OK.
>
>
>           
>
>           
>
>         Best regards,
>
>         Laszlo
>
>           
>
>           
>
>             +
>
>             +/**
>
>             +  Check to see if mmu successfully initializes.
>
>             +
>
>             +  @param  VOID.
>
>             +
>
>             +  @retval  TRUE  Initialization has been completed.
>
>             +           FALSE Initialization did not complete.
>
>             +**/
>
>             +STATIC
>
>             +BOOLEAN
>
>             +MmuIsInit (
>
>             +  VOID
>
>             +  )
>
>             +{
>
>             +  if (mMmuInited || (SWAP_PAGE_DIR != 0)) {
>
>             +    return TRUE;
>
>             +  }
>
>             +
>
>             +  return FALSE;
>
>             +}
>
>             +
>
>             +/**
>
>             +  Iterates through the page directory to initialize it.
>
>             +
>
>             +  @param  Dst  A pointer to the directory of the page to initialize.
>
>             +  @param  Num  The number of page directories to initialize.
>
>             +  @param  Src  A pointer to the data used to initialize the page directory.
>
>             +
>
>             +  @return VOID.
>
>             +**/
>
>             +STATIC
>
>             +VOID
>
>             +PageDirInit (
>
>             +  IN VOID   *Dst,
>
>             +  IN UINTN  Num,
>
>             +  IN VOID   *Src
>
>             +  )
>
>             +{
>
>             +  UINTN  *Ptr;
>
>             +  UINTN  *End;
>
>             +  UINTN  Entry;
>
>             +
>
>             +  Entry = (UINTN)Src;
>
>             +  Ptr   = (UINTN *)Dst;
>
>             +  End   = Ptr + Num;
>
>             +
>
>             +  for ( ; Ptr < End; Ptr++) {
>
>             +    *Ptr = Entry;
>
>             +  }
>
>             +
>
>             +  return;
>
>             +}
>
>             +
>
>             +/**
>
>             +  Gets the virtual address corresponding to the page global directory table entry.
>
>             +
>
>             +  @param  Address  the virtual address for the table entry.
>
>             +
>
>             +  @retval PGD A pointer to get the table item.
>
>             +**/
>
>             +STATIC
>
>             +PGD *
>
>             +PgdOffset (
>
>             +  IN UINTN  Address
>
>             +  )
>
>             +{
>
>             +  return (PGD *)(SWAP_PAGE_DIR) + PGD_INDEX (Address);
>
>             +}
>
>             +
>
>             +/**
>
>             +  Gets the virtual address corresponding to the page upper directory table entry.
>
>             +
>
>             +  @param  Pgd  A pointer to a page global directory table entry.
>
>             +  @param  Address  the virtual address for the table entry.
>
>             +
>
>             +  @retval PUD A pointer to get the table item.
>
>             +**/
>
>             +STATIC
>
>             +PUD *
>
>             +PudOffset (
>
>             +  IN PGD    *Pgd,
>
>             +  IN UINTN  Address
>
>             +  )
>
>             +{
>
>             +  UINTN  PgdVal;
>
>             +
>
>             +  PgdVal = (UINTN)PGD_VAL (*Pgd);
>
>             +
>
>             +  return (PUD *)PgdVal + PUD_INDEX (Address);
>
>             +}
>
>             +
>
>             +/**
>
>             +  Gets the virtual address corresponding to the page middle directory table entry.
>
>             +
>
>             +  @param  Pud  A pointer to a page upper directory table entry.
>
>             +  @param  Address  the virtual address for the table entry.
>
>             +
>
>             +  @retval PMD A pointer to get the table item.
>
>             +**/
>
>             +STATIC
>
>             +PMD *
>
>             +PmdOffset (
>
>             +  IN PUD    *Pud,
>
>             +  IN UINTN  Address
>
>             +  )
>
>             +{
>
>             +  UINTN  PudVal;
>
>             +
>
>             +  PudVal = PUD_VAL (*Pud);
>
>             +
>
>             +  return (PMD *)PudVal + PMD_INDEX (Address);
>
>             +}
>
>             +
>
>             +/**
>
>             +  Gets the virtual address corresponding to the page table entry.
>
>             +
>
>             +  @param  Pmd  A pointer to a page middle directory table entry.
>
>             +  @param  Address  the virtual address for the table entry.
>
>             +
>
>             +  @retval PTE A pointer to get the table item.
>
>             +**/
>
>             +STATIC
>
>             +PTE *
>
>             +PteOffset (
>
>             +  IN PMD    *Pmd,
>
>             +  IN UINTN  Address
>
>             +  )
>
>             +{
>
>             +  UINTN  PmdVal;
>
>             +
>
>             +  PmdVal = (UINTN)PMD_VAL (*Pmd);
>
>             +
>
>             +  return (PTE *)PmdVal + PTE_INDEX (Address);
>
>             +}
>
>             +
>
>             +/**
>
>             +  Sets the value of the page table entry.
>
>             +
>
>             +  @param  Pte  A pointer to a page table entry.
>
>             +  @param  PteVal  The value of the page table entry to set.
>
>             +
>
>             +**/
>
>             +STATIC
>
>             +VOID
>
>             +SetPte (
>
>             +  IN PTE  *Pte,
>
>             +  IN PTE  PteVal
>
>             +  )
>
>             +{
>
>             +  *Pte = PteVal;
>
>             +}
>
>             +
>
>             +/**
>
>             +  Sets the value of the page global directory.
>
>             +
>
>             +  @param  Pgd  A pointer to a page global directory.
>
>             +  @param  Pud  The value of the page global directory to set.
>
>             +
>
>             +**/
>
>             +STATIC
>
>             +VOID
>
>             +SetPgd (
>
>             +  IN PGD  *Pgd,
>
>             +  IN PUD  *Pud
>
>             +  )
>
>             +{
>
>             +  *Pgd = (PGD) {
>
>             +    ((UINTN)Pud)
>
>             +  };
>
>             +}
>
>             +
>
>             +/**
>
>             +  Sets the value of the page upper directory.
>
>             +
>
>             +  @param  Pud  A pointer to a page upper directory.
>
>             +  @param  Pmd  The value of the page upper directory to set.
>
>             +
>
>             +**/
>
>             +STATIC
>
>             +VOID
>
>             +SetPud (
>
>             +  IN PUD  *Pud,
>
>             +  IN PMD  *Pmd
>
>             +  )
>
>             +{
>
>             +  *Pud = (PUD) {
>
>             +    ((UINTN)Pmd)
>
>             +  };
>
>             +}
>
>             +
>
>             +/**
>
>             +  Sets the value of the page middle directory.
>
>             +
>
>             +  @param  Pmd  A pointer to a page middle directory.
>
>             +  @param  Pte  The value of the page middle directory to set.
>
>             +
>
>             +**/
>
>             +STATIC
>
>             +VOID
>
>             +SetPmd (
>
>             +  IN PMD  *Pmd,
>
>             +  IN PTE  *Pte
>
>             +  )
>
>             +{
>
>             +  *Pmd = (PMD) {
>
>             +    ((UINTN)Pte)
>
>             +  };
>
>             +}
>
>             +
>
>             +/**
>
>             +  Free up memory space occupied by page tables.
>
>             +
>
>             +  @param  Pte  A pointer to the page table.
>
>             +
>
>             +**/
>
>             +VOID
>
>             +PteFree (
>
>             +  IN PTE  *Pte
>
>             +  )
>
>             +{
>
>             +  FreePages ((VOID *)Pte, 1);
>
>             +}
>
>             +
>
>             +/**
>
>             +  Free up memory space occupied by page middle directory.
>
>             +
>
>             +  @param  Pmd  A pointer to the page middle directory.
>
>             +
>
>             +**/
>
>             +VOID
>
>             +PmdFree (
>
>             +  IN PMD  *Pmd
>
>             +  )
>
>             +{
>
>             +  FreePages ((VOID *)Pmd, 1);
>
>             +}
>
>             +
>
>             +/**
>
>             +  Free up memory space occupied by page upper directory.
>
>             +
>
>             +  @param  Pud  A pointer to the page upper directory.
>
>             +
>
>             +**/
>
>             +VOID
>
>             +PudFree (
>
>             +  IN PUD  *Pud
>
>             +  )
>
>             +{
>
>             +  FreePages ((VOID *)Pud, 1);
>
>             +}
>
>             +
>
>             +/**
>
>             +  Requests the memory space required for the page upper directory,
>
>             +  initializes it, and places it in the specified page global directory
>
>             +
>
>             +  @param  Pgd  A pointer to the page global directory.
>
>             +
>
>             +  @retval  EFI_SUCCESS  Memory request successful.
>
>             +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
>
>             +**/
>
>             +STATIC
>
>             +EFI_STATUS
>
>             +PudAlloc (
>
>             +  IN PGD  *Pgd
>
>             +  )
>
>             +{
>
>             +  PUD  *Pud;
>
>             +
>
>             +  Pud = (PUD *)AllocatePages (1);
>
>             +  if (Pud == NULL) {
>
>             +    return EFI_OUT_OF_RESOURCES;
>
>             +  }
>
>             +
>
>             +  PageDirInit ((VOID *)Pud, ENTRYS_PER_PUD, (VOID *)INVALID_PAGE);
>
>             +
>
>             +  SetPgd (Pgd, Pud);
>
>             +
>
>             +  return EFI_SUCCESS;
>
>             +}
>
>             +
>
>             +/**
>
>             +  Requests the memory space required for the page middle directory,
>
>             +  initializes it, and places it in the specified page upper directory
>
>             +
>
>             +  @param  Pud  A pointer to the page upper directory.
>
>             +
>
>             +  @retval  EFI_SUCCESS  Memory request successful.
>
>             +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
>
>             +**/
>
>             +STATIC
>
>             +EFI_STATUS
>
>             +PmdAlloc (
>
>             +  IN PUD  *Pud
>
>             +  )
>
>             +{
>
>             +  PMD  *Pmd;
>
>             +
>
>             +  Pmd = (PMD *)AllocatePages (1);
>
>             +  if (!Pmd) {
>
>             +    return EFI_OUT_OF_RESOURCES;
>
>             +  }
>
>             +
>
>             +  PageDirInit ((VOID *)Pmd, ENTRYS_PER_PMD, (VOID *)INVALID_PAGE);
>
>             +
>
>             +  SetPud (Pud, Pmd);
>
>             +
>
>             +  return EFI_SUCCESS;
>
>             +}
>
>             +
>
>             +/**
>
>             +  Requests the memory space required for the page table,
>
>             +  initializes it, and places it in the specified page middle directory
>
>             +
>
>             +  @param  Pmd  A pointer to the page middle directory.
>
>             +
>
>             +  @retval  EFI_SUCCESS  Memory request successful.
>
>             +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
>
>             +**/
>
>             +STATIC
>
>             +EFI_STATUS
>
>             +PteAlloc (
>
>             +  IN PMD  *Pmd
>
>             +  )
>
>             +{
>
>             +  PTE  *Pte;
>
>             +
>
>             +  Pte = (PTE *)AllocatePages (1);
>
>             +  if (!Pte) {
>
>             +    return EFI_OUT_OF_RESOURCES;
>
>             +  }
>
>             +
>
>             +  Pte = ZeroMem (Pte, EFI_PAGE_SIZE);
>
>             +
>
>             +  SetPmd (Pmd, Pte);
>
>             +
>
>             +  return EFI_SUCCESS;
>
>             +}
>
>             +
>
>             +/**
>
>             +  Requests the memory space required for the page upper directory,
>
>             +  initializes it, and places it in the specified page global directory,
>
>             +  and get the page upper directory entry corresponding to the virtual address.
>
>             +
>
>             +  @param  Pgd      A pointer to the page global directory.
>
>             +  @param  Address  The corresponding virtual address of the page table entry.
>
>             +
>
>             +  @retval          A pointer to the page upper directory entry. Return NULL, if
>
>             +                   allocate the memory buffer is fail.
>
>             +**/
>
>             +STATIC
>
>             +PUD *
>
>             +PudAllocGet (
>
>             +  IN PGD    *Pgd,
>
>             +  IN UINTN  Address
>
>             +  )
>
>             +{
>
>             +  EFI_STATUS  Status;
>
>             +
>
>             +  if (PGD_IS_EMPTY (*Pgd)) {
>
>             +    Status = PudAlloc (Pgd);
>
>             +    ASSERT_EFI_ERROR (Status);
>
>             +    if (EFI_ERROR (Status)) {
>
>             +      return NULL;
>
>             +    }
>
>             +  }
>
>             +
>
>             +  return PudOffset (Pgd, Address);
>
>             +}
>
>             +
>
>             +/**
>
>             +  Requests the memory space required for the page middle directory,
>
>             +  initializes it, and places it in the specified page upper directory,
>
>             +  and get the page middle directory entry corresponding to the virtual address.
>
>             +
>
>             +  @param  Pud      A pointer to the page upper directory.
>
>             +  @param  Address  The corresponding virtual address of the page table entry.
>
>             +
>
>             +  @retval          A pointer to the page middle directory entry. Return NULL, if
>
>             +                   allocate the memory buffer is fail.
>
>             +**/
>
>             +STATIC
>
>             +PMD *
>
>             +PmdAllocGet (
>
>             +  IN PUD    *Pud,
>
>             +  IN UINTN  Address
>
>             +  )
>
>             +{
>
>             +  EFI_STATUS  Status;
>
>             +
>
>             +  if (PUD_IS_EMPTY (*Pud)) {
>
>             +    Status = PmdAlloc (Pud);
>
>             +    ASSERT_EFI_ERROR (Status);
>
>             +    if (EFI_ERROR (Status)) {
>
>             +      return NULL;
>
>             +    }
>
>             +  }
>
>             +
>
>             +  return PmdOffset (Pud, Address);
>
>             +}
>
>             +
>
>             +/**
>
>             +  Requests the memory space required for the page table,
>
>             +  initializes it, and places it in the specified page middle directory,
>
>             +  and get the page table entry corresponding to the virtual address.
>
>             +
>
>             +  @param  Pmd      A pointer to the page upper directory.
>
>             +  @param  Address  The corresponding virtual address of the page table entry.
>
>             +
>
>             +  @retval          A pointer to the page table entry. Return NULL, if allocate
>
>             +                   the memory buffer is fail.
>
>             +**/
>
>             +STATIC
>
>             +PTE *
>
>             +PteAllocGet (
>
>             +  IN PMD    *Pmd,
>
>             +  IN UINTN  Address
>
>             +  )
>
>             +{
>
>             +  EFI_STATUS  Status;
>
>             +
>
>             +  if (PMD_IS_EMPTY (*Pmd)) {
>
>             +    Status = PteAlloc (Pmd);
>
>             +    ASSERT_EFI_ERROR (Status);
>
>             +    if (EFI_ERROR (Status)) {
>
>             +      return NULL;
>
>             +    }
>
>             +  }
>
>             +
>
>             +  return PteOffset (Pmd, Address);
>
>             +}
>
>             +
>
>             +/**
>
>             +  Gets the physical address of the page table entry corresponding to the specified virtual address.
>
>             +
>
>             +  @param  Address  The corresponding virtual address of the page table entry.
>
>             +
>
>             +  @retval  A pointer to the page table entry.
>
>             +  @retval  NULL
>
>             +**/
>
>             +STATIC
>
>             +PTE *
>
>             +GetPteAddress (
>
>             +  IN UINTN  Address
>
>             +  )
>
>             +{
>
>             +  PGD  *Pgd;
>
>             +  PUD  *Pud;
>
>             +  PMD  *Pmd;
>
>             +
>
>             +  Pgd = PgdOffset (Address);
>
>             +
>
>             +  if (PGD_IS_EMPTY (*Pgd)) {
>
>             +    return NULL;
>
>             +  }
>
>             +
>
>             +  Pud = PudOffset (Pgd, Address);
>
>             +
>
>             +  if (PUD_IS_EMPTY (*Pud)) {
>
>             +    return NULL;
>
>             +  }
>
>             +
>
>             +  Pmd = PmdOffset (Pud, Address);
>
>             +  if (PMD_IS_EMPTY (*Pmd)) {
>
>             +    return NULL;
>
>             +  }
>
>             +
>
>             +  if (IS_HUGE_PAGE (Pmd->PmdVal)) {
>
>             +    return ((PTE *)Pmd);
>
>             +  }
>
>             +
>
>             +  return PteOffset (Pmd, Address);
>
>             +}
>
>             +
>
>             +/**
>
>             +  Gets the Attributes of Huge Page.
>
>             +
>
>             +  @param  Pmd  A pointer to the page middle directory.
>
>             +
>
>             +  @retval     Value of Attributes.
>
>             +**/
>
>             +STATIC
>
>             +UINTN
>
>             +GetHugePageAttributes (
>
>             +  IN  PMD  *Pmd
>
>             +  )
>
>             +{
>
>             +  UINTN  Attributes;
>
>             +  UINTN  GlobalFlag;
>
>             +  UINTN  HugeVal;
>
>             +
>
>             +  HugeVal     = PMD_VAL (*Pmd);
>
>             +  Attributes  = HugeVal & (~HUGEP_PAGE_MASK);
>
>             +  GlobalFlag  = ((Attributes & (1 << PAGE_HGLOBAL_SHIFT)) >> PAGE_HGLOBAL_SHIFT) << PAGE_GLOBAL_SHIFT;
>
>             +  Attributes &= ~(1 << PAGE_HGLOBAL_SHIFT);
>
>             +  Attributes |= GlobalFlag;
>
>             +  return Attributes;
>
>             +}
>
>             +
>
>             +/**
>
>             +  Establishes a page table entry based on the specified memory region.
>
>             +
>
>             +  @param  Pmd  A pointer to the page middle directory.
>
>             +  @param  Address  The memory space start address.
>
>             +  @param  End  The end address of the memory space.
>
>             +  @param  Attributes  Memory space Attributes.
>
>             +
>
>             +  @retval     EFI_SUCCESS   The page table entry was created successfully.
>
>             +  @retval     EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.
>
>             +**/
>
>             +STATIC
>
>             +EFI_STATUS
>
>             +MemoryMapPteRange (
>
>             +  IN PMD    *Pmd,
>
>             +  IN UINTN  Address,
>
>             +  IN UINTN  End,
>
>             +  IN UINTN  Attributes
>
>             +  )
>
>             +{
>
>             +  PTE      *Pte;
>
>             +  PTE      PteVal;
>
>             +  BOOLEAN  UpDate;
>
>             +
>
>             +  Pte = PteAllocGet (Pmd, Address);
>
>             +  if (!Pte) {
>
>             +    return EFI_OUT_OF_RESOURCES;
>
>             +  }
>
>             +
>
>             +  DEBUG ((
>
>             +    DEBUG_INFO,
>
>             +    "%a %d Address %p End %p  Attributes %llx\n",
>
>             +    __func__,
>
>             +    __LINE__,
>
>             +    Address,
>
>             +    End,
>
>             +    Attributes
>
>             +    ));
>
>             +
>
>             +  do {
>
>             +    UpDate = FALSE;
>
>             +    PteVal = MAKE_PTE (Address, Attributes);
>
>             +
>
>             +    if ((!PTE_IS_EMPTY (*Pte)) &&
>
>             +        (PTE_VAL (*Pte) != PTE_VAL (PteVal)))
>
>             +    {
>
>             +      UpDate = TRUE;
>
>             +    }
>
>             +
>
>             +    SetPte (Pte, PteVal);
>
>             +    if (UpDate) {
>
>             +      InvalidTlb (Address);
>
>             +    }
>
>             +  } while (Pte++, Address += EFI_PAGE_SIZE, Address != End);
>
>             +
>
>             +  return EFI_SUCCESS;
>
>             +}
>
>             +
>
>             +/**
>
>             +  Convert Huge Page to Page.
>
>             +
>
>             +  @param  Pmd  A pointer to the page middle directory.
>
>             +  @param  Address  The memory space start address.
>
>             +  @param  End  The end address of the memory space.
>
>             +  @param  Attributes  Memory space Attributes.
>
>             +
>
>             +  @retval  EFI_SUCCESS   The page table entry was created successfully.
>
>             +  @retval  EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.
>
>             +**/
>
>             +STATIC
>
>             +EFI_STATUS
>
>             +ConvertHugePageToPage (
>
>             +  IN  PMD   *Pmd,
>
>             +  IN UINTN  Address,
>
>             +  IN UINTN  End,
>
>             +  IN UINTN  Attributes
>
>             +  )
>
>             +{
>
>             +  UINTN       OldAttributes;
>
>             +  UINTN       HugePageEnd;
>
>             +  UINTN       HugePageStart;
>
>             +  EFI_STATUS  Status;
>
>             +
>
>             +  Status = EFI_SUCCESS;
>
>             +
>
>             +  if ((PMD_IS_EMPTY (*Pmd)) ||
>
>             +      (!IS_HUGE_PAGE (Pmd->PmdVal)))
>
>             +  {
>
>             +    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
>
>             +  } else {
>
>             +    OldAttributes = GetHugePageAttributes (Pmd);
>
>             +    if (Attributes == OldAttributes) {
>
>             +      return Status;
>
>             +    }
>
>             +
>
>             +    SetPmd (Pmd, (PTE *)(INVALID_PAGE));
>
>             +    HugePageStart = Address & PMD_MASK;
>
>             +    HugePageEnd   = HugePageStart + HUGE_PAGE_SIZE;
>
>             +    ASSERT (HugePageEnd >= End);
>
>             +
>
>             +    if (Address > HugePageStart) {
>
>             +      Status |= MemoryMapPteRange (Pmd, HugePageStart, Address, OldAttributes);
>
>             +    }
>
>             +
>
>             +    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
>
>             +
>
>             +    if (End < HugePageEnd) {
>
>             +      Status |= MemoryMapPteRange (Pmd, End, HugePageEnd, OldAttributes);
>
>             +    }
>
>             +  }
>
>             +
>
>             +  return Status;
>
>             +}
>
>             +
>
>             +/**
>
>             +  Establishes a page middle directory based on the specified memory region.
>
>             +
>
>             +  @param  Pud  A pointer to the page upper directory.
>
>             +  @param  Address  The memory space start address.
>
>             +  @param  End  The end address of the memory space.
>
>             +  @param  Attributes  Memory space Attributes.
>
>             +
>
>             +  @retval     EFI_SUCCESS   The page middle directory was created successfully.
>
>             +  @retval     EFI_OUT_OF_RESOURCES  Page middle directory establishment failed due to resource exhaustion.
>
>             +**/
>
>             +STATIC
>
>             +EFI_STATUS
>
>             +MemoryMapPmdRange (
>
>             +  IN PUD    *Pud,
>
>             +  IN UINTN  Address,
>
>             +  IN UINTN  End,
>
>             +  IN UINTN  Attributes
>
>             +  )
>
>             +{
>
>             +  PMD      *Pmd;
>
>             +  UINTN    Next;
>
>             +  PTE      PteVal;
>
>             +  BOOLEAN  UpDate;
>
>             +
>
>             +  Pmd = PmdAllocGet (Pud, Address);
>
>             +  if (Pmd == NULL) {
>
>             +    return EFI_OUT_OF_RESOURCES;
>
>             +  }
>
>             +
>
>             +  do {
>
>             +    Next = PMD_ADDRESS_END (Address, End);
>
>             +    if (((Address & (~PMD_MASK)) == 0) &&
>
>             +        ((Next &  (~PMD_MASK)) == 0) &&
>
>             +        (PMD_IS_EMPTY (*Pmd) || IS_HUGE_PAGE (Pmd->PmdVal)))
>
>             +    {
>
>             +      UpDate = FALSE;
>
>             +      PteVal = MAKE_HUGE_PTE (Address, Attributes);
>
>             +
>
>             +      if ((!PMD_IS_EMPTY (*Pmd)) &&
>
>             +          (PMD_VAL (*Pmd) != PTE_VAL (PteVal)))
>
>             +      {
>
>             +        UpDate = TRUE;
>
>             +      }
>
>             +
>
>             +      SetPmd (Pmd, (PTE *)PteVal.PteVal);
>
>             +      if (UpDate) {
>
>             +        InvalidTlb (Address);
>
>             +      }
>
>             +    } else {
>
>             +      ConvertHugePageToPage (Pmd, Address, Next, Attributes);
>
>             +    }
>
>             +  } while (Pmd++, Address = Next, Address != End);
>
>             +
>
>             +  return EFI_SUCCESS;
>
>             +}
>
>             +
>
>             +/**
>
>             +  Establishes a page upper directory based on the specified memory region.
>
>             +
>
>             +  @param  Pgd  A pointer to the page global directory.
>
>             +  @param  Address  The memory space start address.
>
>             +  @param  End  The end address of the memory space.
>
>             +  @param  Attributes  Memory space Attributes.
>
>             +
>
>             +  @retval     EFI_SUCCESS   The page upper directory was created successfully.
>
>             +  @retval     EFI_OUT_OF_RESOURCES  Page upper directory establishment failed due to resource exhaustion.
>
>             +**/
>
>             +STATIC
>
>             +EFI_STATUS
>
>             +MemoryMapPudRange (
>
>             +  IN PGD    *Pgd,
>
>             +  IN UINTN  Address,
>
>             +  IN UINTN  End,
>
>             +  IN UINTN  Attributes
>
>             +  )
>
>             +{
>
>             +  PUD    *Pud;
>
>             +  UINTN  Next;
>
>             +
>
>             +  Pud = PudAllocGet (Pgd, Address);
>
>             +  if (Pud == NULL) {
>
>             +    return EFI_OUT_OF_RESOURCES;
>
>             +  }
>
>             +
>
>             +  do {
>
>             +    Next = PUD_ADDRESS_END (Address, End);
>
>             +    if (EFI_ERROR (MemoryMapPmdRange (Pud, Address, Next, Attributes))) {
>
>             +      return EFI_OUT_OF_RESOURCES;
>
>             +    }
>
>             +  } while (Pud++, Address = Next, Address != End);
>
>             +
>
>             +  return EFI_SUCCESS;
>
>             +}
>
>             +
>
>             +/**
>
>             +  Establishes a page global directory based on the specified memory region.
>
>             +
>
>             +  @param  Start  The memory space start address.
>
>             +  @param  End  The end address of the memory space.
>
>             +  @param  Attributes  Memory space Attributes.
>
>             +
>
>             +  @retval     EFI_SUCCESS   The page global directory was created successfully.
>
>             +  @retval     EFI_OUT_OF_RESOURCES  Page global directory establishment failed due to resource exhaustion.
>
>             +**/
>
>             +STATIC
>
>             +EFI_STATUS
>
>             +MemoryMapPageRange (
>
>             +  IN UINTN  Start,
>
>             +  IN UINTN  End,
>
>             +  IN UINTN  Attributes
>
>             +  )
>
>             +{
>
>             +  PGD         *Pgd;
>
>             +  UINTN       Next;
>
>             +  UINTN       Address;
>
>             +  EFI_STATUS  Err;
>
>             +
>
>             +  Address = Start;
>
>             +
>
>             +  /* Get PGD(PTE PMD PUD PGD) in PageTables */
>
>             +  Pgd = PgdOffset (Address);
>
>             +  do {
>
>             +    Next = PGD_ADDRESS_END (Address, End);
>
>             +    /* Get Next Align Page to Map */
>
>             +    Err = MemoryMapPudRange (Pgd, Address, Next, Attributes);
>
>             +    if (Err) {
>
>             +      return Err;
>
>             +    }
>
>             +  } while (Pgd++, Address = Next, Address != End);
>
>             +
>
>             +  return EFI_SUCCESS;
>
>             +}
>
>             +
>
>             +/**
>
>             +  Page tables are established from memory-mapped tables.
>
>             +
>
>             +  @param  MemoryRegion   A pointer to a memory-mapped table entry.
>
>             +
>
>             +  @retval     EFI_SUCCESS   The page table was created successfully.
>
>             +  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
>
>             +**/
>
>             +EFI_STATUS
>
>             +FillTranslationTable (
>
>             +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
>
>             +  )
>
>             +{
>
>             +  return MemoryMapPageRange (
>
>             +           MemoryRegion->VirtualBase,
>
>             +           (MemoryRegion->Length + MemoryRegion->VirtualBase),
>
>             +           MemoryRegion->Attributes
>
>             +           );
>
>             +}
>
>             +
>
>             +/**
>
>             +  Convert EFI Attributes to Loongarch Attributes.
>
>             +
>
>             +  @param[in]  EfiAttributes     Efi Attributes.
>
>             +
>
>             +  @retval  Corresponding architecture attributes.
>
>             +**/
>
>             +UINTN
>
>             +EFIAPI
>
>             +EfiAttributeConverse (
>
>             +  IN UINTN  EfiAttributes
>
>             +  )
>
>             +{
>
>             +  UINTN  LoongArchAttributes;
>
>             +
>
>             +  LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | PLV_KERNEL | PAGE_GLOBAL;
>
>             +
>
>             +  switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
>
>             +    case EFI_MEMORY_UC:
>
>             +      LoongArchAttributes |= CACHE_SUC;
>
>             +      break;
>
>             +    case EFI_MEMORY_WC:
>
>             +      LoongArchAttributes |= CACHE_WUC;
>
>             +      break;
>
>             +    case EFI_MEMORY_WT:
>
>             +    case EFI_MEMORY_WB:
>
>             +      LoongArchAttributes |= CACHE_CC;
>
>             +      break;
>
>             +    default:
>
>             +      LoongArchAttributes |= CACHE_CC;
>
>             +      break;
>
>             +  }
>
>             +
>
>             +  // Write protection attributes
>
>             +  if (((EfiAttributes & EFI_MEMORY_RO) != 0) ||
>
>             +      ((EfiAttributes & EFI_MEMORY_WP) != 0))
>
>             +  {
>
>             +    LoongArchAttributes &= ~PAGE_DIRTY;
>
>             +  }
>
>             +
>
>             +  if ((EfiAttributes & EFI_MEMORY_RP) != 0) {
>
>             +    LoongArchAttributes |= PAGE_NO_READ;
>
>             +  }
>
>             +
>
>             +  // eXecute protection attribute
>
>             +  if ((EfiAttributes & EFI_MEMORY_XP) != 0) {
>
>             +    LoongArchAttributes |= PAGE_NO_EXEC;
>
>             +  }
>
>             +
>
>             +  return LoongArchAttributes;
>
>             +}
>
>             +
>
>             +/**
>
>             +  Finds the first of the length and memory properties of the memory region corresponding
>
>             +  to the specified base address.
>
>             +
>
>             +  @param[in]       BaseAddress       To find the base address of the memory region.
>
>             +  @param[in, out]  RegionLength      Pointer holding:
>
>             +                                      - At entry, the length of the memory region
>
>             +                                        expected to be found.
>
>             +                                      - At exit, the length of the memory region found.
>
>             +  @param[out]      RegionAttributes  Properties of the memory region found.
>
>             +
>
>             +  @retval  EFI_SUCCESS           The corresponding memory area was successfully found
>
>             +           EFI_NOT_FOUND         No memory area found
>
>             +           EFI_OUT_OF_RESOURCES  Base address or expected memory region exceeds the maximum
>
>             +                                 address.
>
>             +**/
>
>             +EFI_STATUS
>
>             +EFIAPI
>
>             +GetMemoryRegionAttributes (
>
>             +  IN     UINTN  BaseAddress,
>
>             +  IN OUT UINTN  *RegionLength,
>
>             +  OUT    UINTN  *RegionAttributes
>
>             +  )
>
>             +{
>
>             +  PTE    *Pte;
>
>             +  UINTN  Attributes;
>
>             +  UINTN  AttributesTmp;
>
>             +  UINTN  MaxAddress;
>
>             +  UINTN  EndAddress;
>
>             +  UINTN  AddSize;
>
>             +
>
>             +  if (!MmuIsInit ()) {
>
>             +    return EFI_UNSUPPORTED;
>
>             +  }
>
>             +
>
>             +  EndAddress = BaseAddress + *RegionLength;
>
>             +  MaxAddress = LShiftU64 (1ULL, MAX_VA_BITS) - 1;
>
>             +
>
>             +  // Clean the value to prepare output to find region size.
>
>             +  *RegionLength = 0x0;
>
>             +
>
>             +  if ((BaseAddress >= MaxAddress) || (EndAddress >= MaxAddress)) {
>
>             +    return EFI_OUT_OF_RESOURCES;
>
>             +  }
>
>             +
>
>             +  Pte = GetPteAddress (BaseAddress);
>
>             +
>
>             +  if (Pte == NULL) {
>
>             +    return EFI_NOT_FOUND;
>
>             +  }
>
>             +
>
>             +  Attributes = GET_PAGE_ATTRIBUTES (*Pte);
>
>             +  if (IS_HUGE_PAGE (Pte->PteVal)) {
>
>             +    *RegionAttributes = Attributes & (~(PAGE_HUGE));
>
>             +  } else {
>
>             +    *RegionAttributes = Attributes;
>
>             +  }
>
>             +
>
>             +  do {
>
>             +    Pte = GetPteAddress (BaseAddress);
>
>             +    if (Pte == NULL) {
>
>             +      return EFI_SUCCESS;
>
>             +    }
>
>             +
>
>             +    AttributesTmp = GET_PAGE_ATTRIBUTES (*Pte);
>
>             +    if (AttributesTmp == Attributes) {
>
>             +      if (IS_HUGE_PAGE (Pte->PteVal)) {
>
>             +        AddSize = HUGE_PAGE_SIZE;
>
>             +      } else {
>
>             +        AddSize = EFI_PAGE_SIZE;
>
>             +      }
>
>             +
>
>             +      *RegionLength += AddSize;
>
>             +      BaseAddress   += AddSize;
>
>             +    } else {
>
>             +      return EFI_SUCCESS;
>
>             +    }
>
>             +  } while (BaseAddress <= EndAddress);
>
>             +
>
>             +  return EFI_SUCCESS;
>
>             +}
>
>             +
>
>             +/**
>
>             +  Sets the Attributes  of the specified memory region
>
>             +
>
>             +  @param[in]  BaseAddress    The base address of the memory region to set the Attributes.
>
>             +  @param[in]  Length         The length of the memory region to set the Attributes.
>
>             +  @param[in]  Attributes     The Attributes to be set.
>
>             +  @param[in]  AttributeMask  Mask of memory attributes to take into account.
>
>             +
>
>             +  @retval  EFI_SUCCESS    The Attributes was set successfully
>
>             +**/
>
>             +EFI_STATUS
>
>             +EFIAPI
>
>             +SetMemoryRegionAttributes (
>
>             +  IN EFI_PHYSICAL_ADDRESS  BaseAddress,
>
>             +  IN UINTN                 Length,
>
>             +  IN UINTN                 Attributes,
>
>             +  IN UINT64                AttributeMask
>
>             +  )
>
>             +{
>
>             +  EFI_STATUS  Status;
>
>             +
>
>             +  if (!MmuIsInit ()) {
>
>             +    return EFI_UNSUPPORTED;
>
>             +  }
>
>             +
>
>             +  Attributes = EfiAttributeConverse (Attributes);
>
>             +  Status     = MemoryMapPageRange (BaseAddress, BaseAddress + Length, Attributes);
>
>             +  ASSERT_EFI_ERROR (Status);
>
>             +
>
>             +  return Status;
>
>             +}
>
>             +
>
>             +/**
>
>             +  Check to see if mmu successfully initializes and saves the result.
>
>             +
>
>             +  @param[in]  ImageHandle  The firmware allocated handle for the EFI image.
>
>             +  @param[in]  SystemTable  A pointer to the EFI System Table.
>
>             +
>
>             +  @retval  RETURN_SUCCESS    Initialization succeeded.
>
>             +**/
>
>             +RETURN_STATUS
>
>             +MmuInitialize (
>
>             +  IN EFI_HANDLE        ImageHandle,
>
>             +  IN EFI_SYSTEM_TABLE  *SystemTable
>
>             +  )
>
>             +{
>
>             +  if (SWAP_PAGE_DIR != 0) {
>
>             +    mMmuInited = TRUE;
>
>             +  }
>
>             +
>
>             +  return RETURN_SUCCESS;
>
>             +}
>
>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
>
>             new file mode 100644
>
>             index 0000000000..d8c922c8fa
>
>             --- /dev/null
>
>             +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
>
>             @@ -0,0 +1,43 @@
>
>             +/** @file
>
>             +
>
>             +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>
>             +
>
>             +  SPDX-License-Identifier: BSD-2-Clause-Patent
>
>             +
>
>             +  @par Glossary:
>
>             +    - Dir    - Directory
>
>             +**/
>
>             +
>
>             +#ifndef  MMU_LIB_CORE_H_
>
>             +#define  MMU_LIB_CORE_H_
>
>             +
>
>             +/**
>
>             +  Iterates through the page directory to initialize it.
>
>             +
>
>             +  @param  Dst  A pointer to the directory of the page to initialize.
>
>             +  @param  Num  The number of page directories to initialize.
>
>             +  @param  Src  A pointer to the data used to initialize the page directory.
>
>             +
>
>             +  @retval VOID.
>
>             +**/
>
>             +VOID
>
>             +PageDirInit (
>
>             +  IN VOID   *dest,
>
>             +  IN UINTN  Count,
>
>             +  IN VOID   *src
>
>             +  );
>
>             +
>
>             +/**
>
>             +  Page tables are established from memory-mapped tables.
>
>             +
>
>             +  @param  MemoryRegion   A pointer to a memory-mapped table entry.
>
>             +
>
>             +  @retval     EFI_SUCCESS   The page table was created successfully.
>
>             +  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
>
>             +**/
>
>             +EFI_STATUS
>
>             +FillTranslationTable (
>
>             +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
>
>             +  );
>
>             +
>
>             +#endif // MMU_LIB_CORE_H_
>
>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
>
>             new file mode 100644
>
>             index 0000000000..bac4f52327
>
>             --- /dev/null
>
>             +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
>
>             @@ -0,0 +1,279 @@
>
>             +/** @file
>
>             +
>
>             +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>
>             +
>
>             +  SPDX-License-Identifier: BSD-2-Clause-Patent
>
>             +
>
>             +  @par Glossary:
>
>             +    - Pgd or Pgd or PGD    - Page Global Directory
>
>             +    - Pud or Pud or PUD    - Page Upper Directory
>
>             +    - Pmd or Pmd or PMD    - Page Middle Directory
>
>             +    - Pte or pte or PTE    - Page Table Entry
>
>             +    - Val or VAL or val    - Value
>
>             +    - Dir    - Directory
>
>             +**/
>
>             +
>
>             +#ifndef PAGE_H_
>
>             +#define PAGE_H_
>
>             +
>
>             +#include <Library/CpuMmuLib.h>
>
>             +
>
>             +#define MAX_VA_BITS  47
>
>             +#define PGD_WIDE     (8)
>
>             +#define PUD_WIDE     (9)
>
>             +#define PMD_WIDE     (9)
>
>             +#define PTE_WIDE     (9)
>
>             +
>
>             +#define ENTRYS_PER_PGD  (1 << PGD_WIDE)
>
>             +#define ENTRYS_PER_PUD  (1 << PUD_WIDE)
>
>             +#define ENTRYS_PER_PMD  (1 << PMD_WIDE)
>
>             +#define ENTRYS_PER_PTE  (1 << PTE_WIDE)
>
>             +
>
>             +#define PGD_SHIFT  (PUD_SHIFT + PUD_WIDE)
>
>             +#define PUD_SHIFT  (PMD_SHIFT + PMD_WIDE)
>
>             +#define PMD_SHIFT  (EFI_PAGE_SHIFT + PTE_WIDE)
>
>             +#define PTE_SHIFT  (EFI_PAGE_SHIFT)
>
>             +
>
>             +#define PGD_SIZE  (1UL << PGD_SHIFT)
>
>             +#define PUD_SIZE  (1UL << PUD_SHIFT)
>
>             +#define PMD_SIZE  (1UL << PMD_SHIFT)
>
>             +
>
>             +#define PGD_MASK   (~(PGD_SIZE-1))
>
>             +#define PUD_MASK   (~(PUD_SIZE-1))
>
>             +#define PMD_MASK   (~(PMD_SIZE-1))
>
>             +#define PAGE_MASK  (~(EFI_PAGE_SIZE - 1))
>
>             +#define PFN_MASK   (~(((UINTN)(1) << (EFI_PAGE_SHIFT)) - 1) & \
>
>             +                   (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
>
>             +
>
>             +#define HUGEP_PAGE_MASK  (~(((UINTN)(1) << (PMD_SHIFT)) - 1) & \
>
>             +                          (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
>
>             +
>
>             +#define INVALID_PAGE  0
>
>             +
>
>             +typedef struct {
>
>             +  UINTN    PgdVal;
>
>             +} PGD;
>
>             +typedef struct {
>
>             +  UINTN    PudVal;
>
>             +} PUD;
>
>             +typedef struct {
>
>             +  UINTN    PmdVal;
>
>             +} PMD;
>
>             +typedef struct {
>
>             +  UINTN    PteVal;
>
>             +} PTE;
>
>             +
>
>             +/**
>
>             +  Gets the value of the page global directory table entry.
>
>             +
>
>             +  @param  x    Page global directory struct variables.
>
>             +
>
>             +  @retval   the value of the page global directory table entry.
>
>             + **/
>
>             +#define PGD_VAL(x)  ((x).PgdVal)
>
>             +
>
>             +/**
>
>             +  Gets the value of the page upper directory table entry.
>
>             +
>
>             +  @param  x    Page upper directory struct variables.
>
>             +
>
>             +  @retval  the value of the page upper directory table entry.
>
>             + **/
>
>             +#define PUD_VAL(x)  ((x).PudVal)
>
>             +
>
>             +/**
>
>             +  Gets the value of the page middle directory table entry.
>
>             +
>
>             +  @param  x    Page middle directory struct variables.
>
>             +
>
>             +  @retval  the value of the page middle directory table entry.
>
>             + **/
>
>             +#define PMD_VAL(x)  ((x).PmdVal)
>
>             +
>
>             +/**
>
>             +  Gets the value of the page table entry.
>
>             +
>
>             +  @param  x    Page table entry struct variables.
>
>             +
>
>             +  @retval  the value of the page table entry.
>
>             + **/
>
>             +#define PTE_VAL(x)  ((x).PteVal)
>
>             +
>
>             +#define PGD_TABLE_SIZE  (ENTRYS_PER_PGD * sizeof(PGD))
>
>             +#define PUD_TABLE_SIZE  (ENTRYS_PER_PUD * sizeof(PUD))
>
>             +#define PMD_TABLE_SIZE  (ENTRYS_PER_PMD * sizeof(PMD))
>
>             +#define PTE_TABLE_SIZE  (ENTRYS_PER_PTE * sizeof(PTE))
>
>             +
>
>             +/**
>
>             +  Gets the physical address of the record in the page table entry.
>
>             +
>
>             +  @param  x    Page table entry struct variables.
>
>             +
>
>             +  @retval  the value of the physical address.
>
>             + **/
>
>             +#define GET_PAGE_ATTRIBUTES(x)  (UINTN) {(PTE_VAL(x) & ~PFN_MASK)}
>
>             +
>
>             +/**
>
>             +  Gets the virtual address of the next block of the specified virtual address
>
>             +  that is aligned with the size of the global page directory mapping.
>
>             +
>
>             +  @param  Address  Specifies the virtual address.
>
>             +  @param  End    The end address of the memory region.
>
>             +
>
>             +  @retval   the specified virtual address  of the next block.
>
>             + **/
>
>             +#define PGD_ADDRESS_END(Address, End)                  \
>
>             +({                                                     \
>
>             +  UINTN Boundary = ((Address) + PGD_SIZE) & PGD_MASK;  \
>
>             +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
>
>             +})
>
>             +
>
>             +/**
>
>             +  Gets the virtual address of the next block of the specified virtual address
>
>             +  that is aligned with the size of the page upper directory mapping.
>
>             +
>
>             +  @param  Address  Specifies the virtual address.
>
>             +  @param  End    The end address of the memory region.
>
>             +
>
>             +  @retval   the specified virtual address  of the next block.
>
>             + **/
>
>             +#define PUD_ADDRESS_END(Address, End)                  \
>
>             +({                                                     \
>
>             +  UINTN Boundary = ((Address) + PUD_SIZE) & PUD_MASK;  \
>
>             +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
>
>             +})
>
>             +
>
>             +/**
>
>             +  Gets the virtual address of the next block of the specified virtual address
>
>             +  that is aligned with the size of the page middle directory mapping.
>
>             +
>
>             +  @param  Address  Specifies the virtual address.
>
>             +  @param  End    The end address of the memory region.
>
>             +
>
>             +  @retval   the specified virtual address  of the next block.
>
>             + **/
>
>             +#define PMD_ADDRESS_END(Address, End)                  \
>
>             +({                                                     \
>
>             +  UINTN Boundary = ((Address) + PMD_SIZE) & PMD_MASK;  \
>
>             +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
>
>             +})
>
>             +
>
>             +/**
>
>             +  Get Specifies the virtual address corresponding to the index of the page global directory table entry.
>
>             +
>
>             +  @param  Address  Specifies the virtual address.
>
>             +
>
>             +  @retval   the index of the page global directory table entry.
>
>             + **/
>
>             +#define PGD_INDEX(Address)  (((Address) >> PGD_SHIFT) & (ENTRYS_PER_PGD-1))
>
>             +
>
>             +/**
>
>             +  Get Specifies the virtual address corresponding to the index of the page upper directory table entry.
>
>             +
>
>             +  @param  Address  Specifies the virtual address.
>
>             +  @param  End    The end address of the memory region.
>
>             +
>
>             +  @retval   the index of the page upper directory table entry.
>
>             + **/
>
>             +#define PUD_INDEX(Address)  (((Address) >> PUD_SHIFT) & (ENTRYS_PER_PUD - 1))
>
>             +
>
>             +/**
>
>             +  Get Specifies the virtual address corresponding to the index of the page middle directory table entry.
>
>             +
>
>             +  @param  Address  Specifies the virtual address.
>
>             +
>
>             +  @retval   the index of the page middle directory table entry.
>
>             + **/
>
>             +#define PMD_INDEX(Address)  (((Address) >> PMD_SHIFT) & (ENTRYS_PER_PMD - 1))
>
>             +
>
>             +/**
>
>             +  Get Specifies the virtual address corresponding to the index of the page table entry.
>
>             +
>
>             +  @param  Address  Specifies the virtual address.
>
>             +
>
>             +  @retval   the index of the page table entry.
>
>             + **/
>
>             +#define PTE_INDEX(Address)  (((Address) >> EFI_PAGE_SHIFT) & (ENTRYS_PER_PTE - 1))
>
>             +
>
>             +/**
>
>             +  Calculates the value of the page table entry based on the specified virtual address and properties.
>
>             +
>
>             +  @param  Address  Specifies the virtual address.
>
>             +  @param  Attributes  Specifies the Attributes.
>
>             +
>
>             +  @retval    the value of the page table entry.
>
>             + **/
>
>             +#define MAKE_PTE(Address, Attributes)  (PTE){((((Address) >> EFI_PAGE_SHIFT) << 12) | (Attributes))}
>
>             +
>
>             +/**
>
>             +  Get Global bit from Attributes
>
>             +
>
>             +  @param  Attributes  Specifies the Attributes.
>
>             + * */
>
>             +#define GET_GLOBALBIT(Attributes)  ((Attributes & PAGE_GLOBAL) >> PAGE_GLOBAL_SHIFT)
>
>             +
>
>             +/**
>
>             +  Calculates the value of the Huge page table entry based on the specified virtual address and properties.
>
>             +
>
>             +  @param  Address  Specifies the virtual address.
>
>             +  @param  Attributes  Specifies the Attributes.
>
>             +
>
>             +  @retval    the value of the HUGE page table entry.
>
>             + **/
>
>             +#define MAKE_HUGE_PTE(Address, Attributes)  (PTE){(((((Address) >> PMD_SHIFT) << PMD_SHIFT) | \
>
>             +                                             ((Attributes) | (GET_GLOBALBIT(Attributes) << PAGE_HGLOBAL_SHIFT) | \
>
>             +                                             PAGE_HUGE)))}
>
>             +
>
>             +/**
>
>             +  Check whether the large page table entry is.
>
>             +
>
>             +  @param  Val The value of the page table entry.
>
>             +
>
>             +  @retval    1   Is huge page table entry.
>
>             +  @retval    0   Isn't huge page table entry.
>
>             +**/
>
>             +#define IS_HUGE_PAGE(Val)  ((((Val) & PAGE_HUGE) == PAGE_HUGE) && \
>
>             +                            (((Val) & PAGE_HGLOBAL) == PAGE_HGLOBAL))
>
>             +
>
>             +#define HUGE_PAGE_SIZE  (PMD_SIZE)
>
>             +
>
>             +/**
>
>             +  Check that the global page directory table entry is empty.
>
>             +
>
>             +  @param  pgd   the global page directory struct variables.
>
>             +
>
>             +  @retval    1   The page table is invalid.
>
>             +  @retval    0   The page table is valid.
>
>             +**/
>
>             +#define PGD_IS_EMPTY(Val)  (PGD_VAL(Val) == INVALID_PAGE)
>
>             +
>
>             +/**
>
>             +  Check that the page upper directory table entry is empty.
>
>             +
>
>             +  @param  pud   Page upper directory struct variables.
>
>             +
>
>             +  @retval    1   The page table is invalid.
>
>             +  @retval    0   The page table is valid.
>
>             +**/
>
>             +#define PUD_IS_EMPTY(Val)  (PUD_VAL(Val) == INVALID_PAGE)
>
>             +
>
>             +/**
>
>             +  Check that the page middle directory table entry is empty.
>
>             +
>
>             +  @param  pmd   Page middle directory struct variables.
>
>             +
>
>             +  @retval    1   The page table is invalid.
>
>             +  @retval    0   The page table is valid.
>
>             +**/
>
>             +#define PMD_IS_EMPTY(Val)  (PMD_VAL(Val) == INVALID_PAGE)
>
>             +
>
>             +/**
>
>             +  Check that the page the page table entry is empty.
>
>             +
>
>             +  @param  pte   Page table entry struct variables.
>
>             +
>
>             +  @retval    1   The page table is invalid.
>
>             +  @retval    0   The page table is valid.
>
>             +**/
>
>             +#define PTE_IS_EMPTY(Val)  (!(PTE_VAL(Val) & (~PAGE_VALID)))
>
>             +#endif // PAGE_H_
>
>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
>
>             new file mode 100644
>
>             index 0000000000..c214e8d847
>
>             --- /dev/null
>
>             +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
>
>             @@ -0,0 +1,178 @@
>
>             +/** @file
>
>             +  CPU Memory Map Unit PEI phase driver.
>
>             +
>
>             +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>
>             +
>
>             +  SPDX-License-Identifier: BSD-2-Clause-Patent
>
>             +
>
>             +  @par Glossary:
>
>             +    - Tlb      - Translation Lookaside Buffer
>
>             +**/
>
>             +
>
>             +#include <Uefi.h>
>
>             +#include <Library/BaseLib.h>
>
>             +#include <Library/BaseMemoryLib.h>
>
>             +#include <Library/CacheMaintenanceLib.h>
>
>             +#include <Library/CpuMmuLib.h>
>
>             +#include <Library/DebugLib.h>
>
>             +#include <Library/MemoryAllocationLib.h>
>
>             +#include <Library/PcdLib.h>
>
>             +#include <Register/LoongArch64/Csr.h>
>
>             +
>
>             +#include "Page.h"
>
>             +#include "Tlb.h"
>
>             +#include "CommonMmuLib.h"
>
>             +
>
>             +//
>
>             +// For coding convenience, define the maximum valid
>
>             +// LoongArch exception.
>
>             +// Since UEFI V2.11, it will be present in DebugSupport.h.
>
>             +//
>
>             +#define MAX_LOONGARCH_EXCEPTION  64
>
>             +
>
>             +/**
>
>             +  Create a page table and initialize the memory management unit(MMU).
>
>             +
>
>             +  @param[in]   MemoryTable           A pointer to a memory ragion table.
>
>             +  @param[out]  TranslationTableBase  A pointer to a translation table base address.
>
>             +  @param[out]  TranslationTableSize  A pointer to a translation table base size.
>
>             +
>
>             +  @retval  EFI_SUCCESS                Configure MMU successfully.
>
>             +           EFI_INVALID_PARAMETER      MemoryTable is NULL.
>
>             +           EFI_UNSUPPORTED            Out of memory space or size not aligned.
>
>             +**/
>
>             +EFI_STATUS
>
>             +EFIAPI
>
>             +ConfigureMemoryManagementUnit (
>
>             +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryTable,
>
>             +  OUT VOID                      **TranslationTableBase OPTIONAL,
>
>             +  OUT UINTN                     *TranslationTableSize  OPTIONAL
>
>             +  )
>
>             +{
>
>             +  PGD            *SwapperPageDir;
>
>             +  UINTN          PgdShift;
>
>             +  UINTN          PgdWide;
>
>             +  UINTN          PudShift;
>
>             +  UINTN          PudWide;
>
>             +  UINTN          PmdShift;
>
>             +  UINTN          PmdWide;
>
>             +  UINTN          PteShift;
>
>             +  UINTN          PteWide;
>
>             +  UINTN          Length;
>
>             +  UINTN          TlbReEntry;
>
>             +  UINTN          TlbReEntryOffset;
>
>             +  UINTN          Remaining;
>
>             +  RETURN_STATUS  Status;
>
>             +
>
>             +  SwapperPageDir = NULL;
>
>             +  PgdShift       = PGD_SHIFT;
>
>             +  PgdWide        = PGD_WIDE;
>
>             +  PudShift       = PUD_SHIFT;
>
>             +  PudWide        = PUD_WIDE;
>
>             +  PmdShift       = PMD_SHIFT;
>
>             +  PmdWide        = PMD_WIDE;
>
>             +  PteShift       = PTE_SHIFT;
>
>             +  PteWide        = PTE_WIDE;
>
>             +
>
>             +  if (MemoryTable == NULL) {
>
>             +    ASSERT (MemoryTable != NULL);
>
>             +    return EFI_INVALID_PARAMETER;
>
>             +  }
>
>             +
>
>             +  SwapperPageDir = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
>
>             +  ZeroMem (SwapperPageDir, PGD_TABLE_SIZE);
>
>             +
>
>             +  if (SwapperPageDir == NULL) {
>
>             +    goto FreeTranslationTable;
>
>             +  }
>
>             +
>
>             +  CsrWrite (LOONGARCH_CSR_PGDL, (UINTN)SwapperPageDir);
>
>             +
>
>             +  while (MemoryTable->Length != 0) {
>
>             +    DEBUG ((
>
>             +      DEBUG_INFO,
>
>             +      "%a %d VirtualBase %p VirtualEnd %p Attributes %p .\n",
>
>             +      __func__,
>
>             +      __LINE__,
>
>             +      MemoryTable->VirtualBase,
>
>             +      (MemoryTable->Length + MemoryTable->VirtualBase),
>
>             +      MemoryTable->Attributes
>
>             +      ));
>
>             +
>
>             +    Status = FillTranslationTable (MemoryTable);
>
>             +    if (EFI_ERROR (Status)) {
>
>             +      goto FreeTranslationTable;
>
>             +    }
>
>             +
>
>             +    MemoryTable++;
>
>             +  }
>
>             +
>
>             +  //
>
>             +  // TLB Re-entry address at the end of exception vector, a vector is up to 512 bytes,
>
>             +  // so the starting address is: total exception vector size + total interrupt vector size + base.
>
>             +  // The total size of TLB handler and exception vector size and interrupt vector size should not
>
>             +  // be lager than 64KB.
>
>             +  //
>
>             +  Length           = (UINTN)HandleTlbRefillEnd - (UINTN)HandleTlbRefillStart;
>
>             +  TlbReEntryOffset = (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * 512;
>
>             +  Remaining        = TlbReEntryOffset % SIZE_4KB;
>
>             +  if (Remaining != 0x0) {
>
>             +    TlbReEntryOffset += (SIZE_4KB - Remaining);
>
>             +  }
>
>             +
>
>             +  TlbReEntry = PcdGet64 (PcdCpuExceptionVectorBaseAddress) + TlbReEntryOffset;
>
>             +  if ((TlbReEntryOffset + Length) > SIZE_64KB) {
>
>             +    goto FreeTranslationTable;
>
>             +  }
>
>             +
>
>             +  //
>
>             +  // Ensure that TLB refill exception base address alignment is equals to 4KB and is valid.
>
>             +  //
>
>             +  if (TlbReEntry & (SIZE_4KB - 1)) {
>
>             +    goto FreeTranslationTable;
>
>             +  }
>
>             +
>
>             +  CopyMem ((VOID *)TlbReEntry, HandleTlbRefillStart, Length);
>
>             +  InvalidateInstructionCacheRange ((VOID *)(UINTN)HandleTlbRefillStart, Length);
>
>             +
>
>             +  DEBUG ((
>
>             +    DEBUG_INFO,
>
>             +    "%a  %d PteShift %d PteWide %d PmdShift %d PmdWide %d PudShift %d PudWide %d PgdShift %d PgdWide %d.\n",
>
>             +    __func__,
>
>             +    __LINE__,
>
>             +    PteShift,
>
>             +    PteWide,
>
>             +    PmdShift,
>
>             +    PmdWide,
>
>             +    PudShift,
>
>             +    PudWide,
>
>             +    PgdShift,
>
>             +    PgdWide
>
>             +    ));
>
>             +
>
>             +  //
>
>             +  // Set the address of TLB refill exception handler
>
>             +  //
>
>             +  SetTlbRebaseAddress ((UINTN)TlbReEntry);
>
>             +
>
>             +  //
>
>             +  // Set page size
>
>             +  //
>
>             +  CsrXChg (LOONGARCH_CSR_TLBIDX, (DEFAULT_PAGE_SIZE << CSR_TLBIDX_SIZE), CSR_TLBIDX_SIZE_MASK);
>
>             +  CsrWrite (LOONGARCH_CSR_STLBPGSIZE, DEFAULT_PAGE_SIZE);
>
>             +  CsrXChg (LOONGARCH_CSR_TLBREHI, (DEFAULT_PAGE_SIZE << CSR_TLBREHI_PS_SHIFT), CSR_TLBREHI_PS);
>
>             +
>
>             +  CsrWrite (LOONGARCH_CSR_PWCTL0, (PteShift | PteWide << 5 | PmdShift << 10 | PmdWide << 15 | PudShift << 20 | PudWide << 25));
>
>             +  CsrWrite (LOONGARCH_CSR_PWCTL1, (PgdShift | PgdWide << 6));
>
>             +
>
>             +  DEBUG ((DEBUG_INFO, "%a %d Enable Mmu Start PageBassAddress %p.\n", __func__, __LINE__, SwapperPageDir));
>
>             +
>
>             +  return EFI_SUCCESS;
>
>             +
>
>             +FreeTranslationTable:
>
>             +  if (SwapperPageDir != NULL) {
>
>             +    FreePages (SwapperPageDir, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
>
>             +  }
>
>             +
>
>             +  return EFI_UNSUPPORTED;
>
>             +}
>
>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
>
>             new file mode 100644
>
>             index 0000000000..9a681ce8e1
>
>             --- /dev/null
>
>             +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
>
>             @@ -0,0 +1,48 @@
>
>             +/** @file
>
>             +
>
>             +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>
>             +
>
>             +  SPDX-License-Identifier: BSD-2-Clause-Patent
>
>             +
>
>             +**/
>
>             +
>
>             +#ifndef TLB_H_
>
>             +#define TLB_H_
>
>             +
>
>             +/**
>
>             +  Invalid corresponding TLB entries are based on the address given
>
>             +
>
>             +  @param Address The address corresponding to the invalid page table entry
>
>             +
>
>             +  @retval  none
>
>             +**/
>
>             +VOID
>
>             +InvalidTlb (
>
>             +  UINTN  Address
>
>             +  );
>
>             +
>
>             +/**
>
>             +  TLB refill handler start.
>
>             +
>
>             +  @param  none
>
>             +
>
>             +  @retval none
>
>             +**/
>
>             +VOID
>
>             +HandleTlbRefillStart (
>
>             +  VOID
>
>             +  );
>
>             +
>
>             +/**
>
>             +  TLB refill handler end.
>
>             +
>
>             +  @param  none
>
>             +
>
>             +  @retval none
>
>             +**/
>
>             +VOID
>
>             +HandleTlbRefillEnd (
>
>             +  VOID
>
>             +  );
>
>             +
>
>             +#endif // TLB_H_
>
>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
>
>             new file mode 100644
>
>             index 0000000000..c9a8c16336
>
>             --- /dev/null
>
>             +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
>
>             @@ -0,0 +1,44 @@
>
>             +#------------------------------------------------------------------------------
>
>             +#
>
>             +# TLB operation functions
>
>             +#
>
>             +# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>
>             +#
>
>             +# SPDX-License-Identifier: BSD-2-Clause-Patent
>
>             +#
>
>             +#-----------------------------------------------------------------------------
>
>             +
>
>             +#include <Register/LoongArch64/Csr.h>
>
>             +
>
>             +ASM_GLOBAL ASM_PFX(HandleTlbRefillStart)
>
>             +ASM_GLOBAL ASM_PFX(HandleTlbRefillEnd)
>
>             +ASM_GLOBAL ASM_PFX(InvalidTlb)
>
>             +
>
>             +#
>
>             +#  Refill the page table.
>
>             +#  @param  VOID
>
>             +#  @retval  VOID
>
>             +#
>
>             +ASM_PFX(HandleTlbRefillStart):
>
>             +  csrwr   $t0, LOONGARCH_CSR_TLBRSAVE
>
>             +  csrrd   $t0, LOONGARCH_CSR_PGD
>
>             +  lddir   $t0, $t0, 3   #Put pud BaseAddress into T0
>
>             +  lddir   $t0, $t0, 2   #Put pmd BaseAddress into T0
>
>             +  lddir   $t0, $t0, 1   #Put pte BaseAddress into T0
>
>             +  ldpte   $t0, 0
>
>             +  ldpte   $t0, 1
>
>             +  tlbfill   // refill hi,lo0,lo1
>
>             +  csrrd   $t0, LOONGARCH_CSR_TLBRSAVE
>
>             +  ertn
>
>             +ASM_PFX(HandleTlbRefillEnd):
>
>             +
>
>             +#
>
>             +# Invalid corresponding TLB entries are based on the address given
>
>             +# @param a0 The address corresponding to the invalid page table entry
>
>             +# @retval  none
>
>             +#
>
>             +ASM_PFX(InvalidTlb):
>
>             +    invtlb  INVTLB_ADDR_GTRUE_OR_ASID, $zero, $a0
>
>             +    jirl    $zero, $ra, 0
>
>             +
>
>             +    .end
>
>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>
>             new file mode 100644
>
>             index 0000000000..45b15db4c9
>
>             --- /dev/null
>
>             +++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>
>             @@ -0,0 +1,44 @@
>
>             +## @file
>
>             +#  CPU Memory Map Unit PEI phase driver.
>
>             +#
>
>             +#  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                      = PeiCpuMmuLib
>
>             +  MODULE_UNI_FILE                = PeiCpuMmuLib.uni
>
>             +  FILE_GUID                      = F67EB983-AC2A-7550-AB69-3BC51A1C895B
>
>             +  MODULE_TYPE                    = PEIM
>
>             +  VERSION_STRING                 = 1.0
>
>             +  LIBRARY_CLASS                  = CpuMmuLib | SEC PEIM
>
>             +
>
>             +#
>
>             +#  VALID_ARCHITECTURES           = LOONGARCH64
>
>             +#
>
>             +
>
>             +[Sources.LoongArch64]
>
>             +  LoongArch64/TlbOperation.S   | GCC
>
>             +  LoongArch64/CommonMmuLib.c
>
>             +  LoongArch64/PeiCpuMmuLib.c
>
>             +  LoongArch64/CommonMmuLib.h
>
>             +  LoongArch64/Tlb.h
>
>             +  LoongArch64/Page.h
>
>             +
>
>             +[Packages]
>
>             +  MdePkg/MdePkg.dec
>
>             +  MdeModulePkg/MdeModulePkg.dec
>
>             +  UefiCpuPkg/UefiCpuPkg.dec
>
>             +
>
>             +[PCD]
>
>             +  gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask
>
>             +  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress
>
>             +
>
>             +[LibraryClasses]
>
>             +  CacheMaintenanceLib
>
>             +  DebugLib
>
>             +  MemoryAllocationLib
>
>             +  PcdLib
>
>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
>
>             new file mode 100644
>
>             index 0000000000..3e21334f3e
>
>             --- /dev/null
>
>             +++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
>
>             @@ -0,0 +1,14 @@
>
>             +// /** @file
>
>             +// CPU Memory Manager Unit library instance for PEI modules.
>
>             +//
>
>             +// CPU Memory Manager Unit library instance for PEI modules.
>
>             +//
>
>             +// 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 "CPU Memory Manager Unit library instance for PEI modules."
>
>             +
>
>             +#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for PEI modules."
>
>             diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
>
>             index 28eed85bce..178dc3c0f9 100644
>
>             --- a/UefiCpuPkg/UefiCpuPkg.dsc
>
>             +++ b/UefiCpuPkg/UefiCpuPkg.dsc
>
>             @@ -207,5 +207,9 @@
>
>                 UefiCpuPkg/CpuTimerDxeRiscV64/CpuTimerDxeRiscV64.inf
>
>                 UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf
>
>               
>
>             +[Components.LOONGARCH64]
>
>             +  UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>
>             +  UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>
>             +
>
>               [BuildOptions]
>
>                 *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
>
>           
>
>           
>
>           
>
>           
>
>     
>


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#116195): https://edk2.groups.io/g/devel/message/116195
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
       [not found]             ` <17B87F9FA8D0E543.14067@groups.io>
@ 2024-03-01  1:53               ` Chao Li
  0 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-03-01  1:53 UTC (permalink / raw)
  To: Ni, Ray, devel@edk2.groups.io, lersek@redhat.com
  Cc: Dong, Eric, Kumar, Rahul R, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

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

Hi Laszlo,

I'm sorry that your name was misspelled in the last email, sorry again.


Thanks,
Chao
On 2024/3/1 09:26, Chao Li wrote:
>
> Hi Ray, Lazslo,
>
> This library is almost complete to refactored, it refer to ARM and 
> RISC-V version, the API include set/get memory region attribute.
>
> I have one last question, in ARM and RISC-V version, even LoongArch 
> old and current version, they all request a configure interface, which 
> may be called in PEI or DXE stage, so should we open configure API? If 
> so, it is possible for ARM RISC-V and LongArch's MMU libraries to be 
> meged into the same instance if they wished.
>
> On 2024/2/2 12:30, Ni, Ray wrote:
>>
>> Sure.
>>
>> A local function externed by another PEIM and called from that PEIM 
>> is not a good practice.
>>
>> Thanks,
>>
>> Ray
>>
>> *From:* Chao Li <lichao@loongson.cn>
>> *Sent:* Friday, February 2, 2024 11:51 AM
>> *To:* devel@edk2.groups.io; Ni, Ray <ray.ni@intel.com>; lersek@redhat.com
>> *Cc:* Dong, Eric <eric.dong@intel.com>; Kumar, Rahul R 
>> <rahul.r.kumar@intel.com>; Gerd Hoffmann <kraxel@redhat.com>; Baoqi 
>> Zhang <zhangbaoqi@loongson.cn>; Dongyan Qian 
>> <qiandongyan@loongson.cn>; Xianglai Li <lixianglai@loongson.cn>; Bibo 
>> Mao <maobibo@loongson.cn>
>> *Subject:* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add 
>> CpuMmuLib to UefiCpuPkg
>>
>> Hi Ray,
>>
>> Thanks,
>> Chao
>>
>> On 2024/2/2 11:33, Ni, Ray wrote:
>>
>>     Chao,
>>
>>     ConfigureMemoryManagementUnit() is not a library API anymore.
>>
>>     Who will call it? I don’t see any reference to this function
>>     inside the lib source.
>>
>> It become a private API, called at the LoongArch virtual/physical 
>> machine PEIM, and when called it, the C file should extern this symbol.
>>
>> Please refer incoming and outgoing mails, this library probably need 
>> to be refactored, sothe APIs may change, so PLS focus on this module 
>> in future and help me more, please...
>>
>>     Thanks,
>>
>>     Ray
>>
>>     *From:* Chao Li <lichao@loongson.cn> <mailto:lichao@loongson.cn>
>>     *Sent:* Thursday, February 1, 2024 3:58 PM
>>     *To:* devel@edk2.groups.io; lersek@redhat.com
>>     *Cc:* Dong, Eric <eric.dong@intel.com>
>>     <mailto:eric.dong@intel.com>; Ni, Ray <ray.ni@intel.com>
>>     <mailto:ray.ni@intel.com>; Kumar, Rahul R
>>     <rahul.r.kumar@intel.com> <mailto:rahul.r.kumar@intel.com>; Gerd
>>     Hoffmann <kraxel@redhat.com> <mailto:kraxel@redhat.com>; Baoqi
>>     Zhang <zhangbaoqi@loongson.cn> <mailto:zhangbaoqi@loongson.cn>;
>>     Dongyan Qian <qiandongyan@loongson.cn>
>>     <mailto:qiandongyan@loongson.cn>; Xianglai Li
>>     <lixianglai@loongson.cn> <mailto:lixianglai@loongson.cn>; Bibo
>>     Mao <maobibo@loongson.cn> <mailto:maobibo@loongson.cn>
>>     *Subject:* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add
>>     CpuMmuLib to UefiCpuPkg
>>
>>     Hi Lazslo,
>>
>>     Thanks,
>>     Chao
>>
>>     On 2024/1/31 17:47, Laszlo Ersek wrote:
>>
>>         On 1/26/24 07:29, Chao Li wrote:
>>
>>             Add a new library named CpuMmuLib and add a LoongArch64 instance with in
>>
>>             the library.
>>
>>             It provides two-stage MMU libraryinstances, PEI and DXE.
>>
>>               
>>
>>             BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=4584
>>
>>               
>>
>>             Cc: Eric Dong<eric.dong@intel.com>  <mailto:eric.dong@intel.com>
>>
>>             Cc: Ray Ni<ray.ni@intel.com>  <mailto:ray.ni@intel.com>
>>
>>             Cc: Laszlo Ersek<lersek@redhat.com>  <mailto:lersek@redhat.com>
>>
>>             Cc: Rahul Kumar<rahul1.kumar@intel.com>  <mailto:rahul1.kumar@intel.com>
>>
>>             Cc: Gerd Hoffmann<kraxel@redhat.com>  <mailto:kraxel@redhat.com>
>>
>>             Signed-off-by: Chao Li<lichao@loongson.cn>  <mailto:lichao@loongson.cn>
>>
>>             Co-authored-by: Baoqi Zhang<zhangbaoqi@loongson.cn>  <mailto:zhangbaoqi@loongson.cn>
>>
>>             Co-authored-by: Dongyan Qian<qiandongyan@loongson.cn>  <mailto:qiandongyan@loongson.cn>
>>
>>             Co-authored-by: Xianglai Li<lixianglai@loongson.cn>  <mailto:lixianglai@loongson.cn>
>>
>>             Co-authored-by: Bibo Mao<maobibo@loongson.cn>  <mailto:maobibo@loongson.cn>
>>
>>             ---
>>
>>               UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf |  36 +
>>
>>               UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni |  14 +
>>
>>               .../CpuMmuLib/LoongArch64/CommonMmuLib.c      | 988 ++++++++++++++++++
>>
>>               .../CpuMmuLib/LoongArch64/CommonMmuLib.h      |  43 +
>>
>>               .../Library/CpuMmuLib/LoongArch64/Page.h      | 279 +++++
>>
>>               .../CpuMmuLib/LoongArch64/PeiCpuMmuLib.c      | 178 ++++
>>
>>               .../Library/CpuMmuLib/LoongArch64/Tlb.h       |  48 +
>>
>>               .../CpuMmuLib/LoongArch64/TlbOperation.S      |  44 +
>>
>>               UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf |  44 +
>>
>>               UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni |  14 +
>>
>>               UefiCpuPkg/UefiCpuPkg.dsc                     |   4 +
>>
>>               11 files changed, 1692 insertions(+)
>>
>>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>>
>>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
>>
>>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>>
>>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
>>
>>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
>>
>>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
>>
>>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
>>
>>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
>>
>>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>>
>>               create mode 100644 UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
>>
>>               
>>
>>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>>
>>             new file mode 100644
>>
>>             index 0000000000..bfce3ce96d
>>
>>             --- /dev/null
>>
>>             +++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>>
>>             @@ -0,0 +1,36 @@
>>
>>             +## @file
>>
>>             +#  CPU Memory Map Unit DXE phase driver.
>>
>>             +#
>>
>>             +#  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                      = DxeCpuMmuLib
>>
>>             +  MODULE_UNI_FILE                = DxeCpuMmuLib.uni
>>
>>             +  FILE_GUID                      = DA8F0232-FB14-42F0-922C-63104D2C70BE
>>
>>           
>>
>>         (1) This FILE_GUID was created from the FILE_GUID of
>>
>>         "ArmPkg/Library/ArmMmuLib/ArmMmuBaseLib.inf" by adding 1. That's not an
>>
>>         acceptable method for GUID generation.
>>
>>           
>>
>>         A method is only acceptable for GUID generation if multiple (= an
>>
>>         unlimited number of) parties can execute the method at any time, and the
>>
>>         output remains conflict-free.
>>
>>           
>>
>>         Taking an existent (known) FILE_GUID and incrementing it by 1 is not
>>
>>         such a method.
>>
>>           
>>
>>         Please regenerate the GUID with "uuidgen".
>>
>>           
>>
>>         Please also review the rest of your new GUIDs over this series (not only
>>
>>         FILE_GUIDs in INF files, but any other GUIDs, too).
>>
>>     OK, I will regenerate the GUID in next commit.
>>
>>
>>           
>>
>>           
>>
>>           
>>
>>             +  MODULE_TYPE                    = DXE_DRIVER
>>
>>             +  VERSION_STRING                 = 1.0
>>
>>             +  LIBRARY_CLASS                  = CpuMmuLib | DXE_DRIVER
>>
>>             +  CONSTRUCTOR                    = MmuInitialize
>>
>>             +
>>
>>             +#
>>
>>             +#  VALID_ARCHITECTURES           = LOONGARCH64
>>
>>             +#
>>
>>             +
>>
>>             +[Sources.LoongArch64]
>>
>>             +  LoongArch64/TlbOperation.S   | GCC
>>
>>             +  LoongArch64/CommonMmuLib.c
>>
>>             +  LoongArch64/Page.h
>>
>>             +  LoongArch64/Tlb.h
>>
>>             +
>>
>>             +[Packages]
>>
>>             +  MdePkg/MdePkg.dec
>>
>>             +  UefiCpuPkg/UefiCpuPkg.dec
>>
>>             +
>>
>>             +[LibraryClasses]
>>
>>             +  DebugLib
>>
>>             +  MemoryAllocationLib
>>
>>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
>>
>>             new file mode 100644
>>
>>             index 0000000000..7342249516
>>
>>             --- /dev/null
>>
>>             +++ b/UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.uni
>>
>>             @@ -0,0 +1,14 @@
>>
>>             +// /** @file
>>
>>             +// CPU Memory Manager Unit library instance for DXE modules.
>>
>>             +//
>>
>>             +// CPU Memory Manager Unit library instance for DXE modules.
>>
>>             +//
>>
>>             +// 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 "CPU Memory Manager Unit library instance for DXE modules."
>>
>>             +
>>
>>             +#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for DXE modules."
>>
>>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>>
>>             new file mode 100644
>>
>>             index 0000000000..2e852c3371
>>
>>             --- /dev/null
>>
>>             +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.c
>>
>>             @@ -0,0 +1,988 @@
>>
>>             +/** @file
>>
>>             +
>>
>>             +  CPU Memory Map Unit Handler Library common functions.
>>
>>             +
>>
>>             +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>>
>>             +
>>
>>             +  SPDX-License-Identifier: BSD-2-Clause-Patent
>>
>>             +
>>
>>             +  @par Glossary:
>>
>>             +    - Pgd or Pgd or PGD    - Page Global Directory
>>
>>             +    - Pud or Pud or PUD    - Page Upper Directory
>>
>>             +    - Pmd or Pmd or PMD    - Page Middle Directory
>>
>>             +    - Pte or pte or PTE    - Page Table Entry
>>
>>             +    - Val or VAL or val    - Value
>>
>>             +    - Dir    - Directory
>>
>>             +**/
>>
>>             +#include <Uefi.h>
>>
>>             +#include <Library/BaseLib.h>
>>
>>             +#include <Library/BaseMemoryLib.h>
>>
>>             +#include <Library/CpuMmuLib.h>
>>
>>             +#include <Library/DebugLib.h>
>>
>>             +#include <Library/MemoryAllocationLib.h>
>>
>>             +#include <Register/LoongArch64/Csr.h>
>>
>>             +#include "Tlb.h"
>>
>>             +#include "Page.h"
>>
>>             +
>>
>>             +#define SWAP_PAGE_DIR              CsrRead(LOONGARCH_CSR_PGDL)
>>
>>           
>>
>>         (2) Missing space after "CsrRead".
>>
>>     OK.
>>
>>
>>           
>>
>>           
>>
>>             +#define EFI_MEMORY_CACHETYPE_MASK  (EFI_MEMORY_UC  | \
>>
>>             +                                    EFI_MEMORY_WC  | \
>>
>>             +                                    EFI_MEMORY_WT  | \
>>
>>             +                                    EFI_MEMORY_WB  | \
>>
>>             +                                    EFI_MEMORY_UCE   \
>>
>>             +                                    )
>>
>>           
>>
>>         (3) This seems to come from "ArmPkg/Include/Library/ArmLib.h"; it's not
>>
>>         great regardless: we shouldn't use the EFI_ prefix for symbols that are
>>
>>         not standard. (Put differently, EFI_ is a reserved namespace prefix for
>>
>>         the UEFI and PI specs.)
>>
>>     OK, I will rename with out EFI_perfix next time.
>>
>>
>>           
>>
>>           
>>
>>             +
>>
>>             +BOOLEAN  mMmuInited = FALSE;
>>
>>           
>>
>>         (4) This should be STATIC, I believe.
>>
>>           
>>
>>         (5) So this is the point where I realize that the library design makes
>>
>>         no sense to me.
>>
>>           
>>
>>         Normally you create SEC/PEI vs. DXE phase instances of a library
>>
>>         because, using writable global variables, you can gain performance (you
>>
>>         can remember the initialization) in DXE, but in SEC/PEI, you don't have
>>
>>         writeable global variables.
>>
>>           
>>
>>         This pattern does not seem to apply here, or at least it doesn't seem to
>>
>>         work. Here's why:
>>
>>           
>>
>>         - the variable mMmuInited and the function MmuInitialize (which contains
>>
>>         an assignment to the variable) are in "CommonMmuLib.c", which gets built
>>
>>         into both library instances. This makes no sense; that assignment cannot
>>
>>         work in SEC/PEI (I presume you're not going to have writeable globals --
>>
>>         executing from flash).
>>
>>           
>>
>>         - I think you may be trying to make up for that problem by checking
>>
>>         SWAP_PAGE_DIR (the LOONGARCH_CSR_PGDL register) in MmuInitialize() and
>>
>>         MmuIsInit(). That doesn't seem right.
>>
>>     Yes, you are right, the PEI stage may be executed from flash, in
>>     this case, we have no way to write global variables, so we can
>>     only check whether CSR_PGDL is NULL in the DXE stage to get
>>     whether the MMU has been initialized.
>>
>>           
>>
>>           
>>
>>         - The PEI instance of the library contains an EFIAPI function called
>>
>>         ConfigureMemoryManagementUnit(). This function is never called in this
>>
>>         patch, which makes me think it's supposed to be called from driver or
>>
>>         application code (i.e., not from within the library itself, but from
>>
>>         client code). However, ConfigureMemoryManagementUnit() is also not
>>
>>         declared in the previous patch (in
>>
>>         "UefiCpuPkg/Include/Library/CpuMmuLib.h"); therefore client code cannot
>>
>>         reach it at all -- so that function doesn't even belong in this library.
>>
>>     This API was discussed with Ray in the V3 patch 13. Actually, the
>>     CpuMmuLib.h include more APIs before, Ray believed that other
>>     APIs should not be open to users or some APIs can be done by the
>>     base APIs, and in my opinion, the ConfigureMemoryManagementUnit()
>>     is a private API, so in V4, it has been removed from CpuMmuLib.h.
>>
>>     So what do you think? the ConfigureMemoryManagementUnit should be
>>     added back?
>>
>>           
>>
>>           
>>
>>         - MmuInitialize() doesn't actually do anything, it just checks (via the
>>
>>         CSR) whether some other component has already initialized the MMU
>>
>>         (likely with ConfigureMemoryManagementUnit()). And the sole purpose of
>>
>>         MmuIsInit() appears to be to return from the public library APIs
>>
>>         SetMemoryRegionAttributes() and GetMemoryRegionAttributes() early, if
>>
>>         that "other" (unknown) component has not called
>>
>>         ConfigureMemoryManagementUnit() yet.
>>
>>           
>>
>>         So, I don't understand what you are trying to do. I could explain how to
>>
>>         keep the initialization logic *differences* minimal between the SEC/PEI
>>
>>         and the DXE library instances, but I don't understand how / when the MMU
>>
>>         initialization is supposed to occur in the first place.
>>
>>     Let's me tell you this library how to work:
>>
>>     In PEI stage, in addition to ensuring that the MMU is not used,
>>     the user must call the ConfigureMemoryManagementUnit
>>     toinitialization the MMU, such as filling the static page tables,
>>     set the TLB refill exception entry point, set the page size etc.
>>     the ConfigureMemoryManagementUnit is a private API but related to
>>     ARCH.
>>
>>     During DXE stage, this library will provide services for CpuDxe
>>     and other drivers, but almost changes to memory page attributes
>>     use the protocols provided by CpuDxe. That is why the CpuMmuLib.h
>>     only contains two APIs, it only can get/set the attribute.
>>
>>     In short, the PEI stage needs to initialize and set up TLB refill
>>     entry point and method(dynamically populate the TLB, keep the PA
>>     == VA), and DXE stage is provides services for changing the
>>     memory attributes.
>>
>>           
>>
>>           
>>
>>         (6) The patch is too large in general. You should construct these
>>
>>         library instances over multiple patches. The first patch could add some
>>
>>         declarations / macro definitions, such as "Page.h" and "Tlb.h". Another
>>
>>         patch could add the assembly language helper functions. Another patch
>>
>>         could add the PEI phase library instance (including the common code). A
>>
>>         final patch could add the DXE-phase bits.
>>
>>           
>>
>>         The idea is to proceed in layers, logically building one on top of the
>>
>>         other. It's fine if the library doesn't build initially; there is no
>>
>>         attempt to build it anyway until you actually reference the INF files in
>>
>>         some DSC files. So please split this at least in 4 patches.
>>
>>     OK, I will try it.
>>
>>
>>           
>>
>>           
>>
>>         (7) The commit message should explain the expected usage model in detail.
>>
>>     OK.
>>
>>
>>           
>>
>>           
>>
>>         Best regards,
>>
>>         Laszlo
>>
>>           
>>
>>           
>>
>>             +
>>
>>             +/**
>>
>>             +  Check to see if mmu successfully initializes.
>>
>>             +
>>
>>             +  @param  VOID.
>>
>>             +
>>
>>             +  @retval  TRUE  Initialization has been completed.
>>
>>             +           FALSE Initialization did not complete.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +BOOLEAN
>>
>>             +MmuIsInit (
>>
>>             +  VOID
>>
>>             +  )
>>
>>             +{
>>
>>             +  if (mMmuInited || (SWAP_PAGE_DIR != 0)) {
>>
>>             +    return TRUE;
>>
>>             +  }
>>
>>             +
>>
>>             +  return FALSE;
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Iterates through the page directory to initialize it.
>>
>>             +
>>
>>             +  @param  Dst  A pointer to the directory of the page to initialize.
>>
>>             +  @param  Num  The number of page directories to initialize.
>>
>>             +  @param  Src  A pointer to the data used to initialize the page directory.
>>
>>             +
>>
>>             +  @return VOID.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +VOID
>>
>>             +PageDirInit (
>>
>>             +  IN VOID   *Dst,
>>
>>             +  IN UINTN  Num,
>>
>>             +  IN VOID   *Src
>>
>>             +  )
>>
>>             +{
>>
>>             +  UINTN  *Ptr;
>>
>>             +  UINTN  *End;
>>
>>             +  UINTN  Entry;
>>
>>             +
>>
>>             +  Entry = (UINTN)Src;
>>
>>             +  Ptr   = (UINTN *)Dst;
>>
>>             +  End   = Ptr + Num;
>>
>>             +
>>
>>             +  for ( ; Ptr < End; Ptr++) {
>>
>>             +    *Ptr = Entry;
>>
>>             +  }
>>
>>             +
>>
>>             +  return;
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Gets the virtual address corresponding to the page global directory table entry.
>>
>>             +
>>
>>             +  @param  Address  the virtual address for the table entry.
>>
>>             +
>>
>>             +  @retval PGD A pointer to get the table item.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +PGD *
>>
>>             +PgdOffset (
>>
>>             +  IN UINTN  Address
>>
>>             +  )
>>
>>             +{
>>
>>             +  return (PGD *)(SWAP_PAGE_DIR) + PGD_INDEX (Address);
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Gets the virtual address corresponding to the page upper directory table entry.
>>
>>             +
>>
>>             +  @param  Pgd  A pointer to a page global directory table entry.
>>
>>             +  @param  Address  the virtual address for the table entry.
>>
>>             +
>>
>>             +  @retval PUD A pointer to get the table item.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +PUD *
>>
>>             +PudOffset (
>>
>>             +  IN PGD    *Pgd,
>>
>>             +  IN UINTN  Address
>>
>>             +  )
>>
>>             +{
>>
>>             +  UINTN  PgdVal;
>>
>>             +
>>
>>             +  PgdVal = (UINTN)PGD_VAL (*Pgd);
>>
>>             +
>>
>>             +  return (PUD *)PgdVal + PUD_INDEX (Address);
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Gets the virtual address corresponding to the page middle directory table entry.
>>
>>             +
>>
>>             +  @param  Pud  A pointer to a page upper directory table entry.
>>
>>             +  @param  Address  the virtual address for the table entry.
>>
>>             +
>>
>>             +  @retval PMD A pointer to get the table item.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +PMD *
>>
>>             +PmdOffset (
>>
>>             +  IN PUD    *Pud,
>>
>>             +  IN UINTN  Address
>>
>>             +  )
>>
>>             +{
>>
>>             +  UINTN  PudVal;
>>
>>             +
>>
>>             +  PudVal = PUD_VAL (*Pud);
>>
>>             +
>>
>>             +  return (PMD *)PudVal + PMD_INDEX (Address);
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Gets the virtual address corresponding to the page table entry.
>>
>>             +
>>
>>             +  @param  Pmd  A pointer to a page middle directory table entry.
>>
>>             +  @param  Address  the virtual address for the table entry.
>>
>>             +
>>
>>             +  @retval PTE A pointer to get the table item.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +PTE *
>>
>>             +PteOffset (
>>
>>             +  IN PMD    *Pmd,
>>
>>             +  IN UINTN  Address
>>
>>             +  )
>>
>>             +{
>>
>>             +  UINTN  PmdVal;
>>
>>             +
>>
>>             +  PmdVal = (UINTN)PMD_VAL (*Pmd);
>>
>>             +
>>
>>             +  return (PTE *)PmdVal + PTE_INDEX (Address);
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Sets the value of the page table entry.
>>
>>             +
>>
>>             +  @param  Pte  A pointer to a page table entry.
>>
>>             +  @param  PteVal  The value of the page table entry to set.
>>
>>             +
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +VOID
>>
>>             +SetPte (
>>
>>             +  IN PTE  *Pte,
>>
>>             +  IN PTE  PteVal
>>
>>             +  )
>>
>>             +{
>>
>>             +  *Pte = PteVal;
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Sets the value of the page global directory.
>>
>>             +
>>
>>             +  @param  Pgd  A pointer to a page global directory.
>>
>>             +  @param  Pud  The value of the page global directory to set.
>>
>>             +
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +VOID
>>
>>             +SetPgd (
>>
>>             +  IN PGD  *Pgd,
>>
>>             +  IN PUD  *Pud
>>
>>             +  )
>>
>>             +{
>>
>>             +  *Pgd = (PGD) {
>>
>>             +    ((UINTN)Pud)
>>
>>             +  };
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Sets the value of the page upper directory.
>>
>>             +
>>
>>             +  @param  Pud  A pointer to a page upper directory.
>>
>>             +  @param  Pmd  The value of the page upper directory to set.
>>
>>             +
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +VOID
>>
>>             +SetPud (
>>
>>             +  IN PUD  *Pud,
>>
>>             +  IN PMD  *Pmd
>>
>>             +  )
>>
>>             +{
>>
>>             +  *Pud = (PUD) {
>>
>>             +    ((UINTN)Pmd)
>>
>>             +  };
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Sets the value of the page middle directory.
>>
>>             +
>>
>>             +  @param  Pmd  A pointer to a page middle directory.
>>
>>             +  @param  Pte  The value of the page middle directory to set.
>>
>>             +
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +VOID
>>
>>             +SetPmd (
>>
>>             +  IN PMD  *Pmd,
>>
>>             +  IN PTE  *Pte
>>
>>             +  )
>>
>>             +{
>>
>>             +  *Pmd = (PMD) {
>>
>>             +    ((UINTN)Pte)
>>
>>             +  };
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Free up memory space occupied by page tables.
>>
>>             +
>>
>>             +  @param  Pte  A pointer to the page table.
>>
>>             +
>>
>>             +**/
>>
>>             +VOID
>>
>>             +PteFree (
>>
>>             +  IN PTE  *Pte
>>
>>             +  )
>>
>>             +{
>>
>>             +  FreePages ((VOID *)Pte, 1);
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Free up memory space occupied by page middle directory.
>>
>>             +
>>
>>             +  @param  Pmd  A pointer to the page middle directory.
>>
>>             +
>>
>>             +**/
>>
>>             +VOID
>>
>>             +PmdFree (
>>
>>             +  IN PMD  *Pmd
>>
>>             +  )
>>
>>             +{
>>
>>             +  FreePages ((VOID *)Pmd, 1);
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Free up memory space occupied by page upper directory.
>>
>>             +
>>
>>             +  @param  Pud  A pointer to the page upper directory.
>>
>>             +
>>
>>             +**/
>>
>>             +VOID
>>
>>             +PudFree (
>>
>>             +  IN PUD  *Pud
>>
>>             +  )
>>
>>             +{
>>
>>             +  FreePages ((VOID *)Pud, 1);
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Requests the memory space required for the page upper directory,
>>
>>             +  initializes it, and places it in the specified page global directory
>>
>>             +
>>
>>             +  @param  Pgd  A pointer to the page global directory.
>>
>>             +
>>
>>             +  @retval  EFI_SUCCESS  Memory request successful.
>>
>>             +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +EFI_STATUS
>>
>>             +PudAlloc (
>>
>>             +  IN PGD  *Pgd
>>
>>             +  )
>>
>>             +{
>>
>>             +  PUD  *Pud;
>>
>>             +
>>
>>             +  Pud = (PUD *)AllocatePages (1);
>>
>>             +  if (Pud == NULL) {
>>
>>             +    return EFI_OUT_OF_RESOURCES;
>>
>>             +  }
>>
>>             +
>>
>>             +  PageDirInit ((VOID *)Pud, ENTRYS_PER_PUD, (VOID *)INVALID_PAGE);
>>
>>             +
>>
>>             +  SetPgd (Pgd, Pud);
>>
>>             +
>>
>>             +  return EFI_SUCCESS;
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Requests the memory space required for the page middle directory,
>>
>>             +  initializes it, and places it in the specified page upper directory
>>
>>             +
>>
>>             +  @param  Pud  A pointer to the page upper directory.
>>
>>             +
>>
>>             +  @retval  EFI_SUCCESS  Memory request successful.
>>
>>             +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +EFI_STATUS
>>
>>             +PmdAlloc (
>>
>>             +  IN PUD  *Pud
>>
>>             +  )
>>
>>             +{
>>
>>             +  PMD  *Pmd;
>>
>>             +
>>
>>             +  Pmd = (PMD *)AllocatePages (1);
>>
>>             +  if (!Pmd) {
>>
>>             +    return EFI_OUT_OF_RESOURCES;
>>
>>             +  }
>>
>>             +
>>
>>             +  PageDirInit ((VOID *)Pmd, ENTRYS_PER_PMD, (VOID *)INVALID_PAGE);
>>
>>             +
>>
>>             +  SetPud (Pud, Pmd);
>>
>>             +
>>
>>             +  return EFI_SUCCESS;
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Requests the memory space required for the page table,
>>
>>             +  initializes it, and places it in the specified page middle directory
>>
>>             +
>>
>>             +  @param  Pmd  A pointer to the page middle directory.
>>
>>             +
>>
>>             +  @retval  EFI_SUCCESS  Memory request successful.
>>
>>             +  @retval  EFI_OUT_OF_RESOURCES  Resource exhaustion cannot be requested to memory.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +EFI_STATUS
>>
>>             +PteAlloc (
>>
>>             +  IN PMD  *Pmd
>>
>>             +  )
>>
>>             +{
>>
>>             +  PTE  *Pte;
>>
>>             +
>>
>>             +  Pte = (PTE *)AllocatePages (1);
>>
>>             +  if (!Pte) {
>>
>>             +    return EFI_OUT_OF_RESOURCES;
>>
>>             +  }
>>
>>             +
>>
>>             +  Pte = ZeroMem (Pte, EFI_PAGE_SIZE);
>>
>>             +
>>
>>             +  SetPmd (Pmd, Pte);
>>
>>             +
>>
>>             +  return EFI_SUCCESS;
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Requests the memory space required for the page upper directory,
>>
>>             +  initializes it, and places it in the specified page global directory,
>>
>>             +  and get the page upper directory entry corresponding to the virtual address.
>>
>>             +
>>
>>             +  @param  Pgd      A pointer to the page global directory.
>>
>>             +  @param  Address  The corresponding virtual address of the page table entry.
>>
>>             +
>>
>>             +  @retval          A pointer to the page upper directory entry. Return NULL, if
>>
>>             +                   allocate the memory buffer is fail.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +PUD *
>>
>>             +PudAllocGet (
>>
>>             +  IN PGD    *Pgd,
>>
>>             +  IN UINTN  Address
>>
>>             +  )
>>
>>             +{
>>
>>             +  EFI_STATUS  Status;
>>
>>             +
>>
>>             +  if (PGD_IS_EMPTY (*Pgd)) {
>>
>>             +    Status = PudAlloc (Pgd);
>>
>>             +    ASSERT_EFI_ERROR (Status);
>>
>>             +    if (EFI_ERROR (Status)) {
>>
>>             +      return NULL;
>>
>>             +    }
>>
>>             +  }
>>
>>             +
>>
>>             +  return PudOffset (Pgd, Address);
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Requests the memory space required for the page middle directory,
>>
>>             +  initializes it, and places it in the specified page upper directory,
>>
>>             +  and get the page middle directory entry corresponding to the virtual address.
>>
>>             +
>>
>>             +  @param  Pud      A pointer to the page upper directory.
>>
>>             +  @param  Address  The corresponding virtual address of the page table entry.
>>
>>             +
>>
>>             +  @retval          A pointer to the page middle directory entry. Return NULL, if
>>
>>             +                   allocate the memory buffer is fail.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +PMD *
>>
>>             +PmdAllocGet (
>>
>>             +  IN PUD    *Pud,
>>
>>             +  IN UINTN  Address
>>
>>             +  )
>>
>>             +{
>>
>>             +  EFI_STATUS  Status;
>>
>>             +
>>
>>             +  if (PUD_IS_EMPTY (*Pud)) {
>>
>>             +    Status = PmdAlloc (Pud);
>>
>>             +    ASSERT_EFI_ERROR (Status);
>>
>>             +    if (EFI_ERROR (Status)) {
>>
>>             +      return NULL;
>>
>>             +    }
>>
>>             +  }
>>
>>             +
>>
>>             +  return PmdOffset (Pud, Address);
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Requests the memory space required for the page table,
>>
>>             +  initializes it, and places it in the specified page middle directory,
>>
>>             +  and get the page table entry corresponding to the virtual address.
>>
>>             +
>>
>>             +  @param  Pmd      A pointer to the page upper directory.
>>
>>             +  @param  Address  The corresponding virtual address of the page table entry.
>>
>>             +
>>
>>             +  @retval          A pointer to the page table entry. Return NULL, if allocate
>>
>>             +                   the memory buffer is fail.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +PTE *
>>
>>             +PteAllocGet (
>>
>>             +  IN PMD    *Pmd,
>>
>>             +  IN UINTN  Address
>>
>>             +  )
>>
>>             +{
>>
>>             +  EFI_STATUS  Status;
>>
>>             +
>>
>>             +  if (PMD_IS_EMPTY (*Pmd)) {
>>
>>             +    Status = PteAlloc (Pmd);
>>
>>             +    ASSERT_EFI_ERROR (Status);
>>
>>             +    if (EFI_ERROR (Status)) {
>>
>>             +      return NULL;
>>
>>             +    }
>>
>>             +  }
>>
>>             +
>>
>>             +  return PteOffset (Pmd, Address);
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Gets the physical address of the page table entry corresponding to the specified virtual address.
>>
>>             +
>>
>>             +  @param  Address  The corresponding virtual address of the page table entry.
>>
>>             +
>>
>>             +  @retval  A pointer to the page table entry.
>>
>>             +  @retval  NULL
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +PTE *
>>
>>             +GetPteAddress (
>>
>>             +  IN UINTN  Address
>>
>>             +  )
>>
>>             +{
>>
>>             +  PGD  *Pgd;
>>
>>             +  PUD  *Pud;
>>
>>             +  PMD  *Pmd;
>>
>>             +
>>
>>             +  Pgd = PgdOffset (Address);
>>
>>             +
>>
>>             +  if (PGD_IS_EMPTY (*Pgd)) {
>>
>>             +    return NULL;
>>
>>             +  }
>>
>>             +
>>
>>             +  Pud = PudOffset (Pgd, Address);
>>
>>             +
>>
>>             +  if (PUD_IS_EMPTY (*Pud)) {
>>
>>             +    return NULL;
>>
>>             +  }
>>
>>             +
>>
>>             +  Pmd = PmdOffset (Pud, Address);
>>
>>             +  if (PMD_IS_EMPTY (*Pmd)) {
>>
>>             +    return NULL;
>>
>>             +  }
>>
>>             +
>>
>>             +  if (IS_HUGE_PAGE (Pmd->PmdVal)) {
>>
>>             +    return ((PTE *)Pmd);
>>
>>             +  }
>>
>>             +
>>
>>             +  return PteOffset (Pmd, Address);
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Gets the Attributes of Huge Page.
>>
>>             +
>>
>>             +  @param  Pmd  A pointer to the page middle directory.
>>
>>             +
>>
>>             +  @retval     Value of Attributes.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +UINTN
>>
>>             +GetHugePageAttributes (
>>
>>             +  IN  PMD  *Pmd
>>
>>             +  )
>>
>>             +{
>>
>>             +  UINTN  Attributes;
>>
>>             +  UINTN  GlobalFlag;
>>
>>             +  UINTN  HugeVal;
>>
>>             +
>>
>>             +  HugeVal     = PMD_VAL (*Pmd);
>>
>>             +  Attributes  = HugeVal & (~HUGEP_PAGE_MASK);
>>
>>             +  GlobalFlag  = ((Attributes & (1 << PAGE_HGLOBAL_SHIFT)) >> PAGE_HGLOBAL_SHIFT) << PAGE_GLOBAL_SHIFT;
>>
>>             +  Attributes &= ~(1 << PAGE_HGLOBAL_SHIFT);
>>
>>             +  Attributes |= GlobalFlag;
>>
>>             +  return Attributes;
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Establishes a page table entry based on the specified memory region.
>>
>>             +
>>
>>             +  @param  Pmd  A pointer to the page middle directory.
>>
>>             +  @param  Address  The memory space start address.
>>
>>             +  @param  End  The end address of the memory space.
>>
>>             +  @param  Attributes  Memory space Attributes.
>>
>>             +
>>
>>             +  @retval     EFI_SUCCESS   The page table entry was created successfully.
>>
>>             +  @retval     EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +EFI_STATUS
>>
>>             +MemoryMapPteRange (
>>
>>             +  IN PMD    *Pmd,
>>
>>             +  IN UINTN  Address,
>>
>>             +  IN UINTN  End,
>>
>>             +  IN UINTN  Attributes
>>
>>             +  )
>>
>>             +{
>>
>>             +  PTE      *Pte;
>>
>>             +  PTE      PteVal;
>>
>>             +  BOOLEAN  UpDate;
>>
>>             +
>>
>>             +  Pte = PteAllocGet (Pmd, Address);
>>
>>             +  if (!Pte) {
>>
>>             +    return EFI_OUT_OF_RESOURCES;
>>
>>             +  }
>>
>>             +
>>
>>             +  DEBUG ((
>>
>>             +    DEBUG_INFO,
>>
>>             +    "%a %d Address %p End %p  Attributes %llx\n",
>>
>>             +    __func__,
>>
>>             +    __LINE__,
>>
>>             +    Address,
>>
>>             +    End,
>>
>>             +    Attributes
>>
>>             +    ));
>>
>>             +
>>
>>             +  do {
>>
>>             +    UpDate = FALSE;
>>
>>             +    PteVal = MAKE_PTE (Address, Attributes);
>>
>>             +
>>
>>             +    if ((!PTE_IS_EMPTY (*Pte)) &&
>>
>>             +        (PTE_VAL (*Pte) != PTE_VAL (PteVal)))
>>
>>             +    {
>>
>>             +      UpDate = TRUE;
>>
>>             +    }
>>
>>             +
>>
>>             +    SetPte (Pte, PteVal);
>>
>>             +    if (UpDate) {
>>
>>             +      InvalidTlb (Address);
>>
>>             +    }
>>
>>             +  } while (Pte++, Address += EFI_PAGE_SIZE, Address != End);
>>
>>             +
>>
>>             +  return EFI_SUCCESS;
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Convert Huge Page to Page.
>>
>>             +
>>
>>             +  @param  Pmd  A pointer to the page middle directory.
>>
>>             +  @param  Address  The memory space start address.
>>
>>             +  @param  End  The end address of the memory space.
>>
>>             +  @param  Attributes  Memory space Attributes.
>>
>>             +
>>
>>             +  @retval  EFI_SUCCESS   The page table entry was created successfully.
>>
>>             +  @retval  EFI_OUT_OF_RESOURCES  Page table entry establishment failed due to resource exhaustion.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +EFI_STATUS
>>
>>             +ConvertHugePageToPage (
>>
>>             +  IN  PMD   *Pmd,
>>
>>             +  IN UINTN  Address,
>>
>>             +  IN UINTN  End,
>>
>>             +  IN UINTN  Attributes
>>
>>             +  )
>>
>>             +{
>>
>>             +  UINTN       OldAttributes;
>>
>>             +  UINTN       HugePageEnd;
>>
>>             +  UINTN       HugePageStart;
>>
>>             +  EFI_STATUS  Status;
>>
>>             +
>>
>>             +  Status = EFI_SUCCESS;
>>
>>             +
>>
>>             +  if ((PMD_IS_EMPTY (*Pmd)) ||
>>
>>             +      (!IS_HUGE_PAGE (Pmd->PmdVal)))
>>
>>             +  {
>>
>>             +    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
>>
>>             +  } else {
>>
>>             +    OldAttributes = GetHugePageAttributes (Pmd);
>>
>>             +    if (Attributes == OldAttributes) {
>>
>>             +      return Status;
>>
>>             +    }
>>
>>             +
>>
>>             +    SetPmd (Pmd, (PTE *)(INVALID_PAGE));
>>
>>             +    HugePageStart = Address & PMD_MASK;
>>
>>             +    HugePageEnd   = HugePageStart + HUGE_PAGE_SIZE;
>>
>>             +    ASSERT (HugePageEnd >= End);
>>
>>             +
>>
>>             +    if (Address > HugePageStart) {
>>
>>             +      Status |= MemoryMapPteRange (Pmd, HugePageStart, Address, OldAttributes);
>>
>>             +    }
>>
>>             +
>>
>>             +    Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
>>
>>             +
>>
>>             +    if (End < HugePageEnd) {
>>
>>             +      Status |= MemoryMapPteRange (Pmd, End, HugePageEnd, OldAttributes);
>>
>>             +    }
>>
>>             +  }
>>
>>             +
>>
>>             +  return Status;
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Establishes a page middle directory based on the specified memory region.
>>
>>             +
>>
>>             +  @param  Pud  A pointer to the page upper directory.
>>
>>             +  @param  Address  The memory space start address.
>>
>>             +  @param  End  The end address of the memory space.
>>
>>             +  @param  Attributes  Memory space Attributes.
>>
>>             +
>>
>>             +  @retval     EFI_SUCCESS   The page middle directory was created successfully.
>>
>>             +  @retval     EFI_OUT_OF_RESOURCES  Page middle directory establishment failed due to resource exhaustion.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +EFI_STATUS
>>
>>             +MemoryMapPmdRange (
>>
>>             +  IN PUD    *Pud,
>>
>>             +  IN UINTN  Address,
>>
>>             +  IN UINTN  End,
>>
>>             +  IN UINTN  Attributes
>>
>>             +  )
>>
>>             +{
>>
>>             +  PMD      *Pmd;
>>
>>             +  UINTN    Next;
>>
>>             +  PTE      PteVal;
>>
>>             +  BOOLEAN  UpDate;
>>
>>             +
>>
>>             +  Pmd = PmdAllocGet (Pud, Address);
>>
>>             +  if (Pmd == NULL) {
>>
>>             +    return EFI_OUT_OF_RESOURCES;
>>
>>             +  }
>>
>>             +
>>
>>             +  do {
>>
>>             +    Next = PMD_ADDRESS_END (Address, End);
>>
>>             +    if (((Address & (~PMD_MASK)) == 0) &&
>>
>>             +        ((Next &  (~PMD_MASK)) == 0) &&
>>
>>             +        (PMD_IS_EMPTY (*Pmd) || IS_HUGE_PAGE (Pmd->PmdVal)))
>>
>>             +    {
>>
>>             +      UpDate = FALSE;
>>
>>             +      PteVal = MAKE_HUGE_PTE (Address, Attributes);
>>
>>             +
>>
>>             +      if ((!PMD_IS_EMPTY (*Pmd)) &&
>>
>>             +          (PMD_VAL (*Pmd) != PTE_VAL (PteVal)))
>>
>>             +      {
>>
>>             +        UpDate = TRUE;
>>
>>             +      }
>>
>>             +
>>
>>             +      SetPmd (Pmd, (PTE *)PteVal.PteVal);
>>
>>             +      if (UpDate) {
>>
>>             +        InvalidTlb (Address);
>>
>>             +      }
>>
>>             +    } else {
>>
>>             +      ConvertHugePageToPage (Pmd, Address, Next, Attributes);
>>
>>             +    }
>>
>>             +  } while (Pmd++, Address = Next, Address != End);
>>
>>             +
>>
>>             +  return EFI_SUCCESS;
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Establishes a page upper directory based on the specified memory region.
>>
>>             +
>>
>>             +  @param  Pgd  A pointer to the page global directory.
>>
>>             +  @param  Address  The memory space start address.
>>
>>             +  @param  End  The end address of the memory space.
>>
>>             +  @param  Attributes  Memory space Attributes.
>>
>>             +
>>
>>             +  @retval     EFI_SUCCESS   The page upper directory was created successfully.
>>
>>             +  @retval     EFI_OUT_OF_RESOURCES  Page upper directory establishment failed due to resource exhaustion.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +EFI_STATUS
>>
>>             +MemoryMapPudRange (
>>
>>             +  IN PGD    *Pgd,
>>
>>             +  IN UINTN  Address,
>>
>>             +  IN UINTN  End,
>>
>>             +  IN UINTN  Attributes
>>
>>             +  )
>>
>>             +{
>>
>>             +  PUD    *Pud;
>>
>>             +  UINTN  Next;
>>
>>             +
>>
>>             +  Pud = PudAllocGet (Pgd, Address);
>>
>>             +  if (Pud == NULL) {
>>
>>             +    return EFI_OUT_OF_RESOURCES;
>>
>>             +  }
>>
>>             +
>>
>>             +  do {
>>
>>             +    Next = PUD_ADDRESS_END (Address, End);
>>
>>             +    if (EFI_ERROR (MemoryMapPmdRange (Pud, Address, Next, Attributes))) {
>>
>>             +      return EFI_OUT_OF_RESOURCES;
>>
>>             +    }
>>
>>             +  } while (Pud++, Address = Next, Address != End);
>>
>>             +
>>
>>             +  return EFI_SUCCESS;
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Establishes a page global directory based on the specified memory region.
>>
>>             +
>>
>>             +  @param  Start  The memory space start address.
>>
>>             +  @param  End  The end address of the memory space.
>>
>>             +  @param  Attributes  Memory space Attributes.
>>
>>             +
>>
>>             +  @retval     EFI_SUCCESS   The page global directory was created successfully.
>>
>>             +  @retval     EFI_OUT_OF_RESOURCES  Page global directory establishment failed due to resource exhaustion.
>>
>>             +**/
>>
>>             +STATIC
>>
>>             +EFI_STATUS
>>
>>             +MemoryMapPageRange (
>>
>>             +  IN UINTN  Start,
>>
>>             +  IN UINTN  End,
>>
>>             +  IN UINTN  Attributes
>>
>>             +  )
>>
>>             +{
>>
>>             +  PGD         *Pgd;
>>
>>             +  UINTN       Next;
>>
>>             +  UINTN       Address;
>>
>>             +  EFI_STATUS  Err;
>>
>>             +
>>
>>             +  Address = Start;
>>
>>             +
>>
>>             +  /* Get PGD(PTE PMD PUD PGD) in PageTables */
>>
>>             +  Pgd = PgdOffset (Address);
>>
>>             +  do {
>>
>>             +    Next = PGD_ADDRESS_END (Address, End);
>>
>>             +    /* Get Next Align Page to Map */
>>
>>             +    Err = MemoryMapPudRange (Pgd, Address, Next, Attributes);
>>
>>             +    if (Err) {
>>
>>             +      return Err;
>>
>>             +    }
>>
>>             +  } while (Pgd++, Address = Next, Address != End);
>>
>>             +
>>
>>             +  return EFI_SUCCESS;
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Page tables are established from memory-mapped tables.
>>
>>             +
>>
>>             +  @param  MemoryRegion   A pointer to a memory-mapped table entry.
>>
>>             +
>>
>>             +  @retval     EFI_SUCCESS   The page table was created successfully.
>>
>>             +  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
>>
>>             +**/
>>
>>             +EFI_STATUS
>>
>>             +FillTranslationTable (
>>
>>             +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
>>
>>             +  )
>>
>>             +{
>>
>>             +  return MemoryMapPageRange (
>>
>>             +           MemoryRegion->VirtualBase,
>>
>>             +           (MemoryRegion->Length + MemoryRegion->VirtualBase),
>>
>>             +           MemoryRegion->Attributes
>>
>>             +           );
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Convert EFI Attributes to Loongarch Attributes.
>>
>>             +
>>
>>             +  @param[in]  EfiAttributes     Efi Attributes.
>>
>>             +
>>
>>             +  @retval  Corresponding architecture attributes.
>>
>>             +**/
>>
>>             +UINTN
>>
>>             +EFIAPI
>>
>>             +EfiAttributeConverse (
>>
>>             +  IN UINTN  EfiAttributes
>>
>>             +  )
>>
>>             +{
>>
>>             +  UINTN  LoongArchAttributes;
>>
>>             +
>>
>>             +  LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | PLV_KERNEL | PAGE_GLOBAL;
>>
>>             +
>>
>>             +  switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
>>
>>             +    case EFI_MEMORY_UC:
>>
>>             +      LoongArchAttributes |= CACHE_SUC;
>>
>>             +      break;
>>
>>             +    case EFI_MEMORY_WC:
>>
>>             +      LoongArchAttributes |= CACHE_WUC;
>>
>>             +      break;
>>
>>             +    case EFI_MEMORY_WT:
>>
>>             +    case EFI_MEMORY_WB:
>>
>>             +      LoongArchAttributes |= CACHE_CC;
>>
>>             +      break;
>>
>>             +    default:
>>
>>             +      LoongArchAttributes |= CACHE_CC;
>>
>>             +      break;
>>
>>             +  }
>>
>>             +
>>
>>             +  // Write protection attributes
>>
>>             +  if (((EfiAttributes & EFI_MEMORY_RO) != 0) ||
>>
>>             +      ((EfiAttributes & EFI_MEMORY_WP) != 0))
>>
>>             +  {
>>
>>             +    LoongArchAttributes &= ~PAGE_DIRTY;
>>
>>             +  }
>>
>>             +
>>
>>             +  if ((EfiAttributes & EFI_MEMORY_RP) != 0) {
>>
>>             +    LoongArchAttributes |= PAGE_NO_READ;
>>
>>             +  }
>>
>>             +
>>
>>             +  // eXecute protection attribute
>>
>>             +  if ((EfiAttributes & EFI_MEMORY_XP) != 0) {
>>
>>             +    LoongArchAttributes |= PAGE_NO_EXEC;
>>
>>             +  }
>>
>>             +
>>
>>             +  return LoongArchAttributes;
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Finds the first of the length and memory properties of the memory region corresponding
>>
>>             +  to the specified base address.
>>
>>             +
>>
>>             +  @param[in]       BaseAddress       To find the base address of the memory region.
>>
>>             +  @param[in, out]  RegionLength      Pointer holding:
>>
>>             +                                      - At entry, the length of the memory region
>>
>>             +                                        expected to be found.
>>
>>             +                                      - At exit, the length of the memory region found.
>>
>>             +  @param[out]      RegionAttributes  Properties of the memory region found.
>>
>>             +
>>
>>             +  @retval  EFI_SUCCESS           The corresponding memory area was successfully found
>>
>>             +           EFI_NOT_FOUND         No memory area found
>>
>>             +           EFI_OUT_OF_RESOURCES  Base address or expected memory region exceeds the maximum
>>
>>             +                                 address.
>>
>>             +**/
>>
>>             +EFI_STATUS
>>
>>             +EFIAPI
>>
>>             +GetMemoryRegionAttributes (
>>
>>             +  IN     UINTN  BaseAddress,
>>
>>             +  IN OUT UINTN  *RegionLength,
>>
>>             +  OUT    UINTN  *RegionAttributes
>>
>>             +  )
>>
>>             +{
>>
>>             +  PTE    *Pte;
>>
>>             +  UINTN  Attributes;
>>
>>             +  UINTN  AttributesTmp;
>>
>>             +  UINTN  MaxAddress;
>>
>>             +  UINTN  EndAddress;
>>
>>             +  UINTN  AddSize;
>>
>>             +
>>
>>             +  if (!MmuIsInit ()) {
>>
>>             +    return EFI_UNSUPPORTED;
>>
>>             +  }
>>
>>             +
>>
>>             +  EndAddress = BaseAddress + *RegionLength;
>>
>>             +  MaxAddress = LShiftU64 (1ULL, MAX_VA_BITS) - 1;
>>
>>             +
>>
>>             +  // Clean the value to prepare output to find region size.
>>
>>             +  *RegionLength = 0x0;
>>
>>             +
>>
>>             +  if ((BaseAddress >= MaxAddress) || (EndAddress >= MaxAddress)) {
>>
>>             +    return EFI_OUT_OF_RESOURCES;
>>
>>             +  }
>>
>>             +
>>
>>             +  Pte = GetPteAddress (BaseAddress);
>>
>>             +
>>
>>             +  if (Pte == NULL) {
>>
>>             +    return EFI_NOT_FOUND;
>>
>>             +  }
>>
>>             +
>>
>>             +  Attributes = GET_PAGE_ATTRIBUTES (*Pte);
>>
>>             +  if (IS_HUGE_PAGE (Pte->PteVal)) {
>>
>>             +    *RegionAttributes = Attributes & (~(PAGE_HUGE));
>>
>>             +  } else {
>>
>>             +    *RegionAttributes = Attributes;
>>
>>             +  }
>>
>>             +
>>
>>             +  do {
>>
>>             +    Pte = GetPteAddress (BaseAddress);
>>
>>             +    if (Pte == NULL) {
>>
>>             +      return EFI_SUCCESS;
>>
>>             +    }
>>
>>             +
>>
>>             +    AttributesTmp = GET_PAGE_ATTRIBUTES (*Pte);
>>
>>             +    if (AttributesTmp == Attributes) {
>>
>>             +      if (IS_HUGE_PAGE (Pte->PteVal)) {
>>
>>             +        AddSize = HUGE_PAGE_SIZE;
>>
>>             +      } else {
>>
>>             +        AddSize = EFI_PAGE_SIZE;
>>
>>             +      }
>>
>>             +
>>
>>             +      *RegionLength += AddSize;
>>
>>             +      BaseAddress   += AddSize;
>>
>>             +    } else {
>>
>>             +      return EFI_SUCCESS;
>>
>>             +    }
>>
>>             +  } while (BaseAddress <= EndAddress);
>>
>>             +
>>
>>             +  return EFI_SUCCESS;
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Sets the Attributes  of the specified memory region
>>
>>             +
>>
>>             +  @param[in]  BaseAddress    The base address of the memory region to set the Attributes.
>>
>>             +  @param[in]  Length         The length of the memory region to set the Attributes.
>>
>>             +  @param[in]  Attributes     The Attributes to be set.
>>
>>             +  @param[in]  AttributeMask  Mask of memory attributes to take into account.
>>
>>             +
>>
>>             +  @retval  EFI_SUCCESS    The Attributes was set successfully
>>
>>             +**/
>>
>>             +EFI_STATUS
>>
>>             +EFIAPI
>>
>>             +SetMemoryRegionAttributes (
>>
>>             +  IN EFI_PHYSICAL_ADDRESS  BaseAddress,
>>
>>             +  IN UINTN                 Length,
>>
>>             +  IN UINTN                 Attributes,
>>
>>             +  IN UINT64                AttributeMask
>>
>>             +  )
>>
>>             +{
>>
>>             +  EFI_STATUS  Status;
>>
>>             +
>>
>>             +  if (!MmuIsInit ()) {
>>
>>             +    return EFI_UNSUPPORTED;
>>
>>             +  }
>>
>>             +
>>
>>             +  Attributes = EfiAttributeConverse (Attributes);
>>
>>             +  Status     = MemoryMapPageRange (BaseAddress, BaseAddress + Length, Attributes);
>>
>>             +  ASSERT_EFI_ERROR (Status);
>>
>>             +
>>
>>             +  return Status;
>>
>>             +}
>>
>>             +
>>
>>             +/**
>>
>>             +  Check to see if mmu successfully initializes and saves the result.
>>
>>             +
>>
>>             +  @param[in]  ImageHandle  The firmware allocated handle for the EFI image.
>>
>>             +  @param[in]  SystemTable  A pointer to the EFI System Table.
>>
>>             +
>>
>>             +  @retval  RETURN_SUCCESS    Initialization succeeded.
>>
>>             +**/
>>
>>             +RETURN_STATUS
>>
>>             +MmuInitialize (
>>
>>             +  IN EFI_HANDLE        ImageHandle,
>>
>>             +  IN EFI_SYSTEM_TABLE  *SystemTable
>>
>>             +  )
>>
>>             +{
>>
>>             +  if (SWAP_PAGE_DIR != 0) {
>>
>>             +    mMmuInited = TRUE;
>>
>>             +  }
>>
>>             +
>>
>>             +  return RETURN_SUCCESS;
>>
>>             +}
>>
>>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
>>
>>             new file mode 100644
>>
>>             index 0000000000..d8c922c8fa
>>
>>             --- /dev/null
>>
>>             +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CommonMmuLib.h
>>
>>             @@ -0,0 +1,43 @@
>>
>>             +/** @file
>>
>>             +
>>
>>             +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>>
>>             +
>>
>>             +  SPDX-License-Identifier: BSD-2-Clause-Patent
>>
>>             +
>>
>>             +  @par Glossary:
>>
>>             +    - Dir    - Directory
>>
>>             +**/
>>
>>             +
>>
>>             +#ifndef  MMU_LIB_CORE_H_
>>
>>             +#define  MMU_LIB_CORE_H_
>>
>>             +
>>
>>             +/**
>>
>>             +  Iterates through the page directory to initialize it.
>>
>>             +
>>
>>             +  @param  Dst  A pointer to the directory of the page to initialize.
>>
>>             +  @param  Num  The number of page directories to initialize.
>>
>>             +  @param  Src  A pointer to the data used to initialize the page directory.
>>
>>             +
>>
>>             +  @retval VOID.
>>
>>             +**/
>>
>>             +VOID
>>
>>             +PageDirInit (
>>
>>             +  IN VOID   *dest,
>>
>>             +  IN UINTN  Count,
>>
>>             +  IN VOID   *src
>>
>>             +  );
>>
>>             +
>>
>>             +/**
>>
>>             +  Page tables are established from memory-mapped tables.
>>
>>             +
>>
>>             +  @param  MemoryRegion   A pointer to a memory-mapped table entry.
>>
>>             +
>>
>>             +  @retval     EFI_SUCCESS   The page table was created successfully.
>>
>>             +  @retval     EFI_OUT_OF_RESOURCES  Page table  establishment failed due to resource exhaustion.
>>
>>             +**/
>>
>>             +EFI_STATUS
>>
>>             +FillTranslationTable (
>>
>>             +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryRegion
>>
>>             +  );
>>
>>             +
>>
>>             +#endif // MMU_LIB_CORE_H_
>>
>>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
>>
>>             new file mode 100644
>>
>>             index 0000000000..bac4f52327
>>
>>             --- /dev/null
>>
>>             +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
>>
>>             @@ -0,0 +1,279 @@
>>
>>             +/** @file
>>
>>             +
>>
>>             +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>>
>>             +
>>
>>             +  SPDX-License-Identifier: BSD-2-Clause-Patent
>>
>>             +
>>
>>             +  @par Glossary:
>>
>>             +    - Pgd or Pgd or PGD    - Page Global Directory
>>
>>             +    - Pud or Pud or PUD    - Page Upper Directory
>>
>>             +    - Pmd or Pmd or PMD    - Page Middle Directory
>>
>>             +    - Pte or pte or PTE    - Page Table Entry
>>
>>             +    - Val or VAL or val    - Value
>>
>>             +    - Dir    - Directory
>>
>>             +**/
>>
>>             +
>>
>>             +#ifndef PAGE_H_
>>
>>             +#define PAGE_H_
>>
>>             +
>>
>>             +#include <Library/CpuMmuLib.h>
>>
>>             +
>>
>>             +#define MAX_VA_BITS  47
>>
>>             +#define PGD_WIDE     (8)
>>
>>             +#define PUD_WIDE     (9)
>>
>>             +#define PMD_WIDE     (9)
>>
>>             +#define PTE_WIDE     (9)
>>
>>             +
>>
>>             +#define ENTRYS_PER_PGD  (1 << PGD_WIDE)
>>
>>             +#define ENTRYS_PER_PUD  (1 << PUD_WIDE)
>>
>>             +#define ENTRYS_PER_PMD  (1 << PMD_WIDE)
>>
>>             +#define ENTRYS_PER_PTE  (1 << PTE_WIDE)
>>
>>             +
>>
>>             +#define PGD_SHIFT  (PUD_SHIFT + PUD_WIDE)
>>
>>             +#define PUD_SHIFT  (PMD_SHIFT + PMD_WIDE)
>>
>>             +#define PMD_SHIFT  (EFI_PAGE_SHIFT + PTE_WIDE)
>>
>>             +#define PTE_SHIFT  (EFI_PAGE_SHIFT)
>>
>>             +
>>
>>             +#define PGD_SIZE  (1UL << PGD_SHIFT)
>>
>>             +#define PUD_SIZE  (1UL << PUD_SHIFT)
>>
>>             +#define PMD_SIZE  (1UL << PMD_SHIFT)
>>
>>             +
>>
>>             +#define PGD_MASK   (~(PGD_SIZE-1))
>>
>>             +#define PUD_MASK   (~(PUD_SIZE-1))
>>
>>             +#define PMD_MASK   (~(PMD_SIZE-1))
>>
>>             +#define PAGE_MASK  (~(EFI_PAGE_SIZE - 1))
>>
>>             +#define PFN_MASK   (~(((UINTN)(1) << (EFI_PAGE_SHIFT)) - 1) & \
>>
>>             +                   (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
>>
>>             +
>>
>>             +#define HUGEP_PAGE_MASK  (~(((UINTN)(1) << (PMD_SHIFT)) - 1) & \
>>
>>             +                          (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
>>
>>             +
>>
>>             +#define INVALID_PAGE  0
>>
>>             +
>>
>>             +typedef struct {
>>
>>             +  UINTN    PgdVal;
>>
>>             +} PGD;
>>
>>             +typedef struct {
>>
>>             +  UINTN    PudVal;
>>
>>             +} PUD;
>>
>>             +typedef struct {
>>
>>             +  UINTN    PmdVal;
>>
>>             +} PMD;
>>
>>             +typedef struct {
>>
>>             +  UINTN    PteVal;
>>
>>             +} PTE;
>>
>>             +
>>
>>             +/**
>>
>>             +  Gets the value of the page global directory table entry.
>>
>>             +
>>
>>             +  @param  x    Page global directory struct variables.
>>
>>             +
>>
>>             +  @retval   the value of the page global directory table entry.
>>
>>             + **/
>>
>>             +#define PGD_VAL(x)  ((x).PgdVal)
>>
>>             +
>>
>>             +/**
>>
>>             +  Gets the value of the page upper directory table entry.
>>
>>             +
>>
>>             +  @param  x    Page upper directory struct variables.
>>
>>             +
>>
>>             +  @retval  the value of the page upper directory table entry.
>>
>>             + **/
>>
>>             +#define PUD_VAL(x)  ((x).PudVal)
>>
>>             +
>>
>>             +/**
>>
>>             +  Gets the value of the page middle directory table entry.
>>
>>             +
>>
>>             +  @param  x    Page middle directory struct variables.
>>
>>             +
>>
>>             +  @retval  the value of the page middle directory table entry.
>>
>>             + **/
>>
>>             +#define PMD_VAL(x)  ((x).PmdVal)
>>
>>             +
>>
>>             +/**
>>
>>             +  Gets the value of the page table entry.
>>
>>             +
>>
>>             +  @param  x    Page table entry struct variables.
>>
>>             +
>>
>>             +  @retval  the value of the page table entry.
>>
>>             + **/
>>
>>             +#define PTE_VAL(x)  ((x).PteVal)
>>
>>             +
>>
>>             +#define PGD_TABLE_SIZE  (ENTRYS_PER_PGD * sizeof(PGD))
>>
>>             +#define PUD_TABLE_SIZE  (ENTRYS_PER_PUD * sizeof(PUD))
>>
>>             +#define PMD_TABLE_SIZE  (ENTRYS_PER_PMD * sizeof(PMD))
>>
>>             +#define PTE_TABLE_SIZE  (ENTRYS_PER_PTE * sizeof(PTE))
>>
>>             +
>>
>>             +/**
>>
>>             +  Gets the physical address of the record in the page table entry.
>>
>>             +
>>
>>             +  @param  x    Page table entry struct variables.
>>
>>             +
>>
>>             +  @retval  the value of the physical address.
>>
>>             + **/
>>
>>             +#define GET_PAGE_ATTRIBUTES(x)  (UINTN) {(PTE_VAL(x) & ~PFN_MASK)}
>>
>>             +
>>
>>             +/**
>>
>>             +  Gets the virtual address of the next block of the specified virtual address
>>
>>             +  that is aligned with the size of the global page directory mapping.
>>
>>             +
>>
>>             +  @param  Address  Specifies the virtual address.
>>
>>             +  @param  End    The end address of the memory region.
>>
>>             +
>>
>>             +  @retval   the specified virtual address  of the next block.
>>
>>             + **/
>>
>>             +#define PGD_ADDRESS_END(Address, End)                  \
>>
>>             +({                                                     \
>>
>>             +  UINTN Boundary = ((Address) + PGD_SIZE) & PGD_MASK;  \
>>
>>             +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
>>
>>             +})
>>
>>             +
>>
>>             +/**
>>
>>             +  Gets the virtual address of the next block of the specified virtual address
>>
>>             +  that is aligned with the size of the page upper directory mapping.
>>
>>             +
>>
>>             +  @param  Address  Specifies the virtual address.
>>
>>             +  @param  End    The end address of the memory region.
>>
>>             +
>>
>>             +  @retval   the specified virtual address  of the next block.
>>
>>             + **/
>>
>>             +#define PUD_ADDRESS_END(Address, End)                  \
>>
>>             +({                                                     \
>>
>>             +  UINTN Boundary = ((Address) + PUD_SIZE) & PUD_MASK;  \
>>
>>             +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
>>
>>             +})
>>
>>             +
>>
>>             +/**
>>
>>             +  Gets the virtual address of the next block of the specified virtual address
>>
>>             +  that is aligned with the size of the page middle directory mapping.
>>
>>             +
>>
>>             +  @param  Address  Specifies the virtual address.
>>
>>             +  @param  End    The end address of the memory region.
>>
>>             +
>>
>>             +  @retval   the specified virtual address  of the next block.
>>
>>             + **/
>>
>>             +#define PMD_ADDRESS_END(Address, End)                  \
>>
>>             +({                                                     \
>>
>>             +  UINTN Boundary = ((Address) + PMD_SIZE) & PMD_MASK;  \
>>
>>             +  (Boundary - 1 < (End) - 1)? Boundary: (End);         \
>>
>>             +})
>>
>>             +
>>
>>             +/**
>>
>>             +  Get Specifies the virtual address corresponding to the index of the page global directory table entry.
>>
>>             +
>>
>>             +  @param  Address  Specifies the virtual address.
>>
>>             +
>>
>>             +  @retval   the index of the page global directory table entry.
>>
>>             + **/
>>
>>             +#define PGD_INDEX(Address)  (((Address) >> PGD_SHIFT) & (ENTRYS_PER_PGD-1))
>>
>>             +
>>
>>             +/**
>>
>>             +  Get Specifies the virtual address corresponding to the index of the page upper directory table entry.
>>
>>             +
>>
>>             +  @param  Address  Specifies the virtual address.
>>
>>             +  @param  End    The end address of the memory region.
>>
>>             +
>>
>>             +  @retval   the index of the page upper directory table entry.
>>
>>             + **/
>>
>>             +#define PUD_INDEX(Address)  (((Address) >> PUD_SHIFT) & (ENTRYS_PER_PUD - 1))
>>
>>             +
>>
>>             +/**
>>
>>             +  Get Specifies the virtual address corresponding to the index of the page middle directory table entry.
>>
>>             +
>>
>>             +  @param  Address  Specifies the virtual address.
>>
>>             +
>>
>>             +  @retval   the index of the page middle directory table entry.
>>
>>             + **/
>>
>>             +#define PMD_INDEX(Address)  (((Address) >> PMD_SHIFT) & (ENTRYS_PER_PMD - 1))
>>
>>             +
>>
>>             +/**
>>
>>             +  Get Specifies the virtual address corresponding to the index of the page table entry.
>>
>>             +
>>
>>             +  @param  Address  Specifies the virtual address.
>>
>>             +
>>
>>             +  @retval   the index of the page table entry.
>>
>>             + **/
>>
>>             +#define PTE_INDEX(Address)  (((Address) >> EFI_PAGE_SHIFT) & (ENTRYS_PER_PTE - 1))
>>
>>             +
>>
>>             +/**
>>
>>             +  Calculates the value of the page table entry based on the specified virtual address and properties.
>>
>>             +
>>
>>             +  @param  Address  Specifies the virtual address.
>>
>>             +  @param  Attributes  Specifies the Attributes.
>>
>>             +
>>
>>             +  @retval    the value of the page table entry.
>>
>>             + **/
>>
>>             +#define MAKE_PTE(Address, Attributes)  (PTE){((((Address) >> EFI_PAGE_SHIFT) << 12) | (Attributes))}
>>
>>             +
>>
>>             +/**
>>
>>             +  Get Global bit from Attributes
>>
>>             +
>>
>>             +  @param  Attributes  Specifies the Attributes.
>>
>>             + * */
>>
>>             +#define GET_GLOBALBIT(Attributes)  ((Attributes & PAGE_GLOBAL) >> PAGE_GLOBAL_SHIFT)
>>
>>             +
>>
>>             +/**
>>
>>             +  Calculates the value of the Huge page table entry based on the specified virtual address and properties.
>>
>>             +
>>
>>             +  @param  Address  Specifies the virtual address.
>>
>>             +  @param  Attributes  Specifies the Attributes.
>>
>>             +
>>
>>             +  @retval    the value of the HUGE page table entry.
>>
>>             + **/
>>
>>             +#define MAKE_HUGE_PTE(Address, Attributes)  (PTE){(((((Address) >> PMD_SHIFT) << PMD_SHIFT) | \
>>
>>             +                                             ((Attributes) | (GET_GLOBALBIT(Attributes) << PAGE_HGLOBAL_SHIFT) | \
>>
>>             +                                             PAGE_HUGE)))}
>>
>>             +
>>
>>             +/**
>>
>>             +  Check whether the large page table entry is.
>>
>>             +
>>
>>             +  @param  Val The value of the page table entry.
>>
>>             +
>>
>>             +  @retval    1   Is huge page table entry.
>>
>>             +  @retval    0   Isn't huge page table entry.
>>
>>             +**/
>>
>>             +#define IS_HUGE_PAGE(Val)  ((((Val) & PAGE_HUGE) == PAGE_HUGE) && \
>>
>>             +                            (((Val) & PAGE_HGLOBAL) == PAGE_HGLOBAL))
>>
>>             +
>>
>>             +#define HUGE_PAGE_SIZE  (PMD_SIZE)
>>
>>             +
>>
>>             +/**
>>
>>             +  Check that the global page directory table entry is empty.
>>
>>             +
>>
>>             +  @param  pgd   the global page directory struct variables.
>>
>>             +
>>
>>             +  @retval    1   The page table is invalid.
>>
>>             +  @retval    0   The page table is valid.
>>
>>             +**/
>>
>>             +#define PGD_IS_EMPTY(Val)  (PGD_VAL(Val) == INVALID_PAGE)
>>
>>             +
>>
>>             +/**
>>
>>             +  Check that the page upper directory table entry is empty.
>>
>>             +
>>
>>             +  @param  pud   Page upper directory struct variables.
>>
>>             +
>>
>>             +  @retval    1   The page table is invalid.
>>
>>             +  @retval    0   The page table is valid.
>>
>>             +**/
>>
>>             +#define PUD_IS_EMPTY(Val)  (PUD_VAL(Val) == INVALID_PAGE)
>>
>>             +
>>
>>             +/**
>>
>>             +  Check that the page middle directory table entry is empty.
>>
>>             +
>>
>>             +  @param  pmd   Page middle directory struct variables.
>>
>>             +
>>
>>             +  @retval    1   The page table is invalid.
>>
>>             +  @retval    0   The page table is valid.
>>
>>             +**/
>>
>>             +#define PMD_IS_EMPTY(Val)  (PMD_VAL(Val) == INVALID_PAGE)
>>
>>             +
>>
>>             +/**
>>
>>             +  Check that the page the page table entry is empty.
>>
>>             +
>>
>>             +  @param  pte   Page table entry struct variables.
>>
>>             +
>>
>>             +  @retval    1   The page table is invalid.
>>
>>             +  @retval    0   The page table is valid.
>>
>>             +**/
>>
>>             +#define PTE_IS_EMPTY(Val)  (!(PTE_VAL(Val) & (~PAGE_VALID)))
>>
>>             +#endif // PAGE_H_
>>
>>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
>>
>>             new file mode 100644
>>
>>             index 0000000000..c214e8d847
>>
>>             --- /dev/null
>>
>>             +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/PeiCpuMmuLib.c
>>
>>             @@ -0,0 +1,178 @@
>>
>>             +/** @file
>>
>>             +  CPU Memory Map Unit PEI phase driver.
>>
>>             +
>>
>>             +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>>
>>             +
>>
>>             +  SPDX-License-Identifier: BSD-2-Clause-Patent
>>
>>             +
>>
>>             +  @par Glossary:
>>
>>             +    - Tlb      - Translation Lookaside Buffer
>>
>>             +**/
>>
>>             +
>>
>>             +#include <Uefi.h>
>>
>>             +#include <Library/BaseLib.h>
>>
>>             +#include <Library/BaseMemoryLib.h>
>>
>>             +#include <Library/CacheMaintenanceLib.h>
>>
>>             +#include <Library/CpuMmuLib.h>
>>
>>             +#include <Library/DebugLib.h>
>>
>>             +#include <Library/MemoryAllocationLib.h>
>>
>>             +#include <Library/PcdLib.h>
>>
>>             +#include <Register/LoongArch64/Csr.h>
>>
>>             +
>>
>>             +#include "Page.h"
>>
>>             +#include "Tlb.h"
>>
>>             +#include "CommonMmuLib.h"
>>
>>             +
>>
>>             +//
>>
>>             +// For coding convenience, define the maximum valid
>>
>>             +// LoongArch exception.
>>
>>             +// Since UEFI V2.11, it will be present in DebugSupport.h.
>>
>>             +//
>>
>>             +#define MAX_LOONGARCH_EXCEPTION  64
>>
>>             +
>>
>>             +/**
>>
>>             +  Create a page table and initialize the memory management unit(MMU).
>>
>>             +
>>
>>             +  @param[in]   MemoryTable           A pointer to a memory ragion table.
>>
>>             +  @param[out]  TranslationTableBase  A pointer to a translation table base address.
>>
>>             +  @param[out]  TranslationTableSize  A pointer to a translation table base size.
>>
>>             +
>>
>>             +  @retval  EFI_SUCCESS                Configure MMU successfully.
>>
>>             +           EFI_INVALID_PARAMETER      MemoryTable is NULL.
>>
>>             +           EFI_UNSUPPORTED            Out of memory space or size not aligned.
>>
>>             +**/
>>
>>             +EFI_STATUS
>>
>>             +EFIAPI
>>
>>             +ConfigureMemoryManagementUnit (
>>
>>             +  IN  MEMORY_REGION_DESCRIPTOR  *MemoryTable,
>>
>>             +  OUT VOID                      **TranslationTableBase OPTIONAL,
>>
>>             +  OUT UINTN                     *TranslationTableSize  OPTIONAL
>>
>>             +  )
>>
>>             +{
>>
>>             +  PGD            *SwapperPageDir;
>>
>>             +  UINTN          PgdShift;
>>
>>             +  UINTN          PgdWide;
>>
>>             +  UINTN          PudShift;
>>
>>             +  UINTN          PudWide;
>>
>>             +  UINTN          PmdShift;
>>
>>             +  UINTN          PmdWide;
>>
>>             +  UINTN          PteShift;
>>
>>             +  UINTN          PteWide;
>>
>>             +  UINTN          Length;
>>
>>             +  UINTN          TlbReEntry;
>>
>>             +  UINTN          TlbReEntryOffset;
>>
>>             +  UINTN          Remaining;
>>
>>             +  RETURN_STATUS  Status;
>>
>>             +
>>
>>             +  SwapperPageDir = NULL;
>>
>>             +  PgdShift       = PGD_SHIFT;
>>
>>             +  PgdWide        = PGD_WIDE;
>>
>>             +  PudShift       = PUD_SHIFT;
>>
>>             +  PudWide        = PUD_WIDE;
>>
>>             +  PmdShift       = PMD_SHIFT;
>>
>>             +  PmdWide        = PMD_WIDE;
>>
>>             +  PteShift       = PTE_SHIFT;
>>
>>             +  PteWide        = PTE_WIDE;
>>
>>             +
>>
>>             +  if (MemoryTable == NULL) {
>>
>>             +    ASSERT (MemoryTable != NULL);
>>
>>             +    return EFI_INVALID_PARAMETER;
>>
>>             +  }
>>
>>             +
>>
>>             +  SwapperPageDir = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
>>
>>             +  ZeroMem (SwapperPageDir, PGD_TABLE_SIZE);
>>
>>             +
>>
>>             +  if (SwapperPageDir == NULL) {
>>
>>             +    goto FreeTranslationTable;
>>
>>             +  }
>>
>>             +
>>
>>             +  CsrWrite (LOONGARCH_CSR_PGDL, (UINTN)SwapperPageDir);
>>
>>             +
>>
>>             +  while (MemoryTable->Length != 0) {
>>
>>             +    DEBUG ((
>>
>>             +      DEBUG_INFO,
>>
>>             +      "%a %d VirtualBase %p VirtualEnd %p Attributes %p .\n",
>>
>>             +      __func__,
>>
>>             +      __LINE__,
>>
>>             +      MemoryTable->VirtualBase,
>>
>>             +      (MemoryTable->Length + MemoryTable->VirtualBase),
>>
>>             +      MemoryTable->Attributes
>>
>>             +      ));
>>
>>             +
>>
>>             +    Status = FillTranslationTable (MemoryTable);
>>
>>             +    if (EFI_ERROR (Status)) {
>>
>>             +      goto FreeTranslationTable;
>>
>>             +    }
>>
>>             +
>>
>>             +    MemoryTable++;
>>
>>             +  }
>>
>>             +
>>
>>             +  //
>>
>>             +  // TLB Re-entry address at the end of exception vector, a vector is up to 512 bytes,
>>
>>             +  // so the starting address is: total exception vector size + total interrupt vector size + base.
>>
>>             +  // The total size of TLB handler and exception vector size and interrupt vector size should not
>>
>>             +  // be lager than 64KB.
>>
>>             +  //
>>
>>             +  Length           = (UINTN)HandleTlbRefillEnd - (UINTN)HandleTlbRefillStart;
>>
>>             +  TlbReEntryOffset = (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * 512;
>>
>>             +  Remaining        = TlbReEntryOffset % SIZE_4KB;
>>
>>             +  if (Remaining != 0x0) {
>>
>>             +    TlbReEntryOffset += (SIZE_4KB - Remaining);
>>
>>             +  }
>>
>>             +
>>
>>             +  TlbReEntry = PcdGet64 (PcdCpuExceptionVectorBaseAddress) + TlbReEntryOffset;
>>
>>             +  if ((TlbReEntryOffset + Length) > SIZE_64KB) {
>>
>>             +    goto FreeTranslationTable;
>>
>>             +  }
>>
>>             +
>>
>>             +  //
>>
>>             +  // Ensure that TLB refill exception base address alignment is equals to 4KB and is valid.
>>
>>             +  //
>>
>>             +  if (TlbReEntry & (SIZE_4KB - 1)) {
>>
>>             +    goto FreeTranslationTable;
>>
>>             +  }
>>
>>             +
>>
>>             +  CopyMem ((VOID *)TlbReEntry, HandleTlbRefillStart, Length);
>>
>>             +  InvalidateInstructionCacheRange ((VOID *)(UINTN)HandleTlbRefillStart, Length);
>>
>>             +
>>
>>             +  DEBUG ((
>>
>>             +    DEBUG_INFO,
>>
>>             +    "%a  %d PteShift %d PteWide %d PmdShift %d PmdWide %d PudShift %d PudWide %d PgdShift %d PgdWide %d.\n",
>>
>>             +    __func__,
>>
>>             +    __LINE__,
>>
>>             +    PteShift,
>>
>>             +    PteWide,
>>
>>             +    PmdShift,
>>
>>             +    PmdWide,
>>
>>             +    PudShift,
>>
>>             +    PudWide,
>>
>>             +    PgdShift,
>>
>>             +    PgdWide
>>
>>             +    ));
>>
>>             +
>>
>>             +  //
>>
>>             +  // Set the address of TLB refill exception handler
>>
>>             +  //
>>
>>             +  SetTlbRebaseAddress ((UINTN)TlbReEntry);
>>
>>             +
>>
>>             +  //
>>
>>             +  // Set page size
>>
>>             +  //
>>
>>             +  CsrXChg (LOONGARCH_CSR_TLBIDX, (DEFAULT_PAGE_SIZE << CSR_TLBIDX_SIZE), CSR_TLBIDX_SIZE_MASK);
>>
>>             +  CsrWrite (LOONGARCH_CSR_STLBPGSIZE, DEFAULT_PAGE_SIZE);
>>
>>             +  CsrXChg (LOONGARCH_CSR_TLBREHI, (DEFAULT_PAGE_SIZE << CSR_TLBREHI_PS_SHIFT), CSR_TLBREHI_PS);
>>
>>             +
>>
>>             +  CsrWrite (LOONGARCH_CSR_PWCTL0, (PteShift | PteWide << 5 | PmdShift << 10 | PmdWide << 15 | PudShift << 20 | PudWide << 25));
>>
>>             +  CsrWrite (LOONGARCH_CSR_PWCTL1, (PgdShift | PgdWide << 6));
>>
>>             +
>>
>>             +  DEBUG ((DEBUG_INFO, "%a %d Enable Mmu Start PageBassAddress %p.\n", __func__, __LINE__, SwapperPageDir));
>>
>>             +
>>
>>             +  return EFI_SUCCESS;
>>
>>             +
>>
>>             +FreeTranslationTable:
>>
>>             +  if (SwapperPageDir != NULL) {
>>
>>             +    FreePages (SwapperPageDir, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
>>
>>             +  }
>>
>>             +
>>
>>             +  return EFI_UNSUPPORTED;
>>
>>             +}
>>
>>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
>>
>>             new file mode 100644
>>
>>             index 0000000000..9a681ce8e1
>>
>>             --- /dev/null
>>
>>             +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Tlb.h
>>
>>             @@ -0,0 +1,48 @@
>>
>>             +/** @file
>>
>>             +
>>
>>             +  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>>
>>             +
>>
>>             +  SPDX-License-Identifier: BSD-2-Clause-Patent
>>
>>             +
>>
>>             +**/
>>
>>             +
>>
>>             +#ifndef TLB_H_
>>
>>             +#define TLB_H_
>>
>>             +
>>
>>             +/**
>>
>>             +  Invalid corresponding TLB entries are based on the address given
>>
>>             +
>>
>>             +  @param Address The address corresponding to the invalid page table entry
>>
>>             +
>>
>>             +  @retval  none
>>
>>             +**/
>>
>>             +VOID
>>
>>             +InvalidTlb (
>>
>>             +  UINTN  Address
>>
>>             +  );
>>
>>             +
>>
>>             +/**
>>
>>             +  TLB refill handler start.
>>
>>             +
>>
>>             +  @param  none
>>
>>             +
>>
>>             +  @retval none
>>
>>             +**/
>>
>>             +VOID
>>
>>             +HandleTlbRefillStart (
>>
>>             +  VOID
>>
>>             +  );
>>
>>             +
>>
>>             +/**
>>
>>             +  TLB refill handler end.
>>
>>             +
>>
>>             +  @param  none
>>
>>             +
>>
>>             +  @retval none
>>
>>             +**/
>>
>>             +VOID
>>
>>             +HandleTlbRefillEnd (
>>
>>             +  VOID
>>
>>             +  );
>>
>>             +
>>
>>             +#endif // TLB_H_
>>
>>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
>>
>>             new file mode 100644
>>
>>             index 0000000000..c9a8c16336
>>
>>             --- /dev/null
>>
>>             +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbOperation.S
>>
>>             @@ -0,0 +1,44 @@
>>
>>             +#------------------------------------------------------------------------------
>>
>>             +#
>>
>>             +# TLB operation functions
>>
>>             +#
>>
>>             +# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
>>
>>             +#
>>
>>             +# SPDX-License-Identifier: BSD-2-Clause-Patent
>>
>>             +#
>>
>>             +#-----------------------------------------------------------------------------
>>
>>             +
>>
>>             +#include <Register/LoongArch64/Csr.h>
>>
>>             +
>>
>>             +ASM_GLOBAL ASM_PFX(HandleTlbRefillStart)
>>
>>             +ASM_GLOBAL ASM_PFX(HandleTlbRefillEnd)
>>
>>             +ASM_GLOBAL ASM_PFX(InvalidTlb)
>>
>>             +
>>
>>             +#
>>
>>             +#  Refill the page table.
>>
>>             +#  @param  VOID
>>
>>             +#  @retval  VOID
>>
>>             +#
>>
>>             +ASM_PFX(HandleTlbRefillStart):
>>
>>             +  csrwr   $t0, LOONGARCH_CSR_TLBRSAVE
>>
>>             +  csrrd   $t0, LOONGARCH_CSR_PGD
>>
>>             +  lddir   $t0, $t0, 3   #Put pud BaseAddress into T0
>>
>>             +  lddir   $t0, $t0, 2   #Put pmd BaseAddress into T0
>>
>>             +  lddir   $t0, $t0, 1   #Put pte BaseAddress into T0
>>
>>             +  ldpte   $t0, 0
>>
>>             +  ldpte   $t0, 1
>>
>>             +  tlbfill   // refill hi,lo0,lo1
>>
>>             +  csrrd   $t0, LOONGARCH_CSR_TLBRSAVE
>>
>>             +  ertn
>>
>>             +ASM_PFX(HandleTlbRefillEnd):
>>
>>             +
>>
>>             +#
>>
>>             +# Invalid corresponding TLB entries are based on the address given
>>
>>             +# @param a0 The address corresponding to the invalid page table entry
>>
>>             +# @retval  none
>>
>>             +#
>>
>>             +ASM_PFX(InvalidTlb):
>>
>>             +    invtlb  INVTLB_ADDR_GTRUE_OR_ASID, $zero, $a0
>>
>>             +    jirl    $zero, $ra, 0
>>
>>             +
>>
>>             +    .end
>>
>>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>>
>>             new file mode 100644
>>
>>             index 0000000000..45b15db4c9
>>
>>             --- /dev/null
>>
>>             +++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>>
>>             @@ -0,0 +1,44 @@
>>
>>             +## @file
>>
>>             +#  CPU Memory Map Unit PEI phase driver.
>>
>>             +#
>>
>>             +#  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                      = PeiCpuMmuLib
>>
>>             +  MODULE_UNI_FILE                = PeiCpuMmuLib.uni
>>
>>             +  FILE_GUID                      = F67EB983-AC2A-7550-AB69-3BC51A1C895B
>>
>>             +  MODULE_TYPE                    = PEIM
>>
>>             +  VERSION_STRING                 = 1.0
>>
>>             +  LIBRARY_CLASS                  = CpuMmuLib | SEC PEIM
>>
>>             +
>>
>>             +#
>>
>>             +#  VALID_ARCHITECTURES           = LOONGARCH64
>>
>>             +#
>>
>>             +
>>
>>             +[Sources.LoongArch64]
>>
>>             +  LoongArch64/TlbOperation.S   | GCC
>>
>>             +  LoongArch64/CommonMmuLib.c
>>
>>             +  LoongArch64/PeiCpuMmuLib.c
>>
>>             +  LoongArch64/CommonMmuLib.h
>>
>>             +  LoongArch64/Tlb.h
>>
>>             +  LoongArch64/Page.h
>>
>>             +
>>
>>             +[Packages]
>>
>>             +  MdePkg/MdePkg.dec
>>
>>             +  MdeModulePkg/MdeModulePkg.dec
>>
>>             +  UefiCpuPkg/UefiCpuPkg.dec
>>
>>             +
>>
>>             +[PCD]
>>
>>             +  gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask
>>
>>             +  gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress
>>
>>             +
>>
>>             +[LibraryClasses]
>>
>>             +  CacheMaintenanceLib
>>
>>             +  DebugLib
>>
>>             +  MemoryAllocationLib
>>
>>             +  PcdLib
>>
>>             diff --git a/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
>>
>>             new file mode 100644
>>
>>             index 0000000000..3e21334f3e
>>
>>             --- /dev/null
>>
>>             +++ b/UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.uni
>>
>>             @@ -0,0 +1,14 @@
>>
>>             +// /** @file
>>
>>             +// CPU Memory Manager Unit library instance for PEI modules.
>>
>>             +//
>>
>>             +// CPU Memory Manager Unit library instance for PEI modules.
>>
>>             +//
>>
>>             +// 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 "CPU Memory Manager Unit library instance for PEI modules."
>>
>>             +
>>
>>             +#string STR_MODULE_DESCRIPTION          #language en-US "CPU Memory Manager Unit library instance for PEI modules."
>>
>>             diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
>>
>>             index 28eed85bce..178dc3c0f9 100644
>>
>>             --- a/UefiCpuPkg/UefiCpuPkg.dsc
>>
>>             +++ b/UefiCpuPkg/UefiCpuPkg.dsc
>>
>>             @@ -207,5 +207,9 @@
>>
>>                 UefiCpuPkg/CpuTimerDxeRiscV64/CpuTimerDxeRiscV64.inf
>>
>>                 UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf
>>
>>               
>>
>>             +[Components.LOONGARCH64]
>>
>>             +  UefiCpuPkg/Library/CpuMmuLib/DxeCpuMmuLib.inf
>>
>>             +  UefiCpuPkg/Library/CpuMmuLib/PeiCpuMmuLib.inf
>>
>>             +
>>
>>               [BuildOptions]
>>
>>                 *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
>>
>>           
>>
>>           
>>
>>           
>>
>>           
>>
> 


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#116200): https://edk2.groups.io/g/devel/message/116200
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-03-01  1:26             ` Chao Li
@ 2024-03-01 11:27               ` Laszlo Ersek
  2024-03-04  3:39                 ` Chao Li
  0 siblings, 1 reply; 89+ messages in thread
From: Laszlo Ersek @ 2024-03-01 11:27 UTC (permalink / raw)
  To: devel, lichao, Ni, Ray
  Cc: Dong, Eric, Kumar, Rahul R, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

On 3/1/24 02:26, Chao Li wrote:
> Hi Ray, Lazslo,
> 
> This library is almost complete to refactored, it refer to ARM and
> RISC-V version, the API include set/get memory region attribute.
> 
> I have one last question, in ARM and RISC-V version, even LoongArch old
> and current version, they all request a configure interface, which may
> be called in PEI or DXE stage, so should we open configure API? If so,
> it is possible for ARM RISC-V and LongArch's MMU libraries to be meged
> into the same instance if they wished.

Sorry, I've lost all context by now. I don't understand what you mean by
"they all request a configure interface".

Please provide pathnames and function names for the existent code that
you are referring to as examples (and then please restate / rephrase
your question as well).

Laszlo



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#116233): https://edk2.groups.io/g/devel/message/116233
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-03-01 11:27               ` Laszlo Ersek
@ 2024-03-04  3:39                 ` Chao Li
  2024-03-05  9:26                   ` Laszlo Ersek
  0 siblings, 1 reply; 89+ messages in thread
From: Chao Li @ 2024-03-04  3:39 UTC (permalink / raw)
  To: Laszlo Ersek, devel, Ni, Ray
  Cc: Dong, Eric, Kumar, Rahul R, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

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

Hi Laszlo,

OK.

When I discussed the CpuMmuLib API as a public API with Ray in the early 
days, the API recommended by Ray should be the patch 13 in this series, 
which only contains set/get memory region attribute, but in the first 
version in this series, it contains more API, one is ConfigureMmu API, 
which is included in ARM and RISCV versions. This API provides a 
interface called in PEI or DXE stage, it will configures the MMU and 
enables it, such as creating the page tables, filling the static page 
tables, cofniguring the MMU registers, enbale MMU, etc.

The paths for ARM and RISCV version APIs:

ARM: ArmPkg/Include/Library/ArmMmuLib.h

RISCV: UefiCpuPkg/Include/Library/BaseRiscVMmuLib.h


Thanks,
Chao
On 2024/3/1 19:27, Laszlo Ersek wrote:
> On 3/1/24 02:26, Chao Li wrote:
>> Hi Ray, Lazslo,
>>
>> This library is almost complete to refactored, it refer to ARM and
>> RISC-V version, the API include set/get memory region attribute.
>>
>> I have one last question, in ARM and RISC-V version, even LoongArch old
>> and current version, they all request a configure interface, which may
>> be called in PEI or DXE stage, so should we open configure API? If so,
>> it is possible for ARM RISC-V and LongArch's MMU libraries to be meged
>> into the same instance if they wished.
> Sorry, I've lost all context by now. I don't understand what you mean by
> "they all request a configure interface".
>
> Please provide pathnames and function names for the existent code that
> you are referring to as examples (and then please restate / rephrase
> your question as well).
>
> Laszlo


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#116303): https://edk2.groups.io/g/devel/message/116303
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-03-04  3:39                 ` Chao Li
@ 2024-03-05  9:26                   ` Laszlo Ersek
  2024-03-05 11:50                     ` Chao Li
  0 siblings, 1 reply; 89+ messages in thread
From: Laszlo Ersek @ 2024-03-05  9:26 UTC (permalink / raw)
  To: devel, lichao, Ni, Ray
  Cc: Dong, Eric, Kumar, Rahul R, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

Hello Chao,

On 3/4/24 04:39, Chao Li wrote:
> Hi Laszlo,
> 
> OK.
> 
> When I discussed the CpuMmuLib API as a public API with Ray in the early
> days, the API recommended by Ray should be the patch 13 in this series,
> which only contains set/get memory region attribute, but in the first
> version in this series, it contains more API, one is ConfigureMmu API,
> which is included in ARM and RISCV versions. This API provides a
> interface called in PEI or DXE stage, it will configures the MMU and
> enables it, such as creating the page tables, filling the static page
> tables, cofniguring the MMU registers, enbale MMU, etc.
> 
> The paths for ARM and RISCV version APIs:
> 
> ARM: ArmPkg/Include/Library/ArmMmuLib.h
> 
> RISCV: UefiCpuPkg/Include/Library/BaseRiscVMmuLib.h

I have now re-read this subthread in its entirey, and I think I
understand what's up.

What confused me recently was your expression "so should we open
configure API?"

I think what you meant by "opening" was "publicly declaring".

Anyway, here's my recommendation (consistently with what I said earlier
in this thread):

- I think you need a new (lower-level) library class and instance for
exposing ConfigureMemoryManagementUnit().

Please see: <https://edk2.groups.io/g/devel/message/114972> (msgid
<2a91f2f0-df4c-e106-65cd-79be167224f2@redhat.com>).

- I do not recommend trying to unify the new LoongArch library classes
(low level and high level) with "ArmMmuLib.h" or "BaseRiscVMmuLib.h".

Each of "ArmMmuLib.h" and "BaseRiscVMmuLib.h" seems to declare both
low-level and high-level interfaces. For LoongArch, we apparently don't
want that; hence the idea to introduce two library classes for LoongArch
-- that may be considered an improved design.

At the same time, I don't believe that this requires us to unify
"ArmMmuLib.h" and "BaseRiscVMmuLib.h" with the new, LoongArch-motivated
library class design.

Laszlo



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#116357): https://edk2.groups.io/g/devel/message/116357
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-03-05  9:26                   ` Laszlo Ersek
@ 2024-03-05 11:50                     ` Chao Li
  2024-03-05 12:09                       ` Laszlo Ersek
  0 siblings, 1 reply; 89+ messages in thread
From: Chao Li @ 2024-03-05 11:50 UTC (permalink / raw)
  To: Laszlo Ersek, devel, Ni, Ray
  Cc: Dong, Eric, Kumar, Rahul R, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

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

Hi Laszlo,

OK, I see, let's me try. And I have another question:

Where should the low-level library be placed? Under the 
UefiCpuPkg/Library and as the same folder as CpuMmuLib?


Thanks,
Chao
On 2024/3/5 17:26, Laszlo Ersek wrote:
> Hello Chao,
>
> On 3/4/24 04:39, Chao Li wrote:
>> Hi Laszlo,
>>
>> OK.
>>
>> When I discussed the CpuMmuLib API as a public API with Ray in the early
>> days, the API recommended by Ray should be the patch 13 in this series,
>> which only contains set/get memory region attribute, but in the first
>> version in this series, it contains more API, one is ConfigureMmu API,
>> which is included in ARM and RISCV versions. This API provides a
>> interface called in PEI or DXE stage, it will configures the MMU and
>> enables it, such as creating the page tables, filling the static page
>> tables, cofniguring the MMU registers, enbale MMU, etc.
>>
>> The paths for ARM and RISCV version APIs:
>>
>> ARM: ArmPkg/Include/Library/ArmMmuLib.h
>>
>> RISCV: UefiCpuPkg/Include/Library/BaseRiscVMmuLib.h
> I have now re-read this subthread in its entirey, and I think I
> understand what's up.
>
> What confused me recently was your expression "so should we open
> configure API?"
>
> I think what you meant by "opening" was "publicly declaring".
>
> Anyway, here's my recommendation (consistently with what I said earlier
> in this thread):
>
> - I think you need a new (lower-level) library class and instance for
> exposing ConfigureMemoryManagementUnit().
>
> Please see:<https://edk2.groups.io/g/devel/message/114972>  (msgid
> <2a91f2f0-df4c-e106-65cd-79be167224f2@redhat.com>).
>
> - I do not recommend trying to unify the new LoongArch library classes
> (low level and high level) with "ArmMmuLib.h" or "BaseRiscVMmuLib.h".
>
> Each of "ArmMmuLib.h" and "BaseRiscVMmuLib.h" seems to declare both
> low-level and high-level interfaces. For LoongArch, we apparently don't
> want that; hence the idea to introduce two library classes for LoongArch
> -- that may be considered an improved design.
>
> At the same time, I don't believe that this requires us to unify
> "ArmMmuLib.h" and "BaseRiscVMmuLib.h" with the new, LoongArch-motivated
> library class design.
>
> Laszlo


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#116377): https://edk2.groups.io/g/devel/message/116377
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-03-05 11:50                     ` Chao Li
@ 2024-03-05 12:09                       ` Laszlo Ersek
  2024-03-05 12:12                         ` Chao Li
  0 siblings, 1 reply; 89+ messages in thread
From: Laszlo Ersek @ 2024-03-05 12:09 UTC (permalink / raw)
  To: Chao Li, devel, Ni, Ray
  Cc: Dong, Eric, Kumar, Rahul R, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

On 3/5/24 12:50, Chao Li wrote:
> Hi Laszlo,
> 
> OK, I see, let's me try. And I have another question:
> 
> Where should the low-level library be placed? Under the
> UefiCpuPkg/Library and as the same folder as CpuMmuLib?

In my opinion:

Under UefiCpuPkg/Library -- yes.

In the same folder as CpuMmuLib -- no. Instances of different library
classes are never placed in the same folder.

Laszlo

> 
> 
> Thanks,
> Chao
> On 2024/3/5 17:26, Laszlo Ersek wrote:
>> Hello Chao,
>>
>> On 3/4/24 04:39, Chao Li wrote:
>>> Hi Laszlo,
>>>
>>> OK.
>>>
>>> When I discussed the CpuMmuLib API as a public API with Ray in the early
>>> days, the API recommended by Ray should be the patch 13 in this series,
>>> which only contains set/get memory region attribute, but in the first
>>> version in this series, it contains more API, one is ConfigureMmu API,
>>> which is included in ARM and RISCV versions. This API provides a
>>> interface called in PEI or DXE stage, it will configures the MMU and
>>> enables it, such as creating the page tables, filling the static page
>>> tables, cofniguring the MMU registers, enbale MMU, etc.
>>>
>>> The paths for ARM and RISCV version APIs:
>>>
>>> ARM: ArmPkg/Include/Library/ArmMmuLib.h
>>>
>>> RISCV: UefiCpuPkg/Include/Library/BaseRiscVMmuLib.h
>> I have now re-read this subthread in its entirey, and I think I
>> understand what's up.
>>
>> What confused me recently was your expression "so should we open
>> configure API?"
>>
>> I think what you meant by "opening" was "publicly declaring".
>>
>> Anyway, here's my recommendation (consistently with what I said earlier
>> in this thread):
>>
>> - I think you need a new (lower-level) library class and instance for
>> exposing ConfigureMemoryManagementUnit().
>>
>> Please see: <https://edk2.groups.io/g/devel/message/114972> (msgid
>> <2a91f2f0-df4c-e106-65cd-79be167224f2@redhat.com>).
>>
>> - I do not recommend trying to unify the new LoongArch library classes
>> (low level and high level) with "ArmMmuLib.h" or "BaseRiscVMmuLib.h".
>>
>> Each of "ArmMmuLib.h" and "BaseRiscVMmuLib.h" seems to declare both
>> low-level and high-level interfaces. For LoongArch, we apparently don't
>> want that; hence the idea to introduce two library classes for LoongArch
>> -- that may be considered an improved design.
>>
>> At the same time, I don't believe that this requires us to unify
>> "ArmMmuLib.h" and "BaseRiscVMmuLib.h" with the new, LoongArch-motivated
>> library class design.
>>
>> Laszlo



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#116383): https://edk2.groups.io/g/devel/message/116383
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

* Re: [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
  2024-03-05 12:09                       ` Laszlo Ersek
@ 2024-03-05 12:12                         ` Chao Li
  0 siblings, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-03-05 12:12 UTC (permalink / raw)
  To: devel, lersek, Ni, Ray
  Cc: Dong, Eric, Kumar, Rahul R, Gerd Hoffmann, Baoqi Zhang,
	Dongyan Qian, Xianglai Li, Bibo Mao

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

OK, I'll give it a try.


Thanks,
Chao
On 2024/3/5 20:09, Laszlo Ersek wrote:
> On 3/5/24 12:50, Chao Li wrote:
>> Hi Laszlo,
>>
>> OK, I see, let's me try. And I have another question:
>>
>> Where should the low-level library be placed? Under the
>> UefiCpuPkg/Library and as the same folder as CpuMmuLib?
> In my opinion:
>
> Under UefiCpuPkg/Library -- yes.
>
> In the same folder as CpuMmuLib -- no. Instances of different library
> classes are never placed in the same folder.
>
> Laszlo
>
>>
>> Thanks,
>> Chao
>> On 2024/3/5 17:26, Laszlo Ersek wrote:
>>> Hello Chao,
>>>
>>> On 3/4/24 04:39, Chao Li wrote:
>>>> Hi Laszlo,
>>>>
>>>> OK.
>>>>
>>>> When I discussed the CpuMmuLib API as a public API with Ray in the early
>>>> days, the API recommended by Ray should be the patch 13 in this series,
>>>> which only contains set/get memory region attribute, but in the first
>>>> version in this series, it contains more API, one is ConfigureMmu API,
>>>> which is included in ARM and RISCV versions. This API provides a
>>>> interface called in PEI or DXE stage, it will configures the MMU and
>>>> enables it, such as creating the page tables, filling the static page
>>>> tables, cofniguring the MMU registers, enbale MMU, etc.
>>>>
>>>> The paths for ARM and RISCV version APIs:
>>>>
>>>> ARM: ArmPkg/Include/Library/ArmMmuLib.h
>>>>
>>>> RISCV: UefiCpuPkg/Include/Library/BaseRiscVMmuLib.h
>>> I have now re-read this subthread in its entirey, and I think I
>>> understand what's up.
>>>
>>> What confused me recently was your expression "so should we open
>>> configure API?"
>>>
>>> I think what you meant by "opening" was "publicly declaring".
>>>
>>> Anyway, here's my recommendation (consistently with what I said earlier
>>> in this thread):
>>>
>>> - I think you need a new (lower-level) library class and instance for
>>> exposing ConfigureMemoryManagementUnit().
>>>
>>> Please see:<https://edk2.groups.io/g/devel/message/114972>  (msgid
>>> <2a91f2f0-df4c-e106-65cd-79be167224f2@redhat.com>).
>>>
>>> - I do not recommend trying to unify the new LoongArch library classes
>>> (low level and high level) with "ArmMmuLib.h" or "BaseRiscVMmuLib.h".
>>>
>>> Each of "ArmMmuLib.h" and "BaseRiscVMmuLib.h" seems to declare both
>>> low-level and high-level interfaces. For LoongArch, we apparently don't
>>> want that; hence the idea to introduce two library classes for LoongArch
>>> -- that may be considered an improved design.
>>>
>>> At the same time, I don't believe that this requires us to unify
>>> "ArmMmuLib.h" and "BaseRiscVMmuLib.h" with the new, LoongArch-motivated
>>> library class design.
>>>
>>> Laszlo
>
>
> 
>


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#116384): https://edk2.groups.io/g/devel/message/116384
Mute This Topic: https://groups.io/mt/103971653/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

* Re: [edk2-devel] [PATCH v8 12/37] UefiCpuPkg: Add CPU exception library for LoongArch
  2024-02-02  3:30   ` Ni, Ray
  2024-02-02  3:44     ` Chao Li
@ 2024-03-08  8:02     ` Chao Li
  1 sibling, 0 replies; 89+ messages in thread
From: Chao Li @ 2024-03-08  8:02 UTC (permalink / raw)
  To: devel, ray.ni

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

Hi Ray,


Thanks,
Chao
On 2024/2/2 11:30, Ni, Ray wrote:
> 1. Most of INF changes are not necessary for x86. Can you avoid them?
>
>> +  ## This PCD Contains the pointer to a CPU exception vector base address.
>> +  # @Prompt The pointer to a CPU exception vector base address.
>> +
>> gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress|0x0|UINT
>> 64|0x60000022
>> +
> 2. I do not see any reference to the above PCD in source/INF files.
> Any mistake here?
This PCD used in CpuMmuLib and OvmfPkg/LoongArchVirt.
>
>
>
> 
>


-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#116492): https://edk2.groups.io/g/devel/message/116492
Mute This Topic: https://groups.io/mt/103971651/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-



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

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

end of thread, other threads:[~2024-03-08  8:02 UTC | newest]

Thread overview: 89+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-01-26  6:27 [edk2-devel] [PATCH v8 00/37] Enable LoongArch virtual machine in edk2 Chao Li
2024-01-26  6:27 ` [edk2-devel] [PATCH v8 01/37] MdePkg: Add the header file named Csr.h for LoongArch64 Chao Li
2024-01-26  6:28 ` [edk2-devel] [PATCH v8 02/37] MdePkg: Add LoongArch64 FPU function set into BaseCpuLib Chao Li
2024-01-26  6:28 ` [edk2-devel] [PATCH v8 03/37] MdePkg: Add LoongArch64 exception function set into BaseLib Chao Li
2024-01-26  6:28 ` [edk2-devel] [PATCH v8 04/37] MdePkg: Add LoongArch64 local interrupt " Chao Li
2024-01-26  6:28 ` [edk2-devel] [PATCH v8 05/37] MdePkg: Add LoongArch Cpucfg function Chao Li
2024-01-26  6:28 ` [edk2-devel] [PATCH v8 06/37] MdePkg: Add read stable counter operation for LoongArch Chao Li
2024-01-26  6:28 ` [edk2-devel] [PATCH v8 07/37] MdePkg: Add CSR " Chao Li
2024-01-26  6:28 ` [edk2-devel] [PATCH v8 08/37] MdePkg: Add IOCSR " Chao Li
2024-01-26  6:28 ` [edk2-devel] [PATCH v8 09/37] MdePkg: Add a new library named PeiServicesTablePointerLibKs0 Chao Li
2024-01-26  6:28 ` [edk2-devel] [PATCH v8 10/37] MdePkg: Add some comments for LoongArch exceptions Chao Li
2024-01-26  6:29 ` [edk2-devel] [PATCH v8 11/37] UefiCpuPkg: Add LoongArch64 CPU Timer instance Chao Li
2024-02-02  3:24   ` Ni, Ray
2024-02-02  3:38     ` Chao Li
2024-01-26  6:29 ` [edk2-devel] [PATCH v8 12/37] UefiCpuPkg: Add CPU exception library for LoongArch Chao Li
2024-02-02  3:30   ` Ni, Ray
2024-02-02  3:44     ` Chao Li
2024-02-02  4:30       ` Ni, Ray
2024-03-08  8:02     ` Chao Li
2024-01-26  6:29 ` [edk2-devel] [PATCH v8 13/37] UefiCpuPkg: Add CpuMmuLib.h to UefiCpuPkg Chao Li
2024-01-26  6:29 ` [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib " Chao Li
2024-01-31  9:47   ` Laszlo Ersek
2024-02-01  7:57     ` Chao Li
2024-02-01 22:46       ` Laszlo Ersek
2024-02-02  3:30         ` Chao Li
2024-02-02  3:33       ` Ni, Ray
2024-02-02  3:50         ` Chao Li
2024-02-02  4:30           ` Ni, Ray
2024-03-01  1:26             ` Chao Li
2024-03-01 11:27               ` Laszlo Ersek
2024-03-04  3:39                 ` Chao Li
2024-03-05  9:26                   ` Laszlo Ersek
2024-03-05 11:50                     ` Chao Li
2024-03-05 12:09                       ` Laszlo Ersek
2024-03-05 12:12                         ` Chao Li
     [not found]             ` <17B87F9FA8D0E543.14067@groups.io>
2024-03-01  1:53               ` Chao Li
2024-01-31 10:33   ` Pedro Falcato
2024-01-31 13:41     ` Laszlo Ersek
2024-01-31 17:46       ` Pedro Falcato
2024-02-01  3:05         ` Chao Li
2024-02-01 19:36           ` Pedro Falcato
2024-02-01 23:02             ` Laszlo Ersek
2024-02-02 15:14             ` Leif Lindholm
2024-02-04  2:58               ` Chao Li
     [not found]               ` <17B0898B4883051D.13964@groups.io>
2024-02-06  2:57                 ` Chao Li
2024-02-06 14:32                   ` Laszlo Ersek
2024-02-06 16:45                     ` Pedro Falcato
2024-01-26  6:29 ` [edk2-devel] [PATCH v8 15/37] UefiCpuPkg: Add multiprocessor library for LoongArch64 Chao Li
2024-01-26  6:29 ` [edk2-devel] [PATCH v8 16/37] UefiCpuPkg: Add CpuDxe driver " Chao Li
2024-01-26  6:29 ` [edk2-devel] [PATCH v8 17/37] EmbeddedPkg: Add PcdPrePiCpuIoSize width for LOONGARCH64 Chao Li
2024-01-26  6:29 ` [edk2-devel] [PATCH v8 18/37] ArmVirtPkg: Move PCD of FDT base address and FDT padding to OvmfPkg Chao Li
2024-02-01 23:20   ` Laszlo Ersek
2024-01-26  6:29 ` [edk2-devel] [PATCH v8 19/37] UefiCpuPkg: Add a new CPU IO 2 driver named CpuMmio2Dxe Chao Li
2024-01-26  6:29 ` [edk2-devel] [PATCH v8 20/37] ArmVirtPkg: Enable CpuMmio2Dxe Chao Li
2024-02-01 22:19   ` Laszlo Ersek
2024-01-26  6:30 ` [edk2-devel] [PATCH v8 21/37] OvmfPkg/RiscVVirt: " Chao Li
2024-01-26  6:30 ` [edk2-devel] [PATCH v8 22/37] OvmfPkg/RiscVVirt: Remove PciCpuIo2Dxe from RiscVVirt Chao Li
2024-01-26  6:30 ` [edk2-devel] [PATCH v8 23/37] ArmVirtPkg: Move the FdtSerialPortAddressLib to OvmfPkg Chao Li
2024-01-29 19:27   ` Laszlo Ersek
2024-01-26  6:30 ` [edk2-devel] [PATCH v8 24/37] ArmVirtPkg: Move two PCD variables into OvmfPkg Chao Li
2024-01-29 19:49   ` Laszlo Ersek
2024-01-30  1:24     ` Chao Li
2024-01-30 16:45       ` Laszlo Ersek
2024-01-31  1:30         ` Chao Li
2024-01-26  6:30 ` [edk2-devel] [PATCH v8 25/37] ArmVirtPkg: Move PlatformBootManagerLib to OvmfPkg Chao Li
2024-01-29 19:51   ` Laszlo Ersek
2024-01-26  6:30 ` [edk2-devel] [PATCH v8 26/37] OvmfPkg/LoongArchVirt: Add stable timer driver Chao Li
2024-01-26  6:30 ` [edk2-devel] [PATCH v8 27/37] OvmfPkg/LoongArchVirt: Add a NULL library named CollectApResouceLibNull Chao Li
2024-01-26  6:30 ` [edk2-devel] [PATCH v8 28/37] OvmfPkg/LoongArchVirt: Add serial port hook library Chao Li
2024-01-26  6:30 ` [edk2-devel] [PATCH v8 29/37] OvmfPkg/LoongArchVirt: Add the early serial port output library Chao Li
2024-01-26  6:30 ` [edk2-devel] [PATCH v8 30/37] OvmfPkg/LoongArchVirt: Add real time clock library Chao Li
2024-01-26  6:30 ` [edk2-devel] [PATCH v8 31/37] OvmfPkg/LoongArchVirt: Add NorFlashQemuLib Chao Li
2024-01-26  6:30 ` [edk2-devel] [PATCH v8 32/37] OvmfPkg/LoongArchVirt: Add FdtQemuFwCfgLib Chao Li
2024-01-26  6:31 ` [edk2-devel] [PATCH v8 33/37] OvmfPkg/LoongArchVirt: Add reset system library Chao Li
2024-01-26  6:31 ` [edk2-devel] [PATCH v8 34/37] OvmfPkg/LoongArchVirt: Support SEC phase Chao Li
2024-01-26  6:31 ` [edk2-devel] [PATCH v8 35/37] OvmfPkg/LoongArchVirt: Support PEI phase Chao Li
2024-01-26  6:31 ` [edk2-devel] [PATCH v8 36/37] OvmfPkg/LoongArchVirt: Add build file Chao Li
2024-01-26  6:31 ` [edk2-devel] [PATCH v8 37/37] OvmfPkg/LoongArchVirt: Add self introduction file Chao Li
     [not found] ` <17ADD1D5A196C454.24595@groups.io>
2024-01-31  3:30   ` [edk2-devel] [PATCH v8 11/37] UefiCpuPkg: Add LoongArch64 CPU Timer instance Chao Li
     [not found]   ` <17AF510405DE784C.15701@groups.io>
2024-01-31  5:28     ` Chao Li
2024-01-31 10:47       ` Laszlo Ersek
     [not found] ` <17ADD1D7001C37D6.11113@groups.io>
2024-01-31  3:31   ` [edk2-devel] [PATCH v8 12/37] UefiCpuPkg: Add CPU exception library for LoongArch Chao Li
     [not found]   ` <17AF510933F4B8FA.15701@groups.io>
2024-01-31  5:29     ` Chao Li
     [not found] ` <17ADD1D9CA04F352.11113@groups.io>
2024-01-31  3:31   ` [edk2-devel] [PATCH v8 14/37] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg Chao Li
     [not found]   ` <17AF511188DE2475.15701@groups.io>
2024-01-31  5:32     ` Chao Li
     [not found] ` <17ADD1DB56FC4702.24595@groups.io>
2024-01-31  3:32   ` [edk2-devel] [PATCH v8 15/37] UefiCpuPkg: Add multiprocessor library for LoongArch64 Chao Li
     [not found]   ` <17AF511741BD9C8B.15701@groups.io>
2024-01-31  5:33     ` Chao Li
     [not found] ` <17ADD1DCBDD4B7FE.11113@groups.io>
2024-01-31  3:32   ` [edk2-devel] [PATCH v8 16/37] UefiCpuPkg: Add CpuDxe driver " Chao Li
     [not found]   ` <17AF511F29808828.16460@groups.io>
2024-01-31  5:33     ` Chao Li

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