ipdbg: fix double free of virtual-ir data
[openocd.git] / src / rtos / eCos.c
blob10ed12809f381f743d39c3de5a4c322d1a235f1d
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 /***************************************************************************
4 ***************************************************************************/
6 #ifdef HAVE_CONFIG_H
7 #include "config.h"
8 #endif
10 #include <helper/time_support.h>
11 #include <jtag/jtag.h>
12 #include "target/target.h"
13 #include "target/target_type.h"
14 #include "target/armv7m.h"
15 #include "rtos.h"
16 #include "helper/log.h"
17 #include "helper/types.h"
18 #include "helper/bits.h"
19 #include "rtos_standard_stackings.h"
20 #include "rtos_ecos_stackings.h"
21 #include "server/gdb_server.h"
23 /* Unfortunately for the moment we are limited to returning the hardwired
24 * register count (ARMV7M_NUM_CORE_REGS for Cortex-M) since the openocd RTOS
25 * support does not yet support accessing all per-thread "stacked"
26 * registers. e.g. For Cortex-M under eCos we have a per-thread BASEPRI, and for
27 * all eCos targets we may have per-thread VFP/FPU register state.
29 * So, for the moment, we continue to use the hardwired limit for the depth of
30 * the returned register description vector. The current openocd
31 * rtos_standard_stackings.c just provides the main core regs for the Cortex_M*
32 * targets regardless of whether FPU is present/enabled.
34 * However, this code is written with the expectation that we may eventually be
35 * able to provide more register information ("m-system" and "vfp" for example)
36 * and also with the expectation of supporting different register sets being
37 * returned depending on the per-thread Cortex-M eCos contex_m for
38 * example. Hence the fact that the eCos_stack_layout_*() functions below allow
39 * for the stack context descriptor vector to be returned by those calls
40 * allowing for eventual support where this code will potentially cache
41 * different sets of register descriptors for the different shapes of contexts
42 * in a *single* application/binary run-time.
44 * TODO: Extend openocd generic RTOS support to allow thread-specific system and
45 * FPU register state to be returned. */
47 struct ecos_params;
49 static bool ecos_detect_rtos(struct target *target);
50 static int ecos_create(struct target *target);
51 static int ecos_update_threads(struct rtos *rtos);
52 static int ecos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, struct rtos_reg **reg_list, int *num_regs);
53 static int ecos_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[]);
54 static int ecos_stack_layout_cortexm(struct rtos *rtos, struct ecos_params *param,
55 int64_t stack_ptr, const struct rtos_register_stacking **si);
56 static int ecos_stack_layout_arm(struct rtos *rtos, struct ecos_params *param,
57 int64_t stack_ptr, const struct rtos_register_stacking **si);
59 /* The current eCos thread IDentifier uses 0 as an unused (not a valid thread
60 * ID) value. Currently the unique_id field is 16-bits, but the eCos SMP support
61 * convention is that only 12-bits of the ID will be used. This
62 * ECOS_MAX_THREAD_COUNT manifest is provided to limit the potential for
63 * interpreting stale/inconsistent thread list state when the debug host scans
64 * the thread list before the target RTOS has completed its initialisation. This
65 * support will need to revisited when eCos is re-engineered to support more
66 * than 16 CPU SMP setups. */
67 #define ECOS_MAX_THREAD_COUNT (4095)
69 struct ecos_thread_state {
70 int value;
71 const char *desc;
74 /* The status is actually a logical-OR bitmask of states: */
75 enum ecos_thread_state_flags {
76 RUNNING = 0, /* explicit no-bits-set value */
77 SLEEPING = BIT(0),
78 COUNTSLEEP = BIT(1),
79 SUSPENDED = BIT(2),
80 CREATING = BIT(3),
81 EXITED = BIT(4),
82 SLEEPSET = (SLEEPING | COUNTSLEEP)
85 /* Cyg_Thread:: reason codes for wake and sleep fields: */
86 static const struct ecos_thread_state ecos_thread_reasons[] = {
87 { 0, "NONE" }, /* normally indicates "not yet started" */
88 { 1, "WAIT" }, /* wait with no timeout */
89 { 2, "DELAY" }, /* simple time delay */
90 { 3, "TIMEOUT" }, /* wait with timeout *or* timeout expired */
91 { 4, "BREAK" }, /* forced break out of sleep */
92 { 5, "DESTRUCT" }, /* wait on object being destroyed */
93 { 6, "EXIT" }, /* forced termination */
94 { 7, "DONE" } /* wait/delay completed */
97 static const char * const target_cortex_m[] = {
98 "cortex_m",
99 "hla_target",
100 NULL
103 static const char * const target_arm[] = {
104 "cortex_a",
105 "arm7tdmi",
106 "arm720t",
107 "arm9tdmi",
108 "arm920t",
109 "arm926ejs",
110 "arm946e",
111 "arm966e",
112 "arm11",
113 NULL
116 /* Since individual eCos application configurations may have different thread
117 * object structure layouts depending on the actual build-time enabled features
118 * we provide support for applications built containing the relevant symbolic
119 * support to match the actual application binary being debugged, rather than
120 * relying on a set of default/fixed (and potentially incorrect)
121 * offsets. However, for backwards compatibility, we do *NOT* enforce the
122 * requirement for the common extra helper symbols to be present to allow the
123 * fallback to the simple fixed CM3 model to avoid affecting existing users of
124 * older eCos worlds. Similarly we need to provide support for per-thread
125 * register context offsets, as well as for per-application-configurations,
126 * since some targets can have different stacked state on a per-thread basis
127 * (e.g. "cortex_m"). This is why the stacking_info is now set at run-time
128 * rather than being fixed. */
130 struct ecos_params {
131 const char * const *target_names; /* NULL terminated list of targets */
132 int (*target_stack_layout)(struct rtos *rtos, struct ecos_params *param,
133 int64_t stack_ptr, const struct rtos_register_stacking **si);
134 bool flush_common;
135 unsigned char pointer_width;
136 unsigned char uid_width;
137 unsigned char state_width;
138 unsigned int thread_stack_offset;
139 unsigned int thread_name_offset;
140 unsigned int thread_state_offset;
141 unsigned int thread_next_offset;
142 unsigned int thread_uniqueid_offset;
143 const struct rtos_register_stacking *stacking_info;
146 /* As mentioned above we provide default offset values for the "cortex_m"
147 * targets for backwards compatibility with older eCos application builds and
148 * previous users of this RTOS specific support that do not have the
149 * configuration specific offsets provided in the symbol table. The support for
150 * other targets (e.g. "cortex_a") we do expect the application to provide the
151 * required symbolic information. We do not populate the stacking_info reference
152 * until we have had a chance to interrogate the symbol table. */
154 static struct ecos_params ecos_params_list[] = {
156 .target_names = target_cortex_m,
157 .pointer_width = 4,
158 .uid_width = 2,
159 .state_width = 4,
160 .thread_stack_offset = 0x0c,
161 .thread_name_offset = 0x9c,
162 .thread_state_offset = 0x3c,
163 .thread_next_offset = 0xa0,
164 .thread_uniqueid_offset = 0x4c,
165 .target_stack_layout = ecos_stack_layout_cortexm,
166 .stacking_info = NULL
169 .target_names = target_arm,
170 .pointer_width = 0,
171 .uid_width = 0,
172 .state_width = 0,
173 .thread_stack_offset = 0,
174 .thread_name_offset = 0,
175 .thread_state_offset = 0,
176 .thread_next_offset = 0,
177 .thread_uniqueid_offset = 0,
178 .target_stack_layout = ecos_stack_layout_arm,
179 .stacking_info = NULL
183 #define ECOS_NUM_PARAMS ARRAY_SIZE(ecos_params_list)
185 /* To eventually allow for more than just the ARMV7M_NUM_CORE_REGS to be
186 * returned by the Cortex-M support, and to avoid run-time lookups we manually
187 * maintain our own mapping for the supplied stack register vector entries. This
188 * enum needs to match the rtos_ecos_regoff_cortexm[] vector. Admittedly the
189 * initial indices just match the corresponding ARMV7M_R* definitions, but after
190 * the base registers the ARMV7M_* number space does not match the vector we
191 * wish to populate in this eCos support code. */
192 enum ecos_reglist_cortexm {
193 ECOS_REGLIST_R0 = 0,
194 ECOS_REGLIST_R1,
195 ECOS_REGLIST_R2,
196 ECOS_REGLIST_R3,
197 ECOS_REGLIST_R4,
198 ECOS_REGLIST_R5,
199 ECOS_REGLIST_R6,
200 ECOS_REGLIST_R7,
201 ECOS_REGLIST_R8,
202 ECOS_REGLIST_R9,
203 ECOS_REGLIST_R10,
204 ECOS_REGLIST_R11,
205 ECOS_REGLIST_R12,
206 ECOS_REGLIST_R13,
207 ECOS_REGLIST_R14,
208 ECOS_REGLIST_PC,
209 ECOS_REGLIST_XPSR, /* ARMV7M_NUM_CORE_REGS */
210 ECOS_REGLIST_BASEPRI,
211 ECOS_REGLIST_FPSCR, /* Following for FPU contexts */
212 ECOS_REGLIST_D0,
213 ECOS_REGLIST_D1,
214 ECOS_REGLIST_D2,
215 ECOS_REGLIST_D3,
216 ECOS_REGLIST_D4,
217 ECOS_REGLIST_D5,
218 ECOS_REGLIST_D6,
219 ECOS_REGLIST_D7,
220 ECOS_REGLIST_D8,
221 ECOS_REGLIST_D9,
222 ECOS_REGLIST_D10,
223 ECOS_REGLIST_D11,
224 ECOS_REGLIST_D12,
225 ECOS_REGLIST_D13,
226 ECOS_REGLIST_D14,
227 ECOS_REGLIST_D15
230 #define ECOS_CORTEXM_BASE_NUMREGS (ARMV7M_NUM_CORE_REGS)
232 /* NOTE: The offsets in this vector are overwritten by the architecture specific
233 * layout functions depending on the specific application configuration. The
234 * ordering of this vector MUST match eCos_reglist. */
235 static struct stack_register_offset rtos_ecos_regoff_cortexm[] = {
236 { ARMV7M_R0, -1, 32 }, /* r0 */
237 { ARMV7M_R1, -1, 32 }, /* r1 */
238 { ARMV7M_R2, -1, 32 }, /* r2 */
239 { ARMV7M_R3, -1, 32 }, /* r3 */
240 { ARMV7M_R4, -1, 32 }, /* r4 */
241 { ARMV7M_R5, -1, 32 }, /* r5 */
242 { ARMV7M_R6, -1, 32 }, /* r6 */
243 { ARMV7M_R7, -1, 32 }, /* r7 */
244 { ARMV7M_R8, -1, 32 }, /* r8 */
245 { ARMV7M_R9, -1, 32 }, /* r9 */
246 { ARMV7M_R10, -1, 32 }, /* r10 */
247 { ARMV7M_R11, -1, 32 }, /* r11 */
248 { ARMV7M_R12, -1, 32 }, /* r12 */
249 { ARMV7M_R13, -1, 32 }, /* sp */
250 { ARMV7M_R14, -1, 32 }, /* lr */
251 { ARMV7M_PC, -1, 32 }, /* pc */
252 { ARMV7M_XPSR, -1, 32 }, /* xPSR */
253 { ARMV7M_BASEPRI, -1, 32 }, /* BASEPRI */
254 { ARMV7M_FPSCR, -1, 32 }, /* FPSCR */
255 { ARMV7M_D0, -1, 64 }, /* D0 (S0/S1) */
256 { ARMV7M_D1, -1, 64 }, /* D1 (S2/S3) */
257 { ARMV7M_D2, -1, 64 }, /* D2 (S4/S5) */
258 { ARMV7M_D3, -1, 64 }, /* D3 (S6/S7) */
259 { ARMV7M_D4, -1, 64 }, /* D4 (S8/S9) */
260 { ARMV7M_D5, -1, 64 }, /* D5 (S10/S11) */
261 { ARMV7M_D6, -1, 64 }, /* D6 (S12/S13) */
262 { ARMV7M_D7, -1, 64 }, /* D7 (S14/S15) */
263 { ARMV7M_D8, -1, 64 }, /* D8 (S16/S17) */
264 { ARMV7M_D9, -1, 64 }, /* D9 (S18/S19) */
265 { ARMV7M_D10, -1, 64 }, /* D10 (S20/S21) */
266 { ARMV7M_D11, -1, 64 }, /* D11 (S22/S23) */
267 { ARMV7M_D12, -1, 64 }, /* D12 (S24/S25) */
268 { ARMV7M_D13, -1, 64 }, /* D13 (S26/S27) */
269 { ARMV7M_D14, -1, 64 }, /* D14 (S28/S29) */
270 { ARMV7M_D15, -1, 64 }, /* D15 (S30/S31) */
273 static struct stack_register_offset rtos_ecos_regoff_arm[] = {
274 { 0, -1, 32 }, /* r0 */
275 { 1, -1, 32 }, /* r1 */
276 { 2, -1, 32 }, /* r2 */
277 { 3, -1, 32 }, /* r3 */
278 { 4, -1, 32 }, /* r4 */
279 { 5, -1, 32 }, /* r5 */
280 { 6, -1, 32 }, /* r6 */
281 { 7, -1, 32 }, /* r7 */
282 { 8, -1, 32 }, /* r8 */
283 { 9, -1, 32 }, /* r9 */
284 { 10, -1, 32 }, /* r10 */
285 { 11, -1, 32 }, /* r11 (fp) */
286 { 12, -1, 32 }, /* r12 (ip) */
287 { 13, -1, 32 }, /* sp (r13) */
288 { 14, -1, 32 }, /* lr (r14) */
289 { 15, -1, 32 }, /* pc (r15) */
290 { 16, -1, 32 }, /* xPSR */
293 static struct rtos_register_stacking rtos_ecos_stacking = {
294 .stack_registers_size = 0,
295 .stack_growth_direction = -1,
296 .num_output_registers = 0,
297 .calculate_process_stack = NULL, /* stack_alignment */
298 .register_offsets = NULL
301 /* To avoid the run-time cost of matching explicit symbol names we push the
302 * lookup offsets to this *manually* maintained enumeration which must match the
303 * ecos_symbol_list[] order below. */
304 enum ecos_symbol_values {
305 ECOS_VAL_THREAD_LIST = 0,
306 ECOS_VAL_CURRENT_THREAD_PTR,
307 ECOS_VAL_COMMON_THREAD_NEXT_OFF,
308 ECOS_VAL_COMMON_THREAD_NEXT_SIZE,
309 ECOS_VAL_COMMON_THREAD_STATE_OFF,
310 ECOS_VAL_COMMON_THREAD_STATE_SIZE,
311 ECOS_VAL_COMMON_THREAD_SLEEP_OFF,
312 ECOS_VAL_COMMON_THREAD_SLEEP_SIZE,
313 ECOS_VAL_COMMON_THREAD_WAKE_OFF,
314 ECOS_VAL_COMMON_THREAD_WAKE_SIZE,
315 ECOS_VAL_COMMON_THREAD_ID_OFF,
316 ECOS_VAL_COMMON_THREAD_ID_SIZE,
317 ECOS_VAL_COMMON_THREAD_NAME_OFF,
318 ECOS_VAL_COMMON_THREAD_NAME_SIZE,
319 ECOS_VAL_COMMON_THREAD_PRI_OFF,
320 ECOS_VAL_COMMON_THREAD_PRI_SIZE,
321 ECOS_VAL_COMMON_THREAD_STACK_OFF,
322 ECOS_VAL_COMMON_THREAD_STACK_SIZE,
323 ECOS_VAL_CORTEXM_THREAD_SAVED,
324 ECOS_VAL_CORTEXM_CTX_THREAD_SIZE,
325 ECOS_VAL_CORTEXM_CTX_TYPE_OFF,
326 ECOS_VAL_CORTEXM_CTX_TYPE_SIZE,
327 ECOS_VAL_CORTEXM_CTX_BASEPRI_OFF,
328 ECOS_VAL_CORTEXM_CTX_BASEPRI_SIZE,
329 ECOS_VAL_CORTEXM_CTX_SP_OFF,
330 ECOS_VAL_CORTEXM_CTX_SP_SIZE,
331 ECOS_VAL_CORTEXM_CTX_REG_OFF,
332 ECOS_VAL_CORTEXM_CTX_REG_SIZE,
333 ECOS_VAL_CORTEXM_CTX_PC_OFF,
334 ECOS_VAL_CORTEXM_CTX_PC_SIZE,
335 ECOS_VAL_CORTEXM_VAL_EXCEPTION,
336 ECOS_VAL_CORTEXM_VAL_THREAD,
337 ECOS_VAL_CORTEXM_VAL_INTERRUPT,
338 ECOS_VAL_CORTEXM_VAL_FPU,
339 ECOS_VAL_CORTEXM_CTX_FPSCR_OFF,
340 ECOS_VAL_CORTEXM_CTX_FPSCR_SIZE,
341 ECOS_VAL_CORTEXM_CTX_S_OFF,
342 ECOS_VAL_CORTEXM_CTX_S_SIZE,
343 ECOS_VAL_ARM_REGSIZE,
344 ECOS_VAL_ARM_CTX_R0_OFF,
345 ECOS_VAL_ARM_CTX_R1_OFF,
346 ECOS_VAL_ARM_CTX_R2_OFF,
347 ECOS_VAL_ARM_CTX_R3_OFF,
348 ECOS_VAL_ARM_CTX_R4_OFF,
349 ECOS_VAL_ARM_CTX_R5_OFF,
350 ECOS_VAL_ARM_CTX_R6_OFF,
351 ECOS_VAL_ARM_CTX_R7_OFF,
352 ECOS_VAL_ARM_CTX_R8_OFF,
353 ECOS_VAL_ARM_CTX_R9_OFF,
354 ECOS_VAL_ARM_CTX_R10_OFF,
355 ECOS_VAL_ARM_CTX_FP_OFF,
356 ECOS_VAL_ARM_CTX_IP_OFF,
357 ECOS_VAL_ARM_CTX_SP_OFF,
358 ECOS_VAL_ARM_CTX_LR_OFF,
359 ECOS_VAL_ARM_CTX_PC_OFF,
360 ECOS_VAL_ARM_CTX_CPSR_OFF,
361 ECOS_VAL_ARM_FPUSIZE,
362 ECOS_VAL_ARM_CTX_FPSCR_OFF,
363 ECOS_VAL_ARM_SCOUNT,
364 ECOS_VAL_ARM_CTX_SVEC_OFF,
365 ECOS_VAL_ARM_VFPCOUNT,
366 ECOS_VAL_ARM_CTX_VFPVEC_OFF
369 struct symbols {
370 const char *name;
371 const char * const *target_names; /* non-NULL when for a specific architecture */
372 bool optional;
375 #define ECOSSYM(_n, _o, _t) { .name = _n, .optional = (_o), .target_names = _t }
377 /* Some of offset/size helper symbols are common to all eCos
378 * targets. Unfortunately, for historical reasons, some information is in
379 * architecture specific namespaces leading to some duplication and a larger
380 * vector below. */
381 static const struct symbols ecos_symbol_list[] = {
382 ECOSSYM("Cyg_Thread::thread_list", false, NULL),
383 ECOSSYM("Cyg_Scheduler_Base::current_thread", false, NULL),
384 /* Following symbols *are* required for generic application-specific
385 * configuration support, but we mark as optional for backwards
386 * compatibility with the previous fixed Cortex-M3 only RTOS plugin
387 * implementation. */
388 ECOSSYM("__ecospro_syminfo.off.cyg_thread.list_next", true, NULL),
389 ECOSSYM("__ecospro_syminfo.size.cyg_thread.list_next", true, NULL),
390 ECOSSYM("__ecospro_syminfo.off.cyg_thread.state", true, NULL),
391 ECOSSYM("__ecospro_syminfo.size.cyg_thread.state", true, NULL),
392 ECOSSYM("__ecospro_syminfo.off.cyg_thread.sleep_reason", true, NULL),
393 ECOSSYM("__ecospro_syminfo.size.cyg_thread.sleep_reason", true, NULL),
394 ECOSSYM("__ecospro_syminfo.off.cyg_thread.wake_reason", true, NULL),
395 ECOSSYM("__ecospro_syminfo.size.cyg_thread.wake_reason", true, NULL),
396 ECOSSYM("__ecospro_syminfo.off.cyg_thread.unique_id", true, NULL),
397 ECOSSYM("__ecospro_syminfo.size.cyg_thread.unique_id", true, NULL),
398 ECOSSYM("__ecospro_syminfo.off.cyg_thread.name", true, NULL),
399 ECOSSYM("__ecospro_syminfo.size.cyg_thread.name", true, NULL),
400 ECOSSYM("__ecospro_syminfo.off.cyg_thread.priority", true, NULL),
401 ECOSSYM("__ecospro_syminfo.size.cyg_thread.priority", true, NULL),
402 ECOSSYM("__ecospro_syminfo.off.cyg_thread.stack_ptr", true, NULL),
403 ECOSSYM("__ecospro_syminfo.size.cyg_thread.stack_ptr", true, NULL),
404 /* optional Cortex-M: */
405 ECOSSYM("__ecospro_syminfo.cortexm.thread.saved", true, target_cortex_m),
406 ECOSSYM("__ecospro_syminfo.size.HAL_SavedRegisters.Thread", true, target_cortex_m),
407 ECOSSYM("__ecospro_syminfo.off.HAL_SavedRegisters.u.thread.type", true, target_cortex_m),
408 ECOSSYM("__ecospro_syminfo.size.HAL_SavedRegisters.u.thread.type", true, target_cortex_m),
409 ECOSSYM("__ecospro_syminfo.off.HAL_SavedRegisters.u.thread.basepri", true, target_cortex_m),
410 ECOSSYM("__ecospro_syminfo.size.HAL_SavedRegisters.u.thread.basepri", true, target_cortex_m),
411 ECOSSYM("__ecospro_syminfo.off.HAL_SavedRegisters.u.thread.sp", true, target_cortex_m),
412 ECOSSYM("__ecospro_syminfo.size.HAL_SavedRegisters.u.thread.sp", true, target_cortex_m),
413 ECOSSYM("__ecospro_syminfo.off.HAL_SavedRegisters.u.thread.r", true, target_cortex_m),
414 ECOSSYM("__ecospro_syminfo.size.HAL_SavedRegisters.u.thread.r", true, target_cortex_m),
415 ECOSSYM("__ecospro_syminfo.off.HAL_SavedRegisters.u.thread.pc", true, target_cortex_m),
416 ECOSSYM("__ecospro_syminfo.size.HAL_SavedRegisters.u.thread.pc", true, target_cortex_m),
417 ECOSSYM("__ecospro_syminfo.value.HAL_SAVEDREGISTERS.EXCEPTION", true, target_cortex_m),
418 ECOSSYM("__ecospro_syminfo.value.HAL_SAVEDREGISTERS.THREAD", true, target_cortex_m),
419 ECOSSYM("__ecospro_syminfo.value.HAL_SAVEDREGISTERS.INTERRUPT", true, target_cortex_m),
420 /* optional Cortex-M with H/W FPU configured: */
421 ECOSSYM("__ecospro_syminfo.value.HAL_SAVEDREGISTERS.WITH_FPU", true, target_cortex_m),
422 ECOSSYM("__ecospro_syminfo.off.HAL_SavedRegisters.u.thread.fpscr", true, target_cortex_m),
423 ECOSSYM("__ecospro_syminfo.size.HAL_SavedRegisters.u.thread.fpscr", true, target_cortex_m),
424 ECOSSYM("__ecospro_syminfo.off.HAL_SavedRegisters.u.thread.s", true, target_cortex_m),
425 ECOSSYM("__ecospro_syminfo.size.HAL_SavedRegisters.u.thread.s", true, target_cortex_m),
426 /* optional ARM: */
427 ECOSSYM("ARMREG_SIZE", true, target_arm),
428 ECOSSYM("armreg_r0", true, target_arm),
429 ECOSSYM("armreg_r1", true, target_arm),
430 ECOSSYM("armreg_r2", true, target_arm),
431 ECOSSYM("armreg_r3", true, target_arm),
432 ECOSSYM("armreg_r4", true, target_arm),
433 ECOSSYM("armreg_r5", true, target_arm),
434 ECOSSYM("armreg_r6", true, target_arm),
435 ECOSSYM("armreg_r7", true, target_arm),
436 ECOSSYM("armreg_r8", true, target_arm),
437 ECOSSYM("armreg_r9", true, target_arm),
438 ECOSSYM("armreg_r10", true, target_arm),
439 ECOSSYM("armreg_fp", true, target_arm),
440 ECOSSYM("armreg_ip", true, target_arm),
441 ECOSSYM("armreg_sp", true, target_arm),
442 ECOSSYM("armreg_lr", true, target_arm),
443 ECOSSYM("armreg_pc", true, target_arm),
444 ECOSSYM("armreg_cpsr", true, target_arm),
445 /* optional ARM FPU common: */
446 ECOSSYM("ARMREG_FPUCONTEXT_SIZE", true, target_arm),
447 ECOSSYM("armreg_fpscr", true, target_arm),
448 /* optional ARM FPU single-precision: */
449 ECOSSYM("ARMREG_S_COUNT", true, target_arm),
450 ECOSSYM("armreg_s_vec", true, target_arm),
451 /* optional ARM FPU double-precision: */
452 ECOSSYM("ARMREG_VFP_COUNT", true, target_arm),
453 ECOSSYM("armreg_vfp_vec", true, target_arm),
456 const struct rtos_type ecos_rtos = {
457 .name = "eCos",
459 .detect_rtos = ecos_detect_rtos,
460 .create = ecos_create,
461 .update_threads = ecos_update_threads,
462 .get_thread_reg_list = ecos_get_thread_reg_list,
463 .get_symbol_list_to_lookup = ecos_get_symbol_list_to_lookup,
467 static symbol_address_t ecos_value(struct rtos *rtos, unsigned int idx)
469 if (idx < ARRAY_SIZE(ecos_symbol_list))
470 return rtos->symbols[idx].address;
472 /* We do not terminate, just return 0 in this case. */
473 LOG_ERROR("eCos: Invalid symbol index %u", idx);
474 return 0;
477 #define XMLENTRY(_c, _s) { .xc = (_c), .rs = (_s), .rlen = (sizeof(_s) - 1) }
479 static const struct {
480 char xc;
481 const char *rs;
482 size_t rlen;
483 } xmlchars[] = {
484 XMLENTRY('<', "&lt;"),
485 XMLENTRY('&', "&amp;"),
486 XMLENTRY('>', "&gt;"),
487 XMLENTRY('\'', "&apos;"),
488 XMLENTRY('"', "&quot;")
491 /** Escape any XML reserved characters in a string. */
492 static bool ecos_escape_string(const char *raw, char *out, size_t limit)
494 static const char *tokens = "<&>\'\"";
495 bool escaped = false;
497 if (!out || !limit)
498 return false;
500 (void)memset(out, '\0', limit);
502 while (raw && *raw && limit) {
503 size_t lok = strcspn(raw, tokens);
504 if (lok) {
505 size_t tocopy;
506 tocopy = ((limit < lok) ? limit : lok);
507 (void)memcpy(out, raw, tocopy);
508 limit -= tocopy;
509 out += tocopy;
510 raw += lok;
511 continue;
514 char *fidx = strchr(tokens, *raw);
515 if (!fidx) {
516 /* Should never happen assuming xmlchars
517 * vector and tokens string match. */
518 LOG_ERROR("eCos: Unexpected XML char %c", *raw);
519 continue;
522 uint32_t cidx = (fidx - tokens);
523 size_t tocopy = xmlchars[cidx].rlen;
524 if (limit < tocopy)
525 break;
527 escaped = true;
528 (void)memcpy(out, xmlchars[cidx].rs, tocopy);
529 limit -= tocopy;
530 out += tocopy;
531 raw++;
534 return escaped;
537 static int ecos_check_app_info(struct rtos *rtos, struct ecos_params *param)
539 if (!rtos || !param)
540 return -1;
542 if (param->flush_common) {
543 if (debug_level >= LOG_LVL_DEBUG) {
544 for (unsigned int idx = 0; idx < ARRAY_SIZE(ecos_symbol_list); idx++) {
545 LOG_DEBUG("eCos: %s 0x%016" PRIX64 " %s",
546 rtos->symbols[idx].optional ? "OPTIONAL" : " ",
547 rtos->symbols[idx].address, rtos->symbols[idx].symbol_name);
551 /* If "__ecospro_syminfo.size.cyg_thread.list_next" is non-zero then we
552 * expect all of the generic thread structure symbols to have been
553 * provided. */
554 symbol_address_t thread_next_size = ecos_value(rtos, ECOS_VAL_COMMON_THREAD_NEXT_SIZE);
555 if (thread_next_size != 0) {
556 param->pointer_width = thread_next_size;
557 param->uid_width = ecos_value(rtos, ECOS_VAL_COMMON_THREAD_ID_SIZE);
558 param->state_width = ecos_value(rtos, ECOS_VAL_COMMON_THREAD_STATE_SIZE);
559 param->thread_stack_offset = ecos_value(rtos, ECOS_VAL_COMMON_THREAD_STACK_OFF);
560 param->thread_name_offset = ecos_value(rtos, ECOS_VAL_COMMON_THREAD_NAME_OFF);
561 param->thread_state_offset = ecos_value(rtos, ECOS_VAL_COMMON_THREAD_STATE_OFF);
562 param->thread_next_offset = ecos_value(rtos, ECOS_VAL_COMMON_THREAD_NEXT_OFF);
563 param->thread_uniqueid_offset = ecos_value(rtos, ECOS_VAL_COMMON_THREAD_ID_OFF);
566 if (param->uid_width != sizeof(uint16_t)) {
567 /* Currently all eCos configurations use a 16-bit field to hold the
568 * unique thread ID. */
569 LOG_WARNING("eCos: Unexpected unique_id width %" PRIu8, param->uid_width);
570 param->uid_width = (unsigned char)sizeof(uint16_t);
573 param->stacking_info = NULL;
574 param->flush_common = false;
577 return ERROR_OK;
580 /* The Cortex-M eCosPro "thread" contexts have a "type" indicator, which tracks
581 * the context state of (THREAD | EXCEPTION | INTERRUPT) and whether FPU
582 * registers are saved.
584 * For thread-aware debugging from GDB we are only interested in THREAD states
585 * and so do not need to implement support for INTERRUPT or EXCEPTION thread
586 * contexts since this code does not expose those stack contexts via the
587 * constructed thread list support. */
588 static int ecos_stack_layout_cortexm(struct rtos *rtos,
589 struct ecos_params *param, int64_t stack_ptr,
590 const struct rtos_register_stacking **si)
592 int retval = ERROR_OK;
594 /* CONSIDER: We could return
595 * ecos_value(rtos, ECOS_VAL_CORTEXM_THREAD_SAVED) as the actual PC
596 * address of a context switch, with the LR being set to the context PC
597 * field to give a true representation of where the thread switch
598 * occurs. However that would require extending the common
599 * rtos_generic_stack_read() code with suitable support for applying a
600 * supplied value, or just implementing our own version of that code that
601 * can inject data into what is passed onwards to GDB. */
603 /* UPDATE: When we can return VFP register state then we will NOT be
604 * basing the cached state on the single param->stacking_info value,
605 * since we will need a different stacking_info structure returned for
606 * each thread type when FPU support is enabled. The use of the single
607 * param->stacking_info is a holder whilst we are limited to the fixed
608 * ARMV7M_NUM_CORE_REGS set of descriptors. */
610 if (!param->stacking_info &&
611 ecos_value(rtos, ECOS_VAL_CORTEXM_THREAD_SAVED) &&
612 ecos_value(rtos, ECOS_VAL_CORTEXM_VAL_THREAD)) {
613 unsigned char numoutreg = ECOS_CORTEXM_BASE_NUMREGS;
615 rtos_ecos_stacking.stack_registers_size = ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_THREAD_SIZE);
616 rtos_ecos_stacking.calculate_process_stack = rtos_generic_stack_align8;
617 rtos_ecos_stacking.register_offsets = rtos_ecos_regoff_cortexm;
619 rtos_ecos_regoff_cortexm[ECOS_REGLIST_R0].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x00);
620 rtos_ecos_regoff_cortexm[ECOS_REGLIST_R1].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x04);
621 rtos_ecos_regoff_cortexm[ECOS_REGLIST_R2].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x08);
622 rtos_ecos_regoff_cortexm[ECOS_REGLIST_R3].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x0C);
623 rtos_ecos_regoff_cortexm[ECOS_REGLIST_R4].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x10);
624 rtos_ecos_regoff_cortexm[ECOS_REGLIST_R5].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x14);
625 rtos_ecos_regoff_cortexm[ECOS_REGLIST_R6].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x18);
626 rtos_ecos_regoff_cortexm[ECOS_REGLIST_R7].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x1C);
627 rtos_ecos_regoff_cortexm[ECOS_REGLIST_R8].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x20);
628 rtos_ecos_regoff_cortexm[ECOS_REGLIST_R9].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x24);
629 rtos_ecos_regoff_cortexm[ECOS_REGLIST_R10].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x28);
630 rtos_ecos_regoff_cortexm[ECOS_REGLIST_R11].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x2C);
631 rtos_ecos_regoff_cortexm[ECOS_REGLIST_R12].offset = (ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_REG_OFF) + 0x30);
632 /* Rather than using the stacked ECOS_VAL_CORTEXM_CTX_SP_OFF
633 * value we force the reported sp to be after the stacked
634 * register context. */
635 rtos_ecos_regoff_cortexm[ECOS_REGLIST_R13].offset = -2;
636 rtos_ecos_regoff_cortexm[ECOS_REGLIST_R14].offset = -1;
637 rtos_ecos_regoff_cortexm[ECOS_REGLIST_PC].offset = ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_PC_OFF);
638 rtos_ecos_regoff_cortexm[ECOS_REGLIST_XPSR].offset = -1;
640 param->stacking_info = &rtos_ecos_stacking;
642 /* Common Cortex-M thread register offsets for the current
643 * symbol table: */
644 if (retval == ERROR_OK && param->stacking_info) {
645 if (numoutreg > ECOS_REGLIST_BASEPRI) {
646 rtos_ecos_regoff_cortexm[ECOS_REGLIST_BASEPRI].offset =
647 ecos_value(rtos, ECOS_VAL_CORTEXM_CTX_BASEPRI_OFF);
650 rtos_ecos_stacking.num_output_registers = numoutreg;
654 if (si)
655 *si = param->stacking_info;
657 return retval;
660 static int ecos_stack_layout_arm(struct rtos *rtos, struct ecos_params *param,
661 int64_t stack_ptr, const struct rtos_register_stacking **si)
663 int retval = ERROR_OK;
665 if (!param->stacking_info && ecos_value(rtos, ECOS_VAL_ARM_REGSIZE)) {
666 /* When OpenOCD is extended to allow FPU registers to be returned from a
667 * stacked thread context we can check:
668 * if (0 != ecos_value(rtos, ECOS_VAL_ARM_FPUSIZE)) { FPU }
669 * for presence of FPU registers in the context. */
671 rtos_ecos_stacking.stack_registers_size = ecos_value(rtos, ECOS_VAL_ARM_REGSIZE);
672 rtos_ecos_stacking.num_output_registers = ARRAY_SIZE(rtos_ecos_regoff_arm);
673 rtos_ecos_stacking.register_offsets = rtos_ecos_regoff_arm;
675 rtos_ecos_regoff_arm[0].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R0_OFF);
676 rtos_ecos_regoff_arm[1].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R1_OFF);
677 rtos_ecos_regoff_arm[2].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R2_OFF);
678 rtos_ecos_regoff_arm[3].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R3_OFF);
679 rtos_ecos_regoff_arm[4].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R4_OFF);
680 rtos_ecos_regoff_arm[5].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R5_OFF);
681 rtos_ecos_regoff_arm[6].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R6_OFF);
682 rtos_ecos_regoff_arm[7].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R7_OFF);
683 rtos_ecos_regoff_arm[8].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R8_OFF);
684 rtos_ecos_regoff_arm[9].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R9_OFF);
685 rtos_ecos_regoff_arm[10].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_R10_OFF);
686 rtos_ecos_regoff_arm[11].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_FP_OFF);
687 rtos_ecos_regoff_arm[12].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_IP_OFF);
688 rtos_ecos_regoff_arm[13].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_SP_OFF);
689 rtos_ecos_regoff_arm[14].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_LR_OFF);
690 rtos_ecos_regoff_arm[15].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_PC_OFF);
691 rtos_ecos_regoff_arm[16].offset = ecos_value(rtos, ECOS_VAL_ARM_CTX_CPSR_OFF);
693 param->stacking_info = &rtos_ecos_stacking;
696 if (si)
697 *si = param->stacking_info;
699 return retval;
702 /* We see this function called on a new connection, it looks like before and
703 * after the "tar rem"/"tar extended-remote". It might be the only point we can
704 * decide to cache information (to check if the symbol table has changed). */
705 static int ecos_update_threads(struct rtos *rtos)
707 int retval;
708 int tasks_found = 0;
709 int thread_list_size = 0;
710 struct ecos_params *param;
712 if (!rtos)
713 return -1;
715 /* wipe out previous thread details if any */
716 rtos_free_threadlist(rtos);
718 if (!rtos->rtos_specific_params)
719 return -3;
721 param = rtos->rtos_specific_params;
723 if (!rtos->symbols) {
724 /* NOTE: We only see this when connecting from GDB the first
725 * time before the application image is loaded. So it is not a
726 * hook for detecting an application change. */
727 param->flush_common = true;
728 LOG_ERROR("No symbols for eCos");
729 return -4;
732 retval = ecos_check_app_info(rtos, param);
733 if (retval != ERROR_OK)
734 return retval;
736 if (rtos->symbols[ECOS_VAL_THREAD_LIST].address == 0) {
737 LOG_ERROR("Don't have the thread list head");
738 return -2;
741 /* determine the number of current threads */
742 uint32_t thread_list_head = rtos->symbols[ECOS_VAL_THREAD_LIST].address;
743 uint32_t thread_index;
744 target_read_buffer(rtos->target,
745 thread_list_head,
746 param->pointer_width,
747 (uint8_t *) &thread_index);
748 uint32_t first_thread = thread_index;
750 /* Even if 0==first_thread indicates a system with no defined eCos
751 * threads, instead of early exiting here we fall through the code to
752 * allow the creation of a faked "Current Execution" descriptor as
753 * needed. */
755 if (first_thread) {
756 /* Since the OpenOCD RTOS support can attempt to obtain thread
757 * information on initial connection when the system *may* have
758 * undefined memory state it is possible for a simple thread count scan
759 * to produce invalid results. To avoid blocking indefinitely when
760 * encountering an invalid closed loop we limit the number of threads to
761 * the maximum possible, and if we pass that limit then something is
762 * wrong so treat the system as having no threads defined. */
763 do {
764 thread_list_size++;
765 if (thread_list_size > ECOS_MAX_THREAD_COUNT) {
766 /* Treat as "no threads" case: */
767 first_thread = 0;
768 thread_list_size = 0;
769 break;
771 retval = target_read_buffer(rtos->target,
772 thread_index + param->thread_next_offset,
773 param->pointer_width,
774 (uint8_t *)&thread_index);
775 if (retval != ERROR_OK)
776 return retval;
777 } while (thread_index != first_thread);
780 /* read the current thread id */
781 rtos->current_thread = 0;
783 uint32_t current_thread_addr;
784 retval = target_read_buffer(rtos->target,
785 rtos->symbols[ECOS_VAL_CURRENT_THREAD_PTR].address,
786 param->pointer_width,
787 (uint8_t *)&current_thread_addr);
788 if (retval != ERROR_OK) {
789 LOG_ERROR("Reading active thread address");
790 return retval;
793 if (current_thread_addr) {
794 uint16_t id = 0;
795 retval = target_read_buffer(rtos->target,
796 current_thread_addr + param->thread_uniqueid_offset,
797 param->uid_width,
798 (uint8_t *)&id);
799 if (retval != ERROR_OK) {
800 LOG_ERROR("Could not read eCos current thread from target");
801 return retval;
803 rtos->current_thread = (threadid_t)id;
806 if (thread_list_size == 0 || rtos->current_thread == 0) {
807 /* Either : No RTOS threads - there is always at least the current execution though */
808 /* OR : No current thread - all threads suspended - show the current execution
809 * of idling */
810 static const char tmp_str[] = "Current Execution";
811 thread_list_size++;
812 tasks_found++;
813 rtos->thread_details = malloc(
814 sizeof(struct thread_detail) * thread_list_size);
815 /* 1 is a valid eCos thread id, so we return 0 for this faked
816 * "current" CPU state: */
817 rtos->thread_details->threadid = 0;
818 rtos->thread_details->exists = true;
819 rtos->thread_details->extra_info_str = NULL;
820 rtos->thread_details->thread_name_str = malloc(sizeof(tmp_str));
821 strcpy(rtos->thread_details->thread_name_str, tmp_str);
823 /* Early exit if current CPU state our only "thread": */
824 if (thread_list_size == 1) {
825 rtos->thread_count = 1;
826 return ERROR_OK;
828 } else {
829 /* create space for new thread details */
830 rtos->thread_details = malloc(
831 sizeof(struct thread_detail) * thread_list_size);
834 /* loop over all threads */
835 thread_index = first_thread;
836 do {
837 #define ECOS_THREAD_NAME_STR_SIZE (200)
838 char tmp_str[ECOS_THREAD_NAME_STR_SIZE];
839 uint32_t name_ptr = 0;
840 uint32_t prev_thread_ptr;
842 /* Save the thread ID. For eCos the thread has a unique ID distinct from
843 * the thread_index descriptor pointer. We present this scheduler ID
844 * instead of the descriptor memory address. */
845 uint16_t thread_id = 0;
846 retval = target_read_buffer(rtos->target,
847 thread_index + param->thread_uniqueid_offset,
848 param->uid_width,
849 (uint8_t *)&thread_id);
850 if (retval != ERROR_OK) {
851 LOG_ERROR("Could not read eCos thread id from target");
852 return retval;
854 rtos->thread_details[tasks_found].threadid = thread_id;
856 /* Read the name pointer */
857 retval = target_read_buffer(rtos->target,
858 thread_index + param->thread_name_offset,
859 param->pointer_width,
860 (uint8_t *)&name_ptr);
861 if (retval != ERROR_OK) {
862 LOG_ERROR("Could not read eCos thread name pointer from target");
863 return retval;
866 /* Read the thread name */
867 retval =
868 target_read_buffer(rtos->target,
869 name_ptr,
870 ECOS_THREAD_NAME_STR_SIZE,
871 (uint8_t *)&tmp_str);
872 if (retval != ERROR_OK) {
873 LOG_ERROR("Error reading thread name from eCos target");
874 return retval;
876 tmp_str[ECOS_THREAD_NAME_STR_SIZE-1] = '\x00';
878 /* Since eCos can have arbitrary C string names we can sometimes
879 * get an internal warning from GDB about "not well-formed
880 * (invalid token)" since the XML post-processing done by GDB on
881 * the OpenOCD returned response containing the thread strings
882 * is not escaped. For example the eCos kernel testsuite
883 * application tm_basic uses the thread name "<<NULL>>" which
884 * will trigger this failure unless escaped. */
885 if (tmp_str[0] == '\x00') {
886 snprintf(tmp_str, ECOS_THREAD_NAME_STR_SIZE, "NoName:[0x%08" PRIX32 "]", thread_index);
887 } else {
888 /* The following is a workaround to avoid any issues
889 * from arbitrary eCos thread names causing GDB/OpenOCD
890 * issues. We limit the escaped thread name passed to
891 * GDB to the same length as the un-escaped just to
892 * avoid overly long strings. */
893 char esc_str[ECOS_THREAD_NAME_STR_SIZE];
894 bool escaped = ecos_escape_string(tmp_str, esc_str, sizeof(esc_str));
895 if (escaped)
896 strcpy(tmp_str, esc_str);
899 rtos->thread_details[tasks_found].thread_name_str =
900 malloc(strlen(tmp_str)+1);
901 strcpy(rtos->thread_details[tasks_found].thread_name_str, tmp_str);
903 /* Read the thread status */
904 int64_t thread_status = 0;
905 retval = target_read_buffer(rtos->target,
906 thread_index + param->thread_state_offset,
907 param->state_width,
908 (uint8_t *)&thread_status);
909 if (retval != ERROR_OK) {
910 LOG_ERROR("Error reading thread state from eCos target");
911 return retval;
914 /* The thread_status is a BITMASK */
915 char state_desc[21]; /* Enough for "suspended+countsleep\0" maximum */
917 if (thread_status & SUSPENDED)
918 strcpy(state_desc, "suspended+");
919 else
920 state_desc[0] = '\0';
922 switch (thread_status & ~SUSPENDED) {
923 case RUNNING:
924 if (thread_index == current_thread_addr)
925 strcat(state_desc, "running");
926 else if (thread_status & SUSPENDED)
927 state_desc[9] = '\0'; /* Drop '+' from "suspended+" */
928 else
929 strcat(state_desc, "ready");
930 break;
931 case SLEEPING:
932 strcat(state_desc, "sleeping");
933 break;
934 case SLEEPSET:
935 case COUNTSLEEP:
936 strcat(state_desc, "counted sleep");
937 break;
938 case CREATING:
939 strcpy(state_desc, "creating");
940 break;
941 case EXITED:
942 strcpy(state_desc, "exited");
943 break;
944 default:
945 strcpy(state_desc, "unknown state");
946 break;
949 /* For the moment we do not bother decoding the wake reason for the
950 * active "running" thread, but it is useful providing the sleep reason
951 * for stacked threads. */
952 int64_t sleep_reason = 0; /* sleep reason */
954 if (thread_index != current_thread_addr &&
955 ecos_value(rtos, ECOS_VAL_COMMON_THREAD_SLEEP_SIZE)) {
956 retval = target_read_buffer(rtos->target,
957 (thread_index + ecos_value(rtos, ECOS_VAL_COMMON_THREAD_SLEEP_OFF)),
958 ecos_value(rtos, ECOS_VAL_COMMON_THREAD_SLEEP_SIZE),
959 (uint8_t *)&sleep_reason);
960 if (retval != ERROR_OK) {
961 LOG_ERROR("Error reading thread sleep reason from eCos target");
962 return retval;
964 if (sleep_reason < 0 ||
965 sleep_reason > (int64_t)ARRAY_SIZE(ecos_thread_reasons)) {
966 sleep_reason = 0;
970 /* We do not display anything for the Cyg_Thread::NONE reason */
971 size_t tr_extra = 0;
972 const char *reason_desc = NULL;
973 if (sleep_reason)
974 reason_desc = ecos_thread_reasons[sleep_reason].desc;
975 if (reason_desc)
976 tr_extra = 2 + strlen(reason_desc) + 1;
978 /* Display thread priority if available: */
979 int64_t priority = 0;
980 size_t pri_extra = 0;
981 if (ecos_value(rtos, ECOS_VAL_COMMON_THREAD_PRI_SIZE)) {
982 retval = target_read_buffer(rtos->target,
983 (thread_index + ecos_value(rtos, ECOS_VAL_COMMON_THREAD_PRI_OFF)),
984 ecos_value(rtos, ECOS_VAL_COMMON_THREAD_PRI_SIZE),
985 (uint8_t *)&priority);
986 if (retval != ERROR_OK) {
987 LOG_ERROR("Error reading thread priority from eCos target");
988 return retval;
990 pri_extra = (12 + 20); /* worst-case ", Priority: " */
993 size_t eilen = (8 + strlen(state_desc) + tr_extra + pri_extra);
994 char *eistr = malloc(eilen);
995 /* We do not need to treat a malloc failure as a fatal error here since
996 * the code below will just not report extra thread information if NULL,
997 * thus allowing all of the threads to be enumerated even with reduced
998 * information when the host is low on memory. However... */
999 if (!eistr) {
1000 LOG_ERROR("OOM allocating extra information buffer");
1001 return ERROR_FAIL;
1004 int soff = snprintf(eistr, eilen, "State: %s", state_desc);
1005 if (tr_extra && reason_desc)
1006 soff += snprintf(&eistr[soff], (eilen - soff), " (%s)", reason_desc);
1007 if (pri_extra)
1008 (void)snprintf(&eistr[soff], (eilen - soff), ", Priority: %" PRId64 "", priority);
1009 rtos->thread_details[tasks_found].extra_info_str = eistr;
1011 rtos->thread_details[tasks_found].exists = true;
1013 tasks_found++;
1014 prev_thread_ptr = thread_index;
1016 /* Get the location of the next thread structure. */
1017 thread_index = rtos->symbols[ECOS_VAL_THREAD_LIST].address;
1018 retval = target_read_buffer(rtos->target,
1019 prev_thread_ptr + param->thread_next_offset,
1020 param->pointer_width,
1021 (uint8_t *) &thread_index);
1022 if (retval != ERROR_OK) {
1023 LOG_ERROR("Error reading next thread pointer in eCos thread list");
1024 return retval;
1026 } while (thread_index != first_thread);
1028 rtos->thread_count = tasks_found;
1029 return ERROR_OK;
1032 static int ecos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
1033 struct rtos_reg **reg_list, int *num_regs)
1035 int retval;
1036 struct ecos_params *param;
1038 if (!rtos)
1039 return -1;
1041 if (thread_id == 0)
1042 return -2;
1044 if (!rtos->rtos_specific_params)
1045 return -3;
1047 param = rtos->rtos_specific_params;
1049 retval = ecos_check_app_info(rtos, param);
1050 if (retval != ERROR_OK)
1051 return retval;
1053 /* We can get memory access errors reported by this function on
1054 * re-connecting to a board with stale thread information in memory. The
1055 * initial ecos_update_threads() is called twice and may read
1056 * stale/invalid information depending on the memory state. This happens
1057 * as part of the "target remote" connection so cannot be avoided by GDB
1058 * scripting. It is not critical and allowing the application to run and
1059 * initialise its BSS etc. will allow correct thread and register
1060 * information to be obtained. This really only affects debug sessions
1061 * where "info thr" is used before the initial run-time initialisation
1062 * has occurred. */
1064 /* Find the thread with that thread id */
1065 uint16_t id = 0;
1066 uint32_t thread_list_head = rtos->symbols[ECOS_VAL_THREAD_LIST].address;
1067 uint32_t thread_index;
1068 target_read_buffer(rtos->target, thread_list_head, param->pointer_width,
1069 (uint8_t *)&thread_index);
1070 bool done = false;
1071 while (!done) {
1072 retval = target_read_buffer(rtos->target,
1073 thread_index + param->thread_uniqueid_offset,
1074 param->uid_width,
1075 (uint8_t *)&id);
1076 if (retval != ERROR_OK) {
1077 LOG_ERROR("Error reading unique id from eCos thread 0x%08" PRIX32 "", thread_index);
1078 return retval;
1081 if (id == thread_id) {
1082 done = true;
1083 break;
1085 target_read_buffer(rtos->target,
1086 thread_index + param->thread_next_offset,
1087 param->pointer_width,
1088 (uint8_t *) &thread_index);
1091 if (done) {
1092 /* Read the stack pointer */
1093 int64_t stack_ptr = 0;
1094 retval = target_read_buffer(rtos->target,
1095 thread_index + param->thread_stack_offset,
1096 param->pointer_width,
1097 (uint8_t *)&stack_ptr);
1098 if (retval != ERROR_OK) {
1099 LOG_ERROR("Error reading stack frame from eCos thread");
1100 return retval;
1103 if (!stack_ptr) {
1104 LOG_ERROR("NULL stack pointer in thread %" PRIu64, thread_id);
1105 return -5;
1108 const struct rtos_register_stacking *stacking_info = NULL;
1109 if (param->target_stack_layout) {
1110 retval = param->target_stack_layout(rtos, param, stack_ptr, &stacking_info);
1111 if (retval != ERROR_OK) {
1112 LOG_ERROR("Error reading stack layout for eCos thread");
1113 return retval;
1116 if (!stacking_info)
1117 stacking_info = &rtos_ecos_cortex_m3_stacking;
1119 return rtos_generic_stack_read(rtos->target,
1120 stacking_info,
1121 stack_ptr,
1122 reg_list,
1123 num_regs);
1126 return -1;
1129 /* NOTE: This is only called once when the first GDB connection is made to
1130 * OpenOCD and not on subsequent connections (when the application symbol table
1131 * may have changed, affecting the offsets of critical fields and the stacked
1132 * context shape). */
1133 static int ecos_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[])
1135 unsigned int i;
1136 *symbol_list = calloc(
1137 ARRAY_SIZE(ecos_symbol_list), sizeof(struct symbol_table_elem));
1139 /* If the target reference was passed into this function we could limit
1140 * the symbols we need to lookup to the target->type->name based
1141 * range. For the moment we need to provide a single vector with all of
1142 * the symbols across all of the supported architectures. */
1143 for (i = 0; i < ARRAY_SIZE(ecos_symbol_list); i++) {
1144 (*symbol_list)[i].symbol_name = ecos_symbol_list[i].name;
1145 (*symbol_list)[i].optional = ecos_symbol_list[i].optional;
1148 return 0;
1151 /* NOTE: Only called by rtos.c:rtos_qsymbol() when auto-detecting the RTOS. If
1152 * the target configuration uses the explicit "-rtos" config option then this
1153 * detection routine is NOT called. */
1154 static bool ecos_detect_rtos(struct target *target)
1156 if ((target->rtos->symbols) &&
1157 (target->rtos->symbols[ECOS_VAL_THREAD_LIST].address != 0)) {
1158 /* looks like eCos */
1159 return true;
1161 return false;
1164 /* Since we should never have 0 as a valid eCos thread ID we use $Hg0 as the
1165 * indicator of a new session as regards flushing any cached state. */
1166 static int ecos_packet_hook(struct connection *connection,
1167 const char *packet, int packet_size)
1169 int64_t current_threadid;
1171 if (packet[0] == 'H' && packet[1] == 'g') {
1172 int numscan = sscanf(packet, "Hg%16" SCNx64, &current_threadid);
1173 if (numscan == 1 && current_threadid == 0) {
1174 struct target *target = get_target_from_connection(connection);
1175 if (target && target->rtos && target->rtos->rtos_specific_params) {
1176 struct ecos_params *param;
1177 param = target->rtos->rtos_specific_params;
1178 param->flush_common = true;
1183 return rtos_thread_packet(connection, packet, packet_size);
1186 /* Called at start of day when eCos detected or specified in config file. */
1187 static int ecos_create(struct target *target)
1189 for (unsigned int i = 0; i < ARRAY_SIZE(ecos_params_list); i++) {
1190 const char * const *tnames = ecos_params_list[i].target_names;
1191 while (*tnames) {
1192 if (strcmp(*tnames, target->type->name) == 0) {
1193 /* LOG_DEBUG("eCos: matched target \"%s\"", target->type->name); */
1194 target->rtos->rtos_specific_params = (void *)&ecos_params_list[i];
1195 ecos_params_list[i].flush_common = true;
1196 ecos_params_list[i].stacking_info = NULL;
1197 target->rtos->current_thread = 0;
1198 target->rtos->thread_details = NULL;
1200 /* We use the $Hg0 packet as a new GDB connection "start-of-day" hook to
1201 * force a re-cache of information. It is possible for a single OpenOCD
1202 * session to be connected to a target with multiple GDB debug sessions
1203 * started/stopped. With eCos it is possible for those GDB sessions to
1204 * present applications with different offsets within a thread
1205 * descriptor for fields used by this module, and for the stacked
1206 * context within the connected target architecture to differ between
1207 * applications and even between threads in a single application. So we
1208 * need to ensure any information we cache is flushed on an application
1209 * change, and GDB referencing an invalid eCos thread ID (0) is a good
1210 * enough point, since we can accept the re-cache hit if that packet
1211 * appears during an established session, whilst benefiting from not
1212 * re-loading information on every update_threads or get_thread_reg_list
1213 * call. */
1214 target->rtos->gdb_thread_packet = ecos_packet_hook;
1215 /* We do not currently use the target->rtos->gdb_target_for_threadid
1216 * hook. */
1217 return 0;
1219 tnames++;
1223 LOG_ERROR("Could not find target in eCos compatibility list");
1224 return -1;