1 /*-
2 * Copyright (c) 2004 Olivier Houchard
3 * Copyright (c) 1994-1998 Mark Brinicombe.
4 * Copyright (c) 1994 Brini.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 #include <sys/param.h>
31 #include <sys/proc.h>
32 #include <sys/ptrace.h>
33 #include <sys/lock.h>
34 #include <sys/mutex.h>
35
36 #include <machine/machdep.h>
37 #include <machine/db_machdep.h>
38
39 static int
ptrace_read_int(struct thread * td,vm_offset_t addr,uint32_t * v)40 ptrace_read_int(struct thread *td, vm_offset_t addr, uint32_t *v)
41 {
42
43 if (proc_readmem(td, td->td_proc, addr, v, sizeof(*v)) != sizeof(*v))
44 return (ENOMEM);
45 return (0);
46 }
47
48 static int
ptrace_write_int(struct thread * td,vm_offset_t addr,uint32_t v)49 ptrace_write_int(struct thread *td, vm_offset_t addr, uint32_t v)
50 {
51
52 if (proc_writemem(td, td->td_proc, addr, &v, sizeof(v)) != sizeof(v))
53 return (ENOMEM);
54 return (0);
55 }
56
57 static u_int
ptrace_get_usr_reg(void * cookie,int reg)58 ptrace_get_usr_reg(void *cookie, int reg)
59 {
60 int ret;
61 struct thread *td = cookie;
62
63 KASSERT(((reg >= 0) && (reg <= ARM_REG_NUM_PC)),
64 ("reg is outside range"));
65
66 switch(reg) {
67 case ARM_REG_NUM_PC:
68 ret = td->td_frame->tf_pc;
69 break;
70 case ARM_REG_NUM_LR:
71 ret = td->td_frame->tf_usr_lr;
72 break;
73 case ARM_REG_NUM_SP:
74 ret = td->td_frame->tf_usr_sp;
75 break;
76 default:
77 ret = *((register_t*)&td->td_frame->tf_r0 + reg);
78 break;
79 }
80
81 return (ret);
82 }
83
84 static u_int
ptrace_get_usr_int(void * cookie,vm_offset_t offset,u_int * val)85 ptrace_get_usr_int(void* cookie, vm_offset_t offset, u_int* val)
86 {
87 struct thread *td = cookie;
88 u_int error;
89
90 error = ptrace_read_int(td, offset, val);
91
92 return (error);
93 }
94
95 /**
96 * This function parses current instruction opcode and decodes
97 * any possible jump (change in PC) which might occur after
98 * the instruction is executed.
99 *
100 * @param td Thread structure of analysed task
101 * @param cur_instr Currently executed instruction
102 * @param alt_next_address Pointer to the variable where
103 * the destination address of the
104 * jump instruction shall be stored.
105 *
106 * @return <0> when jump is possible
107 * <EINVAL> otherwise
108 */
109 static int
ptrace_get_alternative_next(struct thread * td,uint32_t cur_instr,uint32_t * alt_next_address)110 ptrace_get_alternative_next(struct thread *td, uint32_t cur_instr,
111 uint32_t *alt_next_address)
112 {
113 int error;
114
115 if (inst_branch(cur_instr) || inst_call(cur_instr) ||
116 inst_return(cur_instr)) {
117 error = arm_predict_branch(td, cur_instr, td->td_frame->tf_pc,
118 alt_next_address, ptrace_get_usr_reg, ptrace_get_usr_int);
119
120 return (error);
121 }
122
123 return (EINVAL);
124 }
125
126 int
ptrace_single_step(struct thread * td)127 ptrace_single_step(struct thread *td)
128 {
129 struct proc *p;
130 int error, error_alt;
131 uint32_t cur_instr, alt_next = 0;
132
133 /* TODO: This needs to be updated for Thumb-2 */
134 if ((td->td_frame->tf_spsr & PSR_T) != 0)
135 return (EINVAL);
136
137 KASSERT(td->td_md.md_ptrace_instr == 0,
138 ("Didn't clear single step"));
139 KASSERT(td->td_md.md_ptrace_instr_alt == 0,
140 ("Didn't clear alternative single step"));
141 p = td->td_proc;
142 PROC_UNLOCK(p);
143
144 error = ptrace_read_int(td, td->td_frame->tf_pc,
145 &cur_instr);
146 if (error)
147 goto out;
148
149 error = ptrace_read_int(td, td->td_frame->tf_pc + INSN_SIZE,
150 &td->td_md.md_ptrace_instr);
151 if (error == 0) {
152 error = ptrace_write_int(td, td->td_frame->tf_pc + INSN_SIZE,
153 PTRACE_BREAKPOINT);
154 if (error) {
155 td->td_md.md_ptrace_instr = 0;
156 } else {
157 td->td_md.md_ptrace_addr = td->td_frame->tf_pc +
158 INSN_SIZE;
159 }
160 }
161
162 error_alt = ptrace_get_alternative_next(td, cur_instr, &alt_next);
163 if (error_alt == 0) {
164 error_alt = ptrace_read_int(td, alt_next,
165 &td->td_md.md_ptrace_instr_alt);
166 if (error_alt) {
167 td->td_md.md_ptrace_instr_alt = 0;
168 } else {
169 error_alt = ptrace_write_int(td, alt_next,
170 PTRACE_BREAKPOINT);
171 if (error_alt)
172 td->td_md.md_ptrace_instr_alt = 0;
173 else
174 td->td_md.md_ptrace_addr_alt = alt_next;
175 }
176 }
177
178 out:
179 PROC_LOCK(p);
180 return ((error != 0) && (error_alt != 0));
181 }
182
183 int
ptrace_clear_single_step(struct thread * td)184 ptrace_clear_single_step(struct thread *td)
185 {
186 struct proc *p;
187
188 /* TODO: This needs to be updated for Thumb-2 */
189 if ((td->td_frame->tf_spsr & PSR_T) != 0)
190 return (EINVAL);
191
192 if (td->td_md.md_ptrace_instr != 0) {
193 p = td->td_proc;
194 PROC_UNLOCK(p);
195 ptrace_write_int(td, td->td_md.md_ptrace_addr,
196 td->td_md.md_ptrace_instr);
197 PROC_LOCK(p);
198 td->td_md.md_ptrace_instr = 0;
199 }
200
201 if (td->td_md.md_ptrace_instr_alt != 0) {
202 p = td->td_proc;
203 PROC_UNLOCK(p);
204 ptrace_write_int(td, td->td_md.md_ptrace_addr_alt,
205 td->td_md.md_ptrace_instr_alt);
206 PROC_LOCK(p);
207 td->td_md.md_ptrace_instr_alt = 0;
208 }
209
210 return (0);
211 }
212
213 int
ptrace_set_pc(struct thread * td,unsigned long addr)214 ptrace_set_pc(struct thread *td, unsigned long addr)
215 {
216 td->td_frame->tf_pc = addr;
217 return (0);
218 }
219
220 int
arm_predict_branch(void * cookie,u_int insn,register_t pc,register_t * new_pc,u_int (* fetch_reg)(void *,int),u_int (* read_int)(void *,vm_offset_t,u_int *))221 arm_predict_branch(void *cookie, u_int insn, register_t pc, register_t *new_pc,
222 u_int (*fetch_reg)(void*, int),
223 u_int (*read_int)(void*, vm_offset_t, u_int*))
224 {
225 u_int addr, nregs, offset = 0;
226 int error = 0;
227
228 switch ((insn >> 24) & 0xf) {
229 case 0x2: /* add pc, reg1, #value */
230 case 0x0: /* add pc, reg1, reg2, lsl #offset */
231 addr = fetch_reg(cookie, (insn >> 16) & 0xf);
232 if (((insn >> 16) & 0xf) == 15)
233 addr += 8;
234 if (insn & 0x0200000) {
235 offset = (insn >> 7) & 0x1e;
236 offset = (insn & 0xff) << (32 - offset) |
237 (insn & 0xff) >> offset;
238 } else {
239 offset = fetch_reg(cookie, insn & 0x0f);
240 if ((insn & 0x0000ff0) != 0x00000000) {
241 if (insn & 0x10)
242 nregs = fetch_reg(cookie,
243 (insn >> 8) & 0xf);
244 else
245 nregs = (insn >> 7) & 0x1f;
246 switch ((insn >> 5) & 3) {
247 case 0:
248 /* lsl */
249 offset = offset << nregs;
250 break;
251 case 1:
252 /* lsr */
253 offset = offset >> nregs;
254 break;
255 default:
256 break; /* XXX */
257 }
258 }
259 *new_pc = addr + offset;
260 return (0);
261 }
262
263 case 0xa: /* b ... */
264 case 0xb: /* bl ... */
265 addr = ((insn << 2) & 0x03ffffff);
266 if (addr & 0x02000000)
267 addr |= 0xfc000000;
268 *new_pc = (pc + 8 + addr);
269 return (0);
270 case 0x7: /* ldr pc, [pc, reg, lsl #2] */
271 addr = fetch_reg(cookie, insn & 0xf);
272 addr = pc + 8 + (addr << 2);
273 error = read_int(cookie, addr, &addr);
274 *new_pc = addr;
275 return (error);
276 case 0x1: /* mov pc, reg */
277 *new_pc = fetch_reg(cookie, insn & 0xf);
278 return (0);
279 case 0x4:
280 case 0x5: /* ldr pc, [reg] */
281 addr = fetch_reg(cookie, (insn >> 16) & 0xf);
282 /* ldr pc, [reg, #offset] */
283 if (insn & (1 << 24))
284 offset = insn & 0xfff;
285 if (insn & 0x00800000)
286 addr += offset;
287 else
288 addr -= offset;
289 error = read_int(cookie, addr, &addr);
290 *new_pc = addr;
291
292 return (error);
293 case 0x8: /* ldmxx reg, {..., pc} */
294 case 0x9:
295 addr = fetch_reg(cookie, (insn >> 16) & 0xf);
296 nregs = (insn & 0x5555) + ((insn >> 1) & 0x5555);
297 nregs = (nregs & 0x3333) + ((nregs >> 2) & 0x3333);
298 nregs = (nregs + (nregs >> 4)) & 0x0f0f;
299 nregs = (nregs + (nregs >> 8)) & 0x001f;
300 switch ((insn >> 23) & 0x3) {
301 case 0x0: /* ldmda */
302 addr = addr - 0;
303 break;
304 case 0x1: /* ldmia */
305 addr = addr + 0 + ((nregs - 1) << 2);
306 break;
307 case 0x2: /* ldmdb */
308 addr = addr - 4;
309 break;
310 case 0x3: /* ldmib */
311 addr = addr + 4 + ((nregs - 1) << 2);
312 break;
313 }
314 error = read_int(cookie, addr, &addr);
315 *new_pc = addr;
316
317 return (error);
318 default:
319 return (EINVAL);
320 }
321 }
322