target/cortex_m: drop useless target_halt() call
[openocd.git] / src / rtos / nuttx.c
blob0616af0f4c2f65c407ceb083b8eb306854bc263a
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 ***************************************************************************/
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
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"
24 #define NAME_SIZE 32
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.
31 #define PTR_WIDTH 4
33 struct nuttx_params {
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
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 */
55 struct symbols {
56 const char *name;
57 bool optional;
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,
63 NX_SYM_PIDHASH,
64 NX_SYM_NPIDHASH,
65 NX_SYM_TCB_INFO,
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 }
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",
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",
95 .stacking = NULL,
96 .select_stackinfo = cortexm_select_stackinfo,
99 .target_name = "hla_target",
100 .stacking = NULL,
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)
123 uint32_t cpacr;
124 struct armv7m_common *armv7m_target = target_to_armv7m(target);
126 if (!is_armv7m(armv7m_target) || armv7m_target->fp_feature == FP_NONE)
127 return false;
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;
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)
148 return true;
149 return false;
152 static int nuttx_create(struct target *target)
154 const struct nuttx_params *param;
155 unsigned int i;
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;
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;
170 /* We found a target in our list, copy its reference. */
171 target->rtos->rtos_specific_params = (void *)param;
173 return JIM_OK;
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. */
180 return ERROR_OK;
183 static target_addr_t target_buffer_get_addr(struct target *target, const uint8_t *buffer)
185 #if PTR_WIDTH == 8
186 return target_buffer_get_u64(target, buffer);
187 #else
188 return target_buffer_get_u32(target, buffer);
189 #endif
192 static int nuttx_update_threads(struct rtos *rtos)
194 struct tcbinfo tcbinfo;
195 uint32_t pidhashaddr, npidhash, tcbaddr;
196 uint16_t pid;
197 uint8_t state;
199 if (!rtos->symbols) {
200 LOG_ERROR("No symbols for nuttx");
201 return ERROR_FAIL;
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);
214 return ERROR_FAIL;
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);
222 return ERROR_FAIL;
225 LOG_DEBUG("Hash table address (g_pidhash) = %" PRIx32, pidhashaddr);
227 uint8_t *pidhash = malloc(npidhash * PTR_WIDTH);
228 if (!pidhash) {
229 LOG_ERROR("Failed to allocate pidhash");
230 return ERROR_FAIL;
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;
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);
246 goto errout;
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, &current_thread);
262 if (ret != ERROR_OK) {
263 LOG_ERROR("Failed to read g_readytorun: ret = %d", ret);
264 goto errout;
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]);
273 if (!tcbaddr)
274 continue;
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;
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;
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;
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;
303 thread_count++;
305 if (state < ARRAY_SIZE(task_state_str)) {
306 thread->extra_info_str = malloc(EXTRAINFO_SIZE);
307 if (!thread->extra_info_str) {
308 ret = ERROR_FAIL;
309 goto errout;
311 snprintf(thread->extra_info_str, EXTRAINFO_SIZE, "pid:%d, %s",
312 pid,
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) {
319 ret = ERROR_FAIL;
320 goto errout;
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;
328 } else {
329 thread->thread_name_str = strdup("None");
333 ret = ERROR_OK;
334 rtos->thread_count = thread_count;
335 errout:
336 free(pidhash);
337 return ret;
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,
348 REG_CLASS_GENERAL);
349 if (ret != ERROR_OK) {
350 LOG_ERROR("target_get_gdb_reg_list failed %d", ret);
351 return ret;
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;
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);
367 free(gdb_reg_list);
369 return ERROR_OK;
372 static int nuttx_getregs_fromstack(struct rtos *rtos, int64_t thread_id,
373 struct rtos_reg **reg_list, int *num_regs)
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;
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;
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;
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;
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)
409 if (!rtos) {
410 LOG_ERROR("NUTTX: out of memory");
411 return ERROR_FAIL;
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));
422 if (!*symbol_list) {
423 LOG_ERROR("NUTTX: out of memory");
424 return ERROR_FAIL;
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;
432 return ERROR_OK;
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,