summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChao Li <lichao@loongson.cn>2024-04-12 11:27:14 +0800
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>2024-04-16 06:00:32 +0000
commit032830e96841f2a752e364378c3428ac5d2f59d1 (patch)
treef74bf782227ae40dff1c73d5a058d4711d0c2be5
parentc5fb47ddab4f6daa9c3c5437b408576480351420 (diff)
downloadedk2-032830e96841f2a752e364378c3428ac5d2f59d1.tar.gz
edk2-032830e96841f2a752e364378c3428ac5d2f59d1.tar.bz2
edk2-032830e96841f2a752e364378c3428ac5d2f59d1.zip
UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg
Add a new base library named CpuMmuLib and add a LoongArch64 instance with in the library. BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4734 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> Acked-by: Gerd Hoffmann <kraxel@redhat.com> Acked-by: Ray Ni <ray.ni@intel.com>
-rw-r--r--UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.inf39
-rw-r--r--UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.uni14
-rw-r--r--UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CpuMmu.c784
-rw-r--r--UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h33
-rw-r--r--UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.S51
-rw-r--r--UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.h36
-rw-r--r--UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.S24
-rw-r--r--UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.h24
-rw-r--r--UefiCpuPkg/UefiCpuPkg.dsc3
9 files changed, 1008 insertions, 0 deletions
diff --git a/UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.inf
new file mode 100644
index 0000000000..5eecfb4838
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.inf
@@ -0,0 +1,39 @@
+## @file
+# CPU Memory Manager Unit library instance.
+#
+# 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 = CpuMmuLib
+ MODULE_UNI_FILE = CpuMmuLib.uni
+ FILE_GUID = DA8F0232-FB14-42F0-922C-63104D2C70BE
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = CpuMmuLib
+
+#
+# VALID_ARCHITECTURES = LOONGARCH64
+#
+
+[Sources.LoongArch64]
+ LoongArch64/TlbInvalid.S | GCC
+ LoongArch64/TlbExceptionHandle.S | GCC
+ LoongArch64/CpuMmu.c
+ LoongArch64/Page.h
+ LoongArch64/TlbInvalid.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+ DebugLib
+ MemoryAllocationLib
+
+[Pcd.LoongArch64]
+ gUefiCpuPkgTokenSpaceGuid.PcdLoongArchExceptionVectorBaseAddress ## CONSUMES
diff --git a/UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.uni
new file mode 100644
index 0000000000..2408f2f90b
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.uni
@@ -0,0 +1,14 @@
+// /** @file
+// CPU Memory Manager Unit library instance.
+//
+// CPU Memory Manager Unit library instance.
+//
+// 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."
+
+#string STR_MODULE_DESCRIPTION #language en-US "CPU Memory Manager Unit library instance."
diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CpuMmu.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CpuMmu.c
new file mode 100644
index 0000000000..8f8254362f
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CpuMmu.c
@@ -0,0 +1,784 @@
+/** @file
+
+ CPU Memory Map Unit Handler Library common functions.
+
+ Copyright (c) 2011-2020, ARM Limited. All rights reserved.
+ Copyright (c) 2016, Linaro Limited. All rights reserved.
+ Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
+ Copyright (c) 2023, Ventana Micro Systems Inc. All Rights Reserved.<BR>
+ Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#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 <Protocol/DebugSupport.h>
+#include <Register/LoongArch64/Csr.h>
+#include "TlbInvalid.h"
+#include "TlbExceptionHandle.h"
+#include "Page.h"
+
+/**
+ 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 (CsrRead (LOONGARCH_CSR_PGDL) != 0) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Check to see if mmu is enabled.
+
+ @param VOID.
+
+ @retval TRUE MMU has been enabled.
+ FALSE MMU did not enabled.
+**/
+STATIC
+BOOLEAN
+MmuIsEnabled (
+ VOID
+ )
+{
+ if ((CsrRead (LOONGARCH_CSR_CRMD) & BIT4) != 0) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Determine if an entry is valid pte.
+
+ @param Entry The entry value.
+
+ @retval TRUE The entry is a valid pte.
+ @retval FALSE The entry is not a valid pte.
+
+**/
+STATIC
+BOOLEAN
+IsValidPte (
+ IN UINTN Entry
+ )
+{
+ if (Entry != INVALID_PAGE) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+/**
+ Determine if an entry is huge page.
+
+ @param Entry The entry value.
+
+ @retval TRUE The entry is a huge page.
+ @retval FALSE The entry is not a valid huge page.
+
+**/
+STATIC
+BOOLEAN
+IsValidHugePage (
+ IN UINTN Entry
+ )
+{
+ if ((Entry & (PAGE_HGLOBAL | PAGE_HUGE)) == (PAGE_HGLOBAL | PAGE_HUGE)) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+/**
+ Set an entry to be a valid pte.
+
+ @param Entry The entry value.
+
+ @return The entry value.
+
+**/
+STATIC
+UINTN
+SetValidPte (
+ IN UINTN Entry
+ )
+{
+ /* Set Valid and Global mapping bits */
+ return Entry | PAGE_GLOBAL | PAGE_VALID;
+}
+
+/**
+ Parse max page table level.
+
+ @param[in] PageWalkCfg Page table configure value.
+
+ @return 5 MAX page level is 5
+ 4 MAX page level is 4
+ 3 MAX page level is 3
+ 0 Invalid
+**/
+STATIC
+UINTN
+ParseMaxPageTableLevel (
+ IN UINT64 PageWalkCfg
+ )
+{
+ UINT32 Pwctl0;
+ UINT32 Pwctl1;
+
+ Pwctl0 = PageWalkCfg & MAX_UINT32;
+ Pwctl1 = (PageWalkCfg >> 32) & MAX_UINT32;
+
+ if (((Pwctl1 >> 18) & 0x3F) != 0x0) {
+ return LEVEL5;
+ } else if (((Pwctl1 >> 6) & 0x3F) != 0x0) {
+ return LEVEL4;
+ } else if (((Pwctl0 >> 25) & 0x3F) != 0x0) {
+ return LEVEL3;
+ }
+
+ return 0;
+}
+
+/**
+ Parse page table bit width.
+
+ Assume that the bit width of the page table that each level is the same to PTwidth.
+
+ @param[in] PageWalkCfg Page table configure value.
+
+ @return page table bit width
+
+**/
+STATIC
+UINTN
+ParsePageTableBitWidth (
+ IN UINT64 PageWalkCfg
+ )
+{
+ //
+ // PTwidth
+ //
+ return ((PageWalkCfg >> 5) & 0x1F);
+}
+
+/**
+ Determine if an entry is a HUGE PTE or 4K PTE.
+
+ @param Entry The entry value.
+ @param Level The current page table level.
+ @param PageWalkCfg Page table configure value.
+
+ @retval TRUE The entry is a block pte.
+ @retval FALSE The entry is not a block pte.
+
+**/
+STATIC
+BOOLEAN
+IsBlockEntry (
+ IN UINTN Entry,
+ IN UINTN Level,
+ IN UINT64 PageWalkCfg
+ )
+{
+ if (Level == (ParseMaxPageTableLevel (PageWalkCfg) - 1)) {
+ return ((Entry & PAGE_VALID) == PAGE_VALID);
+ }
+
+ return IsValidHugePage (Entry);
+}
+
+/**
+ Determine if an entry is a table pte.
+
+ @param Entry The entry value.
+ @param Level The current page table level.
+ @param PageWalkCfg Page table configure value.
+
+ @retval TRUE The entry is a table pte.
+ @retval FALSE The entry is not a table pte.
+
+**/
+STATIC
+BOOLEAN
+IsTableEntry (
+ IN UINTN Entry,
+ IN UINTN Level,
+ IN UINT64 PageWalkCfg
+ )
+{
+ if (Level == (ParseMaxPageTableLevel (PageWalkCfg) - 1)) {
+ //
+ // The last level is PAGE rather than Table.
+ //
+ return FALSE;
+ }
+
+ //
+ // Is DIR4 or DIR3 or DIR2 a Huge Page ?
+ //
+ return (!IsValidHugePage (Entry)) && (IsValidPte (Entry));
+}
+
+/**
+ Replace an existing entry with new value.
+
+ @param Entry The entry pointer.
+ @param Value The new entry value.
+ @param RegionStart The start of region that new value affects.
+ @param IsLiveBlockMapping TRUE if this is live update, FALSE otherwise.
+
+**/
+STATIC
+VOID
+ReplaceTableEntry (
+ IN UINTN *Entry,
+ IN UINTN Value,
+ IN UINTN RegionStart,
+ IN BOOLEAN IsLiveBlockMapping
+ )
+{
+ *Entry = Value;
+
+ if (IsLiveBlockMapping && MmuIsInit ()) {
+ InvalidTlb (RegionStart);
+ }
+}
+
+/**
+ Get an ppn value from an entry.
+
+ @param Entry The entry value.
+
+ @return The ppn value.
+
+**/
+STATIC
+UINTN
+GetPpnfromPte (
+ IN UINTN Entry
+ )
+{
+ return ((Entry & PTE_PPN_MASK) >> PTE_PPN_SHIFT);
+}
+
+/**
+ Set an ppn value to a entry.
+
+ @param Entry The entry value.
+ @param Address The address.
+
+ @return The new entry value.
+
+**/
+STATIC
+UINTN
+SetPpnToPte (
+ UINTN Entry,
+ UINTN Address
+ )
+{
+ UINTN Ppn;
+
+ Ppn = ((Address >> LOONGARCH_MMU_PAGE_SHIFT) << PTE_PPN_SHIFT);
+ ASSERT (~(Ppn & ~PTE_PPN_MASK));
+ Entry &= ~PTE_PPN_MASK;
+ return Entry | Ppn;
+}
+
+/**
+ Free resources of translation table recursively.
+
+ @param TranslationTable The pointer of table.
+ @param PageWalkCfg Page table configure value.
+ @param Level The current level.
+
+**/
+STATIC
+VOID
+FreePageTablesRecursive (
+ IN UINTN *TranslationTable,
+ IN UINT64 PageWalkCfg,
+ IN UINTN Level
+ )
+{
+ UINTN Index;
+ UINTN TableEntryNum;
+
+ TableEntryNum = (1 << ParsePageTableBitWidth (PageWalkCfg));
+
+ if (Level < (ParseMaxPageTableLevel (PageWalkCfg) - 1)) {
+ for (Index = 0; Index < TableEntryNum; Index++) {
+ if (IsTableEntry (TranslationTable[Index], Level, PageWalkCfg)) {
+ FreePageTablesRecursive (
+ (UINTN *)(GetPpnfromPte ((TranslationTable[Index])) <<
+ LOONGARCH_MMU_PAGE_SHIFT),
+ PageWalkCfg,
+ Level + 1
+ );
+ }
+ }
+ }
+
+ FreePages (TranslationTable, 1);
+}
+
+/**
+ Update region mapping recursively.
+
+ @param RegionStart The start address of the region.
+ @param RegionEnd The end address of the region.
+ @param AttributeSetMask The attribute mask to be set.
+ @param AttributeClearMask The attribute mask to be clear.
+ @param PageTable The pointer of current page table.
+ @param Level The current level.
+ @param PageWalkCfg Page table configure value.
+ @param TableIsLive TRUE if this is live update, FALSE otherwise.
+
+ @retval EFI_OUT_OF_RESOURCES Not enough resource.
+ @retval EFI_SUCCESS The operation succesfully.
+
+**/
+STATIC
+EFI_STATUS
+UpdateRegionMappingRecursive (
+ IN UINTN RegionStart,
+ IN UINTN RegionEnd,
+ IN UINTN AttributeSetMask,
+ IN UINTN AttributeClearMask,
+ IN UINTN *PageTable,
+ IN UINTN Level,
+ IN UINT64 PageWalkCfg,
+ IN BOOLEAN TableIsLive
+ )
+{
+ EFI_STATUS Status;
+ UINTN BlockShift;
+ UINTN BlockMask;
+ UINTN BlockEnd;
+ UINTN *Entry;
+ UINTN EntryValue;
+ UINTN *TranslationTable;
+ UINTN TableEntryNum;
+ UINTN TableBitWidth;
+ BOOLEAN NextTableIsLive;
+
+ ASSERT (Level < ParseMaxPageTableLevel (PageWalkCfg));
+ ASSERT (((RegionStart | RegionEnd) & EFI_PAGE_MASK) == 0);
+
+ TableBitWidth = ParsePageTableBitWidth (PageWalkCfg);
+ BlockShift = (ParseMaxPageTableLevel (PageWalkCfg) - Level - 1) * TableBitWidth + LOONGARCH_MMU_PAGE_SHIFT;
+ BlockMask = MAX_ADDRESS >> (64 - BlockShift);
+
+ DEBUG (
+ (
+ DEBUG_VERBOSE,
+ "%a(%d): %llx - %llx set %lx clr %lx\n",
+ __func__,
+ Level,
+ RegionStart,
+ RegionEnd,
+ AttributeSetMask,
+ AttributeClearMask
+ )
+ );
+
+ TableEntryNum = (1 << TableBitWidth);
+ for ( ; RegionStart < RegionEnd; RegionStart = BlockEnd) {
+ BlockEnd = MIN (RegionEnd, (RegionStart | BlockMask) + 1);
+ Entry = &PageTable[(RegionStart >> BlockShift) & (TableEntryNum - 1)];
+
+ //
+ // If RegionStart or BlockEnd is not aligned to the block size at this
+ // level, we will have to create a table mapping in order to map less
+ // than a block, and recurse to create the block or page entries at
+ // the next level. No block mappings are allowed at all at level 2,
+ // so in that case, we have to recurse unconditionally.
+ //
+ if ((Level < 2) ||
+ (((RegionStart | BlockEnd) & BlockMask) != 0) || IsTableEntry (*Entry, Level, PageWalkCfg))
+ {
+ ASSERT (Level < (ParseMaxPageTableLevel (PageWalkCfg) - 1));
+ if (!IsTableEntry (*Entry, Level, PageWalkCfg)) {
+ //
+ // No table entry exists yet, so we need to allocate a page table
+ // for the next level.
+ //
+ TranslationTable = AllocatePages (1);
+ if (TranslationTable == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ZeroMem (TranslationTable, EFI_PAGE_SIZE);
+
+ if (IsBlockEntry (*Entry, Level, PageWalkCfg)) {
+ //
+ // We are splitting an existing block entry, so we have to populate
+ // the new table with the attributes of the block entry it replaces.
+ //
+ Status = UpdateRegionMappingRecursive (
+ RegionStart & ~BlockMask,
+ (RegionStart | BlockMask) + 1,
+ *Entry & PTE_ATTRIBUTES_MASK,
+ PTE_ATTRIBUTES_MASK,
+ TranslationTable,
+ Level + 1,
+ PageWalkCfg,
+ FALSE
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // The range we passed to UpdateRegionMappingRecursive () is block
+ // aligned, so it is guaranteed that no further pages were allocated
+ // by it, and so we only have to free the page we allocated here.
+ //
+ FreePages (TranslationTable, 1);
+ return Status;
+ }
+ }
+
+ NextTableIsLive = FALSE;
+ } else {
+ TranslationTable = (UINTN *)(GetPpnfromPte (*Entry) << LOONGARCH_MMU_PAGE_SHIFT);
+ NextTableIsLive = TableIsLive;
+ }
+
+ //
+ // Recurse to the next level
+ //
+ Status = UpdateRegionMappingRecursive (
+ RegionStart,
+ BlockEnd,
+ AttributeSetMask,
+ AttributeClearMask,
+ TranslationTable,
+ Level + 1,
+ PageWalkCfg,
+ NextTableIsLive
+ );
+ if (EFI_ERROR (Status)) {
+ if (!IsTableEntry (*Entry, Level, PageWalkCfg)) {
+ //
+ // We are creating a new table entry, so on failure, we can free all
+ // allocations we made recursively, given that the whole subhierarchy
+ // has not been wired into the live page tables yet. (This is not
+ // possible for existing table entries, since we cannot revert the
+ // modifications we made to the subhierarchy it represents.)
+ //
+ FreePageTablesRecursive (TranslationTable, PageWalkCfg, Level + 1);
+ }
+
+ return Status;
+ }
+
+ if (!IsTableEntry (*Entry, Level, PageWalkCfg)) {
+ EntryValue = SetPpnToPte (0, (UINTN)TranslationTable);
+ ReplaceTableEntry (
+ Entry,
+ EntryValue,
+ RegionStart,
+ TableIsLive
+ );
+ }
+ } else {
+ EntryValue = (*Entry & ~AttributeClearMask) | AttributeSetMask;
+
+ EntryValue = SetPpnToPte (EntryValue, RegionStart);
+ EntryValue = SetValidPte (EntryValue);
+
+ if (Level < (ParseMaxPageTableLevel (PageWalkCfg) - 1)) {
+ EntryValue |= (PAGE_HGLOBAL | PAGE_HUGE | PAGE_VALID);
+ } else {
+ EntryValue |= PAGE_GLOBAL | PAGE_VALID;
+ }
+
+ ReplaceTableEntry (Entry, EntryValue, RegionStart, TableIsLive);
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Update region mapping at root table.
+
+ @param RegionStart The start address of the region.
+ @param RegionLength The length of the region.
+ @param PageWalkCfg Page table configure value.
+ @param AttributeSetMask The attribute mask to be set.
+ @param AttributeClearMask The attribute mask to be clear.
+ @param RootTable The pointer of root table.
+ @param TableIsLive TRUE if this is live update, FALSE otherwise.
+
+ @retval EFI_INVALID_PARAMETER The RegionStart or RegionLength was not valid.
+ @retval EFI_OUT_OF_RESOURCES Not enough resource.
+ @retval EFI_SUCCESS The operation succesfully.
+
+**/
+EFI_STATUS
+UpdateRegionMapping (
+ IN UINTN RegionStart,
+ IN UINTN RegionLength,
+ IN UINT64 PageWalkCfg,
+ IN UINTN AttributeSetMask,
+ IN UINTN AttributeClearMask,
+ IN UINTN *RootTable,
+ IN BOOLEAN TableIsLive
+ )
+{
+ if (((RegionStart | RegionLength) & EFI_PAGE_MASK) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return UpdateRegionMappingRecursive (
+ RegionStart,
+ RegionStart + RegionLength,
+ AttributeSetMask,
+ AttributeClearMask,
+ RootTable,
+ 0,
+ PageWalkCfg,
+ TableIsLive
+ );
+}
+
+/**
+ Convert EFI Attributes to Loongarch Attributes.
+
+ @param[in] EfiAttributes Efi Attributes.
+
+ @retval Corresponding architecture attributes.
+**/
+UINT64
+EFIAPI
+EfiAttributeConverse (
+ IN UINT64 EfiAttributes
+ )
+{
+ UINT64 LoongArchAttributes;
+
+ LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | PLV_KERNEL | PAGE_GLOBAL;
+
+ switch (EfiAttributes & EFI_CACHE_ATTRIBUTE_MASK) {
+ case EFI_MEMORY_UC:
+ LoongArchAttributes |= CACHE_SUC;
+ break;
+ case EFI_MEMORY_WC:
+ LoongArchAttributes |= CACHE_WUC;
+ break;
+ case EFI_MEMORY_WT:
+ case EFI_MEMORY_WB:
+ LoongArchAttributes |= CACHE_CC;
+ break;
+ case EFI_MEMORY_WP:
+ LoongArchAttributes &= ~PAGE_DIRTY;
+ break;
+ default:
+ break;
+ }
+
+ // Write protection attributes
+ switch (EfiAttributes & EFI_MEMORY_ACCESS_MASK) {
+ case EFI_MEMORY_RP:
+ LoongArchAttributes |= PAGE_NO_READ;
+ break;
+ case EFI_MEMORY_XP:
+ LoongArchAttributes |= PAGE_NO_EXEC;
+ break;
+ case EFI_MEMORY_RO:
+ LoongArchAttributes &= ~PAGE_DIRTY;
+ break;
+ default:
+ break;
+ }
+
+ return LoongArchAttributes;
+}
+
+/**
+ TLB refill handler configure.
+
+ @param VOID.
+
+ @retval EFI_SUCCESS TLB refill handler configure successfully.
+ EFI_UNSUPPORTED Size not aligned.
+**/
+EFI_STATUS
+TlbRefillHandlerConfigure (
+ VOID
+ )
+{
+ UINTN Length;
+ UINTN TlbReEntry;
+ UINTN TlbReEntryOffset;
+ UINTN Remaining;
+
+ //
+ // Set TLB exception handler
+ //
+ ///
+ /// 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 (PcdLoongArchExceptionVectorBaseAddress) + TlbReEntryOffset;
+ if ((TlbReEntryOffset + Length) > SIZE_64KB) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Ensure that TLB refill exception base address alignment is equals to 4KB and is valid.
+ //
+ if (TlbReEntry & (SIZE_4KB - 1)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ CopyMem ((VOID *)TlbReEntry, HandleTlbRefillStart, Length);
+ InvalidateInstructionCacheRange ((VOID *)(UINTN)HandleTlbRefillStart, Length);
+
+ //
+ // Set the address of TLB refill exception handler
+ //
+ SetTlbRebaseAddress ((UINTN)TlbReEntry);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Maps the memory region in the page table to the specified attributes.
+
+ @param[in, out] PageTable The pointer to the page table to update, or pointer to NULL
+ if a new page table is to be created.
+ @param[in] PageWalkCfg The page walk controller configure.
+ @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 bitmask of attributes to set, which refer to UEFI SPEC
+ 7.2.3(EFI_BOOT_SERVICES.GetMemoryMap()).
+ @param[in] AttributeMask Mask of memory attributes to take into account.
+
+ @retval EFI_SUCCESS The Attributes was set successfully or Length is 0.
+ @retval EFI_INVALID_PARAMETER PageTable is NULL, PageWalkCfg is invalid.
+ @retval EFI_UNSUPPORTED *PageTable is NULL.
+**/
+EFI_STATUS
+EFIAPI
+MemoryRegionMap (
+ IN OUT UINTN *PageTable OPTIONAL,
+ IN UINT64 PageWalkCfg,
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 Attributes,
+ IN UINT64 AttributeMask
+ )
+{
+ EFI_STATUS Status;
+ UINT64 LoongArchAttributes;
+ BOOLEAN Initialization;
+ BOOLEAN CreateNew;
+ UINTN PageTableBitWidth;
+ UINTN MaxLevel;
+ UINTN PgdSize;
+
+ if ((PageTable == NULL) || (PageWalkCfg == 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ PageTableBitWidth = ParsePageTableBitWidth (PageWalkCfg);
+ MaxLevel = ParseMaxPageTableLevel (PageWalkCfg);
+
+ if ((!PageTableBitWidth && !MaxLevel) || (PageTableBitWidth > 0x1F) || (MaxLevel > LEVEL5)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Initialization = FALSE;
+ CreateNew = FALSE;
+
+ //
+ // *PageTable is NULL, create a new and return.
+ //
+ if (*PageTable == 0) {
+ CreateNew = TRUE;
+ //
+ // If the MMU has not been configured yet, configure it later.
+ //
+ if (!MmuIsInit ()) {
+ Initialization = TRUE;
+ }
+ }
+
+ if (Length == 0) {
+ return EFI_SUCCESS;
+ }
+
+ if (Initialization == TRUE) {
+ Status = TlbRefillHandlerConfigure ();
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ if (CreateNew == TRUE) {
+ //
+ // Create a new page table.
+ //
+ PgdSize = (1 << PageTableBitWidth) * sizeof (UINTN);
+ *PageTable = (UINTN)AllocatePages (EFI_SIZE_TO_PAGES (PgdSize));
+ ZeroMem ((VOID *)*PageTable, PgdSize);
+
+ if ((VOID *)*PageTable == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ LoongArchAttributes = EfiAttributeConverse (Attributes);
+
+ //
+ // Update the page table attributes.
+ //
+ // If the MMU has been configured and *PageTable == CSR_PGDL, the page table in use will update.
+ //
+ // If *PageTable != CSR_PGDL, only the page table structure in memory is update, but some TLB
+ // region may be invalidated during the mapping process. So at this time the caller must ensure
+ // that the execution environment must be safe. It is recommended to use the DA mode!
+ //
+ Status = UpdateRegionMapping (
+ BaseAddress,
+ Length,
+ PageWalkCfg,
+ LoongArchAttributes,
+ PTE_ATTRIBUTES_MASK,
+ (UINTN *)(*PageTable),
+ (MmuIsEnabled () && !CreateNew)
+ );
+
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
new file mode 100644
index 0000000000..b115245d68
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
@@ -0,0 +1,33 @@
+/** @file
+
+ Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef PAGE_H_
+#define PAGE_H_
+
+#define INVALID_PAGE 0
+
+#define LEVEL5 5
+#define LEVEL4 4
+#define LEVEL3 3
+#define LEVEL2 2
+#define LEVEL1 1
+
+#define PTE_ATTRIBUTES_MASK 0x600000000000007EULL
+
+#define PTE_PPN_MASK 0xFFFFFFFFF000ULL
+#define PTE_PPN_SHIFT EFI_PAGE_SHIFT
+#define LOONGARCH_MMU_PAGE_SHIFT EFI_PAGE_SHIFT
+
+//
+// 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
+
+#endif // PAGE_H_
diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.S b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.S
new file mode 100644
index 0000000000..4395b574f5
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.S
@@ -0,0 +1,51 @@
+#------------------------------------------------------------------------------
+#
+# TLB refill exception handler
+#
+# 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)
+
+#
+# Refill the page table.
+# @param VOID
+# @retval VOID
+#
+ASM_PFX(HandleTlbRefillStart):
+ csrwr $t0, LOONGARCH_CSR_TLBRSAVE
+ csrrd $t0, LOONGARCH_CSR_PWCTL1
+ srli.d $t0, $t0, 18
+ andi $t0, $t0, 0x3F
+ bnez $t0, Level5
+ csrrd $t0, LOONGARCH_CSR_PWCTL1
+ srli.d $t0, $t0, 6
+ andi $t0, $t0, 0x3F
+ bnez $t0, Level4
+ csrrd $t0, LOONGARCH_CSR_PGD
+ b Level3
+Level5:
+ csrrd $t0, LOONGARCH_CSR_PGD
+ lddir $t0, $t0, 4 #Put pud BaseAddress into T0
+ lddir $t0, $t0, 3 #Put pud BaseAddress into T0
+ b Level3
+Level4:
+ csrrd $t0, LOONGARCH_CSR_PGD
+ lddir $t0, $t0, 3 #Put pud BaseAddress into T0
+Level3:
+ 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):
+
+ .end
diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.h
new file mode 100644
index 0000000000..c164db567d
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.h
@@ -0,0 +1,36 @@
+/** @file
+
+ Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef TLB_EXCEPTION_HANDLE_H_
+#define TLB_EXCEPTION_HANDLE_H_
+
+/**
+ TLB refill handler start.
+
+ @param none
+
+ @retval none
+**/
+VOID
+HandleTlbRefillStart (
+ VOID
+ );
+
+/**
+ TLB refill handler end.
+
+ @param none
+
+ @retval none
+**/
+VOID
+HandleTlbRefillEnd (
+ VOID
+ );
+
+#endif // TLB_EXCEPTION_HANDLE_H_
diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.S b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.S
new file mode 100644
index 0000000000..676aada240
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.S
@@ -0,0 +1,24 @@
+#------------------------------------------------------------------------------
+#
+# Invalid TLB operation function
+#
+# 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(InvalidTlb)
+
+#
+# Invalid corresponding TLB entries are based on the address given
+# @param a0 The address corresponding to the invalid page table entry
+# @retval none
+#
+ASM_PFX(InvalidTlb):
+ invtlb INVTLB_ADDR_GTRUE_OR_ASID, $zero, $a0
+ jirl $zero, $ra, 0
+
+ .end
diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.h
new file mode 100644
index 0000000000..5063e6662b
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.h
@@ -0,0 +1,24 @@
+/** @file
+
+ Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef INVALID_TLB_H_
+#define INVALID_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
+ );
+
+#endif // INVALID_TLB_H_
diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
index 2eebd45125..e92ceb6466 100644
--- a/UefiCpuPkg/UefiCpuPkg.dsc
+++ b/UefiCpuPkg/UefiCpuPkg.dsc
@@ -211,5 +211,8 @@
UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf
UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf
+[Components.LOONGARCH64]
+ UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.inf
+
[BuildOptions]
*_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES