aboutsummaryrefslogtreecommitdiff
path: root/payloads/libpayload/arch/arm64/exception_asm.S
diff options
context:
space:
mode:
Diffstat (limited to 'payloads/libpayload/arch/arm64/exception_asm.S')
-rw-r--r--payloads/libpayload/arch/arm64/exception_asm.S152
1 files changed, 97 insertions, 55 deletions
diff --git a/payloads/libpayload/arch/arm64/exception_asm.S b/payloads/libpayload/arch/arm64/exception_asm.S
index d428940730..efa3f9d7e2 100644
--- a/payloads/libpayload/arch/arm64/exception_asm.S
+++ b/payloads/libpayload/arch/arm64/exception_asm.S
@@ -27,19 +27,25 @@
* SUCH DAMAGE.
*/
+#include <arch/asm.h>
+#include <arch/exception.h>
+
/* Macro for exception entry
* Store x30 before any branch
- * Branch to exception_prologue to save rest of the registers
+ * Branch to exception_prologue to save rest and switch stacks
* Move exception id into x1
- * Branch to exception_handler
+ * Branch to exception_dispatch (exception C entry point)
+ * Branch to exception_return to return from exception
*/
.macro eentry lbl id
.align 7
\lbl:
- stp x30, xzr, [sp, #-16]!
+ /* Note: SP points to exception_state (see exception_set_state_ptr) */
+ str x30, [sp, #EXCEPTION_STATE_REG(30)]
bl exception_prologue
mov x1, \id
- bl exception_handler
+ bl exception_dispatch
+ b exception_return
.endm
/* Exception table has 16 entries and each of 128 bytes
@@ -68,64 +74,100 @@ eentry irq_elx_32,#13
eentry fiq_elx_32,#14
eentry serror_elx_32,#15
-exception_prologue:
- /* Save all registers x0-x29 */
- stp x28, x29, [sp, #-16]!
- stp x26, x27, [sp, #-16]!
- stp x24, x25, [sp, #-16]!
- stp x22, x23, [sp, #-16]!
- stp x20, x21, [sp, #-16]!
- stp x18, x19, [sp, #-16]!
- stp x16, x17, [sp, #-16]!
- stp x14, x15, [sp, #-16]!
- stp x12, x13, [sp, #-16]!
- stp x10, x11, [sp, #-16]!
- stp x8, x9, [sp, #-16]!
- stp x6, x7, [sp, #-16]!
- stp x4, x5, [sp, #-16]!
- stp x2, x3, [sp, #-16]!
- stp x0, x1, [sp, #-16]!
-
- /* Save the exception reason on stack */
- mrs x1, esr_el2
+/* This code must match the layout of struct exception_state (minus x30) */
+ENTRY(exception_prologue)
+ /* Save registers x0-x29 */
+ stp x28, x29, [sp, #EXCEPTION_STATE_REG(28)]
+ stp x26, x27, [sp, #EXCEPTION_STATE_REG(26)]
+ stp x24, x25, [sp, #EXCEPTION_STATE_REG(24)]
+ stp x22, x23, [sp, #EXCEPTION_STATE_REG(22)]
+ stp x20, x21, [sp, #EXCEPTION_STATE_REG(20)]
+ stp x18, x19, [sp, #EXCEPTION_STATE_REG(18)]
+ stp x16, x17, [sp, #EXCEPTION_STATE_REG(16)]
+ stp x14, x15, [sp, #EXCEPTION_STATE_REG(14)]
+ stp x12, x13, [sp, #EXCEPTION_STATE_REG(12)]
+ stp x10, x11, [sp, #EXCEPTION_STATE_REG(10)]
+ stp x8, x9, [sp, #EXCEPTION_STATE_REG(8)]
+ stp x6, x7, [sp, #EXCEPTION_STATE_REG(6)]
+ stp x4, x5, [sp, #EXCEPTION_STATE_REG(4)]
+ stp x2, x3, [sp, #EXCEPTION_STATE_REG(2)]
+ stp x0, x1, [sp, #EXCEPTION_STATE_REG(0)]
+
+ /* Save the stack pointer and SPSR */
+ mrs x1, sp_el0
+ mrs x0, spsr_el2
+ stp x0, x1, [sp, #EXCEPTION_STATE_SPSR]
- /* Save the return address on stack */
+ /* Save return address (ELR) and exception syndrome */
+ mrs x1, esr_el2
mrs x0, elr_el2
- stp x0, x1, [sp, #-16]!
+ stp x0, x1, [sp, #EXCEPTION_STATE_ELR]
+
+ /* Now switch to the actual exception stack. Keep a pointer to the
+ exception_state structure in x0 as an argument for dispatch(). */
+ mov x0, sp
+ adrp x1, exception_stack_end
+ ldr x1, [x1, :lo12:exception_stack_end]
+ msr SPSel, #0
+ mov sp, x1
ret
+ENDPROC(exception_prologue)
-exception_handler:
- /* Save address of saved registers into x0
- * This acts as first argument to exception_dispatch
- */
- mov x0, sp
- bl exception_dispatch
+ENTRY(exception_return)
+ /* Switch SP back to the exception_state structure */
+ msr SPSel, #1
- /* Pop return address saved on stack */
- ldp x0, x1, [sp], #16
+ /* Restore return address (ELR) -- skip ESR (unneeded for return) */
+ ldr x0, [sp, #EXCEPTION_STATE_ELR]
msr elr_el2, x0
- msr esr_el2, x1
- /* Pop exception reason saved on stack, followed by regs x0-x30 */
- ldp x0, x1, [sp], #16
- ldp x2, x3, [sp], #16
- ldp x4, x5, [sp], #16
- ldp x6, x7, [sp], #16
- ldp x8, x9, [sp], #16
- ldp x10, x11, [sp], #16
- ldp x12, x13, [sp], #16
- ldp x14, x15, [sp], #16
- ldp x16, x17, [sp], #16
- ldp x18, x19, [sp], #16
- ldp x20, x21, [sp], #16
- ldp x22, x23, [sp], #16
- ldp x24, x25, [sp], #16
- ldp x26, x27, [sp], #16
- ldp x28, x29, [sp], #16
- ldp x30, xzr, [sp], #16
+
+ /* Restore stack pointer and SPSR */
+ ldp x0, x1, [sp, #EXCEPTION_STATE_SPSR]
+ msr spsr_el2, x0
+ msr sp_el0, x1
+
+ /* Restore all registers (x0-x30) */
+ ldp x0, x1, [sp, #EXCEPTION_STATE_REG(0)]
+ ldp x2, x3, [sp, #EXCEPTION_STATE_REG(2)]
+ ldp x4, x5, [sp, #EXCEPTION_STATE_REG(4)]
+ ldp x6, x7, [sp, #EXCEPTION_STATE_REG(6)]
+ ldp x8, x9, [sp, #EXCEPTION_STATE_REG(8)]
+ ldp x10, x11, [sp, #EXCEPTION_STATE_REG(10)]
+ ldp x12, x13, [sp, #EXCEPTION_STATE_REG(12)]
+ ldp x14, x15, [sp, #EXCEPTION_STATE_REG(14)]
+ ldp x16, x17, [sp, #EXCEPTION_STATE_REG(16)]
+ ldp x18, x19, [sp, #EXCEPTION_STATE_REG(18)]
+ ldp x20, x21, [sp, #EXCEPTION_STATE_REG(20)]
+ ldp x22, x23, [sp, #EXCEPTION_STATE_REG(22)]
+ ldp x24, x25, [sp, #EXCEPTION_STATE_REG(24)]
+ ldp x26, x27, [sp, #EXCEPTION_STATE_REG(26)]
+ ldp x28, x29, [sp, #EXCEPTION_STATE_REG(28)]
+ ldr x30, [sp, #EXCEPTION_STATE_REG(30)]
+
+ /* Return from exception */
eret
+ENDPROC(exception_return)
- .global set_vbar
-set_vbar:
- msr vbar_el2, x0
+ /*
+ * We have two stack pointers on AArch64: SP_EL0 (which despite the
+ * naming is used in all ELs) and SP_EL2. We can select which one to
+ * use by writing to SPSel. Normally we're using SP_EL0, but on
+ * exception entry it automatically switches to SP_EL2.
+ *
+ * It is important for exception reentrancy to switch back to SP_EL0
+ * while handling the exception. We only need SP_EL2 for the assembly
+ * exception entry and exit code that stores all register state
+ * (including the old SP_EL0, before we switch to the real exception
+ * stack). Rather than having yet another stack to push/pop those
+ * register values on so that we can later sort them into the
+ * exception_state structure, it's much easier to just make SP_EL2 point
+ * directly to exception_state and just use it as a normal base register
+ * rather than a real stack. This function sets that up.
+ */
+ENTRY(exception_set_state_ptr)
+ msr SPSel, #1
+ mov sp, x0
+ msr SPSel, #0
ret
+ENDPROC(exception_set_state_ptr)