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