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