refactor(ffa): change the runtime states of an ..

endpoint execution context based on FF-A v1.1. Allow FFA_RUN from SP.

The various state enumerations defined in the specification along
with few other auxiliary states needed for state transitions, such
as VCPU_STATE_OFF and VCPU_STATE_ABORTED, are described as follows:

	/** The vCPU is switched off. */
	VCPU_STATE_OFF,

	/** The vCPU is currently running. */
	VCPU_STATE_RUNNING,

	/** The vCPU is waiting to be allocated CPU cycles to do work.*/
	VCPU_STATE_WAITING,

	/** The vCPU is blocked and waiting for some work to complete
	    on its behalf. */
	VCPU_STATE_BLOCKED,

	/** The vCPU has been preempted by an interrupt. */
	VCPU_STATE_PREEMPTED,

	/** The vCPU is waiting for an interrupt. */
	VCPU_STATE_BLOCKED_INTERRUPT,

	/** The vCPU has aborted. */
	VCPU_STATE_ABORTED,

Moreover, this patch also removes the constraint on FFA_RUN. As per
FF-A v1.1 spec, any SP can invoke FFA_RUN. We leverage this capability
for secure interrupt signal completion in further patches.

Change-Id: I3801b4a053df56a4b5a2803e74d4cbf743ad2678
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/src/api.c b/src/api.c
index 47cae90..d73f330 100644
--- a/src/api.c
+++ b/src/api.c
@@ -71,11 +71,13 @@
  * If VM is UP then return first vCPU.
  * If VM is MP then return vCPU whose index matches current CPU index.
  */
-static struct vcpu *api_ffa_get_vm_vcpu(struct vm *vm, struct vcpu *current)
+struct vcpu *api_ffa_get_vm_vcpu(struct vm *vm, struct vcpu *current)
 {
 	ffa_vcpu_index_t current_cpu_index = cpu_index(current->cpu);
 	struct vcpu *vcpu = NULL;
 
+	CHECK((vm != NULL) && (current != NULL));
+
 	if (vm->vcpu_count == 1) {
 		vcpu = vm_get_vcpu(vm, 0);
 	} else if (current_cpu_index < vm->vcpu_count) {
@@ -123,9 +125,9 @@
  * This triggers the scheduling logic to run. Run in the context of secondary VM
  * to cause FFA_RUN to return and the primary VM to regain control of the CPU.
  */
-static struct vcpu *api_switch_to_primary(struct vcpu *current,
-					  struct ffa_value primary_ret,
-					  enum vcpu_state secondary_state)
+struct vcpu *api_switch_to_primary(struct vcpu *current,
+				   struct ffa_value primary_ret,
+				   enum vcpu_state secondary_state)
 {
 	/*
 	 * If the secondary is blocked but has a timer running, sleep until the
@@ -242,7 +244,7 @@
 		.arg1 = ffa_vm_vcpu(current->vm->id, vcpu_index(current)),
 	};
 
-	return api_switch_to_primary(current, ret, VCPU_STATE_READY);
+	return api_switch_to_primary(current, ret, VCPU_STATE_PREEMPTED);
 }
 
 /**
@@ -280,9 +282,9 @@
 }
 
 /**
- * Returns to the primary VM to allow this CPU to be used for other tasks as the
- * vCPU does not have work to do at this moment. The current vCPU is marked as
- * ready to be scheduled again.
+ * The current vCPU is blocked on some resource and needs to relinquish
+ * 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)
 {
@@ -309,7 +311,7 @@
 		(struct ffa_value){.func = FFA_YIELD_32,
 				   .arg1 = ffa_vm_vcpu(current->vm->id,
 						       vcpu_index(current))},
-		VCPU_STATE_READY);
+		VCPU_STATE_BLOCKED);
 
 	return ret;
 }
@@ -325,7 +327,7 @@
 		.arg1 = ffa_vm_vcpu(target_vcpu->vm->id,
 				    vcpu_index(target_vcpu)),
 	};
-	return api_switch_to_primary(current, ret, VCPU_STATE_READY);
+	return api_switch_to_primary(current, ret, VCPU_STATE_BLOCKED);
 }
 
 /**
@@ -600,6 +602,7 @@
 	struct vm_locked vm_locked;
 	bool need_vm_lock;
 	bool ret;
+	uint64_t timer_remaining_ns = FFA_SLEEP_INDEFINITE;
 
 	/*
 	 * Check that the registers are available so that the vCPU can be run.
@@ -622,7 +625,7 @@
 #endif
 
 	/* The VM needs to be locked to deliver mailbox messages. */
-	need_vm_lock = vcpu->state == VCPU_STATE_BLOCKED_MAILBOX;
+	need_vm_lock = vcpu->state == VCPU_STATE_WAITING;
 	if (need_vm_lock) {
 		vcpu_unlock(&vcpu_locked);
 		vm_locked = vm_lock(vcpu->vm);
@@ -670,7 +673,16 @@
 		ret = false;
 		goto out;
 
-	case VCPU_STATE_BLOCKED_MAILBOX:
+	case VCPU_STATE_WAITING:
+		/*
+		 * An initial FFA_RUN is necessary for secondary VM/SP to reach
+		 * the message wait loop.
+		 */
+		if (!vcpu->is_bootstrapped) {
+			vcpu->is_bootstrapped = true;
+			break;
+		}
+
 		/*
 		 * A pending message allows the vCPU to run so the message can
 		 * be delivered directly.
@@ -681,15 +693,31 @@
 			vcpu->vm->mailbox.state = MAILBOX_STATE_READ;
 			break;
 		}
-		/* Fall through. */
+
+		if (vcpu_interrupt_count_get(vcpu_locked) > 0) {
+			break;
+		}
+
+		if (arch_timer_enabled(&vcpu->regs)) {
+			timer_remaining_ns =
+				arch_timer_remaining_ns(&vcpu->regs);
+			if (timer_remaining_ns == 0) {
+				break;
+			}
+		} else {
+			dlog_verbose("Timer disabled\n");
+		}
+		run_ret->func = FFA_MSG_WAIT_32;
+		run_ret->arg1 = ffa_vm_vcpu(vcpu->vm->id, vcpu_index(vcpu));
+		run_ret->arg2 = timer_remaining_ns;
+		ret = false;
+		goto out;
 	case VCPU_STATE_BLOCKED_INTERRUPT:
 		/* Allow virtual interrupts to be delivered. */
 		if (vcpu_interrupt_count_get(vcpu_locked) > 0) {
 			break;
 		}
 
-		uint64_t timer_remaining_ns = FFA_SLEEP_INDEFINITE;
-
 		if (arch_timer_enabled(&vcpu->regs)) {
 			timer_remaining_ns =
 				arch_timer_remaining_ns(&vcpu->regs);
@@ -707,17 +735,25 @@
 		 * The vCPU is not ready to run, return the appropriate code to
 		 * the primary which called vcpu_run.
 		 */
-		run_ret->func = vcpu->state == VCPU_STATE_BLOCKED_MAILBOX
-					? FFA_MSG_WAIT_32
-					: HF_FFA_RUN_WAIT_FOR_INTERRUPT;
+		run_ret->func = HF_FFA_RUN_WAIT_FOR_INTERRUPT;
 		run_ret->arg1 = ffa_vm_vcpu(vcpu->vm->id, vcpu_index(vcpu));
 		run_ret->arg2 = timer_remaining_ns;
 
 		ret = false;
 		goto out;
 
-	case VCPU_STATE_READY:
+	case VCPU_STATE_BLOCKED:
+		/* A blocked vCPU is run unconditionally. Fall through. */
+	case VCPU_STATE_PREEMPTED:
 		break;
+	default:
+		/*
+		 * Execution not expected to reach here. Deny the request
+		 * gracefully.
+		 */
+		*run_ret = ffa_error(FFA_DENIED);
+		ret = false;
+		goto out;
 	}
 
 	/* It has been decided that the vCPU should be run. */
@@ -743,21 +779,14 @@
 }
 
 struct ffa_value api_ffa_run(ffa_vm_id_t vm_id, ffa_vcpu_index_t vcpu_idx,
-			     const struct vcpu *current, struct vcpu **next)
+			     struct vcpu *current, struct vcpu **next)
 {
 	struct vm *vm;
 	struct vcpu *vcpu;
 	struct ffa_value ret = ffa_error(FFA_INVALID_PARAMETERS);
 
-	/* Only the primary VM can switch vCPUs. */
-	if (current->vm->id != HF_PRIMARY_VM_ID) {
-		ret.arg2 = FFA_DENIED;
-		goto out;
-	}
-
-	/* Only secondary VM vCPUs can be run. */
-	if (vm_id == HF_PRIMARY_VM_ID) {
-		goto out;
+	if (!plat_ffa_run_checks(current, vm_id, &ret, next)) {
+		return ret;
 	}
 
 	if (plat_ffa_run_forward(vm_id, vcpu_idx, &ret)) {
@@ -854,7 +883,7 @@
 	 */
 	*next = api_switch_to_primary(
 		current, (struct ffa_value){.func = FFA_RX_RELEASE_32},
-		VCPU_STATE_READY);
+		VCPU_STATE_WAITING);
 
 	return (struct ffa_value){.func = FFA_SUCCESS_32};
 }
@@ -1218,7 +1247,7 @@
 
 		to.vm->mailbox.state = MAILBOX_STATE_READ;
 		*next = api_switch_to_primary(current, primary_ret,
-					      VCPU_STATE_READY);
+					      VCPU_STATE_BLOCKED);
 		return ret;
 	}
 
@@ -1244,7 +1273,7 @@
 	/* Return to the primary VM directly or with a switch. */
 	if (from_id != HF_PRIMARY_VM_ID) {
 		*next = api_switch_to_primary(current, primary_ret,
-					      VCPU_STATE_READY);
+					      VCPU_STATE_BLOCKED);
 	}
 
 	return ret;
@@ -1428,7 +1457,7 @@
 		/* Return to other world if caller is a SP. */
 		*next = api_switch_to_other_world(
 			current, (struct ffa_value){.func = FFA_MSG_WAIT_32},
-			VCPU_STATE_BLOCKED_MAILBOX);
+			VCPU_STATE_WAITING);
 	} else {
 		/* Switch back to primary VM to block. */
 		struct ffa_value run_return = {
@@ -1437,7 +1466,7 @@
 		};
 
 		*next = api_switch_to_primary(current, run_return,
-					      VCPU_STATE_BLOCKED_MAILBOX);
+					      VCPU_STATE_WAITING);
 	}
 out:
 	sl_unlock(&vm->lock);
@@ -1948,14 +1977,15 @@
 	case VCPU_STATE_OFF:
 	case VCPU_STATE_RUNNING:
 	case VCPU_STATE_ABORTED:
-	case VCPU_STATE_READY:
 	case VCPU_STATE_BLOCKED_INTERRUPT:
+	case VCPU_STATE_BLOCKED:
+	case VCPU_STATE_PREEMPTED:
 		ret = ffa_error(FFA_BUSY);
 		goto out;
-	case VCPU_STATE_BLOCKED_MAILBOX:
+	case VCPU_STATE_WAITING:
 		/*
-		 * Expect target vCPU to be blocked after having called
-		 * ffa_msg_wait or sent a direct message response.
+		 * We expect target vCPU to be in WAITING state after either
+		 * having called ffa_msg_wait or sent a direct message response.
 		 */
 		break;
 	}
@@ -1977,7 +2007,7 @@
 
 	arch_regs_set_retval(&receiver_vcpu->regs, api_ffa_dir_msg_value(args));
 
-	current->state = VCPU_STATE_BLOCKED_MAILBOX;
+	current->state = VCPU_STATE_BLOCKED;
 
 	/* Switch to receiver vCPU targeted to by direct msg request */
 	*next = receiver_vcpu;
@@ -2055,11 +2085,21 @@
 	vcpu_unlock(&current_locked);
 
 	if (!vm_id_is_current_world(receiver_vm_id)) {
-		*next = api_switch_to_other_world(current, to_ret,
-						  VCPU_STATE_BLOCKED_MAILBOX);
+		*next = api_switch_to_other_world(
+			current, to_ret,
+			/*
+			 * Current vcpu sent a direct response. It moves to
+			 * waiting state.
+			 */
+			VCPU_STATE_WAITING);
 	} else if (receiver_vm_id == HF_PRIMARY_VM_ID) {
-		*next = api_switch_to_primary(current, to_ret,
-					      VCPU_STATE_BLOCKED_MAILBOX);
+		*next = api_switch_to_primary(
+			current, to_ret,
+			/*
+			 * Current vcpu sent a direct response. It moves to
+			 * waiting state.
+			 */
+			VCPU_STATE_WAITING);
 	} else if (vm_id_is_current_world(receiver_vm_id)) {
 		/*
 		 * It is expected the receiver_vm_id to be from an SP, otherwise
@@ -2067,8 +2107,11 @@
 		 * made function return error before getting to this point.
 		 */
 		*next = api_switch_to_vm(current, to_ret,
-					 VCPU_STATE_BLOCKED_MAILBOX,
-					 receiver_vm_id);
+					 /*
+					  * current vcpu sent a direct response.
+					  * It moves to waiting state.
+					  */
+					 VCPU_STATE_WAITING, receiver_vm_id);
 	} else {
 		panic("Invalid direct message response invocation");
 	}