feat(spmc): enable handling of the NS bit

In FF-A v1.1 the NS bit is used by the SPMC to specify the
security state of a memory region retrieved by a SP.

Enable the SPMC to set the bit for v1.1 callers or v1.0
callers that explicitly request the usage via FFA_FEATURES.

In this implementation the sender of the memory region must
reside in the normal world and the SPMC does not support
changing the security state of memory regions therefore
always set the NS bit if required by the caller.

Signed-off-by: Marc Bonnici <marc.bonnici@arm.com>
Change-Id: I215756b28e2382082933ba1dcc7584e7faf4b36b
diff --git a/include/services/el3_spmc_ffa_memory.h b/include/services/el3_spmc_ffa_memory.h
index 8e0c5ae..2037eca 100644
--- a/include/services/el3_spmc_ffa_memory.h
+++ b/include/services/el3_spmc_ffa_memory.h
@@ -63,6 +63,8 @@
  * typedef ffa_mem_attr8_t - Memory region attributes v1.0.
  * typedef ffa_mem_attr16_t - Memory region attributes v1.1.
  *
+ * * @FFA_MEM_ATTR_NS_BIT:
+ *     Memory security state.
  * * @FFA_MEM_ATTR_DEVICE_NGNRNE:
  *     Device-nGnRnE.
  * * @FFA_MEM_ATTR_DEVICE_NGNRE:
@@ -84,6 +86,7 @@
  */
 typedef uint8_t ffa_mem_attr8_t;
 typedef uint16_t ffa_mem_attr16_t;
+#define FFA_MEM_ATTR_NS_BIT			(0x1U << 6)
 #define FFA_MEM_ATTR_DEVICE_NGNRNE		((1U << 4) | (0x0U << 2))
 #define FFA_MEM_ATTR_DEVICE_NGNRE		((1U << 4) | (0x1U << 2))
 #define FFA_MEM_ATTR_DEVICE_NGRE		((1U << 4) | (0x2U << 2))
diff --git a/services/std_svc/spm/el3_spmc/spmc.h b/services/std_svc/spm/el3_spmc/spmc.h
index 9926c68..d62be91 100644
--- a/services/std_svc/spm/el3_spmc/spmc.h
+++ b/services/std_svc/spm/el3_spmc/spmc.h
@@ -37,6 +37,7 @@
 #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)
@@ -170,6 +171,12 @@
 	 * 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;
 };
 
 /*
diff --git a/services/std_svc/spm/el3_spmc/spmc_main.c b/services/std_svc/spm/el3_spmc/spmc_main.c
index c7071ab..9b8621a 100644
--- a/services/std_svc/spm/el3_spmc/spmc_main.c
+++ b/services/std_svc/spm/el3_spmc/spmc_main.c
@@ -990,6 +990,53 @@
 	return spmc_ffa_error_return(handle, ret);
 }
 
+static uint64_t ffa_feature_success(void *handle, uint32_t arg2)
+{
+	SMC_RET3(handle, FFA_SUCCESS_SMC32, 0, arg2);
+}
+
+static uint64_t ffa_features_retrieve_request(bool secure_origin,
+					      uint32_t input_properties,
+					      void *handle)
+{
+	/*
+	 * If we're called by the normal world we don't support any
+	 * additional features.
+	 */
+	if (!secure_origin) {
+		if ((input_properties & FFA_FEATURES_RET_REQ_NS_BIT) != 0U) {
+			return spmc_ffa_error_return(handle,
+						     FFA_ERROR_NOT_SUPPORTED);
+		}
+
+	} else {
+		struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
+		/*
+		 * If v1.1 the NS bit must be set otherwise it is an invalid
+		 * call. If v1.0 check and store whether the SP has requested
+		 * the use of the NS bit.
+		 */
+		if (sp->ffa_version == MAKE_FFA_VERSION(1, 1)) {
+			if ((input_properties &
+			     FFA_FEATURES_RET_REQ_NS_BIT) == 0U) {
+				return spmc_ffa_error_return(handle,
+						       FFA_ERROR_NOT_SUPPORTED);
+			}
+			return ffa_feature_success(handle,
+						   FFA_FEATURES_RET_REQ_NS_BIT);
+		} else {
+			sp->ns_bit_requested = (input_properties &
+					       FFA_FEATURES_RET_REQ_NS_BIT) !=
+					       0U;
+		}
+		if (sp->ns_bit_requested) {
+			return ffa_feature_success(handle,
+						   FFA_FEATURES_RET_REQ_NS_BIT);
+		}
+	}
+	SMC_RET1(handle, FFA_SUCCESS_SMC32);
+}
+
 static uint64_t ffa_features_handler(uint32_t smc_fid,
 				     bool secure_origin,
 				     uint64_t x1,
@@ -1003,21 +1050,34 @@
 	uint32_t function_id = (uint32_t) x1;
 	uint32_t input_properties = (uint32_t) x2;
 
-	/*
-	 * We don't currently support any additional input properties
-	 * for any ABI therefore ensure this value is always set to 0.
-	 */
-	if (input_properties != 0) {
-		return spmc_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED);
-	}
-
 	/* Check if a Feature ID was requested. */
 	if ((function_id & FFA_FEATURES_BIT31_MASK) == 0U) {
 		/* We currently don't support any additional features. */
 		return spmc_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED);
 	}
 
-	/* Report if an FF-A ABI is supported. */
+	/*
+	 * Handle the cases where we have separate handlers due to additional
+	 * properties.
+	 */
+	switch (function_id) {
+	case FFA_MEM_RETRIEVE_REQ_SMC32:
+	case FFA_MEM_RETRIEVE_REQ_SMC64:
+		return ffa_features_retrieve_request(secure_origin,
+						     input_properties,
+						     handle);
+	}
+
+	/*
+	 * We don't currently support additional input properties for these
+	 * other ABIs therefore ensure this value is set to 0.
+	 */
+	if (input_properties != 0U) {
+		return spmc_ffa_error_return(handle,
+					     FFA_ERROR_NOT_SUPPORTED);
+	}
+
+	/* Report if any other FF-A ABI is supported. */
 	switch (function_id) {
 	/* Supported features from both worlds. */
 	case FFA_ERROR:
@@ -1052,8 +1112,6 @@
 	case FFA_SECONDARY_EP_REGISTER_SMC64:
 	case FFA_MSG_SEND_DIRECT_RESP_SMC32:
 	case FFA_MSG_SEND_DIRECT_RESP_SMC64:
-	case FFA_MEM_RETRIEVE_REQ_SMC32:
-	case FFA_MEM_RETRIEVE_REQ_SMC64:
 	case FFA_MEM_RELINQUISH:
 	case FFA_MSG_WAIT:
 
diff --git a/services/std_svc/spm/el3_spmc/spmc_shared_mem.c b/services/std_svc/spm/el3_spmc/spmc_shared_mem.c
index 920dfca..7b9a526 100644
--- a/services/std_svc/spm/el3_spmc/spmc_shared_mem.c
+++ b/services/std_svc/spm/el3_spmc/spmc_shared_mem.c
@@ -876,6 +876,13 @@
 		goto err_arg;
 	}
 
+	/* Ensure the NS bit is set to 0. */
+	if ((obj->desc.memory_region_attributes & FFA_MEM_ATTR_NS_BIT) != 0U) {
+		WARN("%s: NS mem attributes flags MBZ.\n", __func__);
+		ret = FFA_ERROR_INVALID_PARAMETER;
+		goto err_arg;
+	}
+
 	/*
 	 * We don't currently support any optional flags so ensure none are
 	 * requested.
@@ -1190,6 +1197,33 @@
 }
 
 /**
+ * spmc_ffa_mem_retrieve_set_ns_bit - Set the NS bit in the response descriptor
+ *				      if the caller implements a version greater
+ *				      than FF-A 1.0 or if they have requested
+ *				      the functionality.
+ *				      TODO: We are assuming that the caller is
+ *				      an SP. To support retrieval from the
+ *				      normal world this function will need to be
+ *				      expanded accordingly.
+ * @resp:       Descriptor populated in callers RX buffer.
+ * @sp_ctx:     Context of the calling SP.
+ */
+void spmc_ffa_mem_retrieve_set_ns_bit(struct ffa_mtd *resp,
+			 struct secure_partition_desc *sp_ctx)
+{
+	if (sp_ctx->ffa_version > MAKE_FFA_VERSION(1, 0) ||
+	    sp_ctx->ns_bit_requested) {
+		/*
+		 * Currently memory senders must reside in the normal
+		 * world, and we do not have the functionlaity to change
+		 * the state of memory dynamically. Therefore we can always set
+		 * the NS bit to 1.
+		 */
+		resp->memory_region_attributes |= FFA_MEM_ATTR_NS_BIT;
+	}
+}
+
+/**
  * spmc_ffa_mem_retrieve_req - FFA_MEM_RETRIEVE_REQ implementation.
  * @smc_fid:            FID of SMC
  * @total_length:       Total length of retrieve request descriptor if this is
@@ -1237,6 +1271,7 @@
 	struct spmc_shmem_obj *obj = NULL;
 	struct mailbox *mbox = spmc_get_mbox_desc(secure_origin);
 	uint32_t ffa_version = get_partition_ffa_version(secure_origin);
+	struct secure_partition_desc *sp_ctx = spmc_get_current_sp_ctx();
 
 	if (!secure_origin) {
 		WARN("%s: unsupported retrieve req direction.\n", __func__);
@@ -1330,6 +1365,13 @@
 		goto err_unlock_all;
 	}
 
+	/* Ensure the NS bit is set to 0 in the request. */
+	if ((req->memory_region_attributes & FFA_MEM_ATTR_NS_BIT) != 0U) {
+		WARN("%s: NS mem attributes flags MBZ.\n", __func__);
+		ret = FFA_ERROR_INVALID_PARAMETER;
+		goto err_unlock_all;
+	}
+
 	if (req->flags != 0U) {
 		if ((req->flags & FFA_MTD_FLAG_TYPE_MASK) !=
 		    (obj->desc.flags & FFA_MTD_FLAG_TYPE_MASK)) {
@@ -1446,6 +1488,9 @@
 		memcpy(resp, &obj->desc, copy_size);
 	}
 
+	/* Set the NS bit in the response if applicable. */
+	spmc_ffa_mem_retrieve_set_ns_bit(resp, sp_ctx);
+
 	spin_unlock(&spmc_shmem_obj_state.lock);
 	spin_unlock(&mbox->lock);