fix(interrupts): check for pending interrupts during direct response
A secure interrupt might trigger while the target SP is currently
running to send a direct response. SPMC would then inject virtual
interrupt to vCPU of target SP and resume it.
However, it is possible that the S-EL1 SP could have its interrupts
masked and hence might not handle the virtual interrupt before
sending direct response message. In such a scenario, SPMC must
return an error with code FFA_INTERRUPTED to inform the S-EL1 SP of
a pending interrupt and allow it to be handled before sending the
direct response.
Change-Id: I8a4517a929799f29e9eba01dab0fd9b8b538b2db
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/src/api.c b/src/api.c
index 461fdd3..2c6fa76 100644
--- a/src/api.c
+++ b/src/api.c
@@ -2706,6 +2706,10 @@
return ffa_error(FFA_DENIED);
}
+ if (plat_ffa_is_direct_response_interrupted(current)) {
+ return ffa_error(FFA_INTERRUPTED);
+ }
+
assert(next_state == VCPU_STATE_WAITING);
current_locked = vcpu_lock(current);
diff --git a/src/arch/aarch64/plat/ffa/absent.c b/src/arch/aarch64/plat/ffa/absent.c
index e08d332..b667336 100644
--- a/src/arch/aarch64/plat/ffa/absent.c
+++ b/src/arch/aarch64/plat/ffa/absent.c
@@ -517,6 +517,12 @@
(void)vm_locked;
}
+bool plat_ffa_is_direct_response_interrupted(struct vcpu *current)
+{
+ (void)current;
+ return false;
+}
+
struct ffa_value plat_ffa_other_world_mem_send(
struct vm *from, struct ffa_memory_region *memory_region,
uint32_t length, uint32_t fragment_length, struct mpool *page_pool)
diff --git a/src/arch/aarch64/plat/ffa/hypervisor.c b/src/arch/aarch64/plat/ffa/hypervisor.c
index 6d847cf..9b601f6 100644
--- a/src/arch/aarch64/plat/ffa/hypervisor.c
+++ b/src/arch/aarch64/plat/ffa/hypervisor.c
@@ -1064,6 +1064,12 @@
}
}
+bool plat_ffa_is_direct_response_interrupted(struct vcpu *current)
+{
+ (void)current;
+ return false;
+}
+
/** Forwards a memory send message on to the other world. */
static struct ffa_value memory_send_other_world_forward(
struct vm_locked other_world_locked, ffa_vm_id_t sender_vm_id,
diff --git a/src/arch/aarch64/plat/ffa/spmc.c b/src/arch/aarch64/plat/ffa/spmc.c
index 150f5acd..eca471a 100644
--- a/src/arch/aarch64/plat/ffa/spmc.c
+++ b/src/arch/aarch64/plat/ffa/spmc.c
@@ -2597,3 +2597,62 @@
return ffa_error(FFA_INVALID_PARAMETERS);
}
+
+bool plat_ffa_is_direct_response_interrupted(struct vcpu *current)
+{
+ struct vcpu_locked current_locked;
+ bool ret;
+
+ /*
+ * A secure interrupt might trigger while the target SP is currently
+ * running to send a direct response. SPMC would then inject virtual
+ * interrupt to vCPU of target SP and resume it.
+ * However, it is possible that the S-EL1 SP could have its interrupts
+ * masked and hence might not handle the virtual interrupt before
+ * sending direct response message. In such a scenario, SPMC must
+ * return an error with code FFA_INTERRUPTED to inform the S-EL1 SP of
+ * a pending interrupt and allow it to be handled before sending the
+ * direct response.
+ */
+ current_locked = vcpu_lock(current);
+
+ /*
+ * An S-EL0 partition can handle virtual secure interrupt only in
+ * WAITING state. Hence it is likely for an S-EL0 SP to have a pending
+ * virtual interrupt during FFA_MSG_SEND_DIRECT_RESP invocation.
+ */
+ if (current->vm->el0_partition) {
+ ret = false;
+ goto out;
+ }
+
+ /*
+ * Check if there are any pending virtual secure interrupts to be
+ * handled.
+ */
+ if (vcpu_interrupt_count_get(current_locked) > 0 &&
+ current->processing_secure_interrupt) {
+ assert(current->implicit_completion_signal);
+
+ dlog_verbose(
+ "Secure virtual interrupt not yet serviced by "
+ "SP %x. FFA_MSG_SEND_DIRECT_RESP interrupted\n",
+ current->vm->id);
+ ret = true;
+ } else {
+ /*
+ * SP must have completed handling of virtual interrupt and
+ * performed de-activation. All the fields corresponding to
+ * secure interrupt handling must have been cleared. Refer to
+ * plat_ffa_interrupt_deactivate().
+ */
+ assert(!current->processing_secure_interrupt);
+ assert(!current->secure_interrupt_deactivated);
+ assert(!current->implicit_completion_signal);
+
+ ret = false;
+ }
+out:
+ vcpu_unlock(¤t_locked);
+ return ret;
+}
diff --git a/src/arch/fake/hypervisor/ffa.c b/src/arch/fake/hypervisor/ffa.c
index ffa87cd..9b74a98 100644
--- a/src/arch/fake/hypervisor/ffa.c
+++ b/src/arch/fake/hypervisor/ffa.c
@@ -490,6 +490,12 @@
(void)vm_locked;
}
+bool plat_ffa_is_direct_response_interrupted(struct vcpu *current)
+{
+ (void)current;
+ return false;
+}
+
struct ffa_value plat_ffa_other_world_mem_send(
struct vm *from, uint32_t share_func,
struct ffa_memory_region **memory_region, uint32_t length,