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