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 */ 36void ptrace_disable(struct task_struct *child) 37{ 38 /* nothing to do */ 39} 40 41enum sparc_regset { 42 REGSET_GENERAL, 43 REGSET_FP, 44}; 45 46static 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 } 116finish: 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 124static 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 } 200finish: 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 208static 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 217 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, 218 fpregs, 219 0, 32 * sizeof(u32)); 220 221 if (!ret) 222 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 223 32 * sizeof(u32), 224 33 * sizeof(u32)); 225 if (!ret) 226 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, 227 &target->thread.fsr, 228 33 * sizeof(u32), 229 34 * sizeof(u32)); 230 231 if (!ret) { 232 unsigned long val; 233 234 val = (1 << 8) | (8 << 16); 235 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, 236 &val, 237 34 * sizeof(u32), 238 35 * sizeof(u32)); 239 } 240 241 if (!ret) 242 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 243 35 * sizeof(u32), -1); 244 245 return ret; 246} 247 248static int fpregs32_set(struct task_struct *target, 249 const struct user_regset *regset, 250 unsigned int pos, unsigned int count, 251 const void *kbuf, const void __user *ubuf) 252{ 253 unsigned long *fpregs = target->thread.float_regs; 254 int ret; 255 256 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, 257 fpregs, 258 0, 32 * sizeof(u32)); 259 if (!ret) 260 user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 261 32 * sizeof(u32), 262 33 * sizeof(u32)); 263 if (!ret && count > 0) { 264 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, 265 &target->thread.fsr, 266 33 * sizeof(u32), 267 34 * sizeof(u32)); 268 } 269 270 if (!ret) 271 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 272 34 * sizeof(u32), -1); 273 return ret; 274} 275 276static const struct user_regset sparc32_regsets[] = { 277 /* Format is: 278 * G0 --> G7 279 * O0 --> O7 280 * L0 --> L7 281 * I0 --> I7 282 * PSR, PC, nPC, Y, WIM, TBR 283 */ 284 [REGSET_GENERAL] = { 285 .core_note_type = NT_PRSTATUS, 286 .n = 38, 287 .size = sizeof(u32), .align = sizeof(u32), 288 .get = genregs32_get, .set = genregs32_set 289 }, 290 /* Format is: 291 * F0 --> F31 292 * empty 32-bit word 293 * FSR (32--bit word) 294 * FPU QUEUE COUNT (8-bit char) 295 * FPU QUEUE ENTRYSIZE (8-bit char) 296 * FPU ENABLED (8-bit char) 297 * empty 8-bit char 298 * FPU QUEUE (64 32-bit ints) 299 */ 300 [REGSET_FP] = { 301 .core_note_type = NT_PRFPREG, 302 .n = 99, 303 .size = sizeof(u32), .align = sizeof(u32), 304 .get = fpregs32_get, .set = fpregs32_set 305 }, 306}; 307 308static const struct user_regset_view user_sparc32_view = { 309 .name = "sparc", .e_machine = EM_SPARC, 310 .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) 311}; 312 313const struct user_regset_view *task_user_regset_view(struct task_struct *task) 314{ 315 return &user_sparc32_view; 316} 317 318long arch_ptrace(struct task_struct *child, long request, long addr, long data) 319{ 320 unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4]; 321 const struct user_regset_view *view; 322 int ret; 323 324 view = task_user_regset_view(current); 325 326 switch(request) { 327 case PTRACE_GETREGS: { 328 struct pt_regs __user *pregs = (struct pt_regs __user *) addr; 329 330 ret = copy_regset_to_user(child, view, REGSET_GENERAL, 331 32 * sizeof(u32), 332 4 * sizeof(u32), 333 &pregs->psr); 334 if (!ret) 335 copy_regset_to_user(child, view, REGSET_GENERAL, 336 1 * sizeof(u32), 337 15 * sizeof(u32), 338 &pregs->u_regs[0]); 339 break; 340 } 341 342 case PTRACE_SETREGS: { 343 struct pt_regs __user *pregs = (struct pt_regs __user *) addr; 344 345 ret = copy_regset_from_user(child, view, REGSET_GENERAL, 346 32 * sizeof(u32), 347 4 * sizeof(u32), 348 &pregs->psr); 349 if (!ret) 350 copy_regset_from_user(child, view, REGSET_GENERAL, 351 1 * sizeof(u32), 352 15 * sizeof(u32), 353 &pregs->u_regs[0]); 354 break; 355 } 356 357 case PTRACE_GETFPREGS: { 358 struct fps { 359 unsigned long regs[32]; 360 unsigned long fsr; 361 unsigned long flags; 362 unsigned long extra; 363 unsigned long fpqd; 364 struct fq { 365 unsigned long *insnaddr; 366 unsigned long insn; 367 } fpq[16]; 368 }; 369 struct fps __user *fps = (struct fps __user *) addr; 370 371 ret = copy_regset_to_user(child, view, REGSET_FP, 372 0 * sizeof(u32), 373 32 * sizeof(u32), 374 &fps->regs[0]); 375 if (!ret) 376 ret = copy_regset_to_user(child, view, REGSET_FP, 377 33 * sizeof(u32), 378 1 * sizeof(u32), 379 &fps->fsr); 380 381 if (!ret) { 382 if (__put_user(0, &fps->fpqd) || 383 __put_user(0, &fps->flags) || 384 __put_user(0, &fps->extra) || 385 clear_user(fps->fpq, sizeof(fps->fpq))) 386 ret = -EFAULT; 387 } 388 break; 389 } 390 391 case PTRACE_SETFPREGS: { 392 struct fps { 393 unsigned long regs[32]; 394 unsigned long fsr; 395 unsigned long flags; 396 unsigned long extra; 397 unsigned long fpqd; 398 struct fq { 399 unsigned long *insnaddr; 400 unsigned long insn; 401 } fpq[16]; 402 }; 403 struct fps __user *fps = (struct fps __user *) addr; 404 405 ret = copy_regset_from_user(child, view, REGSET_FP, 406 0 * sizeof(u32), 407 32 * sizeof(u32), 408 &fps->regs[0]); 409 if (!ret) 410 ret = copy_regset_from_user(child, view, REGSET_FP, 411 33 * sizeof(u32), 412 1 * sizeof(u32), 413 &fps->fsr); 414 break; 415 } 416 417 case PTRACE_READTEXT: 418 case PTRACE_READDATA: 419 ret = ptrace_readdata(child, addr, 420 (void __user *) addr2, data); 421 422 if (ret == data) 423 ret = 0; 424 else if (ret >= 0) 425 ret = -EIO; 426 break; 427 428 case PTRACE_WRITETEXT: 429 case PTRACE_WRITEDATA: 430 ret = ptrace_writedata(child, (void __user *) addr2, 431 addr, data); 432 433 if (ret == data) 434 ret = 0; 435 else if (ret >= 0) 436 ret = -EIO; 437 break; 438 439 default: 440 if (request == PTRACE_SPARC_DETACH) 441 request = PTRACE_DETACH; 442 ret = ptrace_request(child, request, addr, data); 443 break; 444 } 445 446 return ret; 447} 448 449asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p) 450{ 451 int ret = 0; 452 453 if (test_thread_flag(TIF_SYSCALL_TRACE)) { 454 if (syscall_exit_p) 455 tracehook_report_syscall_exit(regs, 0); 456 else 457 ret = tracehook_report_syscall_entry(regs); 458 } 459 460 return ret; 461} 462