Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / legion / src / simcore / breakpoint.c
CommitLineData
920dae64
AT
1/*
2* ========== Copyright Header Begin ==========================================
3*
4* OpenSPARC T2 Processor File: breakpoint.c
5* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
6* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
7*
8* The above named program is free software; you can redistribute it and/or
9* modify it under the terms of the GNU General Public
10* License version 2 as published by the Free Software Foundation.
11*
12* The above named program is distributed in the hope that it will be
13* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15* General Public License for more details.
16*
17* You should have received a copy of the GNU General Public
18* License along with this work; if not, write to the Free Software
19* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
20*
21* ========== Copyright Header End ============================================
22*/
23/*
24 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 */
27
28#pragma ident "@(#)breakpoint.c 1.11 07/05/30 SMI"
29
30#include <stdlib.h>
31#include <errno.h>
32#include <unistd.h>
33#include <alloca.h>
34
35#include <assert.h>
36
37#include "basics.h"
38#include "fatal.h"
39#include "allocate.h"
40#include "simcore.h"
41#include "config.h"
42#include "breakpoint.h"
43#include "xicache.h" /* for xicache_instn_flush */
44
45
46 /*
47 * Breakpoint info is a global structure.
48 * We set a pointer in the cpu structure to it
49 * to indicate that breakpoints are possible for that
50 * cpu. The only thread that can modify this structure
51 * and it's elements is the main run thread ...
52 * .... so we don't need to worry about locking it ....
53 * .... just have to be careful about adding and removing
54 * entries so as to not strand cpus in hyperspace....
55 */
56
57bp_info_t *
58breakpoint_init()
59{
60 int i;
61
62 bp_info_t *bip;
63
64 bip = Xcalloc(1, bp_info_t);
65
66 for (i = 0; i < BREAK_HASH_SIZE; i++) {
67 bip->hash[i] = NULL;
68 }
69
70 bip->free_listp = NULL;
71 bip->active_listp = NULL;
72 bip->active_count = 0;
73 bip->disabled_listp = NULL;
74 bip->disabled_count = 0;
75
76 bip->next_id = 0;
77
78 bip->do_step = false;
79
80 return (bip);
81}
82
83
84
85
86 /*
87 * Insert at top of hash ... recently
88 * added breakpoints should be found first ...
89 */
90
91static void
92bp_hash_insert(bp_info_t *bip, breakpoint_t *bp)
93{
94 int i;
95
96 assert(bp->enabled);
97 assert(bp->hash_nextp == (void*)0);
98 assert(bp->free_nextp == (void*)0);
99 assert(bp->disabled_nextp == (void*)0);
100
101 i = (bp->pc >> 2) & BREAK_HASH_MASK;
102
103 bp->hash_nextp = bip->hash[i];
104 bip->hash[i] = bp;
105}
106
107
108
109 /*
110 * only gets called for existing breakpoints
111 * which are being deleted or disabled
112 */
113static void
114bp_hash_unhook(bp_info_t *bip, breakpoint_t *bp)
115{
116 int i;
117 breakpoint_t **pbp, *bb;
118
119 assert(bp->enabled);
120 assert(bp->free_nextp == (void*)0);
121 assert(bp->disabled_nextp == (void*)0);
122
123 i = (bp->pc >> 2) & BREAK_HASH_MASK;
124
125 /* go find it on list */
126 for (pbp = &(bip->hash[i]); (bb = *pbp) != (void*)0;
127 pbp = &(bb->hash_nextp)) {
128 if (bb == bp) {
129 *pbp = bb->hash_nextp;
130 bb->hash_nextp = NULL; /* sanity */
131 return;
132 }
133 }
134 abort();
135}
136
137
138 /*
139 * just enabled a breakpoint, so clobber the
140 * cpus' xi-caches ... and other stuff ...
141 * this is pretty poor, and we must come up with a
142 * better way to get the cpus to exit their
143 * inner loops.... this may not always work FIXME
144 * ... talk about brute force - FIXME
145 */
146
147static void
148breaks_changed(bp_info_t *bip)
149{
150 int i;
151 for (i = 0; i < simcpu_list.count; i++) {
152 simcpu_t *sp;
153
154 sp = LIST_ENTRY(simcpu_list, i);
155
156 /* blow away decoded versions ... */
157 sp->xicache_instn_flush_pending = true;
158 set_sync_pending(sp);
159 }
160}
161
162 /*
163 * Insert a new breakpoint ...
164 */
165
166breakpoint_t *
167breakpoint_insert(bp_info_t *bip, tvaddr_t pc, int context)
168{
169 breakpoint_t *bp;
170
171/* CSTYLED */
172DBG( printf("breakpoint_insert: @ 0x%llx, ctxt=0x%x\n", pc, (int)context); );
173
174 if (bip->free_listp != NULL) {
175 bp = bip->free_listp;
176 bip->free_listp = bp->free_nextp;
177 bp->free_nextp = NULL;
178 } else {
179 /* use calloc to clear all pointer fields */
180 bp = (breakpoint_t *)calloc(1, sizeof (*bp));
181 if (bp == NULL) fatal("out of memory in breakpoint_insert");
182 }
183
184 bp->id = bip->next_id++;
185 bp->pc = pc;
186 bp->context = context;
187 bp->enabled = true;
188
189 /* add to active list */
190 bp->active_nextp = bip->active_listp;
191 bip->active_listp = bp;
192 bip->active_count ++;
193
194 /* must not be disabled */
195 assert(bp->disabled_nextp == NULL);
196
197 bp_hash_insert(bip, bp);
198
199 breaks_changed(bip);
200
201 return (bp);
202}
203
204void
205breakpoint_insert_next(bp_info_t *bip)
206{
207 bip->do_step = true;
208 bip->on_this = false;
209
210 breaks_changed(bip);
211}
212
213void
214breakpoint_clear_next(bp_info_t *bip)
215{
216 bip->do_step = false;
217
218 breaks_changed(bip);
219}
220
221breakpoint_t *
222breakpoint_find_by_addr(bp_info_t *bip, uint64_t addr, int context)
223{
224 breakpoint_t *bp;
225 int i;
226
227 /* search the bp hash */
228 i = (addr >> 2) & BREAK_HASH_MASK;
229
230 for (bp = bip->hash[i]; bp != NULL; bp = bp->hash_nextp) {
231 if (bp->pc == addr && bp->context == context)
232 return (bp);
233 }
234
235 return ((breakpoint_t *)0);
236}
237
238
239int
240breakpoint_delete_by_id(bp_info_t *bip, int id)
241{
242 breakpoint_t **pbp, *bp;
243
244 /* search first active list */
245
246 for (pbp = &(bip->active_listp); (bp = *pbp) != NULL;
247 pbp = &(bp->active_nextp)) {
248 assert(bp->enabled); /* sanity */
249 if (bp->id == id) {
250 /* hit on active list */
251 /* unhook from list and unhook from hash */
252 bp_hash_unhook(bip, bp);
253
254 *pbp = bp->active_nextp;
255 bip->active_count --;
256 bp->enabled = false;
257
258 goto delete_me;
259 }
260 }
261
262 /* then search disabled list */
263
264 for (pbp = &(bip->disabled_listp); (bp = *pbp) != NULL;
265 pbp = &(bp->disabled_nextp)) {
266 assert(!bp->enabled); /* sanity */
267 if (bp->id == id) {
268 /* hit on disabled list */
269 *pbp = bp->disabled_nextp;
270 bip->disabled_count --;
271
272 goto delete_me;
273 }
274 }
275
276 return (-1); /* no such id */
277
278delete_me:
279
280 /* just stuff back on free list */
281 bp->active_nextp = NULL;
282 bp->disabled_nextp = NULL;
283 assert(bp->free_nextp == NULL);
284 assert(bp->hash_nextp == NULL);
285
286 bp->free_nextp = bip->free_listp;
287 bip->free_listp = bp;
288
289 breaks_changed(bip);
290
291 return (0);
292}
293
294
295
296 /*
297 * Hardly efficient, but effective - FIXME
298 * Since don't often add or remove breakpoints
299 * hopefully this isn't too painful.
300 */
301
302int
303breakpoint_delete_by_addr(bp_info_t *bip, uint64_t addr, int context)
304{
305 bool_t flag_deleted = false;
306 breakpoint_t *bp;
307 while ((bp = breakpoint_find_by_addr(bip, addr, context)) \
308 != (breakpoint_t *)0) {
309 breakpoint_delete_by_id(bip, bp->id);
310 flag_deleted = true;
311 }
312
313 return (flag_deleted);
314}
315
316
317
318
319 /*
320 * Either the bp is enabled, disabled or doesn't exist !
321 */
322int
323breakpoint_disable_by_id(bp_info_t *bip, int id)
324{
325 breakpoint_t **pbp, *bp;
326
327 /* search first active list */
328
329 for (pbp = &(bip->active_listp); (bp = *pbp) != NULL;
330 pbp = &(bp->active_nextp)) {
331 assert(bp->enabled); /* sanity */
332 if (bp->id == id) {
333 /* hit on active list */
334 /* unhook from active list and unhook from hash */
335 bp_hash_unhook(bip, bp);
336
337 *pbp = bp->active_nextp;
338 bip->active_count --;
339 bp->enabled = false;
340
341 goto disable_me;
342 }
343 }
344
345 /* then search disabled list */
346
347 for (bp = bip->disabled_listp; bp != NULL; bp = bp->disabled_nextp) {
348 assert(!bp->enabled); /* sanity */
349 if (bp->id == id) {
350 /* hit on disabled list */
351 printf("Breakpoint %d is already disabled\n", bp->id);
352 return (0); /* job done */
353 }
354 }
355
356 printf("No breakpoint with id %d\n", bp->id);
357 return (-1); /* no such id */
358
359disable_me:
360 /* mark disabled */
361 bp->active_nextp = NULL;
362 assert(bp->disabled_nextp == NULL);
363 assert(bp->free_nextp == NULL);
364 assert(bp->hash_nextp == NULL);
365
366 bp->disabled_nextp = bip->disabled_listp;
367 bip->disabled_listp = bp;
368 bip->disabled_count ++;
369
370 breaks_changed(bip);
371
372 return (0);
373}
374
375
376
377int
378breakpoint_enable_by_id(bp_info_t *bip, int id)
379{
380 breakpoint_t **pbp, *bp;
381
382 /* make sure not already enabled */
383 for (bp = bip->active_listp; bp != NULL; bp = bp->active_nextp) {
384 assert(bp->enabled); /* sanity */
385 if (bp->id == id) {
386 printf("Breakpoint %d is already enabled\n", bp->id);
387 return (0); /* job done */
388 }
389 }
390
391 /* then search disabled list */
392 for (pbp = &(bip->disabled_listp); (bp = *pbp) != NULL;
393 pbp = &(bp->disabled_nextp)) {
394 assert(!bp->enabled); /* sanity */
395 if (bp->id == id) {
396 *pbp = bp->disabled_nextp;
397 bp->disabled_nextp = NULL;
398 bip->disabled_count --;
399 goto enable_me;
400 }
401 }
402
403 printf("No breakpoint with id %d\n", bp->id);
404 return (-1); /* no such id */
405
406enable_me:
407 assert(bp->active_nextp == NULL);
408 assert(bp->disabled_nextp == NULL);
409 assert(bp->free_nextp == NULL);
410 assert(bp->hash_nextp == NULL);
411 assert(!bp->enabled);
412
413 bp->enabled = true;
414
415 bp->active_nextp = bip->active_listp;
416 bip->active_listp = bp;
417 bip->active_count ++;
418
419 bp_hash_insert(bip, bp);
420
421 /* no need to flush xcache here, but do set ptrs */
422 breaks_changed(bip);
423
424 return (0);
425}
426
427
428
429
430 /*
431 * This is what gets called if we're checking for a possible
432 * breakpoint hit.
433 * We return one of three options:
434 * a) NOTHING - didn't hit anything
435 * b) ON_BREAKPOINT - we landed on a breakpoint
436 */
437breakpoint_check_code_t
438breakpoint_check(bp_info_t *bip, uint64_t pc, int context, breakpoint_t **bpp)
439{
440 breakpoint_t *bp;
441 int i;
442
443 if (bip->do_step) {
444 if (!bip->on_this) {
445 bip->on_this = true;
446 } else {
447 *bpp = NULL;
448 return (ON_BREAKPOINT);
449 }
450 }
451
452 /* search the bp hash */
453 i = (pc >> 2) & BREAK_HASH_MASK;
454
455 for (bp = bip->hash[i]; bp != NULL; bp = bp->hash_nextp) {
456 if (!bp->enabled)
457 continue; /* in process of being deleted */
458 if (bp->pc == pc) {
459/* CSTYLED */
460DBG( printf("Matched pc @ 0x%llx : current ctxt = 0x%x, match ctxt = 0x%x\n", pc, context, bp->context ); );
461 if (bp->context == context) {
462 /* bingo ! */
463 *bpp = bp;
464 return (ON_BREAKPOINT);
465 }
466 }
467 }
468
469 return (NOTHING);
470}
471
472bool_t
473breakpoint_any_reached(void)
474{
475 int i;
476
477 for (i = 0; i < simcpu_list.count; i++) {
478 simcpu_t *sp;
479 breakpoint_t *bp;
480
481 sp = LIST_ENTRY(simcpu_list, i);
482
483 if (sp->bp_infop != NULL &&
484 breakpoint_check(sp->bp_infop, sp->pc, DEFAULT_BP_CONTEXT,
485 /* FIXME */ &bp) == ON_BREAKPOINT) {
486 return (true);
487 }
488 }
489
490 return (false);
491}
492
493
494
495/*
496 * Print all the breakpoints (in order)
497 */
498void
499breakpoint_print(bp_info_t *bip)
500{
501 breakpoint_t **pbp, *bp;
502 bool_t found = false;
503
504 printf("List of active simulation breakpoints:\n");
505 for (pbp = &(bip->active_listp); (bp = *pbp) != NULL;
506 pbp = &(bp->active_nextp)) {
507 assert(bp->enabled); /* sanity */
508 found = true;
509 printf("breakpoint id=%d: pc=0x%llx, context=0x%x, "
510 "enabled=%s\n", bp->id, bp->pc, bp->context,
511 bp->enabled? "true" : "false");
512 }
513
514 if (found == false)
515 printf("No simulation Breakpoints set\n");
516}
517
518/*
519 * Dump all the breakpoints (in order)
520 */
521bool_t
522breakpoint_dump(bp_info_t *bip, FILE *fp)
523{
524 breakpoint_t **pbp, *bp;
525 int count = 0;
526
527 for (pbp = &(bip->active_listp); (bp = *pbp) != NULL;
528 pbp = &(bp->active_nextp)) {
529 count++;
530 printf("breakpoint_dump: saving: 0x%016llx\n", bp->pc);
531 fprintf(fp, "0x%016llx\n", bp->pc);
532 }
533
534 if (count) {
535 printf("%d simulation Breakpoints saved\n", count);
536 } else {
537 printf("No simulation Breakpoints set, none to save\n");
538 }
539
540 return (true);
541}
542
543bool_t
544breakpoint_restore(bp_info_t *bip, FILE *fp)
545{
546 char line[256];
547 tvaddr_t bp;
548 int count = 0;
549
550 while (fgets(line, sizeof (line), fp) != NULL) {
551 /* skip comment lines */
552 if (*line != '#') {
553 int rv;
554
555 rv = sscanf(line, "%llx", &bp);
556 if (rv < 1) {
557 printf("breakpoint_restore: "
558 "file format error\n");
559 return (true);
560 } else {
561 count++;
562 printf("breakpoint_restore: restoring: "
563 "0x%016llx\n", bp);
564 breakpoint_insert(bip, bp, DEFAULT_BP_CONTEXT);
565 }
566 }
567 }
568 if (count) {
569 printf("%d simulation Breakpoints restored\n", count);
570 } else {
571 printf("No simulation Breakpoints restored\n");
572 }
573 return (false);
574}