target/xtensa: avoid IHI for writes to non-executable memory
[openocd.git] / src / rtos / chibios.c
blob20378274ec0c03bd7a4665589cea3008be05ba44
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 /***************************************************************************
4 * Copyright (C) 2012 by Matthias Blaicher *
5 * Matthias Blaicher - matthias@blaicher.com *
6 * *
7 * Copyright (C) 2011 by Broadcom Corporation *
8 * Evan Hunter - ehunter@broadcom.com *
9 ***************************************************************************/
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif
15 #include <helper/time_support.h>
16 #include <jtag/jtag.h>
17 #include "target/target.h"
18 #include "target/target_type.h"
19 #include "target/armv7m.h"
20 #include "target/cortex_m.h"
21 #include "rtos.h"
22 #include "helper/log.h"
23 #include "helper/types.h"
24 #include "rtos_chibios_stackings.h"
26 /**
27 * @brief ChibiOS/RT memory signature record.
29 * @details Definition copied from os/kernel/include/chregistry.h of ChibiOS/RT.
31 struct chibios_chdebug {
32 char ch_identifier[4]; /**< @brief Always set to "main". */
33 uint8_t ch_zero; /**< @brief Must be zero. */
34 uint8_t ch_size; /**< @brief Size of this structure. */
35 uint16_t ch_version; /**< @brief Encoded ChibiOS/RT version. */
36 uint8_t ch_ptrsize; /**< @brief Size of a pointer. */
37 uint8_t ch_timesize; /**< @brief Size of a @p systime_t. */
38 uint8_t ch_threadsize; /**< @brief Size of a @p Thread struct. */
39 uint8_t cf_off_prio; /**< @brief Offset of @p p_prio field. */
40 uint8_t cf_off_ctx; /**< @brief Offset of @p p_ctx field. */
41 uint8_t cf_off_newer; /**< @brief Offset of @p p_newer field. */
42 uint8_t cf_off_older; /**< @brief Offset of @p p_older field. */
43 uint8_t cf_off_name; /**< @brief Offset of @p p_name field. */
44 uint8_t cf_off_stklimit; /**< @brief Offset of @p p_stklimit
45 field. */
46 uint8_t cf_off_state; /**< @brief Offset of @p p_state field. */
47 uint8_t cf_off_flags; /**< @brief Offset of @p p_flags field. */
48 uint8_t cf_off_refs; /**< @brief Offset of @p p_refs field. */
49 uint8_t cf_off_preempt; /**< @brief Offset of @p p_preempt
50 field. */
51 uint8_t cf_off_time; /**< @brief Offset of @p p_time field. */
54 #define GET_CH_KERNEL_MAJOR(coded_version) ((coded_version >> 11) & 0x1f)
55 #define GET_CH_KERNEL_MINOR(coded_version) ((coded_version >> 6) & 0x1f)
56 #define GET_CH_KERNEL_PATCH(coded_version) ((coded_version >> 0) & 0x3f)
58 /**
59 * @brief ChibiOS thread states.
61 static const char * const chibios_thread_states[] = { "READY", "CURRENT",
62 "WTSTART", "SUSPENDED", "QUEUED", "WTSEM", "WTMTX", "WTCOND", "SLEEPING",
63 "WTEXIT", "WTOREVT", "WTANDEVT", "SNDMSGQ", "SNDMSG", "WTMSG", "FINAL"
66 #define CHIBIOS_NUM_STATES ARRAY_SIZE(chibios_thread_states)
68 /* Maximum ChibiOS thread name. There is no real limit set by ChibiOS but 64
69 * chars ought to be enough.
71 #define CHIBIOS_THREAD_NAME_STR_SIZE (64)
73 struct chibios_params {
74 const char *target_name;
76 struct chibios_chdebug *signature;
77 const struct rtos_register_stacking *stacking_info;
80 static struct chibios_params chibios_params_list[] = {
82 "cortex_m", /* target_name */
83 NULL,
84 NULL, /* stacking_info */
87 "hla_target", /* target_name */
88 NULL,
89 NULL, /* stacking_info */
93 static bool chibios_detect_rtos(struct target *target);
94 static int chibios_create(struct target *target);
95 static int chibios_update_threads(struct rtos *rtos);
96 static int chibios_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
97 struct rtos_reg **reg_list, int *num_regs);
98 static int chibios_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[]);
100 const struct rtos_type chibios_rtos = {
101 .name = "chibios",
103 .detect_rtos = chibios_detect_rtos,
104 .create = chibios_create,
105 .update_threads = chibios_update_threads,
106 .get_thread_reg_list = chibios_get_thread_reg_list,
107 .get_symbol_list_to_lookup = chibios_get_symbol_list_to_lookup,
111 /* In ChibiOS/RT 3.0 the rlist structure has become part of a system
112 * data structure ch. We declare both symbols as optional and later
113 * use whatever is available.
116 enum chibios_symbol_values {
117 CHIBIOS_VAL_RLIST = 0,
118 CHIBIOS_VAL_CH = 1,
119 CHIBIOS_VAL_CH_DEBUG = 2
122 static struct symbol_table_elem chibios_symbol_list[] = {
123 { "rlist", 0, true}, /* Thread ready list */
124 { "ch", 0, true}, /* System data structure */
125 { "ch_debug", 0, false}, /* Memory Signature containing offsets of fields in rlist */
126 { NULL, 0, false}
129 /* Offset of the rlist structure within the system data structure (ch) */
130 #define CH_RLIST_OFFSET 0x00
132 static int chibios_update_memory_signature(struct rtos *rtos)
134 int retval;
135 struct chibios_params *param;
136 struct chibios_chdebug *signature;
138 param = (struct chibios_params *) rtos->rtos_specific_params;
140 /* Free existing memory description.*/
141 free(param->signature);
142 param->signature = NULL;
144 signature = malloc(sizeof(*signature));
145 if (!signature) {
146 LOG_ERROR("Could not allocate space for ChibiOS/RT memory signature");
147 return -1;
150 retval = target_read_buffer(rtos->target,
151 rtos->symbols[CHIBIOS_VAL_CH_DEBUG].address,
152 sizeof(*signature),
153 (uint8_t *) signature);
154 if (retval != ERROR_OK) {
155 LOG_ERROR("Could not read ChibiOS/RT memory signature from target");
156 goto errfree;
159 if (strncmp(signature->ch_identifier, "main", 4) != 0) {
160 LOG_ERROR("Memory signature identifier does not contain magic bytes.");
161 goto errfree;
164 if (signature->ch_size < sizeof(*signature)) {
165 LOG_ERROR("ChibiOS/RT memory signature claims to be smaller "
166 "than expected");
167 goto errfree;
170 if (signature->ch_size > sizeof(*signature)) {
171 LOG_WARNING("ChibiOS/RT memory signature claims to be bigger than"
172 " expected. Assuming compatibility...");
175 /* Convert endianness of version field */
176 const uint8_t *versiontarget = (const uint8_t *)
177 &signature->ch_version;
178 signature->ch_version = rtos->target->endianness == TARGET_LITTLE_ENDIAN ?
179 le_to_h_u32(versiontarget) : be_to_h_u32(versiontarget);
181 const uint16_t ch_version = signature->ch_version;
182 LOG_INFO("Successfully loaded memory map of ChibiOS/RT target "
183 "running version %i.%i.%i", GET_CH_KERNEL_MAJOR(ch_version),
184 GET_CH_KERNEL_MINOR(ch_version), GET_CH_KERNEL_PATCH(ch_version));
186 /* Currently, we have the inherent assumption that all address pointers
187 * are 32 bit wide. */
188 if (signature->ch_ptrsize != sizeof(uint32_t)) {
189 LOG_ERROR("ChibiOS/RT target memory signature claims an address "
190 "width unequal to 32 bits!");
191 free(signature);
192 return -1;
195 param->signature = signature;
196 return 0;
198 errfree:
199 /* Error reading the ChibiOS memory structure */
200 free(signature);
201 param->signature = NULL;
202 return -1;
206 static int chibios_update_stacking(struct rtos *rtos)
208 /* Sometimes the stacking can not be determined only by looking at the
209 * target name but only a runtime.
211 * For example, this is the case for Cortex-M4 targets and ChibiOS which
212 * only stack the FPU registers if it is enabled during ChibiOS build.
214 * Terminating which stacking is used is target depending.
216 * Assumptions:
217 * - Once ChibiOS is actually initialized, the stacking is fixed.
218 * - During startup code, the FPU might not be initialized and the
219 * detection might fail.
220 * - Since no threads are running during startup, the problem is solved
221 * by delaying stacking detection until there are more threads
222 * available than the current execution. In which case
223 * chibios_get_thread_reg_list is called.
225 int retval;
227 if (!rtos->rtos_specific_params)
228 return -1;
230 struct chibios_params *param;
231 param = (struct chibios_params *) rtos->rtos_specific_params;
233 /* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4 */
234 struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target);
235 if (is_armv7m(armv7m_target)) {
236 if (armv7m_target->fp_feature != FP_NONE) {
237 /* Found ARM v7m target which includes a FPU */
238 uint32_t cpacr;
240 retval = target_read_u32(rtos->target, FPU_CPACR, &cpacr);
241 if (retval != ERROR_OK) {
242 LOG_ERROR("Could not read CPACR register to check FPU state");
243 return -1;
246 /* Check if CP10 and CP11 are set to full access.
247 * In ChibiOS this is done in ResetHandler() in crt0.c */
248 if (cpacr & 0x00F00000) {
249 LOG_DEBUG("Enabled FPU detected.");
250 param->stacking_info = &rtos_chibios_arm_v7m_stacking_w_fpu;
251 return 0;
255 /* Found ARM v7m target with no or disabled FPU */
256 param->stacking_info = &rtos_chibios_arm_v7m_stacking;
257 return 0;
260 return -1;
263 static int chibios_update_threads(struct rtos *rtos)
265 int retval;
266 const struct chibios_params *param;
267 int tasks_found = 0;
268 int rtos_valid = -1;
270 if (!rtos->rtos_specific_params)
271 return -1;
273 if (!rtos->symbols) {
274 LOG_ERROR("No symbols for ChibiOS");
275 return -3;
278 param = (const struct chibios_params *) rtos->rtos_specific_params;
279 /* Update the memory signature saved in the target memory */
280 if (!param->signature) {
281 retval = chibios_update_memory_signature(rtos);
282 if (retval != ERROR_OK) {
283 LOG_ERROR("Reading the memory signature of ChibiOS/RT failed");
284 return retval;
288 /* wipe out previous thread details if any */
289 rtos_free_threadlist(rtos);
291 /* ChibiOS does not save the current thread count. We have to first
292 * parse the double linked thread list to check for errors and the number of
293 * threads. */
294 const uint32_t rlist = rtos->symbols[CHIBIOS_VAL_RLIST].address ?
295 rtos->symbols[CHIBIOS_VAL_RLIST].address :
296 rtos->symbols[CHIBIOS_VAL_CH].address + CH_RLIST_OFFSET /* ChibiOS3 */;
297 const struct chibios_chdebug *signature = param->signature;
298 uint32_t current;
299 uint32_t previous;
300 uint32_t older;
302 current = rlist;
303 previous = rlist;
304 while (1) {
305 retval = target_read_u32(rtos->target,
306 current + signature->cf_off_newer, &current);
307 if (retval != ERROR_OK) {
308 LOG_ERROR("Could not read next ChibiOS thread");
309 return retval;
311 /* Could be NULL if the kernel is not initialized yet or if the
312 * registry is corrupted. */
313 if (current == 0) {
314 LOG_ERROR("ChibiOS registry integrity check failed, NULL pointer");
316 rtos_valid = 0;
317 break;
319 /* Fetch previous thread in the list as a integrity check. */
320 retval = target_read_u32(rtos->target,
321 current + signature->cf_off_older, &older);
322 if ((retval != ERROR_OK) || (older == 0) || (older != previous)) {
323 LOG_ERROR("ChibiOS registry integrity check failed, "
324 "double linked list violation");
325 rtos_valid = 0;
326 break;
328 /* Check for full iteration of the linked list. */
329 if (current == rlist)
330 break;
331 tasks_found++;
332 previous = current;
334 if (!rtos_valid) {
335 /* No RTOS, there is always at least the current execution, though */
336 LOG_INFO("Only showing current execution because of a broken "
337 "ChibiOS thread registry.");
339 const char tmp_thread_name[] = "Current Execution";
340 const char tmp_thread_extra_info[] = "No RTOS thread";
342 rtos->thread_details = malloc(
343 sizeof(struct thread_detail));
344 rtos->thread_details->threadid = 1;
345 rtos->thread_details->exists = true;
347 rtos->thread_details->extra_info_str = malloc(
348 sizeof(tmp_thread_extra_info));
349 strcpy(rtos->thread_details->extra_info_str, tmp_thread_extra_info);
351 rtos->thread_details->thread_name_str = malloc(
352 sizeof(tmp_thread_name));
353 strcpy(rtos->thread_details->thread_name_str, tmp_thread_name);
355 rtos->current_thread = 1;
356 rtos->thread_count = 1;
357 return ERROR_OK;
360 /* create space for new thread details */
361 rtos->thread_details = malloc(
362 sizeof(struct thread_detail) * tasks_found);
363 if (!rtos->thread_details) {
364 LOG_ERROR("Could not allocate space for thread details");
365 return -1;
368 rtos->thread_count = tasks_found;
369 /* Loop through linked list. */
370 struct thread_detail *curr_thrd_details = rtos->thread_details;
371 while (curr_thrd_details < rtos->thread_details + tasks_found) {
372 uint32_t name_ptr = 0;
373 char tmp_str[CHIBIOS_THREAD_NAME_STR_SIZE];
375 retval = target_read_u32(rtos->target,
376 current + signature->cf_off_newer, &current);
377 if (retval != ERROR_OK) {
378 LOG_ERROR("Could not read next ChibiOS thread");
379 return -6;
382 /* Check for full iteration of the linked list. */
383 if (current == rlist)
384 break;
386 /* Save the thread pointer */
387 curr_thrd_details->threadid = current;
389 /* read the name pointer */
390 retval = target_read_u32(rtos->target,
391 current + signature->cf_off_name, &name_ptr);
392 if (retval != ERROR_OK) {
393 LOG_ERROR("Could not read ChibiOS thread name pointer from target");
394 return retval;
397 /* Read the thread name */
398 retval = target_read_buffer(rtos->target, name_ptr,
399 CHIBIOS_THREAD_NAME_STR_SIZE,
400 (uint8_t *)&tmp_str);
401 if (retval != ERROR_OK) {
402 LOG_ERROR("Error reading thread name from ChibiOS target");
403 return retval;
405 tmp_str[CHIBIOS_THREAD_NAME_STR_SIZE - 1] = '\x00';
407 if (tmp_str[0] == '\x00')
408 strcpy(tmp_str, "No Name");
410 curr_thrd_details->thread_name_str = malloc(
411 strlen(tmp_str) + 1);
412 strcpy(curr_thrd_details->thread_name_str, tmp_str);
414 /* State info */
415 uint8_t thread_state;
416 const char *state_desc;
418 retval = target_read_u8(rtos->target,
419 current + signature->cf_off_state, &thread_state);
420 if (retval != ERROR_OK) {
421 LOG_ERROR("Error reading thread state from ChibiOS target");
422 return retval;
426 if (thread_state < CHIBIOS_NUM_STATES)
427 state_desc = chibios_thread_states[thread_state];
428 else
429 state_desc = "Unknown";
431 curr_thrd_details->extra_info_str = malloc(strlen(
432 state_desc)+8);
433 sprintf(curr_thrd_details->extra_info_str, "State: %s", state_desc);
435 curr_thrd_details->exists = true;
437 curr_thrd_details++;
440 uint32_t current_thrd;
441 /* NOTE: By design, cf_off_name equals readylist_current_offset */
442 retval = target_read_u32(rtos->target,
443 rlist + signature->cf_off_name,
444 &current_thrd);
445 if (retval != ERROR_OK) {
446 LOG_ERROR("Could not read current Thread from ChibiOS target");
447 return retval;
449 rtos->current_thread = current_thrd;
451 return 0;
454 static int chibios_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
455 struct rtos_reg **reg_list, int *num_regs)
457 int retval;
458 const struct chibios_params *param;
459 uint32_t stack_ptr = 0;
461 if ((!rtos) || (thread_id == 0) ||
462 (!rtos->rtos_specific_params))
463 return -1;
465 param = (const struct chibios_params *) rtos->rtos_specific_params;
467 if (!param->signature)
468 return -1;
470 /* Update stacking if it can only be determined from runtime information */
471 if (!param->stacking_info &&
472 (chibios_update_stacking(rtos) != ERROR_OK)) {
473 LOG_ERROR("Failed to determine exact stacking for the target type %s", rtos->target->type->name);
474 return -1;
477 /* Read the stack pointer */
478 retval = target_read_u32(rtos->target,
479 thread_id + param->signature->cf_off_ctx, &stack_ptr);
480 if (retval != ERROR_OK) {
481 LOG_ERROR("Error reading stack frame from ChibiOS thread");
482 return retval;
485 return rtos_generic_stack_read(rtos->target, param->stacking_info, stack_ptr, reg_list, num_regs);
488 static int chibios_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[])
490 *symbol_list = malloc(sizeof(chibios_symbol_list));
492 if (!*symbol_list)
493 return ERROR_FAIL;
495 memcpy(*symbol_list, chibios_symbol_list, sizeof(chibios_symbol_list));
496 return 0;
499 static bool chibios_detect_rtos(struct target *target)
501 if ((target->rtos->symbols) &&
502 ((target->rtos->symbols[CHIBIOS_VAL_RLIST].address != 0) ||
503 (target->rtos->symbols[CHIBIOS_VAL_CH].address != 0))) {
505 if (target->rtos->symbols[CHIBIOS_VAL_CH_DEBUG].address == 0) {
506 LOG_INFO("It looks like the target may be running ChibiOS "
507 "without ch_debug.");
508 return false;
511 /* looks like ChibiOS with memory map enabled.*/
512 return true;
515 return false;
518 static int chibios_create(struct target *target)
520 for (unsigned int i = 0; i < ARRAY_SIZE(chibios_params_list); i++)
521 if (strcmp(chibios_params_list[i].target_name, target->type->name) == 0) {
522 target->rtos->rtos_specific_params = (void *)&chibios_params_list[i];
523 return 0;
526 LOG_WARNING("Could not find target \"%s\" in ChibiOS compatibility "
527 "list", target->type->name);
528 return -1;