feat(ffa): secure interrupt management in NWd
This patch adds support for secure interrupt management while running
in normal world.
Secure interrupts in the normal world are trapped to EL3. SPMD
then routes the interrupt to SPMC through FFA_INTERRUPT_32 ABI
synchronously using eret conduit. The secure interrupt is then
delegated to target secure partition where the interrupt gets
handled appropriately.
Change-Id: I8a813fbd86f7002ae442ddaa5ce28892d9db8e93
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index e9d277c..cad9392 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -641,6 +641,9 @@
case FFA_NOTIFICATION_INFO_GET_64:
*args = api_ffa_notification_info_get(current);
return true;
+ case FFA_INTERRUPT_32:
+ *args = plat_ffa_delegate_ffa_interrupt(current, next);
+ return true;
}
return false;
diff --git a/src/arch/aarch64/plat/ffa/absent.c b/src/arch/aarch64/plat/ffa/absent.c
index 6da2e09..879f5c6 100644
--- a/src/arch/aarch64/plat/ffa/absent.c
+++ b/src/arch/aarch64/plat/ffa/absent.c
@@ -258,3 +258,17 @@
return true;
}
+
+struct ffa_value plat_ffa_delegate_ffa_interrupt(struct vcpu *current,
+ struct vcpu **next)
+{
+ (void)current;
+ (void)next;
+ /*
+ * SPMD uses FFA_INTERRUPT ABI to convey secure interrupt to SPMC.
+ * Execution not expected to reach here.
+ */
+ CHECK(false);
+
+ return ffa_error(FFA_NOT_SUPPORTED);
+}
diff --git a/src/arch/aarch64/plat/ffa/hypervisor.c b/src/arch/aarch64/plat/ffa/hypervisor.c
index f7144ea..863af36 100644
--- a/src/arch/aarch64/plat/ffa/hypervisor.c
+++ b/src/arch/aarch64/plat/ffa/hypervisor.c
@@ -425,3 +425,18 @@
return true;
}
+
+struct ffa_value plat_ffa_delegate_ffa_interrupt(struct vcpu *current,
+ struct vcpu **next)
+{
+ (void)current;
+ (void)next;
+
+ /*
+ * SPMD uses FFA_INTERRUPT ABI to convey secure interrupt to SPMC.
+ * Execution should not reach hypervisor with this ABI.
+ */
+ CHECK(false);
+
+ return ffa_error(FFA_NOT_SUPPORTED);
+}
diff --git a/src/arch/aarch64/plat/ffa/spmc.c b/src/arch/aarch64/plat/ffa/spmc.c
index c0e506a..01f523e 100644
--- a/src/arch/aarch64/plat/ffa/spmc.c
+++ b/src/arch/aarch64/plat/ffa/spmc.c
@@ -863,3 +863,50 @@
target_vcpu_locked.vcpu->current_sec_interrupt_id = id;
vcpu_unlock(&target_vcpu_locked);
}
+
+/**
+ * Secure interrupts in the normal world are trapped to EL3. SPMD then routes
+ * the interrupt to SPMC through FFA_INTERRUPT_32 ABI synchronously using eret
+ * conduit.
+ */
+struct ffa_value plat_ffa_delegate_ffa_interrupt(struct vcpu *current,
+ struct vcpu **next)
+{
+ struct ffa_value ffa_ret = ffa_error(FFA_NOT_SUPPORTED);
+ uint32_t id;
+ struct vcpu_locked target_vcpu_locked;
+
+ /*
+ * A malicious SP could invoke a HVC call with FFA_INTERRUPT_32 as
+ * the function argument. Return error to avoid DoS.
+ */
+ if (current->vm->id != HF_OTHER_WORLD_ID) {
+ return ffa_error(FFA_DENIED);
+ }
+
+ target_vcpu_locked = plat_ffa_secure_interrupt_prepare(current, &id);
+ plat_ffa_signal_secure_interrupt(target_vcpu_locked, id, next);
+
+ /*
+ * current refers to other world. target must be a vCPU in the secure
+ * world.
+ */
+ CHECK(*next != current);
+
+ target_vcpu_locked.vcpu->processing_secure_interrupt = true;
+ target_vcpu_locked.vcpu->current_sec_interrupt_id = id;
+
+ /*
+ * next==NULL represents a scenario where SPMC cannot resume target SP.
+ * Resume normal world using FFA_NORMAL_WORLD_RESUME.
+ */
+ if (*next == NULL) {
+ ffa_ret = (struct ffa_value){.func = FFA_NORMAL_WORLD_RESUME};
+ target_vcpu_locked.vcpu->preempted_vcpu = NULL;
+ } else {
+ target_vcpu_locked.vcpu->preempted_vcpu = current;
+ }
+ vcpu_unlock(&target_vcpu_locked);
+
+ return ffa_ret;
+}