feat(el3-runtime): modify vector entry paths

Vector entries in EL3 from lower ELs, first check for any pending
async EAs from lower EL before handling the original exception.
This happens when there is an error (EA) in the system which is not
yet signaled to PE while executing at lower EL. During entry into EL3
the errors (EA) are synchronized causing async EA to pend at EL3.

On detecting the pending EA (via ISR_EL1.A) EL3 either reflects it back
to lower EL (KFH) or handles it in EL3 (FFH) based on EA routing model.

In case of Firmware First handling mode (FFH), EL3 handles the pended
EA first before returing back to handle the original exception.

While in case of Kernel First handling mode (KFH), EL3 will return back
to lower EL without handling the original exception. On returing to
lower EL, EA will be pended. In KFH mode there is a risk of back and
forth between EL3 and lower EL if the EA is masked at lower EL or
priority of EA is lower than that of original exception. This is a
limitation in current architecture but can be solved in future if EL3
gets a capability to inject virtual SError.

Signed-off-by: Manish Pandey <manish.pandey2@arm.com>
Change-Id: I3a2a31de7cf454d9d690b1ef769432a5b24f6c11
diff --git a/bl31/aarch64/ea_delegate.S b/bl31/aarch64/ea_delegate.S
index dd6b4dc..188c724 100644
--- a/bl31/aarch64/ea_delegate.S
+++ b/bl31/aarch64/ea_delegate.S
@@ -15,26 +15,10 @@
 #include <cpu_macros.S>
 #include <context.h>
 
-	.globl	handle_lower_el_ea_esb
 	.globl	handle_lower_el_sync_ea
 	.globl	handle_lower_el_async_ea
-
-
-/*
- * Function to delegate External Aborts synchronized by ESB instruction at EL3
- * vector entry. This function assumes GP registers x0-x29 have been saved, and
- * are available for use. It delegates the handling of the EA to platform
- * handler, and returns only upon successfully handling the EA; otherwise
- * panics. On return from this function, the original exception handler is
- * expected to resume.
- */
-func handle_lower_el_ea_esb
-	mov	x0, #ERROR_EA_ESB
-	mrs	x1, DISR_EL1
-	b	ea_proceed
-endfunc handle_lower_el_ea_esb
-
-
+	.globl	handle_pending_async_ea
+	.globl	reflect_pending_async_ea_to_lower_el
 /*
  * This function forms the tail end of Synchronous Exception entry from lower
  * EL, and expects to handle Synchronous External Aborts from lower EL and CPU
@@ -140,6 +124,165 @@
 	b	el3_exit
 endfunc handle_lower_el_async_ea
 
+/*
+ * NOTE 1 : Synchronized async EA handling
+ *
+ * Comment here applicable to following two functions
+ *   - handle_pending_async_ea
+ *   - reflect_pending_async_ea_to_lower_el
+ *
+ * Must be called from exception vector directly.
+ *
+ * These special handling is required to cater for handling async EA from
+ * lower EL synchronized at EL3 entry.
+ *
+ * This scenario may arise when there is an error (EA) in the system which is not
+ * yet signaled to PE while executing in lower EL. During entry into EL3, the errors
+ * are synchronized either implicitly or explicitly causing async EA to pend at EL3.
+ *
+ * On detecting the pending EA (via ISR_EL1.A), based on routing model of EA
+ * either handle it in EL3 using "handle_pending_async_ea" (FFH)  or return to
+ * lower EL using "reflect_pending_async_ea_to_lower_el" (KFH) .
+ */
+
+/*
+ * Refer to NOTE 1 : Firmware First Handling (FFH)
+ *  Called when FFH is enabled and outgoing world is Non-Secure (scr_el3.ea = 1).
+ *
+ * This function assumes x30 has been saved.
+ */
+#if HANDLE_EA_EL3_FIRST_NS
+func handle_pending_async_ea
+	/*
+	 * Prepare for nested handling of EA. Stash sysregs clobbered by nested
+	 * exception and handler
+	 */
+	str	x30, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_GPREG_LR]
+	mrs	x30, esr_el3
+	str	x30, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_ESR_EL3]
+	mrs	x30, spsr_el3
+	str	x30, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_SPSR_EL3]
+	mrs	x30, elr_el3
+	str	x30, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_ELR_EL3]
+
+	mov	x30, #1
+	str	x30, [sp, #CTX_EL3STATE_OFFSET + CTX_NESTED_EA_FLAG]
+	/*
+	 * Restore the original x30 saved as part of entering EL3. This is not
+	 * required for the current function but for EL3 SError vector entry
+	 * once PSTATE.A bit is unmasked. We restore x30 and then the same
+	 * value is stored in EL3 SError vector entry.
+	 */
+	ldr	x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
+
+	/*
+	 * After clearing PSTATE.A bit pending SError will trigger at current EL.
+	 * Put explicit synchronization event to ensure newly unmasked interrupt
+	 * is taken immediately.
+	 */
+	unmask_async_ea
+
+	/* Restore the original exception information along with zeroing the storage */
+	ldr	x30, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_ELR_EL3]
+	msr	elr_el3, x30
+	str	xzr, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_ELR_EL3]
+	ldr	x30, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_SPSR_EL3]
+	msr	spsr_el3, x30
+	str	xzr, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_SPSR_EL3]
+	ldr	x30, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_ESR_EL3]
+	msr	esr_el3, x30
+	str	xzr, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_ESR_EL3]
+
+	/*
+	 * If the original exception corresponds to SError from lower El, eret back
+	 * to lower EL, otherwise return to vector table for original exception handling.
+	 */
+	ubfx	x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
+	cmp	x30, #EC_SERROR
+	ldr	x30, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_GPREG_LR]
+	str	xzr, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_GPREG_LR]
+	b.eq	1f
+	ret
+1:
+	exception_return
+endfunc handle_pending_async_ea
+#endif /* HANDLE_EA_EL3_FIRST_NS */
+
+/*
+ * Refer to NOTE 1 : Kernel First handling (KFH)
+ *   Called in following scenarios
+ *     - Always, if outgoing world is either Secure or Realm
+ *     - KFH mode if outgoing world is Non-secure.
+ *
+ * This function assumes x30 has been saved.
+ */
+
+func reflect_pending_async_ea_to_lower_el
+	/*
+	 * As the original exception was not handled we need to ensure that we return
+	 * back to the instruction which caused the exception. To acheive that, eret
+	 * to "elr-4" (Label "subtract_elr_el3") for SMC or simply eret otherwise
+	 * (Label "skip_smc_check").
+	 *
+	 * LIMITATION: It could be that async EA is masked at the target exception level
+	 * or the priority of async EA wrt to the EL3/secure interrupt is lower, which
+	 * causes back and forth between lower EL and EL3. In case of back and forth between
+	 * lower EL and EL3, we can track the loop count in "CTX_NESTED_EA_FLAG" and leverage
+	 * previous ELR in "CTX_SAVED_ELR_EL3" to detect this cycle and further panic
+	 * to indicate a problem here (Label "check_loop_ctr").
+	 * However, setting SCR_EL3.IESB = 1, should give priority to SError handling
+	 * as per AArch64.TakeException pseudo code in Arm ARM.
+	 *
+	 * TODO: In future if EL3 gets a capability to inject a virtual SError to lower
+	 * ELs, we can remove the el3_panic and handle the original exception first and
+	 * inject SError to lower EL before ereting back.
+	 */
+	stp	x28, x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X28]
+	ldr	x29, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_ELR_EL3]
+	mrs	x28, elr_el3
+	cmp	x29, x28
+	b.eq	check_loop_ctr
+	str	x28, [sp, #CTX_EL3STATE_OFFSET + CTX_SAVED_ELR_EL3]
+	/* Zero the loop counter */
+	str	xzr, [sp, #CTX_EL3STATE_OFFSET + CTX_NESTED_EA_FLAG]
+	b	skip_loop_ctr
+check_loop_ctr:
+	ldr	x29, [sp, #CTX_EL3STATE_OFFSET + CTX_NESTED_EA_FLAG]
+	add	x29, x29, #1
+	str	x29, [sp, #CTX_EL3STATE_OFFSET + CTX_NESTED_EA_FLAG]
+	cmp	x29, #ASYNC_EA_REPLAY_COUNTER
+	b.ge	el3_panic
+skip_loop_ctr:
+	/*
+	 * Logic to distinguish if we came from SMC or any other exception.
+	 * Use offsets in vector entry to get which exception we are handling.
+	 * In each vector entry of size 0x200, address "0x0-0x80" is for sync
+	 * exception and "0x80-0x200" is for async exceptions.
+	 * Use vector base address (vbar_el3) and exception offset (LR) to
+	 * calculate whether the address we came from is any of the following
+	 * "0x0-0x80", "0x200-0x280", "0x400-0x480" or "0x600-0x680"
+	 */
+	mrs	x29, vbar_el3
+	sub	x30, x30, x29
+	and	x30, x30, #0x1ff
+	cmp	x30, #0x80
+	b.ge	skip_smc_check
+	/* Its a synchronous exception, Now check if it is SMC or not? */
+	mrs	x30, esr_el3
+	ubfx	x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
+	cmp	x30, #EC_AARCH32_SMC
+	b.eq	subtract_elr_el3
+	cmp	x30, #EC_AARCH64_SMC
+	b.eq	subtract_elr_el3
+	b	skip_smc_check
+subtract_elr_el3:
+	sub	x28, x28, #4
+skip_smc_check:
+	msr	elr_el3, x28
+	ldp	x28, x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X28]
+	ldr	x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
+	exception_return
+endfunc reflect_pending_async_ea_to_lower_el
 
 /*
  * Prelude for Synchronous External Abort handling. This function assumes that