1/*-
2 * Mach Operating System
3 * Copyright (c) 1991,1990 Carnegie Mellon University
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation is hereby granted, provided that both the copyright
8 * notice and this permission notice appear in all copies of the
9 * software, derivative works or modified versions, and any portions
10 * thereof, and that both notices appear in supporting documentation.
11 *
12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
13 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15 *
16 * Carnegie Mellon requests users of this software to return to
17 *
18 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19 *  School of Computer Science
20 *  Carnegie Mellon University
21 *  Pittsburgh PA 15213-3890
22 *
23 * any improvements or extensions that they make and grant Carnegie the
24 * rights to redistribute these changes.
25 *
26 */
27/*
28 *	Author: David B. Golub, Carnegie Mellon University
29 *	Date:	7/90
30 */
31/*
32 * Breakpoints.
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: releng/10.2/sys/ddb/db_break.c 273265 2014-10-18 19:22:59Z pfg $");
37
38#include <sys/param.h>
39
40#include <vm/vm.h>
41#include <vm/vm_kern.h>
42
43#include <ddb/ddb.h>
44#include <ddb/db_break.h>
45#include <ddb/db_access.h>
46#include <ddb/db_sym.h>
47
48#define	NBREAKPOINTS	100
49static struct db_breakpoint	db_break_table[NBREAKPOINTS];
50static db_breakpoint_t		db_next_free_breakpoint = &db_break_table[0];
51static db_breakpoint_t		db_free_breakpoints = 0;
52static db_breakpoint_t		db_breakpoint_list = 0;
53
54static db_breakpoint_t	db_breakpoint_alloc(void);
55static void	db_breakpoint_free(db_breakpoint_t bkpt);
56static void	db_delete_breakpoint(vm_map_t map, db_addr_t addr);
57static db_breakpoint_t	db_find_breakpoint(vm_map_t map, db_addr_t addr);
58static void	db_list_breakpoints(void);
59static void	db_set_breakpoint(vm_map_t map, db_addr_t addr, int count);
60
61static db_breakpoint_t
62db_breakpoint_alloc(void)
63{
64	register db_breakpoint_t	bkpt;
65
66	if ((bkpt = db_free_breakpoints) != 0) {
67	    db_free_breakpoints = bkpt->link;
68	    return (bkpt);
69	}
70	if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) {
71	    db_printf("All breakpoints used.\n");
72	    return (0);
73	}
74	bkpt = db_next_free_breakpoint;
75	db_next_free_breakpoint++;
76
77	return (bkpt);
78}
79
80static void
81db_breakpoint_free(db_breakpoint_t bkpt)
82{
83	bkpt->link = db_free_breakpoints;
84	db_free_breakpoints = bkpt;
85}
86
87static void
88db_set_breakpoint(vm_map_t map, db_addr_t addr, int count)
89{
90	register db_breakpoint_t	bkpt;
91
92	if (db_find_breakpoint(map, addr)) {
93	    db_printf("Already set.\n");
94	    return;
95	}
96
97	bkpt = db_breakpoint_alloc();
98	if (bkpt == 0) {
99	    db_printf("Too many breakpoints.\n");
100	    return;
101	}
102
103	bkpt->map = map;
104	bkpt->address = addr;
105	bkpt->flags = 0;
106	bkpt->init_count = count;
107	bkpt->count = count;
108
109	bkpt->link = db_breakpoint_list;
110	db_breakpoint_list = bkpt;
111}
112
113static void
114db_delete_breakpoint(vm_map_t map, db_addr_t addr)
115{
116	register db_breakpoint_t	bkpt;
117	register db_breakpoint_t	*prev;
118
119	for (prev = &db_breakpoint_list;
120	     (bkpt = *prev) != 0;
121	     prev = &bkpt->link) {
122	    if (db_map_equal(bkpt->map, map) &&
123		(bkpt->address == addr)) {
124		*prev = bkpt->link;
125		break;
126	    }
127	}
128	if (bkpt == 0) {
129	    db_printf("Not set.\n");
130	    return;
131	}
132
133	db_breakpoint_free(bkpt);
134}
135
136static db_breakpoint_t
137db_find_breakpoint(vm_map_t map, db_addr_t addr)
138{
139	register db_breakpoint_t	bkpt;
140
141	for (bkpt = db_breakpoint_list;
142	     bkpt != 0;
143	     bkpt = bkpt->link)
144	{
145	    if (db_map_equal(bkpt->map, map) &&
146		(bkpt->address == addr))
147		return (bkpt);
148	}
149	return (0);
150}
151
152db_breakpoint_t
153db_find_breakpoint_here(db_addr_t addr)
154{
155	return db_find_breakpoint(db_map_addr(addr), addr);
156}
157
158static boolean_t	db_breakpoints_inserted = TRUE;
159
160#ifndef BKPT_WRITE
161#define	BKPT_WRITE(addr, storage)				\
162do {								\
163	*storage = db_get_value(addr, BKPT_SIZE, FALSE);	\
164	db_put_value(addr, BKPT_SIZE, BKPT_SET(*storage));	\
165} while (0)
166#endif
167
168#ifndef BKPT_CLEAR
169#define	BKPT_CLEAR(addr, storage) \
170	db_put_value(addr, BKPT_SIZE, *storage)
171#endif
172
173void
174db_set_breakpoints(void)
175{
176	register db_breakpoint_t	bkpt;
177
178	if (!db_breakpoints_inserted) {
179
180		for (bkpt = db_breakpoint_list;
181		     bkpt != 0;
182		     bkpt = bkpt->link)
183			if (db_map_current(bkpt->map)) {
184				BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst);
185			}
186		db_breakpoints_inserted = TRUE;
187	}
188}
189
190void
191db_clear_breakpoints(void)
192{
193	register db_breakpoint_t	bkpt;
194
195	if (db_breakpoints_inserted) {
196
197		for (bkpt = db_breakpoint_list;
198		     bkpt != 0;
199		     bkpt = bkpt->link)
200			if (db_map_current(bkpt->map)) {
201				BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst);
202			}
203		db_breakpoints_inserted = FALSE;
204	}
205}
206
207#ifdef SOFTWARE_SSTEP
208/*
209 * Set a temporary breakpoint.
210 * The instruction is changed immediately,
211 * so the breakpoint does not have to be on the breakpoint list.
212 */
213db_breakpoint_t
214db_set_temp_breakpoint(db_addr_t addr)
215{
216	register db_breakpoint_t	bkpt;
217
218	bkpt = db_breakpoint_alloc();
219	if (bkpt == 0) {
220	    db_printf("Too many breakpoints.\n");
221	    return 0;
222	}
223
224	bkpt->map = NULL;
225	bkpt->address = addr;
226	bkpt->flags = BKPT_TEMP;
227	bkpt->init_count = 1;
228	bkpt->count = 1;
229
230	BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst);
231	return bkpt;
232}
233
234void
235db_delete_temp_breakpoint(db_breakpoint_t bkpt)
236{
237	BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst);
238	db_breakpoint_free(bkpt);
239}
240#endif /* SOFTWARE_SSTEP */
241
242/*
243 * List breakpoints.
244 */
245static void
246db_list_breakpoints(void)
247{
248	register db_breakpoint_t	bkpt;
249
250	if (db_breakpoint_list == 0) {
251	    db_printf("No breakpoints set\n");
252	    return;
253	}
254
255	db_printf(" Map      Count    Address\n");
256	for (bkpt = db_breakpoint_list;
257	     bkpt != 0;
258	     bkpt = bkpt->link) {
259	    db_printf("%s%8p %5d    ",
260		      db_map_current(bkpt->map) ? "*" : " ",
261		      (void *)bkpt->map, bkpt->init_count);
262	    db_printsym(bkpt->address, DB_STGY_PROC);
263	    db_printf("\n");
264	}
265}
266
267/* Delete breakpoint */
268/*ARGSUSED*/
269void
270db_delete_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, char *modif)
271{
272	db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr);
273}
274
275/* Set breakpoint with skip count */
276/*ARGSUSED*/
277void
278db_breakpoint_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count,
279    char *modif)
280{
281	if (count == -1)
282	    count = 1;
283
284	db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count);
285}
286
287/* list breakpoints */
288void
289db_listbreak_cmd(db_expr_t dummy1, boolean_t dummy2, db_expr_t dummy3,
290    char *dummy4)
291{
292	db_list_breakpoints();
293}
294
295/*
296 *	We want ddb to be usable before most of the kernel has been
297 *	initialized.  In particular, current_thread() or kernel_map
298 *	(or both) may be null.
299 */
300
301boolean_t
302db_map_equal(vm_map_t map1, vm_map_t map2)
303{
304	return ((map1 == map2) ||
305		((map1 == NULL) && (map2 == kernel_map)) ||
306		((map1 == kernel_map) && (map2 == NULL)));
307}
308
309boolean_t
310db_map_current(vm_map_t map)
311{
312#if 0
313	thread_t	thread;
314
315	return ((map == NULL) ||
316		(map == kernel_map) ||
317		(((thread = current_thread()) != NULL) &&
318		 (map == thread->task->map)));
319#else
320	return (1);
321#endif
322}
323
324vm_map_t
325db_map_addr(vm_offset_t addr)
326{
327#if 0
328	thread_t	thread;
329
330	/*
331	 *	We want to return kernel_map for all
332	 *	non-user addresses, even when debugging
333	 *	kernel tasks with their own maps.
334	 */
335
336	if ((VM_MIN_ADDRESS <= addr) &&
337	    (addr < VM_MAX_ADDRESS) &&
338	    ((thread = current_thread()) != NULL))
339	    return thread->task->map;
340	else
341#endif
342	    return kernel_map;
343}
344