1139735Simp/*- 2129198Scognet * Copyright (c) 2004 Olivier Houchard 3129198Scognet * Copyright (c) 1994-1998 Mark Brinicombe. 4129198Scognet * Copyright (c) 1994 Brini. 5129198Scognet * All rights reserved. 6129198Scognet * 7129198Scognet * Redistribution and use in source and binary forms, with or without 8129198Scognet * modification, are permitted provided that the following conditions 9129198Scognet * are met: 10129198Scognet * 1. Redistributions of source code must retain the above copyright 11129198Scognet * notice, this list of conditions and the following disclaimer. 12129198Scognet * 2. Redistributions in binary form must reproduce the above copyright 13129198Scognet * notice, this list of conditions and the following disclaimer in the 14129198Scognet * documentation and/or other materials provided with the distribution. 15129198Scognet * 16315059Smmel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17315059Smmel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18315059Smmel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19315059Smmel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20315059Smmel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21315059Smmel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22315059Smmel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23129198Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24129198Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25129198Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26129198Scognet * SUCH DAMAGE. 27129198Scognet */ 28129198Scognet 29129198Scognet#include <sys/cdefs.h> 30129198Scognet__FBSDID("$FreeBSD: stable/11/sys/arm/arm/machdep_ptrace.c 317004 2017-04-16 07:21:20Z mmel $"); 31129198Scognet 32129198Scognet#include <sys/param.h> 33141378Snjl#include <sys/proc.h> 34315059Smmel#include <sys/ptrace.h> 35129198Scognet#include <sys/mutex.h> 36129198Scognet 37315059Smmel#include <machine/machdep.h> 38290273Szbb#include <machine/db_machdep.h> 39129198Scognet 40140001Scognetstatic int 41291961Smarkjptrace_read_int(struct thread *td, vm_offset_t addr, uint32_t *v) 42140001Scognet{ 43155922Sjhb 44291961Smarkj if (proc_readmem(td, td->td_proc, addr, v, sizeof(*v)) != sizeof(*v)) 45291961Smarkj return (ENOMEM); 46291961Smarkj return (0); 47140001Scognet} 48140001Scognet 49140001Scognetstatic int 50291961Smarkjptrace_write_int(struct thread *td, vm_offset_t addr, uint32_t v) 51140001Scognet{ 52155922Sjhb 53291961Smarkj if (proc_writemem(td, td->td_proc, addr, &v, sizeof(v)) != sizeof(v)) 54291961Smarkj return (ENOMEM); 55291961Smarkj return (0); 56140001Scognet} 57140001Scognet 58290273Szbbstatic u_int 59290273Szbbptrace_get_usr_reg(void *cookie, int reg) 60290273Szbb{ 61290273Szbb int ret; 62290273Szbb struct thread *td = cookie; 63290273Szbb 64290273Szbb KASSERT(((reg >= 0) && (reg <= ARM_REG_NUM_PC)), 65290273Szbb ("reg is outside range")); 66290273Szbb 67290273Szbb switch(reg) { 68290273Szbb case ARM_REG_NUM_PC: 69290273Szbb ret = td->td_frame->tf_pc; 70290273Szbb break; 71290273Szbb case ARM_REG_NUM_LR: 72290273Szbb ret = td->td_frame->tf_usr_lr; 73290273Szbb break; 74290273Szbb case ARM_REG_NUM_SP: 75290273Szbb ret = td->td_frame->tf_usr_sp; 76290273Szbb break; 77290273Szbb default: 78290273Szbb ret = *((register_t*)&td->td_frame->tf_r0 + reg); 79290273Szbb break; 80290273Szbb } 81290273Szbb 82290273Szbb return (ret); 83290273Szbb} 84290273Szbb 85290273Szbbstatic u_int 86290273Szbbptrace_get_usr_int(void* cookie, vm_offset_t offset, u_int* val) 87290273Szbb{ 88290273Szbb struct thread *td = cookie; 89290273Szbb u_int error; 90290273Szbb 91290273Szbb error = ptrace_read_int(td, offset, val); 92290273Szbb 93290273Szbb return (error); 94290273Szbb} 95290273Szbb 96290273Szbb/** 97290273Szbb * This function parses current instruction opcode and decodes 98290273Szbb * any possible jump (change in PC) which might occur after 99290273Szbb * the instruction is executed. 100290273Szbb * 101290273Szbb * @param td Thread structure of analysed task 102290273Szbb * @param cur_instr Currently executed instruction 103290273Szbb * @param alt_next_address Pointer to the variable where 104290273Szbb * the destination address of the 105290273Szbb * jump instruction shall be stored. 106290273Szbb * 107290273Szbb * @return <0> when jump is possible 108290273Szbb * <EINVAL> otherwise 109290273Szbb */ 110290273Szbbstatic int 111290273Szbbptrace_get_alternative_next(struct thread *td, uint32_t cur_instr, 112290273Szbb uint32_t *alt_next_address) 113290273Szbb{ 114290273Szbb int error; 115290273Szbb 116290273Szbb if (inst_branch(cur_instr) || inst_call(cur_instr) || 117290273Szbb inst_return(cur_instr)) { 118290273Szbb error = arm_predict_branch(td, cur_instr, td->td_frame->tf_pc, 119290273Szbb alt_next_address, ptrace_get_usr_reg, ptrace_get_usr_int); 120290273Szbb 121290273Szbb return (error); 122290273Szbb } 123290273Szbb 124290273Szbb return (EINVAL); 125290273Szbb} 126290273Szbb 127129198Scognetint 128129198Scognetptrace_single_step(struct thread *td) 129129198Scognet{ 130155922Sjhb struct proc *p; 131290273Szbb int error, error_alt; 132290273Szbb uint32_t cur_instr, alt_next = 0; 133283366Sandrew 134282779Sandrew /* TODO: This needs to be updated for Thumb-2 */ 135282779Sandrew if ((td->td_frame->tf_spsr & PSR_T) != 0) 136282779Sandrew return (EINVAL); 137282779Sandrew 138140001Scognet KASSERT(td->td_md.md_ptrace_instr == 0, 139140001Scognet ("Didn't clear single step")); 140290273Szbb KASSERT(td->td_md.md_ptrace_instr_alt == 0, 141290273Szbb ("Didn't clear alternative single step")); 142155922Sjhb p = td->td_proc; 143155922Sjhb PROC_UNLOCK(p); 144290273Szbb 145290273Szbb error = ptrace_read_int(td, td->td_frame->tf_pc, 146290273Szbb &cur_instr); 147140001Scognet if (error) 148155922Sjhb goto out; 149290273Szbb 150290273Szbb error = ptrace_read_int(td, td->td_frame->tf_pc + INSN_SIZE, 151290273Szbb &td->td_md.md_ptrace_instr); 152290273Szbb if (error == 0) { 153290273Szbb error = ptrace_write_int(td, td->td_frame->tf_pc + INSN_SIZE, 154290273Szbb PTRACE_BREAKPOINT); 155290273Szbb if (error) { 156290273Szbb td->td_md.md_ptrace_instr = 0; 157290273Szbb } else { 158290273Szbb td->td_md.md_ptrace_addr = td->td_frame->tf_pc + 159290273Szbb INSN_SIZE; 160290273Szbb } 161290273Szbb } 162290273Szbb 163290273Szbb error_alt = ptrace_get_alternative_next(td, cur_instr, &alt_next); 164290273Szbb if (error_alt == 0) { 165290273Szbb error_alt = ptrace_read_int(td, alt_next, 166290273Szbb &td->td_md.md_ptrace_instr_alt); 167290273Szbb if (error_alt) { 168290273Szbb td->td_md.md_ptrace_instr_alt = 0; 169290273Szbb } else { 170290273Szbb error_alt = ptrace_write_int(td, alt_next, 171290273Szbb PTRACE_BREAKPOINT); 172290273Szbb if (error_alt) 173290273Szbb td->td_md.md_ptrace_instr_alt = 0; 174290273Szbb else 175290273Szbb td->td_md.md_ptrace_addr_alt = alt_next; 176290273Szbb } 177290273Szbb } 178290273Szbb 179155922Sjhbout: 180155922Sjhb PROC_LOCK(p); 181290273Szbb return ((error != 0) && (error_alt != 0)); 182129198Scognet} 183129198Scognet 184129198Scognetint 185132474Scognetptrace_clear_single_step(struct thread *td) 186132474Scognet{ 187155922Sjhb struct proc *p; 188155922Sjhb 189282779Sandrew /* TODO: This needs to be updated for Thumb-2 */ 190282779Sandrew if ((td->td_frame->tf_spsr & PSR_T) != 0) 191282779Sandrew return (EINVAL); 192282779Sandrew 193290273Szbb if (td->td_md.md_ptrace_instr != 0) { 194155922Sjhb p = td->td_proc; 195155922Sjhb PROC_UNLOCK(p); 196140001Scognet ptrace_write_int(td, td->td_md.md_ptrace_addr, 197140001Scognet td->td_md.md_ptrace_instr); 198155922Sjhb PROC_LOCK(p); 199140001Scognet td->td_md.md_ptrace_instr = 0; 200140001Scognet } 201290273Szbb 202290273Szbb if (td->td_md.md_ptrace_instr_alt != 0) { 203290273Szbb p = td->td_proc; 204290273Szbb PROC_UNLOCK(p); 205290273Szbb ptrace_write_int(td, td->td_md.md_ptrace_addr_alt, 206290273Szbb td->td_md.md_ptrace_instr_alt); 207290273Szbb PROC_LOCK(p); 208290273Szbb td->td_md.md_ptrace_instr_alt = 0; 209290273Szbb } 210290273Szbb 211132474Scognet return (0); 212132474Scognet} 213132474Scognet 214132474Scognetint 215129198Scognetptrace_set_pc(struct thread *td, unsigned long addr) 216129198Scognet{ 217132474Scognet td->td_frame->tf_pc = addr; 218129198Scognet return (0); 219129198Scognet} 220129198Scognet 221129198Scognetint 222290273Szbbarm_predict_branch(void *cookie, u_int insn, register_t pc, register_t *new_pc, 223315059Smmel u_int (*fetch_reg)(void*, int), 224315059Smmel u_int (*read_int)(void*, vm_offset_t, u_int*)) 225290273Szbb{ 226290273Szbb u_int addr, nregs, offset = 0; 227290273Szbb int error = 0; 228290273Szbb 229290273Szbb switch ((insn >> 24) & 0xf) { 230290273Szbb case 0x2: /* add pc, reg1, #value */ 231290273Szbb case 0x0: /* add pc, reg1, reg2, lsl #offset */ 232290273Szbb addr = fetch_reg(cookie, (insn >> 16) & 0xf); 233290273Szbb if (((insn >> 16) & 0xf) == 15) 234290273Szbb addr += 8; 235290273Szbb if (insn & 0x0200000) { 236290273Szbb offset = (insn >> 7) & 0x1e; 237290273Szbb offset = (insn & 0xff) << (32 - offset) | 238290273Szbb (insn & 0xff) >> offset; 239290273Szbb } else { 240290273Szbb 241290273Szbb offset = fetch_reg(cookie, insn & 0x0f); 242290273Szbb if ((insn & 0x0000ff0) != 0x00000000) { 243290273Szbb if (insn & 0x10) 244290273Szbb nregs = fetch_reg(cookie, 245290273Szbb (insn >> 8) & 0xf); 246290273Szbb else 247290273Szbb nregs = (insn >> 7) & 0x1f; 248290273Szbb switch ((insn >> 5) & 3) { 249290273Szbb case 0: 250290273Szbb /* lsl */ 251290273Szbb offset = offset << nregs; 252290273Szbb break; 253290273Szbb case 1: 254290273Szbb /* lsr */ 255290273Szbb offset = offset >> nregs; 256290273Szbb break; 257290273Szbb default: 258290273Szbb break; /* XXX */ 259290273Szbb } 260290273Szbb 261290273Szbb } 262290273Szbb *new_pc = addr + offset; 263290273Szbb return (0); 264290273Szbb 265290273Szbb } 266290273Szbb 267290273Szbb case 0xa: /* b ... */ 268290273Szbb case 0xb: /* bl ... */ 269290273Szbb addr = ((insn << 2) & 0x03ffffff); 270290273Szbb if (addr & 0x02000000) 271290273Szbb addr |= 0xfc000000; 272290273Szbb *new_pc = (pc + 8 + addr); 273290273Szbb return (0); 274290273Szbb case 0x7: /* ldr pc, [pc, reg, lsl #2] */ 275290273Szbb addr = fetch_reg(cookie, insn & 0xf); 276290273Szbb addr = pc + 8 + (addr << 2); 277290273Szbb error = read_int(cookie, addr, &addr); 278290273Szbb *new_pc = addr; 279290273Szbb return (error); 280290273Szbb case 0x1: /* mov pc, reg */ 281290273Szbb *new_pc = fetch_reg(cookie, insn & 0xf); 282290273Szbb return (0); 283290273Szbb case 0x4: 284290273Szbb case 0x5: /* ldr pc, [reg] */ 285290273Szbb addr = fetch_reg(cookie, (insn >> 16) & 0xf); 286290273Szbb /* ldr pc, [reg, #offset] */ 287290273Szbb if (insn & (1 << 24)) 288290273Szbb offset = insn & 0xfff; 289290273Szbb if (insn & 0x00800000) 290290273Szbb addr += offset; 291290273Szbb else 292290273Szbb addr -= offset; 293290273Szbb error = read_int(cookie, addr, &addr); 294290273Szbb *new_pc = addr; 295290273Szbb 296290273Szbb return (error); 297290273Szbb case 0x8: /* ldmxx reg, {..., pc} */ 298290273Szbb case 0x9: 299290273Szbb addr = fetch_reg(cookie, (insn >> 16) & 0xf); 300290273Szbb nregs = (insn & 0x5555) + ((insn >> 1) & 0x5555); 301290273Szbb nregs = (nregs & 0x3333) + ((nregs >> 2) & 0x3333); 302290273Szbb nregs = (nregs + (nregs >> 4)) & 0x0f0f; 303290273Szbb nregs = (nregs + (nregs >> 8)) & 0x001f; 304290273Szbb switch ((insn >> 23) & 0x3) { 305290273Szbb case 0x0: /* ldmda */ 306290273Szbb addr = addr - 0; 307290273Szbb break; 308290273Szbb case 0x1: /* ldmia */ 309290273Szbb addr = addr + 0 + ((nregs - 1) << 2); 310290273Szbb break; 311290273Szbb case 0x2: /* ldmdb */ 312290273Szbb addr = addr - 4; 313290273Szbb break; 314290273Szbb case 0x3: /* ldmib */ 315290273Szbb addr = addr + 4 + ((nregs - 1) << 2); 316290273Szbb break; 317290273Szbb } 318290273Szbb error = read_int(cookie, addr, &addr); 319290273Szbb *new_pc = addr; 320290273Szbb 321290273Szbb return (error); 322290273Szbb default: 323290273Szbb return (EINVAL); 324290273Szbb } 325290273Szbb} 326