1da9a1c10SChristophe Leroy /* 2da9a1c10SChristophe Leroy * ptrace for 32-bit processes running on a 64-bit kernel. 3da9a1c10SChristophe Leroy * 4da9a1c10SChristophe Leroy * PowerPC version 5da9a1c10SChristophe Leroy * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) 6da9a1c10SChristophe Leroy * 7da9a1c10SChristophe Leroy * Derived from "arch/m68k/kernel/ptrace.c" 8da9a1c10SChristophe Leroy * Copyright (C) 1994 by Hamish Macdonald 9da9a1c10SChristophe Leroy * Taken from linux/kernel/ptrace.c and modified for M680x0. 10da9a1c10SChristophe Leroy * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds 11da9a1c10SChristophe Leroy * 12da9a1c10SChristophe Leroy * Modified by Cort Dougan (cort@hq.fsmlabs.com) 13da9a1c10SChristophe Leroy * and Paul Mackerras (paulus@samba.org). 14da9a1c10SChristophe Leroy * 15da9a1c10SChristophe Leroy * This file is subject to the terms and conditions of the GNU General 16da9a1c10SChristophe Leroy * Public License. See the file COPYING in the main directory of 17da9a1c10SChristophe Leroy * this archive for more details. 18da9a1c10SChristophe Leroy */ 19da9a1c10SChristophe Leroy 20da9a1c10SChristophe Leroy #include <linux/ptrace.h> 21da9a1c10SChristophe Leroy #include <linux/regset.h> 22da9a1c10SChristophe Leroy #include <linux/compat.h> 23da9a1c10SChristophe Leroy 24da9a1c10SChristophe Leroy #include <asm/switch_to.h> 25da9a1c10SChristophe Leroy 26*67e364b3SChristophe Leroy #include "ptrace-decl.h" 27*67e364b3SChristophe Leroy 28da9a1c10SChristophe Leroy /* 29da9a1c10SChristophe Leroy * does not yet catch signals sent when the child dies. 30da9a1c10SChristophe Leroy * in exit.c or in signal.c. 31da9a1c10SChristophe Leroy */ 32da9a1c10SChristophe Leroy 33da9a1c10SChristophe Leroy /* Macros to workout the correct index for the FPR in the thread struct */ 34da9a1c10SChristophe Leroy #define FPRNUMBER(i) (((i) - PT_FPR0) >> 1) 35da9a1c10SChristophe Leroy #define FPRHALF(i) (((i) - PT_FPR0) & 1) 36da9a1c10SChristophe Leroy #define FPRINDEX(i) TS_FPRWIDTH * FPRNUMBER(i) * 2 + FPRHALF(i) 37da9a1c10SChristophe Leroy 38da9a1c10SChristophe Leroy long compat_arch_ptrace(struct task_struct *child, compat_long_t request, 39da9a1c10SChristophe Leroy compat_ulong_t caddr, compat_ulong_t cdata) 40da9a1c10SChristophe Leroy { 41da9a1c10SChristophe Leroy unsigned long addr = caddr; 42da9a1c10SChristophe Leroy unsigned long data = cdata; 43da9a1c10SChristophe Leroy int ret; 44da9a1c10SChristophe Leroy 45da9a1c10SChristophe Leroy switch (request) { 46da9a1c10SChristophe Leroy /* 47da9a1c10SChristophe Leroy * Read 4 bytes of the other process' storage 48da9a1c10SChristophe Leroy * data is a pointer specifying where the user wants the 49da9a1c10SChristophe Leroy * 4 bytes copied into 50da9a1c10SChristophe Leroy * addr is a pointer in the user's storage that contains an 8 byte 51da9a1c10SChristophe Leroy * address in the other process of the 4 bytes that is to be read 52da9a1c10SChristophe Leroy * (this is run in a 32-bit process looking at a 64-bit process) 53da9a1c10SChristophe Leroy * when I and D space are separate, these will need to be fixed. 54da9a1c10SChristophe Leroy */ 55da9a1c10SChristophe Leroy case PPC_PTRACE_PEEKTEXT_3264: 56da9a1c10SChristophe Leroy case PPC_PTRACE_PEEKDATA_3264: { 57da9a1c10SChristophe Leroy u32 tmp; 58da9a1c10SChristophe Leroy int copied; 59da9a1c10SChristophe Leroy u32 __user * addrOthers; 60da9a1c10SChristophe Leroy 61da9a1c10SChristophe Leroy ret = -EIO; 62da9a1c10SChristophe Leroy 63da9a1c10SChristophe Leroy /* Get the addr in the other process that we want to read */ 64da9a1c10SChristophe Leroy if (get_user(addrOthers, (u32 __user * __user *)addr) != 0) 65da9a1c10SChristophe Leroy break; 66da9a1c10SChristophe Leroy 67da9a1c10SChristophe Leroy copied = ptrace_access_vm(child, (u64)addrOthers, &tmp, 68da9a1c10SChristophe Leroy sizeof(tmp), FOLL_FORCE); 69da9a1c10SChristophe Leroy if (copied != sizeof(tmp)) 70da9a1c10SChristophe Leroy break; 71da9a1c10SChristophe Leroy ret = put_user(tmp, (u32 __user *)data); 72da9a1c10SChristophe Leroy break; 73da9a1c10SChristophe Leroy } 74da9a1c10SChristophe Leroy 75da9a1c10SChristophe Leroy /* Read a register (specified by ADDR) out of the "user area" */ 76da9a1c10SChristophe Leroy case PTRACE_PEEKUSR: { 77da9a1c10SChristophe Leroy int index; 78da9a1c10SChristophe Leroy unsigned long tmp; 79da9a1c10SChristophe Leroy 80da9a1c10SChristophe Leroy ret = -EIO; 81da9a1c10SChristophe Leroy /* convert to index and check */ 82da9a1c10SChristophe Leroy index = (unsigned long) addr >> 2; 83da9a1c10SChristophe Leroy if ((addr & 3) || (index > PT_FPSCR32)) 84da9a1c10SChristophe Leroy break; 85da9a1c10SChristophe Leroy 86da9a1c10SChristophe Leroy CHECK_FULL_REGS(child->thread.regs); 87da9a1c10SChristophe Leroy if (index < PT_FPR0) { 88da9a1c10SChristophe Leroy ret = ptrace_get_reg(child, index, &tmp); 89da9a1c10SChristophe Leroy if (ret) 90da9a1c10SChristophe Leroy break; 91da9a1c10SChristophe Leroy } else { 92da9a1c10SChristophe Leroy flush_fp_to_thread(child); 93da9a1c10SChristophe Leroy /* 94da9a1c10SChristophe Leroy * the user space code considers the floating point 95da9a1c10SChristophe Leroy * to be an array of unsigned int (32 bits) - the 96da9a1c10SChristophe Leroy * index passed in is based on this assumption. 97da9a1c10SChristophe Leroy */ 98da9a1c10SChristophe Leroy tmp = ((unsigned int *)child->thread.fp_state.fpr) 99da9a1c10SChristophe Leroy [FPRINDEX(index)]; 100da9a1c10SChristophe Leroy } 101da9a1c10SChristophe Leroy ret = put_user((unsigned int)tmp, (u32 __user *)data); 102da9a1c10SChristophe Leroy break; 103da9a1c10SChristophe Leroy } 104da9a1c10SChristophe Leroy 105da9a1c10SChristophe Leroy /* 106da9a1c10SChristophe Leroy * Read 4 bytes out of the other process' pt_regs area 107da9a1c10SChristophe Leroy * data is a pointer specifying where the user wants the 108da9a1c10SChristophe Leroy * 4 bytes copied into 109da9a1c10SChristophe Leroy * addr is the offset into the other process' pt_regs structure 110da9a1c10SChristophe Leroy * that is to be read 111da9a1c10SChristophe Leroy * (this is run in a 32-bit process looking at a 64-bit process) 112da9a1c10SChristophe Leroy */ 113da9a1c10SChristophe Leroy case PPC_PTRACE_PEEKUSR_3264: { 114da9a1c10SChristophe Leroy u32 index; 115da9a1c10SChristophe Leroy u32 reg32bits; 116da9a1c10SChristophe Leroy u64 tmp; 117da9a1c10SChristophe Leroy u32 numReg; 118da9a1c10SChristophe Leroy u32 part; 119da9a1c10SChristophe Leroy 120da9a1c10SChristophe Leroy ret = -EIO; 121da9a1c10SChristophe Leroy /* Determine which register the user wants */ 122da9a1c10SChristophe Leroy index = (u64)addr >> 2; 123da9a1c10SChristophe Leroy numReg = index / 2; 124da9a1c10SChristophe Leroy /* Determine which part of the register the user wants */ 125da9a1c10SChristophe Leroy if (index % 2) 126da9a1c10SChristophe Leroy part = 1; /* want the 2nd half of the register (right-most). */ 127da9a1c10SChristophe Leroy else 128da9a1c10SChristophe Leroy part = 0; /* want the 1st half of the register (left-most). */ 129da9a1c10SChristophe Leroy 130da9a1c10SChristophe Leroy /* Validate the input - check to see if address is on the wrong boundary 131da9a1c10SChristophe Leroy * or beyond the end of the user area 132da9a1c10SChristophe Leroy */ 133da9a1c10SChristophe Leroy if ((addr & 3) || numReg > PT_FPSCR) 134da9a1c10SChristophe Leroy break; 135da9a1c10SChristophe Leroy 136da9a1c10SChristophe Leroy CHECK_FULL_REGS(child->thread.regs); 137da9a1c10SChristophe Leroy if (numReg >= PT_FPR0) { 138da9a1c10SChristophe Leroy flush_fp_to_thread(child); 139da9a1c10SChristophe Leroy /* get 64 bit FPR */ 140da9a1c10SChristophe Leroy tmp = child->thread.fp_state.fpr[numReg - PT_FPR0][0]; 141da9a1c10SChristophe Leroy } else { /* register within PT_REGS struct */ 142da9a1c10SChristophe Leroy unsigned long tmp2; 143da9a1c10SChristophe Leroy ret = ptrace_get_reg(child, numReg, &tmp2); 144da9a1c10SChristophe Leroy if (ret) 145da9a1c10SChristophe Leroy break; 146da9a1c10SChristophe Leroy tmp = tmp2; 147da9a1c10SChristophe Leroy } 148da9a1c10SChristophe Leroy reg32bits = ((u32*)&tmp)[part]; 149da9a1c10SChristophe Leroy ret = put_user(reg32bits, (u32 __user *)data); 150da9a1c10SChristophe Leroy break; 151da9a1c10SChristophe Leroy } 152da9a1c10SChristophe Leroy 153da9a1c10SChristophe Leroy /* 154da9a1c10SChristophe Leroy * Write 4 bytes into the other process' storage 155da9a1c10SChristophe Leroy * data is the 4 bytes that the user wants written 156da9a1c10SChristophe Leroy * addr is a pointer in the user's storage that contains an 157da9a1c10SChristophe Leroy * 8 byte address in the other process where the 4 bytes 158da9a1c10SChristophe Leroy * that is to be written 159da9a1c10SChristophe Leroy * (this is run in a 32-bit process looking at a 64-bit process) 160da9a1c10SChristophe Leroy * when I and D space are separate, these will need to be fixed. 161da9a1c10SChristophe Leroy */ 162da9a1c10SChristophe Leroy case PPC_PTRACE_POKETEXT_3264: 163da9a1c10SChristophe Leroy case PPC_PTRACE_POKEDATA_3264: { 164da9a1c10SChristophe Leroy u32 tmp = data; 165da9a1c10SChristophe Leroy u32 __user * addrOthers; 166da9a1c10SChristophe Leroy 167da9a1c10SChristophe Leroy /* Get the addr in the other process that we want to write into */ 168da9a1c10SChristophe Leroy ret = -EIO; 169da9a1c10SChristophe Leroy if (get_user(addrOthers, (u32 __user * __user *)addr) != 0) 170da9a1c10SChristophe Leroy break; 171da9a1c10SChristophe Leroy ret = 0; 172da9a1c10SChristophe Leroy if (ptrace_access_vm(child, (u64)addrOthers, &tmp, 173da9a1c10SChristophe Leroy sizeof(tmp), 174da9a1c10SChristophe Leroy FOLL_FORCE | FOLL_WRITE) == sizeof(tmp)) 175da9a1c10SChristophe Leroy break; 176da9a1c10SChristophe Leroy ret = -EIO; 177da9a1c10SChristophe Leroy break; 178da9a1c10SChristophe Leroy } 179da9a1c10SChristophe Leroy 180da9a1c10SChristophe Leroy /* write the word at location addr in the USER area */ 181da9a1c10SChristophe Leroy case PTRACE_POKEUSR: { 182da9a1c10SChristophe Leroy unsigned long index; 183da9a1c10SChristophe Leroy 184da9a1c10SChristophe Leroy ret = -EIO; 185da9a1c10SChristophe Leroy /* convert to index and check */ 186da9a1c10SChristophe Leroy index = (unsigned long) addr >> 2; 187da9a1c10SChristophe Leroy if ((addr & 3) || (index > PT_FPSCR32)) 188da9a1c10SChristophe Leroy break; 189da9a1c10SChristophe Leroy 190da9a1c10SChristophe Leroy CHECK_FULL_REGS(child->thread.regs); 191da9a1c10SChristophe Leroy if (index < PT_FPR0) { 192da9a1c10SChristophe Leroy ret = ptrace_put_reg(child, index, data); 193da9a1c10SChristophe Leroy } else { 194da9a1c10SChristophe Leroy flush_fp_to_thread(child); 195da9a1c10SChristophe Leroy /* 196da9a1c10SChristophe Leroy * the user space code considers the floating point 197da9a1c10SChristophe Leroy * to be an array of unsigned int (32 bits) - the 198da9a1c10SChristophe Leroy * index passed in is based on this assumption. 199da9a1c10SChristophe Leroy */ 200da9a1c10SChristophe Leroy ((unsigned int *)child->thread.fp_state.fpr) 201da9a1c10SChristophe Leroy [FPRINDEX(index)] = data; 202da9a1c10SChristophe Leroy ret = 0; 203da9a1c10SChristophe Leroy } 204da9a1c10SChristophe Leroy break; 205da9a1c10SChristophe Leroy } 206da9a1c10SChristophe Leroy 207da9a1c10SChristophe Leroy /* 208da9a1c10SChristophe Leroy * Write 4 bytes into the other process' pt_regs area 209da9a1c10SChristophe Leroy * data is the 4 bytes that the user wants written 210da9a1c10SChristophe Leroy * addr is the offset into the other process' pt_regs structure 211da9a1c10SChristophe Leroy * that is to be written into 212da9a1c10SChristophe Leroy * (this is run in a 32-bit process looking at a 64-bit process) 213da9a1c10SChristophe Leroy */ 214da9a1c10SChristophe Leroy case PPC_PTRACE_POKEUSR_3264: { 215da9a1c10SChristophe Leroy u32 index; 216da9a1c10SChristophe Leroy u32 numReg; 217da9a1c10SChristophe Leroy 218da9a1c10SChristophe Leroy ret = -EIO; 219da9a1c10SChristophe Leroy /* Determine which register the user wants */ 220da9a1c10SChristophe Leroy index = (u64)addr >> 2; 221da9a1c10SChristophe Leroy numReg = index / 2; 222da9a1c10SChristophe Leroy 223da9a1c10SChristophe Leroy /* 224da9a1c10SChristophe Leroy * Validate the input - check to see if address is on the 225da9a1c10SChristophe Leroy * wrong boundary or beyond the end of the user area 226da9a1c10SChristophe Leroy */ 227da9a1c10SChristophe Leroy if ((addr & 3) || (numReg > PT_FPSCR)) 228da9a1c10SChristophe Leroy break; 229da9a1c10SChristophe Leroy CHECK_FULL_REGS(child->thread.regs); 230da9a1c10SChristophe Leroy if (numReg < PT_FPR0) { 231da9a1c10SChristophe Leroy unsigned long freg; 232da9a1c10SChristophe Leroy ret = ptrace_get_reg(child, numReg, &freg); 233da9a1c10SChristophe Leroy if (ret) 234da9a1c10SChristophe Leroy break; 235da9a1c10SChristophe Leroy if (index % 2) 236da9a1c10SChristophe Leroy freg = (freg & ~0xfffffffful) | (data & 0xfffffffful); 237da9a1c10SChristophe Leroy else 238da9a1c10SChristophe Leroy freg = (freg & 0xfffffffful) | (data << 32); 239da9a1c10SChristophe Leroy ret = ptrace_put_reg(child, numReg, freg); 240da9a1c10SChristophe Leroy } else { 241da9a1c10SChristophe Leroy u64 *tmp; 242da9a1c10SChristophe Leroy flush_fp_to_thread(child); 243da9a1c10SChristophe Leroy /* get 64 bit FPR ... */ 244da9a1c10SChristophe Leroy tmp = &child->thread.fp_state.fpr[numReg - PT_FPR0][0]; 245da9a1c10SChristophe Leroy /* ... write the 32 bit part we want */ 246da9a1c10SChristophe Leroy ((u32 *)tmp)[index % 2] = data; 247da9a1c10SChristophe Leroy ret = 0; 248da9a1c10SChristophe Leroy } 249da9a1c10SChristophe Leroy break; 250da9a1c10SChristophe Leroy } 251da9a1c10SChristophe Leroy 252da9a1c10SChristophe Leroy case PTRACE_GET_DEBUGREG: { 253da9a1c10SChristophe Leroy #ifndef CONFIG_PPC_ADV_DEBUG_REGS 254da9a1c10SChristophe Leroy unsigned long dabr_fake; 255da9a1c10SChristophe Leroy #endif 256da9a1c10SChristophe Leroy ret = -EINVAL; 257da9a1c10SChristophe Leroy /* We only support one DABR and no IABRS at the moment */ 258da9a1c10SChristophe Leroy if (addr > 0) 259da9a1c10SChristophe Leroy break; 260da9a1c10SChristophe Leroy #ifdef CONFIG_PPC_ADV_DEBUG_REGS 261da9a1c10SChristophe Leroy ret = put_user(child->thread.debug.dac1, (u32 __user *)data); 262da9a1c10SChristophe Leroy #else 263da9a1c10SChristophe Leroy dabr_fake = ( 264303e6a9dSRavi Bangoria (child->thread.hw_brk[0].address & (~HW_BRK_TYPE_DABR)) | 265303e6a9dSRavi Bangoria (child->thread.hw_brk[0].type & HW_BRK_TYPE_DABR)); 266da9a1c10SChristophe Leroy ret = put_user(dabr_fake, (u32 __user *)data); 267da9a1c10SChristophe Leroy #endif 268da9a1c10SChristophe Leroy break; 269da9a1c10SChristophe Leroy } 270da9a1c10SChristophe Leroy 271da9a1c10SChristophe Leroy case PTRACE_GETREGS: /* Get all pt_regs from the child. */ 272da9a1c10SChristophe Leroy return copy_regset_to_user( 273da9a1c10SChristophe Leroy child, task_user_regset_view(current), 0, 274da9a1c10SChristophe Leroy 0, PT_REGS_COUNT * sizeof(compat_long_t), 275da9a1c10SChristophe Leroy compat_ptr(data)); 276da9a1c10SChristophe Leroy 277da9a1c10SChristophe Leroy case PTRACE_SETREGS: /* Set all gp regs in the child. */ 278da9a1c10SChristophe Leroy return copy_regset_from_user( 279da9a1c10SChristophe Leroy child, task_user_regset_view(current), 0, 280da9a1c10SChristophe Leroy 0, PT_REGS_COUNT * sizeof(compat_long_t), 281da9a1c10SChristophe Leroy compat_ptr(data)); 282da9a1c10SChristophe Leroy 283da9a1c10SChristophe Leroy case PTRACE_GETFPREGS: 284da9a1c10SChristophe Leroy case PTRACE_SETFPREGS: 285da9a1c10SChristophe Leroy case PTRACE_GETVRREGS: 286da9a1c10SChristophe Leroy case PTRACE_SETVRREGS: 287da9a1c10SChristophe Leroy case PTRACE_GETVSRREGS: 288da9a1c10SChristophe Leroy case PTRACE_SETVSRREGS: 289da9a1c10SChristophe Leroy case PTRACE_GETREGS64: 290da9a1c10SChristophe Leroy case PTRACE_SETREGS64: 291da9a1c10SChristophe Leroy case PTRACE_KILL: 292da9a1c10SChristophe Leroy case PTRACE_SINGLESTEP: 293da9a1c10SChristophe Leroy case PTRACE_DETACH: 294da9a1c10SChristophe Leroy case PTRACE_SET_DEBUGREG: 295da9a1c10SChristophe Leroy case PTRACE_SYSCALL: 296da9a1c10SChristophe Leroy case PTRACE_CONT: 297da9a1c10SChristophe Leroy case PPC_PTRACE_GETHWDBGINFO: 298da9a1c10SChristophe Leroy case PPC_PTRACE_SETHWDEBUG: 299da9a1c10SChristophe Leroy case PPC_PTRACE_DELHWDEBUG: 300da9a1c10SChristophe Leroy ret = arch_ptrace(child, request, addr, data); 301da9a1c10SChristophe Leroy break; 302da9a1c10SChristophe Leroy 303da9a1c10SChristophe Leroy default: 304da9a1c10SChristophe Leroy ret = compat_ptrace_request(child, request, addr, data); 305da9a1c10SChristophe Leroy break; 306da9a1c10SChristophe Leroy } 307da9a1c10SChristophe Leroy 308da9a1c10SChristophe Leroy return ret; 309da9a1c10SChristophe Leroy } 310