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 89void user_enable_single_step(struct task_struct *task) 90{ 91 unsigned long srflags; 92 srflags = get_reg(task, PT_SR) | (TRACE_BITS << 16); 93 put_reg(task, PT_SR, srflags); 94} 95 96void user_disable_single_step(struct task_struct *task) 97{ 98 unsigned long srflags; 99 srflags = get_reg(task, PT_SR) & ~(TRACE_BITS << 16); 100 put_reg(task, PT_SR, srflags); 101} 102 103/* 104 * Called by kernel/ptrace.c when detaching.. 105 * 106 * Make sure the single step bit is not set. 107 */ 108void ptrace_disable(struct task_struct *child) 109{ 110 /* make sure the single step bit is not set. */ 111 user_disable_single_step(child); 112} 113 114long arch_ptrace(struct task_struct *child, long request, long addr, long data) 115{ 116 int ret; 117 118 switch (request) { 119 /* read the word at location addr in the USER area. */ 120 case PTRACE_PEEKUSR: { 121 unsigned long tmp; 122 123 ret = -EIO; 124 if ((addr & 3) || addr < 0 || 125 addr > sizeof(struct user) - 3) 126 break; 127 128 tmp = 0; /* Default return condition */ 129 addr = addr >> 2; /* temporary hack. */ 130 ret = -EIO; 131 if (addr < 19) { 132 tmp = get_reg(child, addr); 133 if (addr == PT_SR) 134 tmp >>= 16; 135 } else if (addr >= 21 && addr < 49) { 136 tmp = child->thread.fp[addr - 21]; 137#ifdef CONFIG_M68KFPU_EMU 138 /* Convert internal fpu reg representation 139 * into long double format 140 */ 141 if (FPU_IS_EMU && (addr < 45) && !(addr % 3)) 142 tmp = ((tmp & 0xffff0000) << 15) | 143 ((tmp & 0x0000ffff) << 16); 144#endif 145 } else if (addr == 49) { 146 tmp = child->mm->start_code; 147 } else if (addr == 50) { 148 tmp = child->mm->start_data; 149 } else if (addr == 51) { 150 tmp = child->mm->end_code; 151 } else 152 break; 153 ret = put_user(tmp,(unsigned long *) data); 154 break; 155 } 156 157 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ 158 ret = -EIO; 159 if ((addr & 3) || addr < 0 || 160 addr > sizeof(struct user) - 3) 161 break; 162 163 addr = addr >> 2; /* temporary hack. */ 164 165 if (addr == PT_SR) { 166 data &= SR_MASK; 167 data <<= 16; 168 data |= get_reg(child, PT_SR) & ~(SR_MASK << 16); 169 } 170 if (addr < 19) { 171 if (put_reg(child, addr, data)) 172 break; 173 ret = 0; 174 break; 175 } 176 if (addr >= 21 && addr < 48) 177 { 178#ifdef CONFIG_M68KFPU_EMU 179 /* Convert long double format 180 * into internal fpu reg representation 181 */ 182 if (FPU_IS_EMU && (addr < 45) && !(addr % 3)) { 183 data = (unsigned long)data << 15; 184 data = (data & 0xffff0000) | 185 ((data & 0x0000ffff) >> 1); 186 } 187#endif 188 child->thread.fp[addr - 21] = data; 189 ret = 0; 190 } 191 break; 192 193 case PTRACE_GETREGS: { /* Get all gp regs from the child. */ 194 int i; 195 unsigned long tmp; 196 for (i = 0; i < 19; i++) { 197 tmp = get_reg(child, i); 198 if (i == PT_SR) 199 tmp >>= 16; 200 if (put_user(tmp, (unsigned long *) data)) { 201 ret = -EFAULT; 202 break; 203 } 204 data += sizeof(long); 205 } 206 ret = 0; 207 break; 208 } 209 210 case PTRACE_SETREGS: { /* Set all gp regs in the child. */ 211 int i; 212 unsigned long tmp; 213 for (i = 0; i < 19; i++) { 214 if (get_user(tmp, (unsigned long *) data)) { 215 ret = -EFAULT; 216 break; 217 } 218 if (i == PT_SR) { 219 tmp &= SR_MASK; 220 tmp <<= 16; 221 tmp |= get_reg(child, PT_SR) & ~(SR_MASK << 16); 222 } 223 put_reg(child, i, tmp); 224 data += sizeof(long); 225 } 226 ret = 0; 227 break; 228 } 229 230#ifdef PTRACE_GETFPREGS 231 case PTRACE_GETFPREGS: { /* Get the child FPU state. */ 232 ret = 0; 233 if (copy_to_user((void *)data, &child->thread.fp, 234 sizeof(struct user_m68kfp_struct))) 235 ret = -EFAULT; 236 break; 237 } 238#endif 239 240#ifdef PTRACE_SETFPREGS 241 case PTRACE_SETFPREGS: { /* Set the child FPU state. */ 242 ret = 0; 243 if (copy_from_user(&child->thread.fp, (void *)data, 244 sizeof(struct user_m68kfp_struct))) 245 ret = -EFAULT; 246 break; 247 } 248#endif 249 250 case PTRACE_GET_THREAD_AREA: 251 ret = put_user(task_thread_info(child)->tp_value, 252 (unsigned long __user *)data); 253 break; 254 255 default: 256 ret = ptrace_request(child, request, addr, data); 257 break; 258 } 259 return ret; 260} 261 262asmlinkage void syscall_trace(void) 263{ 264 if (!test_thread_flag(TIF_SYSCALL_TRACE)) 265 return; 266 if (!(current->ptrace & PT_PTRACED)) 267 return; 268 ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) 269 ? 0x80 : 0)); 270 /* 271 * this isn't the same as continuing with a signal, but it will do 272 * for normal use. strace only continues with a signal if the 273 * stopping signal is not SIGTRAP. -brl 274 */ 275 if (current->exit_code) { 276 send_sig(current->exit_code, current, 1); 277 current->exit_code = 0; 278 } 279} 280