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