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/param.h>
30 #include <sys/proc.h>
31 #include <sys/ptrace.h>
32 #include <sys/lock.h>
33 #include <sys/mutex.h>
34
35 #include <machine/machdep.h>
36 #include <machine/db_machdep.h>
37
38 static int
ptrace_read_int(struct thread * td,vm_offset_t addr,uint32_t * v)39 ptrace_read_int(struct thread *td, vm_offset_t addr, uint32_t *v)
40 {
41
42 if (proc_readmem(td, td->td_proc, addr, v, sizeof(*v)) != sizeof(*v))
43 return (ENOMEM);
44 return (0);
45 }
46
47 static int
ptrace_write_int(struct thread * td,vm_offset_t addr,uint32_t v)48 ptrace_write_int(struct thread *td, vm_offset_t addr, uint32_t v)
49 {
50
51 if (proc_writemem(td, td->td_proc, addr, &v, sizeof(v)) != sizeof(v))
52 return (ENOMEM);
53 return (0);
54 }
55
56 static u_int
ptrace_get_usr_reg(void * cookie,int reg)57 ptrace_get_usr_reg(void *cookie, int reg)
58 {
59 int ret;
60 struct thread *td = cookie;
61
62 KASSERT(((reg >= 0) && (reg <= ARM_REG_NUM_PC)),
63 ("reg is outside range"));
64
65 switch(reg) {
66 case ARM_REG_NUM_PC:
67 ret = td->td_frame->tf_pc;
68 break;
69 case ARM_REG_NUM_LR:
70 ret = td->td_frame->tf_usr_lr;
71 break;
72 case ARM_REG_NUM_SP:
73 ret = td->td_frame->tf_usr_sp;
74 break;
75 default:
76 ret = *((register_t*)&td->td_frame->tf_r0 + reg);
77 break;
78 }
79
80 return (ret);
81 }
82
83 static u_int
ptrace_get_usr_int(void * cookie,vm_offset_t offset,u_int * val)84 ptrace_get_usr_int(void* cookie, vm_offset_t offset, u_int* val)
85 {
86 struct thread *td = cookie;
87 u_int error;
88
89 error = ptrace_read_int(td, offset, val);
90
91 return (error);
92 }
93
94 /**
95 * This function parses current instruction opcode and decodes
96 * any possible jump (change in PC) which might occur after
97 * the instruction is executed.
98 *
99 * @param td Thread structure of analysed task
100 * @param cur_instr Currently executed instruction
101 * @param alt_next_address Pointer to the variable where
102 * the destination address of the
103 * jump instruction shall be stored.
104 *
105 * @return <0> when jump is possible
106 * <EINVAL> otherwise
107 */
108 static int
ptrace_get_alternative_next(struct thread * td,uint32_t cur_instr,uint32_t * alt_next_address)109 ptrace_get_alternative_next(struct thread *td, uint32_t cur_instr,
110 uint32_t *alt_next_address)
111 {
112 int error;
113
114 if (inst_branch(cur_instr) || inst_call(cur_instr) ||
115 inst_return(cur_instr)) {
116 error = arm_predict_branch(td, cur_instr, td->td_frame->tf_pc,
117 alt_next_address, ptrace_get_usr_reg, ptrace_get_usr_int);
118
119 return (error);
120 }
121
122 return (EINVAL);
123 }
124
125 int
ptrace_single_step(struct thread * td)126 ptrace_single_step(struct thread *td)
127 {
128 struct proc *p;
129 int error, error_alt;
130 uint32_t cur_instr, alt_next = 0;
131
132 /* TODO: This needs to be updated for Thumb-2 */
133 if ((td->td_frame->tf_spsr & PSR_T) != 0)
134 return (EINVAL);
135
136 KASSERT(td->td_md.md_ptrace_instr == 0,
137 ("Didn't clear single step"));
138 KASSERT(td->td_md.md_ptrace_instr_alt == 0,
139 ("Didn't clear alternative single step"));
140 p = td->td_proc;
141 PROC_UNLOCK(p);
142
143 error = ptrace_read_int(td, td->td_frame->tf_pc,
144 &cur_instr);
145 if (error)
146 goto out;
147
148 error = ptrace_read_int(td, td->td_frame->tf_pc + INSN_SIZE,
149 &td->td_md.md_ptrace_instr);
150 if (error == 0) {
151 error = ptrace_write_int(td, td->td_frame->tf_pc + INSN_SIZE,
152 PTRACE_BREAKPOINT);
153 if (error) {
154 td->td_md.md_ptrace_instr = 0;
155 } else {
156 td->td_md.md_ptrace_addr = td->td_frame->tf_pc +
157 INSN_SIZE;
158 }
159 }
160
161 error_alt = ptrace_get_alternative_next(td, cur_instr, &alt_next);
162 if (error_alt == 0) {
163 error_alt = ptrace_read_int(td, alt_next,
164 &td->td_md.md_ptrace_instr_alt);
165 if (error_alt) {
166 td->td_md.md_ptrace_instr_alt = 0;
167 } else {
168 error_alt = ptrace_write_int(td, alt_next,
169 PTRACE_BREAKPOINT);
170 if (error_alt)
171 td->td_md.md_ptrace_instr_alt = 0;
172 else
173 td->td_md.md_ptrace_addr_alt = alt_next;
174 }
175 }
176
177 out:
178 PROC_LOCK(p);
179 return ((error != 0) && (error_alt != 0));
180 }
181
182 int
ptrace_clear_single_step(struct thread * td)183 ptrace_clear_single_step(struct thread *td)
184 {
185 struct proc *p;
186
187 /* TODO: This needs to be updated for Thumb-2 */
188 if ((td->td_frame->tf_spsr & PSR_T) != 0)
189 return (EINVAL);
190
191 if (td->td_md.md_ptrace_instr != 0) {
192 p = td->td_proc;
193 PROC_UNLOCK(p);
194 ptrace_write_int(td, td->td_md.md_ptrace_addr,
195 td->td_md.md_ptrace_instr);
196 PROC_LOCK(p);
197 td->td_md.md_ptrace_instr = 0;
198 }
199
200 if (td->td_md.md_ptrace_instr_alt != 0) {
201 p = td->td_proc;
202 PROC_UNLOCK(p);
203 ptrace_write_int(td, td->td_md.md_ptrace_addr_alt,
204 td->td_md.md_ptrace_instr_alt);
205 PROC_LOCK(p);
206 td->td_md.md_ptrace_instr_alt = 0;
207 }
208
209 return (0);
210 }
211
212 int
ptrace_set_pc(struct thread * td,unsigned long addr)213 ptrace_set_pc(struct thread *td, unsigned long addr)
214 {
215 td->td_frame->tf_pc = addr;
216 return (0);
217 }
218
219 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 *))220 arm_predict_branch(void *cookie, u_int insn, register_t pc, register_t *new_pc,
221 u_int (*fetch_reg)(void*, int),
222 u_int (*read_int)(void*, vm_offset_t, u_int*))
223 {
224 u_int addr, nregs, offset = 0;
225 int error = 0;
226
227 switch ((insn >> 24) & 0xf) {
228 case 0x2: /* add pc, reg1, #value */
229 case 0x0: /* add pc, reg1, reg2, lsl #offset */
230 addr = fetch_reg(cookie, (insn >> 16) & 0xf);
231 if (((insn >> 16) & 0xf) == 15)
232 addr += 8;
233 if (insn & 0x0200000) {
234 offset = (insn >> 7) & 0x1e;
235 offset = (insn & 0xff) << (32 - offset) |
236 (insn & 0xff) >> offset;
237 } else {
238 offset = fetch_reg(cookie, insn & 0x0f);
239 if ((insn & 0x0000ff0) != 0x00000000) {
240 if (insn & 0x10)
241 nregs = fetch_reg(cookie,
242 (insn >> 8) & 0xf);
243 else
244 nregs = (insn >> 7) & 0x1f;
245 switch ((insn >> 5) & 3) {
246 case 0:
247 /* lsl */
248 offset = offset << nregs;
249 break;
250 case 1:
251 /* lsr */
252 offset = offset >> nregs;
253 break;
254 default:
255 break; /* XXX */
256 }
257 }
258 *new_pc = addr + offset;
259 return (0);
260 }
261
262 case 0xa: /* b ... */
263 case 0xb: /* bl ... */
264 addr = ((insn << 2) & 0x03ffffff);
265 if (addr & 0x02000000)
266 addr |= 0xfc000000;
267 *new_pc = (pc + 8 + addr);
268 return (0);
269 case 0x7: /* ldr pc, [pc, reg, lsl #2] */
270 addr = fetch_reg(cookie, insn & 0xf);
271 addr = pc + 8 + (addr << 2);
272 error = read_int(cookie, addr, &addr);
273 *new_pc = addr;
274 return (error);
275 case 0x1: /* mov pc, reg */
276 *new_pc = fetch_reg(cookie, insn & 0xf);
277 return (0);
278 case 0x4:
279 case 0x5: /* ldr pc, [reg] */
280 addr = fetch_reg(cookie, (insn >> 16) & 0xf);
281 /* ldr pc, [reg, #offset] */
282 if (insn & (1 << 24))
283 offset = insn & 0xfff;
284 if (insn & 0x00800000)
285 addr += offset;
286 else
287 addr -= offset;
288 error = read_int(cookie, addr, &addr);
289 *new_pc = addr;
290
291 return (error);
292 case 0x8: /* ldmxx reg, {..., pc} */
293 case 0x9:
294 addr = fetch_reg(cookie, (insn >> 16) & 0xf);
295 nregs = (insn & 0x5555) + ((insn >> 1) & 0x5555);
296 nregs = (nregs & 0x3333) + ((nregs >> 2) & 0x3333);
297 nregs = (nregs + (nregs >> 4)) & 0x0f0f;
298 nregs = (nregs + (nregs >> 8)) & 0x001f;
299 switch ((insn >> 23) & 0x3) {
300 case 0x0: /* ldmda */
301 addr = addr - 0;
302 break;
303 case 0x1: /* ldmia */
304 addr = addr + 0 + ((nregs - 1) << 2);
305 break;
306 case 0x2: /* ldmdb */
307 addr = addr - 4;
308 break;
309 case 0x3: /* ldmib */
310 addr = addr + 4 + ((nregs - 1) << 2);
311 break;
312 }
313 error = read_int(cookie, addr, &addr);
314 *new_pc = addr;
315
316 return (error);
317 default:
318 return (EINVAL);
319 }
320 }
321