rtos: Add ChibiOS/RT support
[openocd/libswd.git] / src / rtos / ChibiOS.c
blob2c2b7953334406bd8473fc0f096af1568c1566db
1 /***************************************************************************
2 * Copyright (C) 2012 by Matthias Blaicher *
3 * Matthias Blaicher - matthias@blaicher.com *
4 * *
5 * Copyright (C) 2011 by Broadcom Corporation *
6 * Evan Hunter - ehunter@broadcom.com *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
22 ***************************************************************************/
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
28 #include <helper/time_support.h>
29 #include <jtag/jtag.h>
30 #include "target/target.h"
31 #include "target/target_type.h"
32 #include "rtos.h"
33 #include "helper/log.h"
34 #include "rtos_chibios_stackings.h"
37 /**
38 * @brief ChibiOS/RT memory signature record.
40 * @details Definition copied from os/kernel/include/chregistry.h of ChibiOS/RT.
42 struct ChibiOS_chdebug {
43 char ch_identifier[4]; /**< @brief Always set to "main". */
44 uint8_t ch_zero; /**< @brief Must be zero. */
45 uint8_t ch_size; /**< @brief Size of this structure. */
46 uint16_t ch_version; /**< @brief Encoded ChibiOS/RT version. */
47 uint8_t ch_ptrsize; /**< @brief Size of a pointer. */
48 uint8_t ch_timesize; /**< @brief Size of a @p systime_t. */
49 uint8_t ch_threadsize; /**< @brief Size of a @p Thread struct. */
50 uint8_t cf_off_prio; /**< @brief Offset of @p p_prio field. */
51 uint8_t cf_off_ctx; /**< @brief Offset of @p p_ctx field. */
52 uint8_t cf_off_newer; /**< @brief Offset of @p p_newer field. */
53 uint8_t cf_off_older; /**< @brief Offset of @p p_older field. */
54 uint8_t cf_off_name; /**< @brief Offset of @p p_name field. */
55 uint8_t cf_off_stklimit; /**< @brief Offset of @p p_stklimit
56 field. */
57 uint8_t cf_off_state; /**< @brief Offset of @p p_state field. */
58 uint8_t cf_off_flags; /**< @brief Offset of @p p_flags field. */
59 uint8_t cf_off_refs; /**< @brief Offset of @p p_refs field. */
60 uint8_t cf_off_preempt; /**< @brief Offset of @p p_preempt
61 field. */
62 uint8_t cf_off_time; /**< @brief Offset of @p p_time field. */
65 #define GET_CH_KERNEL_MAJOR(codedVersion) ((codedVersion >> 11) & 0x1f)
66 #define GET_CH_KERNEL_MINOR(codedVersion) ((codedVersion >> 6) & 0x1f)
67 #define GET_CH_KERNEL_PATCH(codedVersion) ((codedVersion >> 0) & 0x3f)
69 /**
70 * @brief ChibiOS thread states.
72 const char *ChibiOS_thread_states[] = {
73 "READY", "CURRENT", "SUSPENDED", "WTSEM", "WTMTX", "WTCOND", "SLEEPING",
74 "WTEXIT", "WTOREVT", "WTANDEVT", "SNDMSGQ", "SNDMSG", "WTMSG", "WTQUEUE",
75 "FINAL"
78 #define CHIBIOS_NUM_STATES (sizeof(ChibiOS_thread_states)/sizeof(char *))
80 /* Maximum ChibiOS thread name. There is no real limit set by ChibiOS but 64
81 * chars ought to be enough.
83 #define CHIBIOS_THREAD_NAME_STR_SIZE (64)
85 struct ChibiOS_params {
86 const char *target_name;
88 struct ChibiOS_chdebug *signature;
89 const struct rtos_register_stacking *stacking_info;
92 struct ChibiOS_params ChibiOS_params_list[] = {
94 "cortex_m3", /* target_name */
96 &rtos_chibios_arm_v7m_stacking, /* stacking_info */
99 "stm32_stlink", /* target_name */
101 &rtos_chibios_arm_v7m_stacking, /* stacking_info */
104 #define CHIBIOS_NUM_PARAMS ((int)(sizeof(ChibiOS_params_list)/sizeof(struct ChibiOS_params)))
106 static int ChibiOS_detect_rtos(struct target *target);
107 static int ChibiOS_create(struct target *target);
108 static int ChibiOS_update_threads(struct rtos *rtos);
109 static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list);
110 static int ChibiOS_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]);
112 struct rtos_type ChibiOS_rtos = {
113 .name = "ChibiOS",
115 .detect_rtos = ChibiOS_detect_rtos,
116 .create = ChibiOS_create,
117 .update_threads = ChibiOS_update_threads,
118 .get_thread_reg_list = ChibiOS_get_thread_reg_list,
119 .get_symbol_list_to_lookup = ChibiOS_get_symbol_list_to_lookup,
122 enum ChibiOS_symbol_values {
123 ChibiOS_VAL_rlist = 0,
124 ChibiOS_VAL_ch_debug = 1,
125 ChibiOS_VAL_chSysInit = 2
128 static char *ChibiOS_symbol_list[] = {
129 "rlist", /* Thread ready list*/
130 "ch_debug", /* Memory Signatur containing offsets of fields in rlist*/
131 "chSysInit", /* Necessary part of API, used for ChibiOS detection*/
132 NULL
135 #define CHIBIOS_NUM_SYMBOLS (sizeof(ChibiOS_symbol_list)/sizeof(char *))
137 static int ChibiOS_update_memory_signature(struct rtos *rtos)
139 int retval;
140 struct ChibiOS_params *param;
141 struct ChibiOS_chdebug *signature;
143 param = (struct ChibiOS_params *) rtos->rtos_specific_params;
145 /* Free existing memory description.*/
146 if (param->signature) {
147 free(param->signature);
148 param->signature = 0;
151 signature = malloc(sizeof(*signature));
152 if (!signature) {
153 LOG_ERROR("Could not allocate space for ChibiOS/RT memory signature");
154 return -1;
157 retval = target_read_buffer(rtos->target,
158 rtos->symbols[ChibiOS_VAL_ch_debug].address,
159 sizeof(*signature),
160 (uint8_t *) signature);
161 if (retval != ERROR_OK) {
162 LOG_ERROR("Could not read ChibiOS/RT memory signature from target");
163 goto errfree;
166 if (strncmp(signature->ch_identifier, "main", 4) != 0) {
167 LOG_ERROR("Memory signature identifier does not contain magic bytes.");
168 goto errfree;
171 if (signature->ch_size < sizeof(*signature)) {
172 LOG_ERROR("ChibiOS/RT memory signature claims to be smaller "
173 "than expected");
174 goto errfree;
177 if (signature->ch_size > sizeof(*signature)) {
178 LOG_WARNING("ChibiOS/RT memory signature claims to be bigger than"
179 " expected. Assuming compatibility...");
182 /* Convert endianness of version field */
183 const uint8_t *versionTarget = (const uint8_t *)
184 &signature->ch_version;
185 signature->ch_version = rtos->target->endianness == TARGET_LITTLE_ENDIAN ?
186 le_to_h_u32(versionTarget) : be_to_h_u32(versionTarget);
188 const uint16_t ch_version = signature->ch_version;
189 LOG_INFO("Successfully loaded memory map of ChibiOS/RT target "
190 "running version %i.%i.%i", GET_CH_KERNEL_MAJOR(ch_version),
191 GET_CH_KERNEL_MINOR(ch_version), GET_CH_KERNEL_PATCH(ch_version));
193 param->signature = signature;
194 return 0;
196 errfree:
197 /* Error reading the ChibiOS memory structure */
198 free(signature);
199 param->signature = 0;
200 return -1;
204 static int ChibiOS_update_stacking(struct rtos *rtos)
206 /* Sometimes the stacking can not be determined only by looking at the
207 * target name but only a runtime.
209 * For example, this is the case for cortex-m4 targets and ChibiOS which
210 * only stack the FPU registers if it is enabled during ChibiOS build.
212 * Terminating which stacking is used is target depending.
214 * Assumptions:
215 * - Once ChibiOS is actually initialized, the stacking is fixed.
216 * - During startup code, the FPU might not be initialized and the
217 * detection might fail.
218 * - Since no threads are running during startup, the problem is solved
219 * by delaying stacking detection until there are more threads
220 * available than the current execution. In which case
221 * ChibiOS_get_thread_reg_list is called.
224 /* TODO: Add actual detection, currently it will not work with FPU enabled.*/
225 return -1;
228 static int ChibiOS_update_threads(struct rtos *rtos)
230 int retval;
231 const struct ChibiOS_params *param;
232 int tasks_found = 0;
233 int rtos_valid = -1;
235 if (!rtos->rtos_specific_params)
236 return -1;
238 if (!rtos->symbols) {
239 LOG_ERROR("No symbols for ChibiOS");
240 return -3;
243 param = (const struct ChibiOS_params *) rtos->rtos_specific_params;
244 /* Update the memory signature saved in the target memory */
245 if (!param->signature) {
246 retval = ChibiOS_update_memory_signature(rtos);
247 if (retval != ERROR_OK) {
248 LOG_ERROR("Reading the memory signature of ChibiOS/RT failed");
249 return retval;
253 /* wipe out previous thread details if any */
254 int j;
255 if (rtos->thread_details) {
256 for (j = 0; j < rtos->thread_count; j++) {
257 struct thread_detail *current_thread = &rtos->thread_details[j];
258 if (current_thread->display_str != NULL)
259 free(current_thread->display_str);
260 if (current_thread->thread_name_str != NULL)
261 free(current_thread->thread_name_str);
262 if (current_thread->extra_info_str != NULL)
263 free(current_thread->extra_info_str);
265 free(rtos->thread_details);
266 rtos->thread_details = NULL;
267 rtos->thread_count = 0;
269 /* ChibiOS does not save the current thread count. We have to first
270 * parse the double linked thread list to check for errors and the number of
271 * threads. */
272 uint32_t rlist;
273 uint32_t current;
274 uint32_t previous;
275 uint32_t older;
277 retval = target_read_buffer(rtos->target,
278 rtos->symbols[ChibiOS_VAL_rlist].address,
279 param->signature->ch_ptrsize,
280 (uint8_t *)&rlist);
281 if (retval != ERROR_OK) {
282 LOG_ERROR("Could not read ChibiOS ReadyList from target");
283 return retval;
285 current = rlist;
286 previous = rlist;
287 while (1) {
288 retval = target_read_buffer(rtos->target,
289 current + param->signature->cf_off_newer,
290 param->signature->ch_ptrsize,
291 (uint8_t *)&current);
292 if (retval != ERROR_OK) {
293 LOG_ERROR("Could not read next ChibiOS thread");
294 return retval;
296 /* Could be NULL if the kernel is not initialized yet or if the
297 * registry is corrupted. */
298 if (current == 0) {
299 LOG_ERROR("ChibiOS registry integrity check failed, NULL pointer");
301 rtos_valid = 0;
302 break;
304 /* Fetch previous thread in the list as a integrity check. */
305 retval = target_read_buffer(rtos->target,
306 current + param->signature->cf_off_older,
307 param->signature->ch_ptrsize,
308 (uint8_t *)&older);
309 if ((retval != ERROR_OK) || (older == 0) || (older != previous)) {
310 LOG_ERROR("ChibiOS registry integrity check failed, "
311 "double linked list violation");
312 rtos_valid = 0;
313 break;
315 /* Check for full iteration of the linked list. */
316 if (current == rlist)
317 break;
318 tasks_found++;
319 previous = current;
321 if (!rtos_valid) {
322 /* No RTOS, there is always at least the current execution, though */
323 LOG_INFO("Only showing current execution because of a broken "
324 "ChibiOS thread registry.");
326 const char tmp_thread_name[] = "Current Execution";
327 const char tmp_thread_extra_info[] = "No RTOS thread";
329 rtos->thread_details = (struct thread_detail *) malloc(
330 sizeof(struct thread_detail));
331 rtos->thread_details->threadid = 1;
332 rtos->thread_details->exists = true;
333 rtos->thread_details->display_str = NULL;
335 rtos->thread_details->extra_info_str = (char *) malloc(
336 sizeof(tmp_thread_extra_info));
337 strcpy(rtos->thread_details->extra_info_str, tmp_thread_extra_info);
339 rtos->thread_details->thread_name_str = (char *) malloc(
340 sizeof(tmp_thread_name));
341 strcpy(rtos->thread_details->thread_name_str, tmp_thread_name);
343 rtos->current_thread = 1;
344 rtos->thread_count = 1;
345 return ERROR_OK;
348 /* create space for new thread details */
349 rtos->thread_details = (struct thread_detail *) malloc(
350 sizeof(struct thread_detail) * tasks_found);
351 if (!rtos->thread_details) {
352 LOG_ERROR("Could not allocate space for thread details");
353 return -1;
356 rtos->thread_count = tasks_found;
357 /* Loop through linked list. */
358 struct thread_detail *curr_thrd_details = rtos->thread_details;
359 while (curr_thrd_details < rtos->thread_details + tasks_found) {
360 uint32_t name_ptr = 0;
361 char tmp_str[CHIBIOS_THREAD_NAME_STR_SIZE];
363 retval = target_read_buffer(rtos->target,
364 current + param->signature->cf_off_newer,
365 param->signature->ch_ptrsize,
366 (uint8_t *)&current);
367 if (retval != ERROR_OK) {
368 LOG_ERROR("Could not read next ChibiOS thread");
369 return -6;
372 /* Check for full iteration of the linked list. */
373 if (current == rlist)
374 break;
376 /* Save the thread pointer */
377 curr_thrd_details->threadid = current;
379 /* read the name pointer */
380 retval = target_read_buffer(rtos->target,
381 current + param->signature->cf_off_name,
382 param->signature->ch_ptrsize,
383 (uint8_t *)&name_ptr);
384 if (retval != ERROR_OK) {
385 LOG_ERROR("Could not read ChibiOS thread name pointer from target");
386 return retval;
389 /* Read the thread name */
390 retval = target_read_buffer(rtos->target, name_ptr,
391 CHIBIOS_THREAD_NAME_STR_SIZE,
392 (uint8_t *)&tmp_str);
393 if (retval != ERROR_OK) {
394 LOG_ERROR("Error reading thread name from ChibiOS target");
395 return retval;
397 tmp_str[CHIBIOS_THREAD_NAME_STR_SIZE - 1] = '\x00';
399 if (tmp_str[0] == '\x00')
400 strcpy(tmp_str, "No Name");
402 curr_thrd_details->thread_name_str = (char *)malloc(
403 strlen(tmp_str) + 1);
404 strcpy(curr_thrd_details->thread_name_str, tmp_str);
406 /* State info */
407 uint8_t threadState;
408 const char *state_desc;
410 retval = target_read_buffer(rtos->target,
411 current + param->signature->cf_off_state,
412 1, &threadState);
413 if (retval != ERROR_OK) {
414 LOG_ERROR("Error reading thread state from ChibiOS target");
415 return retval;
419 if (threadState < CHIBIOS_NUM_STATES)
420 state_desc = ChibiOS_thread_states[threadState];
421 else
422 state_desc = "Unknown state";
424 curr_thrd_details->extra_info_str = (char *)malloc(strlen(
425 state_desc)+1);
426 strcpy(curr_thrd_details->extra_info_str, state_desc);
428 curr_thrd_details->exists = true;
429 curr_thrd_details->display_str = NULL;
431 curr_thrd_details++;
433 /* NOTE: By design, cf_off_name equals readylist_current_offset */
434 retval = target_read_buffer(rtos->target,
435 rlist + param->signature->cf_off_name,
436 param->signature->ch_ptrsize,
437 (uint8_t *)&rtos->current_thread);
438 if (retval != ERROR_OK) {
439 LOG_ERROR("Could not read current Thread from ChibiOS target");
440 return retval;
443 return 0;
446 static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list)
448 int retval;
449 const struct ChibiOS_params *param;
450 int64_t stack_ptr = 0;
452 *hex_reg_list = NULL;
453 if ((rtos == NULL) || (thread_id == 0) ||
454 (rtos->rtos_specific_params == NULL))
455 return -1;
457 param = (const struct ChibiOS_params *) rtos->rtos_specific_params;
459 if (!param->signature)
460 return -1;
462 /* Update stacking if it can only be determined from runtime information */
463 if ((param->stacking_info == 0) &&
464 (ChibiOS_update_stacking(rtos) != ERROR_OK)) {
465 LOG_ERROR("Failed to determine exact stacking for the target type %s", rtos->target->type->name);
466 return -1;
469 /* Read the stack pointer */
470 retval = target_read_buffer(rtos->target,
471 thread_id + param->signature->cf_off_ctx,
472 param->signature->ch_ptrsize,
473 (uint8_t *)&stack_ptr);
474 if (retval != ERROR_OK) {
475 LOG_ERROR("Error reading stack frame from ChibiOS thread");
476 return retval;
479 return rtos_generic_stack_read(rtos->target, param->stacking_info, stack_ptr, hex_reg_list);
482 static int ChibiOS_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[])
484 unsigned int i;
485 *symbol_list = (symbol_table_elem_t *) malloc(
486 sizeof(symbol_table_elem_t) * CHIBIOS_NUM_SYMBOLS);
488 for (i = 0; i < CHIBIOS_NUM_SYMBOLS; i++)
489 (*symbol_list)[i].symbol_name = ChibiOS_symbol_list[i];
491 return 0;
494 static int ChibiOS_detect_rtos(struct target *target)
496 if ((target->rtos->symbols != NULL) &&
497 (target->rtos->symbols[ChibiOS_VAL_rlist].address != 0) &&
498 (target->rtos->symbols[ChibiOS_VAL_chSysInit].address != 0)) {
500 if (target->rtos->symbols[ChibiOS_VAL_ch_debug].address == 0) {
501 LOG_INFO("It looks like the target is running ChibiOS without "
502 "ch_debug.");
503 return 0;
506 /* looks like ChibiOS with memory map enabled.*/
507 return 1;
510 return 0;
513 static int ChibiOS_create(struct target *target)
515 int i = 0;
516 while ((i < CHIBIOS_NUM_PARAMS) &&
517 (0 != strcmp(ChibiOS_params_list[i].target_name, target->type->name))) {
518 i++;
520 if (i >= CHIBIOS_NUM_PARAMS) {
521 LOG_WARNING("Could not find target \"%s\" in ChibiOS compatibility "
522 "list", target->type->name);
523 return -1;
526 target->rtos->rtos_specific_params = (void *) &ChibiOS_params_list[i];
527 return 0;