target: make local symbols static
[fw/openocd] / src / target / breakpoints.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 /***************************************************************************
4  *   Copyright (C) 2005 by Dominic Rath                                    *
5  *   Dominic.Rath@gmx.de                                                   *
6  *                                                                         *
7  *   Copyright (C) ST-Ericsson SA 2011                                     *
8  *   michel.jaouen@stericsson.com : smp minimum support                    *
9  ***************************************************************************/
10
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif
14
15 #include "target.h"
16 #include <helper/log.h>
17 #include "breakpoints.h"
18 #include "smp.h"
19
20 static const char * const breakpoint_type_strings[] = {
21         "hardware",
22         "software"
23 };
24
25 static const char * const watchpoint_rw_strings[] = {
26         "read",
27         "write",
28         "access"
29 };
30
31 /* monotonic counter/id-number for breakpoints and watch points */
32 static int bpwp_unique_id;
33
34 static int breakpoint_add_internal(struct target *target,
35         target_addr_t address,
36         uint32_t length,
37         enum breakpoint_type type)
38 {
39         struct breakpoint *breakpoint = target->breakpoints;
40         struct breakpoint **breakpoint_p = &target->breakpoints;
41         const char *reason;
42         int retval;
43
44         while (breakpoint) {
45                 if (breakpoint->address == address) {
46                         /* FIXME don't assume "same address" means "same
47                          * breakpoint" ... check all the parameters before
48                          * succeeding.
49                          */
50                         LOG_ERROR("Duplicate Breakpoint address: " TARGET_ADDR_FMT " (BP %" PRIu32 ")",
51                                 address, breakpoint->unique_id);
52                         return ERROR_TARGET_DUPLICATE_BREAKPOINT;
53                 }
54                 breakpoint_p = &breakpoint->next;
55                 breakpoint = breakpoint->next;
56         }
57
58         (*breakpoint_p) = malloc(sizeof(struct breakpoint));
59         (*breakpoint_p)->address = address;
60         (*breakpoint_p)->asid = 0;
61         (*breakpoint_p)->length = length;
62         (*breakpoint_p)->type = type;
63         (*breakpoint_p)->is_set = false;
64         (*breakpoint_p)->orig_instr = malloc(length);
65         (*breakpoint_p)->next = NULL;
66         (*breakpoint_p)->unique_id = bpwp_unique_id++;
67
68         retval = target_add_breakpoint(target, *breakpoint_p);
69         switch (retval) {
70                 case ERROR_OK:
71                         break;
72                 case ERROR_TARGET_RESOURCE_NOT_AVAILABLE:
73                         reason = "resource not available";
74                         goto fail;
75                 case ERROR_TARGET_NOT_HALTED:
76                         reason = "target running";
77                         goto fail;
78                 default:
79                         reason = "unknown reason";
80 fail:
81                         LOG_ERROR("can't add breakpoint: %s", reason);
82                         free((*breakpoint_p)->orig_instr);
83                         free(*breakpoint_p);
84                         *breakpoint_p = NULL;
85                         return retval;
86         }
87
88         LOG_DEBUG("[%d] added %s breakpoint at " TARGET_ADDR_FMT
89                         " of length 0x%8.8x, (BPID: %" PRIu32 ")",
90                 target->coreid,
91                 breakpoint_type_strings[(*breakpoint_p)->type],
92                 (*breakpoint_p)->address, (*breakpoint_p)->length,
93                 (*breakpoint_p)->unique_id);
94
95         return ERROR_OK;
96 }
97
98 static int context_breakpoint_add_internal(struct target *target,
99         uint32_t asid,
100         uint32_t length,
101         enum breakpoint_type type)
102 {
103         struct breakpoint *breakpoint = target->breakpoints;
104         struct breakpoint **breakpoint_p = &target->breakpoints;
105         int retval;
106
107         while (breakpoint) {
108                 if (breakpoint->asid == asid) {
109                         /* FIXME don't assume "same address" means "same
110                          * breakpoint" ... check all the parameters before
111                          * succeeding.
112                          */
113                         LOG_ERROR("Duplicate Breakpoint asid: 0x%08" PRIx32 " (BP %" PRIu32 ")",
114                                 asid, breakpoint->unique_id);
115                         return ERROR_TARGET_DUPLICATE_BREAKPOINT;
116                 }
117                 breakpoint_p = &breakpoint->next;
118                 breakpoint = breakpoint->next;
119         }
120
121         (*breakpoint_p) = malloc(sizeof(struct breakpoint));
122         (*breakpoint_p)->address = 0;
123         (*breakpoint_p)->asid = asid;
124         (*breakpoint_p)->length = length;
125         (*breakpoint_p)->type = type;
126         (*breakpoint_p)->is_set = false;
127         (*breakpoint_p)->orig_instr = malloc(length);
128         (*breakpoint_p)->next = NULL;
129         (*breakpoint_p)->unique_id = bpwp_unique_id++;
130         retval = target_add_context_breakpoint(target, *breakpoint_p);
131         if (retval != ERROR_OK) {
132                 LOG_ERROR("could not add breakpoint");
133                 free((*breakpoint_p)->orig_instr);
134                 free(*breakpoint_p);
135                 *breakpoint_p = NULL;
136                 return retval;
137         }
138
139         LOG_DEBUG("added %s Context breakpoint at 0x%8.8" PRIx32 " of length 0x%8.8x, (BPID: %" PRIu32 ")",
140                 breakpoint_type_strings[(*breakpoint_p)->type],
141                 (*breakpoint_p)->asid, (*breakpoint_p)->length,
142                 (*breakpoint_p)->unique_id);
143
144         return ERROR_OK;
145 }
146
147 static int hybrid_breakpoint_add_internal(struct target *target,
148         target_addr_t address,
149         uint32_t asid,
150         uint32_t length,
151         enum breakpoint_type type)
152 {
153         struct breakpoint *breakpoint = target->breakpoints;
154         struct breakpoint **breakpoint_p = &target->breakpoints;
155         int retval;
156
157         while (breakpoint) {
158                 if ((breakpoint->asid == asid) && (breakpoint->address == address)) {
159                         /* FIXME don't assume "same address" means "same
160                          * breakpoint" ... check all the parameters before
161                          * succeeding.
162                          */
163                         LOG_ERROR("Duplicate Hybrid Breakpoint asid: 0x%08" PRIx32 " (BP %" PRIu32 ")",
164                                 asid, breakpoint->unique_id);
165                         return ERROR_TARGET_DUPLICATE_BREAKPOINT;
166                 } else if ((breakpoint->address == address) && (breakpoint->asid == 0)) {
167                         LOG_ERROR("Duplicate Breakpoint IVA: " TARGET_ADDR_FMT " (BP %" PRIu32 ")",
168                                 address, breakpoint->unique_id);
169                         return ERROR_TARGET_DUPLICATE_BREAKPOINT;
170
171                 }
172                 breakpoint_p = &breakpoint->next;
173                 breakpoint = breakpoint->next;
174         }
175         (*breakpoint_p) = malloc(sizeof(struct breakpoint));
176         (*breakpoint_p)->address = address;
177         (*breakpoint_p)->asid = asid;
178         (*breakpoint_p)->length = length;
179         (*breakpoint_p)->type = type;
180         (*breakpoint_p)->is_set = false;
181         (*breakpoint_p)->orig_instr = malloc(length);
182         (*breakpoint_p)->next = NULL;
183         (*breakpoint_p)->unique_id = bpwp_unique_id++;
184
185
186         retval = target_add_hybrid_breakpoint(target, *breakpoint_p);
187         if (retval != ERROR_OK) {
188                 LOG_ERROR("could not add breakpoint");
189                 free((*breakpoint_p)->orig_instr);
190                 free(*breakpoint_p);
191                 *breakpoint_p = NULL;
192                 return retval;
193         }
194         LOG_DEBUG(
195                 "added %s Hybrid breakpoint at address " TARGET_ADDR_FMT " of length 0x%8.8x, (BPID: %" PRIu32 ")",
196                 breakpoint_type_strings[(*breakpoint_p)->type],
197                 (*breakpoint_p)->address,
198                 (*breakpoint_p)->length,
199                 (*breakpoint_p)->unique_id);
200
201         return ERROR_OK;
202 }
203
204 int breakpoint_add(struct target *target,
205         target_addr_t address,
206         uint32_t length,
207         enum breakpoint_type type)
208 {
209         if (target->smp) {
210                 struct target_list *head;
211
212                 if (type == BKPT_SOFT) {
213                         head = list_first_entry(target->smp_targets, struct target_list, lh);
214                         return breakpoint_add_internal(head->target, address, length, type);
215                 }
216
217                 foreach_smp_target(head, target->smp_targets) {
218                         struct target *curr = head->target;
219                         int retval = breakpoint_add_internal(curr, address, length, type);
220                         if (retval != ERROR_OK)
221                                 return retval;
222                 }
223
224                 return ERROR_OK;
225         } else {
226                 return breakpoint_add_internal(target, address, length, type);
227         }
228 }
229
230 int context_breakpoint_add(struct target *target,
231         uint32_t asid,
232         uint32_t length,
233         enum breakpoint_type type)
234 {
235         if (target->smp) {
236                 struct target_list *head;
237
238                 foreach_smp_target(head, target->smp_targets) {
239                         struct target *curr = head->target;
240                         int retval = context_breakpoint_add_internal(curr, asid, length, type);
241                         if (retval != ERROR_OK)
242                                 return retval;
243                 }
244
245                 return ERROR_OK;
246         } else {
247                 return context_breakpoint_add_internal(target, asid, length, type);
248         }
249 }
250
251 int hybrid_breakpoint_add(struct target *target,
252         target_addr_t address,
253         uint32_t asid,
254         uint32_t length,
255         enum breakpoint_type type)
256 {
257         if (target->smp) {
258                 struct target_list *head;
259
260                 foreach_smp_target(head, target->smp_targets) {
261                         struct target *curr = head->target;
262                         int retval = hybrid_breakpoint_add_internal(curr, address, asid, length, type);
263                         if (retval != ERROR_OK)
264                                 return retval;
265                 }
266
267                 return ERROR_OK;
268         } else
269                 return hybrid_breakpoint_add_internal(target, address, asid, length, type);
270 }
271
272 /* free up a breakpoint */
273 static void breakpoint_free(struct target *target, struct breakpoint *breakpoint_to_remove)
274 {
275         struct breakpoint *breakpoint = target->breakpoints;
276         struct breakpoint **breakpoint_p = &target->breakpoints;
277         int retval;
278
279         while (breakpoint) {
280                 if (breakpoint == breakpoint_to_remove)
281                         break;
282                 breakpoint_p = &breakpoint->next;
283                 breakpoint = breakpoint->next;
284         }
285
286         if (!breakpoint)
287                 return;
288
289         retval = target_remove_breakpoint(target, breakpoint);
290
291         LOG_DEBUG("free BPID: %" PRIu32 " --> %d", breakpoint->unique_id, retval);
292         (*breakpoint_p) = breakpoint->next;
293         free(breakpoint->orig_instr);
294         free(breakpoint);
295 }
296
297 static int breakpoint_remove_internal(struct target *target, target_addr_t address)
298 {
299         struct breakpoint *breakpoint = target->breakpoints;
300
301         while (breakpoint) {
302                 if ((breakpoint->address == address) ||
303                     (breakpoint->address == 0 && breakpoint->asid == address))
304                         break;
305                 breakpoint = breakpoint->next;
306         }
307
308         if (breakpoint) {
309                 breakpoint_free(target, breakpoint);
310                 return 1;
311         } else {
312                 if (!target->smp)
313                         LOG_ERROR("no breakpoint at address " TARGET_ADDR_FMT " found", address);
314                 return 0;
315         }
316 }
317
318 static void breakpoint_remove_all_internal(struct target *target)
319 {
320         struct breakpoint *breakpoint = target->breakpoints;
321
322         while (breakpoint) {
323                 struct breakpoint *tmp = breakpoint;
324                 breakpoint = breakpoint->next;
325                 breakpoint_free(target, tmp);
326         }
327 }
328
329 void breakpoint_remove(struct target *target, target_addr_t address)
330 {
331         if (target->smp) {
332                 unsigned int num_breakpoints = 0;
333                 struct target_list *head;
334
335                 foreach_smp_target(head, target->smp_targets) {
336                         struct target *curr = head->target;
337                         num_breakpoints += breakpoint_remove_internal(curr, address);
338                 }
339                 if (!num_breakpoints)
340                         LOG_ERROR("no breakpoint at address " TARGET_ADDR_FMT " found", address);
341         } else {
342                 breakpoint_remove_internal(target, address);
343         }
344 }
345
346 void breakpoint_remove_all(struct target *target)
347 {
348         if (target->smp) {
349                 struct target_list *head;
350
351                 foreach_smp_target(head, target->smp_targets) {
352                         struct target *curr = head->target;
353                         breakpoint_remove_all_internal(curr);
354                 }
355         } else {
356                 breakpoint_remove_all_internal(target);
357         }
358 }
359
360 static void breakpoint_clear_target_internal(struct target *target)
361 {
362         LOG_DEBUG("Delete all breakpoints for target: %s",
363                 target_name(target));
364         while (target->breakpoints)
365                 breakpoint_free(target, target->breakpoints);
366 }
367
368 void breakpoint_clear_target(struct target *target)
369 {
370         if (target->smp) {
371                 struct target_list *head;
372
373                 foreach_smp_target(head, target->smp_targets) {
374                         struct target *curr = head->target;
375                         breakpoint_clear_target_internal(curr);
376                 }
377         } else {
378                 breakpoint_clear_target_internal(target);
379         }
380 }
381
382 struct breakpoint *breakpoint_find(struct target *target, target_addr_t address)
383 {
384         struct breakpoint *breakpoint = target->breakpoints;
385
386         while (breakpoint) {
387                 if (breakpoint->address == address)
388                         return breakpoint;
389                 breakpoint = breakpoint->next;
390         }
391
392         return NULL;
393 }
394
395 static int watchpoint_add_internal(struct target *target, target_addr_t address,
396                 uint32_t length, enum watchpoint_rw rw, uint32_t value, uint32_t mask)
397 {
398         struct watchpoint *watchpoint = target->watchpoints;
399         struct watchpoint **watchpoint_p = &target->watchpoints;
400         int retval;
401         const char *reason;
402
403         while (watchpoint) {
404                 if (watchpoint->address == address) {
405                         if (watchpoint->length != length
406                                 || watchpoint->value != value
407                                 || watchpoint->mask != mask
408                                 || watchpoint->rw != rw) {
409                                 LOG_ERROR("address " TARGET_ADDR_FMT
410                                         " already has watchpoint %d",
411                                         address, watchpoint->unique_id);
412                                 return ERROR_FAIL;
413                         }
414
415                         /* ignore duplicate watchpoint */
416                         return ERROR_OK;
417                 }
418                 watchpoint_p = &watchpoint->next;
419                 watchpoint = watchpoint->next;
420         }
421
422         (*watchpoint_p) = calloc(1, sizeof(struct watchpoint));
423         (*watchpoint_p)->address = address;
424         (*watchpoint_p)->length = length;
425         (*watchpoint_p)->value = value;
426         (*watchpoint_p)->mask = mask;
427         (*watchpoint_p)->rw = rw;
428         (*watchpoint_p)->unique_id = bpwp_unique_id++;
429
430         retval = target_add_watchpoint(target, *watchpoint_p);
431         switch (retval) {
432                 case ERROR_OK:
433                         break;
434                 case ERROR_TARGET_RESOURCE_NOT_AVAILABLE:
435                         reason = "resource not available";
436                         goto bye;
437                 case ERROR_TARGET_NOT_HALTED:
438                         reason = "target running";
439                         goto bye;
440                 default:
441                         reason = "unrecognized error";
442 bye:
443                         LOG_ERROR("can't add %s watchpoint at " TARGET_ADDR_FMT ", %s",
444                                 watchpoint_rw_strings[(*watchpoint_p)->rw],
445                                 address, reason);
446                         free(*watchpoint_p);
447                         *watchpoint_p = NULL;
448                         return retval;
449         }
450
451         LOG_DEBUG("[%d] added %s watchpoint at " TARGET_ADDR_FMT
452                         " of length 0x%8.8" PRIx32 " (WPID: %d)",
453                 target->coreid,
454                 watchpoint_rw_strings[(*watchpoint_p)->rw],
455                 (*watchpoint_p)->address,
456                 (*watchpoint_p)->length,
457                 (*watchpoint_p)->unique_id);
458
459         return ERROR_OK;
460 }
461
462 int watchpoint_add(struct target *target, target_addr_t address,
463                 uint32_t length, enum watchpoint_rw rw, uint32_t value, uint32_t mask)
464 {
465         if (target->smp) {
466                 struct target_list *head;
467
468                 foreach_smp_target(head, target->smp_targets) {
469                         struct target *curr = head->target;
470                         int retval = watchpoint_add_internal(curr, address, length, rw, value, mask);
471                         if (retval != ERROR_OK)
472                                 return retval;
473                 }
474
475                 return ERROR_OK;
476         } else {
477                 return watchpoint_add_internal(target, address, length, rw, value,
478                                 mask);
479         }
480 }
481
482 static void watchpoint_free(struct target *target, struct watchpoint *watchpoint_to_remove)
483 {
484         struct watchpoint *watchpoint = target->watchpoints;
485         struct watchpoint **watchpoint_p = &target->watchpoints;
486         int retval;
487
488         while (watchpoint) {
489                 if (watchpoint == watchpoint_to_remove)
490                         break;
491                 watchpoint_p = &watchpoint->next;
492                 watchpoint = watchpoint->next;
493         }
494
495         if (!watchpoint)
496                 return;
497         retval = target_remove_watchpoint(target, watchpoint);
498         LOG_DEBUG("free WPID: %d --> %d", watchpoint->unique_id, retval);
499         (*watchpoint_p) = watchpoint->next;
500         free(watchpoint);
501 }
502
503 static int watchpoint_remove_internal(struct target *target, target_addr_t address)
504 {
505         struct watchpoint *watchpoint = target->watchpoints;
506
507         while (watchpoint) {
508                 if (watchpoint->address == address)
509                         break;
510                 watchpoint = watchpoint->next;
511         }
512
513         if (watchpoint) {
514                 watchpoint_free(target, watchpoint);
515                 return 1;
516         } else {
517                 if (!target->smp)
518                         LOG_ERROR("no watchpoint at address " TARGET_ADDR_FMT " found", address);
519                 return 0;
520         }
521 }
522
523 void watchpoint_remove(struct target *target, target_addr_t address)
524 {
525         if (target->smp) {
526                 unsigned int num_watchpoints = 0;
527                 struct target_list *head;
528
529                 foreach_smp_target(head, target->smp_targets) {
530                         struct target *curr = head->target;
531                         num_watchpoints += watchpoint_remove_internal(curr, address);
532                 }
533                 if (num_watchpoints == 0)
534                         LOG_ERROR("no watchpoint at address " TARGET_ADDR_FMT " num_watchpoints", address);
535         } else {
536                 watchpoint_remove_internal(target, address);
537         }
538 }
539
540 void watchpoint_clear_target(struct target *target)
541 {
542         LOG_DEBUG("Delete all watchpoints for target: %s",
543                 target_name(target));
544         while (target->watchpoints)
545                 watchpoint_free(target, target->watchpoints);
546 }
547
548 int watchpoint_hit(struct target *target, enum watchpoint_rw *rw,
549                    target_addr_t *address)
550 {
551         int retval;
552         struct watchpoint *hit_watchpoint;
553
554         retval = target_hit_watchpoint(target, &hit_watchpoint);
555         if (retval != ERROR_OK)
556                 return ERROR_FAIL;
557
558         *rw = hit_watchpoint->rw;
559         *address = hit_watchpoint->address;
560
561         LOG_DEBUG("Found hit watchpoint at " TARGET_ADDR_FMT " (WPID: %d)",
562                 hit_watchpoint->address,
563                 hit_watchpoint->unique_id);
564
565         return ERROR_OK;
566 }