feat: forward VM availability messages from SPMC to SP

* Add `plat_ffa_handle_framework_msg` and use it to check framework
  messages are valid on SPMC.
* Modify `api_ffa_dir_msg_value` to preserve the value of `args.arg2` if
  `args` is a framework message.

Change-Id: I9c66d38b9b3a4fdeb39381289418188350d7d410
Signed-off-by: Karl Meakin <karl.meakin@arm.com>
diff --git a/src/api.c b/src/api.c
index 0803a3d..4728f31 100644
--- a/src/api.c
+++ b/src/api.c
@@ -2727,7 +2727,7 @@
 	return (struct ffa_value){
 		.func = (uint32_t)args.func,
 		.arg1 = (uint32_t)args.arg1,
-		.arg2 = (uint32_t)0,
+		.arg2 = (uint32_t)args.arg2,
 		.arg3 = (uint32_t)args.arg3,
 		.arg4 = (uint32_t)args.arg4,
 		.arg5 = (uint32_t)args.arg5,
@@ -2738,27 +2738,38 @@
 
 /**
  * Helper to copy direct message payload, depending on SMC used, direct
- * messaging interface used, and expected registers size.
+ * messaging interface used, and expected registers size. Can be used for both
+ * framework messages and standard messages.
  */
 static struct ffa_value api_ffa_dir_msg_value(struct ffa_value args)
 {
-	if (args.func == FFA_MSG_SEND_DIRECT_REQ_32 ||
-	    args.func == FFA_MSG_SEND_DIRECT_RESP_32) {
-		return api_ffa_value_copy32(args);
-	}
+	switch (args.func) {
+	case FFA_MSG_SEND_DIRECT_REQ_32:
+	case FFA_MSG_SEND_DIRECT_RESP_32:
+		args = api_ffa_value_copy32(args);
+		if (!ffa_is_framework_msg(args)) {
+			args.arg2 = 0;
+		}
+		break;
 
-	if (args.func == FFA_MSG_SEND_DIRECT_REQ_64 ||
-	    args.func == FFA_MSG_SEND_DIRECT_RESP_64) {
-		args.arg2 = 0;
-	}
+	case FFA_MSG_SEND_DIRECT_REQ_64:
+	case FFA_MSG_SEND_DIRECT_RESP_64:
+		if (!ffa_is_framework_msg(args)) {
+			args.arg2 = 0;
+		}
+		break;
 
-	if (args.func == FFA_MSG_SEND_DIRECT_REQ2_64) {
+	case FFA_MSG_SEND_DIRECT_REQ2_64:
 		args.extended_val.valid = true;
-	}
+		break;
 
-	if (args.func == FFA_MSG_SEND_DIRECT_RESP2_64) {
+	case FFA_MSG_SEND_DIRECT_RESP2_64:
 		args.arg2 = 0;
 		args.arg3 = 0;
+		break;
+	default:
+		panic("Invalid direct message function %#x\n", args.func);
+		break;
 	}
 
 	return args;
@@ -2827,10 +2838,18 @@
 
 	if ((args.func == FFA_MSG_SEND_DIRECT_REQ_32 ||
 	     args.func == FFA_MSG_SEND_DIRECT_REQ_64) &&
+	    !ffa_is_framework_msg(args) &&
 	    !api_ffa_dir_msg_is_arg2_zero(args)) {
+		dlog_verbose("Direct messaging: w2 must be zero (w2 = %#lx)\n",
+			     args.arg2);
 		return ffa_error(FFA_INVALID_PARAMETERS);
 	}
 
+	if (ffa_is_framework_msg(args) &&
+	    plat_ffa_handle_framework_msg(args, &ret)) {
+		return ret;
+	}
+
 	if (!plat_ffa_is_direct_request_valid(current, sender_vm_id,
 					      receiver_vm_id)) {
 		dlog_verbose("Invalid direct message request.\n");
@@ -2838,6 +2857,7 @@
 	}
 
 	if (plat_ffa_direct_request_forward(receiver_vm_id, args, &ret)) {
+		dlog_verbose("Direct message request forwarded\n");
 		return ret;
 	}
 
@@ -2861,6 +2881,7 @@
 	 */
 	if (!plat_ffa_is_direct_request_supported(current->vm, receiver_vm,
 						  args.func)) {
+		dlog_verbose("Direct message request not supported\n");
 		return ffa_error(FFA_DENIED);
 	}
 
@@ -2930,8 +2951,6 @@
 	case VCPU_STATE_BLOCKED_INTERRUPT:
 	case VCPU_STATE_BLOCKED:
 	case VCPU_STATE_PREEMPTED:
-		dlog_verbose("Receiver's vCPU can't receive request (%u)!\n",
-			     vcpu_index(receiver_vcpu));
 		ret = ffa_error(FFA_BUSY);
 		goto out;
 	case VCPU_STATE_WAITING:
@@ -2958,6 +2977,8 @@
 	receiver_vcpu->direct_request_origin.is_ffa_req2 =
 		(args.func == FFA_MSG_SEND_DIRECT_REQ2_64);
 	receiver_vcpu->direct_request_origin.vm_id = sender_vm_id;
+	receiver_vcpu->direct_request_origin.is_framework =
+		ffa_is_framework_msg(args);
 
 	arch_regs_set_retval(&receiver_vcpu->regs, api_ffa_dir_msg_value(args));
 
@@ -3057,6 +3078,8 @@
 	struct ffa_value to_ret = api_ffa_dir_msg_value(args);
 	struct two_vcpu_locked vcpus_locked;
 	bool received_req2;
+	bool req_framework;
+	bool resp_framework;
 
 	/*
 	 * If using FFA_MSG_SEND_DIRECT_RESP, the caller's
@@ -3064,7 +3087,10 @@
 	 *  - x8-x17 SBZ if caller's FF-A version >= FF-A v1.2
 	 */
 	if (args.func != FFA_MSG_SEND_DIRECT_RESP2_64) {
-		if (!api_ffa_dir_msg_is_arg2_zero(args)) {
+		if (!ffa_is_framework_msg(args) &&
+		    !api_ffa_dir_msg_is_arg2_zero(args)) {
+			dlog_verbose("%s: w2 Must Be Zero",
+				     ffa_func_name(args.func));
 			return ffa_error(FFA_INVALID_PARAMETERS);
 		}
 
@@ -3105,6 +3131,19 @@
 		goto out;
 	}
 
+	req_framework = current_locked.vcpu->direct_request_origin.is_framework;
+	resp_framework = ffa_is_framework_msg(args);
+
+	if (req_framework && !resp_framework) {
+		ret = ffa_error(FFA_INVALID_PARAMETERS);
+		dlog_verbose(
+			"Mismatch in framework message bit: request was a %s "
+			"message, but response is a %s message\n",
+			req_framework ? "framework" : "non-framework",
+			resp_framework ? "framework" : "non-framework");
+		goto out;
+	}
+
 	received_req2 = current->direct_request_origin.is_ffa_req2;
 
 	if (args.func != FFA_MSG_SEND_DIRECT_RESP2_64 && received_req2) {