Thanks,
Chao
--------
Read the memory map information through the QemuFwCfg interface,then build the page table through the memory map information,and finally enable Mmu.REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054Cc: Bibo Mao <maobibo@loongson.cn>Cc: Chao Li <lichao@loongson.cn>Cc: Leif Lindholm <quic_llindhol@quicinc.com>Cc: Liming Gao <gaoliming@byosoft.com.cn>Cc: Michael D Kinney <michael.d.kinney@intel.com>Signed-off-by: xianglai li <lixianglai@loongson.cn>---.../LoongArchQemuPkg/Include/Library/MmuLib.h | 85 ++.../LoongArchQemuPkg/Library/MmuLib/Mmu.S | 155 ++++.../Library/MmuLib/MmuBaseLib.inf | 40 +.../Library/MmuLib/MmuBaseLibPei.inf | 47 +.../Library/MmuLib/MmuLibCore.c | 831 ++++++++++++++++++.../Library/MmuLib/MmuLibCore.h | 40 +.../Library/MmuLib/MmuLibCorePei.c | 231 +++++.../LoongArchQemuPkg/Library/MmuLib/mmu.h | 190 ++++.../LoongArchQemuPkg/Library/MmuLib/page.h | 280 ++++++.../LoongArchQemuPkg/Library/MmuLib/pte.h | 57 ++10 files changed, 1956 insertions(+)create mode 100644 Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.hcreate mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.Screate mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.infcreate mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.infcreate mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.ccreate mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.hcreate mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.ccreate mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.hcreate mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.hcreate mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.hdiff --git a/Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h b/Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.hnew file mode 100644index 0000000000..9880fc385c--- /dev/null+++ b/Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h@@ -0,0 +1,85 @@+/** @file++ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>++ SPDX-License-Identifier: BSD-2-Clause-Patent++ @par Glossary:+ - EXC - execute+**/+#ifndef MMU_LIB_H_+#define MMU_LIB_H_+/**+ write operation is performed Count times from the first element of Buffer.+ Convert EFI Attributes to Loongarch Attributes.+ @param[in] EfiAttributes Efi Attributes.++ @retval LoongArch Attributes.+**/+UINTN+EfiAttributeToLoongArchAttribute (+ IN UINTN EfiAttributes+ );++/**+ Finds the length and memory properties of the memory region corresponding to the specified base address.++ @param[in] BaseAddress To find the base address of the memory region.+ @param[in] EndAddress To find the end address of the memory region.+ @param[out] RegionLength The length of the memory region found.+ @param[out] RegionAttributes Properties of the memory region found.++ @retval EFI_SUCCESS The corresponding memory area was successfully found+ EFI_NOT_FOUND No memory area found+**/+EFI_STATUS+GetLoongArchMemoryRegion (+ IN UINTN BaseAddress,+ IN UINTN EndAddress,+ OUT UINTN *RegionLength,+ OUT UINTN *RegionAttributes+ );++/**+ Sets the Attributes of the specified memory region++ @param[in] BaseAddress The base address of the memory region to set the Attributes.+ @param[in] Length The length of the memory region to set the Attributes.+ @param[in] Attributes The Attributes to be set.++ @retval EFI_SUCCESS The Attributes was set successfully+**/+EFI_STATUS+LoongArchSetMemoryAttributes (+ IN EFI_PHYSICAL_ADDRESS BaseAddress,+ IN UINTN Length,+ IN UINTN Attributes+ );++/**+ Sets the non-executable Attributes for the specified memory region++ @param[in] BaseAddress The base address of the memory region to set the Attributes.+ @param[in] Length The length of the memory region to set the Attributes.++ @retval EFI_SUCCESS The Attributes was set successfully+**/+EFI_STATUS+LoongArchSetMemoryRegionNoExec (+ IN EFI_PHYSICAL_ADDRESS BaseAddress,+ IN UINTN Length+ );++/**+ Create a page table and initialize the MMU.++ @param[] VOID++ @retval VOID+**/+VOID+EFIAPI+ConfigureMmu (+ VOID+ );+#endif // MMU_LIB_H_diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.Snew file mode 100644index 0000000000..d5863de072--- /dev/null+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S@@ -0,0 +1,155 @@+#------------------------------------------------------------------------------+#+# LoongArch for LoongArch+#+# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>+#+# SPDX-License-Identifier: BSD-2-Clause-Patent+#+#-----------------------------------------------------------------------------++#ifndef __ASSEMBLY__+#define __ASSEMBLY__+#endif++#include "Library/Cpu.h"+#include "mmu.h"++ASM_GLOBAL ASM_PFX(HandleTlbRefill)+ASM_GLOBAL HandleTlbRefillEnd+ASM_GLOBAL ASM_PFX(LoongarchInvalidTlb)+ASM_GLOBAL ASM_PFX(SetTlbRefillFuncBase)+ASM_GLOBAL ASM_PFX(WriteCsrPageSize)+ASM_GLOBAL ASM_PFX(WriteCsrTlbRefillPageSize)+ASM_GLOBAL ASM_PFX(WriteCsrStlbPageSize)+ASM_GLOBAL ASM_PFX(LoongArchWriteqCsrPwctl0)+ASM_GLOBAL ASM_PFX(LoongArchWriteqCsrPwctl1)+ASM_GLOBAL ASM_PFX(LoongArchWriteqCsrPgdl)+ASM_GLOBAL ASM_PFX(LoongArchWriteqCsrPgdh)+ASM_GLOBAL ASM_PFX(LoongArchXchgCsrCrmd)++#+# Refill the page table.+# @param VOID+# @retval VOID+#++ASM_PFX(HandleTlbRefill):+ csrwr T0, LOONGARCH_CSR_TLBRSAVE+ csrrd T0, LOONGARCH_CSR_PGD+ lddir T0, T0, 3 #Put pud BaseAddress into T0+ lddir T0, T0, 2 #Put pmd BaseAddress into T0+ lddir T0, T0, 1 #Put pte BaseAddress into T0+ ldpte T0, 0+ ldpte T0, 1+ tlbfill+ csrrd T0, LOONGARCH_CSR_TLBRSAVE+ ertn+HandleTlbRefillEnd:++#+# Invalid corresponding TLB entries are based on the address given+# @param A0 The address corresponding to the invalid page table entry+# @retval none+#++ASM_PFX(LoongarchInvalidTlb):+ invtlb INVTLB_ADDR_GTRUE_OR_ASID, ZERO, A0+ jirl ZERO, RA, 0++#+# Set Tlb Refill function to hardware+# @param A0 The address of tlb refill function+# @retval none+#++ASM_PFX(SetTlbRefillFuncBase):+ csrwr A0, LOONGARCH_CSR_TLBREBASE+ jirl ZERO, RA,0++#+# Set Cpu Status Register Page Size.+# @param A0 Page Size.+# @retval none+#++ASM_PFX(WriteCsrPageSize):+ li.d T0, CSR_TLBIDX_SIZE+ sll.d A0, A0, T0+ li.d T0, CSR_TLBIDX_SIZE_MASK+ csrxchg A0, T0, LOONGARCH_CSR_TLBIDX+ jirl ZERO, RA,0++#+# Set Cpu Status Register TLBREFILL Page Size.+# @param A0 Page Size.+# @retval none+#++ASM_PFX(WriteCsrTlbRefillPageSize):+ li.d T0, CSR_TLBREHI_PS_SHIFT+ sll.d A0, A0, T0+ li.d T0, CSR_TLBREHI_PS+ csrxchg A0, T0, LOONGARCH_CSR_TLBREHI+ jirl ZERO, RA,0++#+# Set Cpu Status Register STLB Page Size.+# @param val Page Size.+# @retval VOID+#++ASM_PFX(WriteCsrStlbPageSize):+ csrwr A0, LOONGARCH_CSR_STLBPGSIZE+ jirl ZERO, RA,0++#+# Write Csr PWCTL0 register.+# @param A0 The value used to write to the PWCTL0 register+# @retval none+#++ASM_PFX(LoongArchWriteqCsrPwctl0):+ csrwr A0, LOONGARCH_CSR_PWCTL0+ jirl ZERO, RA,0++#+# Write Csr PWCTL1 register.+# @param A0 The value used to write to the PWCTL1 register+# @retval none+#++ASM_PFX(LoongArchWriteqCsrPwctl1):+ csrwr A0, LOONGARCH_CSR_PWCTL1+ jirl ZERO, RA,0++#+# Write Csr PGDL register.+# @param A0 The value used to write to the PGDL register+# @retval none+#++ASM_PFX(LoongArchWriteqCsrPgdl):+ csrwr A0, LOONGARCH_CSR_PGDL+ jirl ZERO, RA,0++#+# Write Csr PGDH register.+# @param A0 The value used to write to the PGDH register+# @retval none+#++ASM_PFX(LoongArchWriteqCsrPgdh):+ csrwr A0, LOONGARCH_CSR_PGDH+ jirl ZERO, RA,0++#+# Exchange specified bit data with the Csr CRMD register.+# @param[IN] A0 The value Exchanged with the CSR CRMD register.+# @param[IN] A1 Specifies the mask for swapping bits+# @retval VOID+#++ASM_PFX(LoongArchXchgCsrCrmd):+ csrxchg A0, A1, LOONGARCH_CSR_CRMD+ jirl ZERO, RA,0diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.infnew file mode 100644index 0000000000..abd864a324--- /dev/null+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf@@ -0,0 +1,40 @@+## @file+#+# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>+#+# SPDX-License-Identifier: BSD-2-Clause-Patent+#+##++[Defines]+ INF_VERSION = 0x00010005+ BASE_NAME = MmuBaseLib+ FILE_GUID = da8f0232-fb14-42f0-922c-63104d2c70be+ MODULE_TYPE = BASE+ VERSION_STRING = 1.0+ LIBRARY_CLASS = MmuLib+ CONSTRUCTOR = MmuInitialize++#+# VALID_ARCHITECTURES = LOONGARCH64+#++[Sources]+ MmuLibCore.c+ Mmu.S++[Packages]+ MdePkg/MdePkg.dec+ Platform/Loongson/LoongArchQemuPkg/Loongson.dec++[PCD]+ gLoongArchQemuPkgTokenSpaceGuid.PcdSwapPageDir+ gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPgd+ gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPud+ gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPmd+ gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPte++[LibraryClasses]+ MemoryAllocationLib+ PcdLib+ DebugLibdiff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.infnew file mode 100644index 0000000000..12848eecfe--- /dev/null+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf@@ -0,0 +1,47 @@+## @file+#+# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>+#+# SPDX-License-Identifier: BSD-2-Clause-Patent+#+##++[Defines]+ INF_VERSION = 0x00010005+ BASE_NAME = MmuPeiLib+ FILE_GUID = da8f0232-fb14-42f0-922c-63104d2c70bd+ MODULE_TYPE = BASE+ VERSION_STRING = 1.0+ LIBRARY_CLASS = MmuLib | SEC PEIM++#+# VALID_ARCHITECTURES = LOONGARCH64+#++[Sources]+ MmuLibCorePei.c+ Mmu.S+ MmuLibCore.h+ MmuLibCore.c++[Packages]+ MdePkg/MdePkg.dec+ Platform/Loongson/LoongArchQemuPkg/Loongson.dec+ OvmfPkg/OvmfPkg.dec++[PCD]+ gLoongArchQemuPkgTokenSpaceGuid.PcdSwapPageDir+ gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPgd+ gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPud+ gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPmd+ gLoongArchQemuPkgTokenSpaceGuid.PcdInvalidPte+ gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvSize+ gLoongArchQemuPkgTokenSpaceGuid.PcdFlashSecFvBase+ gLoongArchQemuPkgTokenSpaceGuid.PcdRamSize++[LibraryClasses]+ MemoryAllocationLib+ CacheMaintenanceLib+ PcdLib+ DebugLib+ QemuFwCfgLibdiff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.cnew file mode 100644index 0000000000..b932e3d568--- /dev/null+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c@@ -0,0 +1,831 @@+/** @file++ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>++ SPDX-License-Identifier: BSD-2-Clause-Patent++ @par Glossary:+ - Pgd or Pgd or PGD - Page Global Directory+ - Pud or Pud or PUD - Page Upper Directory+ - Pmd or Pmd or PMD - Page Middle Directory+ - Pte or pte or PTE - Page Table Entry+ - Val or VAL or val - Value+ - Dir - Directory+**/+#include <Uefi.h>+#include <Library/BaseMemoryLib.h>+#include <Library/MemoryAllocationLib.h>+#include <Library/BaseLib.h>+#include <Library/DebugLib.h>+#include "Library/Cpu.h"+#include "pte.h"+#include "page.h"+#include "mmu.h"++BOOLEAN mMmuInited = FALSE;+/**+ Check to see if mmu successfully initializes.++ @param VOID.++ @retval TRUE Initialization has been completed.+ FALSE Initialization did not complete.+**/+BOOLEAN+MmuIsInit (VOID) {+ if ((mMmuInited == TRUE) ||+ (PcdGet64 (PcdSwapPageDir) != 0)) {+ return TRUE;+ }+ return FALSE;+}++/**+ Iterates through the page directory to initialize it.++ @param Dst A pointer to the directory of the page to initialize.+ @param Num The number of page directories to initialize.+ @param Src A pointer to the data used to initialize the page directory.++ @retval VOID.+**/+VOID+PageDirInit (+ IN VOID *Dst,+ IN UINTN Num,+ IN VOID *Src+ )+{+ UINTN *Ptr;+ UINTN *End;+ UINTN Entry;++ Entry = (UINTN)Src;+ Ptr = (UINTN *)Dst;+ End = Ptr + Num;++ for ( ;Ptr < End; Ptr++) {+ *Ptr = Entry;+ }++ return ;+}++/**+ Gets the virtual address corresponding to the page global directory table entry.++ @param Address the virtual address for the table entry.++ @retval PGD A pointer to get the table item.+**/+PGD *+PgdOffset (+ IN UINTN Address+ )+{+ return ((PGD *)PcdGet64 (PcdSwapPageDir)) + PGD_INDEX (Address);+}++/**+ Gets the virtual address corresponding to the page upper directory table entry.++ @param Pgd A pointer to a page global directory table entry.+ @param Address the virtual address for the table entry.++ @retval PUD A pointer to get the table item.+**/+PUD *+PudOffset (+ IN PGD *Pgd,+ IN UINTN Address+ )+{+ UINTN PgdVal = (UINTN)PGD_VAL (*Pgd);+ return (PUD *)PgdVal + PUD_INDEX (Address);+}++/**+ Gets the virtual address corresponding to the page middle directory table entry.++ @param Pud A pointer to a page upper directory table entry.+ @param Address the virtual address for the table entry.++ @retval PMD A pointer to get the table item.+**/+PMD *+PmdOffset (+ IN PUD *Pud,+ IN UINTN Address+ )+{+ UINTN PudVal = PUD_VAL (*Pud);+ return (PMD *)PudVal + PMD_INDEX (Address);+}++/**+ Gets the virtual address corresponding to the page table entry.++ @param Pmd A pointer to a page middle directory table entry.+ @param Address the virtual address for the table entry.++ @retval PTE A pointer to get the table item.+**/+PTE *+PteOffset (+ IN PMD *Pmd,+ IN UINTN Address+ )+{+ UINTN PmdVal = (UINTN)PMD_VAL (*Pmd);+ return (PTE *)PmdVal + PTE_INDEX (Address);+}++/**+ Sets the value of the page table entry.++ @param Pte A pointer to a page table entry.+ @param PteVal The value of the page table entry to set.++ @retval VOID+**/+VOID+SetPte (+ IN PTE *Pte,+ IN PTE PteVal+ )+{+ *Pte = PteVal;+}++/**+ Sets the value of the page global directory.++ @param Pgd A pointer to a page global directory.+ @param Pud The value of the page global directory to set.++ @retval VOID+**/+VOID+SetPgd (+ IN PGD *Pgd,+ IN PUD *Pud+ )+{+ *Pgd = (PGD) {((UINTN)Pud)};+}++/**+ Sets the value of the page upper directory.++ @param Pud A pointer to a page upper directory.+ @param Pmd The value of the page upper directory to set.++ @retval VOID+**/+VOID+SetPud (+ IN PUD *Pud,+ IN PMD *Pmd+ )+{+ *Pud = (PUD) {((UINTN)Pmd)};+}++/**+ Sets the value of the page middle directory.++ @param Pmd A pointer to a page middle directory.+ @param Pte The value of the page middle directory to set.++ @retval VOID+**/+VOID+SetPmd (+ IN PMD *Pmd,+ IN PTE *Pte+ )+{+ *Pmd = (PMD) {((UINTN)Pte)};+}++/**+ Free up memory space occupied by page tables.++ @param Pte A pointer to the page table.++ @retval VOID+**/+VOID+PteFree (+ IN PTE *Pte+ )+{+ FreePages ((VOID *)Pte, 1);+}++/**+ Free up memory space occupied by page middle directory.++ @param Pmd A pointer to the page middle directory.++ @retval VOID+**/+VOID+PmdFree (+ IN PMD *Pmd+ )+{+ FreePages ((VOID *)Pmd, 1);+}++/**+ Free up memory space occupied by page upper directory.++ @param Pud A pointer to the page upper directory.++ @retval VOID+**/+VOID+PudFree (+ IN PUD *Pud+ )+{+ FreePages ((VOID *)Pud, 1);+}++/**+ Requests the memory space required for the page upper directory,+ initializes it, and places it in the specified page global directory++ @param Pgd A pointer to the page global directory.++ @retval EFI_SUCCESS Memory request successful.+ @retval EFI_OUT_OF_RESOURCES Resource exhaustion cannot be requested to memory.+**/+INTN+PudAlloc (+ IN PGD *Pgd+ )+{+ PUD *Pud = (PUD *) AllocatePages (1);+ if (!Pud) {+ return EFI_OUT_OF_RESOURCES;+ }++ PageDirInit ((VOID *)Pud, ENTRYS_PER_PUD, (VOID *)PcdGet64 (PcdInvalidPmd));++ if (pgd_none (*Pgd)) {+ SetPgd (Pgd, Pud);+ } else { /* Another has populated it */+ PudFree (Pud);+ }++ return EFI_SUCCESS;+}++/**+ Requests the memory space required for the page middle directory,+ initializes it, and places it in the specified page upper directory++ @param Pud A pointer to the page upper directory.++ @retval EFI_SUCCESS Memory request successful.+ @retval EFI_OUT_OF_RESOURCES Resource exhaustion cannot be requested to memory.+**/+EFI_STATUS+PmdAlloc (+ IN PUD *Pud+ )+{+ PMD *Pmd;++ Pmd = (PMD *) AllocatePages (1);+ if (!Pmd) {+ return EFI_OUT_OF_RESOURCES;+ }++ PageDirInit ((VOID *)Pmd, ENTRYS_PER_PMD, (VOID *)PcdGet64 (PcdInvalidPte));++ if (pud_none (*Pud)) {+ SetPud (Pud, Pmd);+ } else {/* Another has populated it */+ PmdFree (Pmd);+ }++ return EFI_SUCCESS;+}++/**+ Requests the memory space required for the page table,+ initializes it, and places it in the specified page middle directory++ @param Pmd A pointer to the page middle directory.++ @retval EFI_SUCCESS Memory request successful.+ @retval EFI_OUT_OF_RESOURCES Resource exhaustion cannot be requested to memory.+**/+INTN+PteAlloc (+ IN PMD *Pmd+ )+{+ PTE *Pte;++ Pte = (PTE *) AllocatePages (1);+ if (!Pte) {+ return EFI_OUT_OF_RESOURCES;+ }++ Pte = ZeroMem (Pte, EFI_PAGE_SIZE);++ if (pmd_none (*Pmd)) {+ SetPmd (Pmd, Pte);+ } else { /* Another has populated it */+ PteFree (Pte);+ }++ return EFI_SUCCESS;+}++/**+ Requests the memory space required for the page upper directory,+ initializes it, and places it in the specified page global directory,+ and get the page upper directory entry corresponding to the virtual address++ @param Pgd A pointer to the page global directory.++ @retval Gets the page upper directory entry+**/+PUD *+PudAllocGet (+ IN PGD *Pgd,+ IN UINTN Address+ )+{+ return ((pgd_none (*(Pgd)) && PudAlloc (Pgd)) ?+ NULL : PudOffset (Pgd, Address));+}++/**+ Requests the memory space required for the page middle directory,+ initializes it, and places it in the specified page upper directory,+ and get the page middle directory entry corresponding to the virtual address++ @param Pud A pointer to the page upper directory.++ @retval Gets the page middle directory entry+**/+PMD *+PmdAllocGet (+ IN PUD *Pud,+ IN UINTN Address+ )+{+ PMD * ret = (pud_none (*Pud) && PmdAlloc (Pud))?+ NULL: PmdOffset (Pud, Address);+ DEBUG ((DEBUG_VERBOSE, "%a %d PudVal %p PmdOffset %p PMD_INDEX %p .\n", __func__, __LINE__,+ Pud->PudVal, PmdOffset (Pud, Address), PMD_INDEX (Address) ));++ return ret;+}++/**+ Requests the memory space required for the page table,+ initializes it, and places it in the specified page middle directory,+ and get the page table entry corresponding to the virtual address++ @param Pmd A pointer to the page upper directory.++ @retval Gets the page table entry+**/+PTE *+PteAllocGet (+ IN PMD *Pmd,+ IN UINTN Address+ )+{+ return (pmd_none (*Pmd) && PteAlloc (Pmd))?+ NULL: PteOffset (Pmd, Address);+}++/**+ Gets the physical address of the page table entry corresponding to the specified virtual address.++ @param Address the corresponding virtual address of the page table entry.++ @retval A pointer to the page table entry.+ @retval NULL+**/+PTE *+GetPteAddress (+ IN UINTN Address+ )+{+ PGD *Pgd;+ PUD *Pud;+ PMD *Pmd;++ Pgd = PgdOffset (Address);++ if (pgd_none (*Pgd)) {+ return NULL;+ }++ Pud = PudOffset (Pgd, Address);++ if (pud_none (*Pud)) {+ return NULL;+ }++ Pmd = PmdOffset (Pud, Address);+ if (pmd_none (*Pmd)) {+ return NULL;+ }++ if (IS_HUGE_PAGE (Pmd->PmdVal)) {+ return ((PTE *)Pmd);+ }++ return PteOffset (Pmd, Address);+}++/**+ Establishes a page table entry based on the specified memory region.++ @param Pmd A pointer to the page middle directory.+ @param Address The memory space start address.+ @param End The end address of the memory space.+ @param Attributes Memory space Attributes.++ @retval EFI_SUCCESS The page table entry was created successfully.+ @retval EFI_OUT_OF_RESOURCES Page table entry establishment failed due to resource exhaustion.+**/+EFI_STATUS+MemoryMapPteRange (+ IN PMD *Pmd,+ IN UINTN Address,+ IN UINTN End,+ IN UINTN Attributes+ )+{+ PTE *Pte;+ PTE PteVal;+ BOOLEAN UpDate;++ Pte = PteAllocGet (Pmd, Address);+ if (!Pte) {+ return EFI_OUT_OF_RESOURCES;+ }++ do {+ UpDate = FALSE;+ PteVal = MAKE_PTE (Address, Attributes);+ DEBUG ((DEBUG_VERBOSE,+ "%a %d Address %p PGD_INDEX %p PUD_INDEX %p PMD_INDEX %p PTE_INDEX %p MAKE_PTE %p\n",+ __func__, __LINE__, Address, PGD_INDEX (Address), PUD_INDEX (Address), PMD_INDEX (Address),+ PTE_INDEX (Address), PteVal));++ if ((!pte_none (*Pte)) &&+ (PTE_VAL(*Pte) != PTE_VAL(PteVal)))+ {+ UpDate = TRUE;+ }++ SetPte (Pte, PteVal);+ if (UpDate) {+ LoongarchInvalidTlb(Address);+ }+ } while (Pte++, Address += EFI_PAGE_SIZE, Address != End);++ return EFI_SUCCESS;+}++/**+ Establishes a page middle directory based on the specified memory region.++ @param Pud A pointer to the page upper directory.+ @param Address The memory space start address.+ @param End The end address of the memory space.+ @param Attributes Memory space Attributes.++ @retval EFI_SUCCESS The page middle directory was created successfully.+ @retval EFI_OUT_OF_RESOURCES Page middle directory establishment failed due to resource exhaustion.+**/+EFI_STATUS+MemoryMapPmdRange (+ IN PUD *Pud,+ IN UINTN Address,+ IN UINTN End,+ IN UINTN Attributes+ )+{+ PMD *Pmd;+ PTE *Pte;+ UINTN Next;+ UINTN AddressStart_HugePage;+ UINTN AddressEnd_HugePage;++ Pmd = PmdAllocGet (Pud, Address);+ if (!Pmd) {+ return EFI_OUT_OF_RESOURCES;+ }++ do {+ Next = PMD_ADDRESS_END (Address, End);+ if (((Address & (~PMD_MASK)) == 0) &&+ ((Next & (~PMD_MASK)) == 0) &&+ (pmd_none (*Pmd)))+ {+ DEBUG ((DEBUG_VERBOSE,+ "%a %d Address %p PGD_INDEX %p PUD_INDEX %p PMD_INDEX %p MAKE_HUGE_PTE %p\n",+ __func__, __LINE__, Address, PGD_INDEX (Address), PUD_INDEX (Address), PMD_INDEX (Address),+ MAKE_HUGE_PTE (Address, Attributes)));++ SetPmd (Pmd, (PTE *)MAKE_HUGE_PTE (Address, Attributes));+ } else {+ if ((pmd_none (*Pmd)) ||+ ((!pmd_none (*Pmd)) &&+ (!IS_HUGE_PAGE (Pmd->PmdVal))))+ {+ if (MemoryMapPteRange (Pmd, Address, Next, Attributes)) {+ return EFI_OUT_OF_RESOURCES;+ }+ } else {+ SetPmd (Pmd, (PTE *)PcdGet64 (PcdInvalidPte));+ AddressStart_HugePage = Address & PMD_MASK;+ AddressEnd_HugePage = AddressStart_HugePage + HUGE_PAGE_SIZE;+ if (MemoryMapPteRange (Pmd, AddressStart_HugePage, AddressEnd_HugePage, Attributes)) {+ return EFI_OUT_OF_RESOURCES;+ }+ Pte = GetPteAddress (AddressStart_HugePage);+ if (Pte == NULL) {+ continue ;+ }+ if (AddressEnd_HugePage > End) {+ Next = End;+ }+ }+ }+ } while (Pmd++, Address = Next, Address != End);++ return 0;+}++/**+ Establishes a page upper directory based on the specified memory region.++ @param Pgd A pointer to the page global directory.+ @param Address The memory space start address.+ @param End The end address of the memory space.+ @param Attributes Memory space Attributes.++ @retval EFI_SUCCESS The page upper directory was created successfully.+ @retval EFI_OUT_OF_RESOURCES Page upper directory establishment failed due to resource exhaustion.+**/+EFI_STATUS+MemoryMapPudRange (+ IN PGD *Pgd,+ IN UINTN Address,+ IN UINTN End,+ IN UINTN Attributes+ )+{+ PUD *Pud;+ UINTN Next;++ Pud = PudAllocGet (Pgd, Address);+ if (!Pud) {+ return EFI_OUT_OF_RESOURCES;+ }++ do {+ Next = PUD_ADDRESS_END (Address, End);+ if (MemoryMapPmdRange (Pud, Address, Next, Attributes)) {+ return EFI_OUT_OF_RESOURCES;+ }+ } while (Pud++, Address = Next, Address != End);+ return EFI_SUCCESS;+}++/**+ Establishes a page global directory based on the specified memory region.++ @param Start The memory space start address.+ @param End The end address of the memory space.+ @param Attributes Memory space Attributes.++ @retval EFI_SUCCESS The page global directory was created successfully.+ @retval EFI_OUT_OF_RESOURCES Page global directory establishment failed due to resource exhaustion.+**/+EFI_STATUS+MemoryMapPageRange (+ IN UINTN Start,+ IN UINTN End,+ IN UINTN Attributes+ )+{+ PGD *Pgd;+ UINTN Next;+ UINTN Address = Start;+ EFI_STATUS Err;++ Pgd = PgdOffset (Address);+ do {+ Next = PGD_ADDRESS_END (Address, End);+ Err = MemoryMapPudRange (Pgd, Address, Next, Attributes);+ if (Err) {+ return Err;+ }+ } while (Pgd++, Address = Next, Address != End);++ return EFI_SUCCESS;+}++/**+ Page tables are established from memory-mapped tables.++ @param MemoryRegion A pointer to a memory-mapped table entry.++ @retval EFI_SUCCESS The page table was created successfully.+ @retval EFI_OUT_OF_RESOURCES Page table establishment failed due to resource exhaustion.+**/+EFI_STATUS+FillTranslationTable (+ IN MEMORY_REGION_DESCRIPTOR *MemoryRegion+ )+{+ return MemoryMapPageRange (MemoryRegion->VirtualBase,+ (MemoryRegion->Length + MemoryRegion->VirtualBase),+ MemoryRegion->Attributes);+}++/**+ write operation is performed Count times from the first element of Buffer.+ Convert EFI Attributes to Loongarch Attributes.+ @param[in] EfiAttributes Efi Attributes.++ @retval LoongArch Attributes.+**/+UINTN+EfiAttributeToLoongArchAttribute (+ IN UINTN EfiAttributes+ )+{+ UINTN LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | CACHE_CC | PAGE_USER | PAGE_GLOBAL;+ switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) {+ case EFI_MEMORY_UC:+ LoongArchAttributes |= CACHE_SUC;+ break;+ case EFI_MEMORY_WC:+ case EFI_MEMORY_WT:+ case EFI_MEMORY_WB:+ LoongArchAttributes |= CACHE_CC;+ break;+ default :+ LoongArchAttributes |= CACHE_CC;+ break;+ }++ // Write protection attributes+ if ((EfiAttributes & EFI_MEMORY_RO) != 0) {+ LoongArchAttributes &= ~PAGE_DIRTY;+ }++ //eXecute protection attribute+ if ((EfiAttributes & EFI_MEMORY_XP) != 0) {+ LoongArchAttributes |= PAGE_NO_EXEC;+ }++ return LoongArchAttributes;+}++/**+ Finds the length and memory properties of the memory region corresponding to the specified base address.++ @param[in] BaseAddress To find the base address of the memory region.+ @param[in] EndAddress To find the end address of the memory region.+ @param[out] RegionLength The length of the memory region found.+ @param[out] RegionAttributes Properties of the memory region found.++ @retval EFI_SUCCESS The corresponding memory area was successfully found+ EFI_NOT_FOUND No memory area found+**/+EFI_STATUS+GetLoongArchMemoryRegion (+ IN UINTN BaseAddress,+ IN UINTN EndAddress,+ OUT UINTN *RegionLength,+ OUT UINTN *RegionAttributes+ )+{+ PTE *Pte;+ UINTN Attributes;+ UINTN AttributesTmp;+ UINTN MaxAddress;+ MaxAddress = LShiftU64 (1ULL, MAX_VA_BITS) - 1;+ Pte = GetPteAddress (BaseAddress);++ if (!MmuIsInit ()) {+ return EFI_SUCCESS;+ }+ if (Pte == NULL) {+ return EFI_NOT_FOUND;+ }+ Attributes = GET_PAGE_ATTRIBUTES (*Pte);+ if (IS_HUGE_PAGE (Pte->PteVal)) {+ *RegionAttributes = Attributes & (~(PAGE_HUGE));+ *RegionLength += HUGE_PAGE_SIZE;+ } else {+ *RegionLength += EFI_PAGE_SIZE;+ *RegionAttributes = Attributes;+ }++ while (BaseAddress <= MaxAddress) {+ Pte = GetPteAddress (BaseAddress);+ if (Pte == NULL) {+ return EFI_SUCCESS;+ }+ AttributesTmp = GET_PAGE_ATTRIBUTES (*Pte);+ if (IS_HUGE_PAGE (Pte->PteVal)) {+ if (AttributesTmp == Attributes) {+ *RegionLength += HUGE_PAGE_SIZE;+ }+ BaseAddress += HUGE_PAGE_SIZE;+ } else {+ if (AttributesTmp == Attributes) {+ *RegionLength += EFI_PAGE_SIZE;+ }+ BaseAddress += EFI_PAGE_SIZE;+ }++ if (BaseAddress > EndAddress) {+ break;+ }+ }+ return EFI_SUCCESS;+}++/**+ Sets the Attributes of the specified memory region++ @param[in] BaseAddress The base address of the memory region to set the Attributes.+ @param[in] Length The length of the memory region to set the Attributes.+ @param[in] Attributes The Attributes to be set.++ @retval EFI_SUCCESS The Attributes was set successfully+**/+EFI_STATUS+LoongArchSetMemoryAttributes (+ IN EFI_PHYSICAL_ADDRESS BaseAddress,+ IN UINTN Length,+ IN UINTN Attributes+ )+{++ if (!MmuIsInit ()) {+ return EFI_SUCCESS;+ }+ Attributes = EfiAttributeToLoongArchAttribute (Attributes);+ DEBUG ((DEBUG_VERBOSE, "%a %d %p %p %p.\n", __func__, __LINE__, BaseAddress , Length, Attributes));+ MemoryMapPageRange (BaseAddress, BaseAddress + Length, Attributes);++ return EFI_SUCCESS;+}++/**+ Sets the non-executable Attributes for the specified memory region++ @param[in] BaseAddress The base address of the memory region to set the Attributes.+ @param[in] Length The length of the memory region to set the Attributes.++ @retval EFI_SUCCESS The Attributes was set successfully+**/+EFI_STATUS+LoongArchSetMemoryRegionNoExec (+ IN EFI_PHYSICAL_ADDRESS BaseAddress,+ IN UINTN Length+ )+{+ if (MmuIsInit ()) {+ Length = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (Length));+ LoongArchSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_XP);+ }+ return EFI_SUCCESS;+}++/**+ Check to see if mmu successfully initializes and saves the result.++ @param VOID.++ @retval EFI_SUCCESS Initialization succeeded.+**/+EFI_STATUS+MmuInitialize (VOID)+{+ if (PcdGet64 (PcdSwapPageDir) != 0) {+ mMmuInited = TRUE;+ }++ return EFI_SUCCESS;+}diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.hnew file mode 100644index 0000000000..7a5e8ea0dd--- /dev/null+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.h@@ -0,0 +1,40 @@+/** @file++ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>++ SPDX-License-Identifier: BSD-2-Clause-Patent++ @par Glossary:+ - Dir - Directory+**/+#ifndef MMU_LIB_CORE_H_+#define MMU_LIB_CORE_H_+/**+ Iterates through the page directory to initialize it.++ @param Dst A pointer to the directory of the page to initialize.+ @param Num The number of page directories to initialize.+ @param Src A pointer to the data used to initialize the page directory.++ @retval VOID.+**/+VOID+PageDirInit (+ IN VOID *dest,+ IN UINTN Count,+ IN VOID *src+ );++/**+ Page tables are established from memory-mapped tables.++ @param MemoryRegion A pointer to a memory-mapped table entry.++ @retval EFI_SUCCESS The page table was created successfully.+ @retval EFI_OUT_OF_RESOURCES Page table establishment failed due to resource exhaustion.+**/+EFI_STATUS+FillTranslationTable (+ IN MEMORY_REGION_DESCRIPTOR *MemoryRegion+ );+#endif // MMU_LIB_CORE_H_diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.cnew file mode 100644index 0000000000..32a7fc0beb--- /dev/null+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c@@ -0,0 +1,231 @@+/** @file+ Platform PEI driver++ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>++ SPDX-License-Identifier: BSD-2-Clause-Patent++ @par Glossary:+ - FwCfg - Firmeware Config+ - Tlb - Translation Lookaside Buffer+**/+#include <Uefi.h>+#include <Library/BaseMemoryLib.h>+#include <Library/MemoryAllocationLib.h>+#include <Library/BaseLib.h>+#include <Library/DebugLib.h>+#include "Library/Cpu.h"+#include "pte.h"+#include "page.h"+#include "mmu.h"+#include <Library/QemuFwCfgLib.h>+#include "MmuLibCore.h"+#include <Library/CacheMaintenanceLib.h>++/**+ Return the Virtual Memory Map of your platform++ This Virtual Memory Map is used by MemoryInitPei Module to initialize the MMU+ on your platform.++ @param[out] VirtualMemoryMap Array of MEMORY_REGION_DESCRIPTOR+ describing a Physical-to-Virtual Memory+ mapping. This array must be ended by a+ zero-filled entry. The allocated memory+ will not be freed.+**/+VOID+GetMemoryMapFromFwCfg (+ OUT MEMORY_REGION_DESCRIPTOR **VirtualMemoryMap+ )+{++ EFI_STATUS Status;+ FIRMWARE_CONFIG_ITEM FwCfgItem;+ UINTN FwCfgSize;+ LOONGARCH_MEMMAP_ENTRY MemoryMapEntry;+ LOONGARCH_MEMMAP_ENTRY *StartEntry;+ LOONGARCH_MEMMAP_ENTRY *pEntry;+ UINTN Processed;+ MEMORY_REGION_DESCRIPTOR *VirtualMemoryTable;+ UINTN Index = 0;+ ASSERT (VirtualMemoryMap != NULL);++ VirtualMemoryTable = AllocatePool (+ sizeof (MEMORY_REGION_DESCRIPTOR) *+ MAX_VIRTUAL_MEMORY_MAP_DESCRIPTORS+ );+ VirtualMemoryTable[Index].PhysicalBase = 0x10000000;+ VirtualMemoryTable[Index].VirtualBase = VirtualMemoryTable[Index].PhysicalBase;+ VirtualMemoryTable[Index].Length = 0x10000000;+ VirtualMemoryTable[Index].Attributes = PAGE_VALID | PLV_KERNEL | CACHE_SUC | PAGE_DIRTY | PAGE_GLOBAL;+ ++Index;++ Status = QemuFwCfgFindFile ("etc/memmap", &FwCfgItem, &FwCfgSize);+ if (EFI_ERROR (Status)) {+ DEBUG ((DEBUG_ERROR, "%a %d read etc/memmap error Status %d \n", __func__, __LINE__, Status));+ ZeroMem (&VirtualMemoryTable[Index], sizeof (MEMORY_REGION_DESCRIPTOR));+ *VirtualMemoryMap = VirtualMemoryTable;+ return ;+ }+ if (FwCfgSize % sizeof MemoryMapEntry != 0) {+ DEBUG ((DEBUG_ERROR, "no MemoryMapEntry FwCfgSize:%d\n", FwCfgSize));+ }++ QemuFwCfgSelectItem (FwCfgItem);+ StartEntry = AllocatePages (EFI_SIZE_TO_PAGES (FwCfgSize));+ QemuFwCfgReadBytes (FwCfgSize, StartEntry);+ for (Processed = 0; Processed < (FwCfgSize / sizeof MemoryMapEntry); Processed++) {+ pEntry = StartEntry + Processed;+ if (pEntry->Length == 0) {+ continue;+ }++ DEBUG ((DEBUG_INFO, "MemmapEntry Base %p length %p type %d\n", pEntry->BaseAddr, pEntry->Length, pEntry->Type));+ VirtualMemoryTable[Index].PhysicalBase = pEntry->BaseAddr;+ VirtualMemoryTable[Index].VirtualBase = VirtualMemoryTable[Index].PhysicalBase;+ VirtualMemoryTable[Index].Length = pEntry->Length;+ VirtualMemoryTable[Index].Attributes = PAGE_VALID | PLV_KERNEL | CACHE_CC | PAGE_DIRTY | PAGE_GLOBAL;+ ++Index;+ }++ FreePages (StartEntry, EFI_SIZE_TO_PAGES (FwCfgSize));+ // End of Table+ ZeroMem (&VirtualMemoryTable[Index], sizeof (MEMORY_REGION_DESCRIPTOR));+ *VirtualMemoryMap = VirtualMemoryTable;+ return ;+}++/**+ Create a page table and initialize the MMU.++ @param[] VOID++ @retval VOID+**/+EFIAPI+VOID+ConfigureMmu (VOID)+{+ PGD *SwapperPageDir = NULL;+ PGD *InvalidPgd = NULL;+ PUD *InvalidPudTable = NULL;+ PMD *InvalidPmdTable = NULL;+ PTE *InvalidPteTable = NULL;+ MEMORY_REGION_DESCRIPTOR *MemoryTable = NULL;+ RETURN_STATUS PcdStatus;+ UINTN PgdShift = PGD_SHIFT;+ UINTN PgdWide = PGD_WIDE;+ UINTN PudShift = PUD_SHIFT;+ UINTN PudWide = PUD_WIDE;+ UINTN PmdShift = PMD_SHIFT;+ UINTN PmdWide = PMD_WIDE;+ UINTN PteShift = PTE_SHIFT;+ UINTN PteWide = PTE_WIDE;+ UINTN PageEnable = 1 << 4;+ VOID *TlbReEntry;++ SwapperPageDir = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));+ InvalidPgd = AllocatePages (EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));+ InvalidPudTable = AllocatePages (EFI_SIZE_TO_PAGES (PUD_TABLE_SIZE));+ InvalidPmdTable = AllocatePages (EFI_SIZE_TO_PAGES (PMD_TABLE_SIZE));+ InvalidPteTable = AllocatePages (EFI_SIZE_TO_PAGES (PTE_TABLE_SIZE));+ ZeroMem (InvalidPteTable, PTE_TABLE_SIZE);++ if ((!InvalidPgd) ||+ (!InvalidPudTable) ||+ (!InvalidPmdTable) ||+ (!InvalidPteTable))+ {+ goto FreeTranslationTable;+ }++ /*pgd init*/+ PageDirInit (SwapperPageDir , ENTRYS_PER_PGD, InvalidPudTable);+ /*pgd init*/+ PageDirInit (InvalidPgd, ENTRYS_PER_PGD, InvalidPudTable);+ /*pud init*/+ PageDirInit (InvalidPudTable, ENTRYS_PER_PUD, InvalidPmdTable);+ /*pmd init*/+ PageDirInit (InvalidPmdTable, ENTRYS_PER_PMD, InvalidPteTable);+ GetMemoryMapFromFwCfg (&MemoryTable);++ PcdStatus |= PcdSet64S (PcdSwapPageDir, (UINTN)SwapperPageDir);+ PcdStatus |= PcdSet64S (PcdInvalidPgd, (UINTN)InvalidPgd);+ PcdStatus |= PcdSet64S (PcdInvalidPud, (UINTN)InvalidPudTable);+ PcdStatus |= PcdSet64S (PcdInvalidPmd, (UINTN)InvalidPmdTable);+ PcdStatus |= PcdSet64S (PcdInvalidPte, (UINTN)InvalidPteTable);+ ASSERT_RETURN_ERROR (PcdStatus);++ while (MemoryTable->Length != 0) {+ DEBUG ((DEBUG_VERBOSE, "%a %d VirtualBase %p VirtualEnd %p Attributes %p .\n", __func__, __LINE__,+ MemoryTable->VirtualBase,+ (MemoryTable->Length + MemoryTable->VirtualBase),+ MemoryTable->Attributes));++ PcdStatus = FillTranslationTable (MemoryTable);+ if (EFI_ERROR (PcdStatus)) {+ goto FreeTranslationTable;+ }+ MemoryTable++;+ }++ TlbReEntry = AllocatePages (1);+ if (TlbReEntry == NULL) {+ goto FreeTranslationTable;+ }+ CopyMem ((char *)TlbReEntry, HandleTlbRefill, (HandleTlbRefillEnd - HandleTlbRefill));+ InvalidateInstructionCacheRange ((VOID *)(UINTN)HandleTlbRefill, (UINTN)(HandleTlbRefillEnd - HandleTlbRefill));++ DEBUG ((DEBUG_VERBOSE,+ "%a %d PteShift %d PteWide %d PmdShift %d PmdWide %d PudShift %d PudWide %d PgdShift %d PgdWide %d.\n",+ __func__, __LINE__,+ PteShift, PteWide, PmdShift, PmdWide,PudShift, PudWide, PgdShift, PgdWide));++ SetTlbRefillFuncBase ((UINTN)TlbReEntry);+ /*set page size*/+ WriteCsrPageSize (DEFAULT_PAGE_SIZE);+ WriteCsrStlbPageSize (DEFAULT_PAGE_SIZE);+ WriteCsrTlbRefillPageSize (DEFAULT_PAGE_SIZE);++ LoongArchWriteqCsrPwctl0 ((PteShift | PteWide << 5 | PmdShift << 10 | PmdWide << 15 | PudShift << 20 | PudWide << 25+ ));+ LoongArchWriteqCsrPwctl1 (PgdShift | PgdWide << 6);+ LoongArchWriteqCsrPgdl ((UINTN)SwapperPageDir);+ LoongArchWriteqCsrPgdh ((UINTN)InvalidPgd);++ DEBUG ((DEBUG_INFO, "%a %d Enable Mmu Start PageBassAddress %p.\n", __func__, __LINE__, SwapperPageDir));+ LoongArchXchgCsrCrmd ( PageEnable, 1 << 4);+ DEBUG ((DEBUG_INFO, "%a %d Enable Mmu End.\n", __func__, __LINE__));++ return ;++FreeTranslationTable:+ if (SwapperPageDir) {+ FreePages (SwapperPageDir, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));+ }++ if (InvalidPgd) {+ FreePages (InvalidPgd, EFI_SIZE_TO_PAGES (PGD_TABLE_SIZE));+ }++ if (InvalidPudTable) {+ FreePages (InvalidPudTable, EFI_SIZE_TO_PAGES (PUD_TABLE_SIZE));+ }++ if (InvalidPmdTable) {+ FreePages (InvalidPmdTable, EFI_SIZE_TO_PAGES (PMD_TABLE_SIZE));+ }++ if (InvalidPteTable) {+ FreePages (InvalidPteTable, EFI_SIZE_TO_PAGES (PTE_TABLE_SIZE));+ }++ PcdSet64S (PcdSwapPageDir, (UINTN)0);+ PcdSet64S (PcdInvalidPgd, (UINTN)0);+ PcdSet64S (PcdInvalidPud, (UINTN)0);+ PcdSet64S (PcdInvalidPmd, (UINTN)0);+ PcdSet64S (PcdInvalidPte, (UINTN)0);++ return ;+}diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.hnew file mode 100644index 0000000000..8e284a2ecd--- /dev/null+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.h@@ -0,0 +1,190 @@+/** @file++ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>++ SPDX-License-Identifier: BSD-2-Clause-Patent++ @par Glossary:+ - Tlb or TLB - Translation Lookaside Buffer+ - CSR - Cpu State Register+ - PGDL - Page Global Directory Low+ - PGDH - Page Global Directory High+ - TLBIDX - TLB Index+ - TLBREHI - TLB Refill Entry High+ - PWCTL - Page Walk Control+ - STLB - Singular Page Size TLB+ - PS - Page Size+**/+#ifndef MMU_H_+#define MMU_H_+/*page size 4k*/+#define DEFAULT_PAGE_SIZE 0x0c+#define LOONGARCH_CSR_PGDL 0x19 /* Page table base address when VA[47] = 0 */+#define LOONGARCH_CSR_PGDH 0x1a /* Page table base address when VA[47] = 1 */+#define LOONGARCH_CSR_TLBIDX 0x10 /* TLB Index, EHINV, PageSize, NP */+#define LOONGARCH_CSR_TLBEHI 0x11 /* TLB EntryHi */+#define LOONGARCH_CSR_TLBELO0 0x12 /* TLB EntryLo0 */+#define LOONGARCH_CSR_TLBELO1 0x13 /* TLB EntryLo1 */+#define LOONGARCH_CSR_TLBREHI 0x8e /* TLB refill entryhi */+#define LOONGARCH_CSR_PWCTL0 0x1c /* PWCtl0 */+#define LOONGARCH_CSR_PWCTL1 0x1d /* PWCtl1 */+#define LOONGARCH_CSR_STLBPGSIZE 0x1e+#define CSR_TLBIDX_SIZE_MASK 0x3f000000+#define CSR_TLBIDX_PS_SHIFT 24+#define CSR_TLBIDX_SIZE CSR_TLBIDX_PS_SHIFT++#define CSR_TLBREHI_PS_SHIFT 0+#define CSR_TLBREHI_PS 0x3f++#define EFI_MEMORY_CACHETYPE_MASK (EFI_MEMORY_UC | \+ EFI_MEMORY_WC | \+ EFI_MEMORY_WT | \+ EFI_MEMORY_WB | \+ EFI_MEMORY_UCE \+ )++typedef struct {+ EFI_PHYSICAL_ADDRESS PhysicalBase;+ EFI_VIRTUAL_ADDRESS VirtualBase;+ UINTN Length;+ UINTN Attributes;+} MEMORY_REGION_DESCRIPTOR;++// The total number of descriptors, including the final "end-of-table" descriptor.+#define MAX_VIRTUAL_MEMORY_MAP_DESCRIPTORS (128)++extern CHAR8 HandleTlbRefill[], HandleTlbRefillEnd[];++/*+ Invalid corresponding TLB entries are based on the address given++ @param Address The address corresponding to the invalid page table entry++ @retval none+*/+extern+VOID+LoongarchInvalidTlb (+ UINTN Address+ );++/*+ Set Tlb Refill function to hardware++ @param A0 The address of tlb refill function++ @retval none+*/+extern+VOID+SetTlbRefillFuncBase (+ UINTN Address+ );++/*+ Set Cpu Status Register Page Size.++ @param PageSize Page Size.++ @retval none+*/+extern+VOID+WriteCsrPageSize (+ UINTN PageSize+ );++/*+ Set Cpu Status Register TLBREFILL Page Size.++ @param PageSize Page Size.++ @retval none+*/+extern+VOID+WriteCsrTlbRefillPageSize (+ UINTN PageSize+ );++/*+ Set Cpu Status Register STLB Page Size.++ @param PageSize Page Size.++ @retval VOID+*/+extern+VOID+WriteCsrStlbPageSize (+ UINTN PageSize+);++/*+ Write Csr PWCTL0 register.++ @param Val The value used to write to the PWCTL0 register++ @retval none+*/+extern+VOID+LoongArchWriteqCsrPwctl0 (+ UINTN Val+ );++/*+ Write Csr PWCTL1 register.++ @param Val The value used to write to the PWCTL1 register++ @retval none+*/+extern+VOID+LoongArchWriteqCsrPwctl1 (+ UINTN Val+ );++/*+ Write Csr PGDL register.++ @param Val The value used to write to the PGDL register++ @retval none+*/+extern+VOID+LoongArchWriteqCsrPgdl (+ UINTN Val+ );++/*+ Write Csr PGDH register.++ @param Val The value used to write to the PGDH register++ @retval none+*/+extern+VOID+LoongArchWriteqCsrPgdh (+ UINTN Val+ );++/*+ Exchange specified bit data with the Csr CRMD register.++ @param[IN] Val The value Exchanged with the CSR CRMD register.+ @param[IN] Mask Specifies the mask for swapping bits++ @retval VOID+*/+extern+VOID+LoongArchXchgCsrCrmd (+ UINTN Val,+ UINTN Mask+ );++#endif // MMU_H_diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.hnew file mode 100644index 0000000000..6ab07e7900--- /dev/null+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h@@ -0,0 +1,280 @@+/** @file++ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>++ SPDX-License-Identifier: BSD-2-Clause-Patent++ @par Glossary:+ - Pgd or Pgd or PGD - Page Global Directory+ - Pud or Pud or PUD - Page Upper Directory+ - Pmd or Pmd or PMD - Page Middle Directory+ - Pte or pte or PTE - Page Table Entry+ - Val or VAL or val - Value+ - Dir - Directory+**/+#ifndef PAGE_H_+#define PAGE_H_++#define MAX_VA_BITS 47+#define PGD_WIDE (8)+#define PUD_WIDE (9)+#define PMD_WIDE (9)+#define PTE_WIDE (9)++#define ENTRYS_PER_PGD (1 << PGD_WIDE)+#define ENTRYS_PER_PUD (1 << PUD_WIDE)+#define ENTRYS_PER_PMD (1 << PMD_WIDE)+#define ENTRYS_PER_PTE (1 << PTE_WIDE)++#define PGD_SHIFT (PUD_SHIFT + PUD_WIDE)+#define PUD_SHIFT (PMD_SHIFT + PMD_WIDE)+#define PMD_SHIFT (EFI_PAGE_SHIFT + PTE_WIDE)+#define PTE_SHIFT (EFI_PAGE_SHIFT)++#define PGD_SIZE (1UL << PGD_SHIFT)+#define PUD_SIZE (1UL << PUD_SHIFT)+#define PMD_SIZE (1UL << PMD_SHIFT)++#define PGD_MASK (~(PGD_SIZE-1))+#define PUD_MASK (~(PUD_SIZE-1))+#define PMD_MASK (~(PMD_SIZE-1))+#define PAGE_MASK (~(EFI_PAGE_SIZE - 1))+#define PFN_MASK (~(((UINTN)(1) << (EFI_PAGE_SHIFT)) - 1) & \+ (((UINTN)(1) << (PAGE_PFN_END_SHIFT)) - 1))++typedef struct { UINTN PgdVal; } PGD;+typedef struct { UINTN PudVal; } PUD;+typedef struct { UINTN PmdVal; } PMD;+typedef struct { UINTN PteVal; } PTE;+/**+ Gets the value of the page global directory table entry.++ @param x Page global directory struct variables.++ @retval the value of the page global directory table entry.+ **/+#define PGD_VAL(x) ((x).PgdVal)+/**+ Gets the value of the page upper directory table entry.++ @param x Page upper directory struct variables.++ @retval the value of the page upper directory table entry.+ **/+#define PUD_VAL(x) ((x).PudVal)+/**+ Gets the value of the page middle directory table entry.++ @param x Page middle directory struct variables.++ @retval the value of the page middle directory table entry.+ **/+#define PMD_VAL(x) ((x).PmdVal)+/**+ Gets the value of the page table entry.++ @param x Page table entry struct variables.++ @retval the value of the page table entry.+ **/+#define PTE_VAL(x) ((x).PteVal)++#define PGD_TABLE_SIZE (ENTRYS_PER_PGD * sizeof(PGD))+#define PUD_TABLE_SIZE (ENTRYS_PER_PUD * sizeof(PUD))+#define PMD_TABLE_SIZE (ENTRYS_PER_PMD * sizeof(PMD))+#define PTE_TABLE_SIZE (ENTRYS_PER_PTE * sizeof(PTE))+/**+ Gets the physical address of the record in the page table entry.++ @param x Page table entry struct variables.++ @retval the value of the physical address.+ **/+#define GET_PAGE_ATTRIBUTES(x) (UINTN) {(PTE_VAL(x) & ~PFN_MASK)}+/**+ Gets the virtual address of the next block of the specified virtual address+ that is aligned with the size of the global page directory mapping.++ @param Address Specifies the virtual address.+ @param End The end address of the memory region.++ @retval the specified virtual address of the next block.+ **/+#define PGD_ADDRESS_END(Address, End) \+({ \+ UINTN Boundary = ((Address) + PGD_SIZE) & PGD_MASK; \+ (Boundary - 1 < (End) - 1)? Boundary: (End); \+})+/**+ Gets the virtual address of the next block of the specified virtual address+ that is aligned with the size of the page upper directory mapping.++ @param Address Specifies the virtual address.+ @param End The end address of the memory region.++ @retval the specified virtual address of the next block.+ **/+#define PUD_ADDRESS_END(Address, End) \+({ \+ UINTN Boundary = ((Address) + PUD_SIZE) & PUD_MASK; \+ (Boundary - 1 < (End) - 1)? Boundary: (End); \+})+/**+ Gets the virtual address of the next block of the specified virtual address+ that is aligned with the size of the page middle directory mapping.++ @param Address Specifies the virtual address.+ @param End The end address of the memory region.++ @retval the specified virtual address of the next block.+ **/+#define PMD_ADDRESS_END(Address, End) \+({ \+ UINTN Boundary = ((Address) + PMD_SIZE) & PMD_MASK; \+ (Boundary - 1 < (End) - 1)? Boundary: (End); \+})+/**+ Get Specifies the virtual address corresponding to the index of the page global directory table entry.++ @param Address Specifies the virtual address.++ @retval the index of the page global directory table entry.+ **/+#define PGD_INDEX(Address) (((Address) >> PGD_SHIFT) & (ENTRYS_PER_PGD-1))+/**+ Get Specifies the virtual address corresponding to the index of the page upper directory table entry.++ @param Address Specifies the virtual address.+ @param End The end address of the memory region.++ @retval the index of the page upper directory table entry.+ **/+#define PUD_INDEX(Address) (((Address) >> PUD_SHIFT) & (ENTRYS_PER_PUD - 1))+/**+ Get Specifies the virtual address corresponding to the index of the page middle directory table entry.++ @param Address Specifies the virtual address.++ @retval the index of the page middle directory table entry.+ **/+#define PMD_INDEX(Address) (((Address) >> PMD_SHIFT) & (ENTRYS_PER_PMD - 1))+/**+ Get Specifies the virtual address corresponding to the index of the page table entry.++ @param Address Specifies the virtual address.++ @retval the index of the page table entry.+ **/+#define PTE_INDEX(Address) (((Address) >> EFI_PAGE_SHIFT) & (ENTRYS_PER_PTE - 1))++/**+ Calculates the value of the page table entry based on the specified virtual address and properties.++ @param Address Specifies the virtual address.+ @param Attributes Specifies the Attributes.++ @retval the value of the page table entry.+ **/+#define MAKE_PTE(Address, Attributes) (PTE){((((Address) >> EFI_PAGE_SHIFT) << 12) | (Attributes))}+/**+ Get Global bit from Attributes++ @param Attributes Specifies the Attributes.+ * */+#define GET_GLOBALBIT(Attributes) ((Attributes & PAGE_GLOBAL) >> PAGE_GLOBAL_SHIFT)+/**+ Calculates the value of the Huge page table entry based on the specified virtual address and properties.++ @param Address Specifies the virtual address.+ @param Attributes Specifies the Attributes.++ @retval the value of the HUGE page table entry.+ **/+#define MAKE_HUGE_PTE(Address, Attributes) (((((Address) >> PMD_SHIFT) << PMD_SHIFT) | \+ ((Attributes) | (GET_GLOBALBIT(Attributes) << PAGE_HGLOBAL_SHIFT) | \+ PAGE_HUGE)))++ /**+ Check whether the large page table entry is.++ @param Val The value of the page table entry.++ @retval 1 Is huge page table entry.+ @retval 0 Isn't huge page table entry.+ **/+#define IS_HUGE_PAGE(Val) ((((Val) & PAGE_HUGE) == PAGE_HUGE) && \+ (((Val) & PAGE_HGLOBAL) == PAGE_HGLOBAL))++#define HUGE_PAGE_SIZE (PMD_SIZE)++ /**+ Check that the global page directory table entry is empty.++ @param pgd the global page directory struct variables.++ @retval 1 Is huge page table entry.+ @retval 0 Isn't huge page table entry.+ **/+STATIC+inline+UINTN+pgd_none (+ IN PGD pgd+ )+{+ return (PGD_VAL(pgd) == (UINTN)PcdGet64(PcdInvalidPud));+}++ /**+ Check that the page upper directory table entry is empty.++ @param pud Page upper directory struct variables.++ @retval 1 Is huge page table entry.+ @retval 0 Isn't huge page table entry.+ **/+STATIC+inline+UINTN+pud_none (+ IN PUD pud+ )+{+ return (PUD_VAL(pud) == (UINTN)PcdGet64 (PcdInvalidPmd));+}++ /**+ Check that the page middle directory table entry is empty.++ @param pmd Page middle directory struct variables.++ @retval 1 Is huge page table entry.+ @retval 0 Isn't huge page table entry.+ **/+STATIC+inline+UINTN+pmd_none (+ IN PMD pmd+ )+{+ return (PMD_VAL(pmd) == (UINTN)PcdGet64(PcdInvalidPte));+}+ /**+ Check that the page table entry is empty.++ @param pmd Page table entry struct variables.++ @retval 1 Is huge page table entry.+ @retval 0 Isn't huge page table entry.+ **/+STATIC+inline+UINTN+pte_none (+ IN PTE pte+ )+{+ return (!(PTE_VAL(pte) & (~PAGE_GLOBAL)));+}+#endif // PAGE_H_diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.hnew file mode 100644index 0000000000..aacbf81744--- /dev/null+++ b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h@@ -0,0 +1,57 @@+/** @file++ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>++ SPDX-License-Identifier: BSD-2-Clause-Patent++ @par Glossary:+ - Tlb or TLB - Translation Lookaside Buffer+ - HGLOBAL - Huge Global+ - PFN - Page Frame number+ - EXEC - Execute+ - PLV - Privilege Level+ - RPLV - Restricted Privilege Level+ - SUC - Strong-ordered UnCached+ - CC - Coherent Cached+ - WUC - Weak-ordered UnCached+**/+#ifndef PTE_H_+#define PTE_H_+/*Page table property definitions */+#define PAGE_VALID_SHIFT 0+#define PAGE_DIRTY_SHIFT 1+#define PAGE_PLV_SHIFT 2 /* 2~3, two bits */+#define CACHE_SHIFT 4 /* 4~5, two bits */+#define PAGE_GLOBAL_SHIFT 6+#define PAGE_HUGE_SHIFT 6 /* HUGE is a PMD bit */++#define PAGE_HGLOBAL_SHIFT 12 /* HGlobal is a PMD bit */+#define PAGE_PFN_SHIFT 12+#define PAGE_PFN_END_SHIFT 48+#define PAGE_NO_READ_SHIFT 61+#define PAGE_NO_EXEC_SHIFT 62+#define PAGE_RPLV_SHIFT 63++/* Used by TLB hardware (placed in EntryLo*) */+#define PAGE_VALID ((UINTN)(1) << PAGE_VALID_SHIFT)+#define PAGE_DIRTY ((UINTN)(1) << PAGE_DIRTY_SHIFT)+#define PAGE_PLV ((UINTN)(3) << PAGE_PLV_SHIFT)+#define PAGE_GLOBAL ((UINTN)(1) << PAGE_GLOBAL_SHIFT)+#define PAGE_HUGE ((UINTN)(1) << PAGE_HUGE_SHIFT)+#define PAGE_HGLOBAL ((UINTN)(1) << PAGE_HGLOBAL_SHIFT)+#define PAGE_NO_READ ((UINTN)(1) << PAGE_NO_READ_SHIFT)+#define PAGE_NO_EXEC ((UINTN)(1) << PAGE_NO_EXEC_SHIFT)+#define PAGE_RPLV ((UINTN)(1) << PAGE_RPLV_SHIFT)+#define CACHE_MASK ((UINTN)(3) << CACHE_SHIFT)+#define PFN_SHIFT (EFI_PAGE_SHIFT - 12 + PAGE_PFN_SHIFT)++#define PLV_KERNEL 0+#define PLV_USER 3++#define PAGE_USER (PLV_USER << PAGE_PLV_SHIFT)+#define PAGE_KERNEL (PLV_KERN << PAGE_PLV_SHIFT)++#define CACHE_SUC (0 << CACHE_SHIFT) /* Strong-ordered UnCached */+#define CACHE_CC (1 << CACHE_SHIFT) /* Coherent Cached */+#define CACHE_WUC (2 << CACHE_SHIFT) /* Weak-ordered UnCached */+#endif // PTE_H_--2.31.1