feat: allow an endpoint to specify optional timeout with FFA_YIELD

An endpoint can yield execution back to the VM that allocated it CPU
cycles rather than busy waiting (such as for an IO operation to
 complete). FF-A v1.2 spec allows the endpoint to specify an optional
64-bit timeout.

Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
Change-Id: I5481c5483f4edd71b95c15a11a118498c71e3fa1
diff --git a/src/api.c b/src/api.c
index 073e011..89c0332 100644
--- a/src/api.c
+++ b/src/api.c
@@ -274,12 +274,27 @@
  * control back to the execution context of the endpoint that originally
  * allocated cycles to it.
  */
-struct ffa_value api_yield(struct vcpu *current, struct vcpu **next)
+struct ffa_value api_yield(struct vcpu *current, struct vcpu **next,
+			   struct ffa_value *args)
 {
 	struct ffa_value ret = (struct ffa_value){.func = FFA_SUCCESS_32};
 	struct vcpu_locked current_locked;
 	bool transition_allowed;
 	enum vcpu_state next_state = VCPU_STATE_BLOCKED;
+	uint32_t timeout_low = 0;
+	uint32_t timeout_high = 0;
+
+	if (args != NULL) {
+		if (args->arg4 != 0U || args->arg5 != 0U || args->arg6 != 0U ||
+		    args->arg7 != 0U) {
+			dlog_error(
+				"Parameters passed through registers X4-X7 "
+				"must be zero\n");
+			return ffa_error(FFA_INVALID_PARAMETERS);
+		}
+		timeout_low = (uint32_t)args->arg2 & 0xFFFFFFFF;
+		timeout_high = (uint32_t)args->arg3 & 0xFFFFFFFF;
+	}
 
 	if (current->vm->id == HF_PRIMARY_VM_ID) {
 		/* NOOP on the primary as it makes the scheduling decisions. */
@@ -298,7 +313,7 @@
 
 	assert(next_state == VCPU_STATE_BLOCKED);
 
-	return plat_ffa_yield_prepare(current, next);
+	return plat_ffa_yield_prepare(current, next, timeout_low, timeout_high);
 }
 
 /**
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index cdc9697..1ee1f3d 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -576,7 +576,7 @@
 		*args = api_ffa_rx_acquire(ffa_receiver(*args), current);
 		return true;
 	case FFA_YIELD_32:
-		*args = api_yield(current, next);
+		*args = api_yield(current, next, args);
 		return true;
 	case FFA_MSG_SEND_32:
 		*args = plat_ffa_msg_send(
@@ -1236,7 +1236,7 @@
 		 * interrupts into EL0 partitions.
 		 */
 		if (is_el0_partition) {
-			api_yield(vcpu, &new_vcpu);
+			api_yield(vcpu, &new_vcpu, NULL);
 			return new_vcpu;
 		}
 
@@ -1247,7 +1247,7 @@
 			 * TODO: consider giving the scheduler more context,
 			 * somehow.
 			 */
-			api_yield(vcpu, &new_vcpu);
+			api_yield(vcpu, &new_vcpu, NULL);
 			return new_vcpu;
 		}
 		/* WFI */
diff --git a/src/arch/aarch64/plat/ffa/absent.c b/src/arch/aarch64/plat/ffa/absent.c
index 91b8b5a..212e33c 100644
--- a/src/arch/aarch64/plat/ffa/absent.c
+++ b/src/arch/aarch64/plat/ffa/absent.c
@@ -597,10 +597,14 @@
 }
 
 struct ffa_value plat_ffa_yield_prepare(struct vcpu *current,
-					struct vcpu **next)
+					struct vcpu **next,
+					uint32_t timeout_low,
+					uint32_t timeout_high)
 {
 	(void)current;
 	(void)next;
+	(void)timeout_low;
+	(void)timeout_high;
 
 	return ffa_error(FFA_NOT_SUPPORTED);
 }
diff --git a/src/arch/aarch64/plat/ffa/hypervisor.c b/src/arch/aarch64/plat/ffa/hypervisor.c
index 575acf2..0c4a39b 100644
--- a/src/arch/aarch64/plat/ffa/hypervisor.c
+++ b/src/arch/aarch64/plat/ffa/hypervisor.c
@@ -2269,11 +2269,15 @@
  * to BLOCKED state.
  */
 struct ffa_value plat_ffa_yield_prepare(struct vcpu *current,
-					struct vcpu **next)
+					struct vcpu **next,
+					uint32_t timeout_low,
+					uint32_t timeout_high)
 {
 	struct ffa_value ret = {
 		.func = FFA_YIELD_32,
 		.arg1 = ffa_vm_vcpu(current->vm->id, vcpu_index(current)),
+		.arg2 = timeout_low,
+		.arg3 = timeout_high,
 	};
 
 	/*
diff --git a/src/arch/aarch64/plat/ffa/spmc.c b/src/arch/aarch64/plat/ffa/spmc.c
index 468772c..3f5fa90 100644
--- a/src/arch/aarch64/plat/ffa/spmc.c
+++ b/src/arch/aarch64/plat/ffa/spmc.c
@@ -2677,12 +2677,16 @@
  * move to BLOCKED state.
  */
 struct ffa_value plat_ffa_yield_prepare(struct vcpu *current,
-					struct vcpu **next)
+					struct vcpu **next,
+					uint32_t timeout_low,
+					uint32_t timeout_high)
 {
 	struct ffa_value ret_args = (struct ffa_value){.func = FFA_SUCCESS_32};
 	struct ffa_value ret = {
 		.func = FFA_YIELD_32,
 		.arg1 = ffa_vm_vcpu(current->vm->id, vcpu_index(current)),
+		.arg2 = timeout_low,
+		.arg3 = timeout_high,
 	};
 
 	switch (current->rt_model) {
diff --git a/src/arch/fake/hypervisor/ffa.c b/src/arch/fake/hypervisor/ffa.c
index 4747a73..bee008c 100644
--- a/src/arch/fake/hypervisor/ffa.c
+++ b/src/arch/fake/hypervisor/ffa.c
@@ -566,10 +566,14 @@
 }
 
 struct ffa_value plat_ffa_yield_prepare(struct vcpu *current,
-					struct vcpu **next)
+					struct vcpu **next,
+					uint32_t timeout_low,
+					uint32_t timeout_high)
 {
 	(void)current;
 	(void)next;
+	(void)timeout_low;
+	(void)timeout_high;
 
 	return ffa_error(FFA_NOT_SUPPORTED);
 }