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) {
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index ed950dd..bf5155a 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -1128,8 +1128,7 @@
vm_locked = vm_lock(current->vm);
- atomic_store_explicit(¤t->vm->aborting, true,
- memory_order_relaxed);
+ CHECK(vm_set_state(vm_locked, VM_STATE_ABORTING));
current_locked = vcpu_lock(current);
/*
diff --git a/src/arch/aarch64/plat/psci/spmc.c b/src/arch/aarch64/plat/psci/spmc.c
index 56a583f..11a5b7b 100644
--- a/src/arch/aarch64/plat/psci/spmc.c
+++ b/src/arch/aarch64/plat/psci/spmc.c
@@ -158,6 +158,14 @@
vcpu_set_phys_core_idx(boot_vcpu);
if (cpu_index(c) == PRIMARY_CPU_IDX) {
+ struct vm_locked vm_locked;
+
+ vcpu_unlock(&vcpu_locked);
+ vm_locked = vm_lock(boot_vm);
+ vcpu_locked = vcpu_lock(boot_vcpu);
+ vm_set_state(vm_locked, VM_STATE_RUNNING);
+ vm_unlock(&vm_locked);
+
/*
* Boot information is passed by the SPMC to the SP's execution
* context only on the primary CPU.
diff --git a/src/ffa/spmc/cpu_cycles.c b/src/ffa/spmc/cpu_cycles.c
index 96aa1aa..d9cab9c 100644
--- a/src/ffa/spmc/cpu_cycles.c
+++ b/src/ffa/spmc/cpu_cycles.c
@@ -290,8 +290,7 @@
return false;
}
- if (!atomic_load_explicit(¤t->vm->aborting,
- memory_order_relaxed)) {
+ if (vm_read_state(current->vm) != VM_STATE_ABORTING) {
/* vCPU has just returned from successful initialization. */
dlog_verbose(
"Initialized execution context of VM: %#x on CPU: %zu, "
@@ -300,7 +299,7 @@
current->vm->boot_order);
}
- if (cpu_index(current_locked.vcpu->cpu) == PRIMARY_CPU_IDX) {
+ if (cpu_indx == PRIMARY_CPU_IDX) {
next_vm = vm_get_next_boot(current->vm);
} else {
/* SP boot chain on secondary CPU. */
@@ -331,8 +330,18 @@
if (vcpu_next->rt_model == RTM_SP_INIT ||
vcpu_next->state == VCPU_STATE_OFF) {
struct vcpu_locked vcpu_next_locked;
+ struct vm_locked vm_locked;
+ vm_locked = vm_lock(next_vm);
vcpu_next_locked = vcpu_lock(vcpu_next);
+
+ if (cpu_indx == PRIMARY_CPU_IDX &&
+ vcpu_next->state == VCPU_STATE_CREATED) {
+ vm_set_state(vm_locked, VM_STATE_RUNNING);
+ }
+
+ vm_unlock(&vm_locked);
+
vcpu_next->rt_model = RTM_SP_INIT;
arch_regs_reset(vcpu_next);
vcpu_next->cpu = current->cpu;
@@ -849,9 +858,7 @@
dlog_error("Aborting SP %#x from vCPU %u\n", current->vm->id,
vcpu_index(current));
- atomic_store_explicit(¤t->vm->aborting, true,
- memory_order_relaxed);
-
+ CHECK(vm_set_state(vm_locked, VM_STATE_ABORTING));
ffa_vm_free_resources(vm_locked);
if (sp_boot_next(current_locked, next)) {
diff --git a/src/ffa/spmc/interrupts.c b/src/ffa/spmc/interrupts.c
index da3b9fc..0be9b59 100644
--- a/src/ffa/spmc/interrupts.c
+++ b/src/ffa/spmc/interrupts.c
@@ -463,8 +463,7 @@
* execution context is resumed.
*/
if (target_vcpu->state == VCPU_STATE_ABORTED ||
- atomic_load_explicit(&target_vcpu->vm->aborting,
- memory_order_relaxed)) {
+ vm_read_state(target_vcpu->vm) == VM_STATE_ABORTING) {
/* Clear fields corresponding to secure interrupt handling. */
vcpu_secure_interrupt_complete(target_vcpu_locked);
ffa_vm_disable_interrupts(target_vm_locked);
diff --git a/src/load.c b/src/load.c
index 219c99b..2e70ebc 100644
--- a/src/load.c
+++ b/src/load.c
@@ -420,6 +420,7 @@
vcpu_prepare(vcpu_locked, primary_entry, params->kernel_arg);
vcpu_unlock(&vcpu_locked);
ret = true;
+ CHECK(vm_set_state(vm_locked, VM_STATE_CREATED));
out:
vm_unlock(&vm_locked);
@@ -853,6 +854,7 @@
}
ret = true;
+ CHECK(vm_set_state(vm_locked, VM_STATE_CREATED));
out:
vm_unlock(&vm_locked);
diff --git a/src/vm.c b/src/vm.c
index e3039d7..857f10e 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -86,7 +86,6 @@
CHECK(vm->vcpus != NULL);
vm->mailbox.state = MAILBOX_STATE_EMPTY;
- atomic_init(&vm->aborting, false);
vm->el0_partition = el0_partition;
vm->dma_device_count = dma_device_count;