nuttx/arch/arm/src/armv7-a/arm_vectors.S

978 lines
30 KiB
ArmAsm

/************************************************************************************
* arch/arm/src/armv7-a/arm_vectors.S
*
* Copyright (C) 2013-2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
************************************************************************************/
/************************************************************************************
* Included Files
************************************************************************************/
#include <nuttx/config.h>
#include <nuttx/irq.h>
#include "arm.h"
#include "cp15.h"
.file "arm_vectors.S"
/************************************************************************************
* Pre-processor Definitions
************************************************************************************/
/************************************************************************************
* Private Data
************************************************************************************/
.data
g_irqtmp:
.word 0 /* Saved lr */
.word 0 /* Saved spsr */
g_undeftmp:
.word 0 /* Saved lr */
.word 0 /* Saved spsr */
g_aborttmp:
.word 0 /* Saved lr */
.word 0 /* Saved spsr */
#ifdef CONFIG_ARMV7A_DECODEFIQ
g_fiqtmp:
.word 0 /* Saved lr */
.word 0 /* Saved spsr */
#endif
/************************************************************************************
* Assembly Macros
************************************************************************************/
/************************************************************************************
* Private Functions
************************************************************************************/
.text
/************************************************************************************
* Public Functions
************************************************************************************/
/************************************************************************************
* Name: arm_vectorirq
*
* Description:
* Interrupt exception. Entered in IRQ mode with spsr = SVC CPSR, lr = SVC PC
*
************************************************************************************/
.globl arm_decodeirq
.globl arm_vectorirq
.type arm_vectorirq, %function
arm_vectorirq:
/* On entry, we are in IRQ mode. We are free to use the IRQ mode r13
* and r14.
*/
ldr r13, .Lirqtmp
sub lr, lr, #4
str lr, [r13] /* Save lr_IRQ */
mrs lr, spsr
str lr, [r13, #4] /* Save spsr_IRQ */
/* Then switch back to SVC mode */
bic lr, lr, #PSR_MODE_MASK /* Keep F and T bits */
#ifdef CONFIG_ARMV7A_DECODEFIQ
orr lr, lr, #(PSR_MODE_SVC | PSR_I_BIT | PSR_F_BIT)
#else
orr lr, lr, #(PSR_MODE_SVC | PSR_I_BIT)
#endif
msr cpsr_c, lr /* Switch to SVC mode */
/* Create a context structure. First set aside a stack frame
* and store r0-r12 into the frame.
*/
sub sp, sp, #XCPTCONTEXT_SIZE
stmia sp, {r0-r12} /* Save the SVC mode regs */
/* Get the values for r15(pc) and CPSR in r3 and r4 */
ldr r0, .Lirqtmp /* Points to temp storage */
ldmia r0, {r3, r4} /* Recover r3=lr_IRQ, r4=spsr_IRQ */
#ifdef CONFIG_BUILD_KERNEL
/* Did we enter from user mode? If so then we need get the values of
* USER mode r13(sp) and r14(lr).
*/
and r1, r4, #PSR_MODE_MASK /* Interrupted mode */
cmp r1, #PSR_MODE_USR /* User mode? */
bne .Lirqentersvc /* Branch if not user mode */
/* ldmia with ^ will return the user mode registers (provided that r15
* is not in the register list).
*/
add r0, sp, #(4*REG_SP) /* Offset to sp/lr storage */
stmia r0, {r13, r14}^ /* Save user mode r13(sp) and r14(lr) */
add r0, sp, #(4*REG_R15) /* Offset to pc/cpsr storage */
stmia r0, {r3, r4} /* Save r15(pc), and the CPSR */
b .Lirqcontinue
.Lirqentersvc:
/* Otherwise, get the correct values of SVC r13(sp) and r14(lr) in r1
* and r2.
*/
add r1, sp, #XCPTCONTEXT_SIZE
mov r2, r14
/* Save r13(sp), r14(lr), r15(pc), and the CPSR */
add r0, sp, #(4*REG_SP) /* Offset to pc, cpsr storage */
stmia r0, {r1-r4}
.Lirqcontinue:
#else
/* Get the correct values of SVC r13(sp) and r14(lr) in r1 and r2 */
add r1, sp, #XCPTCONTEXT_SIZE
mov r2, r14
/* Save r13(sp), r14(lr), r15(pc), and the CPSR */
add r0, sp, #(4*REG_SP) /* Offset to pc, cpsr storage */
stmia r0, {r1-r4}
#endif
/* Then call the IRQ handler with interrupts disabled. */
mov fp, #0 /* Init frame pointer */
mov r0, sp /* Get r0=xcp */
#if CONFIG_ARCH_INTERRUPTSTACK > 3
ldr sp, .Lirqstackbase /* SP = interrupt stack base */
str r0, [sp] /* Save the user stack pointer */
mov r4, sp /* Save the SP in a preserved register */
bic sp, sp, #7 /* Force 8-byte alignment */
bl arm_decodeirq /* Call the handler */
ldr sp, [r4] /* Restore the user stack pointer */
#else
mov r4, sp /* Save the SP in a preserved register */
bic sp, sp, #7 /* Force 8-byte alignment */
bl arm_decodeirq /* Call the handler */
mov sp, r4 /* Restore the possibly unaligned stack pointer */
#endif
/* Upon return from arm_decodeirq, r0 holds the pointer to the register
* state save area to use to restore the registers. This may or may not
* be the same value that was passed to arm_decodeirq: It will differ if a
* context switch is required.
*/
/* Restore the CPSR, SVC mode registers and return */
ldr r1, [r0, #(4*REG_CPSR)] /* Fetch the return SPSR */
msr spsr, r1 /* Set the return mode SPSR */
#ifdef CONFIG_BUILD_KERNEL
/* Are we leaving in user mode? If so then we need to restore the
* values of USER mode r13(sp) and r14(lr).
*/
and r2, r1, #PSR_MODE_MASK /* Interrupted mode */
cmp r2, #PSR_MODE_USR /* User mode? */
bne .Lirqleavesvc /* Branch if not user mode */
/* ldmia with ^ will return the user mode registers (provided that r15
* is not in the register list).
*/
mov r13, r0 /* (SVC) R13=Register storage area */
ldmia r13, {r0-R12} /* Restore common R0-R12 */
add r14, r13, #(4*REG_R13) /* (SVC) R14=address of R13/R14 storage */
ldmia r14, {r13, r14}^ /* Restore user mode R13/R14 */
add r14, r13, #(4*REG_R15) /* (SVC) R14=address of R15 storage */
ldmia r14, {r15}^ /* Return */
.Lirqleavesvc:
#endif
/* Life is simple when everything is SVC mode */
ldmia r0, {r0-r15}^ /* Return */
.Lirqtmp:
.word g_irqtmp
#if CONFIG_ARCH_INTERRUPTSTACK > 3
.Lirqstackbase:
.word g_intstackbase
#endif
.size arm_vectorirq, . - arm_vectorirq
.align 5
/************************************************************************************
* Function: arm_vectorsvc
*
* Description:
* SVC interrupt. We enter the SVC in SVC mode.
*
************************************************************************************/
.globl arm_syscall
.globl arm_vectorsvc
.type arm_vectorsvc, %function
arm_vectorsvc:
/* Create a context structure. First set aside a stack frame
* and store r0-r12 into the frame.
*/
sub sp, sp, #XCPTCONTEXT_SIZE
stmia sp, {r0-r12} /* Save the SVC mode regs */
/* Get the values for r15(pc) and CPSR in r3 and r4 */
mov r3, r14 /* Save r14 as the PC as well */
mrs r4, spsr /* Get the saved CPSR */
#ifdef CONFIG_BUILD_KERNEL
/* Did we enter from user mode? If so then we need get the values of
* USER mode r13(sp) and r14(lr).
*/
and r1, r4, #PSR_MODE_MASK /* Interrupted mode */
cmp r1, #PSR_MODE_USR /* User mode? */
bne .Lsvcentersvc /* Branch if not user mode */
/* ldmia with ^ will return the user mode registers (provided that r15
* is not in the register list).
*/
add r0, sp, #(4*REG_SP) /* Offset to sp/lr storage */
stmia r0, {r13, r14}^ /* Save user mode r13(sp) and r14(lr) */
add r0, sp, #(4*REG_R15) /* Offset to pc/cpsr storage */
stmia r0, {r3, r4} /* Save r15(pc), and the CPSR */
b .Lsvccontinue
.Lsvcentersvc:
/* Otherwise, get the correct values of SVC r13(sp) and r14(lr) in r1
* and r2.
*/
add r1, sp, #XCPTCONTEXT_SIZE
mov r2, r14
/* Save r13(sp), r14(lr), r15(pc), and the CPSR */
add r0, sp, #(4*REG_SP) /* Offset to pc, cpsr storage */
stmia r0, {r1-r4}
.Lsvccontinue:
#else
/* Get the correct values of SVC r13(sp) and r14(lr) in r1 and r2 */
add r1, sp, #XCPTCONTEXT_SIZE
mov r2, r14
/* Save r13(sp), r14(lr), r15(pc), and the CPSR */
add r0, sp, #(4*REG_SP) /* Offset to pc, cpsr storage */
stmia r0, {r1-r4}
#endif
/* Then call the SVC handler with interrupts disabled.
* void arm_syscall(struct xcptcontext *xcp)
*/
mov fp, #0 /* Init frame pointer */
mov r0, sp /* Get r0=xcp */
mov r4, sp /* Save the SP in a preserved register */
bic sp, sp, #7 /* Force 8-byte alignment */
bl arm_syscall /* Call the handler */
mov sp, r4 /* Restore the possibly unaligned stack pointer */
/* Upon return from arm_syscall, r0 holds the pointer to the register
* state save area to use to restore the registers. This may or may not
* be the same value that was passed to arm_syscall: It will differ if a
* context switch is required.
*/
/* Restore the CPSR, SVC mode registers and return */
ldr r1, [r0, #(4*REG_CPSR)] /* Fetch the return SPSR */
msr spsr, r1 /* Set the return mode SPSR */
#ifdef CONFIG_BUILD_KERNEL
/* Are we leaving in user mode? If so then we need to restore the
* values of USER mode r13(sp) and r14(lr).
*/
and r2, r1, #PSR_MODE_MASK /* Interrupted mode */
cmp r2, #PSR_MODE_USR /* User mode? */
bne .Lleavesvcsvc /* Branch if not user mode */
/* ldmia with ^ will return the user mode registers (provided that r15
* is not in the register list).
*/
mov r13, r0 /* (SVC) R13=Register storage area */
ldmia r13, {r0-R12} /* Restore common R0-R12 */
add r14, r13, #(4*REG_R13) /* (SVC) R14=address of R13/R14 storage */
ldmia r14, {r13, r14}^ /* Restore user mode R13/R14 */
add r14, r13, #(4*REG_R15) /* (SVC) R14=address of R15 storage */
ldmia r14, {r15}^ /* Return */
.Lleavesvcsvc:
#endif
/* Life is simple when everything is SVC mode */
ldmia r0, {r0-r15}^ /* Return */
.size arm_vectorsvc, . - arm_vectorsvc
.align 5
/************************************************************************************
* Name: arm_vectordata
*
* Description:
* This is the data abort exception dispatcher. The ARM data abort exception occurs
* when a memory fault is detected during a data transfer. This handler saves the
* current processor state and gives control to data abort handler. This function
* is entered in ABORT mode with spsr = SVC CPSR, lr = SVC PC
*
************************************************************************************/
.globl arm_dataabort
.globl arm_vectordata
.type arm_vectordata, %function
arm_vectordata:
/* On entry we are free to use the ABORT mode registers
* r13 and r14
*/
ldr r13, .Ldaborttmp /* Points to temp storage */
sub lr, lr, #8 /* Fixup return */
str lr, [r13] /* Save in temp storage */
mrs lr, spsr /* Get SPSR */
str lr, [r13, #4] /* Save in temp storage */
/* Then switch back to SVC mode */
bic lr, lr, #PSR_MODE_MASK /* Keep F and T bits */
orr lr, lr, #(PSR_MODE_SVC | PSR_I_BIT | PSR_F_BIT)
msr cpsr_c, lr /* Switch to SVC mode */
/* Create a context structure. First set aside a stack frame
* and store r0-r12 into the frame.
*/
sub sp, sp, #XCPTCONTEXT_SIZE
stmia sp, {r0-r12} /* Save the SVC mode regs */
/* Get the values for r15(pc) and CPSR in r3 and r4 */
ldr r0, .Ldaborttmp /* Points to temp storage */
ldmia r0, {r3, r4} /* Recover r3=lr_ABT, r4=spsr_ABT */
#ifdef CONFIG_BUILD_KERNEL
/* Did we enter from user mode? If so then we need get the values of
* USER mode r13(sp) and r14(lr).
*/
and r1, r4, #PSR_MODE_MASK /* Interrupted mode */
cmp r1, #PSR_MODE_USR /* User mode? */
bne .Ldabtentersvc /* Branch if not user mode */
/* ldmia with ^ will return the user mode registers (provided that r15
* is not in the register list).
*/
add r0, sp, #(4*REG_SP) /* Offset to sp/lr storage */
stmia r0, {r13, r14}^ /* Save user mode r13(sp) and r14(lr) */
add r0, sp, #(4*REG_R15) /* Offset to pc/cpsr storage */
stmia r0, {r3, r4} /* Save r15(pc), and the CPSR */
b .Ldabtcontinue
.Ldabtentersvc:
/* Otherwise, get the correct values of SVC r13(sp) and r14(lr) in r1
* and r2.
*/
add r1, sp, #XCPTCONTEXT_SIZE
mov r2, r14
/* Save r13(sp), r14(lr), r15(pc), and the CPSR */
add r0, sp, #(4*REG_SP) /* Offset to pc, cpsr storage */
stmia r0, {r1-r4}
.Ldabtcontinue:
#else
/* Get the correct values of SVC r13(sp) and r14(lr) in r1 and r2 */
add r1, sp, #XCPTCONTEXT_SIZE
mov r2, r14
/* Save r13(sp), r14(lr), r15(pc), and the CPSR */
add r0, sp, #(4*REG_SP) /* Offset to pc, cpsr storage */
stmia r0, {r1-r4}
#endif
/* Then call the data abort handler with interrupts disabled.
* void arm_dataabort(struct xcptcontext *xcp)
*/
mov fp, #0 /* Init frame pointer */
mov r0, sp /* Get r0=xcp */
mrc CP15_DFAR(r1) /* Get R1=DFAR */
mrc CP15_DFSR(r2) /* Get r2=DFSR */
mov r4, sp /* Save the SP in a preserved register */
bic sp, sp, #7 /* Force 8-byte alignment */
bl arm_dataabort /* Call the handler */
mov sp, r4 /* Restore the possibly unaligned stack pointer */
/* Upon return from arm_dataabort, r0 holds the pointer to the register
* state save area to use to restore the registers. This may or may not
* be the same value that was passed to arm_dataabort: It will differ if a
* context switch is required.
*/
/* Restore the CPSR, SVC mode registers and return */
ldr r1, [r0, #(4*REG_CPSR)] /* Fetch the return SPSR */
msr spsr_cxsf, r1 /* Set the return mode SPSR */
#ifdef CONFIG_BUILD_KERNEL
/* Are we leaving in user mode? If so then we need to restore the
* values of USER mode r13(sp) and r14(lr).
*/
and r2, r1, #PSR_MODE_MASK /* Interrupted mode */
cmp r2, #PSR_MODE_USR /* User mode? */
bne .Ldabtleavesvc /* Branch if not user mode */
/* ldmia with ^ will return the user mode registers (provided that r15
* is not in the register list).
*/
mov r13, r0 /* (SVC) R13=Register storage area */
ldmia r13, {r0-R12} /* Restore common R0-R12 */
add r14, r13, #(4*REG_R13) /* (SVC) R14=address of R13/R14 storage */
ldmia r14, {r13, r14}^ /* Restore user mode R13/R14 */
add r14, r13, #(4*REG_R15) /* (SVC) R14=address of R15 storage */
ldmia r14, {r15}^ /* Return */
.Ldabtleavesvc:
#endif
/* Life is simple when everything is SVC mode */
ldmia r0, {r1-r15}^ /* Return */
.Ldaborttmp:
.word g_aborttmp
.size arm_vectordata, . - arm_vectordata
.align 5
/************************************************************************************
* Name: arm_vectorprefetch
*
* Description:
* This is the prefetch abort exception dispatcher. The ARM prefetch abort exception
* occurs when a memory fault is detected during an an instruction fetch. This
* handler saves the current processor state and gives control to prefetch abort
* handler. This function is entered in ABT mode with spsr = SVC CPSR, lr = SVC PC.
*
************************************************************************************/
.globl arm_prefetchabort
.globl arm_vectorprefetch
.type arm_vectorprefetch, %function
arm_vectorprefetch:
/* On entry we are free to use the ABORT mode registers
* r13 and r14
*/
ldr r13, .Lpaborttmp /* Points to temp storage */
sub lr, lr, #4 /* Fixup return */
str lr, [r13] /* Save in temp storage */
mrs lr, spsr /* Get SPSR */
str lr, [r13, #4] /* Save in temp storage */
/* Then switch back to SVC mode */
bic lr, lr, #PSR_MODE_MASK /* Keep F and T bits */
orr lr, lr, #(PSR_MODE_SVC | PSR_I_BIT | PSR_F_BIT)
msr cpsr_c, lr /* Switch to SVC mode */
/* Create a context structure. First set aside a stack frame
* and store r0-r12 into the frame.
*/
sub sp, sp, #XCPTCONTEXT_SIZE
stmia sp, {r0-r12} /* Save the SVC mode regs */
/* Get the values for r15(pc) and CPSR in r3 and r4 */
ldr r0, .Lpaborttmp /* Points to temp storage */
ldmia r0, {r3, r4} /* Recover r3=lr_ABT, r4=spsr_ABT */
#ifdef CONFIG_BUILD_KERNEL
/* Did we enter from user mode? If so then we need get the values of
* USER mode r13(sp) and r14(lr).
*/
and r1, r4, #PSR_MODE_MASK /* Interrupted mode */
cmp r1, #PSR_MODE_USR /* User mode? */
bne .Lpabtentersvc /* Branch if not user mode */
/* ldmia with ^ will return the user mode registers (provided that r15
* is not in the register list).
*/
add r0, sp, #(4*REG_SP) /* Offset to sp/lr storage */
stmia r0, {r13, r14}^ /* Save user mode r13(sp) and r14(lr) */
add r0, sp, #(4*REG_R15) /* Offset to pc/cpsr storage */
stmia r0, {r3, r4} /* Save r15(pc), and the CPSR */
b .Lpabtcontinue
.Lpabtentersvc:
/* Otherwise, get the correct values of SVC r13(sp) and r14(lr) in r1
* and r2.
*/
add r1, sp, #XCPTCONTEXT_SIZE
mov r2, r14
/* Save r13(sp), r14(lr), r15(pc), and the CPSR */
add r0, sp, #(4*REG_SP) /* Offset to pc, cpsr storage */
stmia r0, {r1-r4}
.Lpabtcontinue:
#else
/* Get the correct values of SVC r13(sp) and r14(lr) in r1 and r2 */
add r1, sp, #XCPTCONTEXT_SIZE
mov r2, r14
/* Save r13(sp), r14(lr), r15(pc), and the CPSR */
add r0, sp, #(4*REG_SP) /* Offset to pc, cpsr storage */
stmia r0, {r1-r4}
#endif
/* Then call the prefetch abort handler with interrupts disabled.
* void arm_prefetchabort(struct xcptcontext *xcp)
*/
mov fp, #0 /* Init frame pointer */
mov r0, sp /* Get r0=xcp */
mrc CP15_IFAR(r1) /* Get R1=IFAR */
mrc CP15_IFSR(r2) /* Get r2=IFSR */
mov r4, sp /* Save the SP in a preserved register */
bic sp, sp, #7 /* Force 8-byte alignment */
bl arm_prefetchabort /* Call the handler */
mov sp, r4 /* Restore the possibly unaligned stack pointer */
/* Upon return from arm_prefetchabort, r0 holds the pointer to the register
* state save area to use to restore the registers. This may or may not
* be the same value that was passed to arm_prefetchabort: It will differ if a
* context switch is required.
*/
/* Restore the CPSR, SVC mode registers and return */
ldr r1, [r0, #(4*REG_CPSR)] /* Fetch the return SPSR */
msr spsr_cxsf, r1 /* Set the return mode SPSR */
#ifdef CONFIG_BUILD_KERNEL
/* Are we leaving in user mode? If so then we need to restore the
* values of USER mode r13(sp) and r14(lr).
*/
and r2, r1, #PSR_MODE_MASK /* Interrupted mode */
cmp r2, #PSR_MODE_USR /* User mode? */
bne .Lpabtleavesvc /* Branch if not user mode */
/* ldmia with ^ will return the user mode registers (provided that r15
* is not in the register list).
*/
mov r13, r0 /* (SVC) R13=Register storage area */
ldmia r13, {r0-R12} /* Restore common R0-R12 */
add r14, r13, #(4*REG_R13) /* (SVC) R14=address of R13/R14 storage */
ldmia r14, {r13, r14}^ /* Restore user mode R13/R14 */
add r14, r13, #(4*REG_R15) /* (SVC) R14=address of R15 storage */
ldmia r14, {r15}^ /* Return */
.Lpabtleavesvc:
#endif
/* Life is simple when everything is SVC mode */
ldmia r0, {r0-r15}^ /* Return */
.Lpaborttmp:
.word g_aborttmp
.size arm_vectorprefetch, . - arm_vectorprefetch
.align 5
/************************************************************************************
* Name: arm_vectorundefinsn
*
* Description:
* Undefined instruction entry exception. Entered in UND mode, spsr = SVC CPSR,
* lr = SVC PC
*
************************************************************************************/
.globl arm_undefinedinsn
.globl arm_vectorundefinsn
.type arm_vectorundefinsn, %function
arm_vectorundefinsn:
/* On entry we are free to use the UND mode registers
* r13 and r14
*/
ldr r13, .Lundeftmp /* Points to temp storage */
str lr, [r13] /* Save in temp storage */
mrs lr, spsr /* Get SPSR */
str lr, [r13, #4] /* Save in temp storage */
/* Then switch back to SVC mode */
bic lr, lr, #PSR_MODE_MASK /* Keep F and T bits */
orr lr, lr, #(PSR_MODE_SVC | PSR_I_BIT | PSR_F_BIT)
msr cpsr_c, lr /* Switch to SVC mode */
/* Create a context structure. First set aside a stack frame
* and store r0-r12 into the frame.
*/
sub sp, sp, #XCPTCONTEXT_SIZE
stmia sp, {r0-r12} /* Save the SVC mode regs */
/* Get the values for r15(pc) and CPSR in r3 and r4 */
ldr r0, .Lundeftmp /* Points to temp storage */
ldmia r0, {r3, r4} /* Recover r3=lr_UND, r4=spsr_UND */
#ifdef CONFIG_BUILD_KERNEL
/* Did we enter from user mode? If so then we need get the values of
* USER mode r13(sp) and r14(lr).
*/
and r1, r4, #PSR_MODE_MASK /* Interrupted mode */
cmp r1, #PSR_MODE_USR /* User mode? */
bne .Lundefentersvc /* Branch if not user mode */
/* ldmia with ^ will return the user mode registers (provided that r15
* is not in the register list).
*/
add r0, sp, #(4*REG_SP) /* Offset to sp/lr storage */
stmia r0, {r13, r14}^ /* Save user mode r13(sp) and r14(lr) */
add r0, sp, #(4*REG_R15) /* Offset to pc/cpsr storage */
stmia r0, {r3, r4} /* Save r15(pc), and the CPSR */
b .Lundefcontinue
.Lundefentersvc:
/* Otherwise, get the correct values of SVC r13(sp) and r14(lr) in r1
* and r2.
*/
add r1, sp, #XCPTCONTEXT_SIZE
mov r2, r14
/* Save r13(sp), r14(lr), r15(pc), and the CPSR */
add r0, sp, #(4*REG_SP) /* Offset to pc, cpsr storage */
stmia r0, {r1-r4}
.Lundefcontinue:
#else
/* Get the correct values of SVC r13(sp) and r14(lr) in r1 and r2 */
add r1, sp, #XCPTCONTEXT_SIZE
mov r2, r14
/* Save r13(sp), r14(lr), r15(pc), and the CPSR */
add r0, sp, #(4*REG_SP) /* Offset to pc, cpsr storage */
stmia r0, {r1-r4}
#endif
/* Then call the undef insn handler with interrupts disabled.
* void arm_undefinedinsn(struct xcptcontext *xcp)
*/
mov fp, #0 /* Init frame pointer */
mov r0, sp /* Get r0=xcp */
mov r4, sp /* Save the SP in a preserved register */
bic sp, sp, #7 /* Force 8-byte alignment */
bl arm_undefinedinsn /* Call the handler */
mov sp, r4 /* Restore the possibly unaligned stack pointer */
/* Upon return from arm_undefinedinsn, r0 holds the pointer to the register
* state save area to use to restore the registers. This may or may not
* be the same value that was passed to arm_undefinedinsn: It will differ if a
* context switch is required.
*/
/* Restore the CPSR, SVC mode registers and return */
ldr r1, [r0, #(4*REG_CPSR)] /* Fetch the return SPSR */
msr spsr_cxsf, r1 /* Set the return mode SPSR */
#ifdef CONFIG_BUILD_KERNEL
/* Are we leaving in user mode? If so then we need to restore the
* values of USER mode r13(sp) and r14(lr).
*/
and r2, r1, #PSR_MODE_MASK /* Interrupted mode */
cmp r2, #PSR_MODE_USR /* User mode? */
bne .Lundefleavesvc /* Branch if not user mode */
/* ldmia with ^ will return the user mode registers (provided that r15
* is not in the register list).
*/
mov r13, r0 /* (SVC) R13=Register storage area */
ldmia r13, {r0-R12} /* Restore common R0-R12 */
add r14, r13, #(4*REG_R13) /* (SVC) R14=address of R13/R14 storage */
ldmia r14, {r13, r14}^ /* Restore user mode R13/R14 */
add r14, r13, #(4*REG_R15) /* (SVC) R14=address of R15 storage */
ldmia r14, {r15}^ /* Return */
.Lundefleavesvc:
#endif
/* Life is simple when everything is SVC mode */
ldmia r0, {r0-r15}^ /* Return */
.Lundeftmp:
.word g_undeftmp
.size arm_vectorundefinsn, . - arm_vectorundefinsn
.align 5
/************************************************************************************
* Name: arm_vectorfiq
*
* Description:
* Shouldn't happen unless a arm_decodefiq() is provided. FIQ is primarily used
* with the TrustZone feature in order to handle secure interrupts.
*
************************************************************************************/
#ifdef CONFIG_ARMV7A_DECODEFIQ
.globl arm_decodefiq
#endif
.globl arm_vectorfiq
.type arm_vectorfiq, %function
arm_vectorfiq:
#ifdef CONFIG_ARMV7A_DECODEFIQ
/* On entry we are free to use the FIQ mode registers r8 through r14 */
ldr r13, .Lfiqtmp /* Points to temp storage */
sub lr, lr, #4 /* Fixup return */
str lr, [r13] /* Save in temp storage */
mrs lr, spsr /* Get SPSR_fiq */
str lr, [r13, #4] /* Save in temp storage */
/* Then switch back to SVC mode */
bic lr, lr, #PSR_MODE_MASK /* Keep F and T bits */
orr lr, lr, #(PSR_MODE_SVC | PSR_I_BIT | PSR_F_BIT)
msr cpsr_c, lr /* Switch to SVC mode */
/* Create a context structure. First set aside a stack frame
* and store r0-r12 into the frame.
*/
sub sp, sp, #XCPTCONTEXT_SIZE
stmia sp, {r0-r12} /* Save the SVC mode regs */
/* Get the values for r15(pc) and CPSR in r3 and r4 */
ldr r0, .Lfiqtmp /* Points to temp storage */
ldmia r0, {r3, r4} /* Recover r3=lr_SVC, r4=spsr_SVC */
#ifdef CONFIG_BUILD_KERNEL
/* Did we enter from user mode? If so then we need get the values of
* USER mode rr13(sp) and r14(lr).
*/
and r1, r4, #PSR_MODE_MASK /* Interrupted mode */
cmp r1, #PSR_MODE_USR /* User mode? */
bne .Lfiqentersvc /* Branch if not user mode */
/* ldmia with ^ will return the user mode registers (provided that r15
* is not in the register list).
*/
add r0, sp, #(4*REG_SP) /* Offset to sp/lr storage */
stmia r0, {r13, r14}^ /* Save user mode r13(sp) and r14(lr) */
add r0, sp, #(4*REG_R15) /* Offset to pc/cpsr storage */
stmia r0, {r3, r4} /* Save r15(pc), and the CPSR */
b .Lfiqcontinue
.Lfiqentersvc:
/* Otherwise, get the correct values of SVC r13(sp) and r14(lr) in r1
* and r2.
*/
add r1, sp, #XCPTCONTEXT_SIZE
mov r2, r14
/* Save r13(sp), r14(lr), r15(pc), and the CPSR */
add r0, sp, #(4*REG_SP) /* Offset to pc, cpsr storage */
stmia r0, {r1-r4}
.Lfiqcontinue:
#else
/* Get the correct values of SVC r13(sp) and r14(lr) in r1 and r2 */
add r1, sp, #XCPTCONTEXT_SIZE
mov r2, r14
/* Save r13(sp), r14(lr), r15(pc), and the CPSR */
add r0, sp, #(4*REG_SP) /* Offset to pc, cpsr storage */
stmia r0, {r1-r4}
#endif
/* Then call the IRQ handler with interrupts disabled. */
mov fp, #0 /* Init frame pointer */
mov r0, sp /* Get r0=xcp */
#if CONFIG_ARCH_INTERRUPTSTACK > 3
ldr sp, .Lfiqstackbase /* SP = interrupt stack base */
str r0, [sp] /* Save the user stack pointer */
mov r4, sp /* Save the SP in a preserved register */
bic sp, sp, #7 /* Force 8-byte alignment */
bl arm_decodefiq /* Call the handler */
ldr sp, [r4] /* Restore the user stack pointer */
#else
mov r4, sp /* Save the SP in a preserved register */
bic sp, sp, #7 /* Force 8-byte alignment */
bl arm_decodefiq /* Call the handler */
mov sp, r4 /* Restore the possibly unaligned stack pointer */
#endif
/* Upon return from arm_decodefiq, r0 holds the pointer to the register
* state save area to use to restore the registers. This may or may not
* be the same value that was passed to arm_decodefiq: It will differ if a
* context switch is required.
*/
/* Restore the CPSR, SVC mode registers and return */
ldr r1, [r0, #(4*REG_CPSR)] /* Fetch the return SPSR */
msr spsr, r1 /* Set the return mode SPSR */
#ifdef CONFIG_BUILD_KERNEL
/* Are we leaving in user mode? If so then we need to restore the
* values of USER mode r13(sp) and r14(lr).
*/
and r2, r1, #PSR_MODE_MASK /* Interrupted mode */
cmp r2, #PSR_MODE_USR /* User mode? */
bne .Lfiqleavesvc /* Branch if not user mode */
/* ldmia with ^ will return the user mode registers (provided that r15
* is not in the register list).
*/
mov r13, r0 /* (SVC) R13=Register storage area */
ldmia r13, {r0-R12} /* Restore common R0-R12 */
add r14, r13, #(4*REG_R13) /* (SVC) R14=address of R13/R14 storage */
ldmia r14, {r13, r14}^ /* Restore user mode R13/R14 */
add r14, r13, #(4*REG_R15) /* (SVC) R14=address of R15 storage */
ldmia r14, {r15}^ /* Return */
.Lfiqleavesvc:
#endif
/* Life is simple when everything is SVC mode */
ldmia r0, {r0-r15}^ /* Return */
.Lfiqtmp:
.word g_fiqtmp
#if CONFIG_ARCH_INTERRUPTSTACK > 3
.Lfiqstackbase:
.word g_intstackbase
#endif
#else
subs pc, lr, #4
#endif
.size arm_vectorfiq, . - arm_vectorfiq
/************************************************************************************
* Name: g_intstackalloc/g_intstackbase
************************************************************************************/
#if CONFIG_ARCH_INTERRUPTSTACK > 3
.bss
.align 4
.globl g_intstackalloc
.type g_intstackalloc, object
.globl g_intstackbase
.type g_intstackbase, object
g_intstackalloc:
.skip ((CONFIG_ARCH_INTERRUPTSTACK & ~3) - 4)
g_intstackbase:
.skip 4
.size g_intstackbase, 4
.size g_intstackalloc, (CONFIG_ARCH_INTERRUPTSTACK & ~3)
#endif /* CONFIG_ARCH_INTERRUPTSTACK > 3 */
.end