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