Reviewed-by: Chao Li Thanks, Chao -------- On 11月 11 2022, at 5:12 δΈ‹εˆ, xianglai li wrote: > Read the memory map information through the QemuFwCfg interface, > > then build the page table through the memory map information, > and finally enable Mmu. > > > REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054 > > > Cc: Bibo Mao > Cc: Chao Li > Cc: Leif Lindholm > Cc: Liming Gao > Cc: Michael D Kinney > Signed-off-by: xianglai li > --- > .../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.h > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/Mmu.S > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.h > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCorePei.c > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/mmu.h > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/page.h > create mode 100644 Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/pte.h > > > diff --git a/Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h b/Platform/Loongson/LoongArchQemuPkg/Include/Library/MmuLib.h > new file mode 100644 > index 0000000000..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.
> + > + 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.S > new file mode 100644 > index 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.
> +# > +# 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,0 > diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLib.inf > new file mode 100644 > index 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.
> +# > +# 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 > + DebugLib > diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuBaseLibPei.inf > new file mode 100644 > index 0000000000..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.
> +# > +# 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 > + QemuFwCfgLib > diff --git a/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c b/Platform/Loongson/LoongArchQemuPkg/Library/MmuLib/MmuLibCore.c > new file mode 100644 > index 0000000000..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.
> + > + 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 > +#include > +#include > +#include > +#include > +#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.h > new file mode 100644 > index 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.
> + > + 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.c > new file mode 100644 > index 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.
> + > + SPDX-License-Identifier: BSD-2-Clause-Patent > + > + @par Glossary: > + - FwCfg - Firmeware Config > + - Tlb - Translation Lookaside Buffer > +**/ > +#include > +#include > +#include > +#include > +#include > +#include "Library/Cpu.h" > +#include "pte.h" > +#include "page.h" > +#include "mmu.h" > +#include > +#include "MmuLibCore.h" > +#include > + > +/** > + 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.h > new file mode 100644 > index 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.
> + > + 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.h > new file mode 100644 > index 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.
> + > + 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.h > new file mode 100644 > index 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.
> + > + 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