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