Skip to content

Commit 5159c59

Browse files
daniel-geotabborneoa
authored andcommitted
src/rtos/rtos_nuttx_stackings.c: Fix stack alignment for cortex-m targets
Backtraces performed by GDB on any thread other than the current thread would fail if hardware 8 byte ISR stack alignment was enabled on cortex_m targets. Stack reads now adjust the stored SP to account for a potential offset introduced by hardware. Fixed incorrect register offsets for cortex_m Nuttx frames by reading the TCB info symbols to determine correct offsets. Fixed offsets can no longer be used since the offsets have changed multiple times for different Nuttx versions. Tested on nuttx-12.1.0. Tested using custom stm32h7 board and custom s32k148 board variants. Built with CONFIG_ARCH_FPU enabled and disabled to test FPU and non FPU frame logic. Change-Id: Ifcbeefb0ddcfbcb528daa9d1d95732ca9584c9ef Signed-off-by: daniellizewski <[email protected]> Reviewed-on: https://review.openocd.org/c/openocd/+/8180 Reviewed-by: Antonio Borneo <[email protected]> Tested-by: jenkins
1 parent b14f63e commit 5159c59

File tree

3 files changed

+104
-93
lines changed

3 files changed

+104
-93
lines changed

src/rtos/nuttx.c

Lines changed: 8 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
struct nuttx_params {
3333
const char *target_name;
3434
const struct rtos_register_stacking *stacking;
35-
const struct rtos_register_stacking *(*select_stackinfo)(struct target *target);
3635
};
3736

3837
/*
@@ -56,19 +55,12 @@ struct symbols {
5655
bool optional;
5756
};
5857

59-
/* Used to index the list of retrieved symbols. See nuttx_symbol_list for the order. */
60-
enum nuttx_symbol_vals {
61-
NX_SYM_READYTORUN = 0,
62-
NX_SYM_PIDHASH,
63-
NX_SYM_NPIDHASH,
64-
NX_SYM_TCB_INFO,
65-
};
66-
6758
static const struct symbols nuttx_symbol_list[] = {
6859
{ "g_readytorun", false },
6960
{ "g_pidhash", false },
7061
{ "g_npidhash", false },
7162
{ "g_tcbinfo", false },
63+
{ "g_reg_offs", false},
7264
{ NULL, false }
7365
};
7466

@@ -86,18 +78,14 @@ static char *task_state_str[] = {
8678
"STOPPED",
8779
};
8880

89-
static const struct rtos_register_stacking *cortexm_select_stackinfo(struct target *target);
90-
9181
static const struct nuttx_params nuttx_params_list[] = {
9282
{
9383
.target_name = "cortex_m",
94-
.stacking = NULL,
95-
.select_stackinfo = cortexm_select_stackinfo,
84+
.stacking = &nuttx_stacking_cortex_m,
9685
},
9786
{
9887
.target_name = "hla_target",
99-
.stacking = NULL,
100-
.select_stackinfo = cortexm_select_stackinfo,
88+
.stacking = &nuttx_stacking_cortex_m,
10189
},
10290
{
10391
.target_name = "esp32",
@@ -117,28 +105,6 @@ static const struct nuttx_params nuttx_params_list[] = {
117105
},
118106
};
119107

120-
static bool cortexm_hasfpu(struct target *target)
121-
{
122-
uint32_t cpacr;
123-
struct armv7m_common *armv7m_target = target_to_armv7m(target);
124-
125-
if (!is_armv7m(armv7m_target) || armv7m_target->fp_feature == FP_NONE)
126-
return false;
127-
128-
int retval = target_read_u32(target, FPU_CPACR, &cpacr);
129-
if (retval != ERROR_OK) {
130-
LOG_ERROR("Could not read CPACR register to check FPU state");
131-
return false;
132-
}
133-
134-
return cpacr & 0x00F00000;
135-
}
136-
137-
static const struct rtos_register_stacking *cortexm_select_stackinfo(struct target *target)
138-
{
139-
return cortexm_hasfpu(target) ? &nuttx_stacking_cortex_m_fpu : &nuttx_stacking_cortex_m;
140-
}
141-
142108
static bool nuttx_detect_rtos(struct target *target)
143109
{
144110
if (target->rtos->symbols &&
@@ -371,29 +337,25 @@ static int nuttx_getreg_current_thread(struct rtos *rtos,
371337
static int nuttx_getregs_fromstack(struct rtos *rtos, int64_t thread_id,
372338
struct rtos_reg **reg_list, int *num_regs)
373339
{
374-
uint16_t xcpreg_off;
340+
uint16_t regs_off;
375341
uint32_t regsaddr;
376342
const struct nuttx_params *priv = rtos->rtos_specific_params;
377343
const struct rtos_register_stacking *stacking = priv->stacking;
378344

379345
if (!stacking) {
380-
if (priv->select_stackinfo) {
381-
stacking = priv->select_stackinfo(rtos->target);
382-
} else {
383-
LOG_ERROR("Can't find a way to get stacking info");
384-
return ERROR_FAIL;
385-
}
346+
LOG_ERROR("Can't find a way to get stacking info");
347+
return ERROR_FAIL;
386348
}
387349

388350
int ret = target_read_u16(rtos->target,
389351
rtos->symbols[NX_SYM_TCB_INFO].address + offsetof(struct tcbinfo, regs_off),
390-
&xcpreg_off);
352+
&regs_off);
391353
if (ret != ERROR_OK) {
392354
LOG_ERROR("Failed to read registers' offset: ret = %d", ret);
393355
return ERROR_FAIL;
394356
}
395357

396-
ret = target_read_u32(rtos->target, thread_id + xcpreg_off, &regsaddr);
358+
ret = target_read_u32(rtos->target, thread_id + regs_off, &regsaddr);
397359
if (ret != ERROR_OK) {
398360
LOG_ERROR("Failed to read registers' address: ret = %d", ret);
399361
return ERROR_FAIL;

src/rtos/rtos_nuttx_stackings.c

Lines changed: 87 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -9,60 +9,100 @@
99
#include "rtos_nuttx_stackings.h"
1010
#include "rtos_standard_stackings.h"
1111
#include <target/riscv/riscv.h>
12+
#include <helper/bits.h>
1213

13-
/* see arch/arm/include/armv7-m/irq_cmnvector.h */
14+
/* The cortex_m target uses nuttx_tcbinfo_stack_read which uses a symbol
15+
* provided by Nuttx to read the registers from memory and place them directly
16+
* in the order we need. This is because the register offsets change with
17+
* different versions of Nuttx, FPU vs non-FPU and ARMv7 vs ARMv8.
18+
* This allows a single function to work with many versions.
19+
*/
1420
static const struct stack_register_offset nuttx_stack_offsets_cortex_m[] = {
15-
{ ARMV7M_R0, 0x28, 32 }, /* r0 */
16-
{ ARMV7M_R1, 0x2c, 32 }, /* r1 */
17-
{ ARMV7M_R2, 0x30, 32 }, /* r2 */
18-
{ ARMV7M_R3, 0x34, 32 }, /* r3 */
19-
{ ARMV7M_R4, 0x08, 32 }, /* r4 */
20-
{ ARMV7M_R5, 0x0c, 32 }, /* r5 */
21-
{ ARMV7M_R6, 0x10, 32 }, /* r6 */
22-
{ ARMV7M_R7, 0x14, 32 }, /* r7 */
23-
{ ARMV7M_R8, 0x18, 32 }, /* r8 */
24-
{ ARMV7M_R9, 0x1c, 32 }, /* r9 */
25-
{ ARMV7M_R10, 0x20, 32 }, /* r10 */
26-
{ ARMV7M_R11, 0x24, 32 }, /* r11 */
27-
{ ARMV7M_R12, 0x38, 32 }, /* r12 */
28-
{ ARMV7M_R13, 0, 32 }, /* sp */
29-
{ ARMV7M_R14, 0x3c, 32 }, /* lr */
30-
{ ARMV7M_PC, 0x40, 32 }, /* pc */
31-
{ ARMV7M_XPSR, 0x44, 32 }, /* xPSR */
21+
{ ARMV7M_R0, 0, 32 }, /* r0 */
22+
{ ARMV7M_R1, 4, 32 }, /* r1 */
23+
{ ARMV7M_R2, 8, 32 }, /* r2 */
24+
{ ARMV7M_R3, 12, 32 }, /* r3 */
25+
{ ARMV7M_R4, 16, 32 }, /* r4 */
26+
{ ARMV7M_R5, 20, 32 }, /* r5 */
27+
{ ARMV7M_R6, 24, 32 }, /* r6 */
28+
{ ARMV7M_R7, 28, 32 }, /* r7 */
29+
{ ARMV7M_R8, 32, 32 }, /* r8 */
30+
{ ARMV7M_R9, 36, 32 }, /* r9 */
31+
{ ARMV7M_R10, 40, 32 }, /* r10 */
32+
{ ARMV7M_R11, 44, 32 }, /* r11 */
33+
{ ARMV7M_R12, 48, 32 }, /* r12 */
34+
{ ARMV7M_R13, 52, 32 }, /* sp */
35+
{ ARMV7M_R14, 56, 32 }, /* lr */
36+
{ ARMV7M_PC, 60, 32 }, /* pc */
37+
{ ARMV7M_XPSR, 64, 32 }, /* xPSR */
3238
};
3339

34-
const struct rtos_register_stacking nuttx_stacking_cortex_m = {
35-
.stack_registers_size = 0x48,
36-
.stack_growth_direction = -1,
37-
.num_output_registers = 17,
38-
.register_offsets = nuttx_stack_offsets_cortex_m,
39-
};
40+
/* The Nuttx stack frame for most architectures has some registers placed
41+
* by hardware and some by software. The hardware register order and number does not change
42+
* but the software registers may change with different versions of Nuttx.
43+
* For example with ARMv7, nuttx-12.3.0 added a new register which changed all
44+
* the offsets. We can either create separate offset tables for each version of Nuttx
45+
* which will break again in the future, or read the offsets from the TCB info.
46+
* Nuttx provides a symbol (g_reg_offs) which holds all the offsets for each stored register.
47+
* This offset table is stored in GDB org.gnu.gdb.xxx feature order.
48+
* The same order we need.
49+
* Please refer:
50+
* https://sourceware.org/gdb/current/onlinedocs/gdb/ARM-Features.html
51+
* https://sourceware.org/gdb/current/onlinedocs/gdb/RISC_002dV-Features.html
52+
*/
53+
static int nuttx_cortex_m_tcbinfo_stack_read(struct target *target,
54+
int64_t stack_ptr, const struct rtos_register_stacking *stacking,
55+
uint8_t *stack_data)
56+
{
57+
struct rtos *rtos = target->rtos;
58+
target_addr_t xcpreg_off = rtos->symbols[NX_SYM_REG_OFFSETS].address;
4059

41-
static const struct stack_register_offset nuttx_stack_offsets_cortex_m_fpu[] = {
42-
{ ARMV7M_R0, 0x6c, 32 }, /* r0 */
43-
{ ARMV7M_R1, 0x70, 32 }, /* r1 */
44-
{ ARMV7M_R2, 0x74, 32 }, /* r2 */
45-
{ ARMV7M_R3, 0x78, 32 }, /* r3 */
46-
{ ARMV7M_R4, 0x08, 32 }, /* r4 */
47-
{ ARMV7M_R5, 0x0c, 32 }, /* r5 */
48-
{ ARMV7M_R6, 0x10, 32 }, /* r6 */
49-
{ ARMV7M_R7, 0x14, 32 }, /* r7 */
50-
{ ARMV7M_R8, 0x18, 32 }, /* r8 */
51-
{ ARMV7M_R9, 0x1c, 32 }, /* r9 */
52-
{ ARMV7M_R10, 0x20, 32 }, /* r10 */
53-
{ ARMV7M_R11, 0x24, 32 }, /* r11 */
54-
{ ARMV7M_R12, 0x7c, 32 }, /* r12 */
55-
{ ARMV7M_R13, 0, 32 }, /* sp */
56-
{ ARMV7M_R14, 0x80, 32 }, /* lr */
57-
{ ARMV7M_PC, 0x84, 32 }, /* pc */
58-
{ ARMV7M_XPSR, 0x88, 32 }, /* xPSR */
59-
};
60+
for (int i = 0; i < stacking->num_output_registers; ++i) {
61+
uint16_t stack_reg_offset;
62+
int ret = target_read_u16(rtos->target, xcpreg_off + 2 * i, &stack_reg_offset);
63+
if (ret != ERROR_OK) {
64+
LOG_ERROR("Failed to read stack_reg_offset: ret = %d", ret);
65+
return ret;
66+
}
67+
if (stack_reg_offset != UINT16_MAX && stacking->register_offsets[i].offset >= 0) {
68+
ret = target_read_buffer(target,
69+
stack_ptr + stack_reg_offset,
70+
stacking->register_offsets[i].width_bits / 8,
71+
&stack_data[stacking->register_offsets[i].offset]);
72+
if (ret != ERROR_OK) {
73+
LOG_ERROR("Failed to read register: ret = %d", ret);
74+
return ret;
75+
}
76+
}
77+
}
78+
79+
/* Offset match nuttx_stack_offsets_cortex_m */
80+
const int XPSR_OFFSET = 64;
81+
const int SP_OFFSET = 52;
82+
/* Nuttx stack frames (produced in exception_common) store the SP of the ISR minus
83+
* the hardware stack frame size. This SP may include an additional 4 byte alignment
84+
* depending in xPSR[9]. The Nuttx stack frame stores post alignment since the
85+
* hardware will add/remove automatically on both enter/exit.
86+
* We need to adjust the SP to get the real SP of the stack.
87+
* See Arm Reference manual "Stack alignment on exception entry"
88+
*/
89+
uint32_t xpsr = target_buffer_get_u32(target, &stack_data[XPSR_OFFSET]);
90+
if (xpsr & BIT(9)) {
91+
uint32_t sp = target_buffer_get_u32(target, &stack_data[SP_OFFSET]);
92+
target_buffer_set_u32(target, &stack_data[SP_OFFSET], sp - 4 * stacking->stack_growth_direction);
93+
}
6094

61-
const struct rtos_register_stacking nuttx_stacking_cortex_m_fpu = {
62-
.stack_registers_size = 0x8c,
95+
return ERROR_OK;
96+
}
97+
98+
const struct rtos_register_stacking nuttx_stacking_cortex_m = {
99+
/* nuttx_tcbinfo_stack_read transforms the stack into just output registers */
100+
.stack_registers_size = ARRAY_SIZE(nuttx_stack_offsets_cortex_m) * 4,
63101
.stack_growth_direction = -1,
64-
.num_output_registers = 17,
65-
.register_offsets = nuttx_stack_offsets_cortex_m_fpu,
102+
.num_output_registers = ARRAY_SIZE(nuttx_stack_offsets_cortex_m),
103+
.read_stack = nuttx_cortex_m_tcbinfo_stack_read,
104+
.calculate_process_stack = NULL, /* Stack alignment done in nuttx_cortex_m_tcbinfo_stack_read */
105+
.register_offsets = nuttx_stack_offsets_cortex_m,
66106
};
67107

68108
static const struct stack_register_offset nuttx_stack_offsets_riscv[] = {

src/rtos/rtos_nuttx_stackings.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@
55

66
#include "rtos.h"
77

8+
/* Used to index the list of retrieved symbols. See nuttx_symbol_list for the order. */
9+
enum nuttx_symbol_vals {
10+
NX_SYM_READYTORUN = 0,
11+
NX_SYM_PIDHASH,
12+
NX_SYM_NPIDHASH,
13+
NX_SYM_TCB_INFO,
14+
NX_SYM_REG_OFFSETS,
15+
};
16+
817
extern const struct rtos_register_stacking nuttx_stacking_cortex_m;
918
extern const struct rtos_register_stacking nuttx_stacking_cortex_m_fpu;
1019
extern const struct rtos_register_stacking nuttx_riscv_stacking;

0 commit comments

Comments
 (0)