ARMv7-A: Exception register save/restore needs to work a little differently if we support user mode processes

This commit is contained in:
Gregory Nutt 2014-09-11 14:34:10 -06:00
parent 17e798993d
commit 57d78ddd93
2 changed files with 363 additions and 33 deletions

2
TODO
View File

@ -1439,7 +1439,7 @@ o ARM (arch/arm/)
Priority: Low
Title: CORTEX-M3 STACK OVERFLOW
Description: There is bit bit logic inf up_fullcontextrestore() that executes on
Description: There is bit bit logic in up_fullcontextrestore() that executes on
return from interrupts (and other context switches) that looks like:
ldr r1, [r0, #(4*REG_CPSR)] /* Fetch the stored CPSR value */

View File

@ -57,6 +57,11 @@
g_irqtmp:
.word 0 /* Saved lr */
.word 0 /* Saved spsr */
#ifdef CONFIG_BUILD_KERNEL
g_svctmp:
.word 0 /* User R13 */
.word 0 /* User R14 */
#endif
g_undeftmp:
.word 0 /* Saved lr */
.word 0 /* Saved spsr */
@ -123,15 +128,46 @@ arm_vectorirq:
sub sp, sp, #XCPTCONTEXT_SIZE
stmia sp, {r0-r12} /* Save the SVC mode regs */
/* Get the correct values of r13(sp) and r14(lr) in r1 and r2 */
/* 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) in r1 and r2.
*/
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).
*/
stmia r0, {r13, r14}^ /* Get user mode R13/R14 into scratch area */
ldmia r0, {r1, r2} /* Then reload into R1 and R2 */
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
/* Get the values for r15(pc) and CPSR in r3 and r4 */
.Lirqcontinue:
ldr r0, .Lirqtmp /* Points to temp storage */
ldmia r0, {r3, r4} /* Recover r1=lr_IRQ, r2=spsr_IRQ */
#else
/* Get the correct values of SVC r13(sp) and r14(lr) in r1 and r2 */
add r1, sp, #XCPTCONTEXT_SIZE
mov r2, r14
#endif
/* 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}
@ -163,8 +199,30 @@ arm_vectorirq:
/* Restore the CPSR, SVC mode registers and return */
ldr r1, [r0, #(4*REG_CPSR)] /* Setup the SVC mode SPSR */
msr spsr, r1
ldr r1, [r0, #(4*REG_CPSR)] /* Setup the return SPSR */
msr spsr, r1 /* Establish the return mode SPSR */
#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) in r1 and r2.
*/
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).
*/
add r1, r0, #(4*REG_R13) /* R1=address of R13/R14 storage */
ldmia r1, {r13, r14}^ /* Restore to user mode USER R13/R14 */
ldmia r0, {r0-R12,r15}^ /* Return */
.Lirqleavesvc:
#endif
/* Life is simple when everything is SVC mode */
ldmia r0, {r0-r15}^ /* Return */
.Lirqtmp:
@ -197,14 +255,47 @@ arm_vectorsvc:
sub sp, sp, #XCPTCONTEXT_SIZE
stmia sp, {r0-r12} /* Save the SVC mode regs */
/* Get the correct values of r13(sp), r14(lr), r15(pc)
* and CPSR in r1-r4.
/* 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) in r1 and r2.
*/
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).
*/
ldr r0, .Lsvctmp /* Points to temp storage */
stmia r0, {r13, r14}^ /* Get user mode R13/R14 into temp storage */
ldmia r0, {r1, r2} /* Then reload into R1 and R2 */
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 /* R14 is altered on return from SVC */
mov r3, r14 /* Save r14 as the PC as well */
mrs r4, spsr /* Get the saved CPSR */
mov r2, r14
.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
#endif
/* 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}
@ -229,8 +320,35 @@ arm_vectorsvc:
/* Restore the CPSR, SVC mode registers and return */
ldr r1, [r0, #(4*REG_CPSR)] /* Setup the SVC mode SPSR */
msr spsr, r1
msr spsr, r1 /* Establish the return mode SPSR */
#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) in r1 and r2.
*/
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).
*/
add r1, r0, #(4*REG_R13) /* R1=address of R13/R14 storage */
ldmia r1, {r13, r14}^ /* Restore to user mode USER R13/R14 */
ldmia r0, {r0-R12,r15}^ /* Return */
.Lleavesvcsvc:
#endif
/* Life is simple when everything is SVC mode */
ldmia r0, {r0-r15}^ /* Return */
#ifdef CONFIG_BUILD_KERNEL
.Lsvctmp:
.word g_svctmp
#endif
.size arm_vectorsvc, . - arm_vectorsvc
.align 5
@ -274,15 +392,46 @@ arm_vectordata:
sub sp, sp, #XCPTCONTEXT_SIZE
stmia sp, {r0-r12} /* Save the SVC mode regs */
/* Get the correct values of r13(sp) and r14(lr) in r1 and r2 */
/* 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) in r1 and r2.
*/
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).
*/
stmia r0, {r13, r14}^ /* Get user mode R13/R14 into scratch area */
ldmia r0, {r1, r2} /* Then reload into R1 and R2 */
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
/* Get the values for r15(pc) and CPSR in r3 and r4 */
.Ldabtcontinue:
ldr r0, .Ldaborttmp /* Points to temp storage */
ldmia r0, {r3, r4} /* Recover r1=lr_IRQ, r2=spsr_IRQ */
#else
/* Get the correct values of SVC r13(sp) and r14(lr) in r1 and r2 */
add r1, sp, #XCPTCONTEXT_SIZE
mov r2, r14
#endif
/* 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}
@ -309,7 +458,29 @@ arm_vectordata:
/* Restore the CPSR, SVC mode registers and return */
ldr r1, [r0, #(4*REG_CPSR)] /* Setup the SVC mode SPSR */
msr spsr_cxsf, r1
msr spsr_cxsf, r1 /* Establish the return mode SPSR */
#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) in r1 and r2.
*/
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).
*/
add r1, r0, #(4*REG_R13) /* R1=address of R13/R14 storage */
ldmia r1, {r13, r14}^ /* Restore to user mode USER R13/R14 */
ldmia r0, {r0-R12,r15}^ /* Return */
.Ldabtleavesvc:
#endif
/* Life is simple when everything is SVC mode */
ldmia r0, {r1-r15}^ /* Return */
.Ldaborttmp:
@ -357,15 +528,46 @@ arm_vectorprefetch:
sub sp, sp, #XCPTCONTEXT_SIZE
stmia sp, {r0-r12} /* Save the SVC mode regs */
/* Get the correct values of r13(sp) and r14(lr) in r1 and r2 */
/* 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) in r1 and r2.
*/
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).
*/
stmia r0, {r13, r14}^ /* Get user mode R13/R14 into scratch area */
ldmia r0, {r1, r2} /* Then reload into R1 and R2 */
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
/* Get the values for r15(pc) and CPSR in r3 and r4 */
.Lpabtcontinue:
ldr r0, .Lpaborttmp /* Points to temp storage */
ldmia r0, {r3, r4} /* Recover r1=lr_IRQ, r2=spsr_IRQ */
#else
/* Get the correct values of SVC r13(sp) and r14(lr) in r1 and r2 */
add r1, sp, #XCPTCONTEXT_SIZE
mov r2, r14
#endif
/* 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}
@ -392,7 +594,29 @@ arm_vectorprefetch:
/* Restore the CPSR, SVC mode registers and return */
ldr r1, [r0, #(4*REG_CPSR)] /* Setup the SVC mode SPSR */
msr spsr_cxsf, r1
msr spsr_cxsf, r1 /* Establish the return mode SPSR */
#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) in r1 and r2.
*/
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).
*/
add r1, r0, #(4*REG_R13) /* R1=address of R13/R14 storage */
ldmia r1, {r13, r14}^ /* Restore to user mode USER R13/R14 */
ldmia r0, {r0-R12,r15}^ /* Return */
.Lpabtleavesvc:
#endif
/* Life is simple when everything is SVC mode */
ldmia r0, {r0-r15}^ /* Return */
.Lpaborttmp:
@ -437,15 +661,46 @@ arm_vectorundefinsn:
sub sp, sp, #XCPTCONTEXT_SIZE
stmia sp, {r0-r12} /* Save the SVC mode regs */
/* Get the correct values of r13(sp) and r14(lr) in r1 and r2 */
/* 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) in r1 and r2.
*/
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).
*/
stmia r0, {r13, r14}^ /* Get user mode R13/R14 into scratch area */
ldmia r0, {r1, r2} /* Then reload into R1 and R2 */
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
/* Get the values for r15(pc) and CPSR in r3 and r4 */
.Lundefcontinue:
ldr r0, .Lundeftmp /* Points to temp storage */
ldmia r0, {r3, r4} /* Recover r1=lr_IRQ, r2=spsr_IRQ */
#else
/* Get the correct values of SVC r13(sp) and r14(lr) in r1 and r2 */
add r1, sp, #XCPTCONTEXT_SIZE
mov r2, r14
#endif
/* 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}
@ -470,7 +725,29 @@ arm_vectorundefinsn:
/* Restore the CPSR, SVC mode registers and return */
ldr r1, [r0, #(4*REG_CPSR)] /* Setup the SVC mode SPSR */
msr spsr_cxsf, r1
msr spsr_cxsf, r1 /* Establish the return mode SPSR */
#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) in r1 and r2.
*/
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).
*/
add r1, r0, #(4*REG_R13) /* R1=address of R13/R14 storage */
ldmia r1, {r13, r14}^ /* Restore to user mode USER R13/R14 */
ldmia r0, {r0-R12,r15}^ /* Return */
.Lundefleavesvc:
#endif
/* Life is simple when everything is SVC mode */
ldmia r0, {r0-r15}^ /* Return */
.Lundeftmp:
@ -517,15 +794,46 @@ arm_vectorfiq:
sub sp, sp, #XCPTCONTEXT_SIZE
stmia sp, {r0-r12} /* Save the SVC mode regs */
/* Get the correct values of r13(sp) and r14(lr) in r1 and r2 */
/* 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 r13(sp) and r14(lr) in r1 and r2.
*/
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).
*/
stmia r0, {r13, r14}^ /* Get user mode R13/R14 into scratch area */
ldmia r0, {r1, r2} /* Then reload into R1 and R2 */
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
/* Get the values for r15(pc) and CPSR in r3 and r4 */
.Lfiqcontinue:
ldr r0, .Lfiqtmp /* Points to temp storage */
ldmia r0, {r3, r4} /* Recover r1=lr_IRQ, r2=spsr_IRQ */
#else
/* Get the correct values of SVC r13(sp) and r14(lr) in r1 and r2 */
add r1, sp, #XCPTCONTEXT_SIZE
mov r2, r14
#endif
/* 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}
@ -558,7 +866,29 @@ arm_vectorfiq:
/* Restore the CPSR, SVC mode registers and return */
ldr r1, [r0, #(4*REG_CPSR)] /* Setup the SVC mode SPSR */
msr spsr, r1
msr spsr, r1 /* Establish the return mode SPSR */
#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) in r1 and r2.
*/
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).
*/
add r1, r0, #(4*REG_R13) /* R1=address of R13/R14 storage */
ldmia r1, {r13, r14}^ /* Restore to user mode USER R13/R14 */
ldmia r0, {r0-R12,r15}^ /* Return */
.Lfiqleavesvc:
#endif
/* Life is simple when everything is SVC mode */
ldmia r0, {r0-r15}^ /* Return */
.Lfiqtmp: