Add get_thread_reg() and set_reg() for hwthread
[fw/openocd] / src / rtos / hwthread.c
1 /***************************************************************************
2  *                                                                         *
3  *   This program is free software; you can redistribute it and/or modify  *
4  *   it under the terms of the GNU General Public License as published by  *
5  *   the Free Software Foundation; either version 2 of the License, or     *
6  *   (at your option) any later version.                                   *
7  *                                                                         *
8  *   This program is distributed in the hope that it will be useful,       *
9  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
10  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
11  *   GNU General Public License for more details.                          *
12  *                                                                         *
13  *   You should have received a copy of the GNU General Public License     *
14  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
15  ***************************************************************************/
16
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include <helper/time_support.h>
22 #include <jtag/jtag.h>
23 #include "target/target.h"
24 #include "target/target_type.h"
25 #include "target/register.h"
26 #include "rtos.h"
27 #include "helper/log.h"
28 #include "helper/types.h"
29 #include "server/gdb_server.h"
30
31 static bool hwthread_detect_rtos(struct target *target);
32 static int hwthread_create(struct target *target);
33 static int hwthread_update_threads(struct rtos *rtos);
34 static int hwthread_get_thread_reg(struct rtos *rtos, int64_t thread_id,
35                 uint32_t reg_num, struct rtos_reg *rtos_reg);
36 static int hwthread_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
37                 struct rtos_reg **reg_list, int *num_regs);
38 static int hwthread_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]);
39 static int hwthread_smp_init(struct target *target);
40 int hwthread_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value);
41
42 #define HW_THREAD_NAME_STR_SIZE (32)
43
44 extern int rtos_thread_packet(struct connection *connection, const char *packet, int packet_size);
45
46 static inline threadid_t threadid_from_target(const struct target *target)
47 {
48         return target->coreid + 1;
49 }
50
51 const struct rtos_type hwthread_rtos = {
52         .name = "hwthread",
53         .detect_rtos = hwthread_detect_rtos,
54         .create = hwthread_create,
55         .update_threads = hwthread_update_threads,
56         .get_thread_reg_list = hwthread_get_thread_reg_list,
57         .get_thread_reg = hwthread_get_thread_reg,
58         .get_symbol_list_to_lookup = hwthread_get_symbol_list_to_lookup,
59         .smp_init = hwthread_smp_init,
60         .set_reg = hwthread_set_reg,
61 };
62
63 struct hwthread_params {
64         int dummy_param;
65 };
66
67 static int hwthread_fill_thread(struct rtos *rtos, struct target *curr, int thread_num)
68 {
69         char tmp_str[HW_THREAD_NAME_STR_SIZE];
70         threadid_t tid = threadid_from_target(curr);
71
72         memset(tmp_str, 0, HW_THREAD_NAME_STR_SIZE);
73
74         /* thread-id is the core-id of this core inside the SMP group plus 1 */
75         rtos->thread_details[thread_num].threadid = tid;
76         /* create the thread name */
77         rtos->thread_details[thread_num].exists = true;
78         rtos->thread_details[thread_num].thread_name_str = strdup(target_name(curr));
79         snprintf(tmp_str, HW_THREAD_NAME_STR_SIZE-1, "state: %s", debug_reason_name(curr));
80         rtos->thread_details[thread_num].extra_info_str = strdup(tmp_str);
81
82         return ERROR_OK;
83 }
84
85 static int hwthread_update_threads(struct rtos *rtos)
86 {
87         int threads_found = 0;
88         int thread_list_size = 0;
89         struct target_list *head;
90         struct target *target;
91         int64_t current_thread = 0;
92         enum target_debug_reason current_reason = DBG_REASON_UNDEFINED;
93
94         if (rtos == NULL)
95                 return -1;
96
97         target = rtos->target;
98
99         /* wipe out previous thread details if any */
100         rtos_free_threadlist(rtos);
101
102         /* determine the number of "threads" */
103         if (target->smp) {
104                 for (head = target->head; head != NULL; head = head->next) {
105                         struct target *curr = head->target;
106
107                         if (!target_was_examined(curr))
108                                 continue;
109
110                         ++thread_list_size;
111                 }
112         } else
113                 thread_list_size = 1;
114
115         /* create space for new thread details */
116         rtos->thread_details = malloc(sizeof(struct thread_detail) * thread_list_size);
117
118         if (target->smp) {
119                 /* loop over all threads */
120                 for (head = target->head; head != NULL; head = head->next) {
121                         struct target *curr = head->target;
122
123                         if (!target_was_examined(curr))
124                                 continue;
125
126                         threadid_t tid = threadid_from_target(curr);
127
128                         hwthread_fill_thread(rtos, curr, threads_found);
129
130                         /* find an interesting thread to set as current */
131                         switch (current_reason) {
132                         case DBG_REASON_UNDEFINED:
133                                 current_reason = curr->debug_reason;
134                                 current_thread = tid;
135                                 break;
136                         case DBG_REASON_SINGLESTEP:
137                                 /* single-step can only be overridden by itself */
138                                 if (curr->debug_reason == DBG_REASON_SINGLESTEP) {
139                                         if (tid == rtos->current_threadid)
140                                                 current_thread = tid;
141                                 }
142                                 break;
143                         case DBG_REASON_BREAKPOINT:
144                                 /* single-step overrides breakpoint */
145                                 if (curr->debug_reason == DBG_REASON_SINGLESTEP) {
146                                         current_reason = curr->debug_reason;
147                                         current_thread = tid;
148                                 } else
149                                 /* multiple breakpoints, prefer gdbs' threadid */
150                                 if (curr->debug_reason == DBG_REASON_BREAKPOINT) {
151                                         if (tid == rtos->current_threadid)
152                                                 current_thread = tid;
153                                 }
154                                 break;
155                         case DBG_REASON_WATCHPOINT:
156                                 /* breakpoint and single-step override watchpoint */
157                                 if (curr->debug_reason == DBG_REASON_SINGLESTEP ||
158                                                 curr->debug_reason == DBG_REASON_BREAKPOINT) {
159                                         current_reason = curr->debug_reason;
160                                         current_thread = tid;
161                                 }
162                                 break;
163                         case DBG_REASON_DBGRQ:
164                                 /* all other reasons override debug-request */
165                                 if (curr->debug_reason == DBG_REASON_SINGLESTEP ||
166                                                 curr->debug_reason == DBG_REASON_WATCHPOINT ||
167                                                 curr->debug_reason == DBG_REASON_BREAKPOINT) {
168                                         current_reason = curr->debug_reason;
169                                         current_thread = tid;
170                                 } else
171                                 if (curr->debug_reason == DBG_REASON_DBGRQ) {
172                                         if (tid == rtos->current_threadid)
173                                                 current_thread = tid;
174                                 }
175
176                                 break;
177
178                         default:
179                                 break;
180                         }
181
182                         threads_found++;
183                 }
184         } else {
185                 hwthread_fill_thread(rtos, target, threads_found);
186                 current_thread = threadid_from_target(target);
187                 threads_found++;
188         }
189
190         rtos->thread_count = threads_found;
191
192         /* we found an interesting thread, set it as current */
193         if (current_thread != 0)
194                 rtos->current_thread = current_thread;
195         else if (rtos->current_threadid != 0)
196                 rtos->current_thread = rtos->current_threadid;
197         else
198                 rtos->current_thread = threadid_from_target(target);
199
200         LOG_DEBUG("%s current_thread=%i", __func__, (int)rtos->current_thread);
201         return 0;
202 }
203
204 static int hwthread_smp_init(struct target *target)
205 {
206         return hwthread_update_threads(target->rtos);
207 }
208
209 static struct target *hwthread_find_thread(struct target *target, int64_t thread_id)
210 {
211         /* Find the thread with that thread_id */
212         if (target == NULL)
213                 return NULL;
214         if (target->smp) {
215                 for (struct target_list *head = target->head; head != NULL; head = head->next) {
216                         if (thread_id == threadid_from_target(head->target))
217                                 return head->target;
218                 }
219         } else if (thread_id == threadid_from_target(target)) {
220                 return target;
221         }
222         return NULL;
223 }
224
225 static int hwthread_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
226                 struct rtos_reg **rtos_reg_list, int *rtos_reg_list_size)
227 {
228         if (rtos == NULL)
229                 return ERROR_FAIL;
230
231         struct target *target = rtos->target;
232
233         struct target *curr = hwthread_find_thread(target, thread_id);
234         if (curr == NULL)
235                 return ERROR_FAIL;
236
237         if (!target_was_examined(curr))
238                 return ERROR_FAIL;
239
240         struct reg **reg_list;
241         int retval = target_get_gdb_reg_list(curr, &reg_list, rtos_reg_list_size,
242                         REG_CLASS_GENERAL);
243         if (retval != ERROR_OK)
244                 return retval;
245
246         *rtos_reg_list = calloc(*rtos_reg_list_size, sizeof(struct rtos_reg));
247         if (*rtos_reg_list == NULL) {
248                 free(reg_list);
249                 return ERROR_FAIL;
250         }
251
252         for (int i = 0; i < *rtos_reg_list_size; i++) {
253                 (*rtos_reg_list)[i].number = (*reg_list)[i].number;
254                 (*rtos_reg_list)[i].size = (*reg_list)[i].size;
255                 memcpy((*rtos_reg_list)[i].value, (*reg_list)[i].value,
256                                 ((*reg_list)[i].size + 7) / 8);
257         }
258         free(reg_list);
259
260         return ERROR_OK;
261 }
262
263 static int hwthread_get_thread_reg(struct rtos *rtos, int64_t thread_id,
264                 uint32_t reg_num, struct rtos_reg *rtos_reg)
265 {
266         if (rtos == NULL)
267                 return ERROR_FAIL;
268
269         struct target *target = rtos->target;
270
271         struct target *curr = hwthread_find_thread(target, thread_id);
272         if (curr == NULL) {
273                 LOG_ERROR("Couldn't find RTOS thread for id %" PRId64 ".", thread_id);
274                 return ERROR_FAIL;
275         }
276
277         if (!target_was_examined(curr)) {
278                 LOG_ERROR("Target %d hasn't been examined yet.", curr->coreid);
279                 return ERROR_FAIL;
280         }
281
282         struct reg *reg = register_get_by_number(curr->reg_cache, reg_num, true);
283         if (!reg) {
284                 LOG_ERROR("Couldn't find register %d in thread %" PRId64 ".", reg_num,
285                                 thread_id);
286                 return ERROR_FAIL;
287         }
288
289         if (reg->type->get(reg) != ERROR_OK)
290                 return ERROR_FAIL;
291
292         rtos_reg->number = reg->number;
293         rtos_reg->size = reg->size;
294         unsigned bytes = (reg->size + 7) / 8;
295         assert(bytes <= sizeof(rtos_reg->value));
296         memcpy(rtos_reg->value, reg->value, bytes);
297
298         return ERROR_OK;
299 }
300
301 int hwthread_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value)
302 {
303         if (rtos == NULL)
304                 return ERROR_FAIL;
305
306         struct target *target = rtos->target;
307
308         struct target *curr = hwthread_find_thread(target, rtos->current_thread);
309         if (curr == NULL)
310                 return ERROR_FAIL;
311
312         struct reg *reg = register_get_by_number(curr->reg_cache, reg_num, true);
313         if (!reg)
314                 return ERROR_FAIL;
315
316         return reg->type->set(reg, reg_value);
317 }
318
319 static int hwthread_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[])
320 {
321         /* return an empty list, we don't have any symbols to look up */
322         *symbol_list = calloc(1, sizeof(symbol_table_elem_t));
323         (*symbol_list)[0].symbol_name = NULL;
324         return 0;
325 }
326
327 static int hwthread_target_for_threadid(struct connection *connection, int64_t thread_id, struct target **p_target)
328 {
329         struct target *target = get_target_from_connection(connection);
330
331         struct target *curr = hwthread_find_thread(target, thread_id);
332         if (curr == NULL)
333                 return ERROR_FAIL;
334
335         *p_target = curr;
336
337         return ERROR_OK;
338 }
339
340 static bool hwthread_detect_rtos(struct target *target)
341 {
342         /* always return 0, avoid auto-detection */
343         return false;
344 }
345
346 static int hwthread_thread_packet(struct connection *connection, const char *packet, int packet_size)
347 {
348         struct target *target = get_target_from_connection(connection);
349
350         struct target *curr = NULL;
351         int64_t current_threadid;
352
353         if (packet[0] == 'H' && packet[1] == 'g') {
354                 sscanf(packet, "Hg%16" SCNx64, &current_threadid);
355
356                 if (current_threadid > 0) {
357                         if (hwthread_target_for_threadid(connection, current_threadid, &curr) != ERROR_OK) {
358                                 LOG_ERROR("hwthread: cannot find thread id %"PRId64, current_threadid);
359                                 gdb_put_packet(connection, "E01", 3);
360                                 return ERROR_FAIL;
361                         }
362                         target->rtos->current_thread = current_threadid;
363                 } else
364                 if (current_threadid == 0 || current_threadid == -1)
365                         target->rtos->current_thread = threadid_from_target(target);
366
367                 target->rtos->current_threadid = current_threadid;
368
369                 gdb_put_packet(connection, "OK", 2);
370                 return ERROR_OK;
371         }
372
373         return rtos_thread_packet(connection, packet, packet_size);
374 }
375
376 static int hwthread_create(struct target *target)
377 {
378         LOG_INFO("Hardware thread awareness created");
379
380         target->rtos->rtos_specific_params = NULL;
381         target->rtos->current_thread = 0;
382         target->rtos->thread_details = NULL;
383         target->rtos->gdb_target_for_threadid = hwthread_target_for_threadid;
384         target->rtos->gdb_thread_packet = hwthread_thread_packet;
385         return 0;
386 }