rtos/nuttx: add Espressif target support and refactoring
[openocd.git] / src / rtos / nuttx.c
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;
35 const struct rtos_register_stacking *stacking;
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. */
61 enum nuttx_symbol_vals {
62 NX_SYM_READYTORUN = 0,
63 NX_SYM_PIDHASH,
64 NX_SYM_NPIDHASH,
65 NX_SYM_TCB_INFO,
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
90 static const struct rtos_register_stacking *cortexm_select_stackinfo(struct target *target);
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
138 static const struct rtos_register_stacking *cortexm_select_stackinfo(struct target *target)
139 {
140 return cortexm_hasfpu(target) ? &nuttx_stacking_cortex_m_fpu : &nuttx_stacking_cortex_m;
141 }
142
143 static bool nuttx_detect_rtos(struct target *target)
144 {
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;
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
186 return target_buffer_get_u64(target, buffer);
187 #else
188 return target_buffer_get_u32(target, buffer);
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 */
205 rtos_free_threadlist(rtos);
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 }
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);
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;
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;
265 }
266 rtos->current_thread = current_thread;
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
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;
310 }
311 snprintf(thread->extra_info_str, EXTRAINFO_SIZE, "pid:%d, %s",
312 pid,
313 task_state_str[state]);
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
340 static int nuttx_getreg_current_thread(struct rtos *rtos,
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,
348 REG_CLASS_GENERAL);
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 };

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)