/* Simulator breakpoint support. Copyright (C) 1997 Free Software Foundation, Inc. Contributed by Cygnus Support. This file is part of GDB, the GNU debugger. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This 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 program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "sim-main.h" #include "sim-assert.h" #include "sim-break.h" #ifndef SIM_BREAKPOINT #define SIM_BREAKPOINT {0x00} #define SIM_BREAKPOINT_SIZE (1) #endif struct sim_breakpoint { struct sim_breakpoint *next; SIM_ADDR addr; /* Address of this breakpoint */ int flags; unsigned char loc_contents[SIM_BREAKPOINT_SIZE]; /* Contents of addr while BP is enabled */ }; #define SIM_BREAK_INSERTED 0x1 /* Breakpoint has been inserted */ #define SIM_BREAK_DISABLED 0x2 /* Breakpoint is disabled */ static unsigned char sim_breakpoint [] = SIM_BREAKPOINT; static void insert_breakpoint PARAMS ((SIM_DESC sd, struct sim_breakpoint *bp)); static void remove_breakpoint PARAMS ((SIM_DESC sd, struct sim_breakpoint *bp)); static SIM_RC resume_handler PARAMS ((SIM_DESC sd)); static SIM_RC suspend_handler PARAMS ((SIM_DESC sd)); /* Do the actual work of inserting a breakpoint into the instruction stream. */ static void insert_breakpoint (sd, bp) SIM_DESC sd; struct sim_breakpoint *bp; { if (bp->flags & (SIM_BREAK_INSERTED | SIM_BREAK_DISABLED)) return; sim_core_read_buffer (sd, NULL, exec_map, bp->loc_contents, bp->addr, SIM_BREAKPOINT_SIZE); sim_core_write_buffer (sd, NULL, exec_map, sim_breakpoint, bp->addr, SIM_BREAKPOINT_SIZE); bp->flags |= SIM_BREAK_INSERTED; } /* Do the actual work of removing a breakpoint. */ static void remove_breakpoint (sd, bp) SIM_DESC sd; struct sim_breakpoint *bp; { if (!(bp->flags & SIM_BREAK_INSERTED)) return; sim_core_write_buffer (sd, NULL, exec_map, bp->loc_contents, bp->addr, SIM_BREAKPOINT_SIZE); bp->flags &= ~SIM_BREAK_INSERTED; } /* Come here when a breakpoint insn is hit. If it's really a breakpoint, we halt things, and never return. If it's a false hit, we return to let the caller handle things. */ void sim_handle_breakpoint (sd, cpu, cia) SIM_DESC sd; sim_cpu *cpu; sim_cia cia; { struct sim_breakpoint *bp; for (bp = STATE_BREAKPOINTS (sd); bp; bp = bp->next) if (bp->addr == CIA_ADDR (cia)) break; if (!bp || !(bp->flags & SIM_BREAK_INSERTED)) return; sim_engine_halt (sd, STATE_CPU (sd, 0), NULL, cia, sim_stopped, SIM_SIGTRAP); } /* Handler functions for simulator resume and suspend events. */ static SIM_RC resume_handler (sd) SIM_DESC sd; { struct sim_breakpoint *bp; for (bp = STATE_BREAKPOINTS (sd); bp; bp = bp->next) insert_breakpoint (sd, bp); return SIM_RC_OK; } static SIM_RC suspend_handler (sd) SIM_DESC sd; { struct sim_breakpoint *bp; for (bp = STATE_BREAKPOINTS (sd); bp; bp = bp->next) remove_breakpoint (sd, bp); return SIM_RC_OK; } /* Called from simulator module initialization. */ SIM_RC sim_break_install (sd) SIM_DESC sd; { sim_module_add_resume_fn (sd, resume_handler); sim_module_add_suspend_fn (sd, suspend_handler); return SIM_RC_OK; } /* Install a breakpoint. This is a user-function. The breakpoint isn't actually installed here. We just record it. Resume_handler does the actual work. */ SIM_RC sim_set_breakpoint (sd, addr) SIM_DESC sd; SIM_ADDR addr; { struct sim_breakpoint *bp; for (bp = STATE_BREAKPOINTS (sd); bp; bp = bp->next) if (bp->addr == addr) return SIM_RC_DUPLICATE_BREAKPOINT; /* Already there */ else break; /* FIXME: why not scan all bp's? */ bp = ZALLOC (struct sim_breakpoint); bp->addr = addr; bp->next = STATE_BREAKPOINTS (sd); bp->flags = 0; STATE_BREAKPOINTS (sd) = bp; return SIM_RC_OK; } /* Delete a breakpoint. All knowlege of the breakpoint is removed from the simulator. */ SIM_RC sim_clear_breakpoint (sd, addr) SIM_DESC sd; SIM_ADDR addr; { struct sim_breakpoint *bp, *bpprev; for (bp = STATE_BREAKPOINTS (sd), bpprev = NULL; bp; bpprev = bp, bp = bp->next) if (bp->addr == addr) break; if (!bp) return SIM_RC_UNKNOWN_BREAKPOINT; remove_breakpoint (sd, bp); if (bpprev) bpprev->next = bp->next; else STATE_BREAKPOINTS (sd) = NULL; zfree (bp); return SIM_RC_OK; } SIM_RC sim_clear_all_breakpoints (sd) SIM_DESC sd; { while (STATE_BREAKPOINTS (sd)) sim_clear_breakpoint (sd, STATE_BREAKPOINTS (sd)->addr); return SIM_RC_OK; } SIM_RC sim_enable_breakpoint (sd, addr) SIM_DESC sd; SIM_ADDR addr; { struct sim_breakpoint *bp; for (bp = STATE_BREAKPOINTS (sd); bp; bp = bp->next) if (bp->addr == addr) break; if (!bp) return SIM_RC_UNKNOWN_BREAKPOINT; bp->flags &= ~SIM_BREAK_DISABLED; return SIM_RC_OK; } SIM_RC sim_disable_breakpoint (sd, addr) SIM_DESC sd; SIM_ADDR addr; { struct sim_breakpoint *bp; for (bp = STATE_BREAKPOINTS (sd); bp; bp = bp->next) if (bp->addr == addr) break; if (!bp) return SIM_RC_UNKNOWN_BREAKPOINT; bp->flags |= SIM_BREAK_DISABLED; return SIM_RC_OK; } SIM_RC sim_enable_all_breakpoints (sd) SIM_DESC sd; { struct sim_breakpoint *bp; for (bp = STATE_BREAKPOINTS (sd); bp; bp = bp->next) bp->flags &= ~SIM_BREAK_DISABLED; return SIM_RC_OK; } SIM_RC sim_disable_all_breakpoints (sd) SIM_DESC sd; { struct sim_breakpoint *bp; for (bp = STATE_BREAKPOINTS (sd); bp; bp = bp->next) bp->flags |= SIM_BREAK_DISABLED; return SIM_RC_OK; }