1/* ptrace.c: Sparc process tracing support. 2 * 3 * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net) 4 * 5 * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, 6 * and David Mosberger. 7 * 8 * Added Linux support -miguel (weird, eh?, the original code was meant 9 * to emulate SunOS). 10 */ 11 12#include <linux/kernel.h> 13#include <linux/sched.h> 14#include <linux/mm.h> 15#include <linux/errno.h> 16#include <linux/ptrace.h> 17#include <linux/user.h> 18#include <linux/smp.h> 19#include <linux/security.h> 20#include <linux/signal.h> 21#include <linux/regset.h> 22#include <linux/elf.h> 23#include <linux/tracehook.h> 24 25#include <asm/pgtable.h> 26#include <asm/uaccess.h> 27#include <asm/cacheflush.h> 28 29#include "kernel.h" 30 31/* #define ALLOW_INIT_TRACING */ 32 33/* 34 * Called by kernel/ptrace.c when detaching.. 35 * 36 * Make sure single step bits etc are not set. 37 */ 38void ptrace_disable(struct task_struct *child) 39{ 40 /* nothing to do */ 41} 42 43enum sparc_regset { 44 REGSET_GENERAL, 45 REGSET_FP, 46}; 47 48static int genregs32_get(struct task_struct *target, 49 const struct user_regset *regset, 50 unsigned int pos, unsigned int count, 51 void *kbuf, void __user *ubuf) 52{ 53 const struct pt_regs *regs = target->thread.kregs; 54 unsigned long __user *reg_window; 55 unsigned long *k = kbuf; 56 unsigned long __user *u = ubuf; 57 unsigned long reg; 58 59 if (target == current) 60 flush_user_windows(); 61 62 pos /= sizeof(reg); 63 count /= sizeof(reg); 64 65 if (kbuf) { 66 for (; count > 0 && pos < 16; count--) 67 *k++ = regs->u_regs[pos++]; 68 69 reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; 70 reg_window -= 16; 71 for (; count > 0 && pos < 32; count--) { 72 if (get_user(*k++, ®_window[pos++])) 73 return -EFAULT; 74 } 75 } else { 76 for (; count > 0 && pos < 16; count--) { 77 if (put_user(regs->u_regs[pos++], u++)) 78 return -EFAULT; 79 } 80 81 reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; 82 reg_window -= 16; 83 for (; count > 0 && pos < 32; count--) { 84 if (get_user(reg, ®_window[pos++]) || 85 put_user(reg, u++)) 86 return -EFAULT; 87 } 88 } 89 while (count > 0) { 90 switch (pos) { 91 case 32: /* PSR */ 92 reg = regs->psr; 93 break; 94 case 33: /* PC */ 95 reg = regs->pc; 96 break; 97 case 34: /* NPC */ 98 reg = regs->npc; 99 break; 100 case 35: /* Y */ 101 reg = regs->y; 102 break; 103 case 36: /* WIM */ 104 case 37: /* TBR */ 105 reg = 0; 106 break; 107 default: 108 goto finish; 109 } 110 111 if (kbuf) 112 *k++ = reg; 113 else if (put_user(reg, u++)) 114 return -EFAULT; 115 pos++; 116 count--; 117 } 118finish: 119 pos *= sizeof(reg); 120 count *= sizeof(reg); 121 122 return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 123 38 * sizeof(reg), -1); 124} 125 126static int genregs32_set(struct task_struct *target, 127 const struct user_regset *regset, 128 unsigned int pos, unsigned int count, 129 const void *kbuf, const void __user *ubuf) 130{ 131 struct pt_regs *regs = target->thread.kregs; 132 unsigned long __user *reg_window; 133 const unsigned long *k = kbuf; 134 const unsigned long __user *u = ubuf; 135 unsigned long reg; 136 137 if (target == current) 138 flush_user_windows(); 139 140 pos /= sizeof(reg); 141 count /= sizeof(reg); 142 143 if (kbuf) { 144 for (; count > 0 && pos < 16; count--) 145 regs->u_regs[pos++] = *k++; 146 147 reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; 148 reg_window -= 16; 149 for (; count > 0 && pos < 32; count--) { 150 if (put_user(*k++, ®_window[pos++])) 151 return -EFAULT; 152 } 153 } else { 154 for (; count > 0 && pos < 16; count--) { 155 if (get_user(reg, u++)) 156 return -EFAULT; 157 regs->u_regs[pos++] = reg; 158 } 159 160 reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; 161 reg_window -= 16; 162 for (; count > 0 && pos < 32; count--) { 163 if (get_user(reg, u++) || 164 put_user(reg, ®_window[pos++])) 165 return -EFAULT; 166 } 167 } 168 while (count > 0) { 169 unsigned long psr; 170 171 if (kbuf) 172 reg = *k++; 173 else if (get_user(reg, u++)) 174 return -EFAULT; 175 176 switch (pos) { 177 case 32: /* PSR */ 178 psr = regs->psr; 179 psr &= ~(PSR_ICC | PSR_SYSCALL); 180 psr |= (reg & (PSR_ICC | PSR_SYSCALL)); 181 regs->psr = psr; 182 break; 183 case 33: /* PC */ 184 regs->pc = reg; 185 break; 186 case 34: /* NPC */ 187 regs->npc = reg; 188 break; 189 case 35: /* Y */ 190 regs->y = reg; 191 break; 192 case 36: /* WIM */ 193 case 37: /* TBR */ 194 break; 195 default: 196 goto finish; 197 } 198 199 pos++; 200 count--; 201 } 202finish: 203 pos *= sizeof(reg); 204 count *= sizeof(reg); 205 206 return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 207 38 * sizeof(reg), -1); 208} 209 210static int fpregs32_get(struct task_struct *target, 211 const struct user_regset *regset, 212 unsigned int pos, unsigned int count, 213 void *kbuf, void __user *ubuf) 214{ 215 const unsigned long *fpregs = target->thread.float_regs; 216 int ret = 0; 217 218#if 0 219 if (target == current) 220 save_and_clear_fpu(); 221#endif 222 223 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, 224 fpregs, 225 0, 32 * sizeof(u32)); 226 227 if (!ret) 228 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 229 32 * sizeof(u32), 230 33 * sizeof(u32)); 231 if (!ret) 232 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, 233 &target->thread.fsr, 234 33 * sizeof(u32), 235 34 * sizeof(u32)); 236 237 if (!ret) { 238 unsigned long val; 239 240 val = (1 << 8) | (8 << 16); 241 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, 242 &val, 243 34 * sizeof(u32), 244 35 * sizeof(u32)); 245 } 246 247 if (!ret) 248 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 249 35 * sizeof(u32), -1); 250 251 return ret; 252} 253 254static int fpregs32_set(struct task_struct *target, 255 const struct user_regset *regset, 256 unsigned int pos, unsigned int count, 257 const void *kbuf, const void __user *ubuf) 258{ 259 unsigned long *fpregs = target->thread.float_regs; 260 int ret; 261 262#if 0 263 if (target == current) 264 save_and_clear_fpu(); 265#endif 266 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, 267 fpregs, 268 0, 32 * sizeof(u32)); 269 if (!ret) 270 user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 271 32 * sizeof(u32), 272 33 * sizeof(u32)); 273 if (!ret && count > 0) { 274 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, 275 &target->thread.fsr, 276 33 * sizeof(u32), 277 34 * sizeof(u32)); 278 } 279 280 if (!ret) 281 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 282 34 * sizeof(u32), -1); 283 return ret; 284} 285 286static const struct user_regset sparc32_regsets[] = { 287 /* Format is: 288 * G0 --> G7 289 * O0 --> O7 290 * L0 --> L7 291 * I0 --> I7 292 * PSR, PC, nPC, Y, WIM, TBR 293 */ 294 [REGSET_GENERAL] = { 295 .core_note_type = NT_PRSTATUS, 296 .n = 38, 297 .size = sizeof(u32), .align = sizeof(u32), 298 .get = genregs32_get, .set = genregs32_set 299 }, 300 /* Format is: 301 * F0 --> F31 302 * empty 32-bit word 303 * FSR (32--bit word) 304 * FPU QUEUE COUNT (8-bit char) 305 * FPU QUEUE ENTRYSIZE (8-bit char) 306 * FPU ENABLED (8-bit char) 307 * empty 8-bit char 308 * FPU QUEUE (64 32-bit ints) 309 */ 310 [REGSET_FP] = { 311 .core_note_type = NT_PRFPREG, 312 .n = 99, 313 .size = sizeof(u32), .align = sizeof(u32), 314 .get = fpregs32_get, .set = fpregs32_set 315 }, 316}; 317 318static const struct user_regset_view user_sparc32_view = { 319 .name = "sparc", .e_machine = EM_SPARC, 320 .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) 321}; 322 323const struct user_regset_view *task_user_regset_view(struct task_struct *task) 324{ 325 return &user_sparc32_view; 326} 327 328struct fps { 329 unsigned long regs[32]; 330 unsigned long fsr; 331 unsigned long flags; 332 unsigned long extra; 333 unsigned long fpqd; 334 struct fq { 335 unsigned long *insnaddr; 336 unsigned long insn; 337 } fpq[16]; 338}; 339 340long arch_ptrace(struct task_struct *child, long request, 341 unsigned long addr, unsigned long data) 342{ 343 unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4]; 344 void __user *addr2p; 345 const struct user_regset_view *view; 346 struct pt_regs __user *pregs; 347 struct fps __user *fps; 348 int ret; 349 350 view = task_user_regset_view(current); 351 addr2p = (void __user *) addr2; 352 pregs = (struct pt_regs __user *) addr; 353 fps = (struct fps __user *) addr; 354 355 switch(request) { 356 case PTRACE_GETREGS: { 357 ret = copy_regset_to_user(child, view, REGSET_GENERAL, 358 32 * sizeof(u32), 359 4 * sizeof(u32), 360 &pregs->psr); 361 if (!ret) 362 copy_regset_to_user(child, view, REGSET_GENERAL, 363 1 * sizeof(u32), 364 15 * sizeof(u32), 365 &pregs->u_regs[0]); 366 break; 367 } 368 369 case PTRACE_SETREGS: { 370 ret = copy_regset_from_user(child, view, REGSET_GENERAL, 371 32 * sizeof(u32), 372 4 * sizeof(u32), 373 &pregs->psr); 374 if (!ret) 375 copy_regset_from_user(child, view, REGSET_GENERAL, 376 1 * sizeof(u32), 377 15 * sizeof(u32), 378 &pregs->u_regs[0]); 379 break; 380 } 381 382 case PTRACE_GETFPREGS: { 383 ret = copy_regset_to_user(child, view, REGSET_FP, 384 0 * sizeof(u32), 385 32 * sizeof(u32), 386 &fps->regs[0]); 387 if (!ret) 388 ret = copy_regset_to_user(child, view, REGSET_FP, 389 33 * sizeof(u32), 390 1 * sizeof(u32), 391 &fps->fsr); 392 393 if (!ret) { 394 if (__put_user(0, &fps->fpqd) || 395 __put_user(0, &fps->flags) || 396 __put_user(0, &fps->extra) || 397 clear_user(fps->fpq, sizeof(fps->fpq))) 398 ret = -EFAULT; 399 } 400 break; 401 } 402 403 case PTRACE_SETFPREGS: { 404 ret = copy_regset_from_user(child, view, REGSET_FP, 405 0 * sizeof(u32), 406 32 * sizeof(u32), 407 &fps->regs[0]); 408 if (!ret) 409 ret = copy_regset_from_user(child, view, REGSET_FP, 410 33 * sizeof(u32), 411 1 * sizeof(u32), 412 &fps->fsr); 413 break; 414 } 415 416 case PTRACE_READTEXT: 417 case PTRACE_READDATA: 418 ret = ptrace_readdata(child, addr, addr2p, data); 419 420 if (ret == data) 421 ret = 0; 422 else if (ret >= 0) 423 ret = -EIO; 424 break; 425 426 case PTRACE_WRITETEXT: 427 case PTRACE_WRITEDATA: 428 ret = ptrace_writedata(child, addr2p, addr, data); 429 430 if (ret == data) 431 ret = 0; 432 else if (ret >= 0) 433 ret = -EIO; 434 break; 435 436 default: 437 if (request == PTRACE_SPARC_DETACH) 438 request = PTRACE_DETACH; 439 ret = ptrace_request(child, request, addr, data); 440 break; 441 } 442 443 return ret; 444} 445 446asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p) 447{ 448 int ret = 0; 449 450 if (test_thread_flag(TIF_SYSCALL_TRACE)) { 451 if (syscall_exit_p) 452 tracehook_report_syscall_exit(regs, 0); 453 else 454 ret = tracehook_report_syscall_entry(regs); 455 } 456 457 return ret; 458} 459