1/* Memory breakpoint operations for the remote server for GDB.
2   Copyright (C) 2002, 2003, 2005, 2007 Free Software Foundation, Inc.
3
4   Contributed by MontaVista Software.
5
6   This file is part of GDB.
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 3 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#include "server.h"
22
23const unsigned char *breakpoint_data;
24int breakpoint_len;
25
26#define MAX_BREAKPOINT_LEN 8
27
28struct breakpoint
29{
30  struct breakpoint *next;
31  CORE_ADDR pc;
32  unsigned char old_data[MAX_BREAKPOINT_LEN];
33
34  /* Non-zero iff we are stepping over this breakpoint.  */
35  int reinserting;
36
37  /* Non-NULL iff this breakpoint was inserted to step over
38     another one.  Points to the other breakpoint (which is also
39     in the *next chain somewhere).  */
40  struct breakpoint *breakpoint_to_reinsert;
41
42  /* Function to call when we hit this breakpoint.  */
43  void (*handler) (CORE_ADDR);
44};
45
46struct breakpoint *breakpoints;
47
48void
49set_breakpoint_at (CORE_ADDR where, void (*handler) (CORE_ADDR))
50{
51  struct breakpoint *bp;
52
53  if (breakpoint_data == NULL)
54    error ("Target does not support breakpoints.");
55
56  bp = malloc (sizeof (struct breakpoint));
57  memset (bp, 0, sizeof (struct breakpoint));
58
59  (*the_target->read_memory) (where, bp->old_data,
60			      breakpoint_len);
61  (*the_target->write_memory) (where, breakpoint_data,
62			       breakpoint_len);
63
64  bp->pc = where;
65  bp->handler = handler;
66
67  bp->next = breakpoints;
68  breakpoints = bp;
69}
70
71static void
72delete_breakpoint (struct breakpoint *bp)
73{
74  struct breakpoint *cur;
75
76  if (breakpoints == bp)
77    {
78      breakpoints = bp->next;
79      (*the_target->write_memory) (bp->pc, bp->old_data,
80				   breakpoint_len);
81      free (bp);
82      return;
83    }
84  cur = breakpoints;
85  while (cur->next)
86    {
87      if (cur->next == bp)
88	{
89	  cur->next = bp->next;
90	  (*the_target->write_memory) (bp->pc, bp->old_data,
91				       breakpoint_len);
92	  free (bp);
93	  return;
94	}
95    }
96  warning ("Could not find breakpoint in list.");
97}
98
99static struct breakpoint *
100find_breakpoint_at (CORE_ADDR where)
101{
102  struct breakpoint *bp = breakpoints;
103
104  while (bp != NULL)
105    {
106      if (bp->pc == where)
107	return bp;
108      bp = bp->next;
109    }
110
111  return NULL;
112}
113
114void
115delete_breakpoint_at (CORE_ADDR addr)
116{
117  struct breakpoint *bp = find_breakpoint_at (addr);
118  if (bp != NULL)
119    delete_breakpoint (bp);
120}
121
122static void
123reinsert_breakpoint_handler (CORE_ADDR stop_pc)
124{
125  struct breakpoint *stop_bp, *orig_bp;
126
127  stop_bp = find_breakpoint_at (stop_pc);
128  if (stop_bp == NULL)
129    error ("lost the stopping breakpoint.");
130
131  orig_bp = stop_bp->breakpoint_to_reinsert;
132  if (orig_bp == NULL)
133    error ("no breakpoint to reinsert");
134
135  (*the_target->write_memory) (orig_bp->pc, breakpoint_data,
136			       breakpoint_len);
137  orig_bp->reinserting = 0;
138  delete_breakpoint (stop_bp);
139}
140
141void
142reinsert_breakpoint_by_bp (CORE_ADDR stop_pc, CORE_ADDR stop_at)
143{
144  struct breakpoint *bp, *orig_bp;
145
146  set_breakpoint_at (stop_at, reinsert_breakpoint_handler);
147
148  orig_bp = find_breakpoint_at (stop_pc);
149  if (orig_bp == NULL)
150    error ("Could not find original breakpoint in list.");
151
152  bp = find_breakpoint_at (stop_at);
153  if (bp == NULL)
154    error ("Could not find breakpoint in list (reinserting by breakpoint).");
155  bp->breakpoint_to_reinsert = orig_bp;
156
157  (*the_target->write_memory) (orig_bp->pc, orig_bp->old_data,
158			       breakpoint_len);
159  orig_bp->reinserting = 1;
160}
161
162void
163uninsert_breakpoint (CORE_ADDR stopped_at)
164{
165  struct breakpoint *bp;
166
167  bp = find_breakpoint_at (stopped_at);
168  if (bp == NULL)
169    error ("Could not find breakpoint in list (uninserting).");
170
171  (*the_target->write_memory) (bp->pc, bp->old_data,
172			       breakpoint_len);
173  bp->reinserting = 1;
174}
175
176void
177reinsert_breakpoint (CORE_ADDR stopped_at)
178{
179  struct breakpoint *bp;
180
181  bp = find_breakpoint_at (stopped_at);
182  if (bp == NULL)
183    error ("Could not find breakpoint in list (uninserting).");
184  if (! bp->reinserting)
185    error ("Breakpoint already inserted at reinsert time.");
186
187  (*the_target->write_memory) (bp->pc, breakpoint_data,
188			       breakpoint_len);
189  bp->reinserting = 0;
190}
191
192int
193check_breakpoints (CORE_ADDR stop_pc)
194{
195  struct breakpoint *bp;
196
197  bp = find_breakpoint_at (stop_pc);
198  if (bp == NULL)
199    return 0;
200  if (bp->reinserting)
201    {
202      warning ("Hit a removed breakpoint?");
203      return 0;
204    }
205
206  (*bp->handler) (bp->pc);
207  return 1;
208}
209
210void
211set_breakpoint_data (const unsigned char *bp_data, int bp_len)
212{
213  breakpoint_data = bp_data;
214  breakpoint_len = bp_len;
215}
216
217void
218check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
219{
220  struct breakpoint *bp = breakpoints;
221  CORE_ADDR mem_end = mem_addr + mem_len;
222
223  for (; bp != NULL; bp = bp->next)
224    {
225      CORE_ADDR bp_end = bp->pc + breakpoint_len;
226      CORE_ADDR start, end;
227      int copy_offset, copy_len, buf_offset;
228
229      if (mem_addr >= bp_end)
230	continue;
231      if (bp->pc >= mem_end)
232	continue;
233
234      start = bp->pc;
235      if (mem_addr > start)
236	start = mem_addr;
237
238      end = bp_end;
239      if (end > mem_end)
240	end = mem_end;
241
242      copy_len = end - start;
243      copy_offset = start - bp->pc;
244      buf_offset = start - mem_addr;
245
246      memcpy (buf + buf_offset, bp->old_data + copy_offset, copy_len);
247    }
248}
249
250void
251check_mem_write (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
252{
253  struct breakpoint *bp = breakpoints;
254  CORE_ADDR mem_end = mem_addr + mem_len;
255
256  for (; bp != NULL; bp = bp->next)
257    {
258      CORE_ADDR bp_end = bp->pc + breakpoint_len;
259      CORE_ADDR start, end;
260      int copy_offset, copy_len, buf_offset;
261
262      if (mem_addr >= bp_end)
263	continue;
264      if (bp->pc >= mem_end)
265	continue;
266
267      start = bp->pc;
268      if (mem_addr > start)
269	start = mem_addr;
270
271      end = bp_end;
272      if (end > mem_end)
273	end = mem_end;
274
275      copy_len = end - start;
276      copy_offset = start - bp->pc;
277      buf_offset = start - mem_addr;
278
279      memcpy (bp->old_data + copy_offset, buf + buf_offset, copy_len);
280      if (bp->reinserting == 0)
281	memcpy (buf + buf_offset, breakpoint_data + copy_offset, copy_len);
282    }
283}
284
285/* Delete all breakpoints.  */
286
287void
288delete_all_breakpoints (void)
289{
290  while (breakpoints)
291    delete_breakpoint (breakpoints);
292}
293