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, &region_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);