1/* 2 * linux/arch/m68knommu/kernel/ptrace.c 3 * 4 * Copyright (C) 1994 by Hamish Macdonald 5 * Taken from linux/kernel/ptrace.c and modified for M680x0. 6 * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds 7 * 8 * This file is subject to the terms and conditions of the GNU General 9 * Public License. See the file COPYING in the main directory of 10 * this archive for more details. 11 */ 12 13#include <linux/kernel.h> 14#include <linux/sched.h> 15#include <linux/mm.h> 16#include <linux/smp.h> 17#include <linux/errno.h> 18#include <linux/ptrace.h> 19#include <linux/user.h> 20#include <linux/signal.h> 21 22#include <asm/uaccess.h> 23#include <asm/page.h> 24#include <asm/pgtable.h> 25#include <asm/system.h> 26#include <asm/processor.h> 27 28/* 29 * does not yet catch signals sent when the child dies. 30 * in exit.c or in signal.c. 31 */ 32 33/* determines which bits in the SR the user has access to. */ 34/* 1 = access 0 = no access */ 35#define SR_MASK 0x001f 36 37/* sets the trace bits. */ 38#define TRACE_BITS 0x8000 39 40/* Find the stack offset for a register, relative to thread.esp0. */ 41#define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg) 42#define SW_REG(reg) ((long)&((struct switch_stack *)0)->reg \ 43 - sizeof(struct switch_stack)) 44/* Mapping from PT_xxx to the stack offset at which the register is 45 saved. Notice that usp has no stack-slot and needs to be treated 46 specially (see get_reg/put_reg below). */ 47static int regoff[] = { 48 PT_REG(d1), PT_REG(d2), PT_REG(d3), PT_REG(d4), 49 PT_REG(d5), SW_REG(d6), SW_REG(d7), PT_REG(a0), 50 PT_REG(a1), PT_REG(a2), SW_REG(a3), SW_REG(a4), 51 SW_REG(a5), SW_REG(a6), PT_REG(d0), -1, 52 PT_REG(orig_d0), PT_REG(sr), PT_REG(pc), 53}; 54 55/* 56 * Get contents of register REGNO in task TASK. 57 */ 58static inline long get_reg(struct task_struct *task, int regno) 59{ 60 unsigned long *addr; 61 62 if (regno == PT_USP) 63 addr = &task->thread.usp; 64 else if (regno < ARRAY_SIZE(regoff)) 65 addr = (unsigned long *)(task->thread.esp0 + regoff[regno]); 66 else 67 return 0; 68 return *addr; 69} 70 71/* 72 * Write contents of register REGNO in task TASK. 73 */ 74static inline int put_reg(struct task_struct *task, int regno, 75 unsigned long data) 76{ 77 unsigned long *addr; 78 79 if (regno == PT_USP) 80 addr = &task->thread.usp; 81 else if (regno < ARRAY_SIZE(regoff)) 82 addr = (unsigned long *) (task->thread.esp0 + regoff[regno]); 83 else 84 return -1; 85 *addr = data; 86 return 0; 87} 88 89/* 90 * Called by kernel/ptrace.c when detaching.. 91 * 92 * Make sure the single step bit is not set. 93 */ 94void ptrace_disable(struct task_struct *child) 95{ 96 unsigned long tmp; 97 /* make sure the single step bit is not set. */ 98 tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16); 99 put_reg(child, PT_SR, tmp); 100} 101 102long arch_ptrace(struct task_struct *child, long request, long addr, long data) 103{ 104 int ret; 105 106 switch (request) { 107 /* when I and D space are separate, these will need to be fixed. */ 108 case PTRACE_PEEKTEXT: /* read word at location addr. */ 109 case PTRACE_PEEKDATA: { 110 unsigned long tmp; 111 int copied; 112 113 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); 114 ret = -EIO; 115 if (copied != sizeof(tmp)) 116 break; 117 ret = put_user(tmp,(unsigned long *) data); 118 break; 119 } 120 121 /* read the word at location addr in the USER area. */ 122 case PTRACE_PEEKUSR: { 123 unsigned long tmp; 124 125 ret = -EIO; 126 if ((addr & 3) || addr < 0 || 127 addr > sizeof(struct user) - 3) 128 break; 129 130 tmp = 0; /* Default return condition */ 131 addr = addr >> 2; /* temporary hack. */ 132 ret = -EIO; 133 if (addr < 19) { 134 tmp = get_reg(child, addr); 135 if (addr == PT_SR) 136 tmp >>= 16; 137 } else if (addr >= 21 && addr < 49) { 138 tmp = child->thread.fp[addr - 21]; 139#ifdef CONFIG_M68KFPU_EMU 140 /* Convert internal fpu reg representation 141 * into long double format 142 */ 143 if (FPU_IS_EMU && (addr < 45) && !(addr % 3)) 144 tmp = ((tmp & 0xffff0000) << 15) | 145 ((tmp & 0x0000ffff) << 16); 146#endif 147 } else if (addr == 49) { 148 tmp = child->mm->start_code; 149 } else if (addr == 50) { 150 tmp = child->mm->start_data; 151 } else if (addr == 51) { 152 tmp = child->mm->end_code; 153 } else 154 break; 155 ret = put_user(tmp,(unsigned long *) data); 156 break; 157 } 158 159 /* when I and D space are separate, this will have to be fixed. */ 160 case PTRACE_POKETEXT: /* write the word at location addr. */ 161 case PTRACE_POKEDATA: 162 ret = 0; 163 if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) 164 break; 165 ret = -EIO; 166 break; 167 168 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ 169 ret = -EIO; 170 if ((addr & 3) || addr < 0 || 171 addr > sizeof(struct user) - 3) 172 break; 173 174 addr = addr >> 2; /* temporary hack. */ 175 176 if (addr == PT_SR) { 177 data &= SR_MASK; 178 data <<= 16; 179 data |= get_reg(child, PT_SR) & ~(SR_MASK << 16); 180 } 181 if (addr < 19) { 182 if (put_reg(child, addr, data)) 183 break; 184 ret = 0; 185 break; 186 } 187 if (addr >= 21 && addr < 48) 188 { 189#ifdef CONFIG_M68KFPU_EMU 190 /* Convert long double format 191 * into internal fpu reg representation 192 */ 193 if (FPU_IS_EMU && (addr < 45) && !(addr % 3)) { 194 data = (unsigned long)data << 15; 195 data = (data & 0xffff0000) | 196 ((data & 0x0000ffff) >> 1); 197 } 198#endif 199 child->thread.fp[addr - 21] = data; 200 ret = 0; 201 } 202 break; 203 204 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ 205 case PTRACE_CONT: { /* restart after signal. */ 206 long tmp; 207 208 ret = -EIO; 209 if (!valid_signal(data)) 210 break; 211 if (request == PTRACE_SYSCALL) 212 set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); 213 else 214 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); 215 child->exit_code = data; 216 /* make sure the single step bit is not set. */ 217 tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16); 218 put_reg(child, PT_SR, tmp); 219 wake_up_process(child); 220 ret = 0; 221 break; 222 } 223 224 /* 225 * make the child exit. Best I can do is send it a sigkill. 226 * perhaps it should be put in the status that it wants to 227 * exit. 228 */ 229 case PTRACE_KILL: { 230 long tmp; 231 232 ret = 0; 233 if (child->exit_state == EXIT_ZOMBIE) /* already dead */ 234 break; 235 child->exit_code = SIGKILL; 236 /* make sure the single step bit is not set. */ 237 tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16); 238 put_reg(child, PT_SR, tmp); 239 wake_up_process(child); 240 break; 241 } 242 243 case PTRACE_SINGLESTEP: { /* set the trap flag. */ 244 long tmp; 245 246 ret = -EIO; 247 if (!valid_signal(data)) 248 break; 249 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); 250 tmp = get_reg(child, PT_SR) | (TRACE_BITS << 16); 251 put_reg(child, PT_SR, tmp); 252 253 child->exit_code = data; 254 /* give it a chance to run. */ 255 wake_up_process(child); 256 ret = 0; 257 break; 258 } 259 260 case PTRACE_DETACH: /* detach a process that was attached. */ 261 ret = ptrace_detach(child, data); 262 break; 263 264 case PTRACE_GETREGS: { /* Get all gp regs from the child. */ 265 int i; 266 unsigned long tmp; 267 for (i = 0; i < 19; i++) { 268 tmp = get_reg(child, i); 269 if (i == PT_SR) 270 tmp >>= 16; 271 if (put_user(tmp, (unsigned long *) data)) { 272 ret = -EFAULT; 273 break; 274 } 275 data += sizeof(long); 276 } 277 ret = 0; 278 break; 279 } 280 281 case PTRACE_SETREGS: { /* Set all gp regs in the child. */ 282 int i; 283 unsigned long tmp; 284 for (i = 0; i < 19; i++) { 285 if (get_user(tmp, (unsigned long *) data)) { 286 ret = -EFAULT; 287 break; 288 } 289 if (i == PT_SR) { 290 tmp &= SR_MASK; 291 tmp <<= 16; 292 tmp |= get_reg(child, PT_SR) & ~(SR_MASK << 16); 293 } 294 put_reg(child, i, tmp); 295 data += sizeof(long); 296 } 297 ret = 0; 298 break; 299 } 300 301#ifdef PTRACE_GETFPREGS 302 case PTRACE_GETFPREGS: { /* Get the child FPU state. */ 303 ret = 0; 304 if (copy_to_user((void *)data, &child->thread.fp, 305 sizeof(struct user_m68kfp_struct))) 306 ret = -EFAULT; 307 break; 308 } 309#endif 310 311#ifdef PTRACE_SETFPREGS 312 case PTRACE_SETFPREGS: { /* Set the child FPU state. */ 313 ret = 0; 314 if (copy_from_user(&child->thread.fp, (void *)data, 315 sizeof(struct user_m68kfp_struct))) 316 ret = -EFAULT; 317 break; 318 } 319#endif 320 321 default: 322 ret = -EIO; 323 break; 324 } 325 return ret; 326} 327 328asmlinkage void syscall_trace(void) 329{ 330 if (!test_thread_flag(TIF_SYSCALL_TRACE)) 331 return; 332 if (!(current->ptrace & PT_PTRACED)) 333 return; 334 ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) 335 ? 0x80 : 0)); 336 /* 337 * this isn't the same as continuing with a signal, but it will do 338 * for normal use. strace only continues with a signal if the 339 * stopping signal is not SIGTRAP. -brl 340 */ 341 if (current->exit_code) { 342 send_sig(current->exit_code, current, 1); 343 current->exit_code = 0; 344 } 345} 346