Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / legion / src / simcore / breakpoint.c
/*
* ========== Copyright Header Begin ==========================================
*
* OpenSPARC T2 Processor File: breakpoint.c
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
*
* The above named program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License version 2 as published by the Free Software Foundation.
*
* The above named program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ========== Copyright Header End ============================================
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "@(#)breakpoint.c 1.11 07/05/30 SMI"
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <alloca.h>
#include <assert.h>
#include "basics.h"
#include "fatal.h"
#include "allocate.h"
#include "simcore.h"
#include "config.h"
#include "breakpoint.h"
#include "xicache.h" /* for xicache_instn_flush */
/*
* Breakpoint info is a global structure.
* We set a pointer in the cpu structure to it
* to indicate that breakpoints are possible for that
* cpu. The only thread that can modify this structure
* and it's elements is the main run thread ...
* .... so we don't need to worry about locking it ....
* .... just have to be careful about adding and removing
* entries so as to not strand cpus in hyperspace....
*/
bp_info_t *
breakpoint_init()
{
int i;
bp_info_t *bip;
bip = Xcalloc(1, bp_info_t);
for (i = 0; i < BREAK_HASH_SIZE; i++) {
bip->hash[i] = NULL;
}
bip->free_listp = NULL;
bip->active_listp = NULL;
bip->active_count = 0;
bip->disabled_listp = NULL;
bip->disabled_count = 0;
bip->next_id = 0;
bip->do_step = false;
return (bip);
}
/*
* Insert at top of hash ... recently
* added breakpoints should be found first ...
*/
static void
bp_hash_insert(bp_info_t *bip, breakpoint_t *bp)
{
int i;
assert(bp->enabled);
assert(bp->hash_nextp == (void*)0);
assert(bp->free_nextp == (void*)0);
assert(bp->disabled_nextp == (void*)0);
i = (bp->pc >> 2) & BREAK_HASH_MASK;
bp->hash_nextp = bip->hash[i];
bip->hash[i] = bp;
}
/*
* only gets called for existing breakpoints
* which are being deleted or disabled
*/
static void
bp_hash_unhook(bp_info_t *bip, breakpoint_t *bp)
{
int i;
breakpoint_t **pbp, *bb;
assert(bp->enabled);
assert(bp->free_nextp == (void*)0);
assert(bp->disabled_nextp == (void*)0);
i = (bp->pc >> 2) & BREAK_HASH_MASK;
/* go find it on list */
for (pbp = &(bip->hash[i]); (bb = *pbp) != (void*)0;
pbp = &(bb->hash_nextp)) {
if (bb == bp) {
*pbp = bb->hash_nextp;
bb->hash_nextp = NULL; /* sanity */
return;
}
}
abort();
}
/*
* just enabled a breakpoint, so clobber the
* cpus' xi-caches ... and other stuff ...
* this is pretty poor, and we must come up with a
* better way to get the cpus to exit their
* inner loops.... this may not always work FIXME
* ... talk about brute force - FIXME
*/
static void
breaks_changed(bp_info_t *bip)
{
int i;
for (i = 0; i < simcpu_list.count; i++) {
simcpu_t *sp;
sp = LIST_ENTRY(simcpu_list, i);
/* blow away decoded versions ... */
sp->xicache_instn_flush_pending = true;
set_sync_pending(sp);
}
}
/*
* Insert a new breakpoint ...
*/
breakpoint_t *
breakpoint_insert(bp_info_t *bip, tvaddr_t pc, int context)
{
breakpoint_t *bp;
/* CSTYLED */
DBG( printf("breakpoint_insert: @ 0x%llx, ctxt=0x%x\n", pc, (int)context); );
if (bip->free_listp != NULL) {
bp = bip->free_listp;
bip->free_listp = bp->free_nextp;
bp->free_nextp = NULL;
} else {
/* use calloc to clear all pointer fields */
bp = (breakpoint_t *)calloc(1, sizeof (*bp));
if (bp == NULL) fatal("out of memory in breakpoint_insert");
}
bp->id = bip->next_id++;
bp->pc = pc;
bp->context = context;
bp->enabled = true;
/* add to active list */
bp->active_nextp = bip->active_listp;
bip->active_listp = bp;
bip->active_count ++;
/* must not be disabled */
assert(bp->disabled_nextp == NULL);
bp_hash_insert(bip, bp);
breaks_changed(bip);
return (bp);
}
void
breakpoint_insert_next(bp_info_t *bip)
{
bip->do_step = true;
bip->on_this = false;
breaks_changed(bip);
}
void
breakpoint_clear_next(bp_info_t *bip)
{
bip->do_step = false;
breaks_changed(bip);
}
breakpoint_t *
breakpoint_find_by_addr(bp_info_t *bip, uint64_t addr, int context)
{
breakpoint_t *bp;
int i;
/* search the bp hash */
i = (addr >> 2) & BREAK_HASH_MASK;
for (bp = bip->hash[i]; bp != NULL; bp = bp->hash_nextp) {
if (bp->pc == addr && bp->context == context)
return (bp);
}
return ((breakpoint_t *)0);
}
int
breakpoint_delete_by_id(bp_info_t *bip, int id)
{
breakpoint_t **pbp, *bp;
/* search first active list */
for (pbp = &(bip->active_listp); (bp = *pbp) != NULL;
pbp = &(bp->active_nextp)) {
assert(bp->enabled); /* sanity */
if (bp->id == id) {
/* hit on active list */
/* unhook from list and unhook from hash */
bp_hash_unhook(bip, bp);
*pbp = bp->active_nextp;
bip->active_count --;
bp->enabled = false;
goto delete_me;
}
}
/* then search disabled list */
for (pbp = &(bip->disabled_listp); (bp = *pbp) != NULL;
pbp = &(bp->disabled_nextp)) {
assert(!bp->enabled); /* sanity */
if (bp->id == id) {
/* hit on disabled list */
*pbp = bp->disabled_nextp;
bip->disabled_count --;
goto delete_me;
}
}
return (-1); /* no such id */
delete_me:
/* just stuff back on free list */
bp->active_nextp = NULL;
bp->disabled_nextp = NULL;
assert(bp->free_nextp == NULL);
assert(bp->hash_nextp == NULL);
bp->free_nextp = bip->free_listp;
bip->free_listp = bp;
breaks_changed(bip);
return (0);
}
/*
* Hardly efficient, but effective - FIXME
* Since don't often add or remove breakpoints
* hopefully this isn't too painful.
*/
int
breakpoint_delete_by_addr(bp_info_t *bip, uint64_t addr, int context)
{
bool_t flag_deleted = false;
breakpoint_t *bp;
while ((bp = breakpoint_find_by_addr(bip, addr, context)) \
!= (breakpoint_t *)0) {
breakpoint_delete_by_id(bip, bp->id);
flag_deleted = true;
}
return (flag_deleted);
}
/*
* Either the bp is enabled, disabled or doesn't exist !
*/
int
breakpoint_disable_by_id(bp_info_t *bip, int id)
{
breakpoint_t **pbp, *bp;
/* search first active list */
for (pbp = &(bip->active_listp); (bp = *pbp) != NULL;
pbp = &(bp->active_nextp)) {
assert(bp->enabled); /* sanity */
if (bp->id == id) {
/* hit on active list */
/* unhook from active list and unhook from hash */
bp_hash_unhook(bip, bp);
*pbp = bp->active_nextp;
bip->active_count --;
bp->enabled = false;
goto disable_me;
}
}
/* then search disabled list */
for (bp = bip->disabled_listp; bp != NULL; bp = bp->disabled_nextp) {
assert(!bp->enabled); /* sanity */
if (bp->id == id) {
/* hit on disabled list */
printf("Breakpoint %d is already disabled\n", bp->id);
return (0); /* job done */
}
}
printf("No breakpoint with id %d\n", bp->id);
return (-1); /* no such id */
disable_me:
/* mark disabled */
bp->active_nextp = NULL;
assert(bp->disabled_nextp == NULL);
assert(bp->free_nextp == NULL);
assert(bp->hash_nextp == NULL);
bp->disabled_nextp = bip->disabled_listp;
bip->disabled_listp = bp;
bip->disabled_count ++;
breaks_changed(bip);
return (0);
}
int
breakpoint_enable_by_id(bp_info_t *bip, int id)
{
breakpoint_t **pbp, *bp;
/* make sure not already enabled */
for (bp = bip->active_listp; bp != NULL; bp = bp->active_nextp) {
assert(bp->enabled); /* sanity */
if (bp->id == id) {
printf("Breakpoint %d is already enabled\n", bp->id);
return (0); /* job done */
}
}
/* then search disabled list */
for (pbp = &(bip->disabled_listp); (bp = *pbp) != NULL;
pbp = &(bp->disabled_nextp)) {
assert(!bp->enabled); /* sanity */
if (bp->id == id) {
*pbp = bp->disabled_nextp;
bp->disabled_nextp = NULL;
bip->disabled_count --;
goto enable_me;
}
}
printf("No breakpoint with id %d\n", bp->id);
return (-1); /* no such id */
enable_me:
assert(bp->active_nextp == NULL);
assert(bp->disabled_nextp == NULL);
assert(bp->free_nextp == NULL);
assert(bp->hash_nextp == NULL);
assert(!bp->enabled);
bp->enabled = true;
bp->active_nextp = bip->active_listp;
bip->active_listp = bp;
bip->active_count ++;
bp_hash_insert(bip, bp);
/* no need to flush xcache here, but do set ptrs */
breaks_changed(bip);
return (0);
}
/*
* This is what gets called if we're checking for a possible
* breakpoint hit.
* We return one of three options:
* a) NOTHING - didn't hit anything
* b) ON_BREAKPOINT - we landed on a breakpoint
*/
breakpoint_check_code_t
breakpoint_check(bp_info_t *bip, uint64_t pc, int context, breakpoint_t **bpp)
{
breakpoint_t *bp;
int i;
if (bip->do_step) {
if (!bip->on_this) {
bip->on_this = true;
} else {
*bpp = NULL;
return (ON_BREAKPOINT);
}
}
/* search the bp hash */
i = (pc >> 2) & BREAK_HASH_MASK;
for (bp = bip->hash[i]; bp != NULL; bp = bp->hash_nextp) {
if (!bp->enabled)
continue; /* in process of being deleted */
if (bp->pc == pc) {
/* CSTYLED */
DBG( printf("Matched pc @ 0x%llx : current ctxt = 0x%x, match ctxt = 0x%x\n", pc, context, bp->context ); );
if (bp->context == context) {
/* bingo ! */
*bpp = bp;
return (ON_BREAKPOINT);
}
}
}
return (NOTHING);
}
bool_t
breakpoint_any_reached(void)
{
int i;
for (i = 0; i < simcpu_list.count; i++) {
simcpu_t *sp;
breakpoint_t *bp;
sp = LIST_ENTRY(simcpu_list, i);
if (sp->bp_infop != NULL &&
breakpoint_check(sp->bp_infop, sp->pc, DEFAULT_BP_CONTEXT,
/* FIXME */ &bp) == ON_BREAKPOINT) {
return (true);
}
}
return (false);
}
/*
* Print all the breakpoints (in order)
*/
void
breakpoint_print(bp_info_t *bip)
{
breakpoint_t **pbp, *bp;
bool_t found = false;
printf("List of active simulation breakpoints:\n");
for (pbp = &(bip->active_listp); (bp = *pbp) != NULL;
pbp = &(bp->active_nextp)) {
assert(bp->enabled); /* sanity */
found = true;
printf("breakpoint id=%d: pc=0x%llx, context=0x%x, "
"enabled=%s\n", bp->id, bp->pc, bp->context,
bp->enabled? "true" : "false");
}
if (found == false)
printf("No simulation Breakpoints set\n");
}
/*
* Dump all the breakpoints (in order)
*/
bool_t
breakpoint_dump(bp_info_t *bip, FILE *fp)
{
breakpoint_t **pbp, *bp;
int count = 0;
for (pbp = &(bip->active_listp); (bp = *pbp) != NULL;
pbp = &(bp->active_nextp)) {
count++;
printf("breakpoint_dump: saving: 0x%016llx\n", bp->pc);
fprintf(fp, "0x%016llx\n", bp->pc);
}
if (count) {
printf("%d simulation Breakpoints saved\n", count);
} else {
printf("No simulation Breakpoints set, none to save\n");
}
return (true);
}
bool_t
breakpoint_restore(bp_info_t *bip, FILE *fp)
{
char line[256];
tvaddr_t bp;
int count = 0;
while (fgets(line, sizeof (line), fp) != NULL) {
/* skip comment lines */
if (*line != '#') {
int rv;
rv = sscanf(line, "%llx", &bp);
if (rv < 1) {
printf("breakpoint_restore: "
"file format error\n");
return (true);
} else {
count++;
printf("breakpoint_restore: restoring: "
"0x%016llx\n", bp);
breakpoint_insert(bip, bp, DEFAULT_BP_CONTEXT);
}
}
}
if (count) {
printf("%d simulation Breakpoints restored\n", count);
} else {
printf("No simulation Breakpoints restored\n");
}
return (false);
}