linux/arch/sparc/kernel/ptrace_32.c
<<
>>
Prefs
   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                for (; count > 0 && pos < 32; count--) {
  69                        if (get_user(*k++, &reg_window[pos++]))
  70                                return -EFAULT;
  71                }
  72        } else {
  73                for (; count > 0 && pos < 16; count--) {
  74                        if (put_user(regs->u_regs[pos++], u++))
  75                                return -EFAULT;
  76                }
  77
  78                reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
  79                for (; count > 0 && pos < 32; count--) {
  80                        if (get_user(reg, &reg_window[pos++]) ||
  81                            put_user(reg, u++))
  82                                return -EFAULT;
  83                }
  84        }
  85        while (count > 0) {
  86                switch (pos) {
  87                case 32: /* PSR */
  88                        reg = regs->psr;
  89                        break;
  90                case 33: /* PC */
  91                        reg = regs->pc;
  92                        break;
  93                case 34: /* NPC */
  94                        reg = regs->npc;
  95                        break;
  96                case 35: /* Y */
  97                        reg = regs->y;
  98                        break;
  99                case 36: /* WIM */
 100                case 37: /* TBR */
 101                        reg = 0;
 102                        break;
 103                default:
 104                        goto finish;
 105                }
 106
 107                if (kbuf)
 108                        *k++ = reg;
 109                else if (put_user(reg, u++))
 110                        return -EFAULT;
 111                pos++;
 112                count--;
 113        }
 114finish:
 115        pos *= sizeof(reg);
 116        count *= sizeof(reg);
 117
 118        return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 119                                        38 * sizeof(reg), -1);
 120}
 121
 122static int genregs32_set(struct task_struct *target,
 123                         const struct user_regset *regset,
 124                         unsigned int pos, unsigned int count,
 125                         const void *kbuf, const void __user *ubuf)
 126{
 127        struct pt_regs *regs = target->thread.kregs;
 128        unsigned long __user *reg_window;
 129        const unsigned long *k = kbuf;
 130        const unsigned long __user *u = ubuf;
 131        unsigned long reg;
 132
 133        if (target == current)
 134                flush_user_windows();
 135
 136        pos /= sizeof(reg);
 137        count /= sizeof(reg);
 138
 139        if (kbuf) {
 140                for (; count > 0 && pos < 16; count--)
 141                        regs->u_regs[pos++] = *k++;
 142
 143                reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
 144                for (; count > 0 && pos < 32; count--) {
 145                        if (put_user(*k++, &reg_window[pos++]))
 146                                return -EFAULT;
 147                }
 148        } else {
 149                for (; count > 0 && pos < 16; count--) {
 150                        if (get_user(reg, u++))
 151                                return -EFAULT;
 152                        regs->u_regs[pos++] = reg;
 153                }
 154
 155                reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
 156                for (; count > 0 && pos < 32; count--) {
 157                        if (get_user(reg, u++) ||
 158                            put_user(reg, &reg_window[pos++]))
 159                                return -EFAULT;
 160                }
 161        }
 162        while (count > 0) {
 163                unsigned long psr;
 164
 165                if (kbuf)
 166                        reg = *k++;
 167                else if (get_user(reg, u++))
 168                        return -EFAULT;
 169
 170                switch (pos) {
 171                case 32: /* PSR */
 172                        psr = regs->psr;
 173                        psr &= ~(PSR_ICC | PSR_SYSCALL);
 174                        psr |= (reg & (PSR_ICC | PSR_SYSCALL));
 175                        regs->psr = psr;
 176                        break;
 177                case 33: /* PC */
 178                        regs->pc = reg;
 179                        break;
 180                case 34: /* NPC */
 181                        regs->npc = reg;
 182                        break;
 183                case 35: /* Y */
 184                        regs->y = reg;
 185                        break;
 186                case 36: /* WIM */
 187                case 37: /* TBR */
 188                        break;
 189                default:
 190                        goto finish;
 191                }
 192
 193                pos++;
 194                count--;
 195        }
 196finish:
 197        pos *= sizeof(reg);
 198        count *= sizeof(reg);
 199
 200        return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 201                                         38 * sizeof(reg), -1);
 202}
 203
 204static int fpregs32_get(struct task_struct *target,
 205                        const struct user_regset *regset,
 206                        unsigned int pos, unsigned int count,
 207                        void *kbuf, void __user *ubuf)
 208{
 209        const unsigned long *fpregs = target->thread.float_regs;
 210        int ret = 0;
 211
 212#if 0
 213        if (target == current)
 214                save_and_clear_fpu();
 215#endif
 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#if 0
 257        if (target == current)
 258                save_and_clear_fpu();
 259#endif
 260        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 261                                 fpregs,
 262                                 0, 32 * sizeof(u32));
 263        if (!ret)
 264                user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 265                                          32 * sizeof(u32),
 266                                          33 * sizeof(u32));
 267        if (!ret && count > 0) {
 268                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 269                                         &target->thread.fsr,
 270                                         33 * sizeof(u32),
 271                                         34 * sizeof(u32));
 272        }
 273
 274        if (!ret)
 275                ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 276                                                34 * sizeof(u32), -1);
 277        return ret;
 278}
 279
 280static const struct user_regset sparc32_regsets[] = {
 281        /* Format is:
 282         *      G0 --> G7
 283         *      O0 --> O7
 284         *      L0 --> L7
 285         *      I0 --> I7
 286         *      PSR, PC, nPC, Y, WIM, TBR
 287         */
 288        [REGSET_GENERAL] = {
 289                .core_note_type = NT_PRSTATUS,
 290                .n = 38,
 291                .size = sizeof(u32), .align = sizeof(u32),
 292                .get = genregs32_get, .set = genregs32_set
 293        },
 294        /* Format is:
 295         *      F0 --> F31
 296         *      empty 32-bit word
 297         *      FSR (32--bit word)
 298         *      FPU QUEUE COUNT (8-bit char)
 299         *      FPU QUEUE ENTRYSIZE (8-bit char)
 300         *      FPU ENABLED (8-bit char)
 301         *      empty 8-bit char
 302         *      FPU QUEUE (64 32-bit ints)
 303         */
 304        [REGSET_FP] = {
 305                .core_note_type = NT_PRFPREG,
 306                .n = 99,
 307                .size = sizeof(u32), .align = sizeof(u32),
 308                .get = fpregs32_get, .set = fpregs32_set
 309        },
 310};
 311
 312static const struct user_regset_view user_sparc32_view = {
 313        .name = "sparc", .e_machine = EM_SPARC,
 314        .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets)
 315};
 316
 317const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 318{
 319        return &user_sparc32_view;
 320}
 321
 322long arch_ptrace(struct task_struct *child, long request, long addr, long data)
 323{
 324        unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4];
 325        const struct user_regset_view *view;
 326        int ret;
 327
 328        view = task_user_regset_view(current);
 329
 330        switch(request) {
 331        case PTRACE_GETREGS: {
 332                struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
 333
 334                ret = copy_regset_to_user(child, view, REGSET_GENERAL,
 335                                          32 * sizeof(u32),
 336                                          4 * sizeof(u32),
 337                                          &pregs->psr);
 338                if (!ret)
 339                        copy_regset_to_user(child, view, REGSET_GENERAL,
 340                                            1 * sizeof(u32),
 341                                            15 * sizeof(u32),
 342                                            &pregs->u_regs[0]);
 343                break;
 344        }
 345
 346        case PTRACE_SETREGS: {
 347                struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
 348
 349                ret = copy_regset_from_user(child, view, REGSET_GENERAL,
 350                                            32 * sizeof(u32),
 351                                            4 * sizeof(u32),
 352                                            &pregs->psr);
 353                if (!ret)
 354                        copy_regset_from_user(child, view, REGSET_GENERAL,
 355                                              1 * sizeof(u32),
 356                                              15 * sizeof(u32),
 357                                              &pregs->u_regs[0]);
 358                break;
 359        }
 360
 361        case PTRACE_GETFPREGS: {
 362                struct fps {
 363                        unsigned long regs[32];
 364                        unsigned long fsr;
 365                        unsigned long flags;
 366                        unsigned long extra;
 367                        unsigned long fpqd;
 368                        struct fq {
 369                                unsigned long *insnaddr;
 370                                unsigned long insn;
 371                        } fpq[16];
 372                };
 373                struct fps __user *fps = (struct fps __user *) addr;
 374
 375                ret = copy_regset_to_user(child, view, REGSET_FP,
 376                                          0 * sizeof(u32),
 377                                          32 * sizeof(u32),
 378                                          &fps->regs[0]);
 379                if (!ret)
 380                        ret = copy_regset_to_user(child, view, REGSET_FP,
 381                                                  33 * sizeof(u32),
 382                                                  1 * sizeof(u32),
 383                                                  &fps->fsr);
 384
 385                if (!ret) {
 386                        if (__put_user(0, &fps->fpqd) ||
 387                            __put_user(0, &fps->flags) ||
 388                            __put_user(0, &fps->extra) ||
 389                            clear_user(fps->fpq, sizeof(fps->fpq)))
 390                                ret = -EFAULT;
 391                }
 392                break;
 393        }
 394
 395        case PTRACE_SETFPREGS: {
 396                struct fps {
 397                        unsigned long regs[32];
 398                        unsigned long fsr;
 399                        unsigned long flags;
 400                        unsigned long extra;
 401                        unsigned long fpqd;
 402                        struct fq {
 403                                unsigned long *insnaddr;
 404                                unsigned long insn;
 405                        } fpq[16];
 406                };
 407                struct fps __user *fps = (struct fps __user *) addr;
 408
 409                ret = copy_regset_from_user(child, view, REGSET_FP,
 410                                            0 * sizeof(u32),
 411                                            32 * sizeof(u32),
 412                                            &fps->regs[0]);
 413                if (!ret)
 414                        ret = copy_regset_from_user(child, view, REGSET_FP,
 415                                                    33 * sizeof(u32),
 416                                                    1 * sizeof(u32),
 417                                                    &fps->fsr);
 418                break;
 419        }
 420
 421        case PTRACE_READTEXT:
 422        case PTRACE_READDATA:
 423                ret = ptrace_readdata(child, addr,
 424                                      (void __user *) addr2, data);
 425
 426                if (ret == data)
 427                        ret = 0;
 428                else if (ret >= 0)
 429                        ret = -EIO;
 430                break;
 431
 432        case PTRACE_WRITETEXT:
 433        case PTRACE_WRITEDATA:
 434                ret = ptrace_writedata(child, (void __user *) addr2,
 435                                       addr, data);
 436
 437                if (ret == data)
 438                        ret = 0;
 439                else if (ret >= 0)
 440                        ret = -EIO;
 441                break;
 442
 443        default:
 444                if (request == PTRACE_SPARC_DETACH)
 445                        request = PTRACE_DETACH;
 446                ret = ptrace_request(child, request, addr, data);
 447                break;
 448        }
 449
 450        return ret;
 451}
 452
 453asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p)
 454{
 455        int ret = 0;
 456
 457        if (test_thread_flag(TIF_SYSCALL_TRACE)) {
 458                if (syscall_exit_p)
 459                        tracehook_report_syscall_exit(regs, 0);
 460                else
 461                        ret = tracehook_report_syscall_entry(regs);
 462        }
 463
 464        return ret;
 465}
 466