target: try to reexamine even when polling fails
[fw/openocd] / src / rtos / mqx.c
1 /***************************************************************************
2  *   Copyright (C) 2014 by Marian Cingel                                   *
3  *   cingel.marian@gmail.com                                               *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.                                        *
18  ***************************************************************************/
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <stdint.h>
25 #include <helper/time_support.h>
26 #include <jtag/jtag.h>
27 #include "target/target.h"
28 #include "target/target_type.h"
29 #include "rtos.h"
30 #include "helper/log.h"
31 #include "helper/types.h"
32 #include "rtos_mqx_stackings.h"
33
34 /* constants */
35 #define MQX_THREAD_NAME_LENGTH                  (255)
36 #define MQX_KERNEL_OFFSET_TDLIST                (0x0108)
37 #define MQX_KERNEL_OFFSET_SYSTEM_TASK   (0x0050)
38 #define MQX_KERNEL_OFFSET_ACTIVE_TASK   (0x001C)
39 #define MQX_KERNEL_OFFSET_CAPABILITY    (0x0000)
40 #define MQX_QUEUE_OFFSET_SIZE                   (0x0008)
41 #define MQX_TASK_OFFSET_STATE                   (0x0008)
42 #define MQX_TASK_OFFSET_ID                              (0x000c)
43 #define MQX_TASK_OFFSET_TEMPLATE                (0x0068)
44 #define MQX_TASK_OFFSET_STACK                   (0x0014)
45 #define MQX_TASK_OFFSET_TDLIST                  (0x006C)
46 #define MQX_TASK_OFFSET_NEXT                    (0x0000)
47 #define MQX_TASK_TEMPLATE_OFFSET_NAME   (0x0010)
48 #define MQX_TASK_OFFSET_ERROR_CODE              (0x005C)
49 #define MQX_TASK_STATE_MASK                             (0xFFF)
50
51 /* types */
52 enum mqx_symbols {
53         mqx_VAL_mqx_kernel_data,
54         mqx_VAL_MQX_init_struct,
55 };
56
57 enum mqx_arch {
58         mqx_arch_cortexm,
59 };
60
61 struct mqx_params {
62         const char *target_name;
63         const enum mqx_arch target_arch;
64         const struct rtos_register_stacking *stacking_info;
65 };
66
67 struct mqx_state {
68         uint32_t state;
69         char *name;
70 };
71
72 /* local data */
73 static const struct mqx_state mqx_states[] = {
74         { 0x0002, "READY" },
75         { 0x0003, "BLOCKED" },
76         { 0x0005, "RCV_SPECIFIC_BLOCKED" },
77         { 0x0007, "RCV_ANY_BLOCKED" },
78         { 0x0009, "DYING" },
79         { 0x000B, "UNHANDLED_INT_BLOCKED" },
80         { 0x000D, "SEND_BLOCKED" },
81         { 0x000F, "BREAKPOINT_BLOCKED" },
82         { 0x0211, "IO_BLOCKED" },
83         { 0x0021, "SEM_BLOCKED" },
84         { 0x0223, "MUTEX_BLOCKED" },
85         { 0x0025, "EVENT_BLOCKED" },
86         { 0x0229, "TASK_QUEUE_BLOCKED" },
87         { 0x042B, "LWSEM_BLOCKED" },
88         { 0x042D, "LWEVENT_BLOCKED" },
89 };
90
91 static const char * const mqx_symbol_list[] = {
92         "_mqx_kernel_data",
93         "MQX_init_struct",
94         NULL
95 };
96
97 static const struct mqx_params mqx_params_list[] = {
98         { "cortex_m", mqx_arch_cortexm, &rtos_mqx_arm_v7m_stacking },
99 };
100
101 /*
102  * Perform simple address check to avoid bus fault.
103  */
104 static int mqx_valid_address_check(
105         struct rtos *rtos,
106         uint32_t address
107 )
108 {
109         enum mqx_arch arch_type = ((struct mqx_params *)rtos->rtos_specific_params)->target_arch;
110         const char * targetname = ((struct mqx_params *)rtos->rtos_specific_params)->target_name;
111
112         /* Cortex M address range */
113         if (arch_type == mqx_arch_cortexm) {
114                 if (
115                         /* code and sram area */
116                         (address && address <= 0x3FFFFFFFu) ||
117                         /* external ram area*/
118                         (address >= 0x6000000u && address <= 0x9FFFFFFFu)
119                 ) {
120                         return ERROR_OK;
121                 }
122                 return ERROR_FAIL;
123         }
124         LOG_ERROR("MQX RTOS - unknown architecture %s", targetname);
125         return ERROR_FAIL;
126 }
127
128 /*
129  * Wrapper of 'target_read_buffer' fn.
130  * Include address check.
131  */
132 static int mqx_target_read_buffer(
133         struct target *target,
134         uint32_t address,
135         uint32_t size,
136         uint8_t *buffer
137 )
138 {
139         int status = mqx_valid_address_check(target->rtos, address);
140         if (status != ERROR_OK) {
141                 LOG_WARNING("MQX RTOS - target address 0x%" PRIx32 " is not allowed to read", address);
142                 return status;
143         }
144         status = target_read_buffer(target, address, size, buffer);
145         if (status != ERROR_OK) {
146                 LOG_ERROR("MQX RTOS - reading target address 0x%" PRIx32" failed", address);
147                 return status;
148         }
149         return ERROR_OK;
150 }
151
152 /*
153  * Get symbol address if present
154  */
155 static int mqx_get_symbol(
156         struct rtos *rtos,
157         enum mqx_symbols symbol,
158         void *result
159 )
160 {
161         /* TODO: additional check ?? */
162         (*(int *)result) = (uint32_t)rtos->symbols[symbol].address;
163         return ERROR_OK;
164 }
165
166 /*
167  * Get value of struct member by passing
168  * member offset, width and name (debug purpose)
169  */
170 static int mqx_get_member(
171         struct rtos *rtos,
172         const uint32_t base_address,
173         int32_t member_offset,
174         int32_t member_width,
175         const char *member_name,
176         void *result
177 )
178 {
179         int status = ERROR_FAIL;
180         status = mqx_target_read_buffer(
181                 rtos->target, base_address + member_offset, member_width, result
182         );
183         if (status != ERROR_OK)
184                 LOG_WARNING("MQX RTOS - cannot read \"%s\" at address 0x%" PRIx32,
185                             member_name, (uint32_t)(base_address + member_offset));
186         return status;
187 }
188
189 /*
190  * Check whether scheduler started
191  */
192 static int mqx_is_scheduler_running(
193         struct rtos *rtos
194 )
195 {
196         uint32_t kernel_data_symbol = 0;
197         uint32_t kernel_data_addr = 0;
198         uint32_t system_td_addr = 0;
199         uint32_t active_td_addr = 0;
200         uint32_t capability_value = 0;
201
202         /* get '_mqx_kernel_data' symbol */
203         if (ERROR_OK != mqx_get_symbol(
204                 rtos, mqx_VAL_mqx_kernel_data, &kernel_data_symbol
205         )) {
206                 return ERROR_FAIL;
207         }
208         /* get '_mqx_kernel_data' */
209         if (ERROR_OK != mqx_get_member(
210                 rtos, kernel_data_symbol, 0, 4,
211                 "_mqx_kernel_data", &kernel_data_addr
212         )) {
213                 return ERROR_FAIL;
214         }
215         /* return if '_mqx_kernel_data' is NULL or default 0xFFFFFFFF */
216         if (0 == kernel_data_addr || (uint32_t)(-1) == kernel_data_addr)
217                 return ERROR_FAIL;
218         /* get kernel_data->ADDRESSING_CAPABILITY */
219         if (ERROR_OK != mqx_get_member(
220                 rtos, kernel_data_addr, MQX_KERNEL_OFFSET_CAPABILITY, 4,
221                 "kernel_data->ADDRESSING_CAPABILITY", (void *)&capability_value
222         )) {
223                 return ERROR_FAIL;
224         }
225         /* check first member, the '_mqx_kernel_data->ADDRESSING_CAPABILITY'.
226            it supose to be set to value 8 */
227         if (capability_value != 8) {
228                 LOG_WARNING("MQX RTOS - value of '_mqx_kernel_data->ADDRESSING_CAPABILITY' contains invalid value");
229                 return ERROR_FAIL;
230         }
231         /* get active ptr */
232         if (ERROR_OK != mqx_get_member(
233                 rtos, kernel_data_addr, MQX_KERNEL_OFFSET_ACTIVE_TASK, 4,
234                 "kernel_data->ACTIVE_PTR", (void *)&active_td_addr
235         )) {
236                 return ERROR_FAIL;
237         }
238         /* active task is system task, scheduler has not not run yet */
239         system_td_addr = kernel_data_addr + MQX_KERNEL_OFFSET_SYSTEM_TASK;
240         if (active_td_addr == system_td_addr) {
241                 LOG_WARNING("MQX RTOS - scheduler does not run");
242                 return ERROR_FAIL;
243         }
244         return ERROR_OK;
245 }
246
247 /*
248  * API function, return 1 if MQX is present
249  */
250 static int mqx_detect_rtos(
251         struct target *target
252 )
253 {
254         if (
255                 (target->rtos->symbols != NULL) &&
256                 (target->rtos->symbols[mqx_VAL_mqx_kernel_data].address != 0)
257         ) {
258                 return 1;
259         }
260         return 0;
261 }
262
263 /*
264  * API function, pass MQX extra info to context data
265  */
266 static int mqx_create(
267         struct target *target
268 )
269 {
270         /* check target name against supported architectures */
271         int mqx_params_list_num = (sizeof(mqx_params_list)/sizeof(struct mqx_params));
272         for (int i = 0; i < mqx_params_list_num; i++) {
273                 if (0 == strcmp(mqx_params_list[i].target_name, target->type->name)) {
274                         target->rtos->rtos_specific_params = (void *)&mqx_params_list[i];
275                         /* LOG_DEBUG("MQX RTOS - valid architecture: %s", target->type->name); */
276                         return 0;
277                 }
278         }
279         LOG_ERROR("MQX RTOS - could not find target \"%s\" in MQX compatibility list", target->type->name);
280         return -1;
281 }
282
283 /*
284  * API function, update list of threads
285  */
286 static int mqx_update_threads(
287         struct rtos *rtos
288 )
289 {
290         uint32_t task_queue_addr = 0;
291         uint32_t kernel_data_addr = 0;
292         uint16_t task_queue_size = 0;
293         uint32_t active_td_addr = 0;
294
295         /* clear old data */
296         rtos_free_threadlist(rtos);
297         /* check scheduler */
298         if (ERROR_OK != mqx_is_scheduler_running(rtos))
299                 return ERROR_FAIL;
300         /* get kernel_data symbol */
301         if (ERROR_OK != mqx_get_symbol(
302                 rtos, mqx_VAL_mqx_kernel_data, &kernel_data_addr
303         )) {
304                 return ERROR_FAIL;
305         }
306         /* read kernel_data */
307         if (ERROR_OK != mqx_get_member(
308                 rtos, kernel_data_addr, 0, 4, "_mqx_kernel_data", &kernel_data_addr
309         )) {
310                 return ERROR_FAIL;
311         }
312         /* get task queue address */
313         task_queue_addr = kernel_data_addr + MQX_KERNEL_OFFSET_TDLIST;
314         /* get task queue size */
315         if (ERROR_OK != mqx_get_member(
316                 rtos, task_queue_addr, MQX_QUEUE_OFFSET_SIZE, 2,
317                 "kernel_data->TD_LIST.SIZE", &task_queue_size
318         )) {
319                 return ERROR_FAIL;
320         }
321         /* get active ptr */
322         if (ERROR_OK != mqx_get_member(
323                 rtos, kernel_data_addr, MQX_KERNEL_OFFSET_ACTIVE_TASK, 4,
324                 "kernel_data->ACTIVE_PTR", (void *)&active_td_addr
325         )) {
326                 return ERROR_FAIL;
327         }
328
329         /* setup threads info */
330         rtos->thread_count = task_queue_size;
331         rtos->current_thread = 0;
332         rtos->thread_details = calloc(rtos->thread_count, sizeof(struct thread_detail));
333         if (NULL == rtos->thread_details)
334                 return ERROR_FAIL;
335
336         /*      loop over each task and setup thread details,
337                 the current_taskpool_addr is set to queue head
338                 NOTE: debugging functions task create/destroy
339                 might cause to show invalid data.
340         */
341         for (
342                 uint32_t i = 0, taskpool_addr = task_queue_addr;
343                 i < (uint32_t)rtos->thread_count;
344                 i++
345         ) {
346                 uint8_t task_name[MQX_THREAD_NAME_LENGTH + 1];
347                 uint32_t task_addr = 0, task_template = 0, task_state = 0;
348                 uint32_t task_name_addr = 0, task_id = 0, task_errno = 0;
349                 uint32_t state_index = 0, state_max = 0;
350                 uint32_t extra_info_length = 0;
351                 char *state_name = "unknown state";
352
353                 /* set current taskpool address */
354                 if (ERROR_OK != mqx_get_member(
355                         rtos, taskpool_addr, MQX_TASK_OFFSET_NEXT, 4,
356                         "td_struct_ptr->NEXT", &taskpool_addr
357                 )) {
358                         return ERROR_FAIL;
359                 }
360                 /* get task address from taskpool */
361                 task_addr = taskpool_addr - MQX_TASK_OFFSET_TDLIST;
362                 /* get address of 'td_struct_ptr->TEMPLATE_LIST_PTR' */
363                 if (ERROR_OK != mqx_get_member(
364                         rtos, task_addr, MQX_TASK_OFFSET_TEMPLATE, 4,
365                         "td_struct_ptr->TEMPLATE_LIST_PTR", &task_template
366                 )) {
367                         return ERROR_FAIL;
368                 }
369                 /* get address of 'td_struct_ptr->TEMPLATE_LIST_PTR->NAME' */
370                 if (ERROR_OK != mqx_get_member(
371                         rtos, task_template, MQX_TASK_TEMPLATE_OFFSET_NAME, 4,
372                         "td_struct_ptr->TEMPLATE_LIST_PTR->NAME", &task_name_addr
373                 )) {
374                         return ERROR_FAIL;
375                 }
376                 /* get value of 'td_struct->TEMPLATE_LIST_PTR->NAME' */
377                 if (ERROR_OK != mqx_get_member(
378                         rtos, task_name_addr, 0, MQX_THREAD_NAME_LENGTH,
379                         "*td_struct_ptr->TEMPLATE_LIST_PTR->NAME", task_name
380                 )) {
381                         return ERROR_FAIL;
382                 }
383                 /* always terminate last character by force,
384                    otherwise openocd might fail if task_name
385                    has corrupted data */
386                 task_name[MQX_THREAD_NAME_LENGTH] = '\0';
387                 /* get value of 'td_struct_ptr->TASK_ID' */
388                 if (ERROR_OK != mqx_get_member(
389                         rtos, task_addr, MQX_TASK_OFFSET_ID, 4,
390                         "td_struct_ptr->TASK_ID", &task_id
391                 )) {
392                         return ERROR_FAIL;
393                 }
394                 /* get task errno */
395                 if (ERROR_OK != mqx_get_member(
396                         rtos, task_addr, MQX_TASK_OFFSET_ERROR_CODE, 4,
397                         "td_struct_ptr->TASK_ERROR_CODE", &task_errno
398                 )) {
399                         return ERROR_FAIL;
400                 }
401                 /* get value of 'td_struct_ptr->STATE' */
402                 if (ERROR_OK != mqx_get_member(
403                         rtos, task_addr, MQX_TASK_OFFSET_STATE, 4,
404                         "td_struct_ptr->STATE", &task_state
405                 )) {
406                         return ERROR_FAIL;
407                 }
408                 task_state &= MQX_TASK_STATE_MASK;
409                 /* and search for defined state */
410                 state_max = (sizeof(mqx_states)/sizeof(struct mqx_state));
411                 for (state_index = 0; (state_index < state_max); state_index++) {
412                         if (mqx_states[state_index].state == task_state) {
413                                 state_name = mqx_states[state_index].name;
414                                 break;
415                         }
416                 }
417
418                 /* setup thread details struct */
419                 rtos->thread_details[i].threadid = task_id;
420                 rtos->thread_details[i].exists = true;
421                 rtos->thread_details[i].display_str = NULL;
422                 /* set thread name */
423                 rtos->thread_details[i].thread_name_str = malloc(strlen((void *)task_name) + 1);
424                 if (NULL == rtos->thread_details[i].thread_name_str)
425                         return ERROR_FAIL;
426                 strcpy(rtos->thread_details[i].thread_name_str, (void *)task_name);
427                 /* set thread extra info
428                  * - task state
429                  * - task address
430                  * - task errno
431                  * calculate length as:
432                  * state length + address length + errno length + formatter length
433                  */
434                 extra_info_length += strlen((void *)state_name) + 8 + 8 + 8;
435                 rtos->thread_details[i].extra_info_str = malloc(extra_info_length + 1);
436                 if (NULL == rtos->thread_details[i].extra_info_str)
437                         return ERROR_FAIL;
438                 snprintf(
439                         rtos->thread_details[i].extra_info_str, extra_info_length, "%s : 0x%"PRIx32 " : %" PRIu32,
440                         state_name, task_addr, task_errno
441                 );
442                 /* set active thread */
443                 if (active_td_addr == task_addr)
444                         rtos->current_thread = task_id;
445         }
446         return ERROR_OK;
447 }
448
449 /*
450  * API function, get info of selected thread
451  */
452 static int mqx_get_thread_reg_list(
453         struct rtos *rtos,
454         int64_t thread_id,
455         char **hex_reg_list
456 )
457 {
458         int64_t stack_ptr = 0;
459         uint32_t my_task_addr = 0;
460         uint32_t task_queue_addr = 0;
461         uint32_t task_queue_size = 0;
462         uint32_t kernel_data_addr = 0;
463
464         *hex_reg_list = NULL;
465         if (thread_id == 0) {
466                 LOG_ERROR("MQX RTOS - invalid threadid: 0x%X", (int)thread_id);
467                 return ERROR_FAIL;
468         }
469         if (ERROR_OK != mqx_is_scheduler_running(rtos))
470                 return ERROR_FAIL;
471         /* get kernel_data symbol */
472         if (ERROR_OK != mqx_get_symbol(
473                 rtos, mqx_VAL_mqx_kernel_data, &kernel_data_addr
474         )) {
475                 return ERROR_FAIL;
476         }
477         /* read kernel_data */
478         if (ERROR_OK != mqx_get_member(
479                 rtos, kernel_data_addr, 0, 4, "_mqx_kernel_data", &kernel_data_addr
480         )) {
481                 return ERROR_FAIL;
482         }
483         /* get task queue address */
484         task_queue_addr = kernel_data_addr + MQX_KERNEL_OFFSET_TDLIST;
485         /* get task queue size */
486         if (ERROR_OK != mqx_get_member(
487                 rtos, task_queue_addr, MQX_QUEUE_OFFSET_SIZE, 2,
488                 "kernel_data->TD_LIST.SIZE", &task_queue_size
489         )) {
490                 return ERROR_FAIL;
491         }
492         /* search for taskid */
493         for (
494                 uint32_t i = 0, taskpool_addr = task_queue_addr;
495                 i < (uint32_t)rtos->thread_count;
496                 i++
497         ) {
498                 uint32_t tmp_address = 0, task_addr = 0;
499                 uint32_t task_id = 0;
500                 /* set current taskpool address */
501                 tmp_address = taskpool_addr;
502                 if (ERROR_OK != mqx_get_member(
503                         rtos, tmp_address, MQX_TASK_OFFSET_NEXT, 4,
504                         "td_struct_ptr->NEXT", &taskpool_addr
505                 )) {
506                         return ERROR_FAIL;
507                 }
508                 /* get task address from taskpool */
509                 task_addr = taskpool_addr - MQX_TASK_OFFSET_TDLIST;
510                 /* get value of td_struct->TASK_ID */
511                 if (ERROR_OK != mqx_get_member(
512                         rtos, task_addr, MQX_TASK_OFFSET_ID, 4,
513                         "td_struct_ptr->TASK_ID", &task_id
514                 )) {
515                         return ERROR_FAIL;
516                 }
517                 /* found taskid, break */
518                 if (task_id == thread_id) {
519                         my_task_addr = task_addr;
520                         break;
521                 }
522         }
523         if (!my_task_addr) {
524                 LOG_ERROR("MQX_RTOS - threadid %" PRId64 " does not match any task", thread_id);
525                 return ERROR_FAIL;
526         }
527         /* get task stack head address */
528         if (ERROR_OK != mqx_get_member(
529                 rtos, my_task_addr, MQX_TASK_OFFSET_STACK, 4, "task->STACK_PTR", &stack_ptr
530         )) {
531                 return ERROR_FAIL;
532         }
533         return rtos_generic_stack_read(
534                 rtos->target, ((struct mqx_params *)rtos->rtos_specific_params)->stacking_info, stack_ptr, hex_reg_list
535         );
536 }
537
538 /* API function, export list of required symbols */
539 static int mqx_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[])
540 {
541         *symbol_list = malloc(sizeof(symbol_table_elem_t) * ARRAY_SIZE(mqx_symbol_list));
542         if (NULL == *symbol_list)
543                 return ERROR_FAIL;
544         /* export required symbols */
545         for (int i = 0; i < (int)(ARRAY_SIZE(mqx_symbol_list)); i++)
546                 (*symbol_list)[i].symbol_name = mqx_symbol_list[i];
547         return ERROR_OK;
548 }
549
550 struct rtos_type mqx_rtos = {
551         .name = "mqx",
552         .detect_rtos = mqx_detect_rtos,
553         .create = mqx_create,
554         .update_threads = mqx_update_threads,
555         .get_thread_reg_list = mqx_get_thread_reg_list,
556         .get_symbol_list_to_lookup = mqx_get_symbol_list_to_lookup,
557 };