xref: /openbsd-src/sys/arch/m88k/m88k/db_sstep.c (revision 83877b198dc3df85fd0fee8f8e66e0f613b2046e)
1 /*	$OpenBSD: db_sstep.c,v 1.8 2019/11/08 15:01:15 mpi Exp $	*/
2 /*
3  * Mach Operating System
4  * Copyright (c) 1993-1991 Carnegie Mellon University
5  * Copyright (c) 1991 OMRON Corporation
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 AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS"
15  * CONDITION.  CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND
16  * FOR 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 #include <sys/param.h>
30 #include <sys/systm.h>
31 
32 #include <machine/db_machdep.h>
33 
34 #include <ddb/db_access.h>	/* db_get_value() */
35 #include <ddb/db_break.h>	/* db_breakpoint_t */
36 #include <ddb/db_run.h>
37 
38 /*
39  * Support routines for software single step.
40  *
41  * Author: Daniel Stodolsky (danner@cs.cmu.edu)
42  *
43  */
44 
45 /*
46  * We can not use the MI ddb SOFTWARE_SSTEP facility, since the 88110 will use
47  * hardware single stepping.
48  * Moreover, our software single stepping implementation is tailor-made for the
49  * 88100 and faster than the MI code.
50  */
51 
52 #ifdef M88100
53 
54 int		inst_branch_or_call(u_int);
55 vaddr_t		branch_taken(u_int, vaddr_t, db_regs_t *);
56 
57 db_breakpoint_t db_not_taken_bkpt = 0;
58 db_breakpoint_t db_taken_bkpt = 0;
59 
60 /*
61  * Returns `1' is the instruction a branch, jump or call instruction
62  * (br, bb0, bb1, bcnd, jmp, bsr, jsr)
63  */
64 int
inst_branch_or_call(u_int ins)65 inst_branch_or_call(u_int ins)
66 {
67 	/* check high five bits */
68 	switch (ins >> (32 - 5)) {
69 	case 0x18: /* br */
70 	case 0x19: /* bsr */
71 	case 0x1a: /* bb0 */
72 	case 0x1b: /* bb1 */
73 	case 0x1d: /* bcnd */
74 		return 1;
75 	case 0x1e: /* could be jmp or jsr */
76 		if ((ins & 0xfffff3e0) == 0xf400c000)
77 			return 1;
78 	}
79 
80 	return 0;
81 }
82 
83 /*
84  * branch_taken(instruction, program counter, regs)
85  *
86  * instruction will be a control flow instruction location at address pc.
87  * Branch taken is supposed to return the address to which the instruction
88  * would jump if the branch is taken.
89  */
90 vaddr_t
branch_taken(u_int inst,vaddr_t pc,db_regs_t * regs)91 branch_taken(u_int inst, vaddr_t pc, db_regs_t *regs)
92 {
93 	u_int regno;
94 
95 	/*
96 	 * Quick check of the instruction. Note that we know we are only
97 	 * invoked if inst_branch_or_call() returns `1', so we do not
98 	 * need to repeat the jmp and jsr stricter checks here.
99 	 */
100 	switch (inst >> (32 - 5)) {
101 	case 0x18: /* br */
102 	case 0x19: /* bsr */
103 		/* signed 26 bit pc relative displacement, shift left two bits */
104 		inst = (inst & 0x03ffffff) << 2;
105 		/* check if sign extension is needed */
106 		if (inst & 0x08000000)
107 			inst |= 0xf0000000;
108 		return (pc + inst);
109 
110 	case 0x1a: /* bb0 */
111 	case 0x1b: /* bb1 */
112 	case 0x1d: /* bcnd */
113 		/* signed 16 bit pc relative displacement, shift left two bits */
114 		inst = (inst & 0x0000ffff) << 2;
115 		/* check if sign extension is needed */
116 		if (inst & 0x00020000)
117 			inst |= 0xfffc0000;
118 		return (pc + inst);
119 
120 	default: /* jmp or jsr */
121 		regno = inst & 0x1f;
122 		return (regno == 0 ? 0 : regs->r[regno]);
123 	}
124 }
125 
126 #endif	/* M88100 */
127 
128 void
db_set_single_step(db_regs_t * regs)129 db_set_single_step(db_regs_t *regs)
130 {
131 #ifdef M88110
132 	if (CPU_IS88110) {
133 		/*
134 		 * On the 88110, we can use the hardware tracing facility...
135 		 */
136 		regs->epsr |= PSR_TRACE | PSR_SER;
137 	}
138 #endif
139 #ifdef M88100
140 	if (CPU_IS88100) {
141 		/*
142 		 * ... while the 88100 will use two breakpoints.
143 		 */
144 		vaddr_t pc = PC_REGS(regs);
145 		vaddr_t brpc;
146 		u_int inst;
147 
148 		/*
149 		 * User was stopped at pc, e.g. the instruction
150 		 * at pc was not executed.
151 		 */
152 		db_read_bytes(pc, sizeof(inst), (caddr_t)&inst);
153 
154 		/*
155 		 * Find if this instruction may cause a branch, and set up a
156 		 * breakpoint at the branch location.
157 		 */
158 		if (inst_branch_or_call(inst)) {
159 			brpc = branch_taken(inst, pc, regs);
160 
161 			/* self-branches are hopeless */
162 			if (brpc != pc && brpc != 0)
163 				db_taken_bkpt = db_set_temp_breakpoint(brpc);
164 		}
165 
166 		db_not_taken_bkpt = db_set_temp_breakpoint(pc + 4);
167 	}
168 #endif
169 }
170 
171 void
db_clear_single_step(regs)172 db_clear_single_step(regs)
173 	db_regs_t *regs;
174 {
175 #ifdef M88110
176 	if (CPU_IS88110) {
177 		regs->epsr &= ~(PSR_TRACE | PSR_SER);
178 	}
179 #endif
180 #ifdef M88100
181 	if (CPU_IS88100) {
182 		if (db_taken_bkpt != 0) {
183 			db_delete_temp_breakpoint(db_taken_bkpt);
184 			db_taken_bkpt = 0;
185 		}
186 		if (db_not_taken_bkpt != 0) {
187 			db_delete_temp_breakpoint(db_not_taken_bkpt);
188 			db_not_taken_bkpt = 0;
189 		}
190 	}
191 #endif
192 }
193