feat: initial support for FFA_ABORT ABI to abort partition

This patch adds initial support for the newly introduced FFA_ABORT ABI.
It is invoked by partition, to enter aborted state, when it encounters
a fatal error.

Subsequent patches will add incremental support for handling this ABI by
partition manager.

Change-Id: I601a0bf4efd61f5008285b6712107795c65c0332
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/src/api.c b/src/api.c
index 816bf16..75f12f4 100644
--- a/src/api.c
+++ b/src/api.c
@@ -354,28 +354,39 @@
  */
 struct vcpu *api_terminate_vm(struct vcpu *current)
 {
-	struct ffa_value ret = ffa_error(FFA_ABORTED);
-	struct vcpu_locked current_locked;
 	struct vcpu *next;
-	struct vm_locked vm_locked;
 
-	dlog_notice("Terminating VM %#x vCPU %u\n", current->vm->id,
-		    vcpu_index(current));
-
-	atomic_store_explicit(&current->vm->aborting, true,
-			      memory_order_relaxed);
-
-	vm_locked = vm_lock(current->vm);
-	ffa_vm_free_resources(vm_locked);
-	vm_unlock(&vm_locked);
-
-	current_locked = vcpu_lock(current);
-	next = api_switch_to_primary(current_locked, ret, VCPU_STATE_ABORTED);
-	vcpu_unlock(&current_locked);
+	ffa_partition_abort(current, &next);
 
 	return next;
 }
 
+/**
+ * An execution context of a partition invokes FFA_ABORT ABI upon encountering
+ * a fatal error and enters ABORTED state. SPMC then takes necessary steps
+ * based on the abort action specified by the partition through its manifest.
+ */
+struct ffa_value api_ffa_abort(struct vcpu *current, struct vcpu **next,
+			       struct ffa_value *args)
+{
+	assert(args != NULL);
+
+	if (ffa_is_vm_id(current->vm->id)) {
+		dlog_error("FFA_ABORT ABI not supported in NWd.\n");
+		return ffa_error(FFA_NOT_SUPPORTED);
+	}
+
+	if (args->arg1 != 0U || args->arg3 != 0U || args->arg4 != 0U ||
+	    args->arg5 != 0U || args->arg6 != 0U || args->arg7 != 0U) {
+		dlog_error(
+			"Parameters passed through registers X1 and X3-X7 "
+			"must be zero\n");
+		return ffa_error(FFA_INVALID_PARAMETERS);
+	}
+
+	return ffa_partition_abort(current, next);
+}
+
 /*
  * Format the partition info descriptors according to the version supported
  * by the endpoint and return the size of the array created.
@@ -2555,10 +2566,6 @@
 		}
 		return api_ffa_feature_success(0);
 
-	/*
-	 * This function is restricted to the secure virtual FF-A instance (i.e.
-	 * only report success to SPs).
-	 */
 	case FFA_YIELD_32:
 		if (!vm_id_is_current_world(current->vm->id)) {
 			dlog_verbose(
@@ -2615,6 +2622,21 @@
 			FFA_FEATURES_MEM_RETRIEVE_REQ_HYPERVISOR_SUPPORT);
 	}
 
+	/*
+	 * This function is restricted to the secure virtual FF-A instance (i.e.
+	 * only report success to SPs).
+	 */
+	case FFA_ABORT_32:
+	case FFA_ABORT_64:
+		if (ffa_is_vm_id(current->vm->id)) {
+			dlog_verbose(
+				"FFA_FEATURES: %s is only supported at secure "
+				"virtual FF-A instance\n",
+				ffa_func_name(FFA_YIELD_32));
+			return ffa_error(FFA_NOT_SUPPORTED);
+		}
+		return api_ffa_feature_success(0);
+
 	default:
 		return ffa_error(FFA_NOT_SUPPORTED);
 	}
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index 56ca9e0..c0989cf 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -585,7 +585,10 @@
 	case FFA_ERROR_32:
 		*args = ffa_cpu_cycles_error_32(current, next, args->arg2);
 		return true;
-
+	case FFA_ABORT_32:
+	case FFA_ABORT_64:
+		*args = api_ffa_abort(current, next, args);
+		return true;
 	default:
 		return false;
 	}
@@ -1110,6 +1113,48 @@
 	return r;
 }
 
+/**
+ * Aborts execution of a partition when a fatal error occurs on a vCPU and
+ * triggers the SPMC to reclaim all resources allocated to it.
+ */
+struct ffa_value ffa_partition_abort(struct vcpu *current, struct vcpu **next)
+{
+	struct vm_locked vm_locked;
+	struct ffa_value ret;
+	struct vcpu_locked current_locked;
+
+	dlog_notice("Aborting VM %#x vCPU %u\n", current->vm->id,
+		    vcpu_index(current));
+
+	vm_locked = vm_lock(current->vm);
+
+	atomic_store_explicit(&current->vm->aborting, true,
+			      memory_order_relaxed);
+	current_locked = vcpu_lock(current);
+
+	/*
+	 * A partition exection is in ABORTED state after it encounters a fatal
+	 * error.
+	 */
+	current->state = VCPU_STATE_ABORTED;
+
+	/*
+	 * SPMC de-allocates and/or uninitializes all the resources allocated
+	 * to the partition.
+	 */
+	ffa_vm_free_resources(vm_locked);
+	vm_unlock(&vm_locked);
+
+	/*
+	 * SPMC performs necessary operations based on the abort action for
+	 * SP.
+	 */
+	ret = ffa_cpu_cycles_abort(current_locked, next);
+	vcpu_unlock(&current_locked);
+
+	return ret;
+}
+
 struct vcpu *sync_lower_exception(uintreg_t esr, uintreg_t far)
 {
 	struct vcpu *vcpu = current();
diff --git a/src/ffa/hypervisor/cpu_cycles.c b/src/ffa/hypervisor/cpu_cycles.c
index cf9f4bb..5ec1a45 100644
--- a/src/ffa/hypervisor/cpu_cycles.c
+++ b/src/ffa/hypervisor/cpu_cycles.c
@@ -10,6 +10,7 @@
 
 #include "hf/api.h"
 #include "hf/ffa/indirect_messaging.h"
+#include "hf/ffa/vm.h"
 #include "hf/ffa_internal.h"
 #include "hf/vcpu.h"
 
@@ -137,3 +138,14 @@
 	/* TODO: Interface not handled in hypervisor. */
 	return ffa_error(FFA_NOT_SUPPORTED);
 }
+
+struct ffa_value ffa_cpu_cycles_abort(struct vcpu_locked *current_locked,
+				      struct vcpu **next)
+{
+	struct ffa_value to_ret = ffa_error(FFA_ABORTED);
+
+	*next = api_switch_to_primary(*current_locked, to_ret,
+				      VCPU_STATE_ABORTED);
+
+	return (struct ffa_value){.func = FFA_SUCCESS_32};
+}
diff --git a/src/ffa/spmc/cpu_cycles.c b/src/ffa/spmc/cpu_cycles.c
index 0b5809b..4516742 100644
--- a/src/ffa/spmc/cpu_cycles.c
+++ b/src/ffa/spmc/cpu_cycles.c
@@ -833,3 +833,18 @@
 	vm_unlock(&vm_locked);
 	return ret;
 }
+
+struct ffa_value ffa_cpu_cycles_abort(struct vcpu_locked current_locked,
+					     struct vcpu **next)
+{
+	struct ffa_value to_ret = ffa_error(FFA_ABORTED);
+	enum vcpu_state next_state = VCPU_STATE_ABORTED;
+
+	/*
+	 * Relinquish control back to the NWd.
+	 * TODO: Support for abort actions will be added in further patches.
+	 */
+	*next = api_switch_to_primary(current_locked, to_ret, next_state);
+
+	return (struct ffa_value){.func = FFA_SUCCESS_32};
+}