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