| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| /* |
| * transition_asm.S: This file handles the entry and exit from an exception |
| * |
| * Flow: exception --> exc_vectors --> exc_entry --> exc_dispatch --> |
| * exc_exit |
| * Transition Flow: transition --> trans_switch --> exc_exit |
| * |
| * |---| Exception Entry |---| |
| * |
| * On exception entry, it saves all the xregs on SP_ELx since SP_ELx is |
| * selected on entry. Some dummy pushes are performed to create space for |
| * elx_state structure. It then passes pointer to this saved set of regs and |
| * a unique id(for identifying exception) to exc_entry. |
| * |
| * |---| Exception Transition Dispatch |---| |
| * |
| * This is the C-component of exception entry. It does the work of initializing |
| * the exc_state registers. Finally it calls exception dispatch implemented by |
| * the user. This is point of no-return. |
| * |
| * |---| Exception Dispatch |---| |
| * |
| * User of this functionality is expected to implement exc_dispatch which |
| * acts as entry point for it. Once exception handling is complete, the user |
| * needs to call exc_exit with pointer to struct regs. |
| * |
| * |---| Exception Exit |---| |
| * |
| * Once exc_dispatch is done with handling the exception based on the id passed |
| * to it, it needs to call exc_exit with pointer to struct regs. This is done to |
| * unwind the exception stack by popping off all the xregs. |
| * |
| * |---| Exception Transition Exit |---| |
| * |
| * This routine makes SP_EL0 point to the regs structure passed and continues |
| * onto the exception exit routine described above. This is the reason that |
| * transition library does not handle initialization of SP_EL0 for the program |
| * to be executed. |
| */ |
| |
| #include <arch/asm.h> |
| #include <arch/lib_helpers.h> |
| #include <arch/transition.h> |
| |
| .macro eentry lbl id |
| .align 7 |
| \lbl: |
| stp x29, x30, [sp, #STACK_PUSH_BYTES]! |
| bl exc_prologue |
| mov x1, \id |
| mov x0, sp |
| b exc_entry |
| .endm |
| |
| /* |
| * exc_vectors: Entry point for an exception |
| */ |
| ENTRY_WITH_ALIGN(exc_vectors, 11) |
| |
| eentry sync_curr_sp0, #EXC_VID_CUR_SP_EL0_SYNC |
| eentry irq_curr_sp0, #EXC_VID_CUR_SP_EL0_IRQ |
| eentry fiq_curr_sp0, #EXC_VID_CUR_SP_EL0_FIRQ |
| eentry serror_curr_sp0, #EXC_VID_CUR_SP_EL0_SERR |
| eentry sync_curr_spx, #EXC_VID_CUR_SP_ELX_SYNC |
| eentry irq_curr_spx, #EXC_VID_CUR_SP_ELX_IRQ |
| eentry fiq_curr_spx, #EXC_VID_CUR_SP_ELX_FIQ |
| eentry serror_curr_spx, #EXC_VID_CUR_SP_ELX_SERR |
| eentry sync_lower_64, #EXC_VID_LOW64_SYNC |
| eentry irq_lower_64, #EXC_VID_LOW64_IRQ |
| eentry fiq_lower_64, #EXC_VID_LOW64_FIQ |
| eentry serror_lower_64, #EXC_VID_LOW64_SERR |
| eentry sync_lower_32, #EXC_VID_LOW32_SYNC |
| eentry irq_lower_32, #EXC_VID_LOW32_IRQ |
| eentry fiq_lower_32, #EXC_VID_LOW32_FIQ |
| eentry serror_lower_32, #EXC_VID_LOW32_SERR |
| |
| ENDPROC(exc_vectors) |
| |
| ENTRY(exc_prologue) |
| stp x27, x28, [sp, #STACK_PUSH_BYTES]! |
| stp x25, x26, [sp, #STACK_PUSH_BYTES]! |
| stp x23, x24, [sp, #STACK_PUSH_BYTES]! |
| stp x21, x22, [sp, #STACK_PUSH_BYTES]! |
| stp x19, x20, [sp, #STACK_PUSH_BYTES]! |
| stp x17, x18, [sp, #STACK_PUSH_BYTES]! |
| stp x15, x16, [sp, #STACK_PUSH_BYTES]! |
| stp x13, x14, [sp, #STACK_PUSH_BYTES]! |
| stp x11, x12, [sp, #STACK_PUSH_BYTES]! |
| stp x9, x10, [sp, #STACK_PUSH_BYTES]! |
| stp x7, x8, [sp, #STACK_PUSH_BYTES]! |
| stp x5, x6, [sp, #STACK_PUSH_BYTES]! |
| stp x3, x4, [sp, #STACK_PUSH_BYTES]! |
| stp x1, x2, [sp, #STACK_PUSH_BYTES]! |
| /* xzr pushed as place holder for sp */ |
| stp xzr, x0, [sp, #STACK_PUSH_BYTES]! |
| /* |
| * xzr pushed as place holder for: |
| * 1. sp_elx and elr |
| */ |
| stp xzr, xzr, [sp, #STACK_PUSH_BYTES]! |
| /* 2. spsr and sp_el0 */ |
| stp xzr, xzr, [sp, #STACK_PUSH_BYTES]! |
| ret |
| ENDPROC(exc_prologue) |
| |
| /* |
| * trans_switch: Set SPSel to use SP_EL0 |
| * x0 = regs structure |
| */ |
| ENTRY(trans_switch) |
| msr SPSel, #SPSR_USE_L |
| b exc_exit |
| ENDPROC(trans_switch) |
| |
| /* |
| * exc_exit: Return from exception by restoring saved state of xregs |
| * x0 = regs structure |
| */ |
| ENTRY(exc_exit) |
| /* Unwind the stack by making sp point to regs structure */ |
| mov sp, x0 |
| /* Load registers x0-x30 */ |
| ldp xzr, x0, [sp], #STACK_POP_BYTES |
| ldp x1, x2, [sp], #STACK_POP_BYTES |
| ldp x3, x4, [sp], #STACK_POP_BYTES |
| ldp x5, x6, [sp], #STACK_POP_BYTES |
| ldp x7, x8, [sp], #STACK_POP_BYTES |
| ldp x9, x10, [sp], #STACK_POP_BYTES |
| ldp x11, x12, [sp], #STACK_POP_BYTES |
| ldp x13, x14, [sp], #STACK_POP_BYTES |
| ldp x15, x16, [sp], #STACK_POP_BYTES |
| ldp x17, x18, [sp], #STACK_POP_BYTES |
| ldp x19, x20, [sp], #STACK_POP_BYTES |
| ldp x21, x22, [sp], #STACK_POP_BYTES |
| ldp x23, x24, [sp], #STACK_POP_BYTES |
| ldp x25, x26, [sp], #STACK_POP_BYTES |
| ldp x27, x28, [sp], #STACK_POP_BYTES |
| ldp x29, x30, [sp], #STACK_POP_BYTES |
| eret |
| ENDPROC(exc_exit) |
| |
| /* |
| * exception_init_asm: Initialize VBAR and point SP_EL3 to exception stack. |
| * Also unmask aborts now that we can report them. x0 = end of exception stack |
| */ |
| ENTRY(exception_init_asm) |
| msr SPSel, #SPSR_USE_H |
| mov sp, x0 |
| msr SPSel, #SPSR_USE_L |
| |
| adr x0, exc_vectors |
| msr vbar_el3, x0 |
| |
| msr DAIFClr, #0xf |
| |
| dsb sy |
| isb |
| ret |
| ENDPROC(exception_init_asm) |