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...@intel.com>
Cc: Rahul Kumar <rahul1.ku...@intel.com>
Cc: Gerd Hoffmann <kra...@redhat.com>
Signed-off-by: Chao Li <lic...@loongson.cn>
Co-authored-by: Baoqi Zhang <zhangba...@loongson.cn>
Co-authored-by: Dongyan Qian <qiandong...@loongson.cn>
Co-authored-by: Xianglai Li <lixiang...@loongson.cn>
Co-authored-by: Bibo Mao <maob...@loongson.cn>
Acked-by: Gerd Hoffmann <kra...@redhat.com>
---
 UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.inf    |  39 +
 UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.uni    |  14 +
 .../Library/CpuMmuLib/LoongArch64/CpuMmu.c    | 785 ++++++++++++++++++
 .../Library/CpuMmuLib/LoongArch64/Page.h      |  33 +
 .../LoongArch64/TlbExceptionHandle.S          |  51 ++
 .../LoongArch64/TlbExceptionHandle.h          |  36 +
 .../CpuMmuLib/LoongArch64/TlbInvalid.S        |  24 +
 .../CpuMmuLib/LoongArch64/TlbInvalid.h        |  24 +
 UefiCpuPkg/UefiCpuPkg.dsc                     |   3 +
 9 files changed, 1009 insertions(+)
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.inf
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.uni
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CpuMmu.c
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h
 create mode 100644 
UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.S
 create mode 100644 
UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.h
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.S
 create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.h

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..6d77a1221f
--- /dev/null
+++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CpuMmu.c
@@ -0,0 +1,785 @@
+/** @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:
+      LoongArchAttributes |= CACHE_CC;
+      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 @@ [Components.RISCV64]
   UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf
   UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf
 
+[Components.LOONGARCH64]
+  UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.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 (#117656): https://edk2.groups.io/g/devel/message/117656
Mute This Topic: https://groups.io/mt/105478500/21656
Group Owner: devel+ow...@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com]
-=-=-=-=-=-=-=-=-=-=-=-


Reply via email to