1 // SPDX-License-Identifier: GPL-2.0-or-later
3 /***************************************************************************
4 * Copyright 2016,2017 Sony Video & Sound Products Inc. *
5 * Masatoshi Tateishi - Masatoshi.Tateishi@jp.sony.com *
6 * Masayuki Ishikawa - Masayuki.Ishikawa@jp.sony.com *
7 ***************************************************************************/
13 #include <jtag/jtag.h>
14 #include "target/target.h"
15 #include "target/target_type.h"
16 #include "target/armv7m.h"
17 #include "target/cortex_m.h"
19 #include "helper/log.h"
20 #include "helper/types.h"
21 #include "target/register.h"
22 #include "rtos_nuttx_stackings.h"
25 #define EXTRAINFO_SIZE 256
27 /* Only 32-bit CPUs are supported by the current implementation. Supporting
28 * other CPUs will require reading this information from the target and
29 * adapting the code accordingly.
34 const char *target_name
;
35 const struct rtos_register_stacking
*stacking
;
36 const struct rtos_register_stacking
*(*select_stackinfo
)(struct target
*target
);
40 * struct tcbinfo_s is located in the sched.h
41 * https://github.com/apache/nuttx/blob/master/include/nuttx/sched.h
43 #define TCBINFO_TARGET_SIZE 22
45 uint16_t pid_off
; /* Offset of tcb.pid */
46 uint16_t state_off
; /* Offset of tcb.task_state */
47 uint16_t pri_off
; /* Offset of tcb.sched_priority */
48 uint16_t name_off
; /* Offset of tcb.name */
49 uint16_t regs_off
; /* Offset of tcb.regs */
50 uint16_t basic_num
; /* Num of genernal regs */
51 uint16_t total_num
; /* Num of regs in tcbinfo.reg_offs */
52 target_addr_t xcpreg_off
; /* Offset pointer of xcp.regs */
60 /* Used to index the list of retrieved symbols. See nuttx_symbol_list for the order. */
61 enum nuttx_symbol_vals
{
62 NX_SYM_READYTORUN
= 0,
68 static const struct symbols nuttx_symbol_list
[] = {
69 { "g_readytorun", false },
70 { "g_pidhash", false },
71 { "g_npidhash", false },
72 { "g_tcbinfo", false },
76 static char *task_state_str
[] = {
90 static const struct rtos_register_stacking
*cortexm_select_stackinfo(struct target
*target
);
92 static const struct nuttx_params nuttx_params_list
[] = {
94 .target_name
= "cortex_m",
96 .select_stackinfo
= cortexm_select_stackinfo
,
99 .target_name
= "hla_target",
101 .select_stackinfo
= cortexm_select_stackinfo
,
104 .target_name
= "esp32",
105 .stacking
= &nuttx_esp32_stacking
,
108 .target_name
= "esp32s2",
109 .stacking
= &nuttx_esp32s2_stacking
,
112 .target_name
= "esp32s3",
113 .stacking
= &nuttx_esp32s3_stacking
,
116 .target_name
= "esp32c3",
117 .stacking
= &nuttx_riscv_stacking
,
121 static bool cortexm_hasfpu(struct target
*target
)
124 struct armv7m_common
*armv7m_target
= target_to_armv7m(target
);
126 if (!is_armv7m(armv7m_target
) || armv7m_target
->fp_feature
== FP_NONE
)
129 int retval
= target_read_u32(target
, FPU_CPACR
, &cpacr
);
130 if (retval
!= ERROR_OK
) {
131 LOG_ERROR("Could not read CPACR register to check FPU state");
135 return cpacr
& 0x00F00000;
138 static const struct rtos_register_stacking
*cortexm_select_stackinfo(struct target
*target
)
140 return cortexm_hasfpu(target
) ? &nuttx_stacking_cortex_m_fpu
: &nuttx_stacking_cortex_m
;
143 static bool nuttx_detect_rtos(struct target
*target
)
145 if (target
->rtos
->symbols
&&
146 target
->rtos
->symbols
[NX_SYM_READYTORUN
].address
!= 0 &&
147 target
->rtos
->symbols
[NX_SYM_PIDHASH
].address
!= 0)
152 static int nuttx_create(struct target
*target
)
154 const struct nuttx_params
*param
;
157 for (i
= 0; i
< ARRAY_SIZE(nuttx_params_list
); i
++) {
158 param
= &nuttx_params_list
[i
];
159 if (strcmp(target_type_name(target
), param
->target_name
) == 0) {
160 LOG_INFO("Detected target \"%s\"", param
->target_name
);
165 if (i
>= ARRAY_SIZE(nuttx_params_list
)) {
166 LOG_ERROR("Could not find \"%s\" target in NuttX compatibility list", target_type_name(target
));
170 /* We found a target in our list, copy its reference. */
171 target
->rtos
->rtos_specific_params
= (void *)param
;
176 static int nuttx_smp_init(struct target
*target
)
178 /* Return OK for now so that the initialisation sequence doesn't stop.
179 * SMP case will be implemented later. */
183 static target_addr_t
target_buffer_get_addr(struct target
*target
, const uint8_t *buffer
)
186 return target_buffer_get_u64(target
, buffer
);
188 return target_buffer_get_u32(target
, buffer
);
192 static int nuttx_update_threads(struct rtos
*rtos
)
194 struct tcbinfo tcbinfo
;
195 uint32_t pidhashaddr
, npidhash
, tcbaddr
;
199 if (!rtos
->symbols
) {
200 LOG_ERROR("No symbols for nuttx");
204 /* Free previous thread details */
205 rtos_free_threadlist(rtos
);
207 /* NuttX provides a hash table that keeps track of all the TCBs.
208 * We first read its size from g_npidhash and its address from g_pidhash.
209 * Its content is then read from these values.
211 int ret
= target_read_u32(rtos
->target
, rtos
->symbols
[NX_SYM_NPIDHASH
].address
, &npidhash
);
212 if (ret
!= ERROR_OK
) {
213 LOG_ERROR("Failed to read g_npidhash: ret = %d", ret
);
217 LOG_DEBUG("Hash table size (g_npidhash) = %" PRId32
, npidhash
);
219 ret
= target_read_u32(rtos
->target
, rtos
->symbols
[NX_SYM_PIDHASH
].address
, &pidhashaddr
);
220 if (ret
!= ERROR_OK
) {
221 LOG_ERROR("Failed to read g_pidhash address: ret = %d", ret
);
225 LOG_DEBUG("Hash table address (g_pidhash) = %" PRIx32
, pidhashaddr
);
227 uint8_t *pidhash
= malloc(npidhash
* PTR_WIDTH
);
229 LOG_ERROR("Failed to allocate pidhash");
233 ret
= target_read_buffer(rtos
->target
, pidhashaddr
, PTR_WIDTH
* npidhash
, pidhash
);
234 if (ret
!= ERROR_OK
) {
235 LOG_ERROR("Failed to read tcbhash: ret = %d", ret
);
239 /* NuttX provides a struct that contains TCB offsets for required members.
240 * Read its content from g_tcbinfo.
242 uint8_t buff
[TCBINFO_TARGET_SIZE
];
243 ret
= target_read_buffer(rtos
->target
, rtos
->symbols
[NX_SYM_TCB_INFO
].address
, sizeof(buff
), buff
);
244 if (ret
!= ERROR_OK
) {
245 LOG_ERROR("Failed to read tcbinfo: ret = %d", ret
);
248 tcbinfo
.pid_off
= target_buffer_get_u16(rtos
->target
, buff
);
249 tcbinfo
.state_off
= target_buffer_get_u16(rtos
->target
, buff
+ 2);
250 tcbinfo
.pri_off
= target_buffer_get_u16(rtos
->target
, buff
+ 4);
251 tcbinfo
.name_off
= target_buffer_get_u16(rtos
->target
, buff
+ 6);
252 tcbinfo
.regs_off
= target_buffer_get_u16(rtos
->target
, buff
+ 8);
253 tcbinfo
.basic_num
= target_buffer_get_u16(rtos
->target
, buff
+ 10);
254 tcbinfo
.total_num
= target_buffer_get_u16(rtos
->target
, buff
+ 12);
255 tcbinfo
.xcpreg_off
= target_buffer_get_addr(rtos
->target
, buff
+ 14);
257 /* The head of the g_readytorun list is the currently running task.
258 * Reading in a temporary variable first to avoid endianness issues,
259 * rtos->current_thread is int64_t. */
260 uint32_t current_thread
;
261 ret
= target_read_u32(rtos
->target
, rtos
->symbols
[NX_SYM_READYTORUN
].address
, ¤t_thread
);
262 if (ret
!= ERROR_OK
) {
263 LOG_ERROR("Failed to read g_readytorun: ret = %d", ret
);
266 rtos
->current_thread
= current_thread
;
268 uint32_t thread_count
= 0;
270 for (unsigned int i
= 0; i
< npidhash
; i
++) {
271 tcbaddr
= target_buffer_get_u32(rtos
->target
, &pidhash
[i
* PTR_WIDTH
]);
276 ret
= target_read_u16(rtos
->target
, tcbaddr
+ tcbinfo
.pid_off
, &pid
);
277 if (ret
!= ERROR_OK
) {
278 LOG_ERROR("Failed to read PID of TCB@0x%x from pidhash[%d]: ret = %d",
283 ret
= target_read_u8(rtos
->target
, tcbaddr
+ tcbinfo
.state_off
, &state
);
284 if (ret
!= ERROR_OK
) {
285 LOG_ERROR("Failed to read state of TCB@0x%x from pidhash[%d]: ret = %d",
290 struct thread_detail
*new_thread_details
= realloc(rtos
->thread_details
,
291 sizeof(struct thread_detail
) * (thread_count
+ 1));
292 if (!new_thread_details
) {
297 struct thread_detail
*thread
= &new_thread_details
[thread_count
];
298 thread
->threadid
= tcbaddr
;
299 thread
->exists
= true;
300 thread
->extra_info_str
= NULL
;
302 rtos
->thread_details
= new_thread_details
;
305 if (state
< ARRAY_SIZE(task_state_str
)) {
306 thread
->extra_info_str
= malloc(EXTRAINFO_SIZE
);
307 if (!thread
->extra_info_str
) {
311 snprintf(thread
->extra_info_str
, EXTRAINFO_SIZE
, "pid:%d, %s",
313 task_state_str
[state
]);
316 if (tcbinfo
.name_off
) {
317 thread
->thread_name_str
= calloc(NAME_SIZE
+ 1, sizeof(char));
318 if (!thread
->thread_name_str
) {
322 ret
= target_read_buffer(rtos
->target
, tcbaddr
+ tcbinfo
.name_off
,
323 sizeof(char) * NAME_SIZE
, (uint8_t *)thread
->thread_name_str
);
324 if (ret
!= ERROR_OK
) {
325 LOG_ERROR("Failed to read thread's name: ret = %d", ret
);
329 thread
->thread_name_str
= strdup("None");
334 rtos
->thread_count
= thread_count
;
340 static int nuttx_getreg_current_thread(struct rtos
*rtos
,
341 struct rtos_reg
**reg_list
, int *num_regs
)
343 struct reg
**gdb_reg_list
;
345 /* Registers for currently running thread are not on task's stack and
346 * should be retrieved from reg caches via target_get_gdb_reg_list */
347 int ret
= target_get_gdb_reg_list(rtos
->target
, &gdb_reg_list
, num_regs
,
349 if (ret
!= ERROR_OK
) {
350 LOG_ERROR("target_get_gdb_reg_list failed %d", ret
);
354 *reg_list
= calloc(*num_regs
, sizeof(struct rtos_reg
));
356 LOG_ERROR("Failed to alloc memory for %d", *num_regs
);
361 for (int i
= 0; i
< *num_regs
; i
++) {
362 (*reg_list
)[i
].number
= gdb_reg_list
[i
]->number
;
363 (*reg_list
)[i
].size
= gdb_reg_list
[i
]->size
;
364 memcpy((*reg_list
)[i
].value
, gdb_reg_list
[i
]->value
, ((*reg_list
)[i
].size
+ 7) / 8);
372 static int nuttx_getregs_fromstack(struct rtos
*rtos
, int64_t thread_id
,
373 struct rtos_reg
**reg_list
, int *num_regs
)
377 const struct nuttx_params
*priv
= rtos
->rtos_specific_params
;
378 const struct rtos_register_stacking
*stacking
= priv
->stacking
;
381 if (priv
->select_stackinfo
) {
382 stacking
= priv
->select_stackinfo(rtos
->target
);
384 LOG_ERROR("Can't find a way to get stacking info");
389 int ret
= target_read_u16(rtos
->target
,
390 rtos
->symbols
[NX_SYM_TCB_INFO
].address
+ offsetof(struct tcbinfo
, regs_off
),
392 if (ret
!= ERROR_OK
) {
393 LOG_ERROR("Failed to read registers' offset: ret = %d", ret
);
397 ret
= target_read_u32(rtos
->target
, thread_id
+ xcpreg_off
, ®saddr
);
398 if (ret
!= ERROR_OK
) {
399 LOG_ERROR("Failed to read registers' address: ret = %d", ret
);
403 return rtos_generic_stack_read(rtos
->target
, stacking
, regsaddr
, reg_list
, num_regs
);
406 static int nuttx_get_thread_reg_list(struct rtos
*rtos
, int64_t thread_id
,
407 struct rtos_reg
**reg_list
, int *num_regs
)
410 LOG_ERROR("NUTTX: out of memory");
414 if (thread_id
== rtos
->current_thread
)
415 return nuttx_getreg_current_thread(rtos
, reg_list
, num_regs
);
416 return nuttx_getregs_fromstack(rtos
, thread_id
, reg_list
, num_regs
);
419 static int nuttx_get_symbol_list_to_lookup(struct symbol_table_elem
*symbol_list
[])
421 *symbol_list
= calloc(ARRAY_SIZE(nuttx_symbol_list
), sizeof(**symbol_list
));
423 LOG_ERROR("NUTTX: out of memory");
427 for (unsigned int i
= 0; i
< ARRAY_SIZE(nuttx_symbol_list
); i
++) {
428 (*symbol_list
)[i
].symbol_name
= nuttx_symbol_list
[i
].name
;
429 (*symbol_list
)[i
].optional
= nuttx_symbol_list
[i
].optional
;
435 const struct rtos_type nuttx_rtos
= {
437 .detect_rtos
= nuttx_detect_rtos
,
438 .create
= nuttx_create
,
439 .smp_init
= nuttx_smp_init
,
440 .update_threads
= nuttx_update_threads
,
441 .get_thread_reg_list
= nuttx_get_thread_reg_list
,
442 .get_symbol_list_to_lookup
= nuttx_get_symbol_list_to_lookup
,