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