1/*-
2 * SPDX-License-Identifier: MIT-CMU
3 *
4 * Mach Operating System
5 * Copyright (c) 1991,1990 Carnegie Mellon University
6 * All Rights Reserved.
7 *
8 * Permission to use, copy, modify and distribute this software and its
9 * documentation is hereby granted, provided that both the copyright
10 * notice and this permission notice appear in all copies of the
11 * software, derivative works or modified versions, and any portions
12 * thereof, and that both notices appear in supporting documentation.
13 *
14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
15 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
16 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17 *
18 * Carnegie Mellon requests users of this software to return to
19 *
20 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
21 *  School of Computer Science
22 *  Carnegie Mellon University
23 *  Pittsburgh PA 15213-3890
24 *
25 * any improvements or extensions that they make and grant Carnegie the
26 * rights to redistribute these changes.
27 */
28/*
29 * 	Author: David B. Golub, Carnegie Mellon University
30 *	Date:	7/90
31 */
32
33/*
34 * Commands to run process.
35 */
36
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD$");
39
40#include <sys/param.h>
41#include <sys/kdb.h>
42#include <sys/proc.h>
43#include <sys/systm.h>
44
45#include <machine/kdb.h>
46#include <machine/pcb.h>
47
48#include <vm/vm.h>
49
50#include <ddb/ddb.h>
51#include <ddb/db_access.h>
52#include <ddb/db_break.h>
53#include <ddb/db_command.h>
54
55#define	STEP_ONCE	1
56#define	STEP_RETURN	2
57#define	STEP_CALLT	3
58#define	STEP_CONTINUE	4
59#define	STEP_INVISIBLE	5
60#define	STEP_COUNT	6
61static int	db_run_mode = STEP_CONTINUE;
62
63static bool		db_sstep_multiple;
64static bool		db_sstep_print;
65static int		db_loop_count;
66static int		db_call_depth;
67
68int		db_inst_count;
69int		db_load_count;
70int		db_store_count;
71
72#ifdef SOFTWARE_SSTEP
73db_breakpoint_t	db_not_taken_bkpt = 0;
74db_breakpoint_t	db_taken_bkpt = 0;
75#endif
76
77#ifndef db_set_single_step
78void db_set_single_step(void);
79#endif
80#ifndef db_clear_single_step
81void db_clear_single_step(void);
82#endif
83#ifndef db_pc_is_singlestep
84static bool
85db_pc_is_singlestep(db_addr_t pc)
86{
87#ifdef SOFTWARE_SSTEP
88	if ((db_not_taken_bkpt != 0 && pc == db_not_taken_bkpt->address)
89	    || (db_taken_bkpt != 0 && pc == db_taken_bkpt->address))
90		return (true);
91#endif
92	return (false);
93}
94#endif
95
96bool
97db_stop_at_pc(int type, int code, bool *is_breakpoint, bool *is_watchpoint)
98{
99	db_addr_t	pc;
100	db_breakpoint_t bkpt;
101
102	*is_breakpoint = IS_BREAKPOINT_TRAP(type, code);
103	*is_watchpoint = IS_WATCHPOINT_TRAP(type, code);
104	pc = PC_REGS();
105	if (db_pc_is_singlestep(pc))
106		*is_breakpoint = false;
107
108	db_clear_single_step();
109	db_clear_breakpoints();
110	db_clear_watchpoints();
111
112#ifdef	FIXUP_PC_AFTER_BREAK
113	if (*is_breakpoint) {
114	    /*
115	     * Breakpoint trap.  Fix up the PC if the
116	     * machine requires it.
117	     */
118	    FIXUP_PC_AFTER_BREAK
119	    pc = PC_REGS();
120	}
121#endif
122
123	/*
124	 * Now check for a breakpoint at this address.
125	 */
126	bkpt = db_find_breakpoint_here(pc);
127	if (bkpt) {
128	    if (--bkpt->count == 0) {
129		bkpt->count = bkpt->init_count;
130		*is_breakpoint = true;
131		return (true);	/* stop here */
132	    }
133	    return (false);	/* continue the countdown */
134	} else if (*is_breakpoint) {
135#ifdef BKPT_SKIP
136		BKPT_SKIP;
137#endif
138	}
139
140	*is_breakpoint = false;	/* might be a breakpoint, but not ours */
141
142	/*
143	 * If not stepping, then silently ignore single-step traps
144	 * (except for clearing the single-step-flag above).
145	 *
146	 * If stepping, then abort if the trap type is unexpected.
147	 * Breakpoints owned by us are expected and were handled above.
148	 * Single-steps are expected and are handled below.  All others
149	 * are unexpected.
150	 *
151	 * Only do either of these if the MD layer claims to classify
152	 * single-step traps unambiguously (by defining IS_SSTEP_TRAP).
153	 * Otherwise, fall through to the bad historical behaviour
154	 * given by turning unexpected traps into expected traps: if not
155	 * stepping, then expect only breakpoints and stop, and if
156	 * stepping, then expect only single-steps and step.
157	 */
158#ifdef IS_SSTEP_TRAP
159	if (db_run_mode == STEP_CONTINUE && IS_SSTEP_TRAP(type, code))
160	    return (false);
161	if (db_run_mode != STEP_CONTINUE && !IS_SSTEP_TRAP(type, code)) {
162	    printf("Stepping aborted\n");
163	    return (true);
164	}
165#endif
166
167	if (db_run_mode == STEP_INVISIBLE) {
168	    db_run_mode = STEP_CONTINUE;
169	    return (false);	/* continue */
170	}
171	if (db_run_mode == STEP_COUNT) {
172	    return (false); /* continue */
173	}
174	if (db_run_mode == STEP_ONCE) {
175	    if (--db_loop_count > 0) {
176		if (db_sstep_print) {
177		    db_printf("\t\t");
178		    db_print_loc_and_inst(pc);
179		}
180		return (false);	/* continue */
181	    }
182	}
183	if (db_run_mode == STEP_RETURN) {
184	    /* continue until matching return */
185	    db_expr_t ins;
186
187	    ins = db_get_value(pc, sizeof(int), false);
188	    if (!inst_trap_return(ins) &&
189		(!inst_return(ins) || --db_call_depth != 0)) {
190		if (db_sstep_print) {
191		    if (inst_call(ins) || inst_return(ins)) {
192			int i;
193
194			db_printf("[after %6d]     ", db_inst_count);
195			for (i = db_call_depth; --i > 0; )
196			    db_printf("  ");
197			db_print_loc_and_inst(pc);
198		    }
199		}
200		if (inst_call(ins))
201		    db_call_depth++;
202		return (false);	/* continue */
203	    }
204	}
205	if (db_run_mode == STEP_CALLT) {
206	    /* continue until call or return */
207	    db_expr_t ins;
208
209	    ins = db_get_value(pc, sizeof(int), false);
210	    if (!inst_call(ins) &&
211		!inst_return(ins) &&
212		!inst_trap_return(ins)) {
213		return (false);	/* continue */
214	    }
215	}
216	return (true);
217}
218
219void
220db_restart_at_pc(bool watchpt)
221{
222	db_addr_t	pc = PC_REGS();
223
224	if ((db_run_mode == STEP_COUNT) ||
225	    ((db_run_mode == STEP_ONCE) && db_sstep_multiple) ||
226	    (db_run_mode == STEP_RETURN) ||
227	    (db_run_mode == STEP_CALLT)) {
228	    /*
229	     * We are about to execute this instruction,
230	     * so count it now.
231	     */
232#ifdef	SOFTWARE_SSTEP
233	    db_expr_t		ins =
234#endif
235	    db_get_value(pc, sizeof(int), false);
236	    db_inst_count++;
237	    db_load_count += inst_load(ins);
238	    db_store_count += inst_store(ins);
239#ifdef	SOFTWARE_SSTEP
240	    /* XXX works on mips, but... */
241	    if (inst_branch(ins) || inst_call(ins)) {
242		ins = db_get_value(next_instr_address(pc,1),
243				   sizeof(int), false);
244		db_inst_count++;
245		db_load_count += inst_load(ins);
246		db_store_count += inst_store(ins);
247	    }
248#endif	/* SOFTWARE_SSTEP */
249	}
250
251	if (db_run_mode == STEP_CONTINUE) {
252	    if (watchpt || db_find_breakpoint_here(pc)) {
253		/*
254		 * Step over breakpoint/watchpoint.
255		 */
256		db_run_mode = STEP_INVISIBLE;
257		db_set_single_step();
258	    } else {
259		db_set_breakpoints();
260		db_set_watchpoints();
261	    }
262	} else {
263	    db_set_single_step();
264	}
265}
266
267#ifdef	SOFTWARE_SSTEP
268/*
269 *	Software implementation of single-stepping.
270 *	If your machine does not have a trace mode
271 *	similar to the vax or sun ones you can use
272 *	this implementation, done for the mips.
273 *	Just define the above conditional and provide
274 *	the functions/macros defined below.
275 *
276 * extern bool
277 *	inst_branch(),		returns true if the instruction might branch
278 * extern unsigned
279 *	branch_taken(),		return the address the instruction might
280 *				branch to
281 *	db_getreg_val();	return the value of a user register,
282 *				as indicated in the hardware instruction
283 *				encoding, e.g. 8 for r8
284 *
285 * next_instr_address(pc,bd)	returns the address of the first
286 *				instruction following the one at "pc",
287 *				which is either in the taken path of
288 *				the branch (bd==1) or not.  This is
289 *				for machines (mips) with branch delays.
290 *
291 *	A single-step may involve at most 2 breakpoints -
292 *	one for branch-not-taken and one for branch taken.
293 *	If one of these addresses does not already have a breakpoint,
294 *	we allocate a breakpoint and save it here.
295 *	These breakpoints are deleted on return.
296 */
297
298void
299db_set_single_step(void)
300{
301	db_addr_t pc = PC_REGS(), brpc;
302	unsigned inst;
303
304	/*
305	 *	User was stopped at pc, e.g. the instruction
306	 *	at pc was not executed.
307	 */
308	inst = db_get_value(pc, sizeof(int), false);
309	if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) {
310		brpc = branch_taken(inst, pc);
311		if (brpc != pc) {	/* self-branches are hopeless */
312			db_taken_bkpt = db_set_temp_breakpoint(brpc);
313		}
314		pc = next_instr_address(pc, 1);
315	}
316	pc = next_instr_address(pc, 0);
317	db_not_taken_bkpt = db_set_temp_breakpoint(pc);
318}
319
320void
321db_clear_single_step(void)
322{
323
324	if (db_not_taken_bkpt != 0) {
325		db_delete_temp_breakpoint(db_not_taken_bkpt);
326		db_not_taken_bkpt = 0;
327	}
328	if (db_taken_bkpt != 0) {
329		db_delete_temp_breakpoint(db_taken_bkpt);
330		db_taken_bkpt = 0;
331	}
332}
333
334#endif	/* SOFTWARE_SSTEP */
335
336/* single-step */
337/*ARGSUSED*/
338void
339db_single_step_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
340{
341	bool		print = false;
342
343	if (count == -1)
344	    count = 1;
345
346	if (modif[0] == 'p')
347	    print = true;
348
349	db_run_mode = STEP_ONCE;
350	db_loop_count = count;
351	db_sstep_multiple = (count != 1);
352	db_sstep_print = print;
353	db_inst_count = 0;
354	db_load_count = 0;
355	db_store_count = 0;
356
357	db_cmd_loop_done = 1;
358}
359
360/* trace and print until call/return */
361/*ARGSUSED*/
362void
363db_trace_until_call_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
364    char *modif)
365{
366	bool	print = false;
367
368	if (modif[0] == 'p')
369	    print = true;
370
371	db_run_mode = STEP_CALLT;
372	db_sstep_print = print;
373	db_inst_count = 0;
374	db_load_count = 0;
375	db_store_count = 0;
376
377	db_cmd_loop_done = 1;
378}
379
380/*ARGSUSED*/
381void
382db_trace_until_matching_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
383    char *modif)
384{
385	bool	print = false;
386
387	if (modif[0] == 'p')
388	    print = true;
389
390	db_run_mode = STEP_RETURN;
391	db_call_depth = 1;
392	db_sstep_print = print;
393	db_inst_count = 0;
394	db_load_count = 0;
395	db_store_count = 0;
396
397	db_cmd_loop_done = 1;
398}
399
400/* continue */
401/*ARGSUSED*/
402void
403db_continue_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
404{
405	if (modif[0] == 'c')
406	    db_run_mode = STEP_COUNT;
407	else
408	    db_run_mode = STEP_CONTINUE;
409	db_inst_count = 0;
410	db_load_count = 0;
411	db_store_count = 0;
412
413	db_cmd_loop_done = 1;
414}
415