SPM: Add support for FFA_MEM_PERM_GET/SET
Handle FFA_MEM_PERM_GET and FFA_MEM_PERM_SET interfaces for enabling
SPs to query and set the access rights of their memory regions. These
interfaces are only permitted in the initialization phase thus a new
state variable is being introduced in sp_session. SPs indicate the end
of their initialization phase through the FFA_MSG_WAIT interface.
Signed-off-by: Imre Kis <imre.kis@arm.com>
Change-Id: I7ffeaca978ed8ba3586c85d46f7f49aef9c300e5
diff --git a/core/arch/arm/include/ffa.h b/core/arch/arm/include/ffa.h
index 3bd59b9..c6ffc3b 100644
--- a/core/arch/arm/include/ffa.h
+++ b/core/arch/arm/include/ffa.h
@@ -69,6 +69,10 @@
#define FFA_MEM_FRAG_RX U(0x8400007A)
#define FFA_MEM_FRAG_TX U(0x8400007B)
#define FFA_SECONDARY_EP_REGISTER_64 U(0xC4000087)
+#define FFA_MEM_PERM_GET_32 U(0x84000088)
+#define FFA_MEM_PERM_GET_64 U(0xC4000088)
+#define FFA_MEM_PERM_SET_32 U(0x84000089)
+#define FFA_MEM_PERM_SET_64 U(0xC4000089)
/* Special value for traffic targeted to the Hypervisor or SPM */
#define FFA_TARGET_INFO_MBZ U(0x0)
diff --git a/core/arch/arm/include/ffa_sp.h b/core/arch/arm/include/ffa_sp.h
index 6d1fb9e..e93d035 100644
--- a/core/arch/arm/include/ffa_sp.h
+++ b/core/arch/arm/include/ffa_sp.h
@@ -9,12 +9,23 @@
#include <kernel/thread.h>
#include <mm/tee_mmu.h>
#include <stdint.h>
+#include <util.h>
/* The minimum id we can assign to a SP */
#define FFA_DST(x) ((x) & 0xffff)
#define FFA_SRC(x) (((x) >> 16) & 0xffff)
#define FFA_NW_ID 0
+#define FFA_MEM_PERM_DATA_PERM GENMASK_32(1, 0)
+#define FFA_MEM_PERM_RW U(0x1)
+#define FFA_MEM_PERM_RO U(0x3)
+
+#define FFA_MEM_PERM_INSTRUCTION_PERM BIT(2)
+#define FFA_MEM_PERM_NX BIT(2)
+#define FFA_MEM_PERM_X U(0)
+
+#define FFA_MEM_PERM_RESERVED GENMASK_32(31, 3)
+
struct ffa_partition_info {
uint16_t id;
uint16_t execution_context_cnt;
diff --git a/core/arch/arm/include/kernel/sp.h b/core/arch/arm/include/kernel/sp.h
index 48cc5dd..b8325dc 100644
--- a/core/arch/arm/include/kernel/sp.h
+++ b/core/arch/arm/include/kernel/sp.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
- * Copyright (c) 2020-2022, Arm Limited
+ * Copyright (c) 2020-2022, Arm Limited. All rights reserved.
*/
#ifndef __KERNEL_SP_H
@@ -64,6 +64,7 @@
TAILQ_ENTRY(sp_session) link_tsd;
struct tee_ta_session *s;
enum sp_status state;
+ bool is_initialized;
struct thread_ctx_regs sp_regs;
uint16_t response_id;
vaddr_t info;
diff --git a/core/arch/arm/kernel/ffa_sp.c b/core/arch/arm/kernel/ffa_sp.c
index bf3d52f..f45b83d 100644
--- a/core/arch/arm/kernel/ffa_sp.c
+++ b/core/arch/arm/kernel/ffa_sp.c
@@ -43,6 +43,16 @@
void ffa_wait(struct thread_smc_args *args, uint16_t caller_id)
{
+ struct sp_session *s = NULL;
+
+ /*
+ * The first FFA_MSG_WAIT call indicates the completion of the
+ * initialization phase.
+ */
+ s = sp_get_session(caller_id);
+ if (s)
+ s->is_initialized = true;
+
ffa_send_message(args, FFA_NW_ID, caller_id);
}
diff --git a/core/arch/arm/kernel/sp.c b/core/arch/arm/kernel/sp.c
index 8b84e35..8fbbdfe 100644
--- a/core/arch/arm/kernel/sp.c
+++ b/core/arch/arm/kernel/sp.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: BSD-2-Clause
/*
- * Copyright (c) 2020-2022, Arm Limited
+ * Copyright (c) 2020-2022, Arm Limited. All rights reserved.
*/
#include <assert.h>
@@ -1079,6 +1079,7 @@
/* Map the session to the SP, and add it to the loaded SP's list*/
sp_s->s = s;
sp_s->state = sp_busy;
+ sp_s->is_initialized = false;
sp_s->response_id = UNDEFINED_SP;
TAILQ_INSERT_TAIL(&tee_open_sp_sessions, sp_s, link);
diff --git a/core/arch/arm/kernel/thread_spmc.c b/core/arch/arm/kernel/thread_spmc.c
index 6585e3a..11fd3c1 100644
--- a/core/arch/arm/kernel/thread_spmc.c
+++ b/core/arch/arm/kernel/thread_spmc.c
@@ -14,6 +14,7 @@
#include <kernel/tee_misc.h>
#include <kernel/thread.h>
#include <kernel/sp.h>
+#include <kernel/user_mode_ctx.h>
#include <mm/core_mmu.h>
#include <mm/mobj.h>
#include <mm/tee_mmu.h>
@@ -1047,6 +1048,126 @@
set_args(args, ret_fid, ret_val, 0, 0, 0, 0, caller_id);
}
+#ifdef CFG_WITH_SP
+static void handle_mem_perm_get(struct thread_smc_args *args,
+ uint16_t caller_id)
+{
+ struct sp_session *sp_s = NULL;
+ struct user_mode_ctx *uctx = NULL;
+ TEE_Result res = TEE_ERROR_BAD_PARAMETERS;
+ uint16_t attrs = 0;
+ uint32_t ret_fid = FFA_ERROR;
+ uint32_t ret_val = FFA_INVALID_PARAMETERS;
+
+ sp_s = sp_get_session(caller_id);
+ if (!sp_s)
+ goto out;
+
+ /*
+ * The FFA_MEM_PERM_GET interface is only allowed during initialization
+ */
+ if (sp_s->is_initialized) {
+ ret_val = FFA_DENIED;
+ goto out;
+ }
+
+ uctx = to_user_mode_ctx(sp_s->s->ctx);
+ if (!uctx)
+ goto out;
+
+ /* Query memory attributes */
+ tee_ta_push_current_session(sp_s->s);
+ res = vm_get_prot(uctx, args->a1, SMALL_PAGE_SIZE, &attrs);
+ tee_ta_pop_current_session();
+ if (res)
+ goto out;
+
+ /* Build response value */
+ ret_fid = FFA_SUCCESS_32;
+ ret_val = 0;
+ if ((attrs & TEE_MATTR_URW) == TEE_MATTR_URW)
+ ret_val |= FFA_MEM_PERM_RW;
+ else if (attrs & TEE_MATTR_UR)
+ ret_val |= FFA_MEM_PERM_RO;
+
+ if ((attrs & TEE_MATTR_UX) == 0)
+ ret_val |= FFA_MEM_PERM_NX;
+
+out:
+ set_args(args, ret_fid, FFA_PARAM_MBZ, ret_val, FFA_PARAM_MBZ,
+ FFA_PARAM_MBZ, FFA_PARAM_MBZ, caller_id);
+}
+
+static void handle_mem_perm_set(struct thread_smc_args *args,
+ uint16_t caller_id)
+{
+ struct sp_session *sp_s = NULL;
+ struct user_mode_ctx *uctx = NULL;
+ TEE_Result res = TEE_ERROR_BAD_PARAMETERS;
+ size_t region_size = 0;
+ uint32_t data_perm = 0;
+ uint32_t instruction_perm = 0;
+ uint16_t attrs = 0;
+ uint32_t ret_fid = FFA_ERROR;
+ uint32_t ret_val = FFA_INVALID_PARAMETERS;
+
+ sp_s = sp_get_session(caller_id);
+ if (!sp_s)
+ goto out;
+
+ /*
+ * The FFA_MEM_PERM_GET interface is only allowed during initialization
+ */
+ if (sp_s->is_initialized) {
+ ret_val = FFA_DENIED;
+ goto out;
+ }
+
+ uctx = to_user_mode_ctx(sp_s->s->ctx);
+ if (!uctx)
+ goto out;
+
+ if (MUL_OVERFLOW(args->a2, SMALL_PAGE_SIZE, ®ion_size))
+ goto out;
+
+ if (args->a3 & FFA_MEM_PERM_RESERVED)
+ /* Non-zero reserved bits */
+ goto out;
+
+ data_perm = (args->a3 & FFA_MEM_PERM_DATA_PERM);
+ instruction_perm = (args->a3 & FFA_MEM_PERM_INSTRUCTION_PERM);
+
+ /* RWX access right configuration is not permitted */
+ if (data_perm == FFA_MEM_PERM_RW && instruction_perm == FFA_MEM_PERM_X)
+ goto out;
+
+ if (data_perm == FFA_MEM_PERM_RO)
+ attrs = TEE_MATTR_UR;
+ else if (data_perm == FFA_MEM_PERM_RW)
+ attrs = TEE_MATTR_URW;
+ else
+ /* Invalid permission value */
+ goto out;
+
+ if (instruction_perm == FFA_MEM_PERM_X)
+ attrs |= TEE_MATTR_UX;
+
+ /* Set access rights */
+ tee_ta_push_current_session(sp_s->s);
+ res = vm_set_prot(uctx, args->a1, region_size, attrs);
+ tee_ta_pop_current_session();
+ if (res != TEE_SUCCESS)
+ goto out;
+
+ ret_fid = FFA_SUCCESS_32;
+ ret_val = FFA_PARAM_MBZ;
+
+out:
+ set_args(args, ret_fid, FFA_PARAM_MBZ, ret_val, FFA_PARAM_MBZ,
+ FFA_PARAM_MBZ, FFA_PARAM_MBZ, caller_id);
+}
+#endif /* CFG_WITH_SP */
+
/* Only called from assembly */
void thread_spmc_msg_recv(struct thread_smc_args *args, uint64_t endpoint_id)
{
@@ -1156,6 +1277,21 @@
case FFA_MEM_FRAG_TX:
handle_mem_frag_tx(args, caller_id);
break;
+#ifdef CFG_WITH_SP
+#ifdef ARM64
+ case FFA_MEM_PERM_GET_64:
+#endif
+ case FFA_MEM_PERM_GET_32:
+ handle_mem_perm_get(args, caller_id);
+ break;
+
+#ifdef ARM64
+ case FFA_MEM_PERM_SET_64:
+#endif
+ case FFA_MEM_PERM_SET_32:
+ handle_mem_perm_set(args, caller_id);
+ break;
+#endif
default:
EMSG("Unhandled FFA function ID %#"PRIx32,
(uint32_t)args->a0);