openocd: fix SPDX tag format for files .c
[fw/openocd] / src / rtos / riot.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /***************************************************************************
4  *   Copyright (C) 2015 by Daniel Krebs                                    *
5  *   Daniel Krebs - github@daniel-krebs.net                                *
6  ***************************************************************************/
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11
12 #include <helper/time_support.h>
13 #include <jtag/jtag.h>
14 #include "target/target.h"
15 #include "target/target_type.h"
16 #include "rtos.h"
17 #include "helper/log.h"
18 #include "helper/types.h"
19 #include "target/armv7m.h"
20 #include "rtos_riot_stackings.h"
21
22 static bool riot_detect_rtos(struct target *target);
23 static int riot_create(struct target *target);
24 static int riot_update_threads(struct rtos *rtos);
25 static int riot_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
26         struct rtos_reg **reg_list, int *num_regs);
27 static int riot_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[]);
28
29 struct riot_thread_state {
30         int value;
31         const char *desc;
32 };
33
34 /* refer RIOT sched.h */
35 static const struct riot_thread_state riot_thread_states[] = {
36         { 0, "Stopped" },
37         { 1, "Zombie" },
38         { 2, "Sleeping" },
39         { 3, "Blocked mutex" },
40         { 4, "Blocked receive" },
41         { 5, "Blocked send" },
42         { 6, "Blocked reply" },
43         { 7, "Blocked any flag" },
44         { 8, "Blocked all flags" },
45         { 9, "Blocked mbox" },
46         { 10, "Blocked condition" },
47         { 11, "Running" },
48         { 12, "Pending" },
49 };
50 #define RIOT_NUM_STATES ARRAY_SIZE(riot_thread_states)
51
52 struct riot_params {
53         const char *target_name;
54         unsigned char thread_sp_offset;
55         unsigned char thread_status_offset;
56 };
57
58 static const struct riot_params riot_params_list[] = {
59         {
60                 "cortex_m",             /* target_name */
61                 0x00,                                   /* thread_sp_offset */
62                 0x04,                                   /* thread_status_offset */
63         },
64         {       /* STLink */
65                 "hla_target",           /* target_name */
66                 0x00,                   /* thread_sp_offset */
67                 0x04,                   /* thread_status_offset */
68         }
69 };
70 #define RIOT_NUM_PARAMS ARRAY_SIZE(riot_params_list)
71
72 /* Initialize in riot_create() depending on architecture */
73 static const struct rtos_register_stacking *stacking_info;
74
75 enum riot_symbol_values {
76         RIOT_THREADS_BASE = 0,
77         RIOT_NUM_THREADS,
78         RIOT_ACTIVE_PID,
79         RIOT_MAX_THREADS,
80         RIOT_NAME_OFFSET,
81 };
82
83 struct riot_symbol {
84         const char *const name;
85         bool optional;
86 };
87
88 /* refer RIOT core/sched.c */
89 static struct riot_symbol const riot_symbol_list[] = {
90         {"sched_threads", false},
91         {"sched_num_threads", false},
92         {"sched_active_pid", false},
93         {"max_threads", false},
94         {"_tcb_name_offset", true},
95         {NULL, false}
96 };
97
98 const struct rtos_type riot_rtos = {
99         .name = "RIOT",
100         .detect_rtos = riot_detect_rtos,
101         .create = riot_create,
102         .update_threads = riot_update_threads,
103         .get_thread_reg_list = riot_get_thread_reg_list,
104         .get_symbol_list_to_lookup = riot_get_symbol_list_to_lookup,
105 };
106
107 static int riot_update_threads(struct rtos *rtos)
108 {
109         int retval;
110         int tasks_found = 0;
111         const struct riot_params *param;
112
113         if (!rtos)
114                 return ERROR_FAIL;
115
116         if (!rtos->rtos_specific_params)
117                 return ERROR_FAIL;
118
119         param = (const struct riot_params *)rtos->rtos_specific_params;
120
121         if (!rtos->symbols) {
122                 LOG_ERROR("No symbols for RIOT");
123                 return ERROR_FAIL;
124         }
125
126         if (rtos->symbols[RIOT_THREADS_BASE].address == 0) {
127                 LOG_ERROR("Can't find symbol `%s`",
128                         riot_symbol_list[RIOT_THREADS_BASE].name);
129                 return ERROR_FAIL;
130         }
131
132         /* wipe out previous thread details if any */
133         rtos_free_threadlist(rtos);
134
135         /* Reset values */
136         rtos->current_thread = 0;
137         rtos->thread_count = 0;
138
139         /* read the current thread id */
140         int16_t active_pid = 0;
141         retval = target_read_u16(rtos->target,
142                         rtos->symbols[RIOT_ACTIVE_PID].address,
143                         (uint16_t *)&active_pid);
144         if (retval != ERROR_OK) {
145                 LOG_ERROR("Can't read symbol `%s`",
146                         riot_symbol_list[RIOT_ACTIVE_PID].name);
147                 return retval;
148         }
149         rtos->current_thread = active_pid;
150
151         /* read the current thread count
152          * It's `int` in RIOT, but this is Cortex M* only anyway */
153         int32_t thread_count = 0;
154         retval = target_read_u16(rtos->target,
155                         rtos->symbols[RIOT_NUM_THREADS].address,
156                         (uint16_t *)&thread_count);
157         if (retval != ERROR_OK) {
158                 LOG_ERROR("Can't read symbol `%s`",
159                         riot_symbol_list[RIOT_NUM_THREADS].name);
160                 return retval;
161         }
162
163         /* read the maximum number of threads */
164         uint8_t max_threads = 0;
165         retval = target_read_u8(rtos->target,
166                         rtos->symbols[RIOT_MAX_THREADS].address,
167                         &max_threads);
168         if (retval != ERROR_OK) {
169                 LOG_ERROR("Can't read symbol `%s`",
170                         riot_symbol_list[RIOT_MAX_THREADS].name);
171                 return retval;
172         }
173         if (thread_count > max_threads) {
174                 LOG_ERROR("Thread count is invalid");
175                 return ERROR_FAIL;
176         }
177         rtos->thread_count = thread_count;
178
179         /* Base address of thread array */
180         uint32_t threads_base = rtos->symbols[RIOT_THREADS_BASE].address;
181
182         /* Try to get the offset of tcb_t::name, if absent RIOT wasn't compiled
183          * with DEVELHELP, so there are no thread names */
184         uint8_t name_offset = 0;
185         if (rtos->symbols[RIOT_NAME_OFFSET].address != 0) {
186                 retval = target_read_u8(rtos->target,
187                                 rtos->symbols[RIOT_NAME_OFFSET].address,
188                                 &name_offset);
189                 if (retval != ERROR_OK) {
190                         LOG_ERROR("Can't read symbol `%s`",
191                                 riot_symbol_list[RIOT_NAME_OFFSET].name);
192                         return retval;
193                 }
194         }
195
196         /* Allocate memory for thread description */
197         rtos->thread_details = calloc(thread_count, sizeof(struct thread_detail));
198         if (!rtos->thread_details) {
199                 LOG_ERROR("RIOT: out of memory");
200                 return ERROR_FAIL;
201         }
202
203         /* Buffer for thread names, maximum to display is 32 */
204         char buffer[32];
205
206         for (unsigned int i = 0; i < max_threads; i++) {
207                 if (tasks_found == rtos->thread_count)
208                         break;
209
210                 /* get pointer to tcb_t */
211                 uint32_t tcb_pointer = 0;
212                 retval = target_read_u32(rtos->target,
213                                 threads_base + (i * 4),
214                                 &tcb_pointer);
215                 if (retval != ERROR_OK) {
216                         LOG_ERROR("Can't parse `%s`",
217                                 riot_symbol_list[RIOT_THREADS_BASE].name);
218                         goto error;
219                 }
220
221                 if (tcb_pointer == 0) {
222                         /* PID unused */
223                         continue;
224                 }
225
226                 /* Index is PID */
227                 rtos->thread_details[tasks_found].threadid = i;
228
229                 /* read thread state */
230                 uint8_t status = 0;
231                 retval = target_read_u8(rtos->target,
232                                 tcb_pointer + param->thread_status_offset,
233                                 &status);
234                 if (retval != ERROR_OK) {
235                         LOG_ERROR("Can't parse `%s`",
236                                 riot_symbol_list[RIOT_THREADS_BASE].name);
237                         goto error;
238                 }
239
240                 /* Search for state */
241                 unsigned int k;
242                 for (k = 0; k < RIOT_NUM_STATES; k++) {
243                         if (riot_thread_states[k].value == status)
244                                 break;
245                 }
246
247                 /* Copy state string */
248                 if (k >= RIOT_NUM_STATES) {
249                         rtos->thread_details[tasks_found].extra_info_str =
250                         strdup("unknown state");
251                 } else {
252                         rtos->thread_details[tasks_found].extra_info_str =
253                         strdup(riot_thread_states[k].desc);
254                 }
255
256                 if (!rtos->thread_details[tasks_found].extra_info_str) {
257                         LOG_ERROR("RIOT: out of memory");
258                         retval = ERROR_FAIL;
259                         goto error;
260                 }
261
262                 /* Thread names are only available if compiled with DEVELHELP */
263                 if (name_offset != 0) {
264                         uint32_t name_pointer = 0;
265                         retval = target_read_u32(rtos->target,
266                                         tcb_pointer + name_offset,
267                                         &name_pointer);
268                         if (retval != ERROR_OK) {
269                                 LOG_ERROR("Can't parse `%s`",
270                                         riot_symbol_list[RIOT_THREADS_BASE].name);
271                                 goto error;
272                         }
273
274                         /* read thread name */
275                         retval = target_read_buffer(rtos->target,
276                                         name_pointer,
277                                         sizeof(buffer),
278                                         (uint8_t *)&buffer);
279                         if (retval != ERROR_OK) {
280                                 LOG_ERROR("Can't parse `%s`",
281                                         riot_symbol_list[RIOT_THREADS_BASE].name);
282                                 goto error;
283                         }
284
285                         /* Make sure the string in the buffer terminates */
286                         if (buffer[sizeof(buffer) - 1] != 0)
287                                 buffer[sizeof(buffer) - 1] = 0;
288
289                         /* Copy thread name */
290                         rtos->thread_details[tasks_found].thread_name_str =
291                         strdup(buffer);
292
293                 } else {
294                         rtos->thread_details[tasks_found].thread_name_str =
295                         strdup("Enable DEVELHELP to see task names");
296                 }
297
298                 if (!rtos->thread_details[tasks_found].thread_name_str) {
299                         LOG_ERROR("RIOT: out of memory");
300                         retval = ERROR_FAIL;
301                         goto error;
302                 }
303
304                 rtos->thread_details[tasks_found].exists = true;
305
306                 tasks_found++;
307         }
308
309         return ERROR_OK;
310
311 error:
312         rtos_free_threadlist(rtos);
313         return retval;
314 }
315
316 static int riot_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
317                 struct rtos_reg **reg_list, int *num_regs)
318 {
319         int retval;
320         const struct riot_params *param;
321
322         if (!rtos)
323                 return ERROR_FAIL;
324
325         if (thread_id == 0)
326                 return ERROR_FAIL;
327
328         if (!rtos->rtos_specific_params)
329                 return ERROR_FAIL;
330
331         param = (const struct riot_params *)rtos->rtos_specific_params;
332
333         /* find the thread with given thread id */
334         uint32_t threads_base = rtos->symbols[RIOT_THREADS_BASE].address;
335         uint32_t tcb_pointer = 0;
336         retval = target_read_u32(rtos->target,
337                         threads_base + (thread_id * 4),
338                         &tcb_pointer);
339         if (retval != ERROR_OK) {
340                 LOG_ERROR("Can't parse `%s`", riot_symbol_list[RIOT_THREADS_BASE].name);
341                 return retval;
342         }
343
344         /* read stack pointer for that thread */
345         uint32_t stackptr = 0;
346         retval = target_read_u32(rtos->target,
347                         tcb_pointer + param->thread_sp_offset,
348                         &stackptr);
349         if (retval != ERROR_OK) {
350                 LOG_ERROR("Can't parse `%s`", riot_symbol_list[RIOT_THREADS_BASE].name);
351                 return retval;
352         }
353
354         return rtos_generic_stack_read(rtos->target,
355                         stacking_info,
356                         stackptr,
357                         reg_list,
358                         num_regs);
359 }
360
361 static int riot_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[])
362 {
363         *symbol_list = calloc(ARRAY_SIZE(riot_symbol_list), sizeof(struct symbol_table_elem));
364
365         if (!*symbol_list) {
366                 LOG_ERROR("RIOT: out of memory");
367                 return ERROR_FAIL;
368         }
369
370         for (unsigned int i = 0; i < ARRAY_SIZE(riot_symbol_list); i++) {
371                 (*symbol_list)[i].symbol_name = riot_symbol_list[i].name;
372                 (*symbol_list)[i].optional = riot_symbol_list[i].optional;
373         }
374
375         return ERROR_OK;
376 }
377
378 static bool riot_detect_rtos(struct target *target)
379 {
380         if ((target->rtos->symbols) &&
381                 (target->rtos->symbols[RIOT_THREADS_BASE].address != 0)) {
382                 /* looks like RIOT */
383                 return true;
384         }
385         return false;
386 }
387
388 static int riot_create(struct target *target)
389 {
390         unsigned int i = 0;
391
392         /* lookup if target is supported by RIOT */
393         while ((i < RIOT_NUM_PARAMS) &&
394                 (strcmp(riot_params_list[i].target_name, target->type->name) != 0)) {
395                 i++;
396         }
397         if (i >= RIOT_NUM_PARAMS) {
398                 LOG_ERROR("Could not find target in RIOT compatibility list");
399                 return ERROR_FAIL;
400         }
401
402         target->rtos->rtos_specific_params = (void *)&riot_params_list[i];
403         target->rtos->current_thread = 0;
404         target->rtos->thread_details = NULL;
405
406         /* Stacking is different depending on architecture */
407         struct armv7m_common *armv7m_target = target_to_armv7m(target);
408
409         if (armv7m_target->arm.arch == ARM_ARCH_V6M)
410                 stacking_info = &rtos_riot_cortex_m0_stacking;
411         else if (is_armv7m(armv7m_target))
412                 stacking_info = &rtos_riot_cortex_m34_stacking;
413         else {
414                 LOG_ERROR("No stacking info for architecture");
415                 return ERROR_FAIL;
416         }
417         return ERROR_OK;
418 }