From 57d78ddd930dd76b7df52e5388a39730c8e5b7ee Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Thu, 11 Sep 2014 14:34:10 -0600 Subject: [PATCH] ARMv7-A: Exception register save/restore needs to work a little differently if we support user mode processes --- TODO | 2 +- arch/arm/src/armv7-a/arm_vectors.S | 394 ++++++++++++++++++++++++++--- 2 files changed, 363 insertions(+), 33 deletions(-) diff --git a/TODO b/TODO index a4253845be..3469b8e3cd 100644 --- a/TODO +++ b/TODO @@ -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 */ diff --git a/arch/arm/src/armv7-a/arm_vectors.S b/arch/arm/src/armv7-a/arm_vectors.S index a5340acb08..bb1cd63f81 100644 --- a/arch/arm/src/armv7-a/arm_vectors.S +++ b/arch/arm/src/armv7-a/arm_vectors.S @@ -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: