From nobody Sun Feb 8 05:40:28 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) client-ip=66.175.222.108; envelope-from=bounce+27952+106944+1787277+3901457@groups.io; helo=mail02.groups.io; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+106944+1787277+3901457@groups.io ARC-Seal: i=1; a=rsa-sha256; t=1689361736; cv=none; d=zohomail.com; s=zohoarc; b=FZthZN13Zu+rI8YdK1ctjePmxJ+Jdkirvn0zJil3si4qc/S/bbjs4ZcRoDQzHMZ/uFVHLpknzzc0LGVswsuKzT/Isr3fTqaVNM8iTu4Be3OceX1S7R4rwJD1366GWWJDUXFqaYMo//YJ75hx9uNQoeb8aBh6vIU0TnP7INsEOmY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1689361736; h=Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=UDoiMjd8HjDdWH4Ic0Z1QXxSz578j0vI+0id758gFiA=; b=G0yXRybhB8d2UlFFaOFrguW/MXlTF7Yl+O6PIiWb4yZTlEavFN7ya2qZ7tRSE78io14GCweTylqMQcf484y37YgB65eu2NGnDMYMrsXHxJf0dxawxGnK5jL5fl2gTUht+kJJCglXcwiEhP5Ja2CAOL0SqAGcVVBtTqqM4+HXu3Y= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+106944+1787277+3901457@groups.io Received: from mail02.groups.io (mail02.groups.io [66.175.222.108]) by mx.zohomail.com with SMTPS id 1689361736945820.2939841476675; Fri, 14 Jul 2023 12:08:56 -0700 (PDT) Return-Path: DKIM-Signature: a=rsa-sha256; bh=sRShno43ZRd1jZLB7tjXODLOiPQwoi9VtffJR8ZXoos=; c=relaxed/simple; d=groups.io; h=X-Received:X-Received:X-Received:X-Gm-Message-State:X-Google-Smtp-Source:X-Received:X-Received:From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References:MIME-Version:Precedence:List-Unsubscribe:List-Subscribe:List-Help:Sender:List-Id:Mailing-List:Delivered-To:Reply-To:Content-Transfer-Encoding; s=20140610; t=1689361736; v=1; b=fYENHOcxZc+3eRjsxRQKer8k54CrkVzPKuoFf329b97mUS6NF7lHhJXZaNGWBzeMWsZZD0o7 USE/hyWqAZ3pHcCHOsw/UpDr0MCFcHZIjInWz0RhbwrmMAWF2XKaj6qQJBiEjMAryfZWuG2HXxa n+WL7ZGqoJxNp+areXO9f4Pk= X-Received: by 127.0.0.2 with SMTP id QqXEYY1788612xeQxKfXqa8I; Fri, 14 Jul 2023 12:08:56 -0700 X-Received: from mail-pl1-f170.google.com (mail-pl1-f170.google.com [209.85.214.170]) by mx.groups.io with SMTP id smtpd.web11.3998.1689361735962712542 for ; Fri, 14 Jul 2023 12:08:56 -0700 X-Received: by mail-pl1-f170.google.com with SMTP id d9443c01a7336-1b852785a65so14692215ad.0 for ; Fri, 14 Jul 2023 12:08:55 -0700 (PDT) X-Gm-Message-State: mcs6awwBoRRdfbdmuw2jEuYFx1787277AA= X-Google-Smtp-Source: APBJJlHdcbIrMEMDskNDDr1BxOr8lwKgkiVWlxQuUBmrOkfsBdkYfX0/R0elVXoR7KikX6j3DqqY4A== X-Received: by 2002:a17:902:e5c4:b0:1b6:6a14:3734 with SMTP id u4-20020a170902e5c400b001b66a143734mr4771435plf.29.1689361734733; Fri, 14 Jul 2023 12:08:54 -0700 (PDT) X-Received: from localhost.localdomain (c-174-50-177-95.hsd1.ca.comcast.net. [174.50.177.95]) by smtp.gmail.com with ESMTPSA id p18-20020a170902ead200b001a98f844e60sm8041413pld.263.2023.07.14.12.08.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 14 Jul 2023 12:08:54 -0700 (PDT) From: "Tuan Phan" To: devel@edk2.groups.io Cc: michael.d.kinney@intel.com, gaoliming@byosoft.com.cn, zhiguang.liu@intel.com, sunilvl@ventanamicro.com, git@danielschaefer.me, andrei.warkentin@intel.com, ardb+tianocore@kernel.org, Tuan Phan Subject: [edk2-devel] [PATCH v5 7/7] UefiCpuPkg: RISC-V: Support MMU with SV39/48/57 mode Date: Fri, 14 Jul 2023 12:08:24 -0700 Message-Id: <20230714190824.16552-8-tphan@ventanamicro.com> In-Reply-To: <20230714190824.16552-1-tphan@ventanamicro.com> References: <20230714190824.16552-1-tphan@ventanamicro.com> MIME-Version: 1.0 Precedence: Bulk List-Unsubscribe: List-Subscribe: List-Help: Sender: devel@edk2.groups.io List-Id: Mailing-List: list devel@edk2.groups.io; contact devel+owner@edk2.groups.io Reply-To: devel@edk2.groups.io,tphan@ventanamicro.com Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @groups.io) X-ZM-MESSAGEID: 1689361738273100027 Content-Type: text/plain; charset="utf-8" During CpuDxe initialization, MMU will be setup with the highest mode that HW supports. Signed-off-by: Tuan Phan Reviewed-by: Andrei Warkentin --- OvmfPkg/RiscVVirt/RiscVVirt.dsc.inc | 1 + UefiCpuPkg/CpuDxeRiscV64/CpuDxe.c | 9 +- UefiCpuPkg/CpuDxeRiscV64/CpuDxe.h | 2 + UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf | 2 + UefiCpuPkg/Include/Library/BaseRiscVMmuLib.h | 68 ++ .../Library/BaseRiscVMmuLib/BaseRiscVMmuLib.c | 730 ++++++++++++++++++ .../BaseRiscVMmuLib/BaseRiscVMmuLib.inf | 27 + .../Library/BaseRiscVMmuLib/RiscVMmuCore.S | 31 + UefiCpuPkg/UefiCpuPkg.dec | 5 + UefiCpuPkg/UefiCpuPkg.dsc | 1 + 10 files changed, 874 insertions(+), 2 deletions(-) create mode 100644 UefiCpuPkg/Include/Library/BaseRiscVMmuLib.h create mode 100644 UefiCpuPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.c create mode 100644 UefiCpuPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.inf create mode 100644 UefiCpuPkg/Library/BaseRiscVMmuLib/RiscVMmuCore.S diff --git a/OvmfPkg/RiscVVirt/RiscVVirt.dsc.inc b/OvmfPkg/RiscVVirt/RiscVV= irt.dsc.inc index 731f54f73f81..bc204ba5fe52 100644 --- a/OvmfPkg/RiscVVirt/RiscVVirt.dsc.inc +++ b/OvmfPkg/RiscVVirt/RiscVVirt.dsc.inc @@ -83,6 +83,7 @@ # RISC-V Architectural Libraries CpuExceptionHandlerLib|UefiCpuPkg/Library/BaseRiscV64CpuExceptionHandler= Lib/BaseRiscV64CpuExceptionHandlerLib.inf RiscVSbiLib|MdePkg/Library/BaseRiscVSbiLib/BaseRiscVSbiLib.inf + RiscVMmuLib|UefiCpuPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.inf PlatformBootManagerLib|OvmfPkg/RiscVVirt/Library/PlatformBootManagerLib/= PlatformBootManagerLib.inf ResetSystemLib|OvmfPkg/RiscVVirt/Library/ResetSystemLib/BaseResetSystemL= ib.inf =20 diff --git a/UefiCpuPkg/CpuDxeRiscV64/CpuDxe.c b/UefiCpuPkg/CpuDxeRiscV64/C= puDxe.c index 25fe3f54c325..2af3b6223450 100644 --- a/UefiCpuPkg/CpuDxeRiscV64/CpuDxe.c +++ b/UefiCpuPkg/CpuDxeRiscV64/CpuDxe.c @@ -296,8 +296,7 @@ CpuSetMemoryAttributes ( IN UINT64 Attributes ) { - DEBUG ((DEBUG_INFO, "%a: Set memory attributes not supported yet\n", __f= unc__)); - return EFI_SUCCESS; + return RiscVSetMemoryAttributes (BaseAddress, Length, Attributes); } =20 /** @@ -340,6 +339,12 @@ InitializeCpu ( // DisableInterrupts (); =20 + // + // Enable MMU + // + Status =3D RiscVConfigureMmu (); + ASSERT_EFI_ERROR (Status); + // // Install Boot protocol // diff --git a/UefiCpuPkg/CpuDxeRiscV64/CpuDxe.h b/UefiCpuPkg/CpuDxeRiscV64/C= puDxe.h index 49f4e119665a..68e6d038b66e 100644 --- a/UefiCpuPkg/CpuDxeRiscV64/CpuDxe.h +++ b/UefiCpuPkg/CpuDxeRiscV64/CpuDxe.h @@ -15,11 +15,13 @@ #include #include #include +#include #include #include #include #include #include +#include =20 /** Flush CPU data cache. If the instruction cache is fully coherent diff --git a/UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf b/UefiCpuPkg/CpuDxe= RiscV64/CpuDxeRiscV64.inf index e8fa25446aef..9d9a5ef8f247 100644 --- a/UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf +++ b/UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf @@ -37,6 +37,8 @@ TimerLib PeCoffGetEntryPointLib RiscVSbiLib + RiscVMmuLib + CacheMaintenanceLib =20 [Sources] CpuDxe.c diff --git a/UefiCpuPkg/Include/Library/BaseRiscVMmuLib.h b/UefiCpuPkg/Incl= ude/Library/BaseRiscVMmuLib.h new file mode 100644 index 000000000000..f1e609d28dc2 --- /dev/null +++ b/UefiCpuPkg/Include/Library/BaseRiscVMmuLib.h @@ -0,0 +1,68 @@ +/** @file + + Copyright (c) 2015 - 2016, Linaro Ltd. All rights reserved.
+ Copyright (c) 2023, Ventana Micro Systems Inc. All Rights Reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef BASE_RISCV_MMU_LIB_H_ +#define BASE_RISCV_MMU_LIB_H_ + +/** + The API to flush all local TLBs. + +**/ +VOID +EFIAPI +RiscVLocalTlbFlushAll ( + VOID + ); + +/** + The API to flush local TLB at a virtual address. + + @param VirtAddr The virtual address. + +**/ +VOID +EFIAPI +RiscVLocalTlbFlush ( + UINTN VirtAddr + ); + +/** + The API to set a GCD attribute on an memory region. + + @param BaseAddress The base address of the region. + @param Length The length of the region. + @param Attributes The GCD attributes. + + @retval EFI_INVALID_PARAMETER The BaseAddress or Length was not valid. + @retval EFI_OUT_OF_RESOURCES Not enough resource. + @retval EFI_SUCCESS The operation succesfully. + +**/ +EFI_STATUS +EFIAPI +RiscVSetMemoryAttributes ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ); + +/** + The API to configure and enable RISC-V MMU with the highest mode support= ed. + + @retval EFI_OUT_OF_RESOURCES Not enough resource. + @retval EFI_SUCCESS The operation succesfully. + +**/ +EFI_STATUS +EFIAPI +RiscVConfigureMmu ( + VOID + ); + +#endif /* BASE_RISCV_MMU_LIB_H_ */ diff --git a/UefiCpuPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.c b/UefiCpu= Pkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.c new file mode 100644 index 000000000000..9cca5fc128af --- /dev/null +++ b/UefiCpuPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.c @@ -0,0 +1,730 @@ +/** @file + MMU library for RISC-V. + + 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.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RISCV_PG_V BIT0 +#define RISCV_PG_R BIT1 +#define RISCV_PG_W BIT2 +#define RISCV_PG_X BIT3 +#define RISCV_PG_G BIT5 +#define RISCV_PG_A BIT6 +#define RISCV_PG_D BIT7 +#define PTE_ATTRIBUTES_MASK 0xE + +#define PTE_PPN_MASK 0x3FFFFFFFFFFC00ULL +#define PTE_PPN_SHIFT 10 +#define RISCV_MMU_PAGE_SHIFT 12 + +STATIC UINTN mModeSupport[] =3D { SATP_MODE_SV57, SATP_MODE_SV48, SATP_MO= DE_SV39 }; +STATIC UINTN mMaxRootTableLevel; +STATIC UINTN mBitPerLevel; +STATIC UINTN mTableEntryCount; + +/** + Determine if the MMU enabled or not. + + @retval TRUE The MMU already enabled. + @retval FALSE The MMU not enabled. + +**/ +STATIC +BOOLEAN +RiscVMmuEnabled ( + VOID + ) +{ + return ((RiscVGetSupervisorAddressTranslationRegister () & + SATP64_MODE) !=3D (SATP_MODE_OFF << SATP64_MODE_SHIFT)); +} + +/** + Retrieve the root translate table. + + @return The root translate table. + +**/ +STATIC +UINTN +RiscVGetRootTranslateTable ( + VOID + ) +{ + return (RiscVGetSupervisorAddressTranslationRegister () & SATP64_PPN) << + RISCV_MMU_PAGE_SHIFT; +} + +/** + 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 & RISCV_PG_V) =3D=3D 0) || + (((Entry & (RISCV_PG_R | RISCV_PG_W)) =3D=3D RISCV_PG_W))) + { + return FALSE; + } + + return TRUE; +} + +/** + 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 | RISCV_PG_G | RISCV_PG_V; +} + +/** + Determine if an entry is a block pte. + + @param Entry The entry value. + + @retval TRUE The entry is a block pte. + @retval FALSE The entry is not a block pte. + +**/ +STATIC +BOOLEAN +IsBlockEntry ( + IN UINTN Entry + ) +{ + return IsValidPte (Entry) && + (Entry & (RISCV_PG_X | RISCV_PG_R)); +} + +/** + Determine if an entry is a table pte. + + @param Entry The entry value. + + @retval TRUE The entry is a table pte. + @retval FALSE The entry is not a table pte. + +**/ +STATIC +BOOLEAN +IsTableEntry ( + IN UINTN Entry + ) +{ + return IsValidPte (Entry) && + !IsBlockEntry (Entry); +} + +/** + Set an entry to be a table pte. + + @param Entry The entry value. + + @return The entry value. + +**/ +STATIC +UINTN +SetTableEntry ( + IN UINTN Entry + ) +{ + Entry =3D SetValidPte (Entry); + Entry &=3D ~(RISCV_PG_X | RISCV_PG_W | RISCV_PG_R); + + return 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 =3D Value; + + if (IsLiveBlockMapping && RiscVMmuEnabled ()) { + RiscVLocalTlbFlush (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 =3D ((Address >> RISCV_MMU_PAGE_SHIFT) << PTE_PPN_SHIFT); + ASSERT (~(Ppn & ~PTE_PPN_MASK)); + Entry &=3D ~PTE_PPN_MASK; + return Entry | Ppn; +} + +/** + Free resources of translation table recursively. + + @param TranslationTable The pointer of table. + @param Level The current level. + +**/ +STATIC +VOID +FreePageTablesRecursive ( + IN UINTN *TranslationTable, + IN UINTN Level + ) +{ + UINTN Index; + + if (Level < mMaxRootTableLevel - 1) { + for (Index =3D 0; Index < mTableEntryCount; Index++) { + if (IsTableEntry (TranslationTable[Index])) { + FreePageTablesRecursive ( + (UINTN *)(GetPpnfromPte ((TranslationTable[Index])) << + RISCV_MMU_PAGE_SHIFT), + 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 TableIsLive TRUE if this is live update, FALSE otherwi= se. + + @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 BOOLEAN TableIsLive + ) +{ + EFI_STATUS Status; + UINTN BlockShift; + UINTN BlockMask; + UINTN BlockEnd; + UINTN *Entry; + UINTN EntryValue; + UINTN *TranslationTable; + BOOLEAN NextTableIsLive; + + ASSERT (Level < mMaxRootTableLevel); + ASSERT (((RegionStart | RegionEnd) & EFI_PAGE_MASK) =3D=3D 0); + + BlockShift =3D (mMaxRootTableLevel - Level - 1) * mBitPerLevel + RISCV_M= MU_PAGE_SHIFT; + BlockMask =3D MAX_ADDRESS >> (64 - BlockShift); + + DEBUG ( + ( + DEBUG_VERBOSE, + "%a(%d): %llx - %llx set %lx clr %lx\n", + __func__, + Level, + RegionStart, + RegionEnd, + AttributeSetMask, + AttributeClearMask + ) + ); + + for ( ; RegionStart < RegionEnd; RegionStart =3D BlockEnd) { + BlockEnd =3D MIN (RegionEnd, (RegionStart | BlockMask) + 1); + Entry =3D &PageTable[(RegionStart >> BlockShift) & (mTableEntryCoun= t - 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 0, + // so in that case, we have to recurse unconditionally. + // + if ((Level =3D=3D 0) || + (((RegionStart | BlockEnd) & BlockMask) !=3D 0) || IsTableEntry (*= Entry)) + { + ASSERT (Level < mMaxRootTableLevel - 1); + if (!IsTableEntry (*Entry)) { + // + // No table entry exists yet, so we need to allocate a page table + // for the next level. + // + TranslationTable =3D AllocatePages (1); + if (TranslationTable =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (TranslationTable, EFI_PAGE_SIZE); + + if (IsBlockEntry (*Entry)) { + // + // We are splitting an existing block entry, so we have to popul= ate + // the new table with the attributes of the block entry it repla= ces. + // + Status =3D UpdateRegionMappingRecursive ( + RegionStart & ~BlockMask, + (RegionStart | BlockMask) + 1, + *Entry & PTE_ATTRIBUTES_MASK, + PTE_ATTRIBUTES_MASK, + TranslationTable, + Level + 1, + FALSE + ); + if (EFI_ERROR (Status)) { + // + // The range we passed to UpdateRegionMappingRecursive () is b= lock + // aligned, so it is guaranteed that no further pages were all= ocated + // by it, and so we only have to free the page we allocated he= re. + // + FreePages (TranslationTable, 1); + return Status; + } + } + + NextTableIsLive =3D FALSE; + } else { + TranslationTable =3D (UINTN *)(GetPpnfromPte (*Entry) << RISCV_MMU= _PAGE_SHIFT); + NextTableIsLive =3D TableIsLive; + } + + // + // Recurse to the next level + // + Status =3D UpdateRegionMappingRecursive ( + RegionStart, + BlockEnd, + AttributeSetMask, + AttributeClearMask, + TranslationTable, + Level + 1, + NextTableIsLive + ); + if (EFI_ERROR (Status)) { + if (!IsTableEntry (*Entry)) { + // + // We are creating a new table entry, so on failure, we can free= all + // allocations we made recursively, given that the whole subhier= archy + // has not been wired into the live page tables yet. (This is not + // possible for existing table entries, since we cannot revert t= he + // modifications we made to the subhierarchy it represents.) + // + FreePageTablesRecursive (TranslationTable, Level + 1); + } + + return Status; + } + + if (!IsTableEntry (*Entry)) { + EntryValue =3D SetPpnToPte (0, (UINTN)TranslationTable); + EntryValue =3D SetTableEntry (EntryValue); + ReplaceTableEntry ( + Entry, + EntryValue, + RegionStart, + TableIsLive + ); + } + } else { + EntryValue =3D (*Entry & ~AttributeClearMask) | AttributeSetMask; + // + // We don't have page fault exception handler when a virtual page is= accessed and + // the A bit is clear, or is written and the D bit is clear. + // So just set A for read and D for write permission. + // + if ((AttributeSetMask & RISCV_PG_R) !=3D 0) { + EntryValue |=3D RISCV_PG_A; + } + + if ((AttributeSetMask & RISCV_PG_W) !=3D 0) { + EntryValue |=3D RISCV_PG_D; + } + + EntryValue =3D SetPpnToPte (EntryValue, RegionStart); + EntryValue =3D SetValidPte (EntryValue); + 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 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 otherwi= se. + + @retval EFI_INVALID_PARAMETER The RegionStart or RegionLength was not va= lid. + @retval EFI_OUT_OF_RESOURCES Not enough resource. + @retval EFI_SUCCESS The operation succesfully. + +**/ +STATIC +EFI_STATUS +UpdateRegionMapping ( + IN UINTN RegionStart, + IN UINTN RegionLength, + IN UINTN AttributeSetMask, + IN UINTN AttributeClearMask, + IN UINTN *RootTable, + IN BOOLEAN TableIsLive + ) +{ + if (((RegionStart | RegionLength) & EFI_PAGE_MASK) !=3D 0) { + return EFI_INVALID_PARAMETER; + } + + return UpdateRegionMappingRecursive ( + RegionStart, + RegionStart + RegionLength, + AttributeSetMask, + AttributeClearMask, + RootTable, + 0, + TableIsLive + ); +} + +/** + Convert GCD attribute to RISC-V page attribute. + + @param GcdAttributes The GCD attribute. + + @return The RISC-V page attribute. + +**/ +STATIC +UINTN +GcdAttributeToPageAttribute ( + IN UINTN GcdAttributes + ) +{ + UINTN RiscVAttributes; + + RiscVAttributes =3D RISCV_PG_R | RISCV_PG_W | RISCV_PG_X; + + // Determine protection attributes + if ((GcdAttributes & EFI_MEMORY_RO) !=3D 0) { + RiscVAttributes &=3D ~(RISCV_PG_W); + } + + // Process eXecute Never attribute + if ((GcdAttributes & EFI_MEMORY_XP) !=3D 0) { + RiscVAttributes &=3D ~RISCV_PG_X; + } + + return RiscVAttributes; +} + +/** + The API to set a GCD attribute on an memory region. + + @param BaseAddress The base address of the region. + @param Length The length of the region. + @param Attributes The GCD attributes. + + @retval EFI_INVALID_PARAMETER The BaseAddress or Length was not valid. + @retval EFI_OUT_OF_RESOURCES Not enough resource. + @retval EFI_SUCCESS The operation succesfully. + +**/ +EFI_STATUS +EFIAPI +RiscVSetMemoryAttributes ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINTN Length, + IN UINTN Attributes + ) +{ + UINTN PageAttributesSet; + + PageAttributesSet =3D GcdAttributeToPageAttribute (Attributes); + + if (!RiscVMmuEnabled ()) { + return EFI_SUCCESS; + } + + DEBUG ( + ( + DEBUG_VERBOSE, + "%a: Set %llX page attribute 0x%X\n", + __func__, + BaseAddress, + PageAttributesSet + ) + ); + + return UpdateRegionMapping ( + BaseAddress, + Length, + PageAttributesSet, + PTE_ATTRIBUTES_MASK, + (UINTN *)RiscVGetRootTranslateTable (), + TRUE + ); +} + +/** + Set SATP mode. + + @param SatpMode The SATP mode to be set. + + @retval EFI_INVALID_PARAMETER The SATP mode was not valid. + @retval EFI_OUT_OF_RESOURCES Not enough resource. + @retval EFI_DEVICE_ERROR The SATP mode not supported by HW. + @retval EFI_SUCCESS The operation succesfully. + +**/ +STATIC +EFI_STATUS +RiscVMmuSetSatpMode ( + UINTN SatpMode + ) +{ + VOID *TranslationTable; + UINTN SatpReg; + UINTN Ppn; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemoryMap; + UINTN NumberOfDescriptors; + UINTN Index; + EFI_STATUS Status; + + switch (SatpMode) { + case SATP_MODE_OFF: + return EFI_SUCCESS; + case SATP_MODE_SV39: + mMaxRootTableLevel =3D 3; + mBitPerLevel =3D 9; + mTableEntryCount =3D 512; + break; + case SATP_MODE_SV48: + mMaxRootTableLevel =3D 4; + mBitPerLevel =3D 9; + mTableEntryCount =3D 512; + break; + case SATP_MODE_SV57: + mMaxRootTableLevel =3D 5; + mBitPerLevel =3D 9; + mTableEntryCount =3D 512; + break; + default: + return EFI_INVALID_PARAMETER; + } + + // Allocate pages for translation table + TranslationTable =3D AllocatePages (1); + if (TranslationTable =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (TranslationTable, mTableEntryCount * sizeof (UINTN)); + + NumberOfDescriptors =3D 0; + MemoryMap =3D NULL; + Status =3D gDS->GetMemorySpaceMap ( + &NumberOfDescriptors, + &MemoryMap + ); + ASSERT_EFI_ERROR (Status); + + for (Index =3D 0; Index < NumberOfDescriptors; Index++) { + if (MemoryMap[Index].GcdMemoryType =3D=3D EfiGcdMemoryTypeMemoryMapped= Io) { + // Default Read/Write attribute for memory mapped IO + UpdateRegionMapping ( + MemoryMap[Index].BaseAddress, + MemoryMap[Index].Length, + RISCV_PG_R | RISCV_PG_W, + PTE_ATTRIBUTES_MASK, + TranslationTable, + FALSE + ); + } else if (MemoryMap[Index].GcdMemoryType =3D=3D EfiGcdMemoryTypeSyste= mMemory) { + // Default Read/Write/Execute attribute for system memory + UpdateRegionMapping ( + MemoryMap[Index].BaseAddress, + MemoryMap[Index].Length, + RISCV_PG_R | RISCV_PG_W | RISCV_PG_X, + PTE_ATTRIBUTES_MASK, + TranslationTable, + FALSE + ); + } + } + + FreePool ((VOID *)MemoryMap); + + if (GetInterruptState ()) { + DisableInterrupts (); + } + + Ppn =3D (UINTN)TranslationTable >> RISCV_MMU_PAGE_SHIFT; + ASSERT (!(Ppn & ~(SATP64_PPN))); + + SatpReg =3D Ppn; + SatpReg |=3D (SatpMode << + SATP64_MODE_SHIFT) & SATP64_MODE; + RiscVSetSupervisorAddressTranslationRegister (SatpReg); + /* Check if HW support the setup satp mode */ + if (SatpReg !=3D RiscVGetSupervisorAddressTranslationRegister ()) { + DEBUG ( + ( + DEBUG_VERBOSE, + "%a: HW does not support SATP mode:%d\n", + __func__, + SatpMode + ) + ); + FreePageTablesRecursive (TranslationTable, 0); + return EFI_DEVICE_ERROR; + } + + RiscVLocalTlbFlushAll (); + + if (GetInterruptState ()) { + EnableInterrupts (); + } + + return Status; +} + +/** + The API to configure and enable RISC-V MMU with the highest mode support= ed. + + @retval EFI_OUT_OF_RESOURCES Not enough resource. + @retval EFI_SUCCESS The operation succesfully. + +**/ +EFI_STATUS +EFIAPI +RiscVConfigureMmu ( + VOID + ) +{ + EFI_STATUS Status; + INTN Idx; + + Status =3D EFI_SUCCESS; + + /* Try to setup MMU with highest mode as possible */ + for (Idx =3D 0; Idx < ARRAY_SIZE (mModeSupport); Idx++) { + Status =3D RiscVMmuSetSatpMode (mModeSupport[Idx]); + if (Status =3D=3D EFI_DEVICE_ERROR) { + continue; + } else if (EFI_ERROR (Status)) { + return Status; + } + + DEBUG ( + ( + DEBUG_INFO, + "%a: SATP mode %d successfully configured\n", + __func__, + mModeSupport[Idx] + ) + ); + break; + } + + return Status; +} diff --git a/UefiCpuPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.inf b/UefiC= puPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.inf new file mode 100644 index 000000000000..9b28a98cb346 --- /dev/null +++ b/UefiCpuPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.inf @@ -0,0 +1,27 @@ +## @file +# RISC-V MMU library. +# +# Copyright (c) 2023, Ventana Micro Systems Inc. All Rights Reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION =3D 0x0001001b + BASE_NAME =3D BaseRiscVMmuLib + FILE_GUID =3D d3bc42ee-c9eb-4339-ba11-06747083d3ae + MODULE_TYPE =3D BASE + VERSION_STRING =3D 1.0 + LIBRARY_CLASS =3D RiscVMmuLib + +[Sources] + BaseRiscVMmuLib.c + RiscVMmuCore.S + +[Packages] + MdePkg/MdePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib diff --git a/UefiCpuPkg/Library/BaseRiscVMmuLib/RiscVMmuCore.S b/UefiCpuPkg= /Library/BaseRiscVMmuLib/RiscVMmuCore.S new file mode 100644 index 000000000000..42eec4cbdf83 --- /dev/null +++ b/UefiCpuPkg/Library/BaseRiscVMmuLib/RiscVMmuCore.S @@ -0,0 +1,31 @@ +/** @file +* +* Copyright (c) 2023, Ventana Micro Systems Inc. All Rights Reserved.
+* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#include +#include + +.text + .align 3 + +// +// Local tlb flush all. +// +// +ASM_FUNC (RiscVLocalTlbFlushAll) +sfence.vma +ret + +// +// Local tlb flush at a virtual address +// @retval a0 : virtual address. +// +ASM_FUNC ( + RiscVLocalTlbFlush + ) +sfence.vma a0 +ret diff --git a/UefiCpuPkg/UefiCpuPkg.dec b/UefiCpuPkg/UefiCpuPkg.dec index 25126c9136d3..e7726a605c5c 100644 --- a/UefiCpuPkg/UefiCpuPkg.dec +++ b/UefiCpuPkg/UefiCpuPkg.dec @@ -64,6 +64,11 @@ ## @libraryclass Provides functions for manipulating smram savestate r= egisters. MmSaveStateLib|Include/Library/MmSaveStateLib.h =20 +[LibraryClasses.RISCV64] + ## @libraryclass Provides functions to manage MMU features on RISCV64 = CPUs. + ## + RiscVMmuLib|Include/Library/BaseRiscVMmuLib.h + [Guids] gUefiCpuPkgTokenSpaceGuid =3D { 0xac05bf33, 0x995a, 0x4ed4, { 0xaa,= 0xb8, 0xef, 0x7a, 0xe8, 0xf, 0x5c, 0xb0 }} gMsegSmramGuid =3D { 0x5802bce4, 0xeeee, 0x4e33, { 0xa1,= 0x30, 0xeb, 0xad, 0x27, 0xf0, 0xe4, 0x39 }} diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc index 45726b9efc9c..074fd774616d 100644 --- a/UefiCpuPkg/UefiCpuPkg.dsc +++ b/UefiCpuPkg/UefiCpuPkg.dsc @@ -201,6 +201,7 @@ [Components.RISCV64] UefiCpuPkg/Library/BaseRiscV64CpuExceptionHandlerLib/BaseRiscV64CpuExcep= tionHandlerLib.inf UefiCpuPkg/Library/BaseRiscV64CpuTimerLib/BaseRiscV64CpuTimerLib.inf + UefiCpuPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.inf UefiCpuPkg/CpuTimerDxeRiscV64/CpuTimerDxeRiscV64.inf UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf =20 --=20 2.25.1 -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#106944): https://edk2.groups.io/g/devel/message/106944 Mute This Topic: https://groups.io/mt/100147815/1787277 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org] -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-