feat: take action when vCPU aborts while processing a message
An execution context of an SP could abort while processing an indirect
or direct message from another endpoint. SPMC shall resume the endpoint,
that allocated CPU cycles, with FFA_ERROR return status.
Please refer to the embedded comments for further details.
Change-Id: I0abadc3e9628d9dec7818d13662dd20c25ffa4eb
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/src/api.c b/src/api.c
index 4032655..ccc72d7 100644
--- a/src/api.c
+++ b/src/api.c
@@ -3103,27 +3103,27 @@
struct vcpu **next,
ffa_id_t receiver_vm_id,
struct ffa_value to_ret,
- bool is_nwd_call_chain)
+ bool is_nwd_call_chain,
+ enum vcpu_state to_state)
{
if (ffa_direct_msg_is_spmd_lp_id(receiver_vm_id) ||
!vm_id_is_current_world(receiver_vm_id)) {
*next = api_switch_to_other_world(current_locked, to_ret,
- VCPU_STATE_WAITING);
+ to_state);
/* End of NWd scheduled call chain. */
assert(!is_nwd_call_chain ||
(current_locked.vcpu->call_chain.prev_node == NULL));
} else if (receiver_vm_id == HF_PRIMARY_VM_ID) {
- *next = api_switch_to_primary(current_locked, to_ret,
- VCPU_STATE_WAITING);
+ *next = api_switch_to_primary(current_locked, to_ret, to_state);
} else if (vm_id_is_current_world(receiver_vm_id)) {
/*
* It is expected the receiver_vm_id to be from an SP, otherwise
* 'ffa_direct_msg_is_direct_response_valid' should have
* made function return error before getting to this point.
*/
- *next = api_switch_to_vm(current_locked, to_ret,
- VCPU_STATE_WAITING, receiver_vm_id);
+ *next = api_switch_to_vm(current_locked, to_ret, to_state,
+ receiver_vm_id);
} else {
panic("Invalid direct message response invocation");
}
@@ -3203,6 +3203,52 @@
}
/**
+ * Unwind a direct message call chain and resume the target vCPU when sending a
+ * direct response.
+ */
+void api_direct_resp_unwind_call_chain_resume_target(
+ struct vcpu_locked *current_locked, struct vcpu **next,
+ struct vcpu_locked *next_locked, struct ffa_value to_ret,
+ enum vcpu_state to_state)
+{
+ struct two_vcpu_locked vcpus_locked;
+ struct vcpu *current;
+
+ /* Ensure caller and callee vCPUs are valid */
+ assert(current_locked != NULL && next_locked != NULL && next != NULL);
+
+ current = current_locked->vcpu;
+ assert(current->direct_request_origin.vm_id != HF_INVALID_VM_ID);
+
+ api_ffa_resume_direct_resp_target(*current_locked, next,
+ current->direct_request_origin.vm_id,
+ to_ret, false, to_state);
+
+ /* Clear direct request origin vm_id and request type for the caller. */
+ current->direct_request_origin.is_ffa_req2 = false;
+ current->direct_request_origin.vm_id = HF_INVALID_VM_ID;
+
+ /*
+ * Unlock current vCPU to allow it to be locked together with next
+ * vcpu.
+ */
+ vcpu_unlock(current_locked);
+
+ /* Lock both vCPUs at once to avoid deadlock. */
+ vcpus_locked = vcpu_lock_both(current, *next);
+ *current_locked = vcpus_locked.vcpu1;
+ *next_locked = vcpus_locked.vcpu2;
+
+ /* Inject timer interrupt if timer has expired. */
+ api_inject_arch_timer_interrupt(*next_locked);
+ ffa_direct_msg_unwind_call_chain_ffa_direct_resp(*current_locked,
+ *next_locked);
+
+ /* Schedule the receiver's vCPU now. */
+ CHECK(vcpu_state_set(*next_locked, VCPU_STATE_RUNNING));
+}
+
+/**
* Send an FF-A direct message response.
* This handler covers both FFA_MSG_SEND_DIRECT_RESP_32/64
* and FFA_MSG_SEND_DIRECT_RESP2_64 (introduced in FF-A v1.2) with
@@ -3227,7 +3273,6 @@
/* Prepare return interrupt if caller goes back to waiting state. */
struct ffa_value ret = (struct ffa_value){.func = FFA_INTERRUPT_32};
struct ffa_value to_ret = api_ffa_dir_msg_value(args);
- struct two_vcpu_locked vcpus_locked;
if (!api_ffa_msg_send_direct_resp_validate_args(args, current)) {
return ffa_error(FFA_INVALID_PARAMETERS);
@@ -3276,31 +3321,9 @@
HF_MANAGED_EXIT_INTID);
}
- /* Clear direct request origin vm_id and request type for the caller. */
- current->direct_request_origin.is_ffa_req2 = false;
- current->direct_request_origin.vm_id = HF_INVALID_VM_ID;
-
- api_ffa_resume_direct_resp_target(current_locked, next, receiver_vm_id,
- to_ret, false);
-
- /*
- * Unlock current vCPU to allow it to be locked together with next
- * vcpu.
- */
- vcpu_unlock(¤t_locked);
-
- /* Lock both vCPUs at once to avoid deadlock. */
- vcpus_locked = vcpu_lock_both(current, *next);
- current_locked = vcpus_locked.vcpu1;
- next_locked = vcpus_locked.vcpu2;
-
- /* Inject timer interrupt if timer has expired. */
- api_inject_arch_timer_interrupt(next_locked);
- ffa_direct_msg_unwind_call_chain_ffa_direct_resp(current_locked,
- next_locked);
-
- /* Schedule the receiver's vCPU now. */
- CHECK(vcpu_state_set(next_locked, VCPU_STATE_RUNNING));
+ api_direct_resp_unwind_call_chain_resume_target(¤t_locked, next,
+ &next_locked, to_ret,
+ VCPU_STATE_WAITING);
/*
* Check if there is a pending interrupt, and if the partition