From: "Chao Li" <lichao@loongson.cn>
To: devel@edk2.groups.io
Cc: Eric Dong <eric.dong@intel.com>, Ray Ni <ray.ni@intel.com>,
Rahul Kumar <rahul1.kumar@intel.com>,
Gerd Hoffmann <kraxel@redhat.com>,
Baoqi Zhang <zhangbaoqi@loongson.cn>,
Dongyan Qian <qiandongyan@loongson.cn>,
Xianglai Li <lixianglai@loongson.cn>,
Bibo Mao <maobibo@loongson.cn>
Subject: [edk2-devel] [PATCH v6 13/36] UefiCpuPkg: Add LoongArch64CpuMmuLib to UefiCpuPkg
Date: Fri, 5 Jan 2024 17:43:53 +0800 [thread overview]
Message-ID: <20240105094353.2280431-1-lichao@loongson.cn> (raw)
In-Reply-To: <20240105094118.2279380-1-lichao@loongson.cn>
Add a new library LoongArch64CpuMmuLib. It provides two-stage MMU library
instances, PEI and DXE.
BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4584
Cc: Eric Dong <eric.dong@intel.com>
Cc: Ray Ni <ray.ni@intel.com>
Cc: Rahul Kumar <rahul1.kumar@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Chao Li <lichao@loongson.cn>
Co-authored-by: Baoqi Zhang <zhangbaoqi@loongson.cn>
Co-authored-by: Dongyan Qian <qiandongyan@loongson.cn>
Co-authored-by: Xianglai Li <lixianglai@loongson.cn>
Co-authored-by: Bibo Mao <maobibo@loongson.cn>
---
.../LoongArch64CpuMmuLib/CommonMmuLib.c | 986 ++++++++++++++++++
.../LoongArch64CpuMmuLib/CommonMmuLib.h | 43 +
.../LoongArch64CpuMmuLib/DxeCpuMmuLib.inf | 36 +
.../LoongArch64CpuMmuLib/DxeCpuMmuLib.uni | 14 +
.../Library/LoongArch64CpuMmuLib/Page.h | 279 +++++
.../LoongArch64CpuMmuLib/PeiCpuMmuLib.c | 178 ++++
.../LoongArch64CpuMmuLib/PeiCpuMmuLib.inf | 44 +
.../LoongArch64CpuMmuLib/PeiCpuMmuLib.uni | 14 +
UefiCpuPkg/Library/LoongArch64CpuMmuLib/Tlb.h | 48 +
.../LoongArch64CpuMmuLib/TlbOperation.S | 44 +
UefiCpuPkg/UefiCpuPkg.dsc | 2 +
11 files changed, 1688 insertions(+)
create mode 100644 UefiCpuPkg/Library/LoongArch64CpuMmuLib/CommonMmuLib.c
create mode 100644 UefiCpuPkg/Library/LoongArch64CpuMmuLib/CommonMmuLib.h
create mode 100644 UefiCpuPkg/Library/LoongArch64CpuMmuLib/DxeCpuMmuLib.inf
create mode 100644 UefiCpuPkg/Library/LoongArch64CpuMmuLib/DxeCpuMmuLib.uni
create mode 100644 UefiCpuPkg/Library/LoongArch64CpuMmuLib/Page.h
create mode 100644 UefiCpuPkg/Library/LoongArch64CpuMmuLib/PeiCpuMmuLib.c
create mode 100644 UefiCpuPkg/Library/LoongArch64CpuMmuLib/PeiCpuMmuLib.inf
create mode 100644 UefiCpuPkg/Library/LoongArch64CpuMmuLib/PeiCpuMmuLib.uni
create mode 100644 UefiCpuPkg/Library/LoongArch64CpuMmuLib/Tlb.h
create mode 100644 UefiCpuPkg/Library/LoongArch64CpuMmuLib/TlbOperation.S
diff --git a/UefiCpuPkg/Library/LoongArch64CpuMmuLib/CommonMmuLib.c b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/CommonMmuLib.c
new file mode 100644
index 0000000000..c92f678f3e
--- /dev/null
+++ b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/CommonMmuLib.c
@@ -0,0 +1,986 @@
+/** @file
+
+ CPU Memory Map Unit Handler Library common functions.
+
+ Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Pgd or Pgd or PGD - Page Global Directory
+ - Pud or Pud or PUD - Page Upper Directory
+ - Pmd or Pmd or PMD - Page Middle Directory
+ - Pte or pte or PTE - Page Table Entry
+ - Val or VAL or val - Value
+ - Dir - Directory
+**/
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/CpuMmuLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Register/LoongArch64/Csr.h>
+#include "Tlb.h"
+#include "Page.h"
+
+#define SWAP_PAGE_DIR CsrRead(LOONGARCH_CSR_PGDL)
+#define EFI_MEMORY_CACHETYPE_MASK (EFI_MEMORY_UC | \
+ EFI_MEMORY_WC | \
+ EFI_MEMORY_WT | \
+ EFI_MEMORY_WB | \
+ EFI_MEMORY_UCE \
+ )
+
+BOOLEAN mMmuInited = FALSE;
+
+/**
+ Check to see if mmu successfully initializes.
+
+ @param VOID.
+
+ @retval TRUE Initialization has been completed.
+ FALSE Initialization did not complete.
+**/
+STATIC
+BOOLEAN
+MmuIsInit (
+ VOID
+ )
+{
+ if (mMmuInited || (SWAP_PAGE_DIR != 0)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Iterates through the page directory to initialize it.
+
+ @param Dst A pointer to the directory of the page to initialize.
+ @param Num The number of page directories to initialize.
+ @param Src A pointer to the data used to initialize the page directory.
+
+ @return VOID.
+**/
+STATIC
+VOID
+PageDirInit (
+ IN VOID *Dst,
+ IN UINTN Num,
+ IN VOID *Src
+ )
+{
+ UINTN *Ptr;
+ UINTN *End;
+ UINTN Entry;
+
+ Entry = (UINTN)Src;
+ Ptr = (UINTN *)Dst;
+ End = Ptr + Num;
+
+ for ( ; Ptr < End; Ptr++) {
+ *Ptr = Entry;
+ }
+
+ return;
+}
+
+/**
+ Gets the virtual address corresponding to the page global directory table entry.
+
+ @param Address the virtual address for the table entry.
+
+ @retval PGD A pointer to get the table item.
+**/
+STATIC
+PGD *
+PgdOffset (
+ IN UINTN Address
+ )
+{
+ return (PGD *)(SWAP_PAGE_DIR) + PGD_INDEX (Address);
+}
+
+/**
+ Gets the virtual address corresponding to the page upper directory table entry.
+
+ @param Pgd A pointer to a page global directory table entry.
+ @param Address the virtual address for the table entry.
+
+ @retval PUD A pointer to get the table item.
+**/
+STATIC
+PUD *
+PudOffset (
+ IN PGD *Pgd,
+ IN UINTN Address
+ )
+{
+ UINTN PgdVal;
+
+ PgdVal = (UINTN)PGD_VAL (*Pgd);
+
+ return (PUD *)PgdVal + PUD_INDEX (Address);
+}
+
+/**
+ Gets the virtual address corresponding to the page middle directory table entry.
+
+ @param Pud A pointer to a page upper directory table entry.
+ @param Address the virtual address for the table entry.
+
+ @retval PMD A pointer to get the table item.
+**/
+STATIC
+PMD *
+PmdOffset (
+ IN PUD *Pud,
+ IN UINTN Address
+ )
+{
+ UINTN PudVal;
+
+ PudVal = PUD_VAL (*Pud);
+
+ return (PMD *)PudVal + PMD_INDEX (Address);
+}
+
+/**
+ Gets the virtual address corresponding to the page table entry.
+
+ @param Pmd A pointer to a page middle directory table entry.
+ @param Address the virtual address for the table entry.
+
+ @retval PTE A pointer to get the table item.
+**/
+STATIC
+PTE *
+PteOffset (
+ IN PMD *Pmd,
+ IN UINTN Address
+ )
+{
+ UINTN PmdVal;
+
+ PmdVal = (UINTN)PMD_VAL (*Pmd);
+
+ return (PTE *)PmdVal + PTE_INDEX (Address);
+}
+
+/**
+ Sets the value of the page table entry.
+
+ @param Pte A pointer to a page table entry.
+ @param PteVal The value of the page table entry to set.
+
+**/
+STATIC
+VOID
+SetPte (
+ IN PTE *Pte,
+ IN PTE PteVal
+ )
+{
+ *Pte = PteVal;
+}
+
+/**
+ Sets the value of the page global directory.
+
+ @param Pgd A pointer to a page global directory.
+ @param Pud The value of the page global directory to set.
+
+**/
+STATIC
+VOID
+SetPgd (
+ IN PGD *Pgd,
+ IN PUD *Pud
+ )
+{
+ *Pgd = (PGD) {
+ ((UINTN)Pud)
+ };
+}
+
+/**
+ Sets the value of the page upper directory.
+
+ @param Pud A pointer to a page upper directory.
+ @param Pmd The value of the page upper directory to set.
+
+**/
+STATIC
+VOID
+SetPud (
+ IN PUD *Pud,
+ IN PMD *Pmd
+ )
+{
+ *Pud = (PUD) {
+ ((UINTN)Pmd)
+ };
+}
+
+/**
+ Sets the value of the page middle directory.
+
+ @param Pmd A pointer to a page middle directory.
+ @param Pte The value of the page middle directory to set.
+
+**/
+STATIC
+VOID
+SetPmd (
+ IN PMD *Pmd,
+ IN PTE *Pte
+ )
+{
+ *Pmd = (PMD) {
+ ((UINTN)Pte)
+ };
+}
+
+/**
+ Free up memory space occupied by page tables.
+
+ @param Pte A pointer to the page table.
+
+**/
+VOID
+PteFree (
+ IN PTE *Pte
+ )
+{
+ FreePages ((VOID *)Pte, 1);
+}
+
+/**
+ Free up memory space occupied by page middle directory.
+
+ @param Pmd A pointer to the page middle directory.
+
+**/
+VOID
+PmdFree (
+ IN PMD *Pmd
+ )
+{
+ FreePages ((VOID *)Pmd, 1);
+}
+
+/**
+ Free up memory space occupied by page upper directory.
+
+ @param Pud A pointer to the page upper directory.
+
+**/
+VOID
+PudFree (
+ IN PUD *Pud
+ )
+{
+ FreePages ((VOID *)Pud, 1);
+}
+
+/**
+ Requests the memory space required for the page upper directory,
+ initializes it, and places it in the specified page global directory
+
+ @param Pgd A pointer to the page global directory.
+
+ @retval EFI_SUCCESS Memory request successful.
+ @retval EFI_OUT_OF_RESOURCES Resource exhaustion cannot be requested to memory.
+**/
+STATIC
+EFI_STATUS
+PudAlloc (
+ IN PGD *Pgd
+ )
+{
+ PUD *Pud;
+
+ Pud = (PUD *)AllocatePages (1);
+ if (Pud == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ PageDirInit ((VOID *)Pud, ENTRYS_PER_PUD, (VOID *)INVALID_PAGE);
+
+ SetPgd (Pgd, Pud);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Requests the memory space required for the page middle directory,
+ initializes it, and places it in the specified page upper directory
+
+ @param Pud A pointer to the page upper directory.
+
+ @retval EFI_SUCCESS Memory request successful.
+ @retval EFI_OUT_OF_RESOURCES Resource exhaustion cannot be requested to memory.
+**/
+STATIC
+EFI_STATUS
+PmdAlloc (
+ IN PUD *Pud
+ )
+{
+ PMD *Pmd;
+
+ Pmd = (PMD *)AllocatePages (1);
+ if (!Pmd) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ PageDirInit ((VOID *)Pmd, ENTRYS_PER_PMD, (VOID *)INVALID_PAGE);
+
+ SetPud (Pud, Pmd);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Requests the memory space required for the page table,
+ initializes it, and places it in the specified page middle directory
+
+ @param Pmd A pointer to the page middle directory.
+
+ @retval EFI_SUCCESS Memory request successful.
+ @retval EFI_OUT_OF_RESOURCES Resource exhaustion cannot be requested to memory.
+**/
+STATIC
+EFI_STATUS
+PteAlloc (
+ IN PMD *Pmd
+ )
+{
+ PTE *Pte;
+
+ Pte = (PTE *)AllocatePages (1);
+ if (!Pte) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Pte = ZeroMem (Pte, EFI_PAGE_SIZE);
+
+ SetPmd (Pmd, Pte);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Requests the memory space required for the page upper directory,
+ initializes it, and places it in the specified page global directory,
+ and get the page upper directory entry corresponding to the virtual address.
+
+ @param Pgd A pointer to the page global directory.
+ @param Address The corresponding virtual address of the page table entry.
+
+ @retval A pointer to the page upper directory entry. Return NULL, if
+ allocate the memory buffer is fail.
+**/
+STATIC
+PUD *
+PudAllocGet (
+ IN PGD *Pgd,
+ IN UINTN Address
+ )
+{
+ EFI_STATUS Status;
+
+ if (PGD_IS_EMPTY (*Pgd)) {
+ Status = PudAlloc (Pgd);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+ }
+
+ return PudOffset (Pgd, Address);
+}
+
+/**
+ Requests the memory space required for the page middle directory,
+ initializes it, and places it in the specified page upper directory,
+ and get the page middle directory entry corresponding to the virtual address.
+
+ @param Pud A pointer to the page upper directory.
+ @param Address The corresponding virtual address of the page table entry.
+
+ @retval A pointer to the page middle directory entry. Return NULL, if
+ allocate the memory buffer is fail.
+**/
+STATIC
+PMD *
+PmdAllocGet (
+ IN PUD *Pud,
+ IN UINTN Address
+ )
+{
+ EFI_STATUS Status;
+
+ if (PUD_IS_EMPTY (*Pud)) {
+ Status = PmdAlloc (Pud);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+ }
+
+ return PmdOffset (Pud, Address);
+}
+
+/**
+ Requests the memory space required for the page table,
+ initializes it, and places it in the specified page middle directory,
+ and get the page table entry corresponding to the virtual address.
+
+ @param Pmd A pointer to the page upper directory.
+ @param Address The corresponding virtual address of the page table entry.
+
+ @retval A pointer to the page table entry. Return NULL, if allocate
+ the memory buffer is fail.
+**/
+STATIC
+PTE *
+PteAllocGet (
+ IN PMD *Pmd,
+ IN UINTN Address
+ )
+{
+ EFI_STATUS Status;
+
+ if (PMD_IS_EMPTY (*Pmd)) {
+ Status = PteAlloc (Pmd);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+ }
+
+ return PteOffset (Pmd, Address);
+}
+
+/**
+ Gets the physical address of the page table entry corresponding to the specified virtual address.
+
+ @param Address The corresponding virtual address of the page table entry.
+
+ @retval A pointer to the page table entry.
+ @retval NULL
+**/
+STATIC
+PTE *
+GetPteAddress (
+ IN UINTN Address
+ )
+{
+ PGD *Pgd;
+ PUD *Pud;
+ PMD *Pmd;
+
+ Pgd = PgdOffset (Address);
+
+ if (PGD_IS_EMPTY (*Pgd)) {
+ return NULL;
+ }
+
+ Pud = PudOffset (Pgd, Address);
+
+ if (PUD_IS_EMPTY (*Pud)) {
+ return NULL;
+ }
+
+ Pmd = PmdOffset (Pud, Address);
+ if (PMD_IS_EMPTY (*Pmd)) {
+ return NULL;
+ }
+
+ if (IS_HUGE_PAGE (Pmd->PmdVal)) {
+ return ((PTE *)Pmd);
+ }
+
+ return PteOffset (Pmd, Address);
+}
+
+/**
+ Gets the Attributes of Huge Page.
+
+ @param Pmd A pointer to the page middle directory.
+
+ @retval Value of Attributes.
+**/
+STATIC
+UINTN
+GetHugePageAttributes (
+ IN PMD *Pmd
+ )
+{
+ UINTN Attributes;
+ UINTN GlobalFlag;
+ UINTN HugeVal;
+
+ HugeVal = PMD_VAL (*Pmd);
+ Attributes = HugeVal & (~HUGEP_PAGE_MASK);
+ GlobalFlag = ((Attributes & (1 << PAGE_HGLOBAL_SHIFT)) >> PAGE_HGLOBAL_SHIFT) << PAGE_GLOBAL_SHIFT;
+ Attributes &= ~(1 << PAGE_HGLOBAL_SHIFT);
+ Attributes |= GlobalFlag;
+ return Attributes;
+}
+
+/**
+ Establishes a page table entry based on the specified memory region.
+
+ @param Pmd A pointer to the page middle directory.
+ @param Address The memory space start address.
+ @param End The end address of the memory space.
+ @param Attributes Memory space Attributes.
+
+ @retval EFI_SUCCESS The page table entry was created successfully.
+ @retval EFI_OUT_OF_RESOURCES Page table entry establishment failed due to resource exhaustion.
+**/
+STATIC
+EFI_STATUS
+MemoryMapPteRange (
+ IN PMD *Pmd,
+ IN UINTN Address,
+ IN UINTN End,
+ IN UINTN Attributes
+ )
+{
+ PTE *Pte;
+ PTE PteVal;
+ BOOLEAN UpDate;
+
+ Pte = PteAllocGet (Pmd, Address);
+ if (!Pte) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a %d Address %p End %p Attributes %llx\n",
+ __func__,
+ __LINE__,
+ Address,
+ End,
+ Attributes
+ ));
+
+ do {
+ UpDate = FALSE;
+ PteVal = MAKE_PTE (Address, Attributes);
+
+ if ((!PTE_IS_EMPTY (*Pte)) &&
+ (PTE_VAL (*Pte) != PTE_VAL (PteVal)))
+ {
+ UpDate = TRUE;
+ }
+
+ SetPte (Pte, PteVal);
+ if (UpDate) {
+ InvalidTlb (Address);
+ }
+ } while (Pte++, Address += EFI_PAGE_SIZE, Address != End);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Convert Huge Page to Page.
+
+ @param Pmd A pointer to the page middle directory.
+ @param Address The memory space start address.
+ @param End The end address of the memory space.
+ @param Attributes Memory space Attributes.
+
+ @retval EFI_SUCCESS The page table entry was created successfully.
+ @retval EFI_OUT_OF_RESOURCES Page table entry establishment failed due to resource exhaustion.
+**/
+STATIC
+EFI_STATUS
+ConvertHugePageToPage (
+ IN PMD *Pmd,
+ IN UINTN Address,
+ IN UINTN End,
+ IN UINTN Attributes
+ )
+{
+ UINTN OldAttributes;
+ UINTN HugePageEnd;
+ UINTN HugePageStart;
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ if ((PMD_IS_EMPTY (*Pmd)) ||
+ (!IS_HUGE_PAGE (Pmd->PmdVal)))
+ {
+ Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
+ } else {
+ OldAttributes = GetHugePageAttributes (Pmd);
+ if (Attributes == OldAttributes) {
+ return Status;
+ }
+
+ SetPmd (Pmd, (PTE *)(INVALID_PAGE));
+ HugePageStart = Address & PMD_MASK;
+ HugePageEnd = HugePageStart + HUGE_PAGE_SIZE;
+ ASSERT (HugePageEnd >= End);
+
+ if (Address > HugePageStart) {
+ Status |= MemoryMapPteRange (Pmd, HugePageStart, Address, OldAttributes);
+ }
+
+ Status |= MemoryMapPteRange (Pmd, Address, End, Attributes);
+
+ if (End < HugePageEnd) {
+ Status |= MemoryMapPteRange (Pmd, End, HugePageEnd, OldAttributes);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Establishes a page middle directory based on the specified memory region.
+
+ @param Pud A pointer to the page upper directory.
+ @param Address The memory space start address.
+ @param End The end address of the memory space.
+ @param Attributes Memory space Attributes.
+
+ @retval EFI_SUCCESS The page middle directory was created successfully.
+ @retval EFI_OUT_OF_RESOURCES Page middle directory establishment failed due to resource exhaustion.
+**/
+STATIC
+EFI_STATUS
+MemoryMapPmdRange (
+ IN PUD *Pud,
+ IN UINTN Address,
+ IN UINTN End,
+ IN UINTN Attributes
+ )
+{
+ PMD *Pmd;
+ UINTN Next;
+ PTE PteVal;
+ BOOLEAN UpDate;
+
+ Pmd = PmdAllocGet (Pud, Address);
+ if (Pmd == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ do {
+ Next = PMD_ADDRESS_END (Address, End);
+ if (((Address & (~PMD_MASK)) == 0) &&
+ ((Next & (~PMD_MASK)) == 0) &&
+ (PMD_IS_EMPTY (*Pmd) || IS_HUGE_PAGE (Pmd->PmdVal)))
+ {
+ UpDate = FALSE;
+ PteVal = MAKE_HUGE_PTE (Address, Attributes);
+
+ if ((!PMD_IS_EMPTY (*Pmd)) &&
+ (PMD_VAL (*Pmd) != PTE_VAL (PteVal)))
+ {
+ UpDate = TRUE;
+ }
+
+ SetPmd (Pmd, (PTE *)PteVal.PteVal);
+ if (UpDate) {
+ InvalidTlb (Address);
+ }
+ } else {
+ ConvertHugePageToPage (Pmd, Address, Next, Attributes);
+ }
+ } while (Pmd++, Address = Next, Address != End);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Establishes a page upper directory based on the specified memory region.
+
+ @param Pgd A pointer to the page global directory.
+ @param Address The memory space start address.
+ @param End The end address of the memory space.
+ @param Attributes Memory space Attributes.
+
+ @retval EFI_SUCCESS The page upper directory was created successfully.
+ @retval EFI_OUT_OF_RESOURCES Page upper directory establishment failed due to resource exhaustion.
+**/
+STATIC
+EFI_STATUS
+MemoryMapPudRange (
+ IN PGD *Pgd,
+ IN UINTN Address,
+ IN UINTN End,
+ IN UINTN Attributes
+ )
+{
+ PUD *Pud;
+ UINTN Next;
+
+ Pud = PudAllocGet (Pgd, Address);
+ if (Pud == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ do {
+ Next = PUD_ADDRESS_END (Address, End);
+ if (EFI_ERROR (MemoryMapPmdRange (Pud, Address, Next, Attributes))) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ } while (Pud++, Address = Next, Address != End);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Establishes a page global directory based on the specified memory region.
+
+ @param Start The memory space start address.
+ @param End The end address of the memory space.
+ @param Attributes Memory space Attributes.
+
+ @retval EFI_SUCCESS The page global directory was created successfully.
+ @retval EFI_OUT_OF_RESOURCES Page global directory establishment failed due to resource exhaustion.
+**/
+STATIC
+EFI_STATUS
+MemoryMapPageRange (
+ IN UINTN Start,
+ IN UINTN End,
+ IN UINTN Attributes
+ )
+{
+ PGD *Pgd;
+ UINTN Next;
+ UINTN Address;
+ EFI_STATUS Err;
+
+ Address = Start;
+
+ /* Get PGD(PTE PMD PUD PGD) in PageTables */
+ Pgd = PgdOffset (Address);
+ do {
+ Next = PGD_ADDRESS_END (Address, End);
+ /* Get Next Align Page to Map */
+ Err = MemoryMapPudRange (Pgd, Address, Next, Attributes);
+ if (Err) {
+ return Err;
+ }
+ } while (Pgd++, Address = Next, Address != End);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Page tables are established from memory-mapped tables.
+
+ @param MemoryRegion A pointer to a memory-mapped table entry.
+
+ @retval EFI_SUCCESS The page table was created successfully.
+ @retval EFI_OUT_OF_RESOURCES Page table establishment failed due to resource exhaustion.
+**/
+EFI_STATUS
+FillTranslationTable (
+ IN MEMORY_REGION_DESCRIPTOR *MemoryRegion
+ )
+{
+ return MemoryMapPageRange (
+ MemoryRegion->VirtualBase,
+ (MemoryRegion->Length + MemoryRegion->VirtualBase),
+ MemoryRegion->Attributes
+ );
+}
+
+/**
+ Convert EFI Attributes to Loongarch Attributes.
+
+ @param[in] EfiAttributes Efi Attributes.
+
+ @retval Corresponding architecture attributes.
+**/
+UINTN
+EFIAPI
+EfiAttributeConverse (
+ IN UINTN EfiAttributes
+ )
+{
+ UINTN LoongArchAttributes;
+
+ LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | PLV_KERNEL | PAGE_GLOBAL;
+
+ switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {
+ case EFI_MEMORY_UC:
+ LoongArchAttributes |= CACHE_SUC;
+ break;
+ case EFI_MEMORY_WC:
+ case EFI_MEMORY_WT:
+ case EFI_MEMORY_WB:
+ LoongArchAttributes |= CACHE_CC;
+ break;
+ default:
+ LoongArchAttributes |= CACHE_CC;
+ break;
+ }
+
+ // Write protection attributes
+ if (((EfiAttributes & EFI_MEMORY_RO) != 0) ||
+ ((EfiAttributes & EFI_MEMORY_WP) != 0))
+ {
+ LoongArchAttributes &= ~PAGE_DIRTY;
+ }
+
+ if ((EfiAttributes & EFI_MEMORY_RP) != 0) {
+ LoongArchAttributes |= PAGE_NO_READ;
+ }
+
+ // eXecute protection attribute
+ if ((EfiAttributes & EFI_MEMORY_XP) != 0) {
+ LoongArchAttributes |= PAGE_NO_EXEC;
+ }
+
+ return LoongArchAttributes;
+}
+
+/**
+ Finds the first of the length and memory properties of the memory region corresponding
+ to the specified base address.
+
+ @param[in] BaseAddress To find the base address of the memory region.
+ @param[in, out] RegionLength Pointer holding:
+ - At entry, the length of the memory region
+ expected to be found.
+ - At exit, the length of the memory region found.
+ @param[out] RegionAttributes Properties of the memory region found.
+
+ @retval EFI_SUCCESS The corresponding memory area was successfully found
+ EFI_NOT_FOUND No memory area found
+ EFI_OUT_OF_RESOURCES Base address or expected memory region exceeds the maximum
+ address.
+**/
+EFI_STATUS
+EFIAPI
+GetMemoryRegionAttributes (
+ IN UINTN BaseAddress,
+ IN OUT UINTN *RegionLength,
+ OUT UINTN *RegionAttributes
+ )
+{
+ PTE *Pte;
+ UINTN Attributes;
+ UINTN AttributesTmp;
+ UINTN MaxAddress;
+ UINTN EndAddress;
+ UINTN AddSize;
+
+ if (!MmuIsInit ()) {
+ return EFI_UNSUPPORTED;
+ }
+
+ EndAddress = BaseAddress + *RegionLength;
+ MaxAddress = LShiftU64 (1ULL, MAX_VA_BITS) - 1;
+
+ // Clean the value to prepare output to find region size.
+ *RegionLength = 0x0;
+
+ if ((BaseAddress >= MaxAddress) || (EndAddress >= MaxAddress)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Pte = GetPteAddress (BaseAddress);
+
+ if (Pte == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ Attributes = GET_PAGE_ATTRIBUTES (*Pte);
+ if (IS_HUGE_PAGE (Pte->PteVal)) {
+ *RegionAttributes = Attributes & (~(PAGE_HUGE));
+ } else {
+ *RegionAttributes = Attributes;
+ }
+
+ do {
+ Pte = GetPteAddress (BaseAddress);
+ if (Pte == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ AttributesTmp = GET_PAGE_ATTRIBUTES (*Pte);
+ if (AttributesTmp == Attributes) {
+ if (IS_HUGE_PAGE (Pte->PteVal)) {
+ AddSize = HUGE_PAGE_SIZE;
+ } else {
+ AddSize = EFI_PAGE_SIZE;
+ }
+
+ *RegionLength += AddSize;
+ BaseAddress += AddSize;
+ } else {
+ return EFI_SUCCESS;
+ }
+ } while (BaseAddress <= EndAddress);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Sets the Attributes of the specified memory region
+
+ @param[in] BaseAddress The base address of the memory region to set the Attributes.
+ @param[in] Length The length of the memory region to set the Attributes.
+ @param[in] Attributes The Attributes to be set.
+ @param[in] AttributeMask Mask of memory attributes to take into account.
+
+ @retval EFI_SUCCESS The Attributes was set successfully
+**/
+EFI_STATUS
+EFIAPI
+SetMemoryRegionAttributes (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINTN Length,
+ IN UINTN Attributes,
+ IN UINT64 AttributeMask
+ )
+{
+ EFI_STATUS Status;
+
+ if (!MmuIsInit ()) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Attributes = EfiAttributeConverse (Attributes);
+ Status = MemoryMapPageRange (BaseAddress, BaseAddress + Length, Attributes);
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
+
+/**
+ Check to see if mmu successfully initializes and saves the result.
+
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval RETURN_SUCCESS Initialization succeeded.
+**/
+RETURN_STATUS
+MmuInitialize (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ if (SWAP_PAGE_DIR != 0) {
+ mMmuInited = TRUE;
+ }
+
+ return RETURN_SUCCESS;
+}
diff --git a/UefiCpuPkg/Library/LoongArch64CpuMmuLib/CommonMmuLib.h b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/CommonMmuLib.h
new file mode 100644
index 0000000000..d8c922c8fa
--- /dev/null
+++ b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/CommonMmuLib.h
@@ -0,0 +1,43 @@
+/** @file
+
+ Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Dir - Directory
+**/
+
+#ifndef MMU_LIB_CORE_H_
+#define MMU_LIB_CORE_H_
+
+/**
+ Iterates through the page directory to initialize it.
+
+ @param Dst A pointer to the directory of the page to initialize.
+ @param Num The number of page directories to initialize.
+ @param Src A pointer to the data used to initialize the page directory.
+
+ @retval VOID.
+**/
+VOID
+PageDirInit (
+ IN VOID *dest,
+ IN UINTN Count,
+ IN VOID *src
+ );
+
+/**
+ Page tables are established from memory-mapped tables.
+
+ @param MemoryRegion A pointer to a memory-mapped table entry.
+
+ @retval EFI_SUCCESS The page table was created successfully.
+ @retval EFI_OUT_OF_RESOURCES Page table establishment failed due to resource exhaustion.
+**/
+EFI_STATUS
+FillTranslationTable (
+ IN MEMORY_REGION_DESCRIPTOR *MemoryRegion
+ );
+
+#endif // MMU_LIB_CORE_H_
diff --git a/UefiCpuPkg/Library/LoongArch64CpuMmuLib/DxeCpuMmuLib.inf b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/DxeCpuMmuLib.inf
new file mode 100644
index 0000000000..bb0875d14f
--- /dev/null
+++ b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/DxeCpuMmuLib.inf
@@ -0,0 +1,36 @@
+## @file
+# CPU Memory Map Unit DXE phase driver.
+#
+# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 1.29
+ BASE_NAME = DxeCpuMmuLib
+ MODULE_UNI_FILE = DxeCpuMmuLib.uni
+ FILE_GUID = DA8F0232-FB14-42F0-922C-63104D2C70BE
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = CpuMmuLib | DXE_DRIVER
+ CONSTRUCTOR = MmuInitialize
+
+#
+# VALID_ARCHITECTURES = LOONGARCH64
+#
+
+[Sources]
+ TlbOperation.S | GCC
+ CommonMmuLib.c
+ Tlb.h
+ Page.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+ DebugLib
+ MemoryAllocationLib
diff --git a/UefiCpuPkg/Library/LoongArch64CpuMmuLib/DxeCpuMmuLib.uni b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/DxeCpuMmuLib.uni
new file mode 100644
index 0000000000..7342249516
--- /dev/null
+++ b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/DxeCpuMmuLib.uni
@@ -0,0 +1,14 @@
+// /** @file
+// CPU Memory Manager Unit library instance for DXE modules.
+//
+// CPU Memory Manager Unit library instance for DXE modules.
+//
+// Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "CPU Memory Manager Unit library instance for DXE modules."
+
+#string STR_MODULE_DESCRIPTION #language en-US "CPU Memory Manager Unit library instance for DXE modules."
diff --git a/UefiCpuPkg/Library/LoongArch64CpuMmuLib/Page.h b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/Page.h
new file mode 100644
index 0000000000..bac4f52327
--- /dev/null
+++ b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/Page.h
@@ -0,0 +1,279 @@
+/** @file
+
+ Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Pgd or Pgd or PGD - Page Global Directory
+ - Pud or Pud or PUD - Page Upper Directory
+ - Pmd or Pmd or PMD - Page Middle Directory
+ - Pte or pte or PTE - Page Table Entry
+ - Val or VAL or val - Value
+ - Dir - Directory
+**/
+
+#ifndef PAGE_H_
+#define PAGE_H_
+
+#include <Library/CpuMmuLib.h>
+
+#define MAX_VA_BITS 47
+#define PGD_WIDE (8)
+#define PUD_WIDE (9)
+#define PMD_WIDE (9)
+#define PTE_WIDE (9)
+
+#define ENTRYS_PER_PGD (1 << PGD_WIDE)
+#define ENTRYS_PER_PUD (1 << PUD_WIDE)
+#define ENTRYS_PER_PMD (1 << PMD_WIDE)
+#define ENTRYS_PER_PTE (1 << PTE_WIDE)
+
+#define PGD_SHIFT (PUD_SHIFT + PUD_WIDE)
+#define PUD_SHIFT (PMD_SHIFT + PMD_WIDE)
+#define PMD_SHIFT (EFI_PAGE_SHIFT + PTE_WIDE)
+#define PTE_SHIFT (EFI_PAGE_SHIFT)
+
+#define PGD_SIZE (1UL << PGD_SHIFT)
+#define PUD_SIZE (1UL << PUD_SHIFT)
+#define PMD_SIZE (1UL << PMD_SHIFT)
+
+#define PGD_MASK (~(PGD_SIZE-1))
+#define PUD_MASK (~(PUD_SIZE-1))
+#define PMD_MASK (~(PMD_SIZE-1))
+#define PAGE_MASK (~(EFI_PAGE_SIZE - 1))
+#define PFN_MASK (~(((UINTN)(1) << (EFI_PAGE_SHIFT)) - 1) & \
+ (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
+
+#define HUGEP_PAGE_MASK (~(((UINTN)(1) << (PMD_SHIFT)) - 1) & \
+ (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))
+
+#define INVALID_PAGE 0
+
+typedef struct {
+ UINTN PgdVal;
+} PGD;
+typedef struct {
+ UINTN PudVal;
+} PUD;
+typedef struct {
+ UINTN PmdVal;
+} PMD;
+typedef struct {
+ UINTN PteVal;
+} PTE;
+
+/**
+ Gets the value of the page global directory table entry.
+
+ @param x Page global directory struct variables.
+
+ @retval the value of the page global directory table entry.
+ **/
+#define PGD_VAL(x) ((x).PgdVal)
+
+/**
+ Gets the value of the page upper directory table entry.
+
+ @param x Page upper directory struct variables.
+
+ @retval the value of the page upper directory table entry.
+ **/
+#define PUD_VAL(x) ((x).PudVal)
+
+/**
+ Gets the value of the page middle directory table entry.
+
+ @param x Page middle directory struct variables.
+
+ @retval the value of the page middle directory table entry.
+ **/
+#define PMD_VAL(x) ((x).PmdVal)
+
+/**
+ Gets the value of the page table entry.
+
+ @param x Page table entry struct variables.
+
+ @retval the value of the page table entry.
+ **/
+#define PTE_VAL(x) ((x).PteVal)
+
+#define PGD_TABLE_SIZE (ENTRYS_PER_PGD * sizeof(PGD))
+#define PUD_TABLE_SIZE (ENTRYS_PER_PUD * sizeof(PUD))
+#define PMD_TABLE_SIZE (ENTRYS_PER_PMD * sizeof(PMD))
+#define PTE_TABLE_SIZE (ENTRYS_PER_PTE * sizeof(PTE))
+
+/**
+ Gets the physical address of the record in the page table entry.
+
+ @param x Page table entry struct variables.
+
+ @retval the value of the physical address.
+ **/
+#define GET_PAGE_ATTRIBUTES(x) (UINTN) {(PTE_VAL(x) & ~PFN_MASK)}
+
+/**
+ Gets the virtual address of the next block of the specified virtual address
+ that is aligned with the size of the global page directory mapping.
+
+ @param Address Specifies the virtual address.
+ @param End The end address of the memory region.
+
+ @retval the specified virtual address of the next block.
+ **/
+#define PGD_ADDRESS_END(Address, End) \
+({ \
+ UINTN Boundary = ((Address) + PGD_SIZE) & PGD_MASK; \
+ (Boundary - 1 < (End) - 1)? Boundary: (End); \
+})
+
+/**
+ Gets the virtual address of the next block of the specified virtual address
+ that is aligned with the size of the page upper directory mapping.
+
+ @param Address Specifies the virtual address.
+ @param End The end address of the memory region.
+
+ @retval the specified virtual address of the next block.
+ **/
+#define PUD_ADDRESS_END(Address, End) \
+({ \
+ UINTN Boundary = ((Address) + PUD_SIZE) & PUD_MASK; \
+ (Boundary - 1 < (End) - 1)? Boundary: (End); \
+})
+
+/**
+ Gets the virtual address of the next block of the specified virtual address
+ that is aligned with the size of the page middle directory mapping.
+
+ @param Address Specifies the virtual address.
+ @param End The end address of the memory region.
+
+ @retval the specified virtual address of the next block.
+ **/
+#define PMD_ADDRESS_END(Address, End) \
+({ \
+ UINTN Boundary = ((Address) + PMD_SIZE) & PMD_MASK; \
+ (Boundary - 1 < (End) - 1)? Boundary: (End); \
+})
+
+/**
+ Get Specifies the virtual address corresponding to the index of the page global directory table entry.
+
+ @param Address Specifies the virtual address.
+
+ @retval the index of the page global directory table entry.
+ **/
+#define PGD_INDEX(Address) (((Address) >> PGD_SHIFT) & (ENTRYS_PER_PGD-1))
+
+/**
+ Get Specifies the virtual address corresponding to the index of the page upper directory table entry.
+
+ @param Address Specifies the virtual address.
+ @param End The end address of the memory region.
+
+ @retval the index of the page upper directory table entry.
+ **/
+#define PUD_INDEX(Address) (((Address) >> PUD_SHIFT) & (ENTRYS_PER_PUD - 1))
+
+/**
+ Get Specifies the virtual address corresponding to the index of the page middle directory table entry.
+
+ @param Address Specifies the virtual address.
+
+ @retval the index of the page middle directory table entry.
+ **/
+#define PMD_INDEX(Address) (((Address) >> PMD_SHIFT) & (ENTRYS_PER_PMD - 1))
+
+/**
+ Get Specifies the virtual address corresponding to the index of the page table entry.
+
+ @param Address Specifies the virtual address.
+
+ @retval the index of the page table entry.
+ **/
+#define PTE_INDEX(Address) (((Address) >> EFI_PAGE_SHIFT) & (ENTRYS_PER_PTE - 1))
+
+/**
+ Calculates the value of the page table entry based on the specified virtual address and properties.
+
+ @param Address Specifies the virtual address.
+ @param Attributes Specifies the Attributes.
+
+ @retval the value of the page table entry.
+ **/
+#define MAKE_PTE(Address, Attributes) (PTE){((((Address) >> EFI_PAGE_SHIFT) << 12) | (Attributes))}
+
+/**
+ Get Global bit from Attributes
+
+ @param Attributes Specifies the Attributes.
+ * */
+#define GET_GLOBALBIT(Attributes) ((Attributes & PAGE_GLOBAL) >> PAGE_GLOBAL_SHIFT)
+
+/**
+ Calculates the value of the Huge page table entry based on the specified virtual address and properties.
+
+ @param Address Specifies the virtual address.
+ @param Attributes Specifies the Attributes.
+
+ @retval the value of the HUGE page table entry.
+ **/
+#define MAKE_HUGE_PTE(Address, Attributes) (PTE){(((((Address) >> PMD_SHIFT) << PMD_SHIFT) | \
+ ((Attributes) | (GET_GLOBALBIT(Attributes) << PAGE_HGLOBAL_SHIFT) | \
+ PAGE_HUGE)))}
+
+/**
+ Check whether the large page table entry is.
+
+ @param Val The value of the page table entry.
+
+ @retval 1 Is huge page table entry.
+ @retval 0 Isn't huge page table entry.
+**/
+#define IS_HUGE_PAGE(Val) ((((Val) & PAGE_HUGE) == PAGE_HUGE) && \
+ (((Val) & PAGE_HGLOBAL) == PAGE_HGLOBAL))
+
+#define HUGE_PAGE_SIZE (PMD_SIZE)
+
+/**
+ Check that the global page directory table entry is empty.
+
+ @param pgd the global page directory struct variables.
+
+ @retval 1 The page table is invalid.
+ @retval 0 The page table is valid.
+**/
+#define PGD_IS_EMPTY(Val) (PGD_VAL(Val) == INVALID_PAGE)
+
+/**
+ Check that the page upper directory table entry is empty.
+
+ @param pud Page upper directory struct variables.
+
+ @retval 1 The page table is invalid.
+ @retval 0 The page table is valid.
+**/
+#define PUD_IS_EMPTY(Val) (PUD_VAL(Val) == INVALID_PAGE)
+
+/**
+ Check that the page middle directory table entry is empty.
+
+ @param pmd Page middle directory struct variables.
+
+ @retval 1 The page table is invalid.
+ @retval 0 The page table is valid.
+**/
+#define PMD_IS_EMPTY(Val) (PMD_VAL(Val) == INVALID_PAGE)
+
+/**
+ Check that the page the page table entry is empty.
+
+ @param pte Page table entry struct variables.
+
+ @retval 1 The page table is invalid.
+ @retval 0 The page table is valid.
+**/
+#define PTE_IS_EMPTY(Val) (!(PTE_VAL(Val) & (~PAGE_VALID)))
+#endif // PAGE_H_
diff --git a/UefiCpuPkg/Library/LoongArch64CpuMmuLib/PeiCpuMmuLib.c b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/PeiCpuMmuLib.c
new file mode 100644
index 0000000000..c214e8d847
--- /dev/null
+++ b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/PeiCpuMmuLib.c
@@ -0,0 +1,178 @@
+/** @file
+ CPU Memory Map Unit PEI phase driver.
+
+ Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+ @par Glossary:
+ - Tlb - Translation Lookaside Buffer
+**/
+
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/CpuMmuLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Register/LoongArch64/Csr.h>
+
+#include "Page.h"
+#include "Tlb.h"
+#include "CommonMmuLib.h"
+
+//
+// For coding convenience, define the maximum valid
+// LoongArch exception.
+// Since UEFI V2.11, it will be present in DebugSupport.h.
+//
+#define MAX_LOONGARCH_EXCEPTION 64
+
+/**
+ Create a page table and initialize the memory management unit(MMU).
+
+ @param[in] MemoryTable A pointer to a memory ragion table.
+ @param[out] TranslationTableBase A pointer to a translation table base address.
+ @param[out] TranslationTableSize A pointer to a translation table base size.
+
+ @retval EFI_SUCCESS Configure MMU successfully.
+ EFI_INVALID_PARAMETER MemoryTable is NULL.
+ EFI_UNSUPPORTED Out of memory space or size not aligned.
+**/
+EFI_STATUS
+EFIAPI
+ConfigureMemoryManagementUnit (
+ IN MEMORY_REGION_DESCRIPTOR *MemoryTable,
+ OUT VOID **TranslationTableBase OPTIONAL,
+ OUT UINTN *TranslationTableSize OPTIONAL
+ )
+{
+ PGD *SwapperPageDir;
+ UINTN PgdShift;
+ UINTN PgdWide;
+ UINTN PudShift;
+ UINTN PudWide;
+ UINTN PmdShift;
+ UINTN PmdWide;
+ UINTN PteShift;
+ UINTN PteWide;
+ UINTN Length;
+ UINTN TlbReEntry;
+ UINTN TlbReEntryOffset;
+ UINTN Remaining;
+ RETURN_STATUS Status;
+
+ SwapperPageDir = NULL;
+ PgdShift = PGD_SHIFT;
+ PgdWide = PGD_WIDE;
+ PudShift = PUD_SHIFT;
+ PudWide = PUD_WIDE;
+ PmdShift = PMD_SHIFT;
+ PmdWide = PMD_WIDE;
+ PteShift = PTE_SHIFT;
+ PteWide = PTE_WIDE;
+
+ if (MemoryTable == NULL) {
+ ASSERT (MemoryTable != NULL);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ SwapperPageDir = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
+ ZeroMem (SwapperPageDir, PGD_TABLE_SIZE);
+
+ if (SwapperPageDir == NULL) {
+ goto FreeTranslationTable;
+ }
+
+ CsrWrite (LOONGARCH_CSR_PGDL, (UINTN)SwapperPageDir);
+
+ while (MemoryTable->Length != 0) {
+ DEBUG ((
+ DEBUG_INFO,
+ "%a %d VirtualBase %p VirtualEnd %p Attributes %p .\n",
+ __func__,
+ __LINE__,
+ MemoryTable->VirtualBase,
+ (MemoryTable->Length + MemoryTable->VirtualBase),
+ MemoryTable->Attributes
+ ));
+
+ Status = FillTranslationTable (MemoryTable);
+ if (EFI_ERROR (Status)) {
+ goto FreeTranslationTable;
+ }
+
+ MemoryTable++;
+ }
+
+ //
+ // TLB Re-entry address at the end of exception vector, a vector is up to 512 bytes,
+ // so the starting address is: total exception vector size + total interrupt vector size + base.
+ // The total size of TLB handler and exception vector size and interrupt vector size should not
+ // be lager than 64KB.
+ //
+ Length = (UINTN)HandleTlbRefillEnd - (UINTN)HandleTlbRefillStart;
+ TlbReEntryOffset = (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * 512;
+ Remaining = TlbReEntryOffset % SIZE_4KB;
+ if (Remaining != 0x0) {
+ TlbReEntryOffset += (SIZE_4KB - Remaining);
+ }
+
+ TlbReEntry = PcdGet64 (PcdCpuExceptionVectorBaseAddress) + TlbReEntryOffset;
+ if ((TlbReEntryOffset + Length) > SIZE_64KB) {
+ goto FreeTranslationTable;
+ }
+
+ //
+ // Ensure that TLB refill exception base address alignment is equals to 4KB and is valid.
+ //
+ if (TlbReEntry & (SIZE_4KB - 1)) {
+ goto FreeTranslationTable;
+ }
+
+ CopyMem ((VOID *)TlbReEntry, HandleTlbRefillStart, Length);
+ InvalidateInstructionCacheRange ((VOID *)(UINTN)HandleTlbRefillStart, Length);
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a %d PteShift %d PteWide %d PmdShift %d PmdWide %d PudShift %d PudWide %d PgdShift %d PgdWide %d.\n",
+ __func__,
+ __LINE__,
+ PteShift,
+ PteWide,
+ PmdShift,
+ PmdWide,
+ PudShift,
+ PudWide,
+ PgdShift,
+ PgdWide
+ ));
+
+ //
+ // Set the address of TLB refill exception handler
+ //
+ SetTlbRebaseAddress ((UINTN)TlbReEntry);
+
+ //
+ // Set page size
+ //
+ CsrXChg (LOONGARCH_CSR_TLBIDX, (DEFAULT_PAGE_SIZE << CSR_TLBIDX_SIZE), CSR_TLBIDX_SIZE_MASK);
+ CsrWrite (LOONGARCH_CSR_STLBPGSIZE, DEFAULT_PAGE_SIZE);
+ CsrXChg (LOONGARCH_CSR_TLBREHI, (DEFAULT_PAGE_SIZE << CSR_TLBREHI_PS_SHIFT), CSR_TLBREHI_PS);
+
+ CsrWrite (LOONGARCH_CSR_PWCTL0, (PteShift | PteWide << 5 | PmdShift << 10 | PmdWide << 15 | PudShift << 20 | PudWide << 25));
+ CsrWrite (LOONGARCH_CSR_PWCTL1, (PgdShift | PgdWide << 6));
+
+ DEBUG ((DEBUG_INFO, "%a %d Enable Mmu Start PageBassAddress %p.\n", __func__, __LINE__, SwapperPageDir));
+
+ return EFI_SUCCESS;
+
+FreeTranslationTable:
+ if (SwapperPageDir != NULL) {
+ FreePages (SwapperPageDir, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));
+ }
+
+ return EFI_UNSUPPORTED;
+}
diff --git a/UefiCpuPkg/Library/LoongArch64CpuMmuLib/PeiCpuMmuLib.inf b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/PeiCpuMmuLib.inf
new file mode 100644
index 0000000000..cbd9907008
--- /dev/null
+++ b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/PeiCpuMmuLib.inf
@@ -0,0 +1,44 @@
+## @file
+# CPU Memory Map Unit PEI phase driver.
+#
+# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 1.29
+ BASE_NAME = PeiCpuMmuLib
+ MODULE_UNI_FILE = PeiCpuMmuLib.uni
+ FILE_GUID = F67EB983-AC2A-7550-AB69-3BC51A1C895B
+ MODULE_TYPE = PEIM
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = CpuMmuLib | SEC PEIM
+
+#
+# VALID_ARCHITECTURES = LOONGARCH64
+#
+
+[Sources]
+ TlbOperation.S | GCC
+ PeiCpuMmuLib.c
+ CommonMmuLib.c
+ CommonMmuLib.h
+ Tlb.h
+ Page.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+
+[PCD]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuExceptionVectorBaseAddress
+
+[LibraryClasses]
+ CacheMaintenanceLib
+ DebugLib
+ MemoryAllocationLib
+ PcdLib
diff --git a/UefiCpuPkg/Library/LoongArch64CpuMmuLib/PeiCpuMmuLib.uni b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/PeiCpuMmuLib.uni
new file mode 100644
index 0000000000..3e21334f3e
--- /dev/null
+++ b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/PeiCpuMmuLib.uni
@@ -0,0 +1,14 @@
+// /** @file
+// CPU Memory Manager Unit library instance for PEI modules.
+//
+// CPU Memory Manager Unit library instance for PEI modules.
+//
+// Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_MODULE_ABSTRACT #language en-US "CPU Memory Manager Unit library instance for PEI modules."
+
+#string STR_MODULE_DESCRIPTION #language en-US "CPU Memory Manager Unit library instance for PEI modules."
diff --git a/UefiCpuPkg/Library/LoongArch64CpuMmuLib/Tlb.h b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/Tlb.h
new file mode 100644
index 0000000000..9a681ce8e1
--- /dev/null
+++ b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/Tlb.h
@@ -0,0 +1,48 @@
+/** @file
+
+ Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef TLB_H_
+#define TLB_H_
+
+/**
+ Invalid corresponding TLB entries are based on the address given
+
+ @param Address The address corresponding to the invalid page table entry
+
+ @retval none
+**/
+VOID
+InvalidTlb (
+ UINTN Address
+ );
+
+/**
+ TLB refill handler start.
+
+ @param none
+
+ @retval none
+**/
+VOID
+HandleTlbRefillStart (
+ VOID
+ );
+
+/**
+ TLB refill handler end.
+
+ @param none
+
+ @retval none
+**/
+VOID
+HandleTlbRefillEnd (
+ VOID
+ );
+
+#endif // TLB_H_
diff --git a/UefiCpuPkg/Library/LoongArch64CpuMmuLib/TlbOperation.S b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/TlbOperation.S
new file mode 100644
index 0000000000..c9a8c16336
--- /dev/null
+++ b/UefiCpuPkg/Library/LoongArch64CpuMmuLib/TlbOperation.S
@@ -0,0 +1,44 @@
+#------------------------------------------------------------------------------
+#
+# TLB operation functions
+#
+# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#-----------------------------------------------------------------------------
+
+#include <Register/LoongArch64/Csr.h>
+
+ASM_GLOBAL ASM_PFX(HandleTlbRefillStart)
+ASM_GLOBAL ASM_PFX(HandleTlbRefillEnd)
+ASM_GLOBAL ASM_PFX(InvalidTlb)
+
+#
+# Refill the page table.
+# @param VOID
+# @retval VOID
+#
+ASM_PFX(HandleTlbRefillStart):
+ csrwr $t0, LOONGARCH_CSR_TLBRSAVE
+ csrrd $t0, LOONGARCH_CSR_PGD
+ lddir $t0, $t0, 3 #Put pud BaseAddress into T0
+ lddir $t0, $t0, 2 #Put pmd BaseAddress into T0
+ lddir $t0, $t0, 1 #Put pte BaseAddress into T0
+ ldpte $t0, 0
+ ldpte $t0, 1
+ tlbfill // refill hi,lo0,lo1
+ csrrd $t0, LOONGARCH_CSR_TLBRSAVE
+ ertn
+ASM_PFX(HandleTlbRefillEnd):
+
+#
+# Invalid corresponding TLB entries are based on the address given
+# @param a0 The address corresponding to the invalid page table entry
+# @retval none
+#
+ASM_PFX(InvalidTlb):
+ invtlb INVTLB_ADDR_GTRUE_OR_ASID, $zero, $a0
+ jirl $zero, $ra, 0
+
+ .end
diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
index 0b8ea858b2..738013ec0c 100644
--- a/UefiCpuPkg/UefiCpuPkg.dsc
+++ b/UefiCpuPkg/UefiCpuPkg.dsc
@@ -211,6 +211,8 @@
UefiCpuPkg/Library/BaseLoongArch64CpuTimerLib/BaseLoongArch64CpuTimerLib.inf
UefiCpuPkg/Library/LoongArch64CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf
UefiCpuPkg/Library/LoongArch64CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf
+ UefiCpuPkg/Library/LoongArch64CpuMmuLib/PeiCpuMmuLib.inf
+ UefiCpuPkg/Library/LoongArch64CpuMmuLib/DxeCpuMmuLib.inf
[BuildOptions]
*_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
--
2.27.0
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#113247): https://edk2.groups.io/g/devel/message/113247
Mute This Topic: https://groups.io/mt/103540106/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
next prev parent reply other threads:[~2024-01-05 9:44 UTC|newest]
Thread overview: 59+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-01-05 9:41 [edk2-devel] [PATCH v6 00/36] Enable LoongArch virtual machine in edk2 Chao Li
2024-01-05 9:42 ` [edk2-devel] [PATCH v6 01/36] MdePkg: Add the header file named Csr.h for LoongArch64 Chao Li
2024-01-05 9:42 ` [edk2-devel] [PATCH v6 02/36] MdePkg: Add LoongArch64 FPU function set into BaseCpuLib Chao Li
2024-01-05 9:42 ` [edk2-devel] [PATCH v6 03/36] MdePkg: Add LoongArch64 exception function set into BaseLib Chao Li
2024-01-05 9:42 ` [edk2-devel] [PATCH v6 04/36] MdePkg: Add LoongArch64 local interrupt " Chao Li
2024-01-05 9:42 ` [edk2-devel] [PATCH v6 05/36] MdePkg: Add LoongArch Cpucfg function Chao Li
2024-01-05 9:43 ` [edk2-devel] [PATCH v6 06/36] MdePkg: Add read stable counter operation for LoongArch Chao Li
2024-01-05 9:43 ` [edk2-devel] [PATCH v6 07/36] MdePkg: Add CSR " Chao Li
2024-01-05 9:43 ` [edk2-devel] [PATCH v6 08/36] MdePkg: Add IOCSR " Chao Li
2024-01-05 9:43 ` [edk2-devel] [PATCH v6 09/36] MdePkg: Add a new library named PeiServicesTablePointerLibKs0 Chao Li
2024-01-05 9:43 ` [edk2-devel] [PATCH v6 10/36] UefiCpuPkg: Add LoongArch64 CPU Timer library Chao Li
2024-01-05 9:43 ` [edk2-devel] [PATCH v6 11/36] UefiCpuPkg: Add CPU exception library for LoongArch Chao Li
2024-01-05 9:43 ` [edk2-devel] [PATCH v6 12/36] UefiCpuPkg: Add CpuMmuLib.h to UefiCpuPkg Chao Li
2024-01-05 12:49 ` Ni, Ray
2024-01-05 9:43 ` Chao Li [this message]
2024-01-05 12:50 ` [edk2-devel] [PATCH v6 13/36] UefiCpuPkg: Add LoongArch64CpuMmuLib " Ni, Ray
2024-01-05 9:44 ` [edk2-devel] [PATCH v6 14/36] UefiCpuPkg: Add multiprocessor library for LoongArch64 Chao Li
2024-01-05 9:44 ` [edk2-devel] [PATCH v6 15/36] UefiCpuPkg: Add CpuDxe driver " Chao Li
2024-01-05 9:44 ` [edk2-devel] [PATCH v6 16/36] EmbeddedPkg: Add PcdPrePiCpuIoSize width for LOONGARCH64 Chao Li
2024-01-05 9:44 ` [edk2-devel] [PATCH v6 17/36] ArmVirtPkg: Move PCD of FDT base address and FDT padding to OvmfPkg Chao Li
2024-01-05 9:44 ` [edk2-devel] [PATCH v6 18/36] UefiCpuPkg: Add a new CPU IO 2 driver named CpuMmio2Dxe Chao Li
2024-01-06 3:20 ` Ni, Ray
2024-01-05 9:44 ` [edk2-devel] [PATCH v6 19/36] ArmVirtPkg: Enable CpuMmio2Dxe Chao Li
2024-01-05 9:44 ` [edk2-devel] [PATCH v6 20/36] OvmfPkg/RiscVVirt: " Chao Li
2024-01-05 9:44 ` [edk2-devel] [PATCH v6 21/36] OvmfPkg/RiscVVirt: Remove PciCpuIo2Dxe from RiscVVirt Chao Li
2024-01-05 9:44 ` [edk2-devel] [PATCH v6 22/36] ArmVirtPkg: Move the FdtSerialPortAddressLib to OvmfPkg Chao Li
2024-01-05 9:45 ` [edk2-devel] [PATCH v6 23/36] ArmVirtPkg: Move two PCD variables into OvmfPkg Chao Li
2024-01-05 9:45 ` [edk2-devel] [PATCH v6 24/36] ArmVirtPkg: Move PlatformBootManagerLib to OvmfPkg Chao Li
2024-01-08 14:02 ` Laszlo Ersek
2024-01-09 6:40 ` Chao Li
2024-01-09 8:00 ` Laszlo Ersek
2024-01-05 9:45 ` [edk2-devel] [PATCH v6 25/36] OvmfPkg/LoongArchVirt: Add stable timer driver Chao Li
2024-01-12 7:05 ` maobibo
2024-01-05 9:45 ` [edk2-devel] [PATCH v6 26/36] OvmfPkg/LoongArchVirt: Add a NULL library named CollectApResouceLibNull Chao Li
2024-01-10 1:24 ` maobibo
2024-01-10 2:47 ` Chao Li
2024-01-10 9:35 ` maobibo
2024-01-05 9:45 ` [edk2-devel] [PATCH v6 27/36] OvmfPkg/LoongArchVirt: Add serial port hook library Chao Li
2024-01-05 9:45 ` [edk2-devel] [PATCH v6 28/36] OvmfPkg/LoongArchVirt: Add the early serial port output library Chao Li
2024-01-10 1:25 ` maobibo
2024-01-05 9:46 ` [edk2-devel] [PATCH v6 29/36] OvmfPkg/LoongArchVirt: Add real time clock library Chao Li
2024-01-05 9:46 ` [edk2-devel] [PATCH v6 30/36] OvmfPkg/LoongArchVirt: Add NorFlashQemuLib Chao Li
2024-01-10 1:26 ` maobibo
2024-01-05 9:46 ` [edk2-devel] [PATCH v6 31/36] OvmfPkg/LoongArchVirt: Add FdtQemuFwCfgLib Chao Li
2024-01-10 1:27 ` maobibo
2024-01-05 9:46 ` [edk2-devel] [PATCH v6 32/36] OvmfPkg/LoongArchVirt: Add reset system library Chao Li
2024-01-05 9:46 ` [edk2-devel] [PATCH v6 33/36] OvmfPkg/LoongArchVirt: Support SEC phase Chao Li
2024-01-08 6:51 ` maobibo
2024-01-05 9:46 ` [edk2-devel] [PATCH v6 34/36] OvmfPkg/LoongArchVirt: Support PEI phase Chao Li
2024-01-05 9:46 ` [edk2-devel] [PATCH v6 35/36] OvmfPkg/LoongArchVirt: Add build file Chao Li
2024-01-10 1:28 ` maobibo
2024-01-05 9:46 ` [edk2-devel] [PATCH v6 36/36] OvmfPkg/LoongArchVirt: Add self introduction file Chao Li
2024-01-10 1:28 ` maobibo
2024-01-08 1:35 ` [edk2-devel] 回复: [PATCH v6 00/36] Enable LoongArch virtual machine in edk2 gaoliming via groups.io
2024-01-08 2:41 ` Chao Li
[not found] ` <17A76A50519959EC.16812@groups.io>
2024-01-08 3:21 ` [edk2-devel] [PATCH v6 19/36] ArmVirtPkg: Enable CpuMmio2Dxe Chao Li
[not found] ` <17A76A543E440C35.16812@groups.io>
2024-01-08 3:24 ` [edk2-devel] [PATCH v6 22/36] ArmVirtPkg: Move the FdtSerialPortAddressLib to OvmfPkg Chao Li
[not found] ` <17A76A5F07C7435C.16812@groups.io>
2024-01-08 3:24 ` [edk2-devel] [PATCH v6 23/36] ArmVirtPkg: Move two PCD variables into OvmfPkg Chao Li
[not found] ` <17A76A601F9A93F4.25044@groups.io>
2024-01-08 3:25 ` [edk2-devel] [PATCH v6 24/36] ArmVirtPkg: Move PlatformBootManagerLib to OvmfPkg Chao Li
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240105094353.2280431-1-lichao@loongson.cn \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox