mirror of https://github.com/acidanthera/audk.git
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>
This commit is contained in:
parent
cc13dcc576
commit
f220dcbba8
|
@ -83,6 +83,7 @@
|
||||||
# RISC-V Architectural Libraries
|
# RISC-V Architectural Libraries
|
||||||
CpuExceptionHandlerLib|UefiCpuPkg/Library/BaseRiscV64CpuExceptionHandlerLib/BaseRiscV64CpuExceptionHandlerLib.inf
|
CpuExceptionHandlerLib|UefiCpuPkg/Library/BaseRiscV64CpuExceptionHandlerLib/BaseRiscV64CpuExceptionHandlerLib.inf
|
||||||
RiscVSbiLib|MdePkg/Library/BaseRiscVSbiLib/BaseRiscVSbiLib.inf
|
RiscVSbiLib|MdePkg/Library/BaseRiscVSbiLib/BaseRiscVSbiLib.inf
|
||||||
|
RiscVMmuLib|UefiCpuPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.inf
|
||||||
PlatformBootManagerLib|OvmfPkg/RiscVVirt/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
|
PlatformBootManagerLib|OvmfPkg/RiscVVirt/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
|
||||||
ResetSystemLib|OvmfPkg/RiscVVirt/Library/ResetSystemLib/BaseResetSystemLib.inf
|
ResetSystemLib|OvmfPkg/RiscVVirt/Library/ResetSystemLib/BaseResetSystemLib.inf
|
||||||
|
|
||||||
|
|
|
@ -296,8 +296,7 @@ CpuSetMemoryAttributes (
|
||||||
IN UINT64 Attributes
|
IN UINT64 Attributes
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
DEBUG ((DEBUG_INFO, "%a: Set memory attributes not supported yet\n", __func__));
|
return RiscVSetMemoryAttributes (BaseAddress, Length, Attributes);
|
||||||
return EFI_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -340,6 +339,12 @@ InitializeCpu (
|
||||||
//
|
//
|
||||||
DisableInterrupts ();
|
DisableInterrupts ();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Enable MMU
|
||||||
|
//
|
||||||
|
Status = RiscVConfigureMmu ();
|
||||||
|
ASSERT_EFI_ERROR (Status);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Install Boot protocol
|
// Install Boot protocol
|
||||||
//
|
//
|
||||||
|
|
|
@ -15,11 +15,13 @@
|
||||||
#include <Protocol/Cpu.h>
|
#include <Protocol/Cpu.h>
|
||||||
#include <Protocol/RiscVBootProtocol.h>
|
#include <Protocol/RiscVBootProtocol.h>
|
||||||
#include <Library/BaseRiscVSbiLib.h>
|
#include <Library/BaseRiscVSbiLib.h>
|
||||||
|
#include <Library/BaseRiscVMmuLib.h>
|
||||||
#include <Library/BaseLib.h>
|
#include <Library/BaseLib.h>
|
||||||
#include <Library/CpuExceptionHandlerLib.h>
|
#include <Library/CpuExceptionHandlerLib.h>
|
||||||
#include <Library/DebugLib.h>
|
#include <Library/DebugLib.h>
|
||||||
#include <Library/UefiBootServicesTableLib.h>
|
#include <Library/UefiBootServicesTableLib.h>
|
||||||
#include <Library/UefiDriverEntryPoint.h>
|
#include <Library/UefiDriverEntryPoint.h>
|
||||||
|
#include <Register/RiscV64/RiscVEncoding.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Flush CPU data cache. If the instruction cache is fully coherent
|
Flush CPU data cache. If the instruction cache is fully coherent
|
||||||
|
|
|
@ -37,6 +37,8 @@
|
||||||
TimerLib
|
TimerLib
|
||||||
PeCoffGetEntryPointLib
|
PeCoffGetEntryPointLib
|
||||||
RiscVSbiLib
|
RiscVSbiLib
|
||||||
|
RiscVMmuLib
|
||||||
|
CacheMaintenanceLib
|
||||||
|
|
||||||
[Sources]
|
[Sources]
|
||||||
CpuDxe.c
|
CpuDxe.c
|
||||||
|
|
|
@ -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_ */
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -64,6 +64,11 @@
|
||||||
## @libraryclass Provides functions for manipulating smram savestate registers.
|
## @libraryclass Provides functions for manipulating smram savestate registers.
|
||||||
MmSaveStateLib|Include/Library/MmSaveStateLib.h
|
MmSaveStateLib|Include/Library/MmSaveStateLib.h
|
||||||
|
|
||||||
|
[LibraryClasses.RISCV64]
|
||||||
|
## @libraryclass Provides functions to manage MMU features on RISCV64 CPUs.
|
||||||
|
##
|
||||||
|
RiscVMmuLib|Include/Library/BaseRiscVMmuLib.h
|
||||||
|
|
||||||
[Guids]
|
[Guids]
|
||||||
gUefiCpuPkgTokenSpaceGuid = { 0xac05bf33, 0x995a, 0x4ed4, { 0xaa, 0xb8, 0xef, 0x7a, 0xe8, 0xf, 0x5c, 0xb0 }}
|
gUefiCpuPkgTokenSpaceGuid = { 0xac05bf33, 0x995a, 0x4ed4, { 0xaa, 0xb8, 0xef, 0x7a, 0xe8, 0xf, 0x5c, 0xb0 }}
|
||||||
gMsegSmramGuid = { 0x5802bce4, 0xeeee, 0x4e33, { 0xa1, 0x30, 0xeb, 0xad, 0x27, 0xf0, 0xe4, 0x39 }}
|
gMsegSmramGuid = { 0x5802bce4, 0xeeee, 0x4e33, { 0xa1, 0x30, 0xeb, 0xad, 0x27, 0xf0, 0xe4, 0x39 }}
|
||||||
|
|
|
@ -201,6 +201,7 @@
|
||||||
[Components.RISCV64]
|
[Components.RISCV64]
|
||||||
UefiCpuPkg/Library/BaseRiscV64CpuExceptionHandlerLib/BaseRiscV64CpuExceptionHandlerLib.inf
|
UefiCpuPkg/Library/BaseRiscV64CpuExceptionHandlerLib/BaseRiscV64CpuExceptionHandlerLib.inf
|
||||||
UefiCpuPkg/Library/BaseRiscV64CpuTimerLib/BaseRiscV64CpuTimerLib.inf
|
UefiCpuPkg/Library/BaseRiscV64CpuTimerLib/BaseRiscV64CpuTimerLib.inf
|
||||||
|
UefiCpuPkg/Library/BaseRiscVMmuLib/BaseRiscVMmuLib.inf
|
||||||
UefiCpuPkg/CpuTimerDxeRiscV64/CpuTimerDxeRiscV64.inf
|
UefiCpuPkg/CpuTimerDxeRiscV64/CpuTimerDxeRiscV64.inf
|
||||||
UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf
|
UefiCpuPkg/CpuDxeRiscV64/CpuDxeRiscV64.inf
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue