feat: vm state transitions based on lifecycle guidance
This patch performs the necessary vm state transitions based on the
guidance provided by FF-A spec for partition lifecycle.
The atomic field `aborting` in vm structure is no more needed.
Change-Id: I8bdcbeab3858f73fc3cd89c78a9314f50394a443
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/src/api.c b/src/api.c
index cf3cd18..3bc2f9c 100644
--- a/src/api.c
+++ b/src/api.c
@@ -1221,6 +1221,7 @@
const struct ffa_value ffa_run_abi =
(struct ffa_value){.func = FFA_RUN_32};
const struct ffa_value *ffa_run_ret = NULL;
+ enum vm_state target_vm_state;
/*
* Check that the registers are available so that the vCPU can be run.
@@ -1235,6 +1236,7 @@
/* The VM needs to be locked to deliver mailbox messages. */
need_vm_lock = vcpu->state == VCPU_STATE_WAITING ||
+ vcpu->state == VCPU_STATE_CREATED ||
(!vcpu->vm->el0_partition &&
(vcpu->state == VCPU_STATE_BLOCKED_INTERRUPT ||
vcpu->state == VCPU_STATE_BLOCKED ||
@@ -1251,6 +1253,41 @@
vcpu_next_locked = vcpus_locked.vcpu2;
}
+ target_vm_state = vm_read_state(vcpu->vm);
+
+ switch (target_vm_state) {
+ case VM_STATE_NULL:
+ *run_ret = ffa_error(FFA_INVALID_PARAMETERS);
+ ret = false;
+ goto out;
+ case VM_STATE_CREATED:
+ /*
+ * An initial FFA_RUN is necessary for vCPUs of secondary VMs
+ * to reach the message wait loop. Note that vCPU(s) of Secure
+ * Partitions don't need it.
+ */
+ if (ffa_is_vm_id(vcpu->vm->id)) {
+ break;
+ }
+ *run_ret = ffa_error(FFA_BUSY);
+ ret = false;
+ goto out;
+ case VM_STATE_ABORTING:
+ if (vcpu->state != VCPU_STATE_NULL &&
+ vcpu->state != VCPU_STATE_STOPPED &&
+ vcpu->state != VCPU_STATE_ABORTED) {
+ dlog_verbose("VM %#x was aborted, cannot run vCPU %u\n",
+ vcpu->vm->id, vcpu_index(vcpu));
+ vcpu->state = VCPU_STATE_ABORTED;
+ }
+ [[fallthrough]];
+ case VM_STATE_RUNNING:
+ [[fallthrough]];
+ default:
+ /* Let the subsequent checks handle further conditions. */
+ break;
+ }
+
/*
* If the vCPU is already running somewhere then we can't run it here
* simultaneously. While it is actually running then the state should be
@@ -1276,16 +1313,6 @@
goto out;
}
- if (atomic_load_explicit(&vcpu->vm->aborting, memory_order_relaxed)) {
- if (vcpu->state != VCPU_STATE_NULL &&
- vcpu->state != VCPU_STATE_STOPPED &&
- vcpu->state != VCPU_STATE_ABORTED) {
- dlog_verbose("VM %#x was aborted, cannot run vCPU %u\n",
- vcpu->vm->id, vcpu_index(vcpu));
- vcpu->state = VCPU_STATE_ABORTED;
- }
- }
-
switch (vcpu->state) {
case VCPU_STATE_ABORTED:
if (vcpu->vm->lifecycle_support) {
@@ -1315,12 +1342,17 @@
* Partitions don't need it.
*/
if (ffa_is_vm_id(vcpu->vm->id)) {
+ size_t cpu_indx = cpu_index(current->cpu);
+
assert(vcpu->rt_model == RTM_SP_INIT);
vcpu->rt_model = RTM_NONE;
vcpu_was_init_state = true;
CHECK(vcpu_state_set(vcpu_next_locked,
VCPU_STATE_STARTING));
+ if (cpu_indx == PRIMARY_CPU_IDX) {
+ vm_set_state(vm_locked, VM_STATE_RUNNING);
+ }
break;
}
*run_ret = ffa_error(FFA_BUSY);
@@ -2940,6 +2972,33 @@
current_locked = vcpus_locked.vcpu1;
receiver_vcpu_locked = vcpus_locked.vcpu2;
+ switch (vm_read_state(receiver_vm)) {
+ case VM_STATE_NULL:
+ ret = ffa_error(FFA_INVALID_PARAMETERS);
+ goto out;
+ case VM_STATE_CREATED:
+ ret = ffa_error(FFA_BUSY);
+ goto out;
+ case VM_STATE_ABORTING:
+ if (receiver_vcpu->state != VCPU_STATE_NULL &&
+ receiver_vcpu->state != VCPU_STATE_ABORTED &&
+ receiver_vcpu->state != VCPU_STATE_STOPPED) {
+ dlog_verbose(
+ "Receiver VM %#x aborted, cannot run vCPU %u\n",
+ receiver_vcpu->vm->id,
+ vcpu_index(receiver_vcpu));
+ CHECK(vcpu_state_set(receiver_vcpu_locked,
+ VCPU_STATE_ABORTED));
+ }
+ /* Let the subsequent checks handle further conditions. */
+ break;
+ case VM_STATE_RUNNING:
+ [[fallthrough]];
+ default:
+ /* Let the subsequent checks handle further conditions. */
+ break;
+ }
+
/*
* If destination vCPU is executing or already received an
* FFA_MSG_SEND_DIRECT_REQ then return to caller hinting recipient is
@@ -2962,20 +3021,6 @@
goto out;
}
- if (atomic_load_explicit(&receiver_vcpu->vm->aborting,
- memory_order_relaxed)) {
- if (receiver_vcpu->state != VCPU_STATE_NULL &&
- receiver_vcpu->state != VCPU_STATE_ABORTED &&
- receiver_vcpu->state != VCPU_STATE_STOPPED) {
- dlog_verbose(
- "Receiver VM %#x aborted, cannot run vCPU %u\n",
- receiver_vcpu->vm->id,
- vcpu_index(receiver_vcpu));
- CHECK(vcpu_state_set(receiver_vcpu_locked,
- VCPU_STATE_ABORTED));
- }
- }
-
switch (receiver_vcpu->state) {
case VCPU_STATE_ABORTED:
if (receiver_vcpu->vm->lifecycle_support) {