OpenOCD
nuttx.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 
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  ***************************************************************************/
8 
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12 
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"
18 #include "rtos.h"
19 #include "helper/log.h"
20 #include "helper/types.h"
21 #include "target/register.h"
22 #include "rtos_nuttx_stackings.h"
23 
24 #define NAME_SIZE 32
25 #define EXTRAINFO_SIZE 256
26 
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.
30  */
31 #define PTR_WIDTH 4
32 
33 struct nuttx_params {
34  const char *target_name;
36  const struct rtos_register_stacking *(*select_stackinfo)(struct target *target);
37 };
38 
39 /*
40  * struct tcbinfo_s is located in the sched.h
41  * https://github.com/apache/nuttx/blob/master/include/nuttx/sched.h
42  */
43 #define TCBINFO_TARGET_SIZE 22
44 struct tcbinfo {
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 */
53 };
54 
55 struct symbols {
56  const char *name;
57  bool optional;
58 };
59 
60 /* Used to index the list of retrieved symbols. See nuttx_symbol_list for the order. */
66 };
67 
68 static const struct symbols nuttx_symbol_list[] = {
69  { "g_readytorun", false },
70  { "g_pidhash", false },
71  { "g_npidhash", false },
72  { "g_tcbinfo", false },
73  { NULL, false }
74 };
75 
76 static char *task_state_str[] = {
77  "INVALID",
78  "PENDING",
79  "READYTORUN",
80  "RUNNING",
81  "INACTIVE",
82  "WAIT_SEM",
83  "WAIT_SIG",
84  "WAIT_MQNOTEMPTY",
85  "WAIT_MQNOTFULL",
86  "WAIT_PAGEFILL",
87  "STOPPED",
88 };
89 
91 
92 static const struct nuttx_params nuttx_params_list[] = {
93  {
94  .target_name = "cortex_m",
95  .stacking = NULL,
96  .select_stackinfo = cortexm_select_stackinfo,
97  },
98  {
99  .target_name = "hla_target",
100  .stacking = NULL,
101  .select_stackinfo = cortexm_select_stackinfo,
102  },
103  {
104  .target_name = "esp32",
105  .stacking = &nuttx_esp32_stacking,
106  },
107  {
108  .target_name = "esp32s2",
109  .stacking = &nuttx_esp32s2_stacking,
110  },
111  {
112  .target_name = "esp32s3",
113  .stacking = &nuttx_esp32s3_stacking,
114  },
115  {
116  .target_name = "esp32c3",
117  .stacking = &nuttx_riscv_stacking,
118  },
119 };
120 
121 static bool cortexm_hasfpu(struct target *target)
122 {
123  uint32_t cpacr;
124  struct armv7m_common *armv7m_target = target_to_armv7m(target);
125 
126  if (!is_armv7m(armv7m_target) || armv7m_target->fp_feature == FP_NONE)
127  return false;
128 
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");
132  return false;
133  }
134 
135  return cpacr & 0x00F00000;
136 }
137 
139 {
141 }
142 
143 static bool nuttx_detect_rtos(struct target *target)
144 {
145  if (target->rtos->symbols &&
148  return true;
149  return false;
150 }
151 
152 static int nuttx_create(struct target *target)
153 {
154  const struct nuttx_params *param;
155  unsigned int i;
156 
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);
161  break;
162  }
163  }
164 
165  if (i >= ARRAY_SIZE(nuttx_params_list)) {
166  LOG_ERROR("Could not find \"%s\" target in NuttX compatibility list", target_type_name(target));
167  return JIM_ERR;
168  }
169 
170  /* We found a target in our list, copy its reference. */
171  target->rtos->rtos_specific_params = (void *)param;
172 
173  return JIM_OK;
174 }
175 
176 static int nuttx_smp_init(struct target *target)
177 {
178  /* Return OK for now so that the initialisation sequence doesn't stop.
179  * SMP case will be implemented later. */
180  return ERROR_OK;
181 }
182 
183 static target_addr_t target_buffer_get_addr(struct target *target, const uint8_t *buffer)
184 {
185 #if PTR_WIDTH == 8
187 #else
189 #endif
190 }
191 
192 static int nuttx_update_threads(struct rtos *rtos)
193 {
194  struct tcbinfo tcbinfo;
195  uint32_t pidhashaddr, npidhash, tcbaddr;
196  uint16_t pid;
197  uint8_t state;
198 
199  if (!rtos->symbols) {
200  LOG_ERROR("No symbols for nuttx");
201  return ERROR_FAIL;
202  }
203 
204  /* Free previous thread details */
206 
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.
210  */
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);
214  return ERROR_FAIL;
215  }
216 
217  LOG_DEBUG("Hash table size (g_npidhash) = %" PRId32, npidhash);
218 
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);
222  return ERROR_FAIL;
223  }
224 
225  LOG_DEBUG("Hash table address (g_pidhash) = %" PRIx32, pidhashaddr);
226 
227  uint8_t *pidhash = malloc(npidhash * PTR_WIDTH);
228  if (!pidhash) {
229  LOG_ERROR("Failed to allocate pidhash");
230  return ERROR_FAIL;
231  }
232 
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);
236  goto errout;
237  }
238 
239  /* NuttX provides a struct that contains TCB offsets for required members.
240  * Read its content from g_tcbinfo.
241  */
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);
246  goto errout;
247  }
256 
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;
262  if (ret != ERROR_OK) {
263  LOG_ERROR("Failed to read g_readytorun: ret = %d", ret);
264  goto errout;
265  }
267 
268  uint32_t thread_count = 0;
269 
270  for (unsigned int i = 0; i < npidhash; i++) {
271  tcbaddr = target_buffer_get_u32(rtos->target, &pidhash[i * PTR_WIDTH]);
272 
273  if (!tcbaddr)
274  continue;
275 
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",
279  tcbaddr, i, ret);
280  goto errout;
281  }
282 
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",
286  tcbaddr, i, ret);
287  goto errout;
288  }
289 
290  struct thread_detail *new_thread_details = realloc(rtos->thread_details,
291  sizeof(struct thread_detail) * (thread_count + 1));
292  if (!new_thread_details) {
293  ret = ERROR_FAIL;
294  goto errout;
295  }
296 
297  struct thread_detail *thread = &new_thread_details[thread_count];
298  thread->threadid = tcbaddr;
299  thread->exists = true;
300  thread->extra_info_str = NULL;
301 
302  rtos->thread_details = new_thread_details;
303  thread_count++;
304 
306  thread->extra_info_str = malloc(EXTRAINFO_SIZE);
307  if (!thread->extra_info_str) {
308  ret = ERROR_FAIL;
309  goto errout;
310  }
311  snprintf(thread->extra_info_str, EXTRAINFO_SIZE, "pid:%d, %s",
312  pid,
314  }
315 
316  if (tcbinfo.name_off) {
317  thread->thread_name_str = calloc(NAME_SIZE + 1, sizeof(char));
318  if (!thread->thread_name_str) {
319  ret = ERROR_FAIL;
320  goto errout;
321  }
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);
326  goto errout;
327  }
328  } else {
329  thread->thread_name_str = strdup("None");
330  }
331  }
332 
333  ret = ERROR_OK;
334  rtos->thread_count = thread_count;
335 errout:
336  free(pidhash);
337  return ret;
338 }
339 
341  struct rtos_reg **reg_list, int *num_regs)
342 {
343  struct reg **gdb_reg_list;
344 
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);
351  return ret;
352  }
353 
354  *reg_list = calloc(*num_regs, sizeof(struct rtos_reg));
355  if (!(*reg_list)) {
356  LOG_ERROR("Failed to alloc memory for %d", *num_regs);
357  free(gdb_reg_list);
358  return ERROR_FAIL;
359  }
360 
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);
365  }
366 
367  free(gdb_reg_list);
368 
369  return ERROR_OK;
370 }
371 
372 static int nuttx_getregs_fromstack(struct rtos *rtos, int64_t thread_id,
373  struct rtos_reg **reg_list, int *num_regs)
374 {
375  uint16_t xcpreg_off;
376  uint32_t regsaddr;
377  const struct nuttx_params *priv = rtos->rtos_specific_params;
378  const struct rtos_register_stacking *stacking = priv->stacking;
379 
380  if (!stacking) {
381  if (priv->select_stackinfo) {
382  stacking = priv->select_stackinfo(rtos->target);
383  } else {
384  LOG_ERROR("Can't find a way to get stacking info");
385  return ERROR_FAIL;
386  }
387  }
388 
389  int ret = target_read_u16(rtos->target,
390  rtos->symbols[NX_SYM_TCB_INFO].address + offsetof(struct tcbinfo, regs_off),
391  &xcpreg_off);
392  if (ret != ERROR_OK) {
393  LOG_ERROR("Failed to read registers' offset: ret = %d", ret);
394  return ERROR_FAIL;
395  }
396 
397  ret = target_read_u32(rtos->target, thread_id + xcpreg_off, &regsaddr);
398  if (ret != ERROR_OK) {
399  LOG_ERROR("Failed to read registers' address: ret = %d", ret);
400  return ERROR_FAIL;
401  }
402 
403  return rtos_generic_stack_read(rtos->target, stacking, regsaddr, reg_list, num_regs);
404 }
405 
406 static int nuttx_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
407  struct rtos_reg **reg_list, int *num_regs)
408 {
409  if (!rtos) {
410  LOG_ERROR("NUTTX: out of memory");
411  return ERROR_FAIL;
412  }
413 
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);
417 }
418 
419 static int nuttx_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[])
420 {
421  *symbol_list = calloc(ARRAY_SIZE(nuttx_symbol_list), sizeof(**symbol_list));
422  if (!*symbol_list) {
423  LOG_ERROR("NUTTX: out of memory");
424  return ERROR_FAIL;
425  }
426 
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;
430  }
431 
432  return ERROR_OK;
433 }
434 
435 const struct rtos_type nuttx_rtos = {
436  .name = "nuttx",
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,
443 };
@ FP_NONE
Definition: armv7m.h:210
static struct armv7m_common * target_to_armv7m(struct target *target)
Definition: armv7m.h:262
static bool is_armv7m(const struct armv7m_common *armv7m)
Definition: armv7m.h:250
#define FPU_CPACR
Definition: cortex_m.h:111
static struct esp_usb_jtag * priv
Definition: esp_usb_jtag.c:219
The JTAG interface can be implemented with a software or hardware fifo.
#define ERROR_FAIL
Definition: log.h:170
#define LOG_ERROR(expr ...)
Definition: log.h:132
#define LOG_INFO(expr ...)
Definition: log.h:126
#define LOG_DEBUG(expr ...)
Definition: log.h:109
#define ERROR_OK
Definition: log.h:164
#define PTR_WIDTH
Definition: nuttx.c:31
static int nuttx_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[])
Definition: nuttx.c:419
static const struct symbols nuttx_symbol_list[]
Definition: nuttx.c:68
static bool cortexm_hasfpu(struct target *target)
Definition: nuttx.c:121
static bool nuttx_detect_rtos(struct target *target)
Definition: nuttx.c:143
static int nuttx_getregs_fromstack(struct rtos *rtos, int64_t thread_id, struct rtos_reg **reg_list, int *num_regs)
Definition: nuttx.c:372
static int nuttx_create(struct target *target)
Definition: nuttx.c:152
static int nuttx_update_threads(struct rtos *rtos)
Definition: nuttx.c:192
static int nuttx_smp_init(struct target *target)
Definition: nuttx.c:176
#define TCBINFO_TARGET_SIZE
Definition: nuttx.c:43
#define NAME_SIZE
Definition: nuttx.c:24
const struct rtos_type nuttx_rtos
Definition: nuttx.c:435
static const struct nuttx_params nuttx_params_list[]
Definition: nuttx.c:92
static char * task_state_str[]
Definition: nuttx.c:76
static target_addr_t target_buffer_get_addr(struct target *target, const uint8_t *buffer)
Definition: nuttx.c:183
#define EXTRAINFO_SIZE
Definition: nuttx.c:25
static int nuttx_getreg_current_thread(struct rtos *rtos, struct rtos_reg **reg_list, int *num_regs)
Definition: nuttx.c:340
static const struct rtos_register_stacking * cortexm_select_stackinfo(struct target *target)
Definition: nuttx.c:138
nuttx_symbol_vals
Definition: nuttx.c:61
@ NX_SYM_PIDHASH
Definition: nuttx.c:63
@ NX_SYM_NPIDHASH
Definition: nuttx.c:64
@ NX_SYM_READYTORUN
Definition: nuttx.c:62
@ NX_SYM_TCB_INFO
Definition: nuttx.c:65
static int nuttx_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, struct rtos_reg **reg_list, int *num_regs)
Definition: nuttx.c:406
int rtos_generic_stack_read(struct target *target, const struct rtos_register_stacking *stacking, int64_t stack_ptr, struct rtos_reg **reg_list, int *num_regs)
Definition: rtos.c:602
void rtos_free_threadlist(struct rtos *rtos)
Definition: rtos.c:695
const struct rtos_register_stacking nuttx_esp32s2_stacking
const struct rtos_register_stacking nuttx_riscv_stacking
const struct rtos_register_stacking nuttx_esp32s3_stacking
const struct rtos_register_stacking nuttx_esp32_stacking
const struct rtos_register_stacking nuttx_stacking_cortex_m_fpu
const struct rtos_register_stacking nuttx_stacking_cortex_m
struct target * target
Definition: rtt/rtt.c:26
size_t size
Size of the control block search area.
Definition: rtt/rtt.c:30
int fp_feature
Definition: armv7m.h:232
const struct rtos_register_stacking * stacking
Definition: nuttx.c:35
const char * target_name
Definition: nuttx.c:34
Definition: register.h:111
uint32_t size
Definition: register.h:132
uint8_t * value
Definition: register.h:122
uint32_t number
Definition: register.h:115
Definition: rtos.h:53
Definition: rtos.h:59
const char * name
Definition: rtos.h:60
Definition: rtos.h:36
int thread_count
Definition: rtos.h:47
struct thread_detail * thread_details
Definition: rtos.h:46
struct symbol_table_elem * symbols
Definition: rtos.h:39
struct target * target
Definition: rtos.h:40
void * rtos_specific_params
Definition: rtos.h:50
threadid_t current_thread
Definition: rtos.h:45
Table should be terminated by an element with NULL in symbol_name.
Definition: rtos.h:23
symbol_address_t address
Definition: rtos.h:25
Definition: eCos.c:369
const char * name
Definition: eCos.c:370
bool optional
Definition: eCos.c:372
Definition: target.h:116
struct rtos * rtos
Definition: target.h:183
Definition: nuttx.c:44
uint16_t pid_off
Definition: nuttx.c:45
uint16_t pri_off
Definition: nuttx.c:47
uint16_t regs_off
Definition: nuttx.c:49
target_addr_t xcpreg_off
Definition: nuttx.c:52
uint16_t basic_num
Definition: nuttx.c:50
uint16_t total_num
Definition: nuttx.c:51
uint16_t state_off
Definition: nuttx.c:46
uint16_t name_off
Definition: nuttx.c:48
char * extra_info_str
Definition: rtos.h:33
char * thread_name_str
Definition: rtos.h:32
bool exists
Definition: rtos.h:31
threadid_t threadid
Definition: rtos.h:30
uint64_t target_buffer_get_u64(struct target *target, const uint8_t *buffer)
Definition: target.c:307
int target_read_buffer(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer)
Definition: target.c:2407
int target_read_u8(struct target *target, target_addr_t address, uint8_t *value)
Definition: target.c:2598
int target_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int *reg_list_size, enum target_register_class reg_class)
Obtain the registers for GDB.
Definition: target.c:1368
int target_read_u16(struct target *target, target_addr_t address, uint16_t *value)
Definition: target.c:2574
int target_read_u32(struct target *target, target_addr_t address, uint32_t *value)
Definition: target.c:2550
uint16_t target_buffer_get_u16(struct target *target, const uint8_t *buffer)
Definition: target.c:334
uint32_t target_buffer_get_u32(struct target *target, const uint8_t *buffer)
Definition: target.c:316
const char * target_type_name(const struct target *target)
Get the target type name.
Definition: target.c:736
@ REG_CLASS_GENERAL
Definition: target.h:112
#define ARRAY_SIZE(x)
Compute the number of elements of a variable length array.
Definition: types.h:57
uint64_t target_addr_t
Definition: types.h:335
#define NULL
Definition: usb.h:16
uint8_t state[4]
Definition: vdebug.c:21