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, see <http://www.gnu.org/licenses/>. *
20  ***************************************************************************/
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <helper/time_support.h>
27 #include <jtag/jtag.h>
28 #include "target/target.h"
29 #include "target/target_type.h"
30 #include "target/armv7m.h"
31 #include "target/cortex_m.h"
32 #include "rtos.h"
33 #include "helper/log.h"
34 #include "helper/types.h"
35 #include "rtos_chibios_stackings.h"
36 
37 /**
38  * @brief   ChibiOS/RT memory signature record.
39  *
40  * @details Definition copied from os/kernel/include/chregistry.h of ChibiOS/RT.
41  */
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.  */
63 };
64 
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)
68 
69 /**
70  * @brief ChibiOS thread states.
71  */
72 static const char * const chibios_thread_states[] = { "READY", "CURRENT",
73 "WTSTART", "SUSPENDED", "QUEUED", "WTSEM", "WTMTX", "WTCOND", "SLEEPING",
74 "WTEXIT", "WTOREVT", "WTANDEVT", "SNDMSGQ", "SNDMSG", "WTMSG", "FINAL"
75 };
76 
77 #define CHIBIOS_NUM_STATES (sizeof(chibios_thread_states)/sizeof(char *))
78 
79 /* Maximum ChibiOS thread name. There is no real limit set by ChibiOS but 64
80  * chars ought to be enough.
81  */
82 #define CHIBIOS_THREAD_NAME_STR_SIZE (64)
83 
84 struct chibios_params {
85 	const char *target_name;
86 
87 	struct chibios_chdebug *signature;
88 	const struct rtos_register_stacking *stacking_info;
89 };
90 
91 static struct chibios_params chibios_params_list[] = {
92 	{
93 	"cortex_m",							/* target_name */
94 	0,
95 	NULL,									/* stacking_info */
96 	},
97 	{
98 	"hla_target",							/* target_name */
99 	0,
100 	NULL,									/* stacking_info */
101 	}
102 };
103 #define CHIBIOS_NUM_PARAMS ((int)(sizeof(chibios_params_list)/sizeof(struct chibios_params)))
104 
105 static bool chibios_detect_rtos(struct target *target);
106 static int chibios_create(struct target *target);
107 static int chibios_update_threads(struct rtos *rtos);
108 static int chibios_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
109 		struct rtos_reg **reg_list, int *num_regs);
110 static int chibios_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]);
111 
112 struct rtos_type chibios_rtos = {
113 	.name = "chibios",
114 
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,
120 };
121 
122 
123 /* In ChibiOS/RT 3.0 the rlist structure has become part of a system
124  * data structure ch. We declare both symbols as optional and later
125  * use whatever is available.
126  */
127 
128 enum chibios_symbol_values {
129 	CHIBIOS_VAL_RLIST = 0,
130 	CHIBIOS_VAL_CH = 1,
131 	CHIBIOS_VAL_CH_DEBUG = 2
132 };
133 
134 static symbol_table_elem_t chibios_symbol_list[] = {
135 	{ "rlist", 0, true},		/* Thread ready list */
136 	{ "ch", 0, true},			/* System data structure */
137 	{ "ch_debug", 0, false},	/* Memory Signature containing offsets of fields in rlist */
138 	{ NULL, 0, false}
139 };
140 
141 /* Offset of the rlist structure within the system data structure (ch) */
142 #define CH_RLIST_OFFSET 0x00
143 
chibios_update_memory_signature(struct rtos * rtos)144 static int chibios_update_memory_signature(struct rtos *rtos)
145 {
146 	int retval;
147 	struct chibios_params *param;
148 	struct chibios_chdebug *signature;
149 
150 	param = (struct chibios_params *) rtos->rtos_specific_params;
151 
152 	/* Free existing memory description.*/
153 	free(param->signature);
154 	param->signature = NULL;
155 
156 	signature = malloc(sizeof(*signature));
157 	if (!signature) {
158 		LOG_ERROR("Could not allocate space for ChibiOS/RT memory signature");
159 		return -1;
160 	}
161 
162 	retval = target_read_buffer(rtos->target,
163 								rtos->symbols[CHIBIOS_VAL_CH_DEBUG].address,
164 								sizeof(*signature),
165 								(uint8_t *) signature);
166 	if (retval != ERROR_OK) {
167 		LOG_ERROR("Could not read ChibiOS/RT memory signature from target");
168 		goto errfree;
169 	}
170 
171 	if (strncmp(signature->ch_identifier, "main", 4) != 0) {
172 		LOG_ERROR("Memory signature identifier does not contain magic bytes.");
173 		goto errfree;
174 	}
175 
176 	if (signature->ch_size < sizeof(*signature)) {
177 		LOG_ERROR("ChibiOS/RT memory signature claims to be smaller "
178 				"than expected");
179 		goto errfree;
180 	}
181 
182 	if (signature->ch_size > sizeof(*signature)) {
183 		LOG_WARNING("ChibiOS/RT memory signature claims to be bigger than"
184 					" expected. Assuming compatibility...");
185 	}
186 
187 	/* Convert endianness of version field */
188 	const uint8_t *versionTarget = (const uint8_t *)
189 										&signature->ch_version;
190 	signature->ch_version = rtos->target->endianness == TARGET_LITTLE_ENDIAN ?
191 			le_to_h_u32(versionTarget) : be_to_h_u32(versionTarget);
192 
193 	const uint16_t ch_version = signature->ch_version;
194 	LOG_INFO("Successfully loaded memory map of ChibiOS/RT target "
195 			"running version %i.%i.%i", GET_CH_KERNEL_MAJOR(ch_version),
196 			GET_CH_KERNEL_MINOR(ch_version), GET_CH_KERNEL_PATCH(ch_version));
197 
198 	/* Currently, we have the inherent assumption that all address pointers
199 	 * are 32 bit wide. */
200 	if (signature->ch_ptrsize != sizeof(uint32_t)) {
201 		LOG_ERROR("ChibiOS/RT target memory signature claims an address "
202 				  "width unequal to 32 bits!");
203 		free(signature);
204 		return -1;
205 	}
206 
207 	param->signature = signature;
208 	return 0;
209 
210 errfree:
211 	/* Error reading the ChibiOS memory structure */
212 	free(signature);
213 	param->signature = 0;
214 	return -1;
215 }
216 
217 
chibios_update_stacking(struct rtos * rtos)218 static int chibios_update_stacking(struct rtos *rtos)
219 {
220 	/* Sometimes the stacking can not be determined only by looking at the
221 	 * target name but only a runtime.
222 	 *
223 	 * For example, this is the case for Cortex-M4 targets and ChibiOS which
224 	 * only stack the FPU registers if it is enabled during ChibiOS build.
225 	 *
226 	 * Terminating which stacking is used is target depending.
227 	 *
228 	 * Assumptions:
229 	 *  - Once ChibiOS is actually initialized, the stacking is fixed.
230 	 *  - During startup code, the FPU might not be initialized and the
231 	 *    detection might fail.
232 	 *  - Since no threads are running during startup, the problem is solved
233 	 *    by delaying stacking detection until there are more threads
234 	 *    available than the current execution. In which case
235 	 *    chibios_get_thread_reg_list is called.
236 	 */
237 	int retval;
238 
239 	if (!rtos->rtos_specific_params)
240 		return -1;
241 
242 	struct chibios_params *param;
243 	param = (struct chibios_params *) rtos->rtos_specific_params;
244 
245 	/* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4  */
246 	struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target);
247 	if (is_armv7m(armv7m_target)) {
248 		if (armv7m_target->fp_feature != FP_NONE) {
249 			/* Found ARM v7m target which includes a FPU */
250 			uint32_t cpacr;
251 
252 			retval = target_read_u32(rtos->target, FPU_CPACR, &cpacr);
253 			if (retval != ERROR_OK) {
254 				LOG_ERROR("Could not read CPACR register to check FPU state");
255 				return -1;
256 			}
257 
258 			/* Check if CP10 and CP11 are set to full access.
259 			 * In ChibiOS this is done in ResetHandler() in crt0.c */
260 			if (cpacr & 0x00F00000) {
261 				LOG_DEBUG("Enabled FPU detected.");
262 				param->stacking_info = &rtos_chibios_arm_v7m_stacking_w_fpu;
263 				return 0;
264 			}
265 		}
266 
267 		/* Found ARM v7m target with no or disabled FPU */
268 		param->stacking_info = &rtos_chibios_arm_v7m_stacking;
269 		return 0;
270 	}
271 
272 	return -1;
273 }
274 
chibios_update_threads(struct rtos * rtos)275 static int chibios_update_threads(struct rtos *rtos)
276 {
277 	int retval;
278 	const struct chibios_params *param;
279 	int tasks_found = 0;
280 	int rtos_valid = -1;
281 
282 	if (!rtos->rtos_specific_params)
283 		return -1;
284 
285 	if (!rtos->symbols) {
286 		LOG_ERROR("No symbols for ChibiOS");
287 		return -3;
288 	}
289 
290 	param = (const struct chibios_params *) rtos->rtos_specific_params;
291 	/* Update the memory signature saved in the target memory */
292 	if (!param->signature) {
293 		retval = chibios_update_memory_signature(rtos);
294 		if (retval != ERROR_OK) {
295 			LOG_ERROR("Reading the memory signature of ChibiOS/RT failed");
296 			return retval;
297 		}
298 	}
299 
300 	/* wipe out previous thread details if any */
301 	rtos_free_threadlist(rtos);
302 
303 	/* ChibiOS does not save the current thread count. We have to first
304 	 * parse the double linked thread list to check for errors and the number of
305 	 * threads. */
306 	const uint32_t rlist = rtos->symbols[CHIBIOS_VAL_RLIST].address ?
307 		rtos->symbols[CHIBIOS_VAL_RLIST].address :
308 		rtos->symbols[CHIBIOS_VAL_CH].address + CH_RLIST_OFFSET /* ChibiOS3 */;
309 	const struct chibios_chdebug *signature = param->signature;
310 	uint32_t current;
311 	uint32_t previous;
312 	uint32_t older;
313 
314 	current = rlist;
315 	previous = rlist;
316 	while (1) {
317 		retval = target_read_u32(rtos->target,
318 								 current + signature->cf_off_newer, &current);
319 		if (retval != ERROR_OK) {
320 			LOG_ERROR("Could not read next ChibiOS thread");
321 			return retval;
322 		}
323 		/* Could be NULL if the kernel is not initialized yet or if the
324 		 * registry is corrupted. */
325 		if (current == 0) {
326 			LOG_ERROR("ChibiOS registry integrity check failed, NULL pointer");
327 
328 			rtos_valid = 0;
329 			break;
330 		}
331 		/* Fetch previous thread in the list as a integrity check. */
332 		retval = target_read_u32(rtos->target,
333 								 current + signature->cf_off_older, &older);
334 		if ((retval != ERROR_OK) || (older == 0) || (older != previous)) {
335 			LOG_ERROR("ChibiOS registry integrity check failed, "
336 						"double linked list violation");
337 			rtos_valid = 0;
338 			break;
339 		}
340 		/* Check for full iteration of the linked list. */
341 		if (current == rlist)
342 			break;
343 		tasks_found++;
344 		previous = current;
345 	}
346 	if (!rtos_valid) {
347 		/* No RTOS, there is always at least the current execution, though */
348 		LOG_INFO("Only showing current execution because of a broken "
349 				"ChibiOS thread registry.");
350 
351 		const char tmp_thread_name[] = "Current Execution";
352 		const char tmp_thread_extra_info[] = "No RTOS thread";
353 
354 		rtos->thread_details = malloc(
355 				sizeof(struct thread_detail));
356 		rtos->thread_details->threadid = 1;
357 		rtos->thread_details->exists = true;
358 
359 		rtos->thread_details->extra_info_str = malloc(
360 				sizeof(tmp_thread_extra_info));
361 		strcpy(rtos->thread_details->extra_info_str, tmp_thread_extra_info);
362 
363 		rtos->thread_details->thread_name_str = malloc(
364 				sizeof(tmp_thread_name));
365 		strcpy(rtos->thread_details->thread_name_str, tmp_thread_name);
366 
367 		rtos->current_thread = 1;
368 		rtos->thread_count = 1;
369 		return ERROR_OK;
370 	}
371 
372 	/* create space for new thread details */
373 	rtos->thread_details = malloc(
374 			sizeof(struct thread_detail) * tasks_found);
375 	if (!rtos->thread_details) {
376 		LOG_ERROR("Could not allocate space for thread details");
377 		return -1;
378 	}
379 
380 	rtos->thread_count = tasks_found;
381 	/* Loop through linked list. */
382 	struct thread_detail *curr_thrd_details = rtos->thread_details;
383 	while (curr_thrd_details < rtos->thread_details + tasks_found) {
384 		uint32_t name_ptr = 0;
385 		char tmp_str[CHIBIOS_THREAD_NAME_STR_SIZE];
386 
387 		retval = target_read_u32(rtos->target,
388 								 current + signature->cf_off_newer, &current);
389 		if (retval != ERROR_OK) {
390 			LOG_ERROR("Could not read next ChibiOS thread");
391 			return -6;
392 		}
393 
394 		/* Check for full iteration of the linked list. */
395 		if (current == rlist)
396 			break;
397 
398 		/* Save the thread pointer */
399 		curr_thrd_details->threadid = current;
400 
401 		/* read the name pointer */
402 		retval = target_read_u32(rtos->target,
403 								 current + signature->cf_off_name, &name_ptr);
404 		if (retval != ERROR_OK) {
405 			LOG_ERROR("Could not read ChibiOS thread name pointer from target");
406 			return retval;
407 		}
408 
409 		/* Read the thread name */
410 		retval = target_read_buffer(rtos->target, name_ptr,
411 									CHIBIOS_THREAD_NAME_STR_SIZE,
412 									(uint8_t *)&tmp_str);
413 		if (retval != ERROR_OK) {
414 			LOG_ERROR("Error reading thread name from ChibiOS target");
415 			return retval;
416 		}
417 		tmp_str[CHIBIOS_THREAD_NAME_STR_SIZE - 1] = '\x00';
418 
419 		if (tmp_str[0] == '\x00')
420 			strcpy(tmp_str, "No Name");
421 
422 		curr_thrd_details->thread_name_str = malloc(
423 				strlen(tmp_str) + 1);
424 		strcpy(curr_thrd_details->thread_name_str, tmp_str);
425 
426 		/* State info */
427 		uint8_t thread_state;
428 		const char *state_desc;
429 
430 		retval = target_read_u8(rtos->target,
431 								current + signature->cf_off_state, &thread_state);
432 		if (retval != ERROR_OK) {
433 			LOG_ERROR("Error reading thread state from ChibiOS target");
434 			return retval;
435 		}
436 
437 
438 		if (thread_state < CHIBIOS_NUM_STATES)
439 			state_desc = chibios_thread_states[thread_state];
440 		else
441 			state_desc = "Unknown";
442 
443 		curr_thrd_details->extra_info_str = malloc(strlen(
444 					state_desc)+8);
445 		sprintf(curr_thrd_details->extra_info_str, "State: %s", state_desc);
446 
447 		curr_thrd_details->exists = true;
448 
449 		curr_thrd_details++;
450 	}
451 
452 	uint32_t current_thrd;
453 	/* NOTE: By design, cf_off_name equals readylist_current_offset */
454 	retval = target_read_u32(rtos->target,
455 							 rlist + signature->cf_off_name,
456 							 &current_thrd);
457 	if (retval != ERROR_OK) {
458 		LOG_ERROR("Could not read current Thread from ChibiOS target");
459 		return retval;
460 	}
461 	rtos->current_thread = current_thrd;
462 
463 	return 0;
464 }
465 
chibios_get_thread_reg_list(struct rtos * rtos,int64_t thread_id,struct rtos_reg ** reg_list,int * num_regs)466 static int chibios_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
467 		struct rtos_reg **reg_list, int *num_regs)
468 {
469 	int retval;
470 	const struct chibios_params *param;
471 	uint32_t stack_ptr = 0;
472 
473 	if ((rtos == NULL) || (thread_id == 0) ||
474 			(rtos->rtos_specific_params == NULL))
475 		return -1;
476 
477 	param = (const struct chibios_params *) rtos->rtos_specific_params;
478 
479 	if (!param->signature)
480 		return -1;
481 
482 	/* Update stacking if it can only be determined from runtime information */
483 	if ((param->stacking_info == 0) &&
484 		(chibios_update_stacking(rtos) != ERROR_OK)) {
485 		LOG_ERROR("Failed to determine exact stacking for the target type %s", rtos->target->type->name);
486 		return -1;
487 	}
488 
489 	/* Read the stack pointer */
490 	retval = target_read_u32(rtos->target,
491 							 thread_id + param->signature->cf_off_ctx, &stack_ptr);
492 	if (retval != ERROR_OK) {
493 		LOG_ERROR("Error reading stack frame from ChibiOS thread");
494 		return retval;
495 	}
496 
497 	return rtos_generic_stack_read(rtos->target, param->stacking_info, stack_ptr, reg_list, num_regs);
498 }
499 
chibios_get_symbol_list_to_lookup(symbol_table_elem_t * symbol_list[])500 static int chibios_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[])
501 {
502 	*symbol_list = malloc(sizeof(chibios_symbol_list));
503 
504 	if (*symbol_list == NULL)
505 		return ERROR_FAIL;
506 
507 	memcpy(*symbol_list, chibios_symbol_list, sizeof(chibios_symbol_list));
508 	return 0;
509 }
510 
chibios_detect_rtos(struct target * target)511 static bool chibios_detect_rtos(struct target *target)
512 {
513 	if ((target->rtos->symbols != NULL) &&
514 			((target->rtos->symbols[CHIBIOS_VAL_RLIST].address != 0) ||
515 			 (target->rtos->symbols[CHIBIOS_VAL_CH].address != 0))) {
516 
517 		if (target->rtos->symbols[CHIBIOS_VAL_CH_DEBUG].address == 0) {
518 			LOG_INFO("It looks like the target may be running ChibiOS "
519 					"without ch_debug.");
520 			return false;
521 		}
522 
523 		/* looks like ChibiOS with memory map enabled.*/
524 		return true;
525 	}
526 
527 	return false;
528 }
529 
chibios_create(struct target * target)530 static int chibios_create(struct target *target)
531 {
532 	int i = 0;
533 	while ((i < CHIBIOS_NUM_PARAMS) &&
534 			(0 != strcmp(chibios_params_list[i].target_name, target->type->name))) {
535 		i++;
536 	}
537 	if (i >= CHIBIOS_NUM_PARAMS) {
538 		LOG_WARNING("Could not find target \"%s\" in ChibiOS compatibility "
539 				"list", target->type->name);
540 		return -1;
541 	}
542 
543 	target->rtos->rtos_specific_params = (void *) &chibios_params_list[i];
544 	return 0;
545 }
546