diff --git a/arch/arm/src/armv7-m/Kconfig b/arch/arm/src/armv7-m/Kconfig index 0dbf6a342a770..17fe61b6c60ae 100644 --- a/arch/arm/src/armv7-m/Kconfig +++ b/arch/arm/src/armv7-m/Kconfig @@ -151,3 +151,15 @@ config ARMV7M_SYSTICK_IRQ_THREAD_STACK_SIZE default DEFAULT_TASK_STACKSIZE endif # ARMV7M_SYSTICK_IRQ_WQUEUE + +config ARMV7M_SP_CONTEXT_RESTORE + bool "Honor SP modifications in signal context" + default n + ---help--- + On ARMv7-M, the hardware exception return determines the + final SP from the physical location of the HW exception + frame, ignoring any software modification to REG_R13 in + the saved context. Enable this to relocate the HW frame + when the desired SP differs from the implied SP, allowing + signal handlers to modify SP (e.g., for managed runtime + stack unwinding). diff --git a/arch/arm/src/armv7-m/arm_exception.S b/arch/arm/src/armv7-m/arm_exception.S index d209e4cf853e4..32a700300f053 100644 --- a/arch/arm/src/armv7-m/arm_exception.S +++ b/arch/arm/src/armv7-m/arm_exception.S @@ -221,6 +221,79 @@ exception_common: addne r0, #(4*SW_FPU_REGS) #endif +#ifdef CONFIG_ARMV7M_SP_CONTEXT_RESTORE + /* Check if the desired SP (r2) differs from the implied SP. + * The implied SP after exception return = r0 + hw_frame_size, + * where hw_frame_size depends on whether the frame includes FPU + * state (determined by EXC_RETURN bit 4 in r14). + * If they differ, we must relocate the HW frame so that the final + * SP after hardware pop equals the desired value (r2). + */ + +#ifdef CONFIG_ARCH_FPU + /* Determine actual HW frame size from EXC_RETURN: + * bit 4 set (STD_CONTEXT): 8 words = 32 bytes (no FPU) + * bit 4 clear: 26 words = 104 bytes (with FPU) + */ + + tst r14, #EXC_RETURN_STD_CONTEXT + ite ne + movne r1, #(4*HW_INT_REGS) /* Standard frame: 32 bytes */ + moveq r1, #HW_XCPT_SIZE /* Extended frame: 104 bytes */ +#else + mov r1, #HW_XCPT_SIZE /* Always 32 bytes without FPU */ +#endif + /* r1 = actual HW frame size in bytes */ + + add r1, r0, r1 /* r1 = implied SP = r0 + frame_size */ + cmp r2, r1 /* desired SP == implied SP? */ + beq .Lno_sp_relocate /* Yes, skip relocation */ + + /* Relocate HW exception frame from r0 to (r2 - frame_size). + * frame_size = r1 - r0. We have r0 (source), r2 (desired SP). + * r4-r11 are already restored. Available scratch: r1, r2, r3. + * r3 holds basepri which we need to preserve — push to MSP. + * + * Copy direction matters for overlapping regions: + * dest < source: forward copy (low to high) + * dest > source: backward copy (high to low) + */ + + push {r3} /* Save basepri on MSP */ + sub r3, r1, r0 /* r3 = frame_size = implied_SP - src */ + sub r2, r2, r3 /* r2 = dest = desired_SP - frame_size */ + cmp r2, r0 /* dest vs source? */ + bhs .Lsp_copy_backward /* dest >= source: copy backward */ + + /* Forward copy (dest < source) */ + + push {r2} /* Save dest start for later */ +.Lsp_copy_fwd: + ldr r1, [r0], #4 + str r1, [r2], #4 + subs r3, r3, #4 + bne .Lsp_copy_fwd + pop {r0} /* r0 = dest start = relocated frame */ + pop {r3} /* Restore basepri */ + b .Lno_sp_relocate + +.Lsp_copy_backward: + /* Backward copy (dest >= source) */ + + push {r2} /* Save dest start for later */ + add r0, r0, r3 /* r0 = source end (one past) */ + add r2, r2, r3 /* r2 = dest end (one past) */ +.Lsp_copy_bwd: + ldr r1, [r0, #-4]! /* Pre-decrement, load */ + str r1, [r2, #-4]! /* Pre-decrement, store */ + subs r3, r3, #4 + bne .Lsp_copy_bwd + pop {r0} /* r0 = dest start = relocated frame */ + pop {r3} /* Restore basepri */ + +.Lno_sp_relocate: +#endif /* CONFIG_ARMV7M_SP_CONTEXT_RESTORE */ + /* The EXC_RETURN value tells us whether we are returning on the MSP or PSP */ diff --git a/arch/arm/src/armv8-m/Kconfig b/arch/arm/src/armv8-m/Kconfig index 6d11fb07ac70e..dbd4a58be4629 100644 --- a/arch/arm/src/armv8-m/Kconfig +++ b/arch/arm/src/armv8-m/Kconfig @@ -155,3 +155,15 @@ config ARMV8M_CMSE bool "ARMv8-M Security Extensions" ---help--- Enable ARMv8-M Security Extensions. + +config ARMV8M_SP_CONTEXT_RESTORE + bool "Honor SP modifications in signal context" + default n + ---help--- + On ARMv8-M, the hardware exception return determines the + final SP from the physical location of the HW exception + frame, ignoring any software modification to REG_R13 in + the saved context. Enable this to relocate the HW frame + when the desired SP differs from the implied SP, allowing + signal handlers to modify SP (e.g., for managed runtime + stack unwinding). diff --git a/arch/arm/src/armv8-m/arm_exception.S b/arch/arm/src/armv8-m/arm_exception.S index 5c54747c1fff0..06f61af12b359 100644 --- a/arch/arm/src/armv8-m/arm_exception.S +++ b/arch/arm/src/armv8-m/arm_exception.S @@ -235,6 +235,78 @@ exception_common: ldmia r0!, {r1} /* Get psplim/msplim */ #endif +#ifdef CONFIG_ARMV8M_SP_CONTEXT_RESTORE + /* Check if the desired SP (r2) differs from the implied SP. + * The implied SP after exception return = r0 + hw_frame_size, + * where hw_frame_size depends on whether the frame includes FPU + * state (determined by EXC_RETURN bit 4 in r14). + * If they differ, we must relocate the HW frame so that the final + * SP after hardware pop equals the desired value (r2). + */ + + push {r1, r3} /* Save psplim + basepri on MSP */ + +#ifdef CONFIG_ARCH_FPU + /* Determine actual HW frame size from EXC_RETURN: + * bit 4 set (STD_CONTEXT): 8 words = 32 bytes (no FPU) + * bit 4 clear: 26 words = 104 bytes (with FPU) + */ + + tst r14, #EXC_RETURN_STD_CONTEXT + ite ne + movne r1, #(4*HW_INT_REGS) /* Standard frame: 32 bytes */ + moveq r1, #HW_XCPT_SIZE /* Extended frame: 104 bytes */ +#else + mov r1, #HW_XCPT_SIZE /* Always 32 bytes without FPU */ +#endif + /* r1 = actual HW frame size in bytes */ + + add r1, r0, r1 /* r1 = implied SP = r0 + frame_size */ + cmp r2, r1 /* desired SP == implied SP? */ + beq .Lno_sp_relocate /* Yes, skip relocation */ + + /* Relocate HW exception frame from r0 to (r2 - frame_size). + * frame_size = r1 - r0. We have r0 (source), r2 (desired SP). + * r4-r11 are already restored. Available scratch: r1, r2, r3. + * + * Copy direction matters for overlapping regions: + * dest < source: forward copy (low to high) + * dest > source: backward copy (high to low) + */ + + sub r3, r1, r0 /* r3 = frame_size = implied_SP - src */ + sub r2, r2, r3 /* r2 = dest = desired_SP - frame_size */ + cmp r2, r0 /* dest vs source? */ + bhs .Lsp_copy_backward /* dest >= source: copy backward */ + + /* Forward copy (dest < source) */ + + push {r2} /* Save dest start for later */ +.Lsp_copy_fwd: + ldr r1, [r0], #4 + str r1, [r2], #4 + subs r3, r3, #4 + bne .Lsp_copy_fwd + pop {r0} /* r0 = dest start = relocated frame */ + b .Lno_sp_relocate + +.Lsp_copy_backward: + /* Backward copy (dest >= source) */ + + push {r2} /* Save dest start for later */ + add r0, r0, r3 /* r0 = source end (one past) */ + add r2, r2, r3 /* r2 = dest end (one past) */ +.Lsp_copy_bwd: + ldr r1, [r0, #-4]! /* Pre-decrement, load */ + str r1, [r2, #-4]! /* Pre-decrement, store */ + subs r3, r3, #4 + bne .Lsp_copy_bwd + pop {r0} /* r0 = dest start = relocated frame */ + +.Lno_sp_relocate: + pop {r1, r3} /* Restore psplim + basepri */ +#endif /* CONFIG_ARMV8M_SP_CONTEXT_RESTORE */ + /* The EXC_RETURN value tells us whether we are returning on the MSP or PSP */