GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <asm_macros.S>
#include "spm_common.h"
.global spm_secure_partition_enter
.global spm_secure_partition_exit
/* ---------------------------------------------------------------------
* This function is called with SP_EL0 as stack. Here we stash our EL3
* callee-saved registers on to the stack as a part of saving the C
* runtime and enter the secure payload.
* 'x0' contains a pointer to the memory where the address of the C
* runtime context is to be saved.
* ---------------------------------------------------------------------
*/
func spm_secure_partition_enter
/* Make space for the registers that we're going to save */
mov x3, sp
str x3, [x0, #0]
sub sp, sp, #SP_C_RT_CTX_SIZE
/* Save callee-saved registers on to the stack */
stp x19, x20, [sp, #SP_C_RT_CTX_X19]
stp x21, x22, [sp, #SP_C_RT_CTX_X21]
stp x23, x24, [sp, #SP_C_RT_CTX_X23]
stp x25, x26, [sp, #SP_C_RT_CTX_X25]
stp x27, x28, [sp, #SP_C_RT_CTX_X27]
stp x29, x30, [sp, #SP_C_RT_CTX_X29]
/* ---------------------------------------------------------------------
* Everything is setup now. el3_exit() will use the secure context to
* restore to the general purpose and EL3 system registers to ERET
* into the secure payload.
* ---------------------------------------------------------------------
*/
b el3_exit
endfunc spm_secure_partition_enter
/* ---------------------------------------------------------------------
* This function is called with 'x0' pointing to a C runtime context
* saved in spm_secure_partition_enter().
* It restores the saved registers and jumps to that runtime with 'x0'
* as the new SP register. This destroys the C runtime context that had
* been built on the stack below the saved context by the caller. Later
* the second parameter 'x1' is passed as a return value to the caller.
* ---------------------------------------------------------------------
*/
func spm_secure_partition_exit
/* Restore the previous stack */
mov sp, x0
/* Restore callee-saved registers on to the stack */
ldp x19, x20, [x0, #(SP_C_RT_CTX_X19 - SP_C_RT_CTX_SIZE)]
ldp x21, x22, [x0, #(SP_C_RT_CTX_X21 - SP_C_RT_CTX_SIZE)]
ldp x23, x24, [x0, #(SP_C_RT_CTX_X23 - SP_C_RT_CTX_SIZE)]
ldp x25, x26, [x0, #(SP_C_RT_CTX_X25 - SP_C_RT_CTX_SIZE)]
ldp x27, x28, [x0, #(SP_C_RT_CTX_X27 - SP_C_RT_CTX_SIZE)]
ldp x29, x30, [x0, #(SP_C_RT_CTX_X29 - SP_C_RT_CTX_SIZE)]
/* ---------------------------------------------------------------------
* This should take us back to the instruction after the call to the
* last spm_secure_partition_enter().* Place the second parameter to x0
* so that the caller will see it as a return value from the original
* entry call.
* ---------------------------------------------------------------------
*/
mov x0, x1
ret
endfunc spm_secure_partition_exit
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SPM_COMMON_H
#define SPM_COMMON_H
#include <context.h>
/*******************************************************************************
* Constants that allow assembler code to preserve callee-saved registers of the
* C runtime context while performing a security state switch.
******************************************************************************/
#define SP_C_RT_CTX_X19 0x0
#define SP_C_RT_CTX_X20 0x8
#define SP_C_RT_CTX_X21 0x10
#define SP_C_RT_CTX_X22 0x18
#define SP_C_RT_CTX_X23 0x20
#define SP_C_RT_CTX_X24 0x28
#define SP_C_RT_CTX_X25 0x30
#define SP_C_RT_CTX_X26 0x38
#define SP_C_RT_CTX_X27 0x40
#define SP_C_RT_CTX_X28 0x48
#define SP_C_RT_CTX_X29 0x50
#define SP_C_RT_CTX_X30 0x58
#define SP_C_RT_CTX_SIZE 0x60
#define SP_C_RT_CTX_ENTRIES (SP_C_RT_CTX_SIZE >> DWORD_SHIFT)
#ifndef __ASSEMBLER__
#include <stdint.h>
/* Assembly helpers */
uint64_t spm_secure_partition_enter(uint64_t *c_rt_ctx);
void __dead2 spm_secure_partition_exit(uint64_t c_rt_ctx, uint64_t ret);
#endif /* __ASSEMBLER__ */
#endif /* SPM_COMMON_H */
@@ -0,0 +1,17 @@
#
# Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
ifneq (${ARCH},aarch64)
$(error "Error: SPM is only supported on aarch64.")
endif
INCLUDES += -Iservices/std_svc/spm/common/include
SPM_SOURCES := $(addprefix services/std_svc/spm/common/,\
${ARCH}/spm_helpers.S)
# Let the top-level Makefile know that we intend to include a BL32 image
NEED_BL32 := yes
@@ -0,0 +1,107 @@
/*
* Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <common/debug.h>
#include <services/el3_spmc_logical_sp.h>
#include <services/ffa_svc.h>
#include "spmc.h"
/*******************************************************************************
* Validate any logical partition descriptors before we initialise.
* Initialization of said partitions will be taken care of during SPMC boot.
******************************************************************************/
int el3_sp_desc_validate(void)
{
struct el3_lp_desc *lp_array;
/*
* Assert the number of descriptors is less than maximum allowed.
* This constant should be define on a per platform basis.
*/
assert(EL3_LP_DESCS_COUNT <= MAX_EL3_LP_DESCS_COUNT);
/* Check the array bounds are valid. */
assert(EL3_LP_DESCS_END >= EL3_LP_DESCS_START);
/* If no logical partitions are implemented then simply bail out. */
if (EL3_LP_DESCS_COUNT == 0U) {
return 0;
}
lp_array = get_el3_lp_array();
for (unsigned int index = 0; index < EL3_LP_DESCS_COUNT; index++) {
struct el3_lp_desc *lp_desc = &lp_array[index];
/* Validate our logical partition descriptors. */
if (lp_desc == NULL) {
ERROR("Invalid Logical SP Descriptor\n");
return -EINVAL;
}
/*
* Ensure the ID follows the convention to indidate it resides
* in the secure world.
*/
if (!ffa_is_secure_world_id(lp_desc->sp_id)) {
ERROR("Invalid Logical SP ID (0x%x)\n",
lp_desc->sp_id);
return -EINVAL;
}
/* Ensure we don't conflict with the SPMC partition ID. */
if (lp_desc->sp_id == FFA_SPMC_ID) {
ERROR("Logical SP ID clashes with SPMC ID(0x%x)\n",
lp_desc->sp_id);
return -EINVAL;
}
/* Ensure the UUID is not the NULL UUID. */
if (lp_desc->uuid[0] == 0 && lp_desc->uuid[1] == 0 &&
lp_desc->uuid[2] == 0 && lp_desc->uuid[3] == 0) {
ERROR("Invalid UUID for Logical SP (0x%x)\n",
lp_desc->sp_id);
return -EINVAL;
}
/* Ensure init function callback is registered. */
if (lp_desc->init == NULL) {
ERROR("Missing init function for Logical SP(0x%x)\n",
lp_desc->sp_id);
return -EINVAL;
}
/* Ensure that LP only supports receiving direct requests. */
if (lp_desc->properties &
~(FFA_PARTITION_DIRECT_REQ_RECV)) {
ERROR("Invalid partition properties (0x%x)\n",
lp_desc->properties);
return -EINVAL;
}
/* Ensure direct request function callback is registered. */
if (lp_desc->direct_req == NULL) {
ERROR("No Direct Req handler for Logical SP (0x%x)\n",
lp_desc->sp_id);
return -EINVAL;
}
/* Ensure that all partition IDs are unique. */
for (unsigned int inner_idx = index + 1;
inner_idx < EL3_LP_DESCS_COUNT; inner_idx++) {
if (lp_desc->sp_id == lp_array[inner_idx].sp_id) {
ERROR("Duplicate SP ID Detected (0x%x)\n",
lp_desc->sp_id);
return -EINVAL;
}
}
}
return 0;
}
@@ -0,0 +1,296 @@
/*
* Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SPMC_H
#define SPMC_H
#include <stdint.h>
#include <common/bl_common.h>
#include <lib/psci/psci.h>
#include <lib/spinlock.h>
#include <services/el3_spmc_logical_sp.h>
#include "spm_common.h"
/*
* Ranges of FF-A IDs for Normal world and Secure world components. The
* convention matches that used by other SPMCs i.e. Hafnium and OP-TEE.
*/
#define FFA_NWD_ID_BASE 0x0
#define FFA_NWD_ID_LIMIT 0x7FFF
#define FFA_SWD_ID_BASE 0x8000
#define FFA_SWD_ID_LIMIT SPMD_DIRECT_MSG_ENDPOINT_ID - 1
#define FFA_SWD_ID_MASK 0x8000
/* ID 0 is reserved for the normal world entity, (Hypervisor or OS Kernel). */
#define FFA_NWD_ID U(0)
/* First ID is reserved for the SPMC */
#define FFA_SPMC_ID U(FFA_SWD_ID_BASE)
/* SP IDs are allocated after the SPMC ID */
#define FFA_SP_ID_BASE (FFA_SPMC_ID + 1)
/* Align with Hafnium implementation */
#define INV_SP_ID 0x7FFF
/* FF-A Related helper macros. */
#define FFA_ID_MASK U(0xFFFF)
#define FFA_PARTITION_ID_SHIFT U(16)
#define FFA_FEATURES_BIT31_MASK U(0x1u << 31)
#define FFA_FEATURES_RET_REQ_NS_BIT U(0x1 << 1)
#define FFA_RUN_EP_ID(ep_vcpu_ids) \
((ep_vcpu_ids >> FFA_PARTITION_ID_SHIFT) & FFA_ID_MASK)
#define FFA_RUN_VCPU_ID(ep_vcpu_ids) \
(ep_vcpu_ids & FFA_ID_MASK)
#define FFA_PAGE_SIZE (4096)
#define FFA_RXTX_PAGE_COUNT_MASK 0x1F
/* Ensure that the page size used by TF-A is 4k aligned. */
CASSERT((PAGE_SIZE % FFA_PAGE_SIZE) == 0, assert_aligned_page_size);
/*
* Defines to allow an SP to subscribe for power management messages
*/
#define FFA_PM_MSG_SUB_CPU_OFF U(1 << 0)
#define FFA_PM_MSG_SUB_CPU_SUSPEND U(1 << 1)
#define FFA_PM_MSG_SUB_CPU_SUSPEND_RESUME U(1 << 2)
/*
* Runtime states of an execution context as per the FF-A v1.1 specification.
*/
enum sp_runtime_states {
RT_STATE_WAITING,
RT_STATE_RUNNING,
RT_STATE_PREEMPTED,
RT_STATE_BLOCKED
};
/*
* Runtime model of an execution context as per the FF-A v1.1 specification. Its
* value is valid only if the execution context is not in the waiting state.
*/
enum sp_runtime_model {
RT_MODEL_DIR_REQ,
RT_MODEL_RUN,
RT_MODEL_INIT,
RT_MODEL_INTR
};
enum sp_runtime_el {
EL1 = 0,
S_EL0,
S_EL1
};
enum sp_execution_state {
SP_STATE_AARCH64 = 0,
SP_STATE_AARCH32
};
enum mailbox_state {
/* There is no message in the mailbox. */
MAILBOX_STATE_EMPTY,
/* There is a message that has been populated in the mailbox. */
MAILBOX_STATE_FULL,
};
struct mailbox {
enum mailbox_state state;
/* RX/TX Buffers. */
void *rx_buffer;
const void *tx_buffer;
/* Size of RX/TX Buffer. */
uint32_t rxtx_page_count;
/* Lock access to mailbox. */
spinlock_t lock;
};
/*
* Execution context members for an SP. This is a bit like struct
* vcpu in a hypervisor.
*/
struct sp_exec_ctx {
/*
* Store the stack address to restore C runtime context from after
* returning from a synchronous entry into the SP.
*/
uint64_t c_rt_ctx;
/* Space to maintain the architectural state of an SP. */
cpu_context_t cpu_ctx;
/* Track the current runtime state of the SP. */
enum sp_runtime_states rt_state;
/* Track the current runtime model of the SP. */
enum sp_runtime_model rt_model;
};
/*
* Structure to describe the cumulative properties of an SP.
*/
struct secure_partition_desc {
/*
* Execution contexts allocated to this endpoint. Ideally,
* we need as many contexts as there are physical cpus only
* for a S-EL1 SP which is MP-pinned.
*/
struct sp_exec_ctx ec[PLATFORM_CORE_COUNT];
/* ID of the Secure Partition. */
uint16_t sp_id;
/* Runtime EL. */
enum sp_runtime_el runtime_el;
/* Partition UUID. */
uint32_t uuid[4];
/* Partition Properties. */
uint32_t properties;
/* Supported FF-A Version. */
uint32_t ffa_version;
/* Execution State. */
enum sp_execution_state execution_state;
/* Mailbox tracking. */
struct mailbox mailbox;
/* Secondary entrypoint. Only valid for a S-EL1 SP. */
uintptr_t secondary_ep;
/*
* Store whether the SP has subscribed to any power management messages.
*/
uint16_t pwr_mgmt_msgs;
/*
* Store whether the SP has requested the use of the NS bit for memory
* management transactions if it is using FF-A v1.0.
*/
bool ns_bit_requested;
};
/*
* This define identifies the only SP that will be initialised and participate
* in FF-A communication. The implementation leaves the door open for more SPs
* to be managed in future but for now it is reasonable to assume that either a
* single S-EL0 or a single S-EL1 SP will be supported. This define will be used
* to identify which SP descriptor to initialise and manage during SP runtime.
*/
#define ACTIVE_SP_DESC_INDEX 0
/*
* Structure to describe the cumulative properties of the Hypervisor and
* NS-Endpoints.
*/
struct ns_endpoint_desc {
/*
* ID of the NS-Endpoint or Hypervisor.
*/
uint16_t ns_ep_id;
/*
* Mailbox tracking.
*/
struct mailbox mailbox;
/*
* Supported FF-A Version
*/
uint32_t ffa_version;
};
/**
* Holds information returned for each partition by the FFA_PARTITION_INFO_GET
* interface.
*/
struct ffa_partition_info_v1_0 {
uint16_t ep_id;
uint16_t execution_ctx_count;
uint32_t properties;
};
/* Extended structure for v1.1. */
struct ffa_partition_info_v1_1 {
uint16_t ep_id;
uint16_t execution_ctx_count;
uint32_t properties;
uint32_t uuid[4];
};
/* Reference to power management hooks */
extern const spd_pm_ops_t spmc_pm;
/* Setup Function for different SP types. */
void spmc_sp_common_setup(struct secure_partition_desc *sp,
entry_point_info_t *ep_info,
int32_t boot_info_reg);
void spmc_el1_sp_setup(struct secure_partition_desc *sp,
entry_point_info_t *ep_info);
void spmc_sp_common_ep_commit(struct secure_partition_desc *sp,
entry_point_info_t *ep_info);
/*
* Helper function to perform a synchronous entry into a SP.
*/
uint64_t spmc_sp_synchronous_entry(struct sp_exec_ctx *ec);
/*
* Helper function to obtain the descriptor of the current SP on a physical cpu.
*/
struct secure_partition_desc *spmc_get_current_sp_ctx(void);
/*
* Helper function to obtain the execution context of an SP on a
* physical cpu.
*/
struct sp_exec_ctx *spmc_get_sp_ec(struct secure_partition_desc *sp);
/*
* Helper function to obtain the index of the execution context of an SP on a
* physical cpu.
*/
unsigned int get_ec_index(struct secure_partition_desc *sp);
uint64_t spmc_ffa_error_return(void *handle, int error_code);
/*
* Ensure a partition ID does not clash and follows the secure world convention.
*/
bool is_ffa_secure_id_valid(uint16_t partition_id);
/*
* Helper function to obtain the array storing the EL3
* Logical Partition descriptors.
*/
struct el3_lp_desc *get_el3_lp_array(void);
/*
* Helper function to obtain the RX/TX buffer pair descriptor of the Hypervisor
* or OS kernel in the normal world or the last SP that was run.
*/
struct mailbox *spmc_get_mbox_desc(bool secure_origin);
/*
* Helper function to obtain the context of an SP with a given partition ID.
*/
struct secure_partition_desc *spmc_get_sp_ctx(uint16_t id);
/*
* Add helper function to obtain the FF-A version of the calling
* partition.
*/
uint32_t get_partition_ffa_version(bool secure_origin);
#endif /* SPMC_H */
@@ -0,0 +1,44 @@
#
# Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
ifneq (${ARCH},aarch64)
$(error "Error: SPMC is only supported on aarch64.")
endif
SPMC_SOURCES := $(addprefix services/std_svc/spm/el3_spmc/, \
spmc_main.c \
spmc_setup.c \
logical_sp.c \
spmc_pm.c \
spmc_shared_mem.c)
# Specify platform specific logical partition implementation.
SPMC_LP_SOURCES := $(addprefix ${PLAT_DIR}/, \
${PLAT}_el3_spmc_logical_sp.c)
SPMC_SOURCES += $(SPMC_LP_SOURCES)
# Let the top-level Makefile know that we intend to include a BL32 image
NEED_BL32 := yes
ifndef BL32
# The SPMC is paired with a Test Secure Payload source and we intend to
# build the Test Secure Payload if no other image has been provided
# for BL32.
#
# In cases where an associated Secure Payload lies outside this build
# system/source tree, the dispatcher Makefile can either invoke an external
# build command or assume it is pre-built.
BL32_ROOT := bl32/tsp
# Conditionally include SP's Makefile. The assumption is that the TSP's build
# system is compatible with that of Trusted Firmware, and it'll add and populate
# necessary build targets and variables.
include ${BL32_ROOT}/tsp.mk
endif
@@ -0,0 +1,283 @@
/*
* Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/spinlock.h>
#include <plat/common/common_def.h>
#include <plat/common/platform.h>
#include <services/ffa_svc.h>
#include "spmc.h"
#include <platform_def.h>
/*******************************************************************************
* spmc_build_pm_message
*
* Builds an SPMC to SP direct message request.
******************************************************************************/
static void spmc_build_pm_message(gp_regs_t *gpregs,
unsigned long long message,
uint8_t pm_msg_type,
uint16_t sp_id)
{
write_ctx_reg(gpregs, CTX_GPREG_X0, FFA_MSG_SEND_DIRECT_REQ_SMC32);
write_ctx_reg(gpregs, CTX_GPREG_X1,
(FFA_SPMC_ID << FFA_DIRECT_MSG_SOURCE_SHIFT) |
sp_id);
write_ctx_reg(gpregs, CTX_GPREG_X2, FFA_FWK_MSG_BIT |
(pm_msg_type & FFA_FWK_MSG_MASK));
write_ctx_reg(gpregs, CTX_GPREG_X3, message);
}
/*******************************************************************************
* This CPU has been turned on. Enter the SP to initialise S-EL1.
******************************************************************************/
static void spmc_cpu_on_finish_handler(u_register_t unused)
{
struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
struct sp_exec_ctx *ec;
unsigned int linear_id = plat_my_core_pos();
entry_point_info_t sec_ec_ep_info = {0};
uint64_t rc;
/* Sanity check for a NULL pointer dereference. */
assert(sp != NULL);
/* Initialize entry point information for the SP. */
SET_PARAM_HEAD(&sec_ec_ep_info, PARAM_EP, VERSION_1,
SECURE | EP_ST_ENABLE);
/*
* Check if the primary execution context registered an entry point else
* bail out early.
* TODO: Add support for boot reason in manifest to allow jumping to
* entrypoint into the primary execution context.
*/
if (sp->secondary_ep == 0) {
WARN("%s: No secondary ep on core%u\n", __func__, linear_id);
return;
}
sec_ec_ep_info.pc = sp->secondary_ep;
/*
* Setup and initialise the SP execution context on this physical cpu.
*/
spmc_el1_sp_setup(sp, &sec_ec_ep_info);
spmc_sp_common_ep_commit(sp, &sec_ec_ep_info);
/* Obtain a reference to the SP execution context. */
ec = spmc_get_sp_ec(sp);
/*
* TODO: Should we do some PM related state tracking of the SP execution
* context here?
*/
/* Update the runtime model and state of the partition. */
ec->rt_model = RT_MODEL_INIT;
ec->rt_state = RT_STATE_RUNNING;
INFO("SP (0x%x) init start on core%u.\n", sp->sp_id, linear_id);
rc = spmc_sp_synchronous_entry(ec);
if (rc != 0ULL) {
ERROR("%s failed (%lu) on CPU%u\n", __func__, rc, linear_id);
}
/* Update the runtime state of the partition. */
ec->rt_state = RT_STATE_WAITING;
VERBOSE("CPU %u on!\n", linear_id);
}
/*******************************************************************************
* Helper function to send a FF-A power management message to an SP.
******************************************************************************/
static int32_t spmc_send_pm_msg(uint8_t pm_msg_type,
unsigned long long psci_event)
{
struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
struct sp_exec_ctx *ec;
gp_regs_t *gpregs_ctx;
unsigned int linear_id = plat_my_core_pos();
u_register_t resp;
uint64_t rc;
/* Obtain a reference to the SP execution context. */
ec = spmc_get_sp_ec(sp);
/*
* TODO: Should we do some PM related state tracking of the SP execution
* context here?
*/
/*
* Build an SPMC to SP direct message request.
* Note that x4-x6 should be populated with the original PSCI arguments.
*/
spmc_build_pm_message(get_gpregs_ctx(&ec->cpu_ctx),
psci_event,
pm_msg_type,
sp->sp_id);
/* Sanity check partition state. */
assert(ec->rt_state == RT_STATE_WAITING);
/* Update the runtime model and state of the partition. */
ec->rt_model = RT_MODEL_DIR_REQ;
ec->rt_state = RT_STATE_RUNNING;
rc = spmc_sp_synchronous_entry(ec);
if (rc != 0ULL) {
ERROR("%s failed (%lu) on CPU%u.\n", __func__, rc, linear_id);
assert(false);
return -EINVAL;
}
/*
* Validate we receive an expected response from the SP.
* TODO: We don't currently support aborting an SP in the scenario
* where it is misbehaving so assert these conditions are not
* met for now.
*/
gpregs_ctx = get_gpregs_ctx(&ec->cpu_ctx);
/* Expect a direct message response from the SP. */
resp = read_ctx_reg(gpregs_ctx, CTX_GPREG_X0);
if (resp != FFA_MSG_SEND_DIRECT_RESP_SMC32) {
ERROR("%s invalid SP response (%lx).\n", __func__, resp);
assert(false);
return -EINVAL;
}
/* Ensure the sender and receiver are populated correctly. */
resp = read_ctx_reg(gpregs_ctx, CTX_GPREG_X1);
if (!(ffa_endpoint_source(resp) == sp->sp_id &&
ffa_endpoint_destination(resp) == FFA_SPMC_ID)) {
ERROR("%s invalid src/dst response (%lx).\n", __func__, resp);
assert(false);
return -EINVAL;
}
/* Expect a PM message response from the SP. */
resp = read_ctx_reg(gpregs_ctx, CTX_GPREG_X2);
if ((resp & FFA_FWK_MSG_BIT) == 0U ||
((resp & FFA_FWK_MSG_MASK) != FFA_PM_MSG_PM_RESP)) {
ERROR("%s invalid PM response (%lx).\n", __func__, resp);
assert(false);
return -EINVAL;
}
/* Update the runtime state of the partition. */
ec->rt_state = RT_STATE_WAITING;
/* Return the status code returned by the SP */
return read_ctx_reg(gpregs_ctx, CTX_GPREG_X3);
}
/*******************************************************************************
* spmc_cpu_suspend_finish_handler
******************************************************************************/
static void spmc_cpu_suspend_finish_handler(u_register_t unused)
{
struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
unsigned int linear_id = plat_my_core_pos();
int32_t rc;
/* Sanity check for a NULL pointer dereference. */
assert(sp != NULL);
/*
* Check if the SP has subscribed for this power management message.
* If not then we don't have anything else to do here.
*/
if ((sp->pwr_mgmt_msgs & FFA_PM_MSG_SUB_CPU_SUSPEND_RESUME) == 0U) {
goto exit;
}
rc = spmc_send_pm_msg(FFA_PM_MSG_WB_REQ, FFA_WB_TYPE_NOTS2RAM);
if (rc < 0) {
ERROR("%s failed (%d) on CPU%u\n", __func__, rc, linear_id);
return;
}
exit:
VERBOSE("CPU %u resumed!\n", linear_id);
}
/*******************************************************************************
* spmc_cpu_suspend_handler
******************************************************************************/
static void spmc_cpu_suspend_handler(u_register_t unused)
{
struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
unsigned int linear_id = plat_my_core_pos();
int32_t rc;
/* Sanity check for a NULL pointer dereference. */
assert(sp != NULL);
/*
* Check if the SP has subscribed for this power management message.
* If not then we don't have anything else to do here.
*/
if ((sp->pwr_mgmt_msgs & FFA_PM_MSG_SUB_CPU_SUSPEND) == 0U) {
goto exit;
}
rc = spmc_send_pm_msg(FFA_FWK_MSG_PSCI, PSCI_CPU_SUSPEND_AARCH64);
if (rc < 0) {
ERROR("%s failed (%d) on CPU%u\n", __func__, rc, linear_id);
return;
}
exit:
VERBOSE("CPU %u suspend!\n", linear_id);
}
/*******************************************************************************
* spmc_cpu_off_handler
******************************************************************************/
static int32_t spmc_cpu_off_handler(u_register_t unused)
{
struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
unsigned int linear_id = plat_my_core_pos();
int32_t ret = 0;
/* Sanity check for a NULL pointer dereference. */
assert(sp != NULL);
/*
* Check if the SP has subscribed for this power management message.
* If not then we don't have anything else to do here.
*/
if ((sp->pwr_mgmt_msgs & FFA_PM_MSG_SUB_CPU_OFF) == 0U) {
goto exit;
}
ret = spmc_send_pm_msg(FFA_FWK_MSG_PSCI, PSCI_CPU_OFF);
if (ret < 0) {
ERROR("%s failed (%d) on CPU%u\n", __func__, ret, linear_id);
return ret;
}
exit:
VERBOSE("CPU %u off!\n", linear_id);
return ret;
}
/*******************************************************************************
* Structure populated by the SPM Core to perform any bookkeeping before
* PSCI executes a power mgmt. operation.
******************************************************************************/
const spd_pm_ops_t spmc_pm = {
.svc_on_finish = spmc_cpu_on_finish_handler,
.svc_off = spmc_cpu_off_handler,
.svc_suspend = spmc_cpu_suspend_handler,
.svc_suspend_finish = spmc_cpu_suspend_finish_handler
};
@@ -0,0 +1,278 @@
/*
* Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <string.h>
#include <arch.h>
#include <arch_helpers.h>
#include <common/debug.h>
#include <common/fdt_wrappers.h>
#include <context.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/utils.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <libfdt.h>
#include <plat/common/common_def.h>
#include <plat/common/platform.h>
#include <services/ffa_svc.h>
#include "spm_common.h"
#include "spmc.h"
#include <tools_share/firmware_image_package.h>
#include <platform_def.h>
/*
* Statically allocate a page of memory for passing boot information to an SP.
*/
static uint8_t ffa_boot_info_mem[PAGE_SIZE] __aligned(PAGE_SIZE);
/*
* This function creates a initialization descriptor in the memory reserved
* for passing boot information to an SP. It then copies the partition manifest
* into this region and ensures that its reference in the initialization
* descriptor is updated.
*/
static void spmc_create_boot_info(entry_point_info_t *ep_info,
struct secure_partition_desc *sp)
{
struct ffa_boot_info_header *boot_header;
struct ffa_boot_info_desc *boot_descriptor;
uintptr_t manifest_addr;
/*
* Calculate the maximum size of the manifest that can be accommodated
* in the boot information memory region.
*/
const unsigned int
max_manifest_sz = sizeof(ffa_boot_info_mem) -
(sizeof(struct ffa_boot_info_header) +
sizeof(struct ffa_boot_info_desc));
/*
* The current implementation only supports the FF-A v1.1
* implementation of the boot protocol, therefore check
* that a v1.0 SP has not requested use of the protocol.
*/
if (sp->ffa_version == MAKE_FFA_VERSION(1, 0)) {
ERROR("FF-A boot protocol not supported for v1.0 clients\n");
return;
}
/*
* Check if the manifest will fit into the boot info memory region else
* bail.
*/
if (ep_info->args.arg1 > max_manifest_sz) {
WARN("Unable to copy manifest into boot information. ");
WARN("Max sz = %u bytes. Manifest sz = %lu bytes\n",
max_manifest_sz, ep_info->args.arg1);
return;
}
/* Zero the memory region before populating. */
memset(ffa_boot_info_mem, 0, PAGE_SIZE);
/*
* Populate the ffa_boot_info_header at the start of the boot info
* region.
*/
boot_header = (struct ffa_boot_info_header *) ffa_boot_info_mem;
/* Position the ffa_boot_info_desc after the ffa_boot_info_header. */
boot_header->offset_boot_info_desc =
sizeof(struct ffa_boot_info_header);
boot_descriptor = (struct ffa_boot_info_desc *)
(ffa_boot_info_mem +
boot_header->offset_boot_info_desc);
/*
* We must use the FF-A version coresponding to the version implemented
* by the SP. Currently this can only be v1.1.
*/
boot_header->version = sp->ffa_version;
/* Populate the boot information header. */
boot_header->size_boot_info_desc = sizeof(struct ffa_boot_info_desc);
/* Set the signature "0xFFA". */
boot_header->signature = FFA_INIT_DESC_SIGNATURE;
/* Set the count. Currently 1 since only the manifest is specified. */
boot_header->count_boot_info_desc = 1;
/* Populate the boot information descriptor for the manifest. */
boot_descriptor->type =
FFA_BOOT_INFO_TYPE(FFA_BOOT_INFO_TYPE_STD) |
FFA_BOOT_INFO_TYPE_ID(FFA_BOOT_INFO_TYPE_ID_FDT);
boot_descriptor->flags =
FFA_BOOT_INFO_FLAG_NAME(FFA_BOOT_INFO_FLAG_NAME_UUID) |
FFA_BOOT_INFO_FLAG_CONTENT(FFA_BOOT_INFO_FLAG_CONTENT_ADR);
/*
* Copy the manifest into boot info region after the boot information
* descriptor.
*/
boot_descriptor->size_boot_info = (uint32_t) ep_info->args.arg1;
manifest_addr = (uintptr_t) (ffa_boot_info_mem +
boot_header->offset_boot_info_desc +
boot_header->size_boot_info_desc);
memcpy((void *) manifest_addr, (void *) ep_info->args.arg0,
boot_descriptor->size_boot_info);
boot_descriptor->content = manifest_addr;
/* Calculate the size of the total boot info blob. */
boot_header->size_boot_info_blob = boot_header->offset_boot_info_desc +
boot_descriptor->size_boot_info +
(boot_header->count_boot_info_desc *
boot_header->size_boot_info_desc);
INFO("SP boot info @ 0x%lx, size: %u bytes.\n",
(uintptr_t) ffa_boot_info_mem,
boot_header->size_boot_info_blob);
INFO("SP manifest @ 0x%lx, size: %u bytes.\n",
boot_descriptor->content,
boot_descriptor->size_boot_info);
}
/*
* We are assuming that the index of the execution
* context used is the linear index of the current physical cpu.
*/
unsigned int get_ec_index(struct secure_partition_desc *sp)
{
return plat_my_core_pos();
}
/* S-EL1 partition specific initialisation. */
void spmc_el1_sp_setup(struct secure_partition_desc *sp,
entry_point_info_t *ep_info)
{
/* Sanity check input arguments. */
assert(sp != NULL);
assert(ep_info != NULL);
/* Initialise the SPSR for S-EL1 SPs. */
ep_info->spsr = SPSR_64(MODE_EL1, MODE_SP_ELX,
DISABLE_ALL_EXCEPTIONS);
/*
* TF-A Implementation defined behaviour to provide the linear
* core ID in the x4 register.
*/
ep_info->args.arg4 = (uintptr_t) plat_my_core_pos();
/*
* Check whether setup is being performed for the primary or a secondary
* execution context. In the latter case, indicate to the SP that this
* is a warm boot.
* TODO: This check would need to be reworked if the same entry point is
* used for both primary and secondary initialisation.
*/
if (sp->secondary_ep != 0U) {
/*
* Sanity check that the secondary entry point is still what was
* originally set.
*/
assert(sp->secondary_ep == ep_info->pc);
ep_info->args.arg0 = FFA_WB_TYPE_S2RAM;
}
}
/* Common initialisation for all SPs. */
void spmc_sp_common_setup(struct secure_partition_desc *sp,
entry_point_info_t *ep_info,
int32_t boot_info_reg)
{
uint16_t sp_id;
/* Assign FF-A Partition ID if not already assigned. */
if (sp->sp_id == INV_SP_ID) {
sp_id = FFA_SP_ID_BASE + ACTIVE_SP_DESC_INDEX;
/*
* Ensure we don't clash with previously assigned partition
* IDs.
*/
while (!is_ffa_secure_id_valid(sp_id)) {
sp_id++;
if (sp_id == FFA_SWD_ID_LIMIT) {
ERROR("Unable to determine valid SP ID.\n");
panic();
}
}
sp->sp_id = sp_id;
}
/*
* We currently only support S-EL1 partitions so ensure this is the
* case.
*/
assert(sp->runtime_el == S_EL1);
/* Check if the SP wants to use the FF-A boot protocol. */
if (boot_info_reg >= 0) {
/*
* Create a boot information descriptor and copy the partition
* manifest into the reserved memory region for consumption by
* the SP.
*/
spmc_create_boot_info(ep_info, sp);
/*
* We have consumed what we need from ep args so we can now
* zero them before we start populating with new information
* specifically for the SP.
*/
zeromem(&ep_info->args, sizeof(ep_info->args));
/*
* Pass the address of the boot information in the
* boot_info_reg.
*/
switch (boot_info_reg) {
case 0:
ep_info->args.arg0 = (uintptr_t) ffa_boot_info_mem;
break;
case 1:
ep_info->args.arg1 = (uintptr_t) ffa_boot_info_mem;
break;
case 2:
ep_info->args.arg2 = (uintptr_t) ffa_boot_info_mem;
break;
case 3:
ep_info->args.arg3 = (uintptr_t) ffa_boot_info_mem;
break;
default:
ERROR("Invalid value for \"gp-register-num\" %d.\n",
boot_info_reg);
}
} else {
/*
* We don't need any of the information that was populated
* in ep_args so we can clear them.
*/
zeromem(&ep_info->args, sizeof(ep_info->args));
}
}
/*
* Initialise the SP context now we have populated the common and EL specific
* entrypoint information.
*/
void spmc_sp_common_ep_commit(struct secure_partition_desc *sp,
entry_point_info_t *ep_info)
{
cpu_context_t *cpu_ctx;
cpu_ctx = &(spmc_get_sp_ec(sp)->cpu_ctx);
print_entry_point_info(ep_info);
cm_setup_context(cpu_ctx, ep_info);
}
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SPMC_SHARED_MEM_H
#define SPMC_SHARED_MEM_H
#include <services/el3_spmc_ffa_memory.h>
/**
* struct ffa_mem_relinquish_descriptor - Relinquish request descriptor.
* @handle:
* Id of shared memory object to relinquish.
* @flags:
* If bit 0 is set clear memory after unmapping from borrower. Must be 0
* for share. Bit[1]: Time slicing. Not supported, must be 0. All other
* bits are reserved 0.
* @endpoint_count:
* Number of entries in @endpoint_array.
* @endpoint_array:
* Array of endpoint ids.
*/
struct ffa_mem_relinquish_descriptor {
uint64_t handle;
uint32_t flags;
uint32_t endpoint_count;
ffa_endpoint_id16_t endpoint_array[];
};
CASSERT(sizeof(struct ffa_mem_relinquish_descriptor) == 16,
assert_ffa_mem_relinquish_descriptor_size_mismatch);
/**
* struct spmc_shmem_obj_state - Global state.
* @data: Backing store for spmc_shmem_obj objects.
* @data_size: The size allocated for the backing store.
* @allocated: Number of bytes allocated in @data.
* @next_handle: Handle used for next allocated object.
* @lock: Lock protecting all state in this file.
*/
struct spmc_shmem_obj_state {
uint8_t *data;
size_t data_size;
size_t allocated;
uint64_t next_handle;
spinlock_t lock;
};
extern struct spmc_shmem_obj_state spmc_shmem_obj_state;
extern int plat_spmc_shmem_begin(struct ffa_mtd *desc);
extern int plat_spmc_shmem_reclaim(struct ffa_mtd *desc);
long spmc_ffa_mem_send(uint32_t smc_fid,
bool secure_origin,
uint64_t total_length,
uint32_t fragment_length,
uint64_t address,
uint32_t page_count,
void *cookie,
void *handle,
uint64_t flags);
long spmc_ffa_mem_frag_tx(uint32_t smc_fid,
bool secure_origin,
uint64_t handle_low,
uint64_t handle_high,
uint32_t fragment_length,
uint32_t sender_id,
void *cookie,
void *handle,
uint64_t flags);
long spmc_ffa_mem_retrieve_req(uint32_t smc_fid,
bool secure_origin,
uint32_t total_length,
uint32_t fragment_length,
uint64_t address,
uint32_t page_count,
void *cookie,
void *handle,
uint64_t flags);
long spmc_ffa_mem_frag_rx(uint32_t smc_fid,
bool secure_origin,
uint32_t handle_low,
uint32_t handle_high,
uint32_t fragment_offset,
uint32_t sender_id,
void *cookie,
void *handle,
uint64_t flags);
int spmc_ffa_mem_relinquish(uint32_t smc_fid,
bool secure_origin,
uint32_t handle_low,
uint32_t handle_high,
uint32_t fragment_offset,
uint32_t sender_id,
void *cookie,
void *handle,
uint64_t flags);
int spmc_ffa_mem_reclaim(uint32_t smc_fid,
bool secure_origin,
uint32_t handle_low,
uint32_t handle_high,
uint32_t mem_flags,
uint64_t x4,
void *cookie,
void *handle,
uint64_t flags);
#endif /* SPMC_SHARED_MEM_H */
@@ -0,0 +1,128 @@
/*
* Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <asm_macros.S>
#include <common/bl_common.h>
#include <context.h>
/* -----------------------------------------------------------------------------
* Very simple stackless exception handlers used by the spm shim layer.
* -----------------------------------------------------------------------------
*/
.globl spm_shim_exceptions_ptr
vector_base spm_shim_exceptions_ptr, .spm_shim_exceptions
/* -----------------------------------------------------
* Current EL with SP0 : 0x0 - 0x200
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionSP0, .spm_shim_exceptions
b .
end_vector_entry SynchronousExceptionSP0
vector_entry IrqSP0, .spm_shim_exceptions
b .
end_vector_entry IrqSP0
vector_entry FiqSP0, .spm_shim_exceptions
b .
end_vector_entry FiqSP0
vector_entry SErrorSP0, .spm_shim_exceptions
b .
end_vector_entry SErrorSP0
/* -----------------------------------------------------
* Current EL with SPx: 0x200 - 0x400
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionSPx, .spm_shim_exceptions
b .
end_vector_entry SynchronousExceptionSPx
vector_entry IrqSPx, .spm_shim_exceptions
b .
end_vector_entry IrqSPx
vector_entry FiqSPx, .spm_shim_exceptions
b .
end_vector_entry FiqSPx
vector_entry SErrorSPx, .spm_shim_exceptions
b .
end_vector_entry SErrorSPx
/* -----------------------------------------------------
* Lower EL using AArch64 : 0x400 - 0x600. No exceptions
* are handled since secure_partition does not implement
* a lower EL
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionA64, .spm_shim_exceptions
msr tpidr_el1, x30
mrs x30, esr_el1
ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
cmp x30, #EC_AARCH64_SVC
b.eq do_smc
cmp x30, #EC_AARCH32_SVC
b.eq do_smc
cmp x30, #EC_AARCH64_SYS
b.eq handle_sys_trap
/* Fail in all the other cases */
b panic
/* ---------------------------------------------
* Tell SPM that we are done initialising
* ---------------------------------------------
*/
do_smc:
mrs x30, tpidr_el1
smc #0
exception_return
/* AArch64 system instructions trap are handled as a panic for now */
handle_sys_trap:
panic:
b panic
end_vector_entry SynchronousExceptionA64
vector_entry IrqA64, .spm_shim_exceptions
b .
end_vector_entry IrqA64
vector_entry FiqA64, .spm_shim_exceptions
b .
end_vector_entry FiqA64
vector_entry SErrorA64, .spm_shim_exceptions
b .
end_vector_entry SErrorA64
/* -----------------------------------------------------
* Lower EL using AArch32 : 0x600 - 0x800
* -----------------------------------------------------
*/
vector_entry SynchronousExceptionA32, .spm_shim_exceptions
b .
end_vector_entry SynchronousExceptionA32
vector_entry IrqA32, .spm_shim_exceptions
b .
end_vector_entry IrqA32
vector_entry FiqA32, .spm_shim_exceptions
b .
end_vector_entry FiqA32
vector_entry SErrorA32, .spm_shim_exceptions
b .
end_vector_entry SErrorA32
@@ -0,0 +1,34 @@
#
# Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
ifneq (${SPD},none)
$(error "Error: SPD and SPM_MM are incompatible build options.")
endif
ifneq (${ARCH},aarch64)
$(error "Error: SPM_MM is only supported on aarch64.")
endif
ifeq (${ENABLE_SVE_FOR_NS},1)
$(error "Error: SPM_MM is not compatible with ENABLE_SVE_FOR_NS")
endif
ifeq (${ENABLE_SME_FOR_NS},1)
$(error "Error: SPM_MM is not compatible with ENABLE_SME_FOR_NS")
endif
ifeq (${CTX_INCLUDE_FPREGS},0)
$(warning "Warning: SPM_MM: CTX_INCLUDE_FPREGS is set to 0")
endif
SPM_MM_SOURCES := $(addprefix services/std_svc/spm/spm_mm/, \
${ARCH}/spm_mm_shim_exceptions.S \
spm_mm_main.c \
spm_mm_setup.c \
spm_mm_xlat.c)
# Let the top-level Makefile know that we intend to include a BL32 image
NEED_BL32 := yes
# required so that SPM code executing at S-EL0 can access the timer registers
NS_TIMER_SWITCH := 1
@@ -0,0 +1,370 @@
/*
* Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_helpers.h>
#include <assert.h>
#include <errno.h>
#include <bl31/bl31.h>
#include <bl31/ehf.h>
#include <common/debug.h>
#include <common/runtime_svc.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/smccc.h>
#include <lib/spinlock.h>
#include <lib/utils.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <plat/common/platform.h>
#include <services/spm_mm_partition.h>
#include <services/spm_mm_svc.h>
#include <smccc_helpers.h>
#include "spm_common.h"
#include "spm_mm_private.h"
/*******************************************************************************
* Secure Partition context information.
******************************************************************************/
static sp_context_t sp_ctx;
/*******************************************************************************
* Set state of a Secure Partition context.
******************************************************************************/
void sp_state_set(sp_context_t *sp_ptr, sp_state_t state)
{
spin_lock(&(sp_ptr->state_lock));
sp_ptr->state = state;
spin_unlock(&(sp_ptr->state_lock));
}
/*******************************************************************************
* Wait until the state of a Secure Partition is the specified one and change it
* to the desired state.
******************************************************************************/
void sp_state_wait_switch(sp_context_t *sp_ptr, sp_state_t from, sp_state_t to)
{
int success = 0;
while (success == 0) {
spin_lock(&(sp_ptr->state_lock));
if (sp_ptr->state == from) {
sp_ptr->state = to;
success = 1;
}
spin_unlock(&(sp_ptr->state_lock));
}
}
/*******************************************************************************
* Check if the state of a Secure Partition is the specified one and, if so,
* change it to the desired state. Returns 0 on success, -1 on error.
******************************************************************************/
int sp_state_try_switch(sp_context_t *sp_ptr, sp_state_t from, sp_state_t to)
{
int ret = -1;
spin_lock(&(sp_ptr->state_lock));
if (sp_ptr->state == from) {
sp_ptr->state = to;
ret = 0;
}
spin_unlock(&(sp_ptr->state_lock));
return ret;
}
/*******************************************************************************
* This function takes an SP context pointer and performs a synchronous entry
* into it.
******************************************************************************/
static uint64_t spm_sp_synchronous_entry(sp_context_t *ctx)
{
uint64_t rc;
assert(ctx != NULL);
/* Assign the context of the SP to this CPU */
cm_set_context(&(ctx->cpu_ctx), SECURE);
/* Restore the context assigned above */
cm_el1_sysregs_context_restore(SECURE);
cm_set_next_eret_context(SECURE);
/* Invalidate TLBs at EL1. */
tlbivmalle1();
dsbish();
/* Enter Secure Partition */
rc = spm_secure_partition_enter(&ctx->c_rt_ctx);
/* Save secure state */
cm_el1_sysregs_context_save(SECURE);
return rc;
}
/*******************************************************************************
* This function returns to the place where spm_sp_synchronous_entry() was
* called originally.
******************************************************************************/
__dead2 static void spm_sp_synchronous_exit(uint64_t rc)
{
sp_context_t *ctx = &sp_ctx;
/*
* The SPM must have initiated the original request through a
* synchronous entry into the secure partition. Jump back to the
* original C runtime context with the value of rc in x0;
*/
spm_secure_partition_exit(ctx->c_rt_ctx, rc);
panic();
}
/*******************************************************************************
* Jump to each Secure Partition for the first time.
******************************************************************************/
static int32_t spm_init(void)
{
uint64_t rc;
sp_context_t *ctx;
INFO("Secure Partition init...\n");
ctx = &sp_ctx;
ctx->state = SP_STATE_RESET;
rc = spm_sp_synchronous_entry(ctx);
assert(rc == 0);
ctx->state = SP_STATE_IDLE;
INFO("Secure Partition initialized.\n");
return !rc;
}
/*******************************************************************************
* Initialize contexts of all Secure Partitions.
******************************************************************************/
int32_t spm_mm_setup(void)
{
sp_context_t *ctx;
/* Disable MMU at EL1 (initialized by BL2) */
disable_mmu_icache_el1();
/* Initialize context of the SP */
INFO("Secure Partition context setup start...\n");
ctx = &sp_ctx;
/* Assign translation tables context. */
ctx->xlat_ctx_handle = spm_get_sp_xlat_context();
spm_sp_setup(ctx);
/* Register init function for deferred init. */
bl31_register_bl32_init(&spm_init);
INFO("Secure Partition setup done.\n");
return 0;
}
/*******************************************************************************
* Function to perform a call to a Secure Partition.
******************************************************************************/
uint64_t spm_mm_sp_call(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3)
{
uint64_t rc;
sp_context_t *sp_ptr = &sp_ctx;
#if CTX_INCLUDE_FPREGS
/*
* SP runs to completion, no need to restore FP registers of secure context.
* Save FP registers only for non secure context.
*/
fpregs_context_save(get_fpregs_ctx(cm_get_context(NON_SECURE)));
#endif
/* Wait until the Secure Partition is idle and set it to busy. */
sp_state_wait_switch(sp_ptr, SP_STATE_IDLE, SP_STATE_BUSY);
/* Set values for registers on SP entry */
cpu_context_t *cpu_ctx = &(sp_ptr->cpu_ctx);
write_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X0, smc_fid);
write_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X1, x1);
write_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X2, x2);
write_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X3, x3);
/* Jump to the Secure Partition. */
rc = spm_sp_synchronous_entry(sp_ptr);
/* Flag Secure Partition as idle. */
assert(sp_ptr->state == SP_STATE_BUSY);
sp_state_set(sp_ptr, SP_STATE_IDLE);
#if CTX_INCLUDE_FPREGS
/*
* SP runs to completion, no need to save FP registers of secure context.
* Restore only non secure world FP registers.
*/
fpregs_context_restore(get_fpregs_ctx(cm_get_context(NON_SECURE)));
#endif
return rc;
}
/*******************************************************************************
* MM_COMMUNICATE handler
******************************************************************************/
static uint64_t mm_communicate(uint32_t smc_fid, uint64_t mm_cookie,
uint64_t comm_buffer_address,
uint64_t comm_size_address, void *handle)
{
uint64_t rc;
/* Cookie. Reserved for future use. It must be zero. */
if (mm_cookie != 0U) {
ERROR("MM_COMMUNICATE: cookie is not zero\n");
SMC_RET1(handle, SPM_MM_INVALID_PARAMETER);
}
if (comm_buffer_address == 0U) {
ERROR("MM_COMMUNICATE: comm_buffer_address is zero\n");
SMC_RET1(handle, SPM_MM_INVALID_PARAMETER);
}
if (comm_size_address != 0U) {
VERBOSE("MM_COMMUNICATE: comm_size_address is not 0 as recommended.\n");
}
/*
* The current secure partition design mandates
* - at any point, only a single core can be
* executing in the secure partiton.
* - a core cannot be preempted by an interrupt
* while executing in secure partition.
* Raise the running priority of the core to the
* interrupt level configured for secure partition
* so as to block any interrupt from preempting this
* core.
*/
ehf_activate_priority(PLAT_SP_PRI);
/* Save the Normal world context */
cm_el1_sysregs_context_save(NON_SECURE);
rc = spm_mm_sp_call(smc_fid, comm_buffer_address, comm_size_address,
plat_my_core_pos());
/* Restore non-secure state */
cm_el1_sysregs_context_restore(NON_SECURE);
cm_set_next_eret_context(NON_SECURE);
/*
* Exited from secure partition. This core can take
* interrupts now.
*/
ehf_deactivate_priority(PLAT_SP_PRI);
SMC_RET1(handle, rc);
}
/*******************************************************************************
* Secure Partition Manager SMC handler.
******************************************************************************/
uint64_t spm_mm_smc_handler(uint32_t smc_fid,
uint64_t x1,
uint64_t x2,
uint64_t x3,
uint64_t x4,
void *cookie,
void *handle,
uint64_t flags)
{
unsigned int ns;
/* Determine which security state this SMC originated from */
ns = is_caller_non_secure(flags);
if (ns == SMC_FROM_SECURE) {
/* Handle SMCs from Secure world. */
assert(handle == cm_get_context(SECURE));
/* Make next ERET jump to S-EL0 instead of S-EL1. */
cm_set_elr_spsr_el3(SECURE, read_elr_el1(), read_spsr_el1());
switch (smc_fid) {
case SPM_MM_VERSION_AARCH32:
SMC_RET1(handle, SPM_MM_VERSION_COMPILED);
case MM_SP_EVENT_COMPLETE_AARCH64:
spm_sp_synchronous_exit(x1);
case MM_SP_MEMORY_ATTRIBUTES_GET_AARCH64:
INFO("Received MM_SP_MEMORY_ATTRIBUTES_GET_AARCH64 SMC\n");
if (sp_ctx.state != SP_STATE_RESET) {
WARN("MM_SP_MEMORY_ATTRIBUTES_GET_AARCH64 is available at boot time only\n");
SMC_RET1(handle, SPM_MM_NOT_SUPPORTED);
}
SMC_RET1(handle,
spm_memory_attributes_get_smc_handler(
&sp_ctx, x1));
case MM_SP_MEMORY_ATTRIBUTES_SET_AARCH64:
INFO("Received MM_SP_MEMORY_ATTRIBUTES_SET_AARCH64 SMC\n");
if (sp_ctx.state != SP_STATE_RESET) {
WARN("MM_SP_MEMORY_ATTRIBUTES_SET_AARCH64 is available at boot time only\n");
SMC_RET1(handle, SPM_MM_NOT_SUPPORTED);
}
SMC_RET1(handle,
spm_memory_attributes_set_smc_handler(
&sp_ctx, x1, x2, x3));
default:
break;
}
} else {
/* Handle SMCs from Non-secure world. */
assert(handle == cm_get_context(NON_SECURE));
switch (smc_fid) {
case MM_VERSION_AARCH32:
SMC_RET1(handle, MM_VERSION_COMPILED);
case MM_COMMUNICATE_AARCH32:
case MM_COMMUNICATE_AARCH64:
return mm_communicate(smc_fid, x1, x2, x3, handle);
case MM_SP_MEMORY_ATTRIBUTES_GET_AARCH64:
case MM_SP_MEMORY_ATTRIBUTES_SET_AARCH64:
/* SMC interfaces reserved for secure callers. */
SMC_RET1(handle, SPM_MM_NOT_SUPPORTED);
default:
break;
}
}
SMC_RET1(handle, SMC_UNK);
}
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SPM_MM_PRIVATE_H
#define SPM_MM_PRIVATE_H
#include <context.h>
#include "spm_common.h"
/*******************************************************************************
* Constants that allow assembler code to preserve callee-saved registers of the
* C runtime context while performing a security state switch.
******************************************************************************/
#define SP_C_RT_CTX_X19 0x0
#define SP_C_RT_CTX_X20 0x8
#define SP_C_RT_CTX_X21 0x10
#define SP_C_RT_CTX_X22 0x18
#define SP_C_RT_CTX_X23 0x20
#define SP_C_RT_CTX_X24 0x28
#define SP_C_RT_CTX_X25 0x30
#define SP_C_RT_CTX_X26 0x38
#define SP_C_RT_CTX_X27 0x40
#define SP_C_RT_CTX_X28 0x48
#define SP_C_RT_CTX_X29 0x50
#define SP_C_RT_CTX_X30 0x58
#define SP_C_RT_CTX_SIZE 0x60
#define SP_C_RT_CTX_ENTRIES (SP_C_RT_CTX_SIZE >> DWORD_SHIFT)
#ifndef __ASSEMBLER__
#include <stdint.h>
#include <lib/spinlock.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
typedef enum sp_state {
SP_STATE_RESET = 0,
SP_STATE_IDLE,
SP_STATE_BUSY
} sp_state_t;
typedef struct sp_context {
uint64_t c_rt_ctx;
cpu_context_t cpu_ctx;
xlat_ctx_t *xlat_ctx_handle;
sp_state_t state;
spinlock_t state_lock;
} sp_context_t;
void spm_sp_setup(sp_context_t *sp_ctx);
xlat_ctx_t *spm_get_sp_xlat_context(void);
int32_t spm_memory_attributes_get_smc_handler(sp_context_t *sp_ctx,
uintptr_t base_va);
int spm_memory_attributes_set_smc_handler(sp_context_t *sp_ctx,
u_register_t page_address,
u_register_t pages_count,
u_register_t smc_attributes);
#endif /* __ASSEMBLER__ */
#endif /* SPM_MM_PRIVATE_H */
@@ -0,0 +1,260 @@
/*
* Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
* Copyright (c) 2021, NVIDIA Corporation. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <string.h>
#include <arch.h>
#include <arch_helpers.h>
#include <context.h>
#include <common/debug.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <platform_def.h>
#include <plat/common/common_def.h>
#include <plat/common/platform.h>
#include <services/spm_mm_partition.h>
#include "spm_common.h"
#include "spm_mm_private.h"
#include "spm_mm_shim_private.h"
/* Setup context of the Secure Partition */
void spm_sp_setup(sp_context_t *sp_ctx)
{
cpu_context_t *ctx = &(sp_ctx->cpu_ctx);
/* Pointer to the MP information from the platform port. */
const spm_mm_boot_info_t *sp_boot_info =
plat_get_secure_partition_boot_info(NULL);
/*
* Initialize CPU context
* ----------------------
*/
entry_point_info_t ep_info = {0};
SET_PARAM_HEAD(&ep_info, PARAM_EP, VERSION_1, SECURE | EP_ST_ENABLE);
/* Setup entrypoint and SPSR */
ep_info.pc = sp_boot_info->sp_image_base;
ep_info.spsr = SPSR_64(MODE_EL0, MODE_SP_EL0, DISABLE_ALL_EXCEPTIONS);
/*
* X0: Virtual address of a buffer shared between EL3 and Secure EL0.
* The buffer will be mapped in the Secure EL1 translation regime
* with Normal IS WBWA attributes and RO data and Execute Never
* instruction access permissions.
*
* X1: Size of the buffer in bytes
*
* X2: cookie value (Implementation Defined)
*
* X3: cookie value (Implementation Defined)
*
* X4 to X7 = 0
*/
ep_info.args.arg0 = sp_boot_info->sp_shared_buf_base;
ep_info.args.arg1 = sp_boot_info->sp_shared_buf_size;
ep_info.args.arg2 = PLAT_SPM_COOKIE_0;
ep_info.args.arg3 = PLAT_SPM_COOKIE_1;
cm_setup_context(ctx, &ep_info);
/*
* SP_EL0: A non-zero value will indicate to the SP that the SPM has
* initialized the stack pointer for the current CPU through
* implementation defined means. The value will be 0 otherwise.
*/
write_ctx_reg(get_gpregs_ctx(ctx), CTX_GPREG_SP_EL0,
sp_boot_info->sp_stack_base + sp_boot_info->sp_pcpu_stack_size);
/*
* Setup translation tables
* ------------------------
*/
#if ENABLE_ASSERTIONS
/* Get max granularity supported by the platform. */
unsigned int max_granule = xlat_arch_get_max_supported_granule_size();
VERBOSE("Max translation granule size supported: %u KiB\n",
max_granule / 1024U);
unsigned int max_granule_mask = max_granule - 1U;
/* Base must be aligned to the max granularity */
assert((sp_boot_info->sp_ns_comm_buf_base & max_granule_mask) == 0);
/* Size must be a multiple of the max granularity */
assert((sp_boot_info->sp_ns_comm_buf_size & max_granule_mask) == 0);
#endif /* ENABLE_ASSERTIONS */
/* This region contains the exception vectors used at S-EL1. */
const mmap_region_t sel1_exception_vectors =
MAP_REGION_FLAT(SPM_SHIM_EXCEPTIONS_START,
SPM_SHIM_EXCEPTIONS_SIZE,
MT_CODE | MT_SECURE | MT_PRIVILEGED);
mmap_add_region_ctx(sp_ctx->xlat_ctx_handle,
&sel1_exception_vectors);
mmap_add_ctx(sp_ctx->xlat_ctx_handle,
plat_get_secure_partition_mmap(NULL));
init_xlat_tables_ctx(sp_ctx->xlat_ctx_handle);
/*
* MMU-related registers
* ---------------------
*/
xlat_ctx_t *xlat_ctx = sp_ctx->xlat_ctx_handle;
uint64_t mmu_cfg_params[MMU_CFG_PARAM_MAX];
setup_mmu_cfg((uint64_t *)&mmu_cfg_params, 0, xlat_ctx->base_table,
xlat_ctx->pa_max_address, xlat_ctx->va_max_address,
EL1_EL0_REGIME);
write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_MAIR_EL1,
mmu_cfg_params[MMU_CFG_MAIR]);
write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_TCR_EL1,
mmu_cfg_params[MMU_CFG_TCR]);
write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_TTBR0_EL1,
mmu_cfg_params[MMU_CFG_TTBR0]);
/* Setup SCTLR_EL1 */
u_register_t sctlr_el1 = read_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_SCTLR_EL1);
sctlr_el1 |=
/*SCTLR_EL1_RES1 |*/
/* Don't trap DC CVAU, DC CIVAC, DC CVAC, DC CVAP, or IC IVAU */
SCTLR_UCI_BIT |
/* RW regions at xlat regime EL1&0 are forced to be XN. */
SCTLR_WXN_BIT |
/* Don't trap to EL1 execution of WFI or WFE at EL0. */
SCTLR_NTWI_BIT | SCTLR_NTWE_BIT |
/* Don't trap to EL1 accesses to CTR_EL0 from EL0. */
SCTLR_UCT_BIT |
/* Don't trap to EL1 execution of DZ ZVA at EL0. */
SCTLR_DZE_BIT |
/* Enable SP Alignment check for EL0 */
SCTLR_SA0_BIT |
/* Don't change PSTATE.PAN on taking an exception to EL1 */
SCTLR_SPAN_BIT |
/* Allow cacheable data and instr. accesses to normal memory. */
SCTLR_C_BIT | SCTLR_I_BIT |
/* Enable MMU. */
SCTLR_M_BIT
;
sctlr_el1 &= ~(
/* Explicit data accesses at EL0 are little-endian. */
SCTLR_E0E_BIT |
/*
* Alignment fault checking disabled when at EL1 and EL0 as
* the UEFI spec permits unaligned accesses.
*/
SCTLR_A_BIT |
/* Accesses to DAIF from EL0 are trapped to EL1. */
SCTLR_UMA_BIT
);
write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_SCTLR_EL1, sctlr_el1);
/*
* Setup other system registers
* ----------------------------
*/
/* Shim Exception Vector Base Address */
write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_VBAR_EL1,
SPM_SHIM_EXCEPTIONS_PTR);
write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_CNTKCTL_EL1,
EL0PTEN_BIT | EL0VTEN_BIT | EL0PCTEN_BIT | EL0VCTEN_BIT);
/*
* FPEN: Allow the Secure Partition to access FP/SIMD registers.
* Note that SPM will not do any saving/restoring of these registers on
* behalf of the SP. This falls under the SP's responsibility.
* TTA: Enable access to trace registers.
* ZEN (v8.2): Trap SVE instructions and access to SVE registers.
*/
write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_CPACR_EL1,
CPACR_EL1_FPEN(CPACR_EL1_FP_TRAP_NONE));
/*
* Prepare information in buffer shared between EL3 and S-EL0
* ----------------------------------------------------------
*/
void *shared_buf_ptr = (void *) sp_boot_info->sp_shared_buf_base;
/* Copy the boot information into the shared buffer with the SP. */
assert((uintptr_t)shared_buf_ptr + sizeof(spm_mm_boot_info_t)
<= (sp_boot_info->sp_shared_buf_base + sp_boot_info->sp_shared_buf_size));
assert(sp_boot_info->sp_shared_buf_base <=
(UINTPTR_MAX - sp_boot_info->sp_shared_buf_size + 1));
assert(sp_boot_info != NULL);
memcpy((void *) shared_buf_ptr, (const void *) sp_boot_info,
sizeof(spm_mm_boot_info_t));
/* Pointer to the MP information from the platform port. */
spm_mm_mp_info_t *sp_mp_info =
((spm_mm_boot_info_t *) shared_buf_ptr)->mp_info;
assert(sp_mp_info != NULL);
/*
* Point the shared buffer MP information pointer to where the info will
* be populated, just after the boot info.
*/
((spm_mm_boot_info_t *) shared_buf_ptr)->mp_info =
(spm_mm_mp_info_t *) ((uintptr_t)shared_buf_ptr
+ sizeof(spm_mm_boot_info_t));
/*
* Update the shared buffer pointer to where the MP information for the
* payload will be populated
*/
shared_buf_ptr = ((spm_mm_boot_info_t *) shared_buf_ptr)->mp_info;
/*
* Copy the cpu information into the shared buffer area after the boot
* information.
*/
assert(sp_boot_info->num_cpus <= PLATFORM_CORE_COUNT);
assert((uintptr_t)shared_buf_ptr
<= (sp_boot_info->sp_shared_buf_base + sp_boot_info->sp_shared_buf_size -
(sp_boot_info->num_cpus * sizeof(*sp_mp_info))));
memcpy(shared_buf_ptr, (const void *) sp_mp_info,
sp_boot_info->num_cpus * sizeof(*sp_mp_info));
/*
* Calculate the linear indices of cores in boot information for the
* secure partition and flag the primary CPU
*/
sp_mp_info = (spm_mm_mp_info_t *) shared_buf_ptr;
for (unsigned int index = 0; index < sp_boot_info->num_cpus; index++) {
u_register_t mpidr = sp_mp_info[index].mpidr;
sp_mp_info[index].linear_id = plat_core_pos_by_mpidr(mpidr);
if (plat_my_core_pos() == sp_mp_info[index].linear_id)
sp_mp_info[index].flags |= MP_INFO_FLAG_PRIMARY_CPU;
}
}
@@ -0,0 +1,26 @@
/*
* Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SPM_MM_SHIM_PRIVATE_H
#define SPM_MM_SHIM_PRIVATE_H
#include <stdint.h>
#include <lib/utils_def.h>
/* Assembly source */
IMPORT_SYM(uintptr_t, spm_shim_exceptions_ptr, SPM_SHIM_EXCEPTIONS_PTR);
/* Linker symbols */
IMPORT_SYM(uintptr_t, __SPM_SHIM_EXCEPTIONS_START__, SPM_SHIM_EXCEPTIONS_START);
IMPORT_SYM(uintptr_t, __SPM_SHIM_EXCEPTIONS_END__, SPM_SHIM_EXCEPTIONS_END);
/* Definitions */
#define SPM_SHIM_EXCEPTIONS_SIZE \
(SPM_SHIM_EXCEPTIONS_END - SPM_SHIM_EXCEPTIONS_START)
#endif /* SPM_MM_SHIM_PRIVATE_H */
@@ -0,0 +1,159 @@
/*
* Copyright (c) 2018-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <arch_helpers.h>
#include <assert.h>
#include <errno.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <platform_def.h>
#include <plat/common/platform.h>
#include <services/spm_mm_partition.h>
#include <services/spm_mm_svc.h>
#include "spm_mm_private.h"
#include "spm_mm_shim_private.h"
/* Place translation tables by default along with the ones used by BL31. */
#ifndef PLAT_SP_IMAGE_XLAT_SECTION_NAME
#define PLAT_SP_IMAGE_XLAT_SECTION_NAME "xlat_table"
#endif
#ifndef PLAT_SP_IMAGE_BASE_XLAT_SECTION_NAME
#define PLAT_SP_IMAGE_BASE_XLAT_SECTION_NAME ".bss"
#endif
/* Allocate and initialise the translation context for the secure partitions. */
REGISTER_XLAT_CONTEXT2(sp,
PLAT_SP_IMAGE_MMAP_REGIONS,
PLAT_SP_IMAGE_MAX_XLAT_TABLES,
PLAT_VIRT_ADDR_SPACE_SIZE, PLAT_PHY_ADDR_SPACE_SIZE,
EL1_EL0_REGIME, PLAT_SP_IMAGE_XLAT_SECTION_NAME,
PLAT_SP_IMAGE_BASE_XLAT_SECTION_NAME);
/* Lock used for SP_MEMORY_ATTRIBUTES_GET and SP_MEMORY_ATTRIBUTES_SET */
static spinlock_t mem_attr_smc_lock;
/* Get handle of Secure Partition translation context */
xlat_ctx_t *spm_get_sp_xlat_context(void)
{
return &sp_xlat_ctx;
};
/*
* Attributes are encoded using a different format in the SMC interface than in
* the Trusted Firmware, where the mmap_attr_t enum type is used. This function
* converts an attributes value from the SMC format to the mmap_attr_t format by
* setting MT_RW/MT_RO, MT_USER/MT_PRIVILEGED and MT_EXECUTE/MT_EXECUTE_NEVER.
* The other fields are left as 0 because they are ignored by the function
* xlat_change_mem_attributes_ctx().
*/
static unsigned int smc_attr_to_mmap_attr(unsigned int attributes)
{
unsigned int tf_attr = 0U;
unsigned int access = (attributes & MM_SP_MEMORY_ATTRIBUTES_ACCESS_MASK)
>> MM_SP_MEMORY_ATTRIBUTES_ACCESS_SHIFT;
if (access == MM_SP_MEMORY_ATTRIBUTES_ACCESS_RW) {
tf_attr |= MT_RW | MT_USER;
} else if (access == MM_SP_MEMORY_ATTRIBUTES_ACCESS_RO) {
tf_attr |= MT_RO | MT_USER;
} else {
/* Other values are reserved. */
assert(access == MM_SP_MEMORY_ATTRIBUTES_ACCESS_NOACCESS);
/* The only requirement is that there's no access from EL0 */
tf_attr |= MT_RO | MT_PRIVILEGED;
}
if ((attributes & MM_SP_MEMORY_ATTRIBUTES_NON_EXEC) == 0) {
tf_attr |= MT_EXECUTE;
} else {
tf_attr |= MT_EXECUTE_NEVER;
}
return tf_attr;
}
/*
* This function converts attributes from the Trusted Firmware format into the
* SMC interface format.
*/
static unsigned int smc_mmap_to_smc_attr(unsigned int attr)
{
unsigned int smc_attr = 0U;
unsigned int data_access;
if ((attr & MT_USER) == 0) {
/* No access from EL0. */
data_access = MM_SP_MEMORY_ATTRIBUTES_ACCESS_NOACCESS;
} else {
if ((attr & MT_RW) != 0) {
assert(MT_TYPE(attr) != MT_DEVICE);
data_access = MM_SP_MEMORY_ATTRIBUTES_ACCESS_RW;
} else {
data_access = MM_SP_MEMORY_ATTRIBUTES_ACCESS_RO;
}
}
smc_attr |= (data_access & MM_SP_MEMORY_ATTRIBUTES_ACCESS_MASK)
<< MM_SP_MEMORY_ATTRIBUTES_ACCESS_SHIFT;
if ((attr & MT_EXECUTE_NEVER) != 0U) {
smc_attr |= MM_SP_MEMORY_ATTRIBUTES_NON_EXEC;
}
return smc_attr;
}
int32_t spm_memory_attributes_get_smc_handler(sp_context_t *sp_ctx,
uintptr_t base_va)
{
uint32_t attributes;
spin_lock(&mem_attr_smc_lock);
int rc = xlat_get_mem_attributes_ctx(sp_ctx->xlat_ctx_handle,
base_va, &attributes);
spin_unlock(&mem_attr_smc_lock);
/* Convert error codes of xlat_get_mem_attributes_ctx() into SPM. */
assert((rc == 0) || (rc == -EINVAL));
if (rc == 0) {
return (int32_t) smc_mmap_to_smc_attr(attributes);
} else {
return SPM_MM_INVALID_PARAMETER;
}
}
int spm_memory_attributes_set_smc_handler(sp_context_t *sp_ctx,
u_register_t page_address,
u_register_t pages_count,
u_register_t smc_attributes)
{
uintptr_t base_va = (uintptr_t) page_address;
size_t size = (size_t) (pages_count * PAGE_SIZE);
uint32_t attributes = (uint32_t) smc_attributes;
INFO(" Start address : 0x%lx\n", base_va);
INFO(" Number of pages: %i (%zi bytes)\n", (int) pages_count, size);
INFO(" Attributes : 0x%x\n", attributes);
spin_lock(&mem_attr_smc_lock);
int ret = xlat_change_mem_attributes_ctx(sp_ctx->xlat_ctx_handle,
base_va, size,
smc_attr_to_mmap_attr(attributes));
spin_unlock(&mem_attr_smc_lock);
/* Convert error codes of xlat_change_mem_attributes_ctx() into SPM. */
assert((ret == 0) || (ret == -EINVAL));
return (ret == 0) ? SPM_MM_SUCCESS : SPM_MM_INVALID_PARAMETER;
}