| /* SPDX-License-Identifier: GPL-2.0+ */ | |
| /* | |
| * vectors - Generic ARM exception table code | |
| * | |
| * Copyright (c) 1998 Dan Malek <dmalek@jlc.net> | |
| * Copyright (c) 1999 Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se> | |
| * Copyright (c) 2000 Wolfgang Denk <wd@denx.de> | |
| * Copyright (c) 2001 Alex Züpke <azu@sysgo.de> | |
| * Copyright (c) 2001 Marius Gröger <mag@sysgo.de> | |
| * Copyright (c) 2002 Alex Züpke <azu@sysgo.de> | |
| * Copyright (c) 2002 Gary Jennejohn <garyj@denx.de> | |
| * Copyright (c) 2002 Kyle Harris <kharris@nexus-tech.net> | |
| */ | |
| #include <config.h> | |
| /* | |
| * A macro to allow insertion of an ARM exception vector either | |
| * for the non-boot0 case or by a boot0-header. | |
| */ | |
| .macro ARM_VECTORS | |
| #ifdef CONFIG_ARCH_K3 | |
| ldr pc, _reset | |
| #else | |
| b reset | |
| #endif | |
| #if !CONFIG_IS_ENABLED(SYS_NO_VECTOR_TABLE) | |
| ldr pc, _undefined_instruction | |
| ldr pc, _software_interrupt | |
| ldr pc, _prefetch_abort | |
| ldr pc, _data_abort | |
| ldr pc, _not_used | |
| ldr pc, _irq | |
| ldr pc, _fiq | |
| #endif | |
| .endm | |
| /* | |
| ************************************************************************* | |
| * | |
| * Symbol _start is referenced elsewhere, so make it global | |
| * | |
| ************************************************************************* | |
| */ | |
| .globl _start | |
| /* | |
| ************************************************************************* | |
| * | |
| * Vectors have their own section so linker script can map them easily | |
| * | |
| ************************************************************************* | |
| */ | |
| .section ".vectors", "ax" | |
| #if defined(CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK) | |
| /* | |
| * Various SoCs need something special and SoC-specific up front in | |
| * order to boot, allow them to set that in their boot0.h file and then | |
| * use it here. | |
| * | |
| * To allow a boot0 hook to insert a 'special' sequence after the vector | |
| * table (e.g. for the socfpga), the presence of a boot0 hook supresses | |
| * the below vector table and assumes that the vector table is filled in | |
| * by the boot0 hook. The requirements for a boot0 hook thus are: | |
| * (1) defines '_start:' as appropriate | |
| * (2) inserts the vector table using ARM_VECTORS as appropriate | |
| */ | |
| #include <asm/arch/boot0.h> | |
| #else | |
| /* | |
| ************************************************************************* | |
| * | |
| * Exception vectors as described in ARM reference manuals | |
| * | |
| * Uses indirect branch to allow reaching handlers anywhere in memory. | |
| * | |
| ************************************************************************* | |
| */ | |
| _start: | |
| #ifdef CFG_SYS_DV_NOR_BOOT_CFG | |
| .word CFG_SYS_DV_NOR_BOOT_CFG | |
| #endif | |
| ARM_VECTORS | |
| #endif /* !defined(CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK) */ | |
| #if !CONFIG_IS_ENABLED(SYS_NO_VECTOR_TABLE) | |
| /* | |
| ************************************************************************* | |
| * | |
| * Indirect vectors table | |
| * | |
| * Symbols referenced here must be defined somewhere else | |
| * | |
| ************************************************************************* | |
| */ | |
| .globl _reset | |
| .globl _undefined_instruction | |
| .globl _software_interrupt | |
| .globl _prefetch_abort | |
| .globl _data_abort | |
| .globl _not_used | |
| .globl _irq | |
| .globl _fiq | |
| #ifdef CONFIG_ARCH_K3 | |
| _reset: .word reset | |
| #endif | |
| _undefined_instruction: .word undefined_instruction | |
| _software_interrupt: .word software_interrupt | |
| _prefetch_abort: .word prefetch_abort | |
| _data_abort: .word data_abort | |
| _not_used: .word not_used | |
| _irq: .word irq | |
| _fiq: .word fiq | |
| .balignl 16,0xdeadbeef | |
| #endif | |
| /* | |
| ************************************************************************* | |
| * | |
| * Interrupt handling | |
| * | |
| ************************************************************************* | |
| */ | |
| /* SPL interrupt handling: just hang */ | |
| #ifdef CONFIG_SPL_BUILD | |
| #if !CONFIG_IS_ENABLED(SYS_NO_VECTOR_TABLE) | |
| .align 5 | |
| undefined_instruction: | |
| #if CONFIG_IS_ENABLED(USE_SEPARATE_FAULT_HANDLERS) | |
| b undefined_instruction | |
| #endif | |
| software_interrupt: | |
| #if CONFIG_IS_ENABLED(USE_SEPARATE_FAULT_HANDLERS) | |
| b software_interrupt | |
| #endif | |
| prefetch_abort: | |
| #if CONFIG_IS_ENABLED(USE_SEPARATE_FAULT_HANDLERS) | |
| b prefetch_abort | |
| #endif | |
| data_abort: | |
| #if CONFIG_IS_ENABLED(USE_SEPARATE_FAULT_HANDLERS) | |
| b data_abort | |
| #endif | |
| not_used: | |
| #if CONFIG_IS_ENABLED(USE_SEPARATE_FAULT_HANDLERS) | |
| b not_used | |
| #endif | |
| irq: | |
| #if CONFIG_IS_ENABLED(USE_SEPARATE_FAULT_HANDLERS) | |
| b irq | |
| #endif | |
| fiq: | |
| 1: | |
| b 1b /* hang and never return */ | |
| #endif | |
| #else /* !CONFIG_SPL_BUILD */ | |
| /* IRQ stack memory (calculated at run-time) + 8 bytes */ | |
| .globl IRQ_STACK_START_IN | |
| IRQ_STACK_START_IN: | |
| #ifdef IRAM_BASE_ADDR | |
| .word IRAM_BASE_ADDR + 0x20 | |
| #else | |
| .word 0x0badc0de | |
| #endif | |
| @ | |
| @ IRQ stack frame. | |
| @ | |
| #define S_FRAME_SIZE 72 | |
| #define S_OLD_R0 68 | |
| #define S_PSR 64 | |
| #define S_PC 60 | |
| #define S_LR 56 | |
| #define S_SP 52 | |
| #define S_IP 48 | |
| #define S_FP 44 | |
| #define S_R10 40 | |
| #define S_R9 36 | |
| #define S_R8 32 | |
| #define S_R7 28 | |
| #define S_R6 24 | |
| #define S_R5 20 | |
| #define S_R4 16 | |
| #define S_R3 12 | |
| #define S_R2 8 | |
| #define S_R1 4 | |
| #define S_R0 0 | |
| #define MODE_SVC 0x13 | |
| #define I_BIT 0x80 | |
| /* | |
| * use bad_save_user_regs for abort/prefetch/undef/swi ... | |
| * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling | |
| */ | |
| .macro bad_save_user_regs | |
| @ carve out a frame on current user stack | |
| sub sp, sp, #S_FRAME_SIZE | |
| stmia sp, {r0 - r12} @ Save user registers (now in svc mode) r0-r12 | |
| ldr r2, IRQ_STACK_START_IN | |
| @ get values for "aborted" pc and cpsr (into parm regs) | |
| ldmia r2, {r2 - r3} | |
| add r0, sp, #S_FRAME_SIZE @ grab pointer to old stack | |
| add r5, sp, #S_SP | |
| mov r1, lr | |
| stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr | |
| mov r0, sp @ save current stack into r0 (param register) | |
| .endm | |
| .macro irq_save_user_regs | |
| sub sp, sp, #S_FRAME_SIZE | |
| stmia sp, {r0 - r12} @ Calling r0-r12 | |
| @ !!!! R8 NEEDS to be saved !!!! a reserved stack spot would be good. | |
| add r8, sp, #S_PC | |
| stmdb r8, {sp, lr}^ @ Calling SP, LR | |
| str lr, [r8, #0] @ Save calling PC | |
| mrs r6, spsr | |
| str r6, [r8, #4] @ Save CPSR | |
| str r0, [r8, #8] @ Save OLD_R0 | |
| mov r0, sp | |
| .endm | |
| .macro irq_restore_user_regs | |
| ldmia sp, {r0 - lr}^ @ Calling r0 - lr | |
| mov r0, r0 | |
| ldr lr, [sp, #S_PC] @ Get PC | |
| add sp, sp, #S_FRAME_SIZE | |
| subs pc, lr, #4 @ return & move spsr_svc into cpsr | |
| .endm | |
| .macro get_bad_stack | |
| ldr r13, IRQ_STACK_START_IN @ setup our mode stack | |
| str lr, [r13] @ save caller lr in position 0 of saved stack | |
| mrs lr, spsr @ get the spsr | |
| str lr, [r13, #4] @ save spsr in position 1 of saved stack | |
| mov r13, #MODE_SVC @ prepare SVC-Mode | |
| @ msr spsr_c, r13 | |
| msr spsr, r13 @ switch modes, make sure moves will execute | |
| mov lr, pc @ capture return pc | |
| movs pc, lr @ jump to next instruction & switch modes. | |
| .endm | |
| .macro get_bad_stack_swi | |
| sub r13, r13, #4 @ space on current stack for scratch reg. | |
| str r0, [r13] @ save R0's value. | |
| ldr r0, IRQ_STACK_START_IN @ get data regions start | |
| str lr, [r0] @ save caller lr in position 0 of saved stack | |
| mrs lr, spsr @ get the spsr | |
| str lr, [r0, #4] @ save spsr in position 1 of saved stack | |
| ldr lr, [r0] @ restore lr | |
| ldr r0, [r13] @ restore r0 | |
| add r13, r13, #4 @ pop stack entry | |
| .endm | |
| .macro get_irq_stack @ setup IRQ stack | |
| ldr sp, IRQ_STACK_START | |
| .endm | |
| .macro get_fiq_stack @ setup FIQ stack | |
| ldr sp, FIQ_STACK_START | |
| .endm | |
| /* | |
| * exception handlers | |
| */ | |
| .align 5 | |
| undefined_instruction: | |
| get_bad_stack | |
| bad_save_user_regs | |
| bl do_undefined_instruction | |
| .align 5 | |
| software_interrupt: | |
| get_bad_stack_swi | |
| bad_save_user_regs | |
| bl do_software_interrupt | |
| #if CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK) | |
| ldmia sp, {r0 - lr}^ @ Calling r0 - lr | |
| mov r0, r0 | |
| ldr lr, [sp, #S_PC] @ Get PC | |
| add sp, sp, #S_FRAME_SIZE | |
| movs pc, lr @ return & move spsr_svc into cpsr | |
| #endif | |
| .align 5 | |
| prefetch_abort: | |
| get_bad_stack | |
| bad_save_user_regs | |
| bl do_prefetch_abort | |
| .align 5 | |
| data_abort: | |
| get_bad_stack | |
| bad_save_user_regs | |
| bl do_data_abort | |
| .align 5 | |
| not_used: | |
| get_bad_stack | |
| bad_save_user_regs | |
| bl do_not_used | |
| .align 5 | |
| irq: | |
| get_bad_stack | |
| bad_save_user_regs | |
| bl do_irq | |
| .align 5 | |
| fiq: | |
| get_bad_stack | |
| bad_save_user_regs | |
| bl do_fiq | |
| #endif /* CONFIG_SPL_BUILD */ |