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