1/* ptrace.c: FRV specific parts of process tracing 2 * 3 * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved. 4 * Written by David Howells (dhowells@redhat.com) 5 * - Derived from arch/m68k/kernel/ptrace.c 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 10 * 2 of the License, or (at your option) any later version. 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/security.h> 21#include <linux/signal.h> 22#include <linux/regset.h> 23#include <linux/elf.h> 24#include <linux/tracehook.h> 25 26#include <asm/uaccess.h> 27#include <asm/page.h> 28#include <asm/pgtable.h> 29#include <asm/processor.h> 30#include <asm/unistd.h> 31 32/* 33 * does not yet catch signals sent when the child dies. 34 * in exit.c or in signal.c. 35 */ 36 37/* 38 * retrieve the contents of FRV userspace general registers 39 */ 40static int genregs_get(struct task_struct *target, 41 const struct user_regset *regset, 42 unsigned int pos, unsigned int count, 43 void *kbuf, void __user *ubuf) 44{ 45 const struct user_int_regs *iregs = &target->thread.user->i; 46 int ret; 47 48 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, 49 iregs, 0, sizeof(*iregs)); 50 if (ret < 0) 51 return ret; 52 53 return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 54 sizeof(*iregs), -1); 55} 56 57/* 58 * update the contents of the FRV userspace general registers 59 */ 60static int genregs_set(struct task_struct *target, 61 const struct user_regset *regset, 62 unsigned int pos, unsigned int count, 63 const void *kbuf, const void __user *ubuf) 64{ 65 struct user_int_regs *iregs = &target->thread.user->i; 66 unsigned int offs_gr0, offs_gr1; 67 int ret; 68 69 /* not allowed to set PSR or __status */ 70 if (pos < offsetof(struct user_int_regs, psr) + sizeof(long) && 71 pos + count > offsetof(struct user_int_regs, psr)) 72 return -EIO; 73 74 if (pos < offsetof(struct user_int_regs, __status) + sizeof(long) && 75 pos + count > offsetof(struct user_int_regs, __status)) 76 return -EIO; 77 78 /* set the control regs */ 79 offs_gr0 = offsetof(struct user_int_regs, gr[0]); 80 offs_gr1 = offsetof(struct user_int_regs, gr[1]); 81 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, 82 iregs, 0, offs_gr0); 83 if (ret < 0) 84 return ret; 85 86 /* skip GR0/TBR */ 87 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 88 offs_gr0, offs_gr1); 89 if (ret < 0) 90 return ret; 91 92 /* set the general regs */ 93 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, 94 &iregs->gr[1], offs_gr1, sizeof(*iregs)); 95 if (ret < 0) 96 return ret; 97 98 return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 99 sizeof(*iregs), -1); 100} 101 102/* 103 * retrieve the contents of FRV userspace FP/Media registers 104 */ 105static int fpmregs_get(struct task_struct *target, 106 const struct user_regset *regset, 107 unsigned int pos, unsigned int count, 108 void *kbuf, void __user *ubuf) 109{ 110 const struct user_fpmedia_regs *fpregs = &target->thread.user->f; 111 int ret; 112 113 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, 114 fpregs, 0, sizeof(*fpregs)); 115 if (ret < 0) 116 return ret; 117 118 return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 119 sizeof(*fpregs), -1); 120} 121 122/* 123 * update the contents of the FRV userspace FP/Media registers 124 */ 125static int fpmregs_set(struct task_struct *target, 126 const struct user_regset *regset, 127 unsigned int pos, unsigned int count, 128 const void *kbuf, const void __user *ubuf) 129{ 130 struct user_fpmedia_regs *fpregs = &target->thread.user->f; 131 int ret; 132 133 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, 134 fpregs, 0, sizeof(*fpregs)); 135 if (ret < 0) 136 return ret; 137 138 return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 139 sizeof(*fpregs), -1); 140} 141 142/* 143 * determine if the FP/Media registers have actually been used 144 */ 145static int fpmregs_active(struct task_struct *target, 146 const struct user_regset *regset) 147{ 148 return tsk_used_math(target) ? regset->n : 0; 149} 150 151/* 152 * Define the register sets available on the FRV under Linux 153 */ 154enum frv_regset { 155 REGSET_GENERAL, 156 REGSET_FPMEDIA, 157}; 158 159static const struct user_regset frv_regsets[] = { 160 /* 161 * General register format is: 162 * PSR, ISR, CCR, CCCR, LR, LCR, PC, (STATUS), SYSCALLNO, ORIG_G8 163 * GNER0-1, IACC0, TBR, GR1-63 164 */ 165 [REGSET_GENERAL] = { 166 .core_note_type = NT_PRSTATUS, 167 .n = ELF_NGREG, 168 .size = sizeof(long), 169 .align = sizeof(long), 170 .get = genregs_get, 171 .set = genregs_set, 172 }, 173 /* 174 * FPU/Media register format is: 175 * FR0-63, FNER0-1, MSR0-1, ACC0-7, ACCG0-8, FSR 176 */ 177 [REGSET_FPMEDIA] = { 178 .core_note_type = NT_PRFPREG, 179 .n = sizeof(struct user_fpmedia_regs) / sizeof(long), 180 .size = sizeof(long), 181 .align = sizeof(long), 182 .get = fpmregs_get, 183 .set = fpmregs_set, 184 .active = fpmregs_active, 185 }, 186}; 187 188static const struct user_regset_view user_frv_native_view = { 189 .name = "frv", 190 .e_machine = EM_FRV, 191 .regsets = frv_regsets, 192 .n = ARRAY_SIZE(frv_regsets), 193}; 194 195const struct user_regset_view *task_user_regset_view(struct task_struct *task) 196{ 197 return &user_frv_native_view; 198} 199 200/* 201 * Get contents of register REGNO in task TASK. 202 */ 203static inline long get_reg(struct task_struct *task, int regno) 204{ 205 struct user_context *user = task->thread.user; 206 207 if (regno < 0 || regno >= PT__END) 208 return 0; 209 210 return ((unsigned long *) user)[regno]; 211} 212 213/* 214 * Write contents of register REGNO in task TASK. 215 */ 216static inline int put_reg(struct task_struct *task, int regno, 217 unsigned long data) 218{ 219 struct user_context *user = task->thread.user; 220 221 if (regno < 0 || regno >= PT__END) 222 return -EIO; 223 224 switch (regno) { 225 case PT_GR(0): 226 return 0; 227 case PT_PSR: 228 case PT__STATUS: 229 return -EIO; 230 default: 231 ((unsigned long *) user)[regno] = data; 232 return 0; 233 } 234} 235 236/* 237 * Called by kernel/ptrace.c when detaching.. 238 * 239 * Control h/w single stepping 240 */ 241void user_enable_single_step(struct task_struct *child) 242{ 243 child->thread.frame0->__status |= REG__STATUS_STEP; 244} 245 246void user_disable_single_step(struct task_struct *child) 247{ 248 child->thread.frame0->__status &= ~REG__STATUS_STEP; 249} 250 251void ptrace_disable(struct task_struct *child) 252{ 253 user_disable_single_step(child); 254} 255 256long arch_ptrace(struct task_struct *child, long request, 257 unsigned long addr, unsigned long data) 258{ 259 unsigned long tmp; 260 int ret; 261 int regno = addr >> 2; 262 unsigned long __user *datap = (unsigned long __user *) data; 263 264 switch (request) { 265 /* read the word at location addr in the USER area. */ 266 case PTRACE_PEEKUSR: { 267 tmp = 0; 268 ret = -EIO; 269 if (addr & 3) 270 break; 271 272 ret = 0; 273 switch (regno) { 274 case 0 ... PT__END - 1: 275 tmp = get_reg(child, regno); 276 break; 277 278 case PT__END + 0: 279 tmp = child->mm->end_code - child->mm->start_code; 280 break; 281 282 case PT__END + 1: 283 tmp = child->mm->end_data - child->mm->start_data; 284 break; 285 286 case PT__END + 2: 287 tmp = child->mm->start_stack - child->mm->start_brk; 288 break; 289 290 case PT__END + 3: 291 tmp = child->mm->start_code; 292 break; 293 294 case PT__END + 4: 295 tmp = child->mm->start_stack; 296 break; 297 298 default: 299 ret = -EIO; 300 break; 301 } 302 303 if (ret == 0) 304 ret = put_user(tmp, datap); 305 break; 306 } 307 308 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ 309 ret = -EIO; 310 if (addr & 3) 311 break; 312 313 switch (regno) { 314 case 0 ... PT__END - 1: 315 ret = put_reg(child, regno, data); 316 break; 317 } 318 break; 319 320 case PTRACE_GETREGS: /* Get all integer regs from the child. */ 321 return copy_regset_to_user(child, &user_frv_native_view, 322 REGSET_GENERAL, 323 0, sizeof(child->thread.user->i), 324 datap); 325 326 case PTRACE_SETREGS: /* Set all integer regs in the child. */ 327 return copy_regset_from_user(child, &user_frv_native_view, 328 REGSET_GENERAL, 329 0, sizeof(child->thread.user->i), 330 datap); 331 332 case PTRACE_GETFPREGS: /* Get the child FP/Media state. */ 333 return copy_regset_to_user(child, &user_frv_native_view, 334 REGSET_FPMEDIA, 335 0, sizeof(child->thread.user->f), 336 datap); 337 338 case PTRACE_SETFPREGS: /* Set the child FP/Media state. */ 339 return copy_regset_from_user(child, &user_frv_native_view, 340 REGSET_FPMEDIA, 341 0, sizeof(child->thread.user->f), 342 datap); 343 344 default: 345 ret = ptrace_request(child, request, addr, data); 346 break; 347 } 348 return ret; 349} 350 351/* 352 * handle tracing of system call entry 353 * - return the revised system call number or ULONG_MAX to cause ENOSYS 354 */ 355asmlinkage unsigned long syscall_trace_entry(void) 356{ 357 __frame->__status |= REG__STATUS_SYSC_ENTRY; 358 if (tracehook_report_syscall_entry(__frame)) { 359 /* tracing decided this syscall should not happen, so 360 * We'll return a bogus call number to get an ENOSYS 361 * error, but leave the original number in 362 * __frame->syscallno 363 */ 364 return ULONG_MAX; 365 } 366 367 return __frame->syscallno; 368} 369 370/* 371 * handle tracing of system call exit 372 */ 373asmlinkage void syscall_trace_exit(void) 374{ 375 __frame->__status |= REG__STATUS_SYSC_EXIT; 376 tracehook_report_syscall_exit(__frame, 0); 377} 378