[edk2-devel] [edk2-platforms][PATCH V3 05/16] Platform/Loongson: Add MmuLib.

xianglai lixianglai at loongson.cn
Thu Sep 29 07:07:53 UTC 2022


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

Signed-off-by: xianglai li <lixianglai at loongson.cn>
---
 .../LoongArchQemuPkg/Include/Library/MmuLib.h |  85 ++
 .../LoongArchQemuPkg/Library/MmuLib/Mmu.S     |  35 +
 .../Library/MmuLib/MmuBaseLib.inf             |  35 +
 .../Library/MmuLib/MmuBaseLibPei.inf          |  42 +
 .../Library/MmuLib/MmuLibCore.c               | 908 ++++++++++++++++++
 .../Library/MmuLib/MmuLibCore.h               |  39 +
 .../Library/MmuLib/MmuLibCorePei.c            | 236 +++++
 .../LoongArchQemuPkg/Library/MmuLib/mmu.h     | 104 ++
 .../LoongArchQemuPkg/Library/MmuLib/page.h    | 267 +++++
 .../LoongArchQemuPkg/Library/MmuLib/pte.h     |  57 ++
 10 files changed, 1808 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..6c501eca07
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h
@@ -0,0 +1,85 @@
+/** @file
+
+  Copyright (c) 2021 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
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S
new file mode 100644
index 0000000000..a697b54e65
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S
@@ -0,0 +1,35 @@
+#------------------------------------------------------------------------------
+#
+# LoongArch for LoongArch
+#
+# Copyright (c) 2021 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#-----------------------------------------------------------------------------
+
+#ifndef _KERNEL
+#define _KERNEL
+#endif
+
+#include "Library/Cpu.h"
+#include "LoongArchAsmMacro.h"
+
+#  Query the page table.
+#
+#  @param  VOID
+#
+#  @retval  VOID
+ASM_FUNC(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
+.globl  HandleTlbRefillEnd
+HandleTlbRefillEnd:
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf
new file mode 100644
index 0000000000..d8cfe6776e
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf
@@ -0,0 +1,35 @@
+## @file
+#
+#  Copyright (c) 2021 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
+[Sources.common]
+  MmuLibCore.c
+
+[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..f6a3cab45f
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf
@@ -0,0 +1,42 @@
+## @file
+#
+#  Copyright (c) 2021 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
+
+[Sources.common]
+  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
+  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..d737759ad2
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c
@@ -0,0 +1,908 @@
+/** @file
+
+  Copyright (c) 2021 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);
+}
+ /**
+  Convert page middle directory table entry  to tlb entry.
+
+  @param  PmdVal   page middle directory table entry value.
+
+  @retval  tlb entry value.
+ **/
+UINTN
+PmdToTlbEntry (
+  UINTN PmdVal
+  )
+{
+  UINTN Value;
+
+  Value = PmdVal ^ PAGE_HUGE;
+  Value |= ((Value & PAGE_HGLOBAL) >>
+           (PAGE_HGLOBAL_SHIFT - PAGE_GLOBAL_SHIFT));
+
+  return Value;
+}
+
+ /**
+  Update huge tlb.
+
+  @param  address  The address corresponding to tlb.
+  @param  Pte   A pointer to the page table entry.
+
+  @retval  VOID.
+ **/
+VOID
+UpdateHugeTlb (
+  IN UINTN address,
+  PTE *Pte)
+{
+  INTN Idx;
+  UINTN TlbEntry;
+  address &= (PAGE_MASK << 1);
+  LOONGARCH_CSR_WRITEQ (address, LOONGARCH_CSR_TLBEHI);
+  LOONGARCH_TLB_SRCH();
+  LOONGARCH_CSR_READQ (Idx, LOONGARCH_CSR_TLBIDX);
+
+  if (Idx < 0) {
+    return ;
+  }
+  WRITE_CSR_PAGE_SIZE (HUGE_PAGE_SIZE);
+  TlbEntry = PmdToTlbEntry(PTE_VAL (*Pte));
+  LOONGARCH_CSR_WRITEQ(TlbEntry, LOONGARCH_CSR_TLBELO0);
+  LOONGARCH_CSR_WRITEQ(TlbEntry + (HUGE_PAGE_SIZE >> 1), LOONGARCH_CSR_TLBELO1);
+  LOONGARCH_TLB_WR ();
+
+  WRITE_CSR_PAGE_SIZE (DEFAULT_PAGE_SIZE);
+
+  return ;
+}
+ /**
+  Update tlb.
+
+  @param  address  The address corresponding to tlb.
+  @param  Pte   A pointer to the page table entry.
+
+  @retval  VOID.
+ **/
+VOID
+UpdateTlb (
+  IN UINTN address,
+  PTE *Pte)
+{
+  INTN Idx;
+  if (IS_HUGE_PAGE (Pte->PteVal)) {
+    return UpdateHugeTlb(address, Pte);
+  }
+
+  address &= (PAGE_MASK << 1);
+  LOONGARCH_CSR_WRITEQ (address, LOONGARCH_CSR_TLBEHI);
+  LOONGARCH_TLB_SRCH();
+  LOONGARCH_CSR_READQ (Idx, LOONGARCH_CSR_TLBIDX);
+
+  if (Idx < 0) {
+    return ;
+  }
+
+  if ((UINTN)Pte & sizeof(PTE)) {
+    Pte--;
+  }
+
+  WRITE_CSR_PAGE_SIZE (DEFAULT_PAGE_SIZE);
+  LOONGARCH_CSR_WRITEQ(PTE_VAL (*Pte), LOONGARCH_CSR_TLBELO0);
+  Pte++;
+  LOONGARCH_CSR_WRITEQ(PTE_VAL (*Pte), LOONGARCH_CSR_TLBELO1);
+  LOONGARCH_TLB_WR ();
+
+  return ;
+}
+
+/**
+  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) {
+      UpdateTlb (Address, Pte);
+    }
+  } 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 ;
+         }
+         UpdateTlb (AddressStart_HugePage, Pte);
+         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;
+  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..e9f294b356
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.h
@@ -0,0 +1,39 @@
+/** @file
+
+  Copyright (c) 2021 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
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c
new file mode 100644
index 0000000000..899f40fd94
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c
@@ -0,0 +1,236 @@
+/** @file
+  Platform PEI driver
+
+  Copyright (c) 2021 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"
+
+/**
+  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       = 0x80000000;
+  VirtualMemoryTable[Index].Attributes   = PAGE_VALID | PAGE_USER |  CACHE_CC | PAGE_DIRTY;
+  ++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 | PAGE_USER |  CACHE_CC | PAGE_DIRTY;
+    ++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;
+  UINTN PageSize;
+
+  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++;
+  }
+
+  /*set page size*/
+  WRITE_CSR_PAGE_SIZE (DEFAULT_PAGE_SIZE);
+  WRITE_CSR_STLB_PAGE_SIZE (DEFAULT_PAGE_SIZE);
+  WRITE_CSR_TLBREFILL_PAGE_SIZE (DEFAULT_PAGE_SIZE);
+  READ_CSR_PAGE_SIZE (PageSize);
+  if (PageSize != DEFAULT_PAGE_SIZE) {
+    goto FreeTranslationTable;
+  }
+
+  TlbReEntry = AllocatePages (1);
+  if (TlbReEntry == NULL) {
+    goto FreeTranslationTable;
+  }
+  CopyMem ((char *)TlbReEntry, HandleTlbRefill, (HandleTlbRefillEnd - HandleTlbRefill));
+  SET_REFILL_TLBBASE ((UINTN)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));
+
+  LOONGARCH_CSR_WRITEQ (PteShift | PteWide << 5 | PmdShift << 10 | PmdWide << 15 | PudShift << 20 | PudWide << 25,
+    LOONGARCH_CSR_PWCTL0);
+  LOONGARCH_CSR_WRITEQ (PgdShift | PgdWide << 6, LOONGARCH_CSR_PWCTL1);
+  LOONGARCH_CSR_WRITEQ ((UINTN)SwapperPageDir, LOONGARCH_CSR_PGDL);
+  LOONGARCH_CSR_WRITEQ ((UINTN)InvalidPgd, LOONGARCH_CSR_PGDH);
+
+  DEBUG ((DEBUG_INFO, "%a %d Enable Mmu Start PageBassAddress %p.\n", __func__, __LINE__, SwapperPageDir));
+  LOONGARCH_CSR_XCHGQ ( PageEnable, 1 << 4, LOONGARCH_CSR_CRMD);
+  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..50b785bb6e
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.h
@@ -0,0 +1,104 @@
+/** @file
+
+  Copyright (c) 2021 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
+
+/*
+  Set Cpu Status Register STLB Page Size.
+
+  @param val  Page Size.
+
+  @retval  VOID
+ */
+#define WRITE_CSR_STLB_PAGE_SIZE(val)  LOONGARCH_CSR_WRITEQ((val), LOONGARCH_CSR_STLBPGSIZE)
+/*
+  Set Cpu Status Register Page Size.
+
+  @param size  Page Size.
+
+  @retval  VOID
+ */
+#define WRITE_CSR_PAGE_SIZE(size)  LOONGARCH_CSR_XCHGQ((size) << CSR_TLBIDX_SIZE, CSR_TLBIDX_SIZE_MASK, LOONGARCH_CSR_TLBIDX)
+/*
+  Set Cpu Status Register TLBREFILL Page Size.
+
+  @param size  Page Size.
+
+  @retval  VOID
+ */
+#define WRITE_CSR_TLBREFILL_PAGE_SIZE(size)  LOONGARCH_CSR_XCHGQ((size) << CSR_TLBREHI_PS_SHIFT, CSR_TLBREHI_PS, LOONGARCH_CSR_TLBREHI)
+/*
+  Set Cpu Status Register TLBREFILL Base Address.
+
+  @param BaseAddress the code base address of TLB refills .
+
+  @retval  VOID
+ */
+#define SET_REFILL_TLBBASE(BaseAddress) LOONGARCH_CSR_WRITEQ((BaseAddress), LOONGARCH_CSR_TLBREBASE);
+/*
+  Get Cpu Status Register Page Size.
+
+  @param  val  Gets the page size.
+
+  @retval  VOID
+ */
+#define READ_CSR_PAGE_SIZE(val)                               \
+{                                                             \
+  LOONGARCH_CSR_READQ ((val), LOONGARCH_CSR_TLBIDX);          \
+  (val) = ((val) & CSR_TLBIDX_SIZE_MASK) >> CSR_TLBIDX_SIZE;  \
+}
+
+
+#define  CSR_TLBREHI_PS_SHIFT    0
+#define  CSR_TLBREHI_PS      ((UINTN)(0x3f) << CSR_TLBREHI_PS_SHIFT)
+
+#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[];
+#endif
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h
new file mode 100644
index 0000000000..3b3b0b72c3
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h
@@ -0,0 +1,267 @@
+/** @file
+
+  Copyright (c) 2021 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))}
+/**
+  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) | 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)
+#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
diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h
new file mode 100644
index 0000000000..d6ac74f9c9
--- /dev/null
+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h
@@ -0,0 +1,57 @@
+/** @file
+
+  Copyright (c) 2021 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
-- 
2.31.1



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#94496): https://edk2.groups.io/g/devel/message/94496
Mute This Topic: https://groups.io/mt/93989524/1813853
Group Owner: devel+owner at edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [edk2-devel-archive at redhat.com]
-=-=-=-=-=-=-=-=-=-=-=-




More information about the edk2-devel-archive mailing list