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