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