From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail04.groups.io (mail04.groups.io [45.79.224.9]) by spool.mail.gandi.net (Postfix) with ESMTPS id A203E740038 for ; Tue, 16 Apr 2024 02:44:35 +0000 (UTC) DKIM-Signature: a=rsa-sha256; bh=y+T7mlO2dhL9+oQ2f98Th5F8LkmUYau6hU5S4x5dw/w=; c=relaxed/simple; d=groups.io; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References:MIME-Version:Precedence:List-Subscribe:List-Help:Sender:List-Id:Mailing-List:Delivered-To:Resent-Date:Resent-From:Reply-To:List-Unsubscribe-Post:List-Unsubscribe:Content-Transfer-Encoding; s=20240206; t=1713235474; v=1; b=3J4/XwpjnIllikrrfpho9InqCM9qEsqQ24Pt+sV5ju46zrFKWzobI89sTDUWQC0qbeR2+Mk2 sLVxhmxCAPql3/H10BxvVTn6hWtnRlvMMHy5ebED5g9miHH+ryb0Vi95Ju0Icq2FccZzzmcuwhE 1BMDS8bCzhgwa0l2EELpcC6yhlIsWvCLVtqUooMQTZExbLmbXxFGnseMkqthxVC5jsIIWW15OaL k75LmDgA8JXETqfSruV+rUCscuh0UVmRz8Uqdgj7+Gr5hNbBlwuLdcmzoXTGt8RQUXyDC4JHMAD 73mO7udVMr1sWgiUKVyWRSbuffjKG3wiA/KAlyolbh/Ng== X-Received: by 127.0.0.2 with SMTP id GjKGYY7687511x8prkMhQdtb; Mon, 15 Apr 2024 19:44:34 -0700 X-Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by mx.groups.io with SMTP id smtpd.web10.11067.1713235473261451885 for ; Mon, 15 Apr 2024 19:44:33 -0700 X-Received: from loongson.cn (unknown [10.2.9.245]) by gateway (Coremail) with SMTP id _____8DxaOgO5h1mFhQoAA--.5571S3; Tue, 16 Apr 2024 10:44:30 +0800 (CST) X-Received: from code-server.gen (unknown [10.2.9.245]) by localhost.localdomain (Coremail) with SMTP id AQAAf8DxdREL5h1mkl58AA--.30530S2; Tue, 16 Apr 2024 10:44:27 +0800 (CST) From: "Chao Li" To: devel@edk2.groups.io Cc: Ray Ni , Rahul Kumar , Gerd Hoffmann , Baoqi Zhang , Dongyan Qian , Xianglai Li , Bibo Mao Subject: [edk2-devel] [PATCH v4 09/13] UefiCpuPkg: Add CpuMmuLib to UefiCpuPkg Date: Tue, 16 Apr 2024 10:44:26 +0800 Message-Id: <20240416024426.3174488-1-lichao@loongson.cn> In-Reply-To: <20240416024251.3173738-1-lichao@loongson.cn> References: <20240416024251.3173738-1-lichao@loongson.cn> MIME-Version: 1.0 X-CM-TRANSID: AQAAf8DxdREL5h1mkl58AA--.30530S2 X-CM-SenderInfo: xolfxt3r6o00pqjv00gofq/1tbiAQAJCGYc5WsGFwAVsw X-Coremail-Antispam: 1Uk129KBjDUn29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7KY7 ZEXasCq-sGcSsGvfJ3UbIjqfuFe4nvWSU5nxnvy29KBjDU0xBIdaVrnUUvcSsGvfC2Kfnx nUUI43ZEXa7xR_UUUUUUUUU== Precedence: Bulk List-Subscribe: List-Help: Sender: devel@edk2.groups.io List-Id: Mailing-List: list devel@edk2.groups.io; contact devel+owner@edk2.groups.io Resent-Date: Mon, 15 Apr 2024 19:44:34 -0700 Resent-From: lichao@loongson.cn Reply-To: devel@edk2.groups.io,lichao@loongson.cn List-Unsubscribe-Post: List-Unsubscribe=One-Click List-Unsubscribe: X-Gm-Message-State: oUFzx08udYJyYqtmMZR8MBg4x7686176AA= Content-Transfer-Encoding: 8bit X-GND-Status: LEGIT Authentication-Results: spool.mail.gandi.net; dkim=pass header.d=groups.io header.s=20240206 header.b="3J4/Xwpj"; dmarc=none; spf=pass (spool.mail.gandi.net: domain of bounce@groups.io designates 45.79.224.9 as permitted sender) smtp.mailfrom=bounce@groups.io Add a new base library named CpuMmuLib and add a LoongArch64 instance with in the library. BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=4734 Cc: Ray Ni Cc: Rahul Kumar Cc: Gerd Hoffmann Signed-off-by: Chao Li Co-authored-by: Baoqi Zhang Co-authored-by: Dongyan Qian Co-authored-by: Xianglai Li Co-authored-by: Bibo Mao Acked-by: Gerd Hoffmann Acked-by: Ray Ni --- UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.inf | 39 + UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.uni | 14 + .../Library/CpuMmuLib/LoongArch64/CpuMmu.c | 784 ++++++++++++++++++ .../Library/CpuMmuLib/LoongArch64/Page.h | 33 + .../LoongArch64/TlbExceptionHandle.S | 51 ++ .../LoongArch64/TlbExceptionHandle.h | 36 + .../CpuMmuLib/LoongArch64/TlbInvalid.S | 24 + .../CpuMmuLib/LoongArch64/TlbInvalid.h | 24 + UefiCpuPkg/UefiCpuPkg.dsc | 3 + 9 files changed, 1008 insertions(+) create mode 100644 UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.inf create mode 100644 UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.uni create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CpuMmu.c create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.S create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.h create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.S create mode 100644 UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.h diff --git a/UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.inf b/UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.inf new file mode 100644 index 0000000000..5eecfb4838 --- /dev/null +++ b/UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.inf @@ -0,0 +1,39 @@ +## @file +# CPU Memory Manager Unit library instance. +# +# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 1.29 + BASE_NAME = CpuMmuLib + MODULE_UNI_FILE = CpuMmuLib.uni + FILE_GUID = DA8F0232-FB14-42F0-922C-63104D2C70BE + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = CpuMmuLib + +# +# VALID_ARCHITECTURES = LOONGARCH64 +# + +[Sources.LoongArch64] + LoongArch64/TlbInvalid.S | GCC + LoongArch64/TlbExceptionHandle.S | GCC + LoongArch64/CpuMmu.c + LoongArch64/Page.h + LoongArch64/TlbInvalid.h + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + DebugLib + MemoryAllocationLib + +[Pcd.LoongArch64] + gUefiCpuPkgTokenSpaceGuid.PcdLoongArchExceptionVectorBaseAddress ## CONSUMES diff --git a/UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.uni b/UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.uni new file mode 100644 index 0000000000..2408f2f90b --- /dev/null +++ b/UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.uni @@ -0,0 +1,14 @@ +// /** @file +// CPU Memory Manager Unit library instance. +// +// CPU Memory Manager Unit library instance. +// +// Copyright (c) 2024, Loongson Technology Corporation Limited. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_MODULE_ABSTRACT #language en-US "CPU Memory Manager Unit library instance." + +#string STR_MODULE_DESCRIPTION #language en-US "CPU Memory Manager Unit library instance." diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CpuMmu.c b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CpuMmu.c new file mode 100644 index 0000000000..8f8254362f --- /dev/null +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/CpuMmu.c @@ -0,0 +1,784 @@ +/** @file + + CPU Memory Map Unit Handler Library common functions. + + Copyright (c) 2011-2020, ARM Limited. All rights reserved. + Copyright (c) 2016, Linaro Limited. All rights reserved. + Copyright (c) 2017, Intel Corporation. All rights reserved.
+ Copyright (c) 2023, Ventana Micro Systems Inc. All Rights Reserved.
+ Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "TlbInvalid.h" +#include "TlbExceptionHandle.h" +#include "Page.h" + +/** + Check to see if mmu successfully initializes. + + @param VOID. + + @retval TRUE Initialization has been completed. + FALSE Initialization did not complete. +**/ +STATIC +BOOLEAN +MmuIsInit ( + VOID + ) +{ + if (CsrRead (LOONGARCH_CSR_PGDL) != 0) { + return TRUE; + } + + return FALSE; +} + +/** + Check to see if mmu is enabled. + + @param VOID. + + @retval TRUE MMU has been enabled. + FALSE MMU did not enabled. +**/ +STATIC +BOOLEAN +MmuIsEnabled ( + VOID + ) +{ + if ((CsrRead (LOONGARCH_CSR_CRMD) & BIT4) != 0) { + return TRUE; + } + + return FALSE; +} + +/** + Determine if an entry is valid pte. + + @param Entry The entry value. + + @retval TRUE The entry is a valid pte. + @retval FALSE The entry is not a valid pte. + +**/ +STATIC +BOOLEAN +IsValidPte ( + IN UINTN Entry + ) +{ + if (Entry != INVALID_PAGE) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Determine if an entry is huge page. + + @param Entry The entry value. + + @retval TRUE The entry is a huge page. + @retval FALSE The entry is not a valid huge page. + +**/ +STATIC +BOOLEAN +IsValidHugePage ( + IN UINTN Entry + ) +{ + if ((Entry & (PAGE_HGLOBAL | PAGE_HUGE)) == (PAGE_HGLOBAL | PAGE_HUGE)) { + return TRUE; + } else { + return FALSE; + } +} + +/** + Set an entry to be a valid pte. + + @param Entry The entry value. + + @return The entry value. + +**/ +STATIC +UINTN +SetValidPte ( + IN UINTN Entry + ) +{ + /* Set Valid and Global mapping bits */ + return Entry | PAGE_GLOBAL | PAGE_VALID; +} + +/** + Parse max page table level. + + @param[in] PageWalkCfg Page table configure value. + + @return 5 MAX page level is 5 + 4 MAX page level is 4 + 3 MAX page level is 3 + 0 Invalid +**/ +STATIC +UINTN +ParseMaxPageTableLevel ( + IN UINT64 PageWalkCfg + ) +{ + UINT32 Pwctl0; + UINT32 Pwctl1; + + Pwctl0 = PageWalkCfg & MAX_UINT32; + Pwctl1 = (PageWalkCfg >> 32) & MAX_UINT32; + + if (((Pwctl1 >> 18) & 0x3F) != 0x0) { + return LEVEL5; + } else if (((Pwctl1 >> 6) & 0x3F) != 0x0) { + return LEVEL4; + } else if (((Pwctl0 >> 25) & 0x3F) != 0x0) { + return LEVEL3; + } + + return 0; +} + +/** + Parse page table bit width. + + Assume that the bit width of the page table that each level is the same to PTwidth. + + @param[in] PageWalkCfg Page table configure value. + + @return page table bit width + +**/ +STATIC +UINTN +ParsePageTableBitWidth ( + IN UINT64 PageWalkCfg + ) +{ + // + // PTwidth + // + return ((PageWalkCfg >> 5) & 0x1F); +} + +/** + Determine if an entry is a HUGE PTE or 4K PTE. + + @param Entry The entry value. + @param Level The current page table level. + @param PageWalkCfg Page table configure value. + + @retval TRUE The entry is a block pte. + @retval FALSE The entry is not a block pte. + +**/ +STATIC +BOOLEAN +IsBlockEntry ( + IN UINTN Entry, + IN UINTN Level, + IN UINT64 PageWalkCfg + ) +{ + if (Level == (ParseMaxPageTableLevel (PageWalkCfg) - 1)) { + return ((Entry & PAGE_VALID) == PAGE_VALID); + } + + return IsValidHugePage (Entry); +} + +/** + Determine if an entry is a table pte. + + @param Entry The entry value. + @param Level The current page table level. + @param PageWalkCfg Page table configure value. + + @retval TRUE The entry is a table pte. + @retval FALSE The entry is not a table pte. + +**/ +STATIC +BOOLEAN +IsTableEntry ( + IN UINTN Entry, + IN UINTN Level, + IN UINT64 PageWalkCfg + ) +{ + if (Level == (ParseMaxPageTableLevel (PageWalkCfg) - 1)) { + // + // The last level is PAGE rather than Table. + // + return FALSE; + } + + // + // Is DIR4 or DIR3 or DIR2 a Huge Page ? + // + return (!IsValidHugePage (Entry)) && (IsValidPte (Entry)); +} + +/** + Replace an existing entry with new value. + + @param Entry The entry pointer. + @param Value The new entry value. + @param RegionStart The start of region that new value affects. + @param IsLiveBlockMapping TRUE if this is live update, FALSE otherwise. + +**/ +STATIC +VOID +ReplaceTableEntry ( + IN UINTN *Entry, + IN UINTN Value, + IN UINTN RegionStart, + IN BOOLEAN IsLiveBlockMapping + ) +{ + *Entry = Value; + + if (IsLiveBlockMapping && MmuIsInit ()) { + InvalidTlb (RegionStart); + } +} + +/** + Get an ppn value from an entry. + + @param Entry The entry value. + + @return The ppn value. + +**/ +STATIC +UINTN +GetPpnfromPte ( + IN UINTN Entry + ) +{ + return ((Entry & PTE_PPN_MASK) >> PTE_PPN_SHIFT); +} + +/** + Set an ppn value to a entry. + + @param Entry The entry value. + @param Address The address. + + @return The new entry value. + +**/ +STATIC +UINTN +SetPpnToPte ( + UINTN Entry, + UINTN Address + ) +{ + UINTN Ppn; + + Ppn = ((Address >> LOONGARCH_MMU_PAGE_SHIFT) << PTE_PPN_SHIFT); + ASSERT (~(Ppn & ~PTE_PPN_MASK)); + Entry &= ~PTE_PPN_MASK; + return Entry | Ppn; +} + +/** + Free resources of translation table recursively. + + @param TranslationTable The pointer of table. + @param PageWalkCfg Page table configure value. + @param Level The current level. + +**/ +STATIC +VOID +FreePageTablesRecursive ( + IN UINTN *TranslationTable, + IN UINT64 PageWalkCfg, + IN UINTN Level + ) +{ + UINTN Index; + UINTN TableEntryNum; + + TableEntryNum = (1 << ParsePageTableBitWidth (PageWalkCfg)); + + if (Level < (ParseMaxPageTableLevel (PageWalkCfg) - 1)) { + for (Index = 0; Index < TableEntryNum; Index++) { + if (IsTableEntry (TranslationTable[Index], Level, PageWalkCfg)) { + FreePageTablesRecursive ( + (UINTN *)(GetPpnfromPte ((TranslationTable[Index])) << + LOONGARCH_MMU_PAGE_SHIFT), + PageWalkCfg, + Level + 1 + ); + } + } + } + + FreePages (TranslationTable, 1); +} + +/** + Update region mapping recursively. + + @param RegionStart The start address of the region. + @param RegionEnd The end address of the region. + @param AttributeSetMask The attribute mask to be set. + @param AttributeClearMask The attribute mask to be clear. + @param PageTable The pointer of current page table. + @param Level The current level. + @param PageWalkCfg Page table configure value. + @param TableIsLive TRUE if this is live update, FALSE otherwise. + + @retval EFI_OUT_OF_RESOURCES Not enough resource. + @retval EFI_SUCCESS The operation succesfully. + +**/ +STATIC +EFI_STATUS +UpdateRegionMappingRecursive ( + IN UINTN RegionStart, + IN UINTN RegionEnd, + IN UINTN AttributeSetMask, + IN UINTN AttributeClearMask, + IN UINTN *PageTable, + IN UINTN Level, + IN UINT64 PageWalkCfg, + IN BOOLEAN TableIsLive + ) +{ + EFI_STATUS Status; + UINTN BlockShift; + UINTN BlockMask; + UINTN BlockEnd; + UINTN *Entry; + UINTN EntryValue; + UINTN *TranslationTable; + UINTN TableEntryNum; + UINTN TableBitWidth; + BOOLEAN NextTableIsLive; + + ASSERT (Level < ParseMaxPageTableLevel (PageWalkCfg)); + ASSERT (((RegionStart | RegionEnd) & EFI_PAGE_MASK) == 0); + + TableBitWidth = ParsePageTableBitWidth (PageWalkCfg); + BlockShift = (ParseMaxPageTableLevel (PageWalkCfg) - Level - 1) * TableBitWidth + LOONGARCH_MMU_PAGE_SHIFT; + BlockMask = MAX_ADDRESS >> (64 - BlockShift); + + DEBUG ( + ( + DEBUG_VERBOSE, + "%a(%d): %llx - %llx set %lx clr %lx\n", + __func__, + Level, + RegionStart, + RegionEnd, + AttributeSetMask, + AttributeClearMask + ) + ); + + TableEntryNum = (1 << TableBitWidth); + for ( ; RegionStart < RegionEnd; RegionStart = BlockEnd) { + BlockEnd = MIN (RegionEnd, (RegionStart | BlockMask) + 1); + Entry = &PageTable[(RegionStart >> BlockShift) & (TableEntryNum - 1)]; + + // + // If RegionStart or BlockEnd is not aligned to the block size at this + // level, we will have to create a table mapping in order to map less + // than a block, and recurse to create the block or page entries at + // the next level. No block mappings are allowed at all at level 2, + // so in that case, we have to recurse unconditionally. + // + if ((Level < 2) || + (((RegionStart | BlockEnd) & BlockMask) != 0) || IsTableEntry (*Entry, Level, PageWalkCfg)) + { + ASSERT (Level < (ParseMaxPageTableLevel (PageWalkCfg) - 1)); + if (!IsTableEntry (*Entry, Level, PageWalkCfg)) { + // + // No table entry exists yet, so we need to allocate a page table + // for the next level. + // + TranslationTable = AllocatePages (1); + if (TranslationTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (TranslationTable, EFI_PAGE_SIZE); + + if (IsBlockEntry (*Entry, Level, PageWalkCfg)) { + // + // We are splitting an existing block entry, so we have to populate + // the new table with the attributes of the block entry it replaces. + // + Status = UpdateRegionMappingRecursive ( + RegionStart & ~BlockMask, + (RegionStart | BlockMask) + 1, + *Entry & PTE_ATTRIBUTES_MASK, + PTE_ATTRIBUTES_MASK, + TranslationTable, + Level + 1, + PageWalkCfg, + FALSE + ); + if (EFI_ERROR (Status)) { + // + // The range we passed to UpdateRegionMappingRecursive () is block + // aligned, so it is guaranteed that no further pages were allocated + // by it, and so we only have to free the page we allocated here. + // + FreePages (TranslationTable, 1); + return Status; + } + } + + NextTableIsLive = FALSE; + } else { + TranslationTable = (UINTN *)(GetPpnfromPte (*Entry) << LOONGARCH_MMU_PAGE_SHIFT); + NextTableIsLive = TableIsLive; + } + + // + // Recurse to the next level + // + Status = UpdateRegionMappingRecursive ( + RegionStart, + BlockEnd, + AttributeSetMask, + AttributeClearMask, + TranslationTable, + Level + 1, + PageWalkCfg, + NextTableIsLive + ); + if (EFI_ERROR (Status)) { + if (!IsTableEntry (*Entry, Level, PageWalkCfg)) { + // + // We are creating a new table entry, so on failure, we can free all + // allocations we made recursively, given that the whole subhierarchy + // has not been wired into the live page tables yet. (This is not + // possible for existing table entries, since we cannot revert the + // modifications we made to the subhierarchy it represents.) + // + FreePageTablesRecursive (TranslationTable, PageWalkCfg, Level + 1); + } + + return Status; + } + + if (!IsTableEntry (*Entry, Level, PageWalkCfg)) { + EntryValue = SetPpnToPte (0, (UINTN)TranslationTable); + ReplaceTableEntry ( + Entry, + EntryValue, + RegionStart, + TableIsLive + ); + } + } else { + EntryValue = (*Entry & ~AttributeClearMask) | AttributeSetMask; + + EntryValue = SetPpnToPte (EntryValue, RegionStart); + EntryValue = SetValidPte (EntryValue); + + if (Level < (ParseMaxPageTableLevel (PageWalkCfg) - 1)) { + EntryValue |= (PAGE_HGLOBAL | PAGE_HUGE | PAGE_VALID); + } else { + EntryValue |= PAGE_GLOBAL | PAGE_VALID; + } + + ReplaceTableEntry (Entry, EntryValue, RegionStart, TableIsLive); + } + } + + return EFI_SUCCESS; +} + +/** + Update region mapping at root table. + + @param RegionStart The start address of the region. + @param RegionLength The length of the region. + @param PageWalkCfg Page table configure value. + @param AttributeSetMask The attribute mask to be set. + @param AttributeClearMask The attribute mask to be clear. + @param RootTable The pointer of root table. + @param TableIsLive TRUE if this is live update, FALSE otherwise. + + @retval EFI_INVALID_PARAMETER The RegionStart or RegionLength was not valid. + @retval EFI_OUT_OF_RESOURCES Not enough resource. + @retval EFI_SUCCESS The operation succesfully. + +**/ +EFI_STATUS +UpdateRegionMapping ( + IN UINTN RegionStart, + IN UINTN RegionLength, + IN UINT64 PageWalkCfg, + IN UINTN AttributeSetMask, + IN UINTN AttributeClearMask, + IN UINTN *RootTable, + IN BOOLEAN TableIsLive + ) +{ + if (((RegionStart | RegionLength) & EFI_PAGE_MASK) != 0) { + return EFI_INVALID_PARAMETER; + } + + return UpdateRegionMappingRecursive ( + RegionStart, + RegionStart + RegionLength, + AttributeSetMask, + AttributeClearMask, + RootTable, + 0, + PageWalkCfg, + TableIsLive + ); +} + +/** + Convert EFI Attributes to Loongarch Attributes. + + @param[in] EfiAttributes Efi Attributes. + + @retval Corresponding architecture attributes. +**/ +UINT64 +EFIAPI +EfiAttributeConverse ( + IN UINT64 EfiAttributes + ) +{ + UINT64 LoongArchAttributes; + + LoongArchAttributes = PAGE_VALID | PAGE_DIRTY | PLV_KERNEL | PAGE_GLOBAL; + + switch (EfiAttributes & EFI_CACHE_ATTRIBUTE_MASK) { + case EFI_MEMORY_UC: + LoongArchAttributes |= CACHE_SUC; + break; + case EFI_MEMORY_WC: + LoongArchAttributes |= CACHE_WUC; + break; + case EFI_MEMORY_WT: + case EFI_MEMORY_WB: + LoongArchAttributes |= CACHE_CC; + break; + case EFI_MEMORY_WP: + LoongArchAttributes &= ~PAGE_DIRTY; + break; + default: + break; + } + + // Write protection attributes + switch (EfiAttributes & EFI_MEMORY_ACCESS_MASK) { + case EFI_MEMORY_RP: + LoongArchAttributes |= PAGE_NO_READ; + break; + case EFI_MEMORY_XP: + LoongArchAttributes |= PAGE_NO_EXEC; + break; + case EFI_MEMORY_RO: + LoongArchAttributes &= ~PAGE_DIRTY; + break; + default: + break; + } + + return LoongArchAttributes; +} + +/** + TLB refill handler configure. + + @param VOID. + + @retval EFI_SUCCESS TLB refill handler configure successfully. + EFI_UNSUPPORTED Size not aligned. +**/ +EFI_STATUS +TlbRefillHandlerConfigure ( + VOID + ) +{ + UINTN Length; + UINTN TlbReEntry; + UINTN TlbReEntryOffset; + UINTN Remaining; + + // + // Set TLB exception handler + // + /// + /// TLB Re-entry address at the end of exception vector, a vector is up to 512 bytes, + /// so the starting address is: total exception vector size + total interrupt vector size + base. + /// The total size of TLB handler and exception vector size and interrupt vector size should not + /// be lager than 64KB. + /// + Length = (UINTN)HandleTlbRefillEnd - (UINTN)HandleTlbRefillStart; + TlbReEntryOffset = (MAX_LOONGARCH_EXCEPTION + MAX_LOONGARCH_INTERRUPT) * 512; + Remaining = TlbReEntryOffset % SIZE_4KB; + if (Remaining != 0x0) { + TlbReEntryOffset += (SIZE_4KB - Remaining); + } + + TlbReEntry = PcdGet64 (PcdLoongArchExceptionVectorBaseAddress) + TlbReEntryOffset; + if ((TlbReEntryOffset + Length) > SIZE_64KB) { + return EFI_UNSUPPORTED; + } + + // + // Ensure that TLB refill exception base address alignment is equals to 4KB and is valid. + // + if (TlbReEntry & (SIZE_4KB - 1)) { + return EFI_UNSUPPORTED; + } + + CopyMem ((VOID *)TlbReEntry, HandleTlbRefillStart, Length); + InvalidateInstructionCacheRange ((VOID *)(UINTN)HandleTlbRefillStart, Length); + + // + // Set the address of TLB refill exception handler + // + SetTlbRebaseAddress ((UINTN)TlbReEntry); + + return EFI_SUCCESS; +} + +/** + Maps the memory region in the page table to the specified attributes. + + @param[in, out] PageTable The pointer to the page table to update, or pointer to NULL + if a new page table is to be created. + @param[in] PageWalkCfg The page walk controller configure. + @param[in] BaseAddress The base address of the memory region to set the Attributes. + @param[in] Length The length of the memory region to set the Attributes. + @param[in] Attributes The bitmask of attributes to set, which refer to UEFI SPEC + 7.2.3(EFI_BOOT_SERVICES.GetMemoryMap()). + @param[in] AttributeMask Mask of memory attributes to take into account. + + @retval EFI_SUCCESS The Attributes was set successfully or Length is 0. + @retval EFI_INVALID_PARAMETER PageTable is NULL, PageWalkCfg is invalid. + @retval EFI_UNSUPPORTED *PageTable is NULL. +**/ +EFI_STATUS +EFIAPI +MemoryRegionMap ( + IN OUT UINTN *PageTable OPTIONAL, + IN UINT64 PageWalkCfg, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes, + IN UINT64 AttributeMask + ) +{ + EFI_STATUS Status; + UINT64 LoongArchAttributes; + BOOLEAN Initialization; + BOOLEAN CreateNew; + UINTN PageTableBitWidth; + UINTN MaxLevel; + UINTN PgdSize; + + if ((PageTable == NULL) || (PageWalkCfg == 0)) { + return EFI_INVALID_PARAMETER; + } + + PageTableBitWidth = ParsePageTableBitWidth (PageWalkCfg); + MaxLevel = ParseMaxPageTableLevel (PageWalkCfg); + + if ((!PageTableBitWidth && !MaxLevel) || (PageTableBitWidth > 0x1F) || (MaxLevel > LEVEL5)) { + return EFI_INVALID_PARAMETER; + } + + Initialization = FALSE; + CreateNew = FALSE; + + // + // *PageTable is NULL, create a new and return. + // + if (*PageTable == 0) { + CreateNew = TRUE; + // + // If the MMU has not been configured yet, configure it later. + // + if (!MmuIsInit ()) { + Initialization = TRUE; + } + } + + if (Length == 0) { + return EFI_SUCCESS; + } + + if (Initialization == TRUE) { + Status = TlbRefillHandlerConfigure (); + ASSERT_EFI_ERROR (Status); + } + + if (CreateNew == TRUE) { + // + // Create a new page table. + // + PgdSize = (1 << PageTableBitWidth) * sizeof (UINTN); + *PageTable = (UINTN)AllocatePages (EFI_SIZE_TO_PAGES (PgdSize)); + ZeroMem ((VOID *)*PageTable, PgdSize); + + if ((VOID *)*PageTable == NULL) { + return EFI_UNSUPPORTED; + } + } + + LoongArchAttributes = EfiAttributeConverse (Attributes); + + // + // Update the page table attributes. + // + // If the MMU has been configured and *PageTable == CSR_PGDL, the page table in use will update. + // + // If *PageTable != CSR_PGDL, only the page table structure in memory is update, but some TLB + // region may be invalidated during the mapping process. So at this time the caller must ensure + // that the execution environment must be safe. It is recommended to use the DA mode! + // + Status = UpdateRegionMapping ( + BaseAddress, + Length, + PageWalkCfg, + LoongArchAttributes, + PTE_ATTRIBUTES_MASK, + (UINTN *)(*PageTable), + (MmuIsEnabled () && !CreateNew) + ); + + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h new file mode 100644 index 0000000000..b115245d68 --- /dev/null +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/Page.h @@ -0,0 +1,33 @@ +/** @file + + Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef PAGE_H_ +#define PAGE_H_ + +#define INVALID_PAGE 0 + +#define LEVEL5 5 +#define LEVEL4 4 +#define LEVEL3 3 +#define LEVEL2 2 +#define LEVEL1 1 + +#define PTE_ATTRIBUTES_MASK 0x600000000000007EULL + +#define PTE_PPN_MASK 0xFFFFFFFFF000ULL +#define PTE_PPN_SHIFT EFI_PAGE_SHIFT +#define LOONGARCH_MMU_PAGE_SHIFT EFI_PAGE_SHIFT + +// +// For coding convenience, define the maximum valid +// LoongArch exception. +// Since UEFI V2.11, it will be present in DebugSupport.h. +// +#define MAX_LOONGARCH_EXCEPTION 64 + +#endif // PAGE_H_ diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.S b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.S new file mode 100644 index 0000000000..4395b574f5 --- /dev/null +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.S @@ -0,0 +1,51 @@ +#------------------------------------------------------------------------------ +# +# TLB refill exception handler +# +# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#----------------------------------------------------------------------------- + +#include + +ASM_GLOBAL ASM_PFX(HandleTlbRefillStart) +ASM_GLOBAL ASM_PFX(HandleTlbRefillEnd) + +# +# Refill the page table. +# @param VOID +# @retval VOID +# +ASM_PFX(HandleTlbRefillStart): + csrwr $t0, LOONGARCH_CSR_TLBRSAVE + csrrd $t0, LOONGARCH_CSR_PWCTL1 + srli.d $t0, $t0, 18 + andi $t0, $t0, 0x3F + bnez $t0, Level5 + csrrd $t0, LOONGARCH_CSR_PWCTL1 + srli.d $t0, $t0, 6 + andi $t0, $t0, 0x3F + bnez $t0, Level4 + csrrd $t0, LOONGARCH_CSR_PGD + b Level3 +Level5: + csrrd $t0, LOONGARCH_CSR_PGD + lddir $t0, $t0, 4 #Put pud BaseAddress into T0 + lddir $t0, $t0, 3 #Put pud BaseAddress into T0 + b Level3 +Level4: + csrrd $t0, LOONGARCH_CSR_PGD + lddir $t0, $t0, 3 #Put pud BaseAddress into T0 +Level3: + lddir $t0, $t0, 2 #Put pmd BaseAddress into T0 + lddir $t0, $t0, 1 #Put pte BaseAddress into T0 + ldpte $t0, 0 + ldpte $t0, 1 + tlbfill // refill hi, lo0, lo1 + csrrd $t0, LOONGARCH_CSR_TLBRSAVE + ertn +ASM_PFX(HandleTlbRefillEnd): + + .end diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.h new file mode 100644 index 0000000000..c164db567d --- /dev/null +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbExceptionHandle.h @@ -0,0 +1,36 @@ +/** @file + + Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef TLB_EXCEPTION_HANDLE_H_ +#define TLB_EXCEPTION_HANDLE_H_ + +/** + TLB refill handler start. + + @param none + + @retval none +**/ +VOID +HandleTlbRefillStart ( + VOID + ); + +/** + TLB refill handler end. + + @param none + + @retval none +**/ +VOID +HandleTlbRefillEnd ( + VOID + ); + +#endif // TLB_EXCEPTION_HANDLE_H_ diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.S b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.S new file mode 100644 index 0000000000..676aada240 --- /dev/null +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.S @@ -0,0 +1,24 @@ +#------------------------------------------------------------------------------ +# +# Invalid TLB operation function +# +# Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#----------------------------------------------------------------------------- + +#include + +ASM_GLOBAL ASM_PFX(InvalidTlb) + +# +# Invalid corresponding TLB entries are based on the address given +# @param a0 The address corresponding to the invalid page table entry +# @retval none +# +ASM_PFX(InvalidTlb): + invtlb INVTLB_ADDR_GTRUE_OR_ASID, $zero, $a0 + jirl $zero, $ra, 0 + + .end diff --git a/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.h b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.h new file mode 100644 index 0000000000..5063e6662b --- /dev/null +++ b/UefiCpuPkg/Library/CpuMmuLib/LoongArch64/TlbInvalid.h @@ -0,0 +1,24 @@ +/** @file + + Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef INVALID_TLB_H_ +#define INVALID_TLB_H_ + +/** + Invalid corresponding TLB entries are based on the address given + + @param Address The address corresponding to the invalid page table entry + + @retval none +**/ +VOID +InvalidTlb ( + UINTN Address + ); + +#endif // INVALID_TLB_H_ diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc index 2eebd45125..e92ceb6466 100644 --- a/UefiCpuPkg/UefiCpuPkg.dsc +++ b/UefiCpuPkg/UefiCpuPkg.dsc @@ -211,5 +211,8 @@ [Components.RISCV64] UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf UefiCpuPkg/CpuMmio2Dxe/CpuMmio2Dxe.inf +[Components.LOONGARCH64] + UefiCpuPkg/Library/CpuMmuLib/CpuMmuLib.inf + [BuildOptions] *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES -- 2.27.0 -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#117833): https://edk2.groups.io/g/devel/message/117833 Mute This Topic: https://groups.io/mt/105550159/7686176 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io] -=-=-=-=-=-=-=-=-=-=-=-