Read the memory map information through the QemuFwCfg interface,
then build the page table through the memory map information,
and finally enable Mmu.
REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054
Cc: Bibo Mao <maobibo@loongson.cn>
Cc: Chao Li <lichao@loongson.cn>
Cc: Leif Lindholm <quic_llindhol@quicinc.com>
Cc: Liming Gao <gaoliming@byosoft.com.cn>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Signed-off-by: xianglai li <lixianglai@loongson.cn>
---
.../LoongArchQemuPkg/Include/Library/MmuLib.h | 85 ++
.../LoongArchQemuPkg/Library/MmuLib/Mmu.S | 155 ++++
.../Library/MmuLib/MmuBaseLib.inf | 40 +
.../Library/MmuLib/MmuBaseLibPei.inf | 47 +
.../Library/MmuLib/MmuLibCore.c | 831 ++++++++++++++++++
.../Library/MmuLib/MmuLibCore.h | 40 +
.../Library/MmuLib/MmuLibCorePei.c | 231 +++++
.../LoongArchQemuPkg/Library/MmuLib/mmu.h | 190 ++++
.../LoongArchQemuPkg/Library/MmuLib/page.h | 280 ++++++
.../LoongArchQemuPkg/Library/MmuLib/pte.h | 57 ++
10 files changed, 1956 insertions(+)
create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h
create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S
create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf
create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf
create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c
create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.h
create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c
create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.h
create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h
create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h
diff --git a/Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h b/Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h
new file mode 100644
index 0000000000..9880fc385c
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h
@@ -0,0 +1,85 @@
+/** @file
+
+ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - EXC - execute
+**/
+#ifndef MMU_LIB_H_
+#define MMU_LIB_H_
+/**
+ write operation is performed Count times from the first element of Buffer.
+ Convert EFI Attributes to Loongarch Attributes.
+ @param[in] EfiAttributes Efi Attributes.
+
+ @retval LoongArch Attributes.
+**/
+UINTN
+EfiAttributeToLoongArchAttribute (
+ IN UINTN EfiAttributes
+ );
+
+/**
+ Finds 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] EndAddress To find the end address of the memory region.
+ @param[out] RegionLength 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
+GetLoongArchMemoryRegion (
+ IN UINTN BaseAddress,
+ IN UINTN EndAddress,
+ 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.
+
+ @retval EFI_SUCCESS The Attributes was set successfully
+**/
+EFI_STATUS
+LoongArchSetMemoryAttributes (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINTN Length,
+ IN UINTN Attributes
+ );
+
+/**
+ Sets the non-executable Attributes for 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.
+
+ @retval EFI_SUCCESS The Attributes was set successfully
+**/
+EFI_STATUS
+LoongArchSetMemoryRegionNoExec (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINTN Length
+ );
+
+/**
+ Create a page table and initialize the MMU.
+
+ @param[] VOID
+
+ @retval VOID
+**/
+VOID
+EFIAPI
+ConfigureMmu (
+ VOID
+ );
+#endif // MMU_LIB_H_
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S
new file mode 100644
index 0000000000..d5863de072
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S
@@ -0,0 +1,155 @@
+#------------------------------------------------------------------------------
+#
+# LoongArch for LoongArch
+#
+# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#-----------------------------------------------------------------------------
+
+#ifndef __ASSEMBLY__
+#define __ASSEMBLY__
+#endif
+
+#include "Library/Cpu.h"
+#include "mmu.h"
+
+ASM_GLOBAL ASM_PFX(HandleTlbRefill)
+ASM_GLOBAL HandleTlbRefillEnd
+ASM_GLOBAL ASM_PFX(LoongarchInvalidTlb)
+ASM_GLOBAL ASM_PFX(SetTlbRefillFuncBase)
+ASM_GLOBAL ASM_PFX(WriteCsrPageSize)
+ASM_GLOBAL ASM_PFX(WriteCsrTlbRefillPageSize)
+ASM_GLOBAL ASM_PFX(WriteCsrStlbPageSize)
+ASM_GLOBAL ASM_PFX(LoongArchWriteqCsrPwctl0)
+ASM_GLOBAL ASM_PFX(LoongArchWriteqCsrPwctl1)
+ASM_GLOBAL ASM_PFX(LoongArchWriteqCsrPgdl)
+ASM_GLOBAL ASM_PFX(LoongArchWriteqCsrPgdh)
+ASM_GLOBAL ASM_PFX(LoongArchXchgCsrCrmd)
+
+#
+# Refill the page table.
+# @param VOID
+# @retval VOID
+#
+
+ASM_PFX(HandleTlbRefill):
+ 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
+ csrrd T0, LOONGARCH_CSR_TLBRSAVE
+ ertn
+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(LoongarchInvalidTlb):
+ invtlb INVTLB_ADDR_GTRUE_OR_ASID, ZERO, A0
+ jirl ZERO, RA, 0
+
+#
+# Set Tlb Refill function to hardware
+# @param A0 The address of tlb refill function
+# @retval none
+#
+
+ASM_PFX(SetTlbRefillFuncBase):
+ csrwr A0, LOONGARCH_CSR_TLBREBASE
+ jirl ZERO, RA,0
+
+#
+# Set Cpu Status Register Page Size.
+# @param A0 Page Size.
+# @retval none
+#
+
+ASM_PFX(WriteCsrPageSize):
+ li.d T0, CSR_TLBIDX_SIZE
+ sll.d A0, A0, T0
+ li.d T0, CSR_TLBIDX_SIZE_MASK
+ csrxchg A0, T0, LOONGARCH_CSR_TLBIDX
+ jirl ZERO, RA,0
+
+#
+# Set Cpu Status Register TLBREFILL Page Size.
+# @param A0 Page Size.
+# @retval none
+#
+
+ASM_PFX(WriteCsrTlbRefillPageSize):
+ li.d T0, CSR_TLBREHI_PS_SHIFT
+ sll.d A0, A0, T0
+ li.d T0, CSR_TLBREHI_PS
+ csrxchg A0, T0, LOONGARCH_CSR_TLBREHI
+ jirl ZERO, RA,0
+
+#
+# Set Cpu Status Register STLB Page Size.
+# @param val Page Size.
+# @retval VOID
+#
+
+ASM_PFX(WriteCsrStlbPageSize):
+ csrwr A0, LOONGARCH_CSR_STLBPGSIZE
+ jirl ZERO, RA,0
+
+#
+# Write Csr PWCTL0 register.
+# @param A0 The value used to write to the PWCTL0 register
+# @retval none
+#
+
+ASM_PFX(LoongArchWriteqCsrPwctl0):
+ csrwr A0, LOONGARCH_CSR_PWCTL0
+ jirl ZERO, RA,0
+
+#
+# Write Csr PWCTL1 register.
+# @param A0 The value used to write to the PWCTL1 register
+# @retval none
+#
+
+ASM_PFX(LoongArchWriteqCsrPwctl1):
+ csrwr A0, LOONGARCH_CSR_PWCTL1
+ jirl ZERO, RA,0
+
+#
+# Write Csr PGDL register.
+# @param A0 The value used to write to the PGDL register
+# @retval none
+#
+
+ASM_PFX(LoongArchWriteqCsrPgdl):
+ csrwr A0, LOONGARCH_CSR_PGDL
+ jirl ZERO, RA,0
+
+#
+# Write Csr PGDH register.
+# @param A0 The value used to write to the PGDH register
+# @retval none
+#
+
+ASM_PFX(LoongArchWriteqCsrPgdh):
+ csrwr A0, LOONGARCH_CSR_PGDH
+ jirl ZERO, RA,0
+
+#
+# Exchange specified bit data with the Csr CRMD register.
+# @param[IN] A0 The value Exchanged with the CSR CRMD register.
+# @param[IN] A1 Specifies the mask for swapping bits
+# @retval VOID
+#
+
+ASM_PFX(LoongArchXchgCsrCrmd):
+ csrxchg A0, A1, LOONGARCH_CSR_CRMD
+ jirl ZERO, RA,0
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf
new file mode 100644
index 0000000000..abd864a324
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf
@@ -0,0 +1,40 @@
+## @file
+#
+# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = MmuBaseLib
+ FILE_GUID = da8f0232-fb14-42f0-922c-63104d2c70be
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = MmuLib
+ CONSTRUCTOR = MmuInitialize
+
+#
+# VALID_ARCHITECTURES = LOONGARCH64
+#
+
+[Sources]
+ MmuLibCore.c
+ Mmu.S
+
+[Packages]
+ MdePkg/MdePkg.dec
+ Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+
+[PCD]
+ gLoongArchQemuPkgTokenSpaceGuid.PcdSwapPageDir
+ gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPgd
+ gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPud
+ gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPmd
+ gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPte
+
+[LibraryClasses]
+ MemoryAllocationLib
+ PcdLib
+ DebugLib
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf
new file mode 100644
index 0000000000..12848eecfe
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf
@@ -0,0 +1,47 @@
+## @file
+#
+# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = MmuPeiLib
+ FILE_GUID = da8f0232-fb14-42f0-922c-63104d2c70bd
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = MmuLib | SEC PEIM
+
+#
+# VALID_ARCHITECTURES = LOONGARCH64
+#
+
+[Sources]
+ MmuLibCorePei.c
+ Mmu.S
+ MmuLibCore.h
+ MmuLibCore.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ Platform/Loongson/LoongArchQemuPkg/Loongson.dec
+ OvmfPkg/OvmfPkg.dec
+
+[PCD]
+ gLoongArchQemuPkgTokenSpaceGuid.PcdSwapPageDir
+ gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPgd
+ gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPud
+ gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPmd
+ gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPte
+ gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvSize
+ gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvBase
+ gLoongArchQemuPkgTokenSpaceGuid.PcdRamSize
+
+[LibraryClasses]
+ MemoryAllocationLib
+ CacheMaintenanceLib
+ PcdLib
+ DebugLib
+ QemuFwCfgLib
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c
new file mode 100644
index 0000000000..b932e3d568
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c
@@ -0,0 +1,831 @@
+/** @file
+
+ Copyright (c) 2022 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/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include "Library/Cpu.h"
+#include "pte.h"
+#include "page.h"
+#include "mmu.h"
+
+BOOLEAN mMmuInited = FALSE;
+/**
+ Check to see if mmu successfully initializes.
+
+ @param VOID.
+
+ @retval TRUE Initialization has been completed.
+ FALSE Initialization did not complete.
+**/
+BOOLEAN
+MmuIsInit (VOID) {
+ if ((mMmuInited == TRUE) ||
+ (PcdGet64 (PcdSwapPageDir) != 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.
+
+ @retval VOID.
+**/
+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.
+**/
+PGD *
+PgdOffset (
+ IN UINTN Address
+ )
+{
+ return ((PGD *)PcdGet64 (PcdSwapPageDir)) + 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.
+**/
+PUD *
+PudOffset (
+ IN PGD *Pgd,
+ IN UINTN Address
+ )
+{
+ UINTN 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.
+**/
+PMD *
+PmdOffset (
+ IN PUD *Pud,
+ IN UINTN Address
+ )
+{
+ UINTN 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.
+**/
+PTE *
+PteOffset (
+ IN PMD *Pmd,
+ IN UINTN Address
+ )
+{
+ UINTN 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.
+
+ @retval VOID
+**/
+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.
+
+ @retval VOID
+**/
+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.
+
+ @retval VOID
+**/
+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.
+
+ @retval VOID
+**/
+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.
+
+ @retval VOID
+**/
+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.
+
+ @retval VOID
+**/
+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.
+
+ @retval VOID
+**/
+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.
+**/
+INTN
+PudAlloc (
+ IN PGD *Pgd
+ )
+{
+ PUD *Pud = (PUD *) AllocatePages (1);
+ if (!Pud) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ PageDirInit ((VOID *)Pud, ENTRYS_PER_PUD, (VOID *)PcdGet64 (PcdInvalidPmd));
+
+ if (pgd_none (*Pgd)) {
+ SetPgd (Pgd, Pud);
+ } else { /* Another has populated it */
+ PudFree (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.
+**/
+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 *)PcdGet64 (PcdInvalidPte));
+
+ if (pud_none (*Pud)) {
+ SetPud (Pud, Pmd);
+ } else {/* Another has populated it */
+ PmdFree (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.
+**/
+INTN
+PteAlloc (
+ IN PMD *Pmd
+ )
+{
+ PTE *Pte;
+
+ Pte = (PTE *) AllocatePages (1);
+ if (!Pte) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Pte = ZeroMem (Pte, EFI_PAGE_SIZE);
+
+ if (pmd_none (*Pmd)) {
+ SetPmd (Pmd, Pte);
+ } else { /* Another has populated it */
+ PteFree (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.
+
+ @retval Gets the page upper directory entry
+**/
+PUD *
+PudAllocGet (
+ IN PGD *Pgd,
+ IN UINTN Address
+ )
+{
+ return ((pgd_none (*(Pgd)) && PudAlloc (Pgd)) ?
+ NULL : 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.
+
+ @retval Gets the page middle directory entry
+**/
+PMD *
+PmdAllocGet (
+ IN PUD *Pud,
+ IN UINTN Address
+ )
+{
+ PMD * ret = (pud_none (*Pud) && PmdAlloc (Pud))?
+ NULL: PmdOffset (Pud, Address);
+ DEBUG ((DEBUG_VERBOSE, "%a %d PudVal %p PmdOffset %p PMD_INDEX %p .\n", __func__, __LINE__,
+ Pud->PudVal, PmdOffset (Pud, Address), PMD_INDEX (Address) ));
+
+ return ret;
+}
+
+/**
+ 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.
+
+ @retval Gets the page table entry
+**/
+PTE *
+PteAllocGet (
+ IN PMD *Pmd,
+ IN UINTN Address
+ )
+{
+ return (pmd_none (*Pmd) && PteAlloc (Pmd))?
+ NULL: 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
+**/
+PTE *
+GetPteAddress (
+ IN UINTN Address
+ )
+{
+ PGD *Pgd;
+ PUD *Pud;
+ PMD *Pmd;
+
+ Pgd = PgdOffset (Address);
+
+ if (pgd_none (*Pgd)) {
+ return NULL;
+ }
+
+ Pud = PudOffset (Pgd, Address);
+
+ if (pud_none (*Pud)) {
+ return NULL;
+ }
+
+ Pmd = PmdOffset (Pud, Address);
+ if (pmd_none (*Pmd)) {
+ return NULL;
+ }
+
+ if (IS_HUGE_PAGE (Pmd->PmdVal)) {
+ return ((PTE *)Pmd);
+ }
+
+ return PteOffset (Pmd, Address);
+}
+
+/**
+ 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.
+**/
+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;
+ }
+
+ do {
+ UpDate = FALSE;
+ PteVal = MAKE_PTE (Address, Attributes);
+ DEBUG ((DEBUG_VERBOSE,
+ "%a %d Address %p PGD_INDEX %p PUD_INDEX %p PMD_INDEX %p PTE_INDEX %p MAKE_PTE %p\n",
+ __func__, __LINE__, Address, PGD_INDEX (Address), PUD_INDEX (Address), PMD_INDEX (Address),
+ PTE_INDEX (Address), PteVal));
+
+ if ((!pte_none (*Pte)) &&
+ (PTE_VAL(*Pte) != PTE_VAL(PteVal)))
+ {
+ UpDate = TRUE;
+ }
+
+ SetPte (Pte, PteVal);
+ if (UpDate) {
+ LoongarchInvalidTlb(Address);
+ }
+ } while (Pte++, Address += EFI_PAGE_SIZE, Address != End);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ 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.
+**/
+EFI_STATUS
+MemoryMapPmdRange (
+ IN PUD *Pud,
+ IN UINTN Address,
+ IN UINTN End,
+ IN UINTN Attributes
+ )
+{
+ PMD *Pmd;
+ PTE *Pte;
+ UINTN Next;
+ UINTN AddressStart_HugePage;
+ UINTN AddressEnd_HugePage;
+
+ Pmd = PmdAllocGet (Pud, Address);
+ if (!Pmd) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ do {
+ Next = PMD_ADDRESS_END (Address, End);
+ if (((Address & (~PMD_MASK)) == 0) &&
+ ((Next & (~PMD_MASK)) == 0) &&
+ (pmd_none (*Pmd)))
+ {
+ DEBUG ((DEBUG_VERBOSE,
+ "%a %d Address %p PGD_INDEX %p PUD_INDEX %p PMD_INDEX %p MAKE_HUGE_PTE %p\n",
+ __func__, __LINE__, Address, PGD_INDEX (Address), PUD_INDEX (Address), PMD_INDEX (Address),
+ MAKE_HUGE_PTE (Address, Attributes)));
+
+ SetPmd (Pmd, (PTE *)MAKE_HUGE_PTE (Address, Attributes));
+ } else {
+ if ((pmd_none (*Pmd)) ||
+ ((!pmd_none (*Pmd)) &&
+ (!IS_HUGE_PAGE (Pmd->PmdVal))))
+ {
+ if (MemoryMapPteRange (Pmd, Address, Next, Attributes)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ } else {
+ SetPmd (Pmd, (PTE *)PcdGet64 (PcdInvalidPte));
+ AddressStart_HugePage = Address & PMD_MASK;
+ AddressEnd_HugePage = AddressStart_HugePage + HUGE_PAGE_SIZE;
+ if (MemoryMapPteRange (Pmd, AddressStart_HugePage, AddressEnd_HugePage, Attributes)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Pte = GetPteAddress (AddressStart_HugePage);
+ if (Pte == NULL) {
+ continue ;
+ }
+ if (AddressEnd_HugePage > End) {
+ Next = End;
+ }
+ }
+ }
+ } while (Pmd++, Address = Next, Address != End);
+
+ return 0;
+}
+
+/**
+ 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.
+**/
+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) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ do {
+ Next = PUD_ADDRESS_END (Address, End);
+ if (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.
+**/
+EFI_STATUS
+MemoryMapPageRange (
+ IN UINTN Start,
+ IN UINTN End,
+ IN UINTN Attributes
+ )
+{
+ PGD *Pgd;
+ UINTN Next;
+ UINTN Address = Start;
+ EFI_STATUS Err;
+
+ Pgd = PgdOffset (Address);
+ do {
+ Next = PGD_ADDRESS_END (Address, End);
+ 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);
+}
+
+/**
+ write operation is performed Count times from the first element of Buffer.
+ Convert EFI Attributes to Loongarch Attributes.
+ @param[in] EfiAttributes Efi Attributes.
+
+ @retval LoongArch Attributes.
+**/
+UINTN
+EfiAttributeToLoongArchAttribute (
+ IN UINTN EfiAttributes
+ )
+{
+ UINTN LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | CACHE_CC | PAGE_USER | PAGE_GLOBAL;
+ switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
+ case EFI_MEMORY_UC:
+ LoongArchAttributes |= CACHE_SUC;
+ break;
+ case EFI_MEMORY_WC:
+ 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) {
+ LoongArchAttributes &= ~PAGE_DIRTY;
+ }
+
+ //eXecute protection attribute
+ if ((EfiAttributes & EFI_MEMORY_XP) != 0) {
+ LoongArchAttributes |= PAGE_NO_EXEC;
+ }
+
+ return LoongArchAttributes;
+}
+
+/**
+ Finds 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] EndAddress To find the end address of the memory region.
+ @param[out] RegionLength 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
+GetLoongArchMemoryRegion (
+ IN UINTN BaseAddress,
+ IN UINTN EndAddress,
+ OUT UINTN *RegionLength,
+ OUT UINTN *RegionAttributes
+ )
+{
+ PTE *Pte;
+ UINTN Attributes;
+ UINTN AttributesTmp;
+ UINTN MaxAddress;
+ MaxAddress = LShiftU64 (1ULL, MAX_VA_BITS) - 1;
+ Pte = GetPteAddress (BaseAddress);
+
+ if (!MmuIsInit ()) {
+ return EFI_SUCCESS;
+ }
+ if (Pte == NULL) {
+ return EFI_NOT_FOUND;
+ }
+ Attributes = GET_PAGE_ATTRIBUTES (*Pte);
+ if (IS_HUGE_PAGE (Pte->PteVal)) {
+ *RegionAttributes = Attributes & (~(PAGE_HUGE));
+ *RegionLength += HUGE_PAGE_SIZE;
+ } else {
+ *RegionLength += EFI_PAGE_SIZE;
+ *RegionAttributes = Attributes;
+ }
+
+ while (BaseAddress <= MaxAddress) {
+ Pte = GetPteAddress (BaseAddress);
+ if (Pte == NULL) {
+ return EFI_SUCCESS;
+ }
+ AttributesTmp = GET_PAGE_ATTRIBUTES (*Pte);
+ if (IS_HUGE_PAGE (Pte->PteVal)) {
+ if (AttributesTmp == Attributes) {
+ *RegionLength += HUGE_PAGE_SIZE;
+ }
+ BaseAddress += HUGE_PAGE_SIZE;
+ } else {
+ if (AttributesTmp == Attributes) {
+ *RegionLength += EFI_PAGE_SIZE;
+ }
+ BaseAddress += EFI_PAGE_SIZE;
+ }
+
+ if (BaseAddress > EndAddress) {
+ break;
+ }
+ }
+ 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.
+
+ @retval EFI_SUCCESS The Attributes was set successfully
+**/
+EFI_STATUS
+LoongArchSetMemoryAttributes (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINTN Length,
+ IN UINTN Attributes
+ )
+{
+
+ if (!MmuIsInit ()) {
+ return EFI_SUCCESS;
+ }
+ Attributes = EfiAttributeToLoongArchAttribute (Attributes);
+ DEBUG ((DEBUG_VERBOSE, "%a %d %p %p %p.\n", __func__, __LINE__, BaseAddress , Length, Attributes));
+ MemoryMapPageRange (BaseAddress, BaseAddress + Length, Attributes);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Sets the non-executable Attributes for 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.
+
+ @retval EFI_SUCCESS The Attributes was set successfully
+**/
+EFI_STATUS
+LoongArchSetMemoryRegionNoExec (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINTN Length
+ )
+{
+ if (MmuIsInit ()) {
+ Length = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Length));
+ LoongArchSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_XP);
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Check to see if mmu successfully initializes and saves the result.
+
+ @param VOID.
+
+ @retval EFI_SUCCESS Initialization succeeded.
+**/
+EFI_STATUS
+MmuInitialize (VOID)
+{
+ if (PcdGet64 (PcdSwapPageDir) != 0) {
+ mMmuInited = TRUE;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.h
new file mode 100644
index 0000000000..7a5e8ea0dd
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.h
@@ -0,0 +1,40 @@
+/** @file
+
+ Copyright (c) 2022 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/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c
new file mode 100644
index 0000000000..32a7fc0beb
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c
@@ -0,0 +1,231 @@
+/** @file
+ Platform PEI driver
+
+ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - FwCfg - Firmeware Config
+ - Tlb - Translation Lookaside Buffer
+**/
+#include <Uefi.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include "Library/Cpu.h"
+#include "pte.h"
+#include "page.h"
+#include "mmu.h"
+#include <Library/QemuFwCfgLib.h>
+#include "MmuLibCore.h"
+#include <Library/CacheMaintenanceLib.h>
+
+/**
+ Return the Virtual Memory Map of your platform
+
+ This Virtual Memory Map is used by MemoryInitPei Module to initialize the MMU
+ on your platform.
+
+ @param[out] VirtualMemoryMap 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
+GetMemoryMapFromFwCfg (
+ OUT MEMORY_REGION_DESCRIPTOR **VirtualMemoryMap
+ )
+{
+
+ EFI_STATUS Status;
+ FIRMWARE_CONFIG_ITEM FwCfgItem;
+ UINTN FwCfgSize;
+ LOONGARCH_MEMMAP_ENTRY MemoryMapEntry;
+ LOONGARCH_MEMMAP_ENTRY *StartEntry;
+ LOONGARCH_MEMMAP_ENTRY *pEntry;
+ UINTN Processed;
+ MEMORY_REGION_DESCRIPTOR *VirtualMemoryTable;
+ UINTN Index = 0;
+ ASSERT (VirtualMemoryMap != NULL);
+
+ VirtualMemoryTable = AllocatePool (
+ sizeof (MEMORY_REGION_DESCRIPTOR) *
+ MAX_VIRTUAL_MEMORY_MAP_DESCRIPTORS
+ );
+ 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));
+ *VirtualMemoryMap = 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));
+ *VirtualMemoryMap = VirtualMemoryTable;
+ return ;
+}
+
+/**
+ Create a page table and initialize the MMU.
+
+ @param[] VOID
+
+ @retval VOID
+**/
+EFIAPI
+VOID
+ConfigureMmu (VOID)
+{
+ PGD *SwapperPageDir = NULL;
+ PGD *InvalidPgd = NULL;
+ PUD *InvalidPudTable = NULL;
+ PMD *InvalidPmdTable = NULL;
+ PTE *InvalidPteTable = NULL;
+ MEMORY_REGION_DESCRIPTOR *MemoryTable = NULL;
+ RETURN_STATUS PcdStatus;
+ UINTN PgdShift = PGD_SHIFT;
+ UINTN PgdWide = PGD_WIDE;
+ UINTN PudShift = PUD_SHIFT;
+ UINTN PudWide = PUD_WIDE;
+ UINTN PmdShift = PMD_SHIFT;
+ UINTN PmdWide = PMD_WIDE;
+ UINTN PteShift = PTE_SHIFT;
+ UINTN PteWide = PTE_WIDE;
+ UINTN PageEnable = 1 << 4;
+ VOID *TlbReEntry;
+
+ SwapperPageDir = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
+ InvalidPgd = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
+ InvalidPudTable = AllocatePages (EFI_SIZE_TO_PAGES (PUD_TABLE_SIZE));
+ InvalidPmdTable = AllocatePages (EFI_SIZE_TO_PAGES (PMD_TABLE_SIZE));
+ InvalidPteTable = AllocatePages (EFI_SIZE_TO_PAGES (PTE_TABLE_SIZE));
+ ZeroMem (InvalidPteTable, PTE_TABLE_SIZE);
+
+ if ((!InvalidPgd) ||
+ (!InvalidPudTable) ||
+ (!InvalidPmdTable) ||
+ (!InvalidPteTable))
+ {
+ goto FreeTranslationTable;
+ }
+
+ /*pgd init*/
+ PageDirInit (SwapperPageDir , ENTRYS_PER_PGD, InvalidPudTable);
+ /*pgd init*/
+ PageDirInit (InvalidPgd, ENTRYS_PER_PGD, InvalidPudTable);
+ /*pud init*/
+ PageDirInit (InvalidPudTable, ENTRYS_PER_PUD, InvalidPmdTable);
+ /*pmd init*/
+ PageDirInit (InvalidPmdTable, ENTRYS_PER_PMD, InvalidPteTable);
+ GetMemoryMapFromFwCfg (&MemoryTable);
+
+ PcdStatus |= PcdSet64S (PcdSwapPageDir, (UINTN)SwapperPageDir);
+ PcdStatus |= PcdSet64S (PcdInvalidPgd, (UINTN)InvalidPgd);
+ PcdStatus |= PcdSet64S (PcdInvalidPud, (UINTN)InvalidPudTable);
+ PcdStatus |= PcdSet64S (PcdInvalidPmd, (UINTN)InvalidPmdTable);
+ PcdStatus |= PcdSet64S (PcdInvalidPte, (UINTN)InvalidPteTable);
+ ASSERT_RETURN_ERROR (PcdStatus);
+
+ while (MemoryTable->Length != 0) {
+ DEBUG ((DEBUG_VERBOSE, "%a %d VirtualBase %p VirtualEnd %p Attributes %p .\n", __func__, __LINE__,
+ MemoryTable->VirtualBase,
+ (MemoryTable->Length + MemoryTable->VirtualBase),
+ MemoryTable->Attributes));
+
+ PcdStatus = FillTranslationTable (MemoryTable);
+ if (EFI_ERROR (PcdStatus)) {
+ goto FreeTranslationTable;
+ }
+ MemoryTable++;
+ }
+
+ TlbReEntry = AllocatePages (1);
+ if (TlbReEntry == NULL) {
+ goto FreeTranslationTable;
+ }
+ CopyMem ((char *)TlbReEntry, HandleTlbRefill, (HandleTlbRefillEnd - HandleTlbRefill));
+ InvalidateInstructionCacheRange ((VOID *)(UINTN)HandleTlbRefill, (UINTN)(HandleTlbRefillEnd - HandleTlbRefill));
+
+ DEBUG ((DEBUG_VERBOSE,
+ "%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));
+
+ SetTlbRefillFuncBase ((UINTN)TlbReEntry);
+ /*set page size*/
+ WriteCsrPageSize (DEFAULT_PAGE_SIZE);
+ WriteCsrStlbPageSize (DEFAULT_PAGE_SIZE);
+ WriteCsrTlbRefillPageSize (DEFAULT_PAGE_SIZE);
+
+ LoongArchWriteqCsrPwctl0 ((PteShift | PteWide << 5 | PmdShift << 10 | PmdWide << 15 | PudShift << 20 | PudWide << 25
+ ));
+ LoongArchWriteqCsrPwctl1 (PgdShift | PgdWide << 6);
+ LoongArchWriteqCsrPgdl ((UINTN)SwapperPageDir);
+ LoongArchWriteqCsrPgdh ((UINTN)InvalidPgd);
+
+ DEBUG ((DEBUG_INFO, "%a %d Enable Mmu Start PageBassAddress %p.\n", __func__, __LINE__, SwapperPageDir));
+ LoongArchXchgCsrCrmd ( PageEnable, 1 << 4);
+ DEBUG ((DEBUG_INFO, "%a %d Enable Mmu End.\n", __func__, __LINE__));
+
+ return ;
+
+FreeTranslationTable:
+ if (SwapperPageDir) {
+ FreePages (SwapperPageDir, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
+ }
+
+ if (InvalidPgd) {
+ FreePages (InvalidPgd, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
+ }
+
+ if (InvalidPudTable) {
+ FreePages (InvalidPudTable, EFI_SIZE_TO_PAGES (PUD_TABLE_SIZE));
+ }
+
+ if (InvalidPmdTable) {
+ FreePages (InvalidPmdTable, EFI_SIZE_TO_PAGES (PMD_TABLE_SIZE));
+ }
+
+ if (InvalidPteTable) {
+ FreePages (InvalidPteTable, EFI_SIZE_TO_PAGES (PTE_TABLE_SIZE));
+ }
+
+ PcdSet64S (PcdSwapPageDir, (UINTN)0);
+ PcdSet64S (PcdInvalidPgd, (UINTN)0);
+ PcdSet64S (PcdInvalidPud, (UINTN)0);
+ PcdSet64S (PcdInvalidPmd, (UINTN)0);
+ PcdSet64S (PcdInvalidPte, (UINTN)0);
+
+ return ;
+}
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.h
new file mode 100644
index 0000000000..8e284a2ecd
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.h
@@ -0,0 +1,190 @@
+/** @file
+
+ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Tlb or TLB - Translation Lookaside Buffer
+ - CSR - Cpu State Register
+ - PGDL - Page Global Directory Low
+ - PGDH - Page Global Directory High
+ - TLBIDX - TLB Index
+ - TLBREHI - TLB Refill Entry High
+ - PWCTL - Page Walk Control
+ - STLB - Singular Page Size TLB
+ - PS - Page Size
+**/
+#ifndef MMU_H_
+#define MMU_H_
+/*page size 4k*/
+#define DEFAULT_PAGE_SIZE 0x0c
+#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_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_TLBREHI 0x8e /* TLB refill entryhi */
+#define LOONGARCH_CSR_PWCTL0 0x1c /* PWCtl0 */
+#define LOONGARCH_CSR_PWCTL1 0x1d /* PWCtl1 */
+#define LOONGARCH_CSR_STLBPGSIZE 0x1e
+#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 0
+#define CSR_TLBREHI_PS 0x3f
+
+#define EFI_MEMORY_CACHETYPE_MASK (EFI_MEMORY_UC | \
+ EFI_MEMORY_WC | \
+ EFI_MEMORY_WT | \
+ EFI_MEMORY_WB | \
+ EFI_MEMORY_UCE \
+ )
+
+typedef struct {
+ EFI_PHYSICAL_ADDRESS PhysicalBase;
+ EFI_VIRTUAL_ADDRESS VirtualBase;
+ UINTN Length;
+ UINTN Attributes;
+} MEMORY_REGION_DESCRIPTOR;
+
+// The total number of descriptors, including the final "end-of-table" descriptor.
+#define MAX_VIRTUAL_MEMORY_MAP_DESCRIPTORS (128)
+
+extern CHAR8 HandleTlbRefill[], HandleTlbRefillEnd[];
+
+/*
+ Invalid corresponding TLB entries are based on the address given
+
+ @param Address The address corresponding to the invalid page table entry
+
+ @retval none
+*/
+extern
+VOID
+LoongarchInvalidTlb (
+ UINTN Address
+ );
+
+/*
+ Set Tlb Refill function to hardware
+
+ @param A0 The address of tlb refill function
+
+ @retval none
+*/
+extern
+VOID
+SetTlbRefillFuncBase (
+ UINTN Address
+ );
+
+/*
+ Set Cpu Status Register Page Size.
+
+ @param PageSize Page Size.
+
+ @retval none
+*/
+extern
+VOID
+WriteCsrPageSize (
+ UINTN PageSize
+ );
+
+/*
+ Set Cpu Status Register TLBREFILL Page Size.
+
+ @param PageSize Page Size.
+
+ @retval none
+*/
+extern
+VOID
+WriteCsrTlbRefillPageSize (
+ UINTN PageSize
+ );
+
+/*
+ Set Cpu Status Register STLB Page Size.
+
+ @param PageSize Page Size.
+
+ @retval VOID
+*/
+extern
+VOID
+WriteCsrStlbPageSize (
+ UINTN PageSize
+);
+
+/*
+ Write Csr PWCTL0 register.
+
+ @param Val The value used to write to the PWCTL0 register
+
+ @retval none
+*/
+extern
+VOID
+LoongArchWriteqCsrPwctl0 (
+ UINTN Val
+ );
+
+/*
+ Write Csr PWCTL1 register.
+
+ @param Val The value used to write to the PWCTL1 register
+
+ @retval none
+*/
+extern
+VOID
+LoongArchWriteqCsrPwctl1 (
+ UINTN Val
+ );
+
+/*
+ Write Csr PGDL register.
+
+ @param Val The value used to write to the PGDL register
+
+ @retval none
+*/
+extern
+VOID
+LoongArchWriteqCsrPgdl (
+ UINTN Val
+ );
+
+/*
+ Write Csr PGDH register.
+
+ @param Val The value used to write to the PGDH register
+
+ @retval none
+*/
+extern
+VOID
+LoongArchWriteqCsrPgdh (
+ UINTN Val
+ );
+
+/*
+ Exchange specified bit data with the Csr CRMD register.
+
+ @param[IN] Val The value Exchanged with the CSR CRMD register.
+ @param[IN] Mask Specifies the mask for swapping bits
+
+ @retval VOID
+*/
+extern
+VOID
+LoongArchXchgCsrCrmd (
+ UINTN Val,
+ UINTN Mask
+ );
+
+#endif // MMU_H_
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h
new file mode 100644
index 0000000000..6ab07e7900
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h
@@ -0,0 +1,280 @@
+/** @file
+
+ Copyright (c) 2022 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_
+
+#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))
+
+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) (((((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 Is huge page table entry.
+ @retval 0 Isn't huge page table entry.
+ **/
+STATIC
+inline
+UINTN
+pgd_none (
+ IN PGD pgd
+ )
+{
+ return (PGD_VAL(pgd) == (UINTN)PcdGet64(PcdInvalidPud));
+}
+
+ /**
+ Check that the page upper directory table entry is empty.
+
+ @param pud Page upper directory struct variables.
+
+ @retval 1 Is huge page table entry.
+ @retval 0 Isn't huge page table entry.
+ **/
+STATIC
+inline
+UINTN
+pud_none (
+ IN PUD pud
+ )
+{
+ return (PUD_VAL(pud) == (UINTN)PcdGet64 (PcdInvalidPmd));
+}
+
+ /**
+ Check that the page middle directory table entry is empty.
+
+ @param pmd Page middle directory struct variables.
+
+ @retval 1 Is huge page table entry.
+ @retval 0 Isn't huge page table entry.
+ **/
+STATIC
+inline
+UINTN
+pmd_none (
+ IN PMD pmd
+ )
+{
+ return (PMD_VAL(pmd) == (UINTN)PcdGet64(PcdInvalidPte));
+}
+ /**
+ Check that the page table entry is empty.
+
+ @param pmd Page table entry struct variables.
+
+ @retval 1 Is huge page table entry.
+ @retval 0 Isn't huge page table entry.
+ **/
+STATIC
+inline
+UINTN
+pte_none (
+ IN PTE pte
+ )
+{
+ return (!(PTE_VAL(pte) & (~PAGE_GLOBAL)));
+}
+#endif // PAGE_H_
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h
new file mode 100644
index 0000000000..aacbf81744
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h
@@ -0,0 +1,57 @@
+/** @file
+
+ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Tlb or TLB - Translation Lookaside Buffer
+ - HGLOBAL - Huge Global
+ - PFN - Page Frame number
+ - EXEC - Execute
+ - PLV - Privilege Level
+ - RPLV - Restricted Privilege Level
+ - SUC - Strong-ordered UnCached
+ - CC - Coherent Cached
+ - WUC - Weak-ordered UnCached
+**/
+#ifndef PTE_H_
+#define PTE_H_
+/*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 */
+#endif // PTE_H_
--
2.31.1
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#96276): https://edk2.groups.io/g/devel/message/96276
Mute This Topic: https://groups.io/mt/94955173/1787277
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org]
-=-=-=-=-=-=-=-=-=-=-=-
Reviewed-by: Chao Li <lichao@loongson.cn> Thanks, Chao -------- On 11月 11 2022, at 5:12 下午, xianglai li <lixianglai@loongson.cn> wrote: > Read the memory map information through the QemuFwCfg interface, > > then build the page table through the memory map information, > and finally enable Mmu. > > > REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054 > > > Cc: Bibo Mao <maobibo@loongson.cn> > Cc: Chao Li <lichao@loongson.cn> > Cc: Leif Lindholm <quic_llindhol@quicinc.com> > Cc: Liming Gao <gaoliming@byosoft.com.cn> > Cc: Michael D Kinney <michael.d.kinney@intel.com> > Signed-off-by: xianglai li <lixianglai@loongson.cn> > --- > .../LoongArchQemuPkg/Include/Library/MmuLib.h | 85 ++ > .../LoongArchQemuPkg/Library/MmuLib/Mmu.S | 155 ++++ > .../Library/MmuLib/MmuBaseLib.inf | 40 + > .../Library/MmuLib/MmuBaseLibPei.inf | 47 + > .../Library/MmuLib/MmuLibCore.c | 831 ++++++++++++++++++ > .../Library/MmuLib/MmuLibCore.h | 40 + > .../Library/MmuLib/MmuLibCorePei.c | 231 +++++ > .../LoongArchQemuPkg/Library/MmuLib/mmu.h | 190 ++++ > .../LoongArchQemuPkg/Library/MmuLib/page.h | 280 ++++++ > .../LoongArchQemuPkg/Library/MmuLib/pte.h | 57 ++ > 10 files changed, 1956 insertions(+) > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.h > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.h > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h > > > diff --git a/Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h b/Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h > new file mode 100644 > index 0000000000..9880fc385c > --- /dev/null > +++ b/Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h > @@ -0,0 +1,85 @@ > +/** @file > + > + Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR> > + > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > + @par Glossary: > + - EXC - execute > +**/ > +#ifndef MMU_LIB_H_ > +#define MMU_LIB_H_ > +/** > + write operation is performed Count times from the first element of Buffer. > + Convert EFI Attributes to Loongarch Attributes. > + @param[in] EfiAttributes Efi Attributes. > + > + @retval LoongArch Attributes. > +**/ > +UINTN > +EfiAttributeToLoongArchAttribute ( > + IN UINTN EfiAttributes > + ); > + > +/** > + Finds 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] EndAddress To find the end address of the memory region. > + @param[out] RegionLength 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 > +GetLoongArchMemoryRegion ( > + IN UINTN BaseAddress, > + IN UINTN EndAddress, > + 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. > + > + @retval EFI_SUCCESS The Attributes was set successfully > +**/ > +EFI_STATUS > +LoongArchSetMemoryAttributes ( > + IN EFI_PHYSICAL_ADDRESS BaseAddress, > + IN UINTN Length, > + IN UINTN Attributes > + ); > + > +/** > + Sets the non-executable Attributes for 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. > + > + @retval EFI_SUCCESS The Attributes was set successfully > +**/ > +EFI_STATUS > +LoongArchSetMemoryRegionNoExec ( > + IN EFI_PHYSICAL_ADDRESS BaseAddress, > + IN UINTN Length > + ); > + > +/** > + Create a page table and initialize the MMU. > + > + @param[] VOID > + > + @retval VOID > +**/ > +VOID > +EFIAPI > +ConfigureMmu ( > + VOID > + ); > +#endif // MMU_LIB_H_ > diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S > new file mode 100644 > index 0000000000..d5863de072 > --- /dev/null > +++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S > @@ -0,0 +1,155 @@ > +#------------------------------------------------------------------------------ > +# > +# LoongArch for LoongArch > +# > +# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR> > +# > +# SPDX-License-Identifier: BSD-2-Clause-Patent > +# > +#----------------------------------------------------------------------------- > + > +#ifndef __ASSEMBLY__ > +#define __ASSEMBLY__ > +#endif > + > +#include "Library/Cpu.h" > +#include "mmu.h" > + > +ASM_GLOBAL ASM_PFX(HandleTlbRefill) > +ASM_GLOBAL HandleTlbRefillEnd > +ASM_GLOBAL ASM_PFX(LoongarchInvalidTlb) > +ASM_GLOBAL ASM_PFX(SetTlbRefillFuncBase) > +ASM_GLOBAL ASM_PFX(WriteCsrPageSize) > +ASM_GLOBAL ASM_PFX(WriteCsrTlbRefillPageSize) > +ASM_GLOBAL ASM_PFX(WriteCsrStlbPageSize) > +ASM_GLOBAL ASM_PFX(LoongArchWriteqCsrPwctl0) > +ASM_GLOBAL ASM_PFX(LoongArchWriteqCsrPwctl1) > +ASM_GLOBAL ASM_PFX(LoongArchWriteqCsrPgdl) > +ASM_GLOBAL ASM_PFX(LoongArchWriteqCsrPgdh) > +ASM_GLOBAL ASM_PFX(LoongArchXchgCsrCrmd) > + > +# > +# Refill the page table. > +# @param VOID > +# @retval VOID > +# > + > +ASM_PFX(HandleTlbRefill): > + 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 > + csrrd T0, LOONGARCH_CSR_TLBRSAVE > + ertn > +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(LoongarchInvalidTlb): > + invtlb INVTLB_ADDR_GTRUE_OR_ASID, ZERO, A0 > + jirl ZERO, RA, 0 > + > +# > +# Set Tlb Refill function to hardware > +# @param A0 The address of tlb refill function > +# @retval none > +# > + > +ASM_PFX(SetTlbRefillFuncBase): > + csrwr A0, LOONGARCH_CSR_TLBREBASE > + jirl ZERO, RA,0 > + > +# > +# Set Cpu Status Register Page Size. > +# @param A0 Page Size. > +# @retval none > +# > + > +ASM_PFX(WriteCsrPageSize): > + li.d T0, CSR_TLBIDX_SIZE > + sll.d A0, A0, T0 > + li.d T0, CSR_TLBIDX_SIZE_MASK > + csrxchg A0, T0, LOONGARCH_CSR_TLBIDX > + jirl ZERO, RA,0 > + > +# > +# Set Cpu Status Register TLBREFILL Page Size. > +# @param A0 Page Size. > +# @retval none > +# > + > +ASM_PFX(WriteCsrTlbRefillPageSize): > + li.d T0, CSR_TLBREHI_PS_SHIFT > + sll.d A0, A0, T0 > + li.d T0, CSR_TLBREHI_PS > + csrxchg A0, T0, LOONGARCH_CSR_TLBREHI > + jirl ZERO, RA,0 > + > +# > +# Set Cpu Status Register STLB Page Size. > +# @param val Page Size. > +# @retval VOID > +# > + > +ASM_PFX(WriteCsrStlbPageSize): > + csrwr A0, LOONGARCH_CSR_STLBPGSIZE > + jirl ZERO, RA,0 > + > +# > +# Write Csr PWCTL0 register. > +# @param A0 The value used to write to the PWCTL0 register > +# @retval none > +# > + > +ASM_PFX(LoongArchWriteqCsrPwctl0): > + csrwr A0, LOONGARCH_CSR_PWCTL0 > + jirl ZERO, RA,0 > + > +# > +# Write Csr PWCTL1 register. > +# @param A0 The value used to write to the PWCTL1 register > +# @retval none > +# > + > +ASM_PFX(LoongArchWriteqCsrPwctl1): > + csrwr A0, LOONGARCH_CSR_PWCTL1 > + jirl ZERO, RA,0 > + > +# > +# Write Csr PGDL register. > +# @param A0 The value used to write to the PGDL register > +# @retval none > +# > + > +ASM_PFX(LoongArchWriteqCsrPgdl): > + csrwr A0, LOONGARCH_CSR_PGDL > + jirl ZERO, RA,0 > + > +# > +# Write Csr PGDH register. > +# @param A0 The value used to write to the PGDH register > +# @retval none > +# > + > +ASM_PFX(LoongArchWriteqCsrPgdh): > + csrwr A0, LOONGARCH_CSR_PGDH > + jirl ZERO, RA,0 > + > +# > +# Exchange specified bit data with the Csr CRMD register. > +# @param[IN] A0 The value Exchanged with the CSR CRMD register. > +# @param[IN] A1 Specifies the mask for swapping bits > +# @retval VOID > +# > + > +ASM_PFX(LoongArchXchgCsrCrmd): > + csrxchg A0, A1, LOONGARCH_CSR_CRMD > + jirl ZERO, RA,0 > diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf > new file mode 100644 > index 0000000000..abd864a324 > --- /dev/null > +++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf > @@ -0,0 +1,40 @@ > +## @file > +# > +# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR> > +# > +# SPDX-License-Identifier: BSD-2-Clause-Patent > +# > +## > + > +[Defines] > + INF_VERSION = 0x00010005 > + BASE_NAME = MmuBaseLib > + FILE_GUID = da8f0232-fb14-42f0-922c-63104d2c70be > + MODULE_TYPE = BASE > + VERSION_STRING = 1.0 > + LIBRARY_CLASS = MmuLib > + CONSTRUCTOR = MmuInitialize > + > +# > +# VALID_ARCHITECTURES = LOONGARCH64 > +# > + > +[Sources] > + MmuLibCore.c > + Mmu.S > + > +[Packages] > + MdePkg/MdePkg.dec > + Platform/Loongson/LoongArchQemuPkg/Loongson.dec > + > +[PCD] > + gLoongArchQemuPkgTokenSpaceGuid.PcdSwapPageDir > + gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPgd > + gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPud > + gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPmd > + gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPte > + > +[LibraryClasses] > + MemoryAllocationLib > + PcdLib > + DebugLib > diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf > new file mode 100644 > index 0000000000..12848eecfe > --- /dev/null > +++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf > @@ -0,0 +1,47 @@ > +## @file > +# > +# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR> > +# > +# SPDX-License-Identifier: BSD-2-Clause-Patent > +# > +## > + > +[Defines] > + INF_VERSION = 0x00010005 > + BASE_NAME = MmuPeiLib > + FILE_GUID = da8f0232-fb14-42f0-922c-63104d2c70bd > + MODULE_TYPE = BASE > + VERSION_STRING = 1.0 > + LIBRARY_CLASS = MmuLib | SEC PEIM > + > +# > +# VALID_ARCHITECTURES = LOONGARCH64 > +# > + > +[Sources] > + MmuLibCorePei.c > + Mmu.S > + MmuLibCore.h > + MmuLibCore.c > + > +[Packages] > + MdePkg/MdePkg.dec > + Platform/Loongson/LoongArchQemuPkg/Loongson.dec > + OvmfPkg/OvmfPkg.dec > + > +[PCD] > + gLoongArchQemuPkgTokenSpaceGuid.PcdSwapPageDir > + gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPgd > + gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPud > + gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPmd > + gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPte > + gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvSize > + gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvBase > + gLoongArchQemuPkgTokenSpaceGuid.PcdRamSize > + > +[LibraryClasses] > + MemoryAllocationLib > + CacheMaintenanceLib > + PcdLib > + DebugLib > + QemuFwCfgLib > diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c > new file mode 100644 > index 0000000000..b932e3d568 > --- /dev/null > +++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c > @@ -0,0 +1,831 @@ > +/** @file > + > + Copyright (c) 2022 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/BaseMemoryLib.h> > +#include <Library/MemoryAllocationLib.h> > +#include <Library/BaseLib.h> > +#include <Library/DebugLib.h> > +#include "Library/Cpu.h" > +#include "pte.h" > +#include "page.h" > +#include "mmu.h" > + > +BOOLEAN mMmuInited = FALSE; > +/** > + Check to see if mmu successfully initializes. > + > + @param VOID. > + > + @retval TRUE Initialization has been completed. > + FALSE Initialization did not complete. > +**/ > +BOOLEAN > +MmuIsInit (VOID) { > + if ((mMmuInited == TRUE) || > + (PcdGet64 (PcdSwapPageDir) != 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. > + > + @retval VOID. > +**/ > +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. > +**/ > +PGD * > +PgdOffset ( > + IN UINTN Address > + ) > +{ > + return ((PGD *)PcdGet64 (PcdSwapPageDir)) + 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. > +**/ > +PUD * > +PudOffset ( > + IN PGD *Pgd, > + IN UINTN Address > + ) > +{ > + UINTN 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. > +**/ > +PMD * > +PmdOffset ( > + IN PUD *Pud, > + IN UINTN Address > + ) > +{ > + UINTN 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. > +**/ > +PTE * > +PteOffset ( > + IN PMD *Pmd, > + IN UINTN Address > + ) > +{ > + UINTN 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. > + > + @retval VOID > +**/ > +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. > + > + @retval VOID > +**/ > +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. > + > + @retval VOID > +**/ > +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. > + > + @retval VOID > +**/ > +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. > + > + @retval VOID > +**/ > +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. > + > + @retval VOID > +**/ > +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. > + > + @retval VOID > +**/ > +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. > +**/ > +INTN > +PudAlloc ( > + IN PGD *Pgd > + ) > +{ > + PUD *Pud = (PUD *) AllocatePages (1); > + if (!Pud) { > + return EFI_OUT_OF_RESOURCES; > + } > + > + PageDirInit ((VOID *)Pud, ENTRYS_PER_PUD, (VOID *)PcdGet64 (PcdInvalidPmd)); > + > + if (pgd_none (*Pgd)) { > + SetPgd (Pgd, Pud); > + } else { /* Another has populated it */ > + PudFree (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. > +**/ > +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 *)PcdGet64 (PcdInvalidPte)); > + > + if (pud_none (*Pud)) { > + SetPud (Pud, Pmd); > + } else {/* Another has populated it */ > + PmdFree (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. > +**/ > +INTN > +PteAlloc ( > + IN PMD *Pmd > + ) > +{ > + PTE *Pte; > + > + Pte = (PTE *) AllocatePages (1); > + if (!Pte) { > + return EFI_OUT_OF_RESOURCES; > + } > + > + Pte = ZeroMem (Pte, EFI_PAGE_SIZE); > + > + if (pmd_none (*Pmd)) { > + SetPmd (Pmd, Pte); > + } else { /* Another has populated it */ > + PteFree (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. > + > + @retval Gets the page upper directory entry > +**/ > +PUD * > +PudAllocGet ( > + IN PGD *Pgd, > + IN UINTN Address > + ) > +{ > + return ((pgd_none (*(Pgd)) && PudAlloc (Pgd)) ? > + NULL : 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. > + > + @retval Gets the page middle directory entry > +**/ > +PMD * > +PmdAllocGet ( > + IN PUD *Pud, > + IN UINTN Address > + ) > +{ > + PMD * ret = (pud_none (*Pud) && PmdAlloc (Pud))? > + NULL: PmdOffset (Pud, Address); > + DEBUG ((DEBUG_VERBOSE, "%a %d PudVal %p PmdOffset %p PMD_INDEX %p .\n", __func__, __LINE__, > + Pud->PudVal, PmdOffset (Pud, Address), PMD_INDEX (Address) )); > + > + return ret; > +} > + > +/** > + 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. > + > + @retval Gets the page table entry > +**/ > +PTE * > +PteAllocGet ( > + IN PMD *Pmd, > + IN UINTN Address > + ) > +{ > + return (pmd_none (*Pmd) && PteAlloc (Pmd))? > + NULL: 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 > +**/ > +PTE * > +GetPteAddress ( > + IN UINTN Address > + ) > +{ > + PGD *Pgd; > + PUD *Pud; > + PMD *Pmd; > + > + Pgd = PgdOffset (Address); > + > + if (pgd_none (*Pgd)) { > + return NULL; > + } > + > + Pud = PudOffset (Pgd, Address); > + > + if (pud_none (*Pud)) { > + return NULL; > + } > + > + Pmd = PmdOffset (Pud, Address); > + if (pmd_none (*Pmd)) { > + return NULL; > + } > + > + if (IS_HUGE_PAGE (Pmd->PmdVal)) { > + return ((PTE *)Pmd); > + } > + > + return PteOffset (Pmd, Address); > +} > + > +/** > + 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. > +**/ > +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; > + } > + > + do { > + UpDate = FALSE; > + PteVal = MAKE_PTE (Address, Attributes); > + DEBUG ((DEBUG_VERBOSE, > + "%a %d Address %p PGD_INDEX %p PUD_INDEX %p PMD_INDEX %p PTE_INDEX %p MAKE_PTE %p\n", > + __func__, __LINE__, Address, PGD_INDEX (Address), PUD_INDEX (Address), PMD_INDEX (Address), > + PTE_INDEX (Address), PteVal)); > + > + if ((!pte_none (*Pte)) && > + (PTE_VAL(*Pte) != PTE_VAL(PteVal))) > + { > + UpDate = TRUE; > + } > + > + SetPte (Pte, PteVal); > + if (UpDate) { > + LoongarchInvalidTlb(Address); > + } > + } while (Pte++, Address += EFI_PAGE_SIZE, Address != End); > + > + return EFI_SUCCESS; > +} > + > +/** > + 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. > +**/ > +EFI_STATUS > +MemoryMapPmdRange ( > + IN PUD *Pud, > + IN UINTN Address, > + IN UINTN End, > + IN UINTN Attributes > + ) > +{ > + PMD *Pmd; > + PTE *Pte; > + UINTN Next; > + UINTN AddressStart_HugePage; > + UINTN AddressEnd_HugePage; > + > + Pmd = PmdAllocGet (Pud, Address); > + if (!Pmd) { > + return EFI_OUT_OF_RESOURCES; > + } > + > + do { > + Next = PMD_ADDRESS_END (Address, End); > + if (((Address & (~PMD_MASK)) == 0) && > + ((Next & (~PMD_MASK)) == 0) && > + (pmd_none (*Pmd))) > + { > + DEBUG ((DEBUG_VERBOSE, > + "%a %d Address %p PGD_INDEX %p PUD_INDEX %p PMD_INDEX %p MAKE_HUGE_PTE %p\n", > + __func__, __LINE__, Address, PGD_INDEX (Address), PUD_INDEX (Address), PMD_INDEX (Address), > + MAKE_HUGE_PTE (Address, Attributes))); > + > + SetPmd (Pmd, (PTE *)MAKE_HUGE_PTE (Address, Attributes)); > + } else { > + if ((pmd_none (*Pmd)) || > + ((!pmd_none (*Pmd)) && > + (!IS_HUGE_PAGE (Pmd->PmdVal)))) > + { > + if (MemoryMapPteRange (Pmd, Address, Next, Attributes)) { > + return EFI_OUT_OF_RESOURCES; > + } > + } else { > + SetPmd (Pmd, (PTE *)PcdGet64 (PcdInvalidPte)); > + AddressStart_HugePage = Address & PMD_MASK; > + AddressEnd_HugePage = AddressStart_HugePage + HUGE_PAGE_SIZE; > + if (MemoryMapPteRange (Pmd, AddressStart_HugePage, AddressEnd_HugePage, Attributes)) { > + return EFI_OUT_OF_RESOURCES; > + } > + Pte = GetPteAddress (AddressStart_HugePage); > + if (Pte == NULL) { > + continue ; > + } > + if (AddressEnd_HugePage > End) { > + Next = End; > + } > + } > + } > + } while (Pmd++, Address = Next, Address != End); > + > + return 0; > +} > + > +/** > + 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. > +**/ > +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) { > + return EFI_OUT_OF_RESOURCES; > + } > + > + do { > + Next = PUD_ADDRESS_END (Address, End); > + if (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. > +**/ > +EFI_STATUS > +MemoryMapPageRange ( > + IN UINTN Start, > + IN UINTN End, > + IN UINTN Attributes > + ) > +{ > + PGD *Pgd; > + UINTN Next; > + UINTN Address = Start; > + EFI_STATUS Err; > + > + Pgd = PgdOffset (Address); > + do { > + Next = PGD_ADDRESS_END (Address, End); > + 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); > +} > + > +/** > + write operation is performed Count times from the first element of Buffer. > + Convert EFI Attributes to Loongarch Attributes. > + @param[in] EfiAttributes Efi Attributes. > + > + @retval LoongArch Attributes. > +**/ > +UINTN > +EfiAttributeToLoongArchAttribute ( > + IN UINTN EfiAttributes > + ) > +{ > + UINTN LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | CACHE_CC | PAGE_USER | PAGE_GLOBAL; > + switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) { > + case EFI_MEMORY_UC: > + LoongArchAttributes |= CACHE_SUC; > + break; > + case EFI_MEMORY_WC: > + 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) { > + LoongArchAttributes &= ~PAGE_DIRTY; > + } > + > + //eXecute protection attribute > + if ((EfiAttributes & EFI_MEMORY_XP) != 0) { > + LoongArchAttributes |= PAGE_NO_EXEC; > + } > + > + return LoongArchAttributes; > +} > + > +/** > + Finds 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] EndAddress To find the end address of the memory region. > + @param[out] RegionLength 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 > +GetLoongArchMemoryRegion ( > + IN UINTN BaseAddress, > + IN UINTN EndAddress, > + OUT UINTN *RegionLength, > + OUT UINTN *RegionAttributes > + ) > +{ > + PTE *Pte; > + UINTN Attributes; > + UINTN AttributesTmp; > + UINTN MaxAddress; > + MaxAddress = LShiftU64 (1ULL, MAX_VA_BITS) - 1; > + Pte = GetPteAddress (BaseAddress); > + > + if (!MmuIsInit ()) { > + return EFI_SUCCESS; > + } > + if (Pte == NULL) { > + return EFI_NOT_FOUND; > + } > + Attributes = GET_PAGE_ATTRIBUTES (*Pte); > + if (IS_HUGE_PAGE (Pte->PteVal)) { > + *RegionAttributes = Attributes & (~(PAGE_HUGE)); > + *RegionLength += HUGE_PAGE_SIZE; > + } else { > + *RegionLength += EFI_PAGE_SIZE; > + *RegionAttributes = Attributes; > + } > + > + while (BaseAddress <= MaxAddress) { > + Pte = GetPteAddress (BaseAddress); > + if (Pte == NULL) { > + return EFI_SUCCESS; > + } > + AttributesTmp = GET_PAGE_ATTRIBUTES (*Pte); > + if (IS_HUGE_PAGE (Pte->PteVal)) { > + if (AttributesTmp == Attributes) { > + *RegionLength += HUGE_PAGE_SIZE; > + } > + BaseAddress += HUGE_PAGE_SIZE; > + } else { > + if (AttributesTmp == Attributes) { > + *RegionLength += EFI_PAGE_SIZE; > + } > + BaseAddress += EFI_PAGE_SIZE; > + } > + > + if (BaseAddress > EndAddress) { > + break; > + } > + } > + 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. > + > + @retval EFI_SUCCESS The Attributes was set successfully > +**/ > +EFI_STATUS > +LoongArchSetMemoryAttributes ( > + IN EFI_PHYSICAL_ADDRESS BaseAddress, > + IN UINTN Length, > + IN UINTN Attributes > + ) > +{ > + > + if (!MmuIsInit ()) { > + return EFI_SUCCESS; > + } > + Attributes = EfiAttributeToLoongArchAttribute (Attributes); > + DEBUG ((DEBUG_VERBOSE, "%a %d %p %p %p.\n", __func__, __LINE__, BaseAddress , Length, Attributes)); > + MemoryMapPageRange (BaseAddress, BaseAddress + Length, Attributes); > + > + return EFI_SUCCESS; > +} > + > +/** > + Sets the non-executable Attributes for 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. > + > + @retval EFI_SUCCESS The Attributes was set successfully > +**/ > +EFI_STATUS > +LoongArchSetMemoryRegionNoExec ( > + IN EFI_PHYSICAL_ADDRESS BaseAddress, > + IN UINTN Length > + ) > +{ > + if (MmuIsInit ()) { > + Length = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Length)); > + LoongArchSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_XP); > + } > + return EFI_SUCCESS; > +} > + > +/** > + Check to see if mmu successfully initializes and saves the result. > + > + @param VOID. > + > + @retval EFI_SUCCESS Initialization succeeded. > +**/ > +EFI_STATUS > +MmuInitialize (VOID) > +{ > + if (PcdGet64 (PcdSwapPageDir) != 0) { > + mMmuInited = TRUE; > + } > + > + return EFI_SUCCESS; > +} > diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.h > new file mode 100644 > index 0000000000..7a5e8ea0dd > --- /dev/null > +++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.h > @@ -0,0 +1,40 @@ > +/** @file > + > + Copyright (c) 2022 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/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c > new file mode 100644 > index 0000000000..32a7fc0beb > --- /dev/null > +++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c > @@ -0,0 +1,231 @@ > +/** @file > + Platform PEI driver > + > + Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR> > + > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > + @par Glossary: > + - FwCfg - Firmeware Config > + - Tlb - Translation Lookaside Buffer > +**/ > +#include <Uefi.h> > +#include <Library/BaseMemoryLib.h> > +#include <Library/MemoryAllocationLib.h> > +#include <Library/BaseLib.h> > +#include <Library/DebugLib.h> > +#include "Library/Cpu.h" > +#include "pte.h" > +#include "page.h" > +#include "mmu.h" > +#include <Library/QemuFwCfgLib.h> > +#include "MmuLibCore.h" > +#include <Library/CacheMaintenanceLib.h> > + > +/** > + Return the Virtual Memory Map of your platform > + > + This Virtual Memory Map is used by MemoryInitPei Module to initialize the MMU > + on your platform. > + > + @param[out] VirtualMemoryMap 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 > +GetMemoryMapFromFwCfg ( > + OUT MEMORY_REGION_DESCRIPTOR **VirtualMemoryMap > + ) > +{ > + > + EFI_STATUS Status; > + FIRMWARE_CONFIG_ITEM FwCfgItem; > + UINTN FwCfgSize; > + LOONGARCH_MEMMAP_ENTRY MemoryMapEntry; > + LOONGARCH_MEMMAP_ENTRY *StartEntry; > + LOONGARCH_MEMMAP_ENTRY *pEntry; > + UINTN Processed; > + MEMORY_REGION_DESCRIPTOR *VirtualMemoryTable; > + UINTN Index = 0; > + ASSERT (VirtualMemoryMap != NULL); > + > + VirtualMemoryTable = AllocatePool ( > + sizeof (MEMORY_REGION_DESCRIPTOR) * > + MAX_VIRTUAL_MEMORY_MAP_DESCRIPTORS > + ); > + 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)); > + *VirtualMemoryMap = 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)); > + *VirtualMemoryMap = VirtualMemoryTable; > + return ; > +} > + > +/** > + Create a page table and initialize the MMU. > + > + @param[] VOID > + > + @retval VOID > +**/ > +EFIAPI > +VOID > +ConfigureMmu (VOID) > +{ > + PGD *SwapperPageDir = NULL; > + PGD *InvalidPgd = NULL; > + PUD *InvalidPudTable = NULL; > + PMD *InvalidPmdTable = NULL; > + PTE *InvalidPteTable = NULL; > + MEMORY_REGION_DESCRIPTOR *MemoryTable = NULL; > + RETURN_STATUS PcdStatus; > + UINTN PgdShift = PGD_SHIFT; > + UINTN PgdWide = PGD_WIDE; > + UINTN PudShift = PUD_SHIFT; > + UINTN PudWide = PUD_WIDE; > + UINTN PmdShift = PMD_SHIFT; > + UINTN PmdWide = PMD_WIDE; > + UINTN PteShift = PTE_SHIFT; > + UINTN PteWide = PTE_WIDE; > + UINTN PageEnable = 1 << 4; > + VOID *TlbReEntry; > + > + SwapperPageDir = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE)); > + InvalidPgd = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE)); > + InvalidPudTable = AllocatePages (EFI_SIZE_TO_PAGES (PUD_TABLE_SIZE)); > + InvalidPmdTable = AllocatePages (EFI_SIZE_TO_PAGES (PMD_TABLE_SIZE)); > + InvalidPteTable = AllocatePages (EFI_SIZE_TO_PAGES (PTE_TABLE_SIZE)); > + ZeroMem (InvalidPteTable, PTE_TABLE_SIZE); > + > + if ((!InvalidPgd) || > + (!InvalidPudTable) || > + (!InvalidPmdTable) || > + (!InvalidPteTable)) > + { > + goto FreeTranslationTable; > + } > + > + /*pgd init*/ > + PageDirInit (SwapperPageDir , ENTRYS_PER_PGD, InvalidPudTable); > + /*pgd init*/ > + PageDirInit (InvalidPgd, ENTRYS_PER_PGD, InvalidPudTable); > + /*pud init*/ > + PageDirInit (InvalidPudTable, ENTRYS_PER_PUD, InvalidPmdTable); > + /*pmd init*/ > + PageDirInit (InvalidPmdTable, ENTRYS_PER_PMD, InvalidPteTable); > + GetMemoryMapFromFwCfg (&MemoryTable); > + > + PcdStatus |= PcdSet64S (PcdSwapPageDir, (UINTN)SwapperPageDir); > + PcdStatus |= PcdSet64S (PcdInvalidPgd, (UINTN)InvalidPgd); > + PcdStatus |= PcdSet64S (PcdInvalidPud, (UINTN)InvalidPudTable); > + PcdStatus |= PcdSet64S (PcdInvalidPmd, (UINTN)InvalidPmdTable); > + PcdStatus |= PcdSet64S (PcdInvalidPte, (UINTN)InvalidPteTable); > + ASSERT_RETURN_ERROR (PcdStatus); > + > + while (MemoryTable->Length != 0) { > + DEBUG ((DEBUG_VERBOSE, "%a %d VirtualBase %p VirtualEnd %p Attributes %p .\n", __func__, __LINE__, > + MemoryTable->VirtualBase, > + (MemoryTable->Length + MemoryTable->VirtualBase), > + MemoryTable->Attributes)); > + > + PcdStatus = FillTranslationTable (MemoryTable); > + if (EFI_ERROR (PcdStatus)) { > + goto FreeTranslationTable; > + } > + MemoryTable++; > + } > + > + TlbReEntry = AllocatePages (1); > + if (TlbReEntry == NULL) { > + goto FreeTranslationTable; > + } > + CopyMem ((char *)TlbReEntry, HandleTlbRefill, (HandleTlbRefillEnd - HandleTlbRefill)); > + InvalidateInstructionCacheRange ((VOID *)(UINTN)HandleTlbRefill, (UINTN)(HandleTlbRefillEnd - HandleTlbRefill)); > + > + DEBUG ((DEBUG_VERBOSE, > + "%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)); > + > + SetTlbRefillFuncBase ((UINTN)TlbReEntry); > + /*set page size*/ > + WriteCsrPageSize (DEFAULT_PAGE_SIZE); > + WriteCsrStlbPageSize (DEFAULT_PAGE_SIZE); > + WriteCsrTlbRefillPageSize (DEFAULT_PAGE_SIZE); > + > + LoongArchWriteqCsrPwctl0 ((PteShift | PteWide << 5 | PmdShift << 10 | PmdWide << 15 | PudShift << 20 | PudWide << 25 > + )); > + LoongArchWriteqCsrPwctl1 (PgdShift | PgdWide << 6); > + LoongArchWriteqCsrPgdl ((UINTN)SwapperPageDir); > + LoongArchWriteqCsrPgdh ((UINTN)InvalidPgd); > + > + DEBUG ((DEBUG_INFO, "%a %d Enable Mmu Start PageBassAddress %p.\n", __func__, __LINE__, SwapperPageDir)); > + LoongArchXchgCsrCrmd ( PageEnable, 1 << 4); > + DEBUG ((DEBUG_INFO, "%a %d Enable Mmu End.\n", __func__, __LINE__)); > + > + return ; > + > +FreeTranslationTable: > + if (SwapperPageDir) { > + FreePages (SwapperPageDir, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE)); > + } > + > + if (InvalidPgd) { > + FreePages (InvalidPgd, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE)); > + } > + > + if (InvalidPudTable) { > + FreePages (InvalidPudTable, EFI_SIZE_TO_PAGES (PUD_TABLE_SIZE)); > + } > + > + if (InvalidPmdTable) { > + FreePages (InvalidPmdTable, EFI_SIZE_TO_PAGES (PMD_TABLE_SIZE)); > + } > + > + if (InvalidPteTable) { > + FreePages (InvalidPteTable, EFI_SIZE_TO_PAGES (PTE_TABLE_SIZE)); > + } > + > + PcdSet64S (PcdSwapPageDir, (UINTN)0); > + PcdSet64S (PcdInvalidPgd, (UINTN)0); > + PcdSet64S (PcdInvalidPud, (UINTN)0); > + PcdSet64S (PcdInvalidPmd, (UINTN)0); > + PcdSet64S (PcdInvalidPte, (UINTN)0); > + > + return ; > +} > diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.h > new file mode 100644 > index 0000000000..8e284a2ecd > --- /dev/null > +++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.h > @@ -0,0 +1,190 @@ > +/** @file > + > + Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR> > + > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > + @par Glossary: > + - Tlb or TLB - Translation Lookaside Buffer > + - CSR - Cpu State Register > + - PGDL - Page Global Directory Low > + - PGDH - Page Global Directory High > + - TLBIDX - TLB Index > + - TLBREHI - TLB Refill Entry High > + - PWCTL - Page Walk Control > + - STLB - Singular Page Size TLB > + - PS - Page Size > +**/ > +#ifndef MMU_H_ > +#define MMU_H_ > +/*page size 4k*/ > +#define DEFAULT_PAGE_SIZE 0x0c > +#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_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_TLBREHI 0x8e /* TLB refill entryhi */ > +#define LOONGARCH_CSR_PWCTL0 0x1c /* PWCtl0 */ > +#define LOONGARCH_CSR_PWCTL1 0x1d /* PWCtl1 */ > +#define LOONGARCH_CSR_STLBPGSIZE 0x1e > +#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 0 > +#define CSR_TLBREHI_PS 0x3f > + > +#define EFI_MEMORY_CACHETYPE_MASK (EFI_MEMORY_UC | \ > + EFI_MEMORY_WC | \ > + EFI_MEMORY_WT | \ > + EFI_MEMORY_WB | \ > + EFI_MEMORY_UCE \ > + ) > + > +typedef struct { > + EFI_PHYSICAL_ADDRESS PhysicalBase; > + EFI_VIRTUAL_ADDRESS VirtualBase; > + UINTN Length; > + UINTN Attributes; > +} MEMORY_REGION_DESCRIPTOR; > + > +// The total number of descriptors, including the final "end-of-table" descriptor. > +#define MAX_VIRTUAL_MEMORY_MAP_DESCRIPTORS (128) > + > +extern CHAR8 HandleTlbRefill[], HandleTlbRefillEnd[]; > + > +/* > + Invalid corresponding TLB entries are based on the address given > + > + @param Address The address corresponding to the invalid page table entry > + > + @retval none > +*/ > +extern > +VOID > +LoongarchInvalidTlb ( > + UINTN Address > + ); > + > +/* > + Set Tlb Refill function to hardware > + > + @param A0 The address of tlb refill function > + > + @retval none > +*/ > +extern > +VOID > +SetTlbRefillFuncBase ( > + UINTN Address > + ); > + > +/* > + Set Cpu Status Register Page Size. > + > + @param PageSize Page Size. > + > + @retval none > +*/ > +extern > +VOID > +WriteCsrPageSize ( > + UINTN PageSize > + ); > + > +/* > + Set Cpu Status Register TLBREFILL Page Size. > + > + @param PageSize Page Size. > + > + @retval none > +*/ > +extern > +VOID > +WriteCsrTlbRefillPageSize ( > + UINTN PageSize > + ); > + > +/* > + Set Cpu Status Register STLB Page Size. > + > + @param PageSize Page Size. > + > + @retval VOID > +*/ > +extern > +VOID > +WriteCsrStlbPageSize ( > + UINTN PageSize > +); > + > +/* > + Write Csr PWCTL0 register. > + > + @param Val The value used to write to the PWCTL0 register > + > + @retval none > +*/ > +extern > +VOID > +LoongArchWriteqCsrPwctl0 ( > + UINTN Val > + ); > + > +/* > + Write Csr PWCTL1 register. > + > + @param Val The value used to write to the PWCTL1 register > + > + @retval none > +*/ > +extern > +VOID > +LoongArchWriteqCsrPwctl1 ( > + UINTN Val > + ); > + > +/* > + Write Csr PGDL register. > + > + @param Val The value used to write to the PGDL register > + > + @retval none > +*/ > +extern > +VOID > +LoongArchWriteqCsrPgdl ( > + UINTN Val > + ); > + > +/* > + Write Csr PGDH register. > + > + @param Val The value used to write to the PGDH register > + > + @retval none > +*/ > +extern > +VOID > +LoongArchWriteqCsrPgdh ( > + UINTN Val > + ); > + > +/* > + Exchange specified bit data with the Csr CRMD register. > + > + @param[IN] Val The value Exchanged with the CSR CRMD register. > + @param[IN] Mask Specifies the mask for swapping bits > + > + @retval VOID > +*/ > +extern > +VOID > +LoongArchXchgCsrCrmd ( > + UINTN Val, > + UINTN Mask > + ); > + > +#endif // MMU_H_ > diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h > new file mode 100644 > index 0000000000..6ab07e7900 > --- /dev/null > +++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h > @@ -0,0 +1,280 @@ > +/** @file > + > + Copyright (c) 2022 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_ > + > +#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)) > + > +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) (((((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 Is huge page table entry. > + @retval 0 Isn't huge page table entry. > + **/ > +STATIC > +inline > +UINTN > +pgd_none ( > + IN PGD pgd > + ) > +{ > + return (PGD_VAL(pgd) == (UINTN)PcdGet64(PcdInvalidPud)); > +} > + > + /** > + Check that the page upper directory table entry is empty. > + > + @param pud Page upper directory struct variables. > + > + @retval 1 Is huge page table entry. > + @retval 0 Isn't huge page table entry. > + **/ > +STATIC > +inline > +UINTN > +pud_none ( > + IN PUD pud > + ) > +{ > + return (PUD_VAL(pud) == (UINTN)PcdGet64 (PcdInvalidPmd)); > +} > + > + /** > + Check that the page middle directory table entry is empty. > + > + @param pmd Page middle directory struct variables. > + > + @retval 1 Is huge page table entry. > + @retval 0 Isn't huge page table entry. > + **/ > +STATIC > +inline > +UINTN > +pmd_none ( > + IN PMD pmd > + ) > +{ > + return (PMD_VAL(pmd) == (UINTN)PcdGet64(PcdInvalidPte)); > +} > + /** > + Check that the page table entry is empty. > + > + @param pmd Page table entry struct variables. > + > + @retval 1 Is huge page table entry. > + @retval 0 Isn't huge page table entry. > + **/ > +STATIC > +inline > +UINTN > +pte_none ( > + IN PTE pte > + ) > +{ > + return (!(PTE_VAL(pte) & (~PAGE_GLOBAL))); > +} > +#endif // PAGE_H_ > diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h > new file mode 100644 > index 0000000000..aacbf81744 > --- /dev/null > +++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h > @@ -0,0 +1,57 @@ > +/** @file > + > + Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR> > + > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > + @par Glossary: > + - Tlb or TLB - Translation Lookaside Buffer > + - HGLOBAL - Huge Global > + - PFN - Page Frame number > + - EXEC - Execute > + - PLV - Privilege Level > + - RPLV - Restricted Privilege Level > + - SUC - Strong-ordered UnCached > + - CC - Coherent Cached > + - WUC - Weak-ordered UnCached > +**/ > +#ifndef PTE_H_ > +#define PTE_H_ > +/*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 */ > +#endif // PTE_H_ > -- > 2.31.1 -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#96290): https://edk2.groups.io/g/devel/message/96290 Mute This Topic: https://groups.io/mt/94955173/1787277 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org] -=-=-=-=-=-=-=-=-=-=-=-
© 2016 - 2025 Red Hat, Inc.