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 /* #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 struct fps { 327 unsigned long regs[32]; 328 unsigned long fsr; 329 unsigned long flags; 330 unsigned long extra; 331 unsigned long fpqd; 332 struct fq { 333 unsigned long *insnaddr; 334 unsigned long insn; 335 } fpq[16]; 336 }; 337 338 long arch_ptrace(struct task_struct *child, long request, 339 unsigned long addr, unsigned long data) 340 { 341 unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4]; 342 void __user *addr2p; 343 const struct user_regset_view *view; 344 struct pt_regs __user *pregs; 345 struct fps __user *fps; 346 int ret; 347 348 view = task_user_regset_view(current); 349 addr2p = (void __user *) addr2; 350 pregs = (struct pt_regs __user *) addr; 351 fps = (struct fps __user *) addr; 352 353 switch(request) { 354 case PTRACE_GETREGS: { 355 ret = copy_regset_to_user(child, view, REGSET_GENERAL, 356 32 * sizeof(u32), 357 4 * sizeof(u32), 358 &pregs->psr); 359 if (!ret) 360 copy_regset_to_user(child, view, REGSET_GENERAL, 361 1 * sizeof(u32), 362 15 * sizeof(u32), 363 &pregs->u_regs[0]); 364 break; 365 } 366 367 case PTRACE_SETREGS: { 368 ret = copy_regset_from_user(child, view, REGSET_GENERAL, 369 32 * sizeof(u32), 370 4 * sizeof(u32), 371 &pregs->psr); 372 if (!ret) 373 copy_regset_from_user(child, view, REGSET_GENERAL, 374 1 * sizeof(u32), 375 15 * sizeof(u32), 376 &pregs->u_regs[0]); 377 break; 378 } 379 380 case PTRACE_GETFPREGS: { 381 ret = copy_regset_to_user(child, view, REGSET_FP, 382 0 * sizeof(u32), 383 32 * sizeof(u32), 384 &fps->regs[0]); 385 if (!ret) 386 ret = copy_regset_to_user(child, view, REGSET_FP, 387 33 * sizeof(u32), 388 1 * sizeof(u32), 389 &fps->fsr); 390 391 if (!ret) { 392 if (__put_user(0, &fps->fpqd) || 393 __put_user(0, &fps->flags) || 394 __put_user(0, &fps->extra) || 395 clear_user(fps->fpq, sizeof(fps->fpq))) 396 ret = -EFAULT; 397 } 398 break; 399 } 400 401 case PTRACE_SETFPREGS: { 402 ret = copy_regset_from_user(child, view, REGSET_FP, 403 0 * sizeof(u32), 404 32 * sizeof(u32), 405 &fps->regs[0]); 406 if (!ret) 407 ret = copy_regset_from_user(child, view, REGSET_FP, 408 33 * sizeof(u32), 409 1 * sizeof(u32), 410 &fps->fsr); 411 break; 412 } 413 414 case PTRACE_READTEXT: 415 case PTRACE_READDATA: 416 ret = ptrace_readdata(child, addr, addr2p, data); 417 418 if (ret == data) 419 ret = 0; 420 else if (ret >= 0) 421 ret = -EIO; 422 break; 423 424 case PTRACE_WRITETEXT: 425 case PTRACE_WRITEDATA: 426 ret = ptrace_writedata(child, addr2p, addr, data); 427 428 if (ret == data) 429 ret = 0; 430 else if (ret >= 0) 431 ret = -EIO; 432 break; 433 434 default: 435 if (request == PTRACE_SPARC_DETACH) 436 request = PTRACE_DETACH; 437 ret = ptrace_request(child, request, addr, data); 438 break; 439 } 440 441 return ret; 442 } 443 444 asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p) 445 { 446 int ret = 0; 447 448 if (test_thread_flag(TIF_SYSCALL_TRACE)) { 449 if (syscall_exit_p) 450 tracehook_report_syscall_exit(regs, 0); 451 else 452 ret = tracehook_report_syscall_entry(regs); 453 } 454 455 return ret; 456 } 457