/** @file | |
File managing the MMU for ARMv8 architecture in S-EL0 | |
Copyright (c) 2017 - 2021, Arm Limited. All rights reserved.<BR> | |
Copyright (c) 2021, Linaro Limited | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
@par Reference(s): | |
- [1] SPM based on the MM interface. | |
(https://trustedfirmware-a.readthedocs.io/en/latest/components/ | |
secure-partition-manager-mm.html) | |
- [2] Arm Firmware Framework for Armv8-A, DEN0077A, version 1.0 | |
(https://developer.arm.com/documentation/den0077/a) | |
**/ | |
#include <Uefi.h> | |
#include <IndustryStandard/ArmMmSvc.h> | |
#include <IndustryStandard/ArmFfaSvc.h> | |
#include <Library/ArmLib.h> | |
#include <Library/ArmMmuLib.h> | |
#include <Library/ArmSvcLib.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/PcdLib.h> | |
/** Send memory permission request to target. | |
@param [in, out] SvcArgs Pointer to SVC arguments to send. On | |
return it contains the response parameters. | |
@param [out] RetVal Pointer to return the response value. | |
@retval EFI_SUCCESS Request successfull. | |
@retval EFI_INVALID_PARAMETER A parameter is invalid. | |
@retval EFI_NOT_READY Callee is busy or not in a state to handle | |
this request. | |
@retval EFI_UNSUPPORTED This function is not implemented by the | |
callee. | |
@retval EFI_ABORTED Message target ran into an unexpected error | |
and has aborted. | |
@retval EFI_ACCESS_DENIED Access denied. | |
@retval EFI_OUT_OF_RESOURCES Out of memory to perform operation. | |
**/ | |
STATIC | |
EFI_STATUS | |
SendMemoryPermissionRequest ( | |
IN OUT ARM_SVC_ARGS *SvcArgs, | |
OUT INT32 *RetVal | |
) | |
{ | |
if ((SvcArgs == NULL) || (RetVal == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
ArmCallSvc (SvcArgs); | |
if (FeaturePcdGet (PcdFfaEnable)) { | |
// Get/Set memory attributes is an atomic call, with | |
// StandaloneMm at S-EL0 being the caller and the SPM | |
// core being the callee. Thus there won't be a | |
// FFA_INTERRUPT or FFA_SUCCESS response to the Direct | |
// Request sent above. This will have to be considered | |
// for other Direct Request calls which are not atomic | |
// We therefore check only for Direct Response by the | |
// callee. | |
if (SvcArgs->Arg0 == ARM_SVC_ID_FFA_MSG_SEND_DIRECT_RESP) { | |
// A Direct Response means FF-A success | |
// Now check the payload for errors | |
// The callee sends back the return value | |
// in Arg3 | |
*RetVal = SvcArgs->Arg3; | |
} else { | |
// If Arg0 is not a Direct Response, that means we | |
// have an FF-A error. We need to check Arg2 for the | |
// FF-A error code. | |
// See [2], Table 10.8: FFA_ERROR encoding. | |
*RetVal = SvcArgs->Arg2; | |
switch (*RetVal) { | |
case ARM_FFA_SPM_RET_INVALID_PARAMETERS: | |
return EFI_INVALID_PARAMETER; | |
case ARM_FFA_SPM_RET_DENIED: | |
return EFI_ACCESS_DENIED; | |
case ARM_FFA_SPM_RET_NOT_SUPPORTED: | |
return EFI_UNSUPPORTED; | |
case ARM_FFA_SPM_RET_BUSY: | |
return EFI_NOT_READY; | |
case ARM_FFA_SPM_RET_ABORTED: | |
return EFI_ABORTED; | |
default: | |
// Undefined error code received. | |
ASSERT (0); | |
return EFI_INVALID_PARAMETER; | |
} | |
} | |
} else { | |
*RetVal = SvcArgs->Arg0; | |
} | |
// Check error response from Callee. | |
if ((*RetVal & BIT31) != 0) { | |
// Bit 31 set means there is an error returned | |
// See [1], Section 13.5.5.1 MM_SP_MEMORY_ATTRIBUTES_GET_AARCH64 and | |
// Section 13.5.5.2 MM_SP_MEMORY_ATTRIBUTES_SET_AARCH64. | |
switch (*RetVal) { | |
case ARM_SVC_SPM_RET_NOT_SUPPORTED: | |
return EFI_UNSUPPORTED; | |
case ARM_SVC_SPM_RET_INVALID_PARAMS: | |
return EFI_INVALID_PARAMETER; | |
case ARM_SVC_SPM_RET_DENIED: | |
return EFI_ACCESS_DENIED; | |
case ARM_SVC_SPM_RET_NO_MEMORY: | |
return EFI_OUT_OF_RESOURCES; | |
default: | |
// Undefined error code received. | |
ASSERT (0); | |
return EFI_INVALID_PARAMETER; | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** Request the permission attributes of a memory region from S-EL0. | |
@param [in] BaseAddress Base address for the memory region. | |
@param [out] MemoryAttributes Pointer to return the memory attributes. | |
@retval EFI_SUCCESS Request successfull. | |
@retval EFI_INVALID_PARAMETER A parameter is invalid. | |
@retval EFI_NOT_READY Callee is busy or not in a state to handle | |
this request. | |
@retval EFI_UNSUPPORTED This function is not implemented by the | |
callee. | |
@retval EFI_ABORTED Message target ran into an unexpected error | |
and has aborted. | |
@retval EFI_ACCESS_DENIED Access denied. | |
@retval EFI_OUT_OF_RESOURCES Out of memory to perform operation. | |
**/ | |
STATIC | |
EFI_STATUS | |
GetMemoryPermissions ( | |
IN EFI_PHYSICAL_ADDRESS BaseAddress, | |
OUT UINT32 *MemoryAttributes | |
) | |
{ | |
EFI_STATUS Status; | |
INT32 Ret; | |
ARM_SVC_ARGS SvcArgs; | |
if (MemoryAttributes == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// Prepare the message parameters. | |
// See [1], Section 13.5.5.1 MM_SP_MEMORY_ATTRIBUTES_GET_AARCH64. | |
ZeroMem (&SvcArgs, sizeof (ARM_SVC_ARGS)); | |
if (FeaturePcdGet (PcdFfaEnable)) { | |
// See [2], Section 10.2 FFA_MSG_SEND_DIRECT_REQ. | |
SvcArgs.Arg0 = ARM_SVC_ID_FFA_MSG_SEND_DIRECT_REQ; | |
SvcArgs.Arg1 = ARM_FFA_DESTINATION_ENDPOINT_ID; | |
SvcArgs.Arg2 = 0; | |
SvcArgs.Arg3 = ARM_SVC_ID_SP_GET_MEM_ATTRIBUTES; | |
SvcArgs.Arg4 = BaseAddress; | |
} else { | |
SvcArgs.Arg0 = ARM_SVC_ID_SP_GET_MEM_ATTRIBUTES; | |
SvcArgs.Arg1 = BaseAddress; | |
SvcArgs.Arg2 = 0; | |
SvcArgs.Arg3 = 0; | |
} | |
Status = SendMemoryPermissionRequest (&SvcArgs, &Ret); | |
if (EFI_ERROR (Status)) { | |
*MemoryAttributes = 0; | |
return Status; | |
} | |
*MemoryAttributes = Ret; | |
return Status; | |
} | |
/** Set the permission attributes of a memory region from S-EL0. | |
@param [in] BaseAddress Base address for the memory region. | |
@param [in] Length Length of the memory region. | |
@param [in] Permissions Memory access controls attributes. | |
@retval EFI_SUCCESS Request successfull. | |
@retval EFI_INVALID_PARAMETER A parameter is invalid. | |
@retval EFI_NOT_READY Callee is busy or not in a state to handle | |
this request. | |
@retval EFI_UNSUPPORTED This function is not implemented by the | |
callee. | |
@retval EFI_ABORTED Message target ran into an unexpected error | |
and has aborted. | |
@retval EFI_ACCESS_DENIED Access denied. | |
@retval EFI_OUT_OF_RESOURCES Out of memory to perform operation. | |
**/ | |
STATIC | |
EFI_STATUS | |
RequestMemoryPermissionChange ( | |
IN EFI_PHYSICAL_ADDRESS BaseAddress, | |
IN UINT64 Length, | |
IN UINT32 Permissions | |
) | |
{ | |
INT32 Ret; | |
ARM_SVC_ARGS SvcArgs; | |
// Prepare the message parameters. | |
// See [1], Section 13.5.5.2 MM_SP_MEMORY_ATTRIBUTES_SET_AARCH64. | |
ZeroMem (&SvcArgs, sizeof (ARM_SVC_ARGS)); | |
if (FeaturePcdGet (PcdFfaEnable)) { | |
// See [2], Section 10.2 FFA_MSG_SEND_DIRECT_REQ. | |
SvcArgs.Arg0 = ARM_SVC_ID_FFA_MSG_SEND_DIRECT_REQ; | |
SvcArgs.Arg1 = ARM_FFA_DESTINATION_ENDPOINT_ID; | |
SvcArgs.Arg2 = 0; | |
SvcArgs.Arg3 = ARM_SVC_ID_SP_SET_MEM_ATTRIBUTES; | |
SvcArgs.Arg4 = BaseAddress; | |
SvcArgs.Arg5 = EFI_SIZE_TO_PAGES (Length); | |
SvcArgs.Arg6 = Permissions; | |
} else { | |
SvcArgs.Arg0 = ARM_SVC_ID_SP_SET_MEM_ATTRIBUTES; | |
SvcArgs.Arg1 = BaseAddress; | |
SvcArgs.Arg2 = EFI_SIZE_TO_PAGES (Length); | |
SvcArgs.Arg3 = Permissions; | |
} | |
return SendMemoryPermissionRequest (&SvcArgs, &Ret); | |
} | |
EFI_STATUS | |
ArmSetMemoryRegionNoExec ( | |
IN EFI_PHYSICAL_ADDRESS BaseAddress, | |
IN UINT64 Length | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 MemoryAttributes; | |
UINT32 CodePermission; | |
Status = GetMemoryPermissions (BaseAddress, &MemoryAttributes); | |
if (!EFI_ERROR (Status)) { | |
CodePermission = SET_MEM_ATTR_CODE_PERM_XN << SET_MEM_ATTR_CODE_PERM_SHIFT; | |
return RequestMemoryPermissionChange ( | |
BaseAddress, | |
Length, | |
MemoryAttributes | CodePermission | |
); | |
} | |
return Status; | |
} | |
EFI_STATUS | |
ArmClearMemoryRegionNoExec ( | |
IN EFI_PHYSICAL_ADDRESS BaseAddress, | |
IN UINT64 Length | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 MemoryAttributes; | |
UINT32 CodePermission; | |
Status = GetMemoryPermissions (BaseAddress, &MemoryAttributes); | |
if (!EFI_ERROR (Status)) { | |
CodePermission = SET_MEM_ATTR_CODE_PERM_XN << SET_MEM_ATTR_CODE_PERM_SHIFT; | |
return RequestMemoryPermissionChange ( | |
BaseAddress, | |
Length, | |
MemoryAttributes & ~CodePermission | |
); | |
} | |
return Status; | |
} | |
EFI_STATUS | |
ArmSetMemoryRegionReadOnly ( | |
IN EFI_PHYSICAL_ADDRESS BaseAddress, | |
IN UINT64 Length | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 MemoryAttributes; | |
UINT32 DataPermission; | |
Status = GetMemoryPermissions (BaseAddress, &MemoryAttributes); | |
if (!EFI_ERROR (Status)) { | |
DataPermission = SET_MEM_ATTR_DATA_PERM_RO << SET_MEM_ATTR_DATA_PERM_SHIFT; | |
return RequestMemoryPermissionChange ( | |
BaseAddress, | |
Length, | |
MemoryAttributes | DataPermission | |
); | |
} | |
return Status; | |
} | |
EFI_STATUS | |
ArmClearMemoryRegionReadOnly ( | |
IN EFI_PHYSICAL_ADDRESS BaseAddress, | |
IN UINT64 Length | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 MemoryAttributes; | |
UINT32 PermissionRequest; | |
Status = GetMemoryPermissions (BaseAddress, &MemoryAttributes); | |
if (!EFI_ERROR (Status)) { | |
PermissionRequest = SET_MEM_ATTR_MAKE_PERM_REQUEST ( | |
SET_MEM_ATTR_DATA_PERM_RW, | |
MemoryAttributes | |
); | |
return RequestMemoryPermissionChange ( | |
BaseAddress, | |
Length, | |
PermissionRequest | |
); | |
} | |
return Status; | |
} |