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