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/param.h>
38#include <sys/kdb.h>
39#include <sys/proc.h>
40#include <sys/reg.h>
41#include <sys/systm.h>
42
43#include <machine/kdb.h>
44#include <machine/pcb.h>
45
46#include <vm/vm.h>
47
48#include <ddb/ddb.h>
49#include <ddb/db_access.h>
50#include <ddb/db_break.h>
51#include <ddb/db_command.h>
52
53#define	STEP_ONCE	1
54#define	STEP_RETURN	2
55#define	STEP_CALLT	3
56#define	STEP_CONTINUE	4
57#define	STEP_INVISIBLE	5
58#define	STEP_COUNT	6
59static int	db_run_mode = STEP_CONTINUE;
60
61static bool		db_sstep_multiple;
62static bool		db_sstep_print;
63static int		db_loop_count;
64static int		db_call_depth;
65
66int		db_inst_count;
67int		db_load_count;
68int		db_store_count;
69
70bool
71db_stop_at_pc(int type, int code, bool *is_breakpoint, bool *is_watchpoint)
72{
73	db_addr_t	pc;
74	db_breakpoint_t bkpt;
75
76	*is_breakpoint = IS_BREAKPOINT_TRAP(type, code);
77	*is_watchpoint = IS_WATCHPOINT_TRAP(type, code);
78	pc = PC_REGS();
79
80	db_clear_single_step();
81	db_clear_breakpoints();
82	db_clear_watchpoints();
83
84#ifdef	FIXUP_PC_AFTER_BREAK
85	if (*is_breakpoint) {
86	    /*
87	     * Breakpoint trap.  Fix up the PC if the
88	     * machine requires it.
89	     */
90	    FIXUP_PC_AFTER_BREAK
91	    pc = PC_REGS();
92	}
93#endif
94
95	/*
96	 * Now check for a breakpoint at this address.
97	 */
98	bkpt = db_find_breakpoint_here(pc);
99	if (bkpt) {
100	    if (--bkpt->count == 0) {
101		bkpt->count = bkpt->init_count;
102		*is_breakpoint = true;
103		return (true);	/* stop here */
104	    }
105	    return (false);	/* continue the countdown */
106	} else if (*is_breakpoint) {
107#ifdef BKPT_SKIP
108		BKPT_SKIP;
109#endif
110	}
111
112	*is_breakpoint = false;	/* might be a breakpoint, but not ours */
113
114	/*
115	 * If not stepping, then silently ignore single-step traps
116	 * (except for clearing the single-step-flag above).
117	 *
118	 * If stepping, then abort if the trap type is unexpected.
119	 * Breakpoints owned by us are expected and were handled above.
120	 * Single-steps are expected and are handled below.  All others
121	 * are unexpected.
122	 *
123	 * Only do either of these if the MD layer claims to classify
124	 * single-step traps unambiguously (by defining IS_SSTEP_TRAP).
125	 * Otherwise, fall through to the bad historical behaviour
126	 * given by turning unexpected traps into expected traps: if not
127	 * stepping, then expect only breakpoints and stop, and if
128	 * stepping, then expect only single-steps and step.
129	 */
130#ifdef IS_SSTEP_TRAP
131	if (db_run_mode == STEP_CONTINUE && IS_SSTEP_TRAP(type, code))
132	    return (false);
133	if (db_run_mode != STEP_CONTINUE && !IS_SSTEP_TRAP(type, code)) {
134	    printf("Stepping aborted\n");
135	    return (true);
136	}
137#endif
138
139	if (db_run_mode == STEP_INVISIBLE) {
140	    db_run_mode = STEP_CONTINUE;
141	    return (false);	/* continue */
142	}
143	if (db_run_mode == STEP_COUNT) {
144	    return (false); /* continue */
145	}
146	if (db_run_mode == STEP_ONCE) {
147	    if (--db_loop_count > 0) {
148		if (db_sstep_print) {
149		    db_printf("\t\t");
150		    db_print_loc_and_inst(pc);
151		}
152		return (false);	/* continue */
153	    }
154	}
155	if (db_run_mode == STEP_RETURN) {
156	    /* continue until matching return */
157	    db_expr_t ins;
158
159	    ins = db_get_value(pc, sizeof(int), false);
160	    if (!inst_trap_return(ins) &&
161		(!inst_return(ins) || --db_call_depth != 0)) {
162		if (db_sstep_print) {
163		    if (inst_call(ins) || inst_return(ins)) {
164			int i;
165
166			db_printf("[after %6d]     ", db_inst_count);
167			for (i = db_call_depth; --i > 0; )
168			    db_printf("  ");
169			db_print_loc_and_inst(pc);
170		    }
171		}
172		if (inst_call(ins))
173		    db_call_depth++;
174		return (false);	/* continue */
175	    }
176	}
177	if (db_run_mode == STEP_CALLT) {
178	    /* continue until call or return */
179	    db_expr_t ins;
180
181	    ins = db_get_value(pc, sizeof(int), false);
182	    if (!inst_call(ins) &&
183		!inst_return(ins) &&
184		!inst_trap_return(ins)) {
185		return (false);	/* continue */
186	    }
187	}
188	return (true);
189}
190
191void
192db_restart_at_pc(bool watchpt)
193{
194	db_addr_t	pc = PC_REGS();
195
196	if ((db_run_mode == STEP_COUNT) ||
197	    ((db_run_mode == STEP_ONCE) && db_sstep_multiple) ||
198	    (db_run_mode == STEP_RETURN) ||
199	    (db_run_mode == STEP_CALLT)) {
200	    /*
201	     * We are about to execute this instruction,
202	     * so count it now.
203	     */
204	    db_get_value(pc, sizeof(int), false);
205	    db_inst_count++;
206	    db_load_count += inst_load(ins);
207	    db_store_count += inst_store(ins);
208	}
209
210	if (db_run_mode == STEP_CONTINUE) {
211	    if (watchpt || db_find_breakpoint_here(pc)) {
212		/*
213		 * Step over breakpoint/watchpoint.
214		 */
215		db_run_mode = STEP_INVISIBLE;
216		db_set_single_step();
217	    } else {
218		db_set_breakpoints();
219		db_set_watchpoints();
220	    }
221	} else {
222	    db_set_single_step();
223	}
224}
225
226/* single-step */
227/*ARGSUSED*/
228void
229db_single_step_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
230{
231	bool		print = false;
232
233	if (count == -1)
234	    count = 1;
235
236	if (modif[0] == 'p')
237	    print = true;
238
239	db_run_mode = STEP_ONCE;
240	db_loop_count = count;
241	db_sstep_multiple = (count != 1);
242	db_sstep_print = print;
243	db_inst_count = 0;
244	db_load_count = 0;
245	db_store_count = 0;
246
247	db_cmd_loop_done = 1;
248}
249
250/* trace and print until call/return */
251/*ARGSUSED*/
252void
253db_trace_until_call_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
254    char *modif)
255{
256	bool	print = false;
257
258	if (modif[0] == 'p')
259	    print = true;
260
261	db_run_mode = STEP_CALLT;
262	db_sstep_print = print;
263	db_inst_count = 0;
264	db_load_count = 0;
265	db_store_count = 0;
266
267	db_cmd_loop_done = 1;
268}
269
270/*ARGSUSED*/
271void
272db_trace_until_matching_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
273    char *modif)
274{
275	bool	print = false;
276
277	if (modif[0] == 'p')
278	    print = true;
279
280	db_run_mode = STEP_RETURN;
281	db_call_depth = 1;
282	db_sstep_print = print;
283	db_inst_count = 0;
284	db_load_count = 0;
285	db_store_count = 0;
286
287	db_cmd_loop_done = 1;
288}
289
290/* continue */
291/*ARGSUSED*/
292void
293db_continue_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
294{
295	if (modif[0] == 'c')
296	    db_run_mode = STEP_COUNT;
297	else
298	    db_run_mode = STEP_CONTINUE;
299	db_inst_count = 0;
300	db_load_count = 0;
301	db_store_count = 0;
302
303	db_cmd_loop_done = 1;
304}
305