From 4ba5ef4a0775043f611a159c075948bf10abc0c9 Mon Sep 17 00:00:00 2001 From: Anuj Deshpande Date: Thu, 21 May 2026 20:41:45 +0530 Subject: [PATCH 1/2] rtos: add ARMv8-M (Cortex-M33/M23) FreeRTOS register stackings ARMv8-M FreeRTOS ports (e.g. the RP2350_ARM_NTZ port used on the RP2350's Cortex-M33) save a different software frame than the classic Cortex-M3/M4F ports the existing stackings assume. From the saved pxTopOfStack the layout is: 0x00 PSPLIM 0x04 EXC_RETURN (software-saved LR) 0x08 R4 .. 0x24 R11 (0x28 S16..S31, only when the task used the FPU - extended frame) i.e. two extra leading words (PSPLIM, EXC_RETURN) and R4-R11 follow EXC_RETURN instead of preceding it, so every register offset differs from rtos_standard_cortex_m4f*. Add rtos_standard_cortex_m33_stacking (basic frame) and rtos_standard_cortex_m33_fpu_stacking (extended FPU frame) with the correct offsets and xPSR-based stack alignment. Signed-off-by: Anuj Deshpande --- src/rtos/rtos_standard_stackings.c | 89 ++++++++++++++++++++++++++++++ src/rtos/rtos_standard_stackings.h | 2 + 2 files changed, 91 insertions(+) diff --git a/src/rtos/rtos_standard_stackings.c b/src/rtos/rtos_standard_stackings.c index 99cd1540f..bdbf474c7 100644 --- a/src/rtos/rtos_standard_stackings.c +++ b/src/rtos/rtos_standard_stackings.c @@ -74,6 +74,61 @@ static const struct stack_register_offset rtos_standard_cortex_m4f_fpu_stack_off }; +/* + * ARMv8-M (Cortex-M33/M23) FreeRTOS ports (e.g. RP2350_ARM_NTZ) push an extra + * software-saved frame that the classic Cortex-M3/M4F layouts do not: + * + * [low addr = saved pxTopOfStack] + * 0x00 PSPLIM + * 0x04 EXC_RETURN (software-saved LR) + * 0x08 R4 .. 0x24 R11 + * (0x28 S16..S31, only when the task used the FPU - extended frame) + * + * + * Compared to rtos_standard_cortex_m4f*, every register is shifted by the two + * leading PSPLIM/EXC_RETURN words and R4-R11 follow EXC_RETURN rather than + * preceding it. Using the M4F tables here yields a garbage call stack. + */ +static const struct stack_register_offset rtos_standard_cortex_m33_stack_offsets[] = { + { ARMV7M_R0, 0x28, 32 }, /* r0 */ + { ARMV7M_R1, 0x2c, 32 }, /* r1 */ + { ARMV7M_R2, 0x30, 32 }, /* r2 */ + { ARMV7M_R3, 0x34, 32 }, /* r3 */ + { ARMV7M_R4, 0x08, 32 }, /* r4 */ + { ARMV7M_R5, 0x0c, 32 }, /* r5 */ + { ARMV7M_R6, 0x10, 32 }, /* r6 */ + { ARMV7M_R7, 0x14, 32 }, /* r7 */ + { ARMV7M_R8, 0x18, 32 }, /* r8 */ + { ARMV7M_R9, 0x1c, 32 }, /* r9 */ + { ARMV7M_R10, 0x20, 32 }, /* r10 */ + { ARMV7M_R11, 0x24, 32 }, /* r11 */ + { ARMV7M_R12, 0x38, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, 0x3c, 32 }, /* lr */ + { ARMV7M_PC, 0x40, 32 }, /* pc */ + { ARMV7M_XPSR, 0x44, 32 }, /* xPSR */ +}; + +static const struct stack_register_offset rtos_standard_cortex_m33_fpu_stack_offsets[] = { + { ARMV7M_R0, 0x68, 32 }, /* r0 */ + { ARMV7M_R1, 0x6c, 32 }, /* r1 */ + { ARMV7M_R2, 0x70, 32 }, /* r2 */ + { ARMV7M_R3, 0x74, 32 }, /* r3 */ + { ARMV7M_R4, 0x08, 32 }, /* r4 */ + { ARMV7M_R5, 0x0c, 32 }, /* r5 */ + { ARMV7M_R6, 0x10, 32 }, /* r6 */ + { ARMV7M_R7, 0x14, 32 }, /* r7 */ + { ARMV7M_R8, 0x18, 32 }, /* r8 */ + { ARMV7M_R9, 0x1c, 32 }, /* r9 */ + { ARMV7M_R10, 0x20, 32 }, /* r10 */ + { ARMV7M_R11, 0x24, 32 }, /* r11 */ + { ARMV7M_R12, 0x78, 32 }, /* r12 */ + { ARMV7M_R13, -2, 32 }, /* sp */ + { ARMV7M_R14, 0x7c, 32 }, /* lr */ + { ARMV7M_PC, 0x80, 32 }, /* pc */ + { ARMV7M_XPSR, 0x84, 32 }, /* xPSR */ +}; + static const struct stack_register_offset rtos_standard_cortex_r4_stack_offsets[] = { { 0, 0x08, 32 }, /* r0 (a1) */ { 1, 0x0c, 32 }, /* r1 (a2) */ @@ -196,6 +251,24 @@ static target_addr_t rtos_standard_cortex_m4f_fpu_stack_align(struct target *tar stack_ptr, XPSR_OFFSET); } +static target_addr_t rtos_standard_cortex_m33_stack_align(struct target *target, + const uint8_t *stack_data, const struct rtos_register_stacking *stacking, + target_addr_t stack_ptr) +{ + const int XPSR_OFFSET = 0x44; + return rtos_cortex_m_stack_align(target, stack_data, stacking, + stack_ptr, XPSR_OFFSET); +} + +static target_addr_t rtos_standard_cortex_m33_fpu_stack_align(struct target *target, + const uint8_t *stack_data, const struct rtos_register_stacking *stacking, + target_addr_t stack_ptr) +{ + const int XPSR_OFFSET = 0x84; + return rtos_cortex_m_stack_align(target, stack_data, stacking, + stack_ptr, XPSR_OFFSET); +} + const struct rtos_register_stacking rtos_standard_cortex_m3_stacking = { .stack_registers_size = 0x40, @@ -221,6 +294,22 @@ const struct rtos_register_stacking rtos_standard_cortex_m4f_fpu_stacking = { .register_offsets = rtos_standard_cortex_m4f_fpu_stack_offsets }; +const struct rtos_register_stacking rtos_standard_cortex_m33_stacking = { + .stack_registers_size = 0x48, + .stack_growth_direction = -1, + .num_output_registers = ARMV7M_NUM_CORE_REGS, + .calculate_process_stack = rtos_standard_cortex_m33_stack_align, + .register_offsets = rtos_standard_cortex_m33_stack_offsets +}; + +const struct rtos_register_stacking rtos_standard_cortex_m33_fpu_stacking = { + .stack_registers_size = 0xd0, + .stack_growth_direction = -1, + .num_output_registers = ARMV7M_NUM_CORE_REGS, + .calculate_process_stack = rtos_standard_cortex_m33_fpu_stack_align, + .register_offsets = rtos_standard_cortex_m33_fpu_stack_offsets +}; + const struct rtos_register_stacking rtos_standard_cortex_r4_stacking = { .stack_registers_size = 0x48, .stack_growth_direction = -1, diff --git a/src/rtos/rtos_standard_stackings.h b/src/rtos/rtos_standard_stackings.h index 99fbe07e4..6b4acff9d 100644 --- a/src/rtos/rtos_standard_stackings.h +++ b/src/rtos/rtos_standard_stackings.h @@ -13,6 +13,8 @@ extern const struct rtos_register_stacking rtos_standard_cortex_m3_stacking; extern const struct rtos_register_stacking rtos_standard_cortex_m4f_stacking; extern const struct rtos_register_stacking rtos_standard_cortex_m4f_fpu_stacking; +extern const struct rtos_register_stacking rtos_standard_cortex_m33_stacking; +extern const struct rtos_register_stacking rtos_standard_cortex_m33_fpu_stacking; extern const struct rtos_register_stacking rtos_standard_cortex_r4_stacking; target_addr_t rtos_generic_stack_align8(struct target *target, const uint8_t *stack_data, const struct rtos_register_stacking *stacking, From bd4179d59dbcb1222876aa232b64affdfbeb2fea Mon Sep 17 00:00:00 2001 From: Anuj Deshpande Date: Thu, 21 May 2026 20:41:45 +0530 Subject: [PATCH 2/2] rtos/FreeRTOS: select ARMv8-M stacking from EXC_RETURN On ARMv8-M targets (Cortex-M33/M23) handle stack reconstruction before the existing armv7m FPU probe and choose the extended (FPU) vs basic layout from bit 4 of the software-saved EXC_RETURN at offset 0x04, which these ports always stack. This must not be gated on the cm4_fpu_enabled detection (fp_feature + CPACR): that probe does not reliably report the FPU on ARMv8-M cores, so relying on it silently reconstructed every FPU-extended-frame task with the basic layout, yielding pc=lr=xpsr=0 and a garbage call stack, while basic-frame tasks looked fine. Tested on an RP2350 (RP2350_ARM_NTZ port) with a CMSIS-DAP probe: all FreeRTOS tasks - both basic and FPU-extended frames - now produce correct backtraces via "thread apply all bt". Signed-off-by: Anuj Deshpande --- src/rtos/freertos.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/rtos/freertos.c b/src/rtos/freertos.c index 5a9224ec0..515208912 100644 --- a/src/rtos/freertos.c +++ b/src/rtos/freertos.c @@ -39,6 +39,8 @@ struct freertos_params { const struct rtos_register_stacking *stacking_info_cm3; const struct rtos_register_stacking *stacking_info_cm4f; const struct rtos_register_stacking *stacking_info_cm4f_fpu; + const struct rtos_register_stacking *stacking_info_cm33; + const struct rtos_register_stacking *stacking_info_cm33_fpu; }; static const struct freertos_params freertos_params_list[] = { @@ -55,6 +57,8 @@ static const struct freertos_params freertos_params_list[] = { &rtos_standard_cortex_m3_stacking, /* stacking_info */ &rtos_standard_cortex_m4f_stacking, &rtos_standard_cortex_m4f_fpu_stacking, + &rtos_standard_cortex_m33_stacking, + &rtos_standard_cortex_m33_fpu_stacking, }, { "hla_target", /* target_name */ @@ -69,6 +73,8 @@ static const struct freertos_params freertos_params_list[] = { &rtos_standard_cortex_m3_stacking, /* stacking_info */ &rtos_standard_cortex_m4f_stacking, &rtos_standard_cortex_m4f_fpu_stacking, + &rtos_standard_cortex_m33_stacking, + &rtos_standard_cortex_m33_fpu_stacking, }, }; @@ -417,9 +423,35 @@ static int freertos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, thread_id + param->thread_stack_offset, stack_ptr); + struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target); + + /* ARMv8-M FreeRTOS ports (Cortex-M33/M23, e.g. RP2350_ARM_NTZ) always push + * two extra software-saved words (PSPLIM, then EXC_RETURN) ahead of R4-R11, + * so both the EXC_RETURN position and all register offsets differ from the + * classic Cortex-M3/M4F layouts. The software-saved EXC_RETURN (at offset + * 0x04) is the authoritative indicator of whether the extended FPU frame + * was stacked, so use it directly here. Note we deliberately do NOT gate + * this on the armv7m FPU detection below (fp_feature / CPACR): that probe + * does not reliably report the FPU on ARMv8-M cores, and getting it wrong + * would silently misread every FPU-frame task with the basic layout. */ + if (is_armv7m(armv7m_target) && armv7m_target->arm.arch == ARM_ARCH_V8M) { + uint32_t exc_return = 0; + retval = target_read_u32(rtos->target, stack_ptr + 0x04, &exc_return); + if (retval != ERROR_OK) { + LOG_ERROR("Error reading EXC_RETURN from FreeRTOS thread stack"); + return retval; + } + /* EXC_RETURN bit 4 == 0 means the extended (FPU) frame is in use. */ + const struct rtos_register_stacking *stacking; + if ((exc_return & 0x10) == 0) + stacking = param->stacking_info_cm33_fpu; + else + stacking = param->stacking_info_cm33; + return rtos_generic_stack_read(rtos->target, stacking, stack_ptr, reg_list, num_regs); + } + /* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4F */ int cm4_fpu_enabled = 0; - struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target); if (is_armv7m(armv7m_target)) { if ((armv7m_target->fp_feature == FPV4_SP) || (armv7m_target->fp_feature == FPV5_SP) || (armv7m_target->fp_feature == FPV5_DP)) {