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