feat: add support for newly added lifecycle states of a vCPU
Please refer to section 7 of the FF-A v1.3 ALP2 specification for
details of newly added lifecycle states. The changes in this patch
help partition manager to support the use case of managing lifecycle
of a secure partition from its creation to deletion.
Change-Id: I78d52b33e0eb3a7ea93dd65e85dbc37fcaa384a9
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/src/api.c b/src/api.c
index 806edd7..aff2efa 100644
--- a/src/api.c
+++ b/src/api.c
@@ -1262,7 +1262,8 @@
* until this has finished, so count this state as still running for the
* purposes of this check.
*/
- if (vcpu->state == VCPU_STATE_RUNNING || !vcpu->regs_available) {
+ if (vcpu->state == VCPU_STATE_RUNNING ||
+ vcpu->state == VCPU_STATE_STARTING || !vcpu->regs_available) {
/*
* vCPU is running on another pCPU.
*
@@ -1276,39 +1277,56 @@
}
if (atomic_load_explicit(&vcpu->vm->aborting, memory_order_relaxed)) {
- if (vcpu->state != VCPU_STATE_ABORTED) {
+ 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;
}
- *run_ret = ffa_error(FFA_ABORTED);
- ret = false;
- goto out;
}
switch (vcpu->state) {
+ case VCPU_STATE_ABORTED:
+ if (vcpu->vm->lifecycle_support) {
+ *run_ret = ffa_error(FFA_BUSY);
+ } else {
+ *run_ret = ffa_error(FFA_ABORTED);
+ }
+ ret = false;
+ goto out;
+ case VCPU_STATE_NULL:
+ *run_ret = ffa_error(FFA_INVALID_PARAMETERS);
+ ret = false;
+ goto out;
+ case VCPU_STATE_STOPPED:
+ case VCPU_STATE_STARTING:
+ *run_ret = ffa_error(FFA_BUSY);
+ [[fallthrough]];
case VCPU_STATE_RUNNING:
case VCPU_STATE_OFF:
- case VCPU_STATE_ABORTED:
+ ret = false;
+ goto out;
+
+ case VCPU_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)) {
+ assert(vcpu->rt_model == RTM_SP_INIT);
+ vcpu->rt_model = RTM_NONE;
+
+ vcpu_was_init_state = true;
+ vcpu->state = VCPU_STATE_STARTING;
+ break;
+ }
+ *run_ret = ffa_error(FFA_BUSY);
ret = false;
goto out;
case VCPU_STATE_WAITING:
- /*
- * An initial FFA_RUN is necessary for SP's secondary vCPUs to
- * reach the message wait loop.
- */
- if (vcpu->rt_model == RTM_SP_INIT) {
- /*
- * TODO: this should be removed, but omitting it makes
- * normal world arch gicv3 tests failing.
- */
- vcpu->rt_model = RTM_NONE;
-
- vcpu_was_init_state = true;
- break;
- }
-
assert(need_vm_lock == true);
if (!vm_locked.vm->el0_partition) {
ffa_interrupts_inject_notification_pending_interrupt(
@@ -1358,7 +1376,8 @@
goto out;
case VCPU_STATE_BLOCKED:
- /* A blocked vCPU is run unconditionally. Fall through. */
+ /* A blocked vCPU is run unconditionally. */
+ [[fallthrough]];
case VCPU_STATE_PREEMPTED:
/* Check NPI is to be injected here. */
if (need_vm_lock) {
@@ -2944,27 +2963,39 @@
if (atomic_load_explicit(&receiver_vcpu->vm->aborting,
memory_order_relaxed)) {
- if (receiver_vcpu->state != VCPU_STATE_ABORTED) {
+ 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));
receiver_vcpu->state = VCPU_STATE_ABORTED;
}
-
- ret = ffa_error(FFA_ABORTED);
- goto out;
}
switch (receiver_vcpu->state) {
+ case VCPU_STATE_ABORTED:
+ if (receiver_vcpu->vm->lifecycle_support) {
+ ret = ffa_error(FFA_BUSY);
+ } else {
+ ret = ffa_error(FFA_ABORTED);
+ }
+ goto out;
case VCPU_STATE_OFF:
case VCPU_STATE_RUNNING:
- case VCPU_STATE_ABORTED:
+ case VCPU_STATE_STARTING:
+ case VCPU_STATE_CREATED:
case VCPU_STATE_BLOCKED_INTERRUPT:
case VCPU_STATE_BLOCKED:
case VCPU_STATE_PREEMPTED:
+ case VCPU_STATE_STOPPED:
+ case VCPU_STATE_STOPPING:
ret = ffa_error(FFA_BUSY);
goto out;
+ case VCPU_STATE_NULL:
+ ret = ffa_error(FFA_INVALID_PARAMETERS);
+ goto out;
case VCPU_STATE_WAITING:
/*
* We expect target vCPU to be in WAITING state after either
diff --git a/src/arch/aarch64/plat/psci/spmc.c b/src/arch/aarch64/plat/psci/spmc.c
index 81574f1..f1c4b11 100644
--- a/src/arch/aarch64/plat/psci/spmc.c
+++ b/src/arch/aarch64/plat/psci/spmc.c
@@ -147,8 +147,9 @@
vcpu_secondary_reset_and_start(vcpu_locked, boot_vcpu->vm->secondary_ep,
0ULL);
- /* Set the vCPU's state to RUNNING. */
- vcpu_set_running(vcpu_locked, NULL);
+ /* Set the vCPU's state to STARTING. */
+ boot_vcpu->state = VCPU_STATE_STARTING;
+ boot_vcpu->regs_available = false;
/* vCPU restarts in runtime model for SP initialization. */
boot_vcpu->rt_model = RTM_SP_INIT;
diff --git a/src/ffa/spmc/cpu_cycles.c b/src/ffa/spmc/cpu_cycles.c
index 938d6a5..8dfd823 100644
--- a/src/ffa/spmc/cpu_cycles.c
+++ b/src/ffa/spmc/cpu_cycles.c
@@ -333,7 +333,7 @@
vcpu_next->rt_model = RTM_SP_INIT;
arch_regs_reset(vcpu_next);
vcpu_next->cpu = current->cpu;
- vcpu_next->state = VCPU_STATE_RUNNING;
+ vcpu_next->state = VCPU_STATE_STARTING;
vcpu_next->regs_available = false;
vcpu_set_phys_core_idx(vcpu_next);
arch_regs_set_pc_arg(&vcpu_next->regs,
@@ -709,6 +709,8 @@
uint32_t func,
enum vcpu_state *next_state)
{
+ assert(current_locked.vcpu->state == VCPU_STATE_STARTING);
+
switch (func) {
case FFA_MSG_SEND_DIRECT_REQ_64:
case FFA_MSG_SEND_DIRECT_REQ_32:
diff --git a/src/ffa/spmc/interrupts.c b/src/ffa/spmc/interrupts.c
index 4868fa7..94594b0 100644
--- a/src/ffa/spmc/interrupts.c
+++ b/src/ffa/spmc/interrupts.c
@@ -378,6 +378,7 @@
}
break;
case VCPU_STATE_RUNNING:
+ case VCPU_STATE_STARTING:
/*
* Interrupt has been injected in the vCPU state.
*/
diff --git a/src/vcpu.c b/src/vcpu.c
index 781c4a3..522973d 100644
--- a/src/vcpu.c
+++ b/src/vcpu.c
@@ -63,12 +63,18 @@
sl_init(&vcpu->lock);
vcpu->regs_available = true;
vcpu->vm = vm;
- vcpu->state = VCPU_STATE_OFF;
vcpu->direct_request_origin.is_ffa_req2 = false;
vcpu->direct_request_origin.vm_id = HF_INVALID_VM_ID;
vcpu->rt_model = RTM_SP_INIT;
list_init(&vcpu->timer_node);
list_init(&vcpu->ipi_list_node);
+
+ /*
+ * Though resources have not been allocated to the partition yet, it is
+ * safe to skip the NULL state for vCPU during cold boot and transition
+ * directly to CREATED state.
+ */
+ vcpu->state = VCPU_STATE_CREATED;
}
/**
diff --git a/src/vm.c b/src/vm.c
index 1a01a6f..bca85bd 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -94,7 +94,10 @@
return NULL;
}
- /* Do basic initialization of vCPUs. */
+ /*
+ * Do basic initialization of vCPUs, i.e. All vCPUs of the partition
+ * shall be in CREATED state.
+ */
for (i = 0; i < vcpu_count; i++) {
vcpu_init(vm_get_vcpu(vm, i), vm);
}