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: stable/11/sys/arm/arm/machdep_ptrace.c 317004 2017-04-16 07:21:20Z mmel $"); 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 40static int 41ptrace_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 49static int 50ptrace_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 58static u_int 59ptrace_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 85static u_int 86ptrace_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 */ 110static int 111ptrace_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 127int 128ptrace_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 179out: 180 PROC_LOCK(p); 181 return ((error != 0) && (error_alt != 0)); 182} 183 184int 185ptrace_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 214int 215ptrace_set_pc(struct thread *td, unsigned long addr) 216{ 217 td->td_frame->tf_pc = addr; 218 return (0); 219} 220 221int 222arm_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} 326