| /** @file | |
| CPU Memory Map Unit Initialization library instance. | |
| Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <Uefi.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/CacheMaintenanceLib.h> | |
| #include <Library/CpuMmuLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Register/LoongArch64/Csr.h> | |
| #include <Register/LoongArch64/Cpucfg.h> | |
| // | |
| // Because the page size in edk2 is 4KB, the lowest level | |
| // page table is align to 12 bits, and the page table width | |
| // of other levels is set to 9 bits by default, which will | |
| // be 3 or 4 or 5 level page tables, and continuous. | |
| // | |
| // Correspondence between max virtual memory address width | |
| // and page table level: | |
| // 39 bit >= VA > 31 bit, 3 level page tables | |
| // 48 bit >= VA > 40 bit, 4 level page tables | |
| // 57 bit >= VA > 49 bit, 5 level page tables | |
| // | |
| #define DEFAULT_BIT_WIDTH_PER_LEVEL (EFI_PAGE_SHIFT - 3) | |
| /** | |
| Decided page walker width, level. | |
| @param[in, out] PageWalkCfg Page walker value instance. | |
| @param[in] BitWidt The bit width what you want, 0 is means use the default bit width. | |
| @retval PageTableLevelNum The max page table level. | |
| **/ | |
| STATIC | |
| UINT8 | |
| DecidePageWalkConfiguration ( | |
| IN OUT UINT64 *PageWalkCfg OPTIONAL, | |
| IN UINT8 BitWidth | |
| ) | |
| { | |
| CPUCFG_REG1_INFO_DATA CpucfgReg1Data; | |
| UINT8 CpuVirtMemAddressWidth; | |
| UINT8 PageTableLevelNum; | |
| UINT8 CurrentPageTableLevel; | |
| UINT32 Pwcl0Value; | |
| UINT32 Pwcl1Value; | |
| // | |
| // If BitWidth is 0, use the default bit width. | |
| // | |
| if (BitWidth == 0) { | |
| BitWidth = DEFAULT_BIT_WIDTH_PER_LEVEL; | |
| } | |
| // | |
| // Get the the CPU virtual memory address width. | |
| // | |
| AsmCpucfg (CPUCFG_REG1_INFO, &CpucfgReg1Data.Uint32); | |
| CpuVirtMemAddressWidth = (UINT8)(CpucfgReg1Data.Bits.VALEN + 1); | |
| // | |
| // Statisitics the maximum page table level | |
| // | |
| PageTableLevelNum = 0x0; | |
| if (((CpuVirtMemAddressWidth - EFI_PAGE_SHIFT) % BitWidth) > 0) { | |
| PageTableLevelNum++; | |
| } | |
| PageTableLevelNum += (CpuVirtMemAddressWidth - EFI_PAGE_SHIFT) / BitWidth; | |
| // | |
| // Set page table level | |
| // | |
| Pwcl0Value = 0x0; | |
| Pwcl1Value = 0x0; | |
| for (CurrentPageTableLevel = 0x0; CurrentPageTableLevel < PageTableLevelNum; CurrentPageTableLevel++) { | |
| if (CurrentPageTableLevel < 0x3) { | |
| // Less then or equal to level 3 | |
| Pwcl0Value |= ((BitWidth * CurrentPageTableLevel + EFI_PAGE_SHIFT) << 10 * CurrentPageTableLevel) | | |
| BitWidth << (10 * CurrentPageTableLevel + 5); | |
| } else { | |
| // Lager then level 3 | |
| Pwcl1Value |= ((BitWidth * CurrentPageTableLevel + EFI_PAGE_SHIFT) << 12 * (CurrentPageTableLevel - 3)) | | |
| BitWidth << (12 * (CurrentPageTableLevel - 3) + 6); | |
| } | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "%a %d Level %d DIR shift %d.\n", | |
| __func__, | |
| __LINE__, | |
| (CurrentPageTableLevel + 1), | |
| (BitWidth * CurrentPageTableLevel + EFI_PAGE_SHIFT) | |
| )); | |
| } | |
| *PageWalkCfg = ((UINT64)Pwcl1Value << 32) | Pwcl0Value; | |
| return PageTableLevelNum; | |
| } | |
| /** | |
| Create a page table and initialize the memory management unit(MMU). | |
| @param[in] MemoryTable A pointer to a memory ragion table. | |
| @retval EFI_SUCCESS Configure MMU successfully. | |
| EFI_INVALID_PARAMETER MemoryTable is NULL. | |
| EFI_UNSUPPORTED MemoryRegionMap failed or out of memory space or size not aligned | |
| or MaxLivel out of bound. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| ConfigureMemoryManagementUnit ( | |
| IN EFI_MEMORY_DESCRIPTOR *MemoryTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN PageTable; | |
| UINT64 PageWalkCfg; | |
| UINT8 MaxLevel; | |
| if (MemoryTable == NULL) { | |
| ASSERT (MemoryTable != NULL); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Automatically obtain the current appropriate page walker configuration. | |
| // | |
| MaxLevel = DecidePageWalkConfiguration (&PageWalkCfg, 0); | |
| if ((MaxLevel < 0) || (MaxLevel > 5)) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| // | |
| // Clear PGD series registers. | |
| // | |
| CsrWrite (LOONGARCH_CSR_PGDL, 0x0); | |
| CsrWrite (LOONGARCH_CSR_PGDH, 0x0); | |
| PageTable = 0; | |
| while (MemoryTable->NumberOfPages != 0) { | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "%a %d VirtualBase %p VirtualEnd %p Attributes %p .\n", | |
| __func__, | |
| __LINE__, | |
| MemoryTable->VirtualStart, | |
| (EFI_PAGES_TO_SIZE (MemoryTable->NumberOfPages) + MemoryTable->VirtualStart), | |
| MemoryTable->Attribute | |
| )); | |
| Status = MemoryRegionMap ( | |
| &PageTable, | |
| PageWalkCfg, | |
| MemoryTable->VirtualStart, | |
| EFI_PAGES_TO_SIZE (MemoryTable->NumberOfPages), | |
| MemoryTable->Attribute, | |
| 0x0 | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| MemoryTable++; | |
| } | |
| // | |
| // Configure page walker. | |
| // | |
| CsrWrite (LOONGARCH_CSR_PWCTL0, (UINT32)PageWalkCfg); | |
| if ((PageWalkCfg >> 32) != 0x0) { | |
| CsrWrite (LOONGARCH_CSR_PWCTL1, (UINT32)(PageWalkCfg >> 32)); | |
| } | |
| // | |
| // Set page size | |
| // | |
| CsrXChg (LOONGARCH_CSR_TLBIDX, (DEFAULT_PAGE_SIZE << CSR_TLBIDX_SIZE), CSR_TLBIDX_SIZE_MASK); | |
| CsrWrite (LOONGARCH_CSR_STLBPGSIZE, DEFAULT_PAGE_SIZE); | |
| CsrXChg (LOONGARCH_CSR_TLBREHI, (DEFAULT_PAGE_SIZE << CSR_TLBREHI_PS_SHIFT), CSR_TLBREHI_PS); | |
| // | |
| // Enable MMU | |
| // | |
| CsrWrite (LOONGARCH_CSR_PGDL, PageTable); | |
| // | |
| // Enable Paging | |
| // | |
| CsrXChg (LOONGARCH_CSR_CRMD, BIT4, BIT4|BIT3); | |
| DEBUG ((DEBUG_INFO, "%a %d Enable MMU Start PageBassAddress %p.\n", __func__, __LINE__, PageTable)); | |
| return EFI_SUCCESS; | |
| } |