breakpoints: Fix endless loop in bp/wp_clear_target
[openocd.git] / 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 enum breakpoint_watchpoint {
21 BREAKPOINT,
22 WATCHPOINT,
23 };
24
25 static const char * const breakpoint_type_strings[] = {
26 "hardware",
27 "software"
28 };
29
30 static const char * const watchpoint_rw_strings[] = {
31 "read",
32 "write",
33 "access"
34 };
35
36 /* monotonic counter/id-number for breakpoints and watch points */
37 static int bpwp_unique_id;
38
39 static int breakpoint_add_internal(struct target *target,
40 target_addr_t address,
41 uint32_t length,
42 enum breakpoint_type type)
43 {
44 struct breakpoint *breakpoint = target->breakpoints;
45 struct breakpoint **breakpoint_p = &target->breakpoints;
46 const char *reason;
47 int retval;
48
49 while (breakpoint) {
50 if (breakpoint->address == address) {
51 /* FIXME don't assume "same address" means "same
52 * breakpoint" ... check all the parameters before
53 * succeeding.
54 */
55 LOG_ERROR("Duplicate Breakpoint address: " TARGET_ADDR_FMT " (BP %" PRIu32 ")",
56 address, breakpoint->unique_id);
57 return ERROR_TARGET_DUPLICATE_BREAKPOINT;
58 }
59 breakpoint_p = &breakpoint->next;
60 breakpoint = breakpoint->next;
61 }
62
63 (*breakpoint_p) = malloc(sizeof(struct breakpoint));
64 (*breakpoint_p)->address = address;
65 (*breakpoint_p)->asid = 0;
66 (*breakpoint_p)->length = length;
67 (*breakpoint_p)->type = type;
68 (*breakpoint_p)->is_set = false;
69 (*breakpoint_p)->orig_instr = malloc(length);
70 (*breakpoint_p)->next = NULL;
71 (*breakpoint_p)->unique_id = bpwp_unique_id++;
72
73 retval = target_add_breakpoint(target, *breakpoint_p);
74 switch (retval) {
75 case ERROR_OK:
76 break;
77 case ERROR_TARGET_RESOURCE_NOT_AVAILABLE:
78 reason = "resource not available";
79 goto fail;
80 case ERROR_TARGET_NOT_HALTED:
81 reason = "target running";
82 goto fail;
83 default:
84 reason = "unknown reason";
85 fail:
86 LOG_ERROR("can't add breakpoint: %s", reason);
87 free((*breakpoint_p)->orig_instr);
88 free(*breakpoint_p);
89 *breakpoint_p = NULL;
90 return retval;
91 }
92
93 LOG_DEBUG("[%d] added %s breakpoint at " TARGET_ADDR_FMT
94 " of length 0x%8.8x, (BPID: %" PRIu32 ")",
95 target->coreid,
96 breakpoint_type_strings[(*breakpoint_p)->type],
97 (*breakpoint_p)->address, (*breakpoint_p)->length,
98 (*breakpoint_p)->unique_id);
99
100 return ERROR_OK;
101 }
102
103 static int context_breakpoint_add_internal(struct target *target,
104 uint32_t asid,
105 uint32_t length,
106 enum breakpoint_type type)
107 {
108 struct breakpoint *breakpoint = target->breakpoints;
109 struct breakpoint **breakpoint_p = &target->breakpoints;
110 int retval;
111
112 while (breakpoint) {
113 if (breakpoint->asid == asid) {
114 /* FIXME don't assume "same address" means "same
115 * breakpoint" ... check all the parameters before
116 * succeeding.
117 */
118 LOG_ERROR("Duplicate Breakpoint asid: 0x%08" PRIx32 " (BP %" PRIu32 ")",
119 asid, breakpoint->unique_id);
120 return ERROR_TARGET_DUPLICATE_BREAKPOINT;
121 }
122 breakpoint_p = &breakpoint->next;
123 breakpoint = breakpoint->next;
124 }
125
126 (*breakpoint_p) = malloc(sizeof(struct breakpoint));
127 (*breakpoint_p)->address = 0;
128 (*breakpoint_p)->asid = asid;
129 (*breakpoint_p)->length = length;
130 (*breakpoint_p)->type = type;
131 (*breakpoint_p)->is_set = false;
132 (*breakpoint_p)->orig_instr = malloc(length);
133 (*breakpoint_p)->next = NULL;
134 (*breakpoint_p)->unique_id = bpwp_unique_id++;
135 retval = target_add_context_breakpoint(target, *breakpoint_p);
136 if (retval != ERROR_OK) {
137 LOG_ERROR("could not add breakpoint");
138 free((*breakpoint_p)->orig_instr);
139 free(*breakpoint_p);
140 *breakpoint_p = NULL;
141 return retval;
142 }
143
144 LOG_DEBUG("added %s Context breakpoint at 0x%8.8" PRIx32 " of length 0x%8.8x, (BPID: %" PRIu32 ")",
145 breakpoint_type_strings[(*breakpoint_p)->type],
146 (*breakpoint_p)->asid, (*breakpoint_p)->length,
147 (*breakpoint_p)->unique_id);
148
149 return ERROR_OK;
150 }
151
152 static int hybrid_breakpoint_add_internal(struct target *target,
153 target_addr_t address,
154 uint32_t asid,
155 uint32_t length,
156 enum breakpoint_type type)
157 {
158 struct breakpoint *breakpoint = target->breakpoints;
159 struct breakpoint **breakpoint_p = &target->breakpoints;
160 int retval;
161
162 while (breakpoint) {
163 if ((breakpoint->asid == asid) && (breakpoint->address == address)) {
164 /* FIXME don't assume "same address" means "same
165 * breakpoint" ... check all the parameters before
166 * succeeding.
167 */
168 LOG_ERROR("Duplicate Hybrid Breakpoint asid: 0x%08" PRIx32 " (BP %" PRIu32 ")",
169 asid, breakpoint->unique_id);
170 return ERROR_TARGET_DUPLICATE_BREAKPOINT;
171 } else if ((breakpoint->address == address) && (breakpoint->asid == 0)) {
172 LOG_ERROR("Duplicate Breakpoint IVA: " TARGET_ADDR_FMT " (BP %" PRIu32 ")",
173 address, breakpoint->unique_id);
174 return ERROR_TARGET_DUPLICATE_BREAKPOINT;
175
176 }
177 breakpoint_p = &breakpoint->next;
178 breakpoint = breakpoint->next;
179 }
180 (*breakpoint_p) = malloc(sizeof(struct breakpoint));
181 (*breakpoint_p)->address = address;
182 (*breakpoint_p)->asid = asid;
183 (*breakpoint_p)->length = length;
184 (*breakpoint_p)->type = type;
185 (*breakpoint_p)->is_set = false;
186 (*breakpoint_p)->orig_instr = malloc(length);
187 (*breakpoint_p)->next = NULL;
188 (*breakpoint_p)->unique_id = bpwp_unique_id++;
189
190
191 retval = target_add_hybrid_breakpoint(target, *breakpoint_p);
192 if (retval != ERROR_OK) {
193 LOG_ERROR("could not add breakpoint");
194 free((*breakpoint_p)->orig_instr);
195 free(*breakpoint_p);
196 *breakpoint_p = NULL;
197 return retval;
198 }
199 LOG_DEBUG(
200 "added %s Hybrid breakpoint at address " TARGET_ADDR_FMT " of length 0x%8.8x, (BPID: %" PRIu32 ")",
201 breakpoint_type_strings[(*breakpoint_p)->type],
202 (*breakpoint_p)->address,
203 (*breakpoint_p)->length,
204 (*breakpoint_p)->unique_id);
205
206 return ERROR_OK;
207 }
208
209 int breakpoint_add(struct target *target,
210 target_addr_t address,
211 uint32_t length,
212 enum breakpoint_type type)
213 {
214 if (target->smp) {
215 struct target_list *head;
216
217 if (type == BKPT_SOFT) {
218 head = list_first_entry(target->smp_targets, struct target_list, lh);
219 return breakpoint_add_internal(head->target, address, length, type);
220 }
221
222 foreach_smp_target(head, target->smp_targets) {
223 struct target *curr = head->target;
224 int retval = breakpoint_add_internal(curr, address, length, type);
225 if (retval != ERROR_OK)
226 return retval;
227 }
228
229 return ERROR_OK;
230 } else {
231 return breakpoint_add_internal(target, address, length, type);
232 }
233 }
234
235 int context_breakpoint_add(struct target *target,
236 uint32_t asid,
237 uint32_t length,
238 enum breakpoint_type type)
239 {
240 if (target->smp) {
241 struct target_list *head;
242
243 foreach_smp_target(head, target->smp_targets) {
244 struct target *curr = head->target;
245 int retval = context_breakpoint_add_internal(curr, asid, length, type);
246 if (retval != ERROR_OK)
247 return retval;
248 }
249
250 return ERROR_OK;
251 } else {
252 return context_breakpoint_add_internal(target, asid, length, type);
253 }
254 }
255
256 int hybrid_breakpoint_add(struct target *target,
257 target_addr_t address,
258 uint32_t asid,
259 uint32_t length,
260 enum breakpoint_type type)
261 {
262 if (target->smp) {
263 struct target_list *head;
264
265 foreach_smp_target(head, target->smp_targets) {
266 struct target *curr = head->target;
267 int retval = hybrid_breakpoint_add_internal(curr, address, asid, length, type);
268 if (retval != ERROR_OK)
269 return retval;
270 }
271
272 return ERROR_OK;
273 } else
274 return hybrid_breakpoint_add_internal(target, address, asid, length, type);
275 }
276
277 /* free up a breakpoint */
278 static int breakpoint_free(struct target *target, struct breakpoint *breakpoint_to_remove)
279 {
280 struct breakpoint *breakpoint = target->breakpoints;
281 struct breakpoint **breakpoint_p = &target->breakpoints;
282 int retval;
283
284 while (breakpoint) {
285 if (breakpoint == breakpoint_to_remove)
286 break;
287 breakpoint_p = &breakpoint->next;
288 breakpoint = breakpoint->next;
289 }
290
291 if (!breakpoint)
292 return ERROR_OK;
293
294 retval = target_remove_breakpoint(target, breakpoint);
295 if (retval != ERROR_OK) {
296 LOG_TARGET_ERROR(target, "could not remove breakpoint #%d on this target",
297 breakpoint->number);
298 return retval;
299 }
300
301 LOG_DEBUG("free BPID: %" PRIu32 " --> %d", breakpoint->unique_id, retval);
302 (*breakpoint_p) = breakpoint->next;
303 free(breakpoint->orig_instr);
304 free(breakpoint);
305
306 return ERROR_OK;
307 }
308
309 static int breakpoint_remove_internal(struct target *target, target_addr_t address)
310 {
311 struct breakpoint *breakpoint = target->breakpoints;
312
313 while (breakpoint) {
314 if ((breakpoint->address == address) ||
315 (breakpoint->address == 0 && breakpoint->asid == address))
316 break;
317 breakpoint = breakpoint->next;
318 }
319
320 if (breakpoint) {
321 return breakpoint_free(target, breakpoint);
322 } else {
323 return ERROR_BREAKPOINT_NOT_FOUND;
324 }
325 }
326
327 static int breakpoint_remove_all_internal(struct target *target)
328 {
329 LOG_TARGET_DEBUG(target, "Delete all breakpoints");
330
331 struct breakpoint *breakpoint = target->breakpoints;
332 int retval = ERROR_OK;
333
334 while (breakpoint) {
335 struct breakpoint *tmp = breakpoint;
336 breakpoint = breakpoint->next;
337 int status = breakpoint_free(target, tmp);
338 if (status != ERROR_OK)
339 retval = status;
340 }
341
342 return retval;
343 }
344
345 int breakpoint_remove(struct target *target, target_addr_t address)
346 {
347 int retval = ERROR_OK;
348 unsigned int num_found_breakpoints = 0;
349 if (target->smp) {
350 struct target_list *head;
351
352 foreach_smp_target(head, target->smp_targets) {
353 struct target *curr = head->target;
354 int status = breakpoint_remove_internal(curr, address);
355
356 if (status != ERROR_BREAKPOINT_NOT_FOUND) {
357 num_found_breakpoints++;
358
359 if (status != ERROR_OK) {
360 LOG_TARGET_ERROR(curr, "failed to remove breakpoint at address " TARGET_ADDR_FMT, address);
361 retval = status;
362 }
363 }
364 }
365
366 } else {
367 retval = breakpoint_remove_internal(target, address);
368
369 if (retval != ERROR_BREAKPOINT_NOT_FOUND) {
370 num_found_breakpoints++;
371
372 if (retval != ERROR_OK)
373 LOG_TARGET_ERROR(target, "failed to remove breakpoint at address " TARGET_ADDR_FMT, address);
374 }
375 }
376
377 if (num_found_breakpoints == 0) {
378 LOG_TARGET_ERROR(target, "no breakpoint at address " TARGET_ADDR_FMT " found", address);
379 return ERROR_BREAKPOINT_NOT_FOUND;
380 }
381
382 return retval;
383 }
384
385 static int watchpoint_free(struct target *target, struct watchpoint *watchpoint_to_remove)
386 {
387 struct watchpoint *watchpoint = target->watchpoints;
388 struct watchpoint **watchpoint_p = &target->watchpoints;
389 int retval;
390
391 while (watchpoint) {
392 if (watchpoint == watchpoint_to_remove)
393 break;
394 watchpoint_p = &watchpoint->next;
395 watchpoint = watchpoint->next;
396 }
397
398 if (!watchpoint)
399 return ERROR_OK;
400 retval = target_remove_watchpoint(target, watchpoint);
401 if (retval != ERROR_OK) {
402 LOG_TARGET_ERROR(target, "could not remove watchpoint #%d on this target",
403 watchpoint->number);
404 return retval;
405 }
406
407 LOG_DEBUG("free WPID: %d --> %d", watchpoint->unique_id, retval);
408 (*watchpoint_p) = watchpoint->next;
409 free(watchpoint);
410
411 return ERROR_OK;
412 }
413
414 static int watchpoint_remove_all_internal(struct target *target)
415 {
416 struct watchpoint *watchpoint = target->watchpoints;
417 int retval = ERROR_OK;
418
419 while (watchpoint) {
420 struct watchpoint *tmp = watchpoint;
421 watchpoint = watchpoint->next;
422 int status = watchpoint_free(target, tmp);
423 if (status != ERROR_OK)
424 retval = status;
425 }
426
427 return retval;
428 }
429
430 static int breakpoint_watchpoint_remove_all(struct target *target, enum breakpoint_watchpoint bp_wp)
431 {
432 assert(bp_wp == BREAKPOINT || bp_wp == WATCHPOINT);
433 int retval = ERROR_OK;
434 if (target->smp) {
435 struct target_list *head;
436
437 foreach_smp_target(head, target->smp_targets) {
438 struct target *curr = head->target;
439
440 int status = ERROR_OK;
441 if (bp_wp == BREAKPOINT)
442 status = breakpoint_remove_all_internal(curr);
443 else
444 status = watchpoint_remove_all_internal(curr);
445
446 if (status != ERROR_OK)
447 retval = status;
448 }
449 } else {
450 if (bp_wp == BREAKPOINT)
451 retval = breakpoint_remove_all_internal(target);
452 else
453 retval = watchpoint_remove_all_internal(target);
454 }
455
456 return retval;
457 }
458
459 int breakpoint_remove_all(struct target *target)
460 {
461 return breakpoint_watchpoint_remove_all(target, BREAKPOINT);
462 }
463
464 int watchpoint_remove_all(struct target *target)
465 {
466 return breakpoint_watchpoint_remove_all(target, WATCHPOINT);
467 }
468
469 int breakpoint_clear_target(struct target *target)
470 {
471 int retval = ERROR_OK;
472
473 if (target->smp) {
474 struct target_list *head;
475
476 foreach_smp_target(head, target->smp_targets) {
477 struct target *curr = head->target;
478 int status = breakpoint_remove_all_internal(curr);
479
480 if (status != ERROR_OK)
481 retval = status;
482 }
483 } else {
484 retval = breakpoint_remove_all_internal(target);
485 }
486
487 return retval;
488 }
489
490 struct breakpoint *breakpoint_find(struct target *target, target_addr_t address)
491 {
492 struct breakpoint *breakpoint = target->breakpoints;
493
494 while (breakpoint) {
495 if (breakpoint->address == address)
496 return breakpoint;
497 breakpoint = breakpoint->next;
498 }
499
500 return NULL;
501 }
502
503 static int watchpoint_add_internal(struct target *target, target_addr_t address,
504 uint32_t length, enum watchpoint_rw rw, uint64_t value, uint64_t mask)
505 {
506 struct watchpoint *watchpoint = target->watchpoints;
507 struct watchpoint **watchpoint_p = &target->watchpoints;
508 int retval;
509 const char *reason;
510
511 while (watchpoint) {
512 if (watchpoint->address == address) {
513 if (watchpoint->length != length
514 || watchpoint->value != value
515 || watchpoint->mask != mask
516 || watchpoint->rw != rw) {
517 LOG_ERROR("address " TARGET_ADDR_FMT
518 " already has watchpoint %d",
519 address, watchpoint->unique_id);
520 return ERROR_FAIL;
521 }
522
523 /* ignore duplicate watchpoint */
524 return ERROR_OK;
525 }
526 watchpoint_p = &watchpoint->next;
527 watchpoint = watchpoint->next;
528 }
529
530 (*watchpoint_p) = calloc(1, sizeof(struct watchpoint));
531 (*watchpoint_p)->address = address;
532 (*watchpoint_p)->length = length;
533 (*watchpoint_p)->value = value;
534 (*watchpoint_p)->mask = mask;
535 (*watchpoint_p)->rw = rw;
536 (*watchpoint_p)->unique_id = bpwp_unique_id++;
537
538 retval = target_add_watchpoint(target, *watchpoint_p);
539 switch (retval) {
540 case ERROR_OK:
541 break;
542 case ERROR_TARGET_RESOURCE_NOT_AVAILABLE:
543 reason = "resource not available";
544 goto bye;
545 case ERROR_TARGET_NOT_HALTED:
546 reason = "target running";
547 goto bye;
548 default:
549 reason = "unrecognized error";
550 bye:
551 LOG_ERROR("can't add %s watchpoint at " TARGET_ADDR_FMT ", %s",
552 watchpoint_rw_strings[(*watchpoint_p)->rw],
553 address, reason);
554 free(*watchpoint_p);
555 *watchpoint_p = NULL;
556 return retval;
557 }
558
559 LOG_DEBUG("[%d] added %s watchpoint at " TARGET_ADDR_FMT
560 " of length 0x%8.8" PRIx32 " (WPID: %d)",
561 target->coreid,
562 watchpoint_rw_strings[(*watchpoint_p)->rw],
563 (*watchpoint_p)->address,
564 (*watchpoint_p)->length,
565 (*watchpoint_p)->unique_id);
566
567 return ERROR_OK;
568 }
569
570 int watchpoint_add(struct target *target, target_addr_t address,
571 uint32_t length, enum watchpoint_rw rw, uint64_t value, uint64_t mask)
572 {
573 if (target->smp) {
574 struct target_list *head;
575
576 foreach_smp_target(head, target->smp_targets) {
577 struct target *curr = head->target;
578 int retval = watchpoint_add_internal(curr, address, length, rw, value, mask);
579 if (retval != ERROR_OK)
580 return retval;
581 }
582
583 return ERROR_OK;
584 } else {
585 return watchpoint_add_internal(target, address, length, rw, value,
586 mask);
587 }
588 }
589
590 static int watchpoint_remove_internal(struct target *target, target_addr_t address)
591 {
592 struct watchpoint *watchpoint = target->watchpoints;
593
594 while (watchpoint) {
595 if (watchpoint->address == address)
596 break;
597 watchpoint = watchpoint->next;
598 }
599
600 if (watchpoint) {
601 return watchpoint_free(target, watchpoint);
602 } else {
603 return ERROR_WATCHPOINT_NOT_FOUND;
604 }
605 }
606
607 int watchpoint_remove(struct target *target, target_addr_t address)
608 {
609 int retval = ERROR_OK;
610 unsigned int num_found_watchpoints = 0;
611 if (target->smp) {
612 struct target_list *head;
613
614 foreach_smp_target(head, target->smp_targets) {
615 struct target *curr = head->target;
616 int status = watchpoint_remove_internal(curr, address);
617
618 if (status != ERROR_WATCHPOINT_NOT_FOUND) {
619 num_found_watchpoints++;
620
621 if (status != ERROR_OK) {
622 LOG_TARGET_ERROR(curr, "failed to remove watchpoint at address " TARGET_ADDR_FMT, address);
623 retval = status;
624 }
625 }
626 }
627 } else {
628 retval = watchpoint_remove_internal(target, address);
629
630 if (retval != ERROR_WATCHPOINT_NOT_FOUND) {
631 num_found_watchpoints++;
632
633 if (retval != ERROR_OK)
634 LOG_TARGET_ERROR(target, "failed to remove watchpoint at address " TARGET_ADDR_FMT, address);
635 }
636 }
637
638 if (num_found_watchpoints == 0) {
639 LOG_TARGET_ERROR(target, "no watchpoint at address " TARGET_ADDR_FMT " found", address);
640 return ERROR_WATCHPOINT_NOT_FOUND;
641 }
642
643 return retval;
644 }
645
646 int watchpoint_clear_target(struct target *target)
647 {
648 LOG_DEBUG("Delete all watchpoints for target: %s",
649 target_name(target));
650
651 struct watchpoint *watchpoint = target->watchpoints;
652 int retval = ERROR_OK;
653
654 while (watchpoint) {
655 struct watchpoint *tmp = watchpoint;
656 watchpoint = watchpoint->next;
657 int status = watchpoint_free(target, tmp);
658 if (status != ERROR_OK)
659 retval = status;
660 }
661 return retval;
662 }
663
664 int watchpoint_hit(struct target *target, enum watchpoint_rw *rw,
665 target_addr_t *address)
666 {
667 int retval;
668 struct watchpoint *hit_watchpoint;
669
670 retval = target_hit_watchpoint(target, &hit_watchpoint);
671 if (retval != ERROR_OK)
672 return ERROR_FAIL;
673
674 *rw = hit_watchpoint->rw;
675 *address = hit_watchpoint->address;
676
677 LOG_DEBUG("Found hit watchpoint at " TARGET_ADDR_FMT " (WPID: %d)",
678 hit_watchpoint->address,
679 hit_watchpoint->unique_id);
680
681 return ERROR_OK;
682 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)