summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTuan Phan <tphan@ventanamicro.com>2023-07-14 12:08:24 -0700
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>2023-07-15 14:10:18 +0000
commitf220dcbba86bfc1222180c61bbd31dd6023433db (patch)
tree8664b8b0e32903c179710e7f7018bc45722d1d7f
parentcc13dcc57675695d51efe0d61d772155c601a35b (diff)
downloadedk2-f220dcbba86bfc1222180c61bbd31dd6023433db.tar.gz
edk2-f220dcbba86bfc1222180c61bbd31dd6023433db.tar.bz2
edk2-f220dcbba86bfc1222180c61bbd31dd6023433db.zip
UefiCpuPkg: RISC-V: Support MMU with SV39/48/57 mode
During CpuDxe initialization, MMU will be setup with the highest mode that HW supports. Signed-off-by: Tuan Phan <tphan@ventanamicro.com> Reviewed-by: Andrei Warkentin <andrei.warkentin@intel.com> Reviewed-by: Sunil V L <sunilvl@ventanamicro.com>
-rw-r--r--OvmfPkg/RiscVVirt/RiscVVirt.dsc.inc1
-rw-r--r--UefiCpuPkg/CpuDxeRiscV64/CpuDxe.c9
-rw-r--r--UefiCpuPkg/CpuDxeRiscV64/CpuDxe.h2
-rw-r--r--UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf2
-rw-r--r--UefiCpuPkg/Include/Library/BaseRiscVMmuLib.h68
-rw-r--r--UefiCpuPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.c730
-rw-r--r--UefiCpuPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.inf27
-rw-r--r--UefiCpuPkg/Library/BaseRiscVMmuLib/RiscVMmuCore.S31
-rw-r--r--UefiCpuPkg/UefiCpuPkg.dec5
-rw-r--r--UefiCpuPkg/UefiCpuPkg.dsc1
10 files changed, 874 insertions, 2 deletions
diff --git a/OvmfPkg/RiscVVirt/RiscVVirt.dsc.inc b/OvmfPkg/RiscVVirt/RiscVVirt.dsc.inc
index 731f54f73f..bc204ba5fe 100644
--- a/OvmfPkg/RiscVVirt/RiscVVirt.dsc.inc
+++ b/OvmfPkg/RiscVVirt/RiscVVirt.dsc.inc
@@ -83,6 +83,7 @@
# RISC-V Architectural Libraries
CpuExceptionHandlerLib|UefiCpuPkg/Library/BaseRiscV64CpuExceptionHandlerLib/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/BaseResetSystemLib.inf
diff --git a/UefiCpuPkg/CpuDxeRiscV64/CpuDxe.c b/UefiCpuPkg/CpuDxeRiscV64/CpuDxe.c
index 25fe3f54c3..2af3b62234 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", __func__));
- return EFI_SUCCESS;
+ return RiscVSetMemoryAttributes (BaseAddress, Length, Attributes);
}
/**
@@ -341,6 +340,12 @@ InitializeCpu (
DisableInterrupts ();
//
+ // Enable MMU
+ //
+ Status = RiscVConfigureMmu ();
+ ASSERT_EFI_ERROR (Status);
+
+ //
// Install Boot protocol
//
Status = gBS->InstallProtocolInterface (
diff --git a/UefiCpuPkg/CpuDxeRiscV64/CpuDxe.h b/UefiCpuPkg/CpuDxeRiscV64/CpuDxe.h
index 49f4e11966..68e6d038b6 100644
--- a/UefiCpuPkg/CpuDxeRiscV64/CpuDxe.h
+++ b/UefiCpuPkg/CpuDxeRiscV64/CpuDxe.h
@@ -15,11 +15,13 @@
#include <Protocol/Cpu.h>
#include <Protocol/RiscVBootProtocol.h>
#include <Library/BaseRiscVSbiLib.h>
+#include <Library/BaseRiscVMmuLib.h>
#include <Library/BaseLib.h>
#include <Library/CpuExceptionHandlerLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiDriverEntryPoint.h>
+#include <Register/RiscV64/RiscVEncoding.h>
/**
Flush CPU data cache. If the instruction cache is fully coherent
diff --git a/UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf b/UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf
index e8fa25446a..9d9a5ef8f2 100644
--- a/UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf
+++ b/UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf
@@ -37,6 +37,8 @@
TimerLib
PeCoffGetEntryPointLib
RiscVSbiLib
+ RiscVMmuLib
+ CacheMaintenanceLib
[Sources]
CpuDxe.c
diff --git a/UefiCpuPkg/Include/Library/BaseRiscVMmuLib.h b/UefiCpuPkg/Include/Library/BaseRiscVMmuLib.h
new file mode 100644
index 0000000000..f1e609d28d
--- /dev/null
+++ b/UefiCpuPkg/Include/Library/BaseRiscVMmuLib.h
@@ -0,0 +1,68 @@
+/** @file
+
+ Copyright (c) 2015 - 2016, Linaro Ltd. All rights reserved.<BR>
+ Copyright (c) 2023, Ventana Micro Systems Inc. All Rights Reserved.<BR>
+
+ 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 supported.
+
+ @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/UefiCpuPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.c
new file mode 100644
index 0000000000..9cca5fc128
--- /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.<BR>
+ Copyright (c) 2023, Ventana Micro Systems Inc. All Rights Reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/BaseRiscVMmuLib.h>
+#include <Library/CacheMaintenanceLib.h>
+#include <Library/DebugLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PcdLib.h>
+#include <Register/RiscV64/RiscVEncoding.h>
+
+#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[] = { SATP_MODE_SV57, SATP_MODE_SV48, SATP_MODE_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) != (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) == 0) ||
+ (((Entry & (RISCV_PG_R | RISCV_PG_W)) == 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 = SetValidPte (Entry);
+ Entry &= ~(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 = 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 = ((Address >> RISCV_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 Level The current level.
+
+**/
+STATIC
+VOID
+FreePageTablesRecursive (
+ IN UINTN *TranslationTable,
+ IN UINTN Level
+ )
+{
+ UINTN Index;
+
+ if (Level < mMaxRootTableLevel - 1) {
+ for (Index = 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 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 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) == 0);
+
+ BlockShift = (mMaxRootTableLevel - Level - 1) * mBitPerLevel + RISCV_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
+ )
+ );
+
+ for ( ; RegionStart < RegionEnd; RegionStart = BlockEnd) {
+ BlockEnd = MIN (RegionEnd, (RegionStart | BlockMask) + 1);
+ Entry = &PageTable[(RegionStart >> BlockShift) & (mTableEntryCount - 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 == 0) ||
+ (((RegionStart | BlockEnd) & BlockMask) != 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 = AllocatePages (1);
+ if (TranslationTable == 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 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,
+ 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) << RISCV_MMU_PAGE_SHIFT);
+ NextTableIsLive = TableIsLive;
+ }
+
+ //
+ // Recurse to the next level
+ //
+ Status = 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 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, Level + 1);
+ }
+
+ return Status;
+ }
+
+ if (!IsTableEntry (*Entry)) {
+ EntryValue = SetPpnToPte (0, (UINTN)TranslationTable);
+ EntryValue = SetTableEntry (EntryValue);
+ ReplaceTableEntry (
+ Entry,
+ EntryValue,
+ RegionStart,
+ TableIsLive
+ );
+ }
+ } else {
+ EntryValue = (*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) != 0) {
+ EntryValue |= RISCV_PG_A;
+ }
+
+ if ((AttributeSetMask & RISCV_PG_W) != 0) {
+ EntryValue |= RISCV_PG_D;
+ }
+
+ EntryValue = SetPpnToPte (EntryValue, RegionStart);
+ EntryValue = 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 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.
+
+**/
+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) != 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 = RISCV_PG_R | RISCV_PG_W | RISCV_PG_X;
+
+ // Determine protection attributes
+ if ((GcdAttributes & EFI_MEMORY_RO) != 0) {
+ RiscVAttributes &= ~(RISCV_PG_W);
+ }
+
+ // Process eXecute Never attribute
+ if ((GcdAttributes & EFI_MEMORY_XP) != 0) {
+ RiscVAttributes &= ~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 = 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 = 3;
+ mBitPerLevel = 9;
+ mTableEntryCount = 512;
+ break;
+ case SATP_MODE_SV48:
+ mMaxRootTableLevel = 4;
+ mBitPerLevel = 9;
+ mTableEntryCount = 512;
+ break;
+ case SATP_MODE_SV57:
+ mMaxRootTableLevel = 5;
+ mBitPerLevel = 9;
+ mTableEntryCount = 512;
+ break;
+ default:
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Allocate pages for translation table
+ TranslationTable = AllocatePages (1);
+ if (TranslationTable == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ZeroMem (TranslationTable, mTableEntryCount * sizeof (UINTN));
+
+ NumberOfDescriptors = 0;
+ MemoryMap = NULL;
+ Status = gDS->GetMemorySpaceMap (
+ &NumberOfDescriptors,
+ &MemoryMap
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ for (Index = 0; Index < NumberOfDescriptors; Index++) {
+ if (MemoryMap[Index].GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) {
+ // 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 == EfiGcdMemoryTypeSystemMemory) {
+ // 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 = (UINTN)TranslationTable >> RISCV_MMU_PAGE_SHIFT;
+ ASSERT (!(Ppn & ~(SATP64_PPN)));
+
+ SatpReg = Ppn;
+ SatpReg |= (SatpMode <<
+ SATP64_MODE_SHIFT) & SATP64_MODE;
+ RiscVSetSupervisorAddressTranslationRegister (SatpReg);
+ /* Check if HW support the setup satp mode */
+ if (SatpReg != 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 supported.
+
+ @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 = EFI_SUCCESS;
+
+ /* Try to setup MMU with highest mode as possible */
+ for (Idx = 0; Idx < ARRAY_SIZE (mModeSupport); Idx++) {
+ Status = RiscVMmuSetSatpMode (mModeSupport[Idx]);
+ if (Status == 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/UefiCpuPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.inf
new file mode 100644
index 0000000000..9b28a98cb3
--- /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.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x0001001b
+ BASE_NAME = BaseRiscVMmuLib
+ FILE_GUID = d3bc42ee-c9eb-4339-ba11-06747083d3ae
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = 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 0000000000..42eec4cbdf
--- /dev/null
+++ b/UefiCpuPkg/Library/BaseRiscVMmuLib/RiscVMmuCore.S
@@ -0,0 +1,31 @@
+/** @file
+*
+* Copyright (c) 2023, Ventana Micro Systems Inc. All Rights Reserved.<BR>
+*
+* SPDX-License-Identifier: BSD-2-Clause-Patent
+*
+**/
+
+#include <Base.h>
+#include <Register/RiscV64/RiscVImpl.h>
+
+.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 25126c9136..e7726a605c 100644
--- a/UefiCpuPkg/UefiCpuPkg.dec
+++ b/UefiCpuPkg/UefiCpuPkg.dec
@@ -64,6 +64,11 @@
## @libraryclass Provides functions for manipulating smram savestate registers.
MmSaveStateLib|Include/Library/MmSaveStateLib.h
+[LibraryClasses.RISCV64]
+ ## @libraryclass Provides functions to manage MMU features on RISCV64 CPUs.
+ ##
+ RiscVMmuLib|Include/Library/BaseRiscVMmuLib.h
+
[Guids]
gUefiCpuPkgTokenSpaceGuid = { 0xac05bf33, 0x995a, 0x4ed4, { 0xaa, 0xb8, 0xef, 0x7a, 0xe8, 0xf, 0x5c, 0xb0 }}
gMsegSmramGuid = { 0x5802bce4, 0xeeee, 0x4e33, { 0xa1, 0x30, 0xeb, 0xad, 0x27, 0xf0, 0xe4, 0x39 }}
diff --git a/UefiCpuPkg/UefiCpuPkg.dsc b/UefiCpuPkg/UefiCpuPkg.dsc
index 45726b9efc..074fd77461 100644
--- a/UefiCpuPkg/UefiCpuPkg.dsc
+++ b/UefiCpuPkg/UefiCpuPkg.dsc
@@ -201,6 +201,7 @@
[Components.RISCV64]
UefiCpuPkg/Library/BaseRiscV64CpuExceptionHandlerLib/BaseRiscV64CpuExceptionHandlerLib.inf
UefiCpuPkg/Library/BaseRiscV64CpuTimerLib/BaseRiscV64CpuTimerLib.inf
+ UefiCpuPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.inf
UefiCpuPkg/CpuTimerDxeRiscV64/CpuTimerDxeRiscV64.inf
UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf