linux/arch/sparc/kernel/ptrace_64.c
<<
>>
Prefs
   1/* ptrace.c: Sparc process tracing support.
   2 *
   3 * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net)
   4 * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
   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/seccomp.h>
  22#include <linux/audit.h>
  23#include <linux/signal.h>
  24#include <linux/regset.h>
  25#include <linux/tracehook.h>
  26#include <trace/syscall.h>
  27#include <linux/compat.h>
  28#include <linux/elf.h>
  29
  30#include <asm/asi.h>
  31#include <asm/pgtable.h>
  32#include <asm/uaccess.h>
  33#include <asm/psrcompat.h>
  34#include <asm/visasm.h>
  35#include <asm/spitfire.h>
  36#include <asm/page.h>
  37#include <asm/cpudata.h>
  38#include <asm/cacheflush.h>
  39
  40#define CREATE_TRACE_POINTS
  41#include <trace/events/syscalls.h>
  42
  43#include "entry.h"
  44
  45/* #define ALLOW_INIT_TRACING */
  46
  47/*
  48 * Called by kernel/ptrace.c when detaching..
  49 *
  50 * Make sure single step bits etc are not set.
  51 */
  52void ptrace_disable(struct task_struct *child)
  53{
  54        /* nothing to do */
  55}
  56
  57/* To get the necessary page struct, access_process_vm() first calls
  58 * get_user_pages().  This has done a flush_dcache_page() on the
  59 * accessed page.  Then our caller (copy_{to,from}_user_page()) did
  60 * to memcpy to read/write the data from that page.
  61 *
  62 * Now, the only thing we have to do is:
  63 * 1) flush the D-cache if it's possible than an illegal alias
  64 *    has been created
  65 * 2) flush the I-cache if this is pre-cheetah and we did a write
  66 */
  67void flush_ptrace_access(struct vm_area_struct *vma, struct page *page,
  68                         unsigned long uaddr, void *kaddr,
  69                         unsigned long len, int write)
  70{
  71        BUG_ON(len > PAGE_SIZE);
  72
  73        if (tlb_type == hypervisor)
  74                return;
  75
  76        preempt_disable();
  77
  78#ifdef DCACHE_ALIASING_POSSIBLE
  79        /* If bit 13 of the kernel address we used to access the
  80         * user page is the same as the virtual address that page
  81         * is mapped to in the user's address space, we can skip the
  82         * D-cache flush.
  83         */
  84        if ((uaddr ^ (unsigned long) kaddr) & (1UL << 13)) {
  85                unsigned long start = __pa(kaddr);
  86                unsigned long end = start + len;
  87                unsigned long dcache_line_size;
  88
  89                dcache_line_size = local_cpu_data().dcache_line_size;
  90
  91                if (tlb_type == spitfire) {
  92                        for (; start < end; start += dcache_line_size)
  93                                spitfire_put_dcache_tag(start & 0x3fe0, 0x0);
  94                } else {
  95                        start &= ~(dcache_line_size - 1);
  96                        for (; start < end; start += dcache_line_size)
  97                                __asm__ __volatile__(
  98                                        "stxa %%g0, [%0] %1\n\t"
  99                                        "membar #Sync"
 100                                        : /* no outputs */
 101                                        : "r" (start),
 102                                        "i" (ASI_DCACHE_INVALIDATE));
 103                }
 104        }
 105#endif
 106        if (write && tlb_type == spitfire) {
 107                unsigned long start = (unsigned long) kaddr;
 108                unsigned long end = start + len;
 109                unsigned long icache_line_size;
 110
 111                icache_line_size = local_cpu_data().icache_line_size;
 112
 113                for (; start < end; start += icache_line_size)
 114                        flushi(start);
 115        }
 116
 117        preempt_enable();
 118}
 119
 120static int get_from_target(struct task_struct *target, unsigned long uaddr,
 121                           void *kbuf, int len)
 122{
 123        if (target == current) {
 124                if (copy_from_user(kbuf, (void __user *) uaddr, len))
 125                        return -EFAULT;
 126        } else {
 127                int len2 = access_process_vm(target, uaddr, kbuf, len, 0);
 128                if (len2 != len)
 129                        return -EFAULT;
 130        }
 131        return 0;
 132}
 133
 134static int set_to_target(struct task_struct *target, unsigned long uaddr,
 135                         void *kbuf, int len)
 136{
 137        if (target == current) {
 138                if (copy_to_user((void __user *) uaddr, kbuf, len))
 139                        return -EFAULT;
 140        } else {
 141                int len2 = access_process_vm(target, uaddr, kbuf, len, 1);
 142                if (len2 != len)
 143                        return -EFAULT;
 144        }
 145        return 0;
 146}
 147
 148static int regwindow64_get(struct task_struct *target,
 149                           const struct pt_regs *regs,
 150                           struct reg_window *wbuf)
 151{
 152        unsigned long rw_addr = regs->u_regs[UREG_I6];
 153
 154        if (!test_thread_64bit_stack(rw_addr)) {
 155                struct reg_window32 win32;
 156                int i;
 157
 158                if (get_from_target(target, rw_addr, &win32, sizeof(win32)))
 159                        return -EFAULT;
 160                for (i = 0; i < 8; i++)
 161                        wbuf->locals[i] = win32.locals[i];
 162                for (i = 0; i < 8; i++)
 163                        wbuf->ins[i] = win32.ins[i];
 164        } else {
 165                rw_addr += STACK_BIAS;
 166                if (get_from_target(target, rw_addr, wbuf, sizeof(*wbuf)))
 167                        return -EFAULT;
 168        }
 169
 170        return 0;
 171}
 172
 173static int regwindow64_set(struct task_struct *target,
 174                           const struct pt_regs *regs,
 175                           struct reg_window *wbuf)
 176{
 177        unsigned long rw_addr = regs->u_regs[UREG_I6];
 178
 179        if (!test_thread_64bit_stack(rw_addr)) {
 180                struct reg_window32 win32;
 181                int i;
 182
 183                for (i = 0; i < 8; i++)
 184                        win32.locals[i] = wbuf->locals[i];
 185                for (i = 0; i < 8; i++)
 186                        win32.ins[i] = wbuf->ins[i];
 187
 188                if (set_to_target(target, rw_addr, &win32, sizeof(win32)))
 189                        return -EFAULT;
 190        } else {
 191                rw_addr += STACK_BIAS;
 192                if (set_to_target(target, rw_addr, wbuf, sizeof(*wbuf)))
 193                        return -EFAULT;
 194        }
 195
 196        return 0;
 197}
 198
 199enum sparc_regset {
 200        REGSET_GENERAL,
 201        REGSET_FP,
 202};
 203
 204static int genregs64_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 struct pt_regs *regs = task_pt_regs(target);
 210        int ret;
 211
 212        if (target == current)
 213                flushw_user();
 214
 215        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 216                                  regs->u_regs,
 217                                  0, 16 * sizeof(u64));
 218        if (!ret && count && pos < (32 * sizeof(u64))) {
 219                struct reg_window window;
 220
 221                if (regwindow64_get(target, regs, &window))
 222                        return -EFAULT;
 223                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 224                                          &window,
 225                                          16 * sizeof(u64),
 226                                          32 * sizeof(u64));
 227        }
 228
 229        if (!ret) {
 230                /* TSTATE, TPC, TNPC */
 231                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 232                                          &regs->tstate,
 233                                          32 * sizeof(u64),
 234                                          35 * sizeof(u64));
 235        }
 236
 237        if (!ret) {
 238                unsigned long y = regs->y;
 239
 240                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 241                                          &y,
 242                                          35 * sizeof(u64),
 243                                          36 * sizeof(u64));
 244        }
 245
 246        if (!ret) {
 247                ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 248                                               36 * sizeof(u64), -1);
 249
 250        }
 251        return ret;
 252}
 253
 254static int genregs64_set(struct task_struct *target,
 255                         const struct user_regset *regset,
 256                         unsigned int pos, unsigned int count,
 257                         const void *kbuf, const void __user *ubuf)
 258{
 259        struct pt_regs *regs = task_pt_regs(target);
 260        int ret;
 261
 262        if (target == current)
 263                flushw_user();
 264
 265        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 266                                 regs->u_regs,
 267                                 0, 16 * sizeof(u64));
 268        if (!ret && count && pos < (32 * sizeof(u64))) {
 269                struct reg_window window;
 270
 271                if (regwindow64_get(target, regs, &window))
 272                        return -EFAULT;
 273
 274                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 275                                         &window,
 276                                         16 * sizeof(u64),
 277                                         32 * sizeof(u64));
 278
 279                if (!ret &&
 280                    regwindow64_set(target, regs, &window))
 281                        return -EFAULT;
 282        }
 283
 284        if (!ret && count > 0) {
 285                unsigned long tstate;
 286
 287                /* TSTATE */
 288                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 289                                         &tstate,
 290                                         32 * sizeof(u64),
 291                                         33 * sizeof(u64));
 292                if (!ret) {
 293                        /* Only the condition codes and the "in syscall"
 294                         * state can be modified in the %tstate register.
 295                         */
 296                        tstate &= (TSTATE_ICC | TSTATE_XCC | TSTATE_SYSCALL);
 297                        regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC | TSTATE_SYSCALL);
 298                        regs->tstate |= tstate;
 299                }
 300        }
 301
 302        if (!ret) {
 303                /* TPC, TNPC */
 304                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 305                                         &regs->tpc,
 306                                         33 * sizeof(u64),
 307                                         35 * sizeof(u64));
 308        }
 309
 310        if (!ret) {
 311                unsigned long y;
 312
 313                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 314                                         &y,
 315                                         35 * sizeof(u64),
 316                                         36 * sizeof(u64));
 317                if (!ret)
 318                        regs->y = y;
 319        }
 320
 321        if (!ret)
 322                ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 323                                                36 * sizeof(u64), -1);
 324
 325        return ret;
 326}
 327
 328static int fpregs64_get(struct task_struct *target,
 329                        const struct user_regset *regset,
 330                        unsigned int pos, unsigned int count,
 331                        void *kbuf, void __user *ubuf)
 332{
 333        const unsigned long *fpregs = task_thread_info(target)->fpregs;
 334        unsigned long fprs, fsr, gsr;
 335        int ret;
 336
 337        if (target == current)
 338                save_and_clear_fpu();
 339
 340        fprs = task_thread_info(target)->fpsaved[0];
 341
 342        if (fprs & FPRS_DL)
 343                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 344                                          fpregs,
 345                                          0, 16 * sizeof(u64));
 346        else
 347                ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 348                                               0,
 349                                               16 * sizeof(u64));
 350
 351        if (!ret) {
 352                if (fprs & FPRS_DU)
 353                        ret = user_regset_copyout(&pos, &count,
 354                                                  &kbuf, &ubuf,
 355                                                  fpregs + 16,
 356                                                  16 * sizeof(u64),
 357                                                  32 * sizeof(u64));
 358                else
 359                        ret = user_regset_copyout_zero(&pos, &count,
 360                                                       &kbuf, &ubuf,
 361                                                       16 * sizeof(u64),
 362                                                       32 * sizeof(u64));
 363        }
 364
 365        if (fprs & FPRS_FEF) {
 366                fsr = task_thread_info(target)->xfsr[0];
 367                gsr = task_thread_info(target)->gsr[0];
 368        } else {
 369                fsr = gsr = 0;
 370        }
 371
 372        if (!ret)
 373                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 374                                          &fsr,
 375                                          32 * sizeof(u64),
 376                                          33 * sizeof(u64));
 377        if (!ret)
 378                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 379                                          &gsr,
 380                                          33 * sizeof(u64),
 381                                          34 * sizeof(u64));
 382        if (!ret)
 383                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 384                                          &fprs,
 385                                          34 * sizeof(u64),
 386                                          35 * sizeof(u64));
 387
 388        if (!ret)
 389                ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 390                                               35 * sizeof(u64), -1);
 391
 392        return ret;
 393}
 394
 395static int fpregs64_set(struct task_struct *target,
 396                        const struct user_regset *regset,
 397                        unsigned int pos, unsigned int count,
 398                        const void *kbuf, const void __user *ubuf)
 399{
 400        unsigned long *fpregs = task_thread_info(target)->fpregs;
 401        unsigned long fprs;
 402        int ret;
 403
 404        if (target == current)
 405                save_and_clear_fpu();
 406
 407        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 408                                 fpregs,
 409                                 0, 32 * sizeof(u64));
 410        if (!ret)
 411                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 412                                         task_thread_info(target)->xfsr,
 413                                         32 * sizeof(u64),
 414                                         33 * sizeof(u64));
 415        if (!ret)
 416                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 417                                         task_thread_info(target)->gsr,
 418                                         33 * sizeof(u64),
 419                                         34 * sizeof(u64));
 420
 421        fprs = task_thread_info(target)->fpsaved[0];
 422        if (!ret && count > 0) {
 423                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 424                                         &fprs,
 425                                         34 * sizeof(u64),
 426                                         35 * sizeof(u64));
 427        }
 428
 429        fprs |= (FPRS_FEF | FPRS_DL | FPRS_DU);
 430        task_thread_info(target)->fpsaved[0] = fprs;
 431
 432        if (!ret)
 433                ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 434                                                35 * sizeof(u64), -1);
 435        return ret;
 436}
 437
 438static const struct user_regset sparc64_regsets[] = {
 439        /* Format is:
 440         *      G0 --> G7
 441         *      O0 --> O7
 442         *      L0 --> L7
 443         *      I0 --> I7
 444         *      TSTATE, TPC, TNPC, Y
 445         */
 446        [REGSET_GENERAL] = {
 447                .core_note_type = NT_PRSTATUS,
 448                .n = 36,
 449                .size = sizeof(u64), .align = sizeof(u64),
 450                .get = genregs64_get, .set = genregs64_set
 451        },
 452        /* Format is:
 453         *      F0 --> F63
 454         *      FSR
 455         *      GSR
 456         *      FPRS
 457         */
 458        [REGSET_FP] = {
 459                .core_note_type = NT_PRFPREG,
 460                .n = 35,
 461                .size = sizeof(u64), .align = sizeof(u64),
 462                .get = fpregs64_get, .set = fpregs64_set
 463        },
 464};
 465
 466static const struct user_regset_view user_sparc64_view = {
 467        .name = "sparc64", .e_machine = EM_SPARCV9,
 468        .regsets = sparc64_regsets, .n = ARRAY_SIZE(sparc64_regsets)
 469};
 470
 471#ifdef CONFIG_COMPAT
 472static int genregs32_get(struct task_struct *target,
 473                         const struct user_regset *regset,
 474                         unsigned int pos, unsigned int count,
 475                         void *kbuf, void __user *ubuf)
 476{
 477        const struct pt_regs *regs = task_pt_regs(target);
 478        compat_ulong_t __user *reg_window;
 479        compat_ulong_t *k = kbuf;
 480        compat_ulong_t __user *u = ubuf;
 481        compat_ulong_t reg;
 482
 483        if (target == current)
 484                flushw_user();
 485
 486        pos /= sizeof(reg);
 487        count /= sizeof(reg);
 488
 489        if (kbuf) {
 490                for (; count > 0 && pos < 16; count--)
 491                        *k++ = regs->u_regs[pos++];
 492
 493                reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6];
 494                reg_window -= 16;
 495                if (target == current) {
 496                        for (; count > 0 && pos < 32; count--) {
 497                                if (get_user(*k++, &reg_window[pos++]))
 498                                        return -EFAULT;
 499                        }
 500                } else {
 501                        for (; count > 0 && pos < 32; count--) {
 502                                if (access_process_vm(target,
 503                                                      (unsigned long)
 504                                                      &reg_window[pos],
 505                                                      k, sizeof(*k), 0)
 506                                    != sizeof(*k))
 507                                        return -EFAULT;
 508                                k++;
 509                                pos++;
 510                        }
 511                }
 512        } else {
 513                for (; count > 0 && pos < 16; count--) {
 514                        if (put_user((compat_ulong_t) regs->u_regs[pos++], u++))
 515                                return -EFAULT;
 516                }
 517
 518                reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6];
 519                reg_window -= 16;
 520                if (target == current) {
 521                        for (; count > 0 && pos < 32; count--) {
 522                                if (get_user(reg, &reg_window[pos++]) ||
 523                                    put_user(reg, u++))
 524                                        return -EFAULT;
 525                        }
 526                } else {
 527                        for (; count > 0 && pos < 32; count--) {
 528                                if (access_process_vm(target,
 529                                                      (unsigned long)
 530                                                      &reg_window[pos],
 531                                                      &reg, sizeof(reg), 0)
 532                                    != sizeof(reg))
 533                                        return -EFAULT;
 534                                if (access_process_vm(target,
 535                                                      (unsigned long) u,
 536                                                      &reg, sizeof(reg), 1)
 537                                    != sizeof(reg))
 538                                        return -EFAULT;
 539                                pos++;
 540                                u++;
 541                        }
 542                }
 543        }
 544        while (count > 0) {
 545                switch (pos) {
 546                case 32: /* PSR */
 547                        reg = tstate_to_psr(regs->tstate);
 548                        break;
 549                case 33: /* PC */
 550                        reg = regs->tpc;
 551                        break;
 552                case 34: /* NPC */
 553                        reg = regs->tnpc;
 554                        break;
 555                case 35: /* Y */
 556                        reg = regs->y;
 557                        break;
 558                case 36: /* WIM */
 559                case 37: /* TBR */
 560                        reg = 0;
 561                        break;
 562                default:
 563                        goto finish;
 564                }
 565
 566                if (kbuf)
 567                        *k++ = reg;
 568                else if (put_user(reg, u++))
 569                        return -EFAULT;
 570                pos++;
 571                count--;
 572        }
 573finish:
 574        pos *= sizeof(reg);
 575        count *= sizeof(reg);
 576
 577        return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 578                                        38 * sizeof(reg), -1);
 579}
 580
 581static int genregs32_set(struct task_struct *target,
 582                         const struct user_regset *regset,
 583                         unsigned int pos, unsigned int count,
 584                         const void *kbuf, const void __user *ubuf)
 585{
 586        struct pt_regs *regs = task_pt_regs(target);
 587        compat_ulong_t __user *reg_window;
 588        const compat_ulong_t *k = kbuf;
 589        const compat_ulong_t __user *u = ubuf;
 590        compat_ulong_t reg;
 591
 592        if (target == current)
 593                flushw_user();
 594
 595        pos /= sizeof(reg);
 596        count /= sizeof(reg);
 597
 598        if (kbuf) {
 599                for (; count > 0 && pos < 16; count--)
 600                        regs->u_regs[pos++] = *k++;
 601
 602                reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6];
 603                reg_window -= 16;
 604                if (target == current) {
 605                        for (; count > 0 && pos < 32; count--) {
 606                                if (put_user(*k++, &reg_window[pos++]))
 607                                        return -EFAULT;
 608                        }
 609                } else {
 610                        for (; count > 0 && pos < 32; count--) {
 611                                if (access_process_vm(target,
 612                                                      (unsigned long)
 613                                                      &reg_window[pos],
 614                                                      (void *) k,
 615                                                      sizeof(*k), 1)
 616                                    != sizeof(*k))
 617                                        return -EFAULT;
 618                                k++;
 619                                pos++;
 620                        }
 621                }
 622        } else {
 623                for (; count > 0 && pos < 16; count--) {
 624                        if (get_user(reg, u++))
 625                                return -EFAULT;
 626                        regs->u_regs[pos++] = reg;
 627                }
 628
 629                reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6];
 630                reg_window -= 16;
 631                if (target == current) {
 632                        for (; count > 0 && pos < 32; count--) {
 633                                if (get_user(reg, u++) ||
 634                                    put_user(reg, &reg_window[pos++]))
 635                                        return -EFAULT;
 636                        }
 637                } else {
 638                        for (; count > 0 && pos < 32; count--) {
 639                                if (access_process_vm(target,
 640                                                      (unsigned long)
 641                                                      u,
 642                                                      &reg, sizeof(reg), 0)
 643                                    != sizeof(reg))
 644                                        return -EFAULT;
 645                                if (access_process_vm(target,
 646                                                      (unsigned long)
 647                                                      &reg_window[pos],
 648                                                      &reg, sizeof(reg), 1)
 649                                    != sizeof(reg))
 650                                        return -EFAULT;
 651                                pos++;
 652                                u++;
 653                        }
 654                }
 655        }
 656        while (count > 0) {
 657                unsigned long tstate;
 658
 659                if (kbuf)
 660                        reg = *k++;
 661                else if (get_user(reg, u++))
 662                        return -EFAULT;
 663
 664                switch (pos) {
 665                case 32: /* PSR */
 666                        tstate = regs->tstate;
 667                        tstate &= ~(TSTATE_ICC | TSTATE_XCC | TSTATE_SYSCALL);
 668                        tstate |= psr_to_tstate_icc(reg);
 669                        if (reg & PSR_SYSCALL)
 670                                tstate |= TSTATE_SYSCALL;
 671                        regs->tstate = tstate;
 672                        break;
 673                case 33: /* PC */
 674                        regs->tpc = reg;
 675                        break;
 676                case 34: /* NPC */
 677                        regs->tnpc = reg;
 678                        break;
 679                case 35: /* Y */
 680                        regs->y = reg;
 681                        break;
 682                case 36: /* WIM */
 683                case 37: /* TBR */
 684                        break;
 685                default:
 686                        goto finish;
 687                }
 688
 689                pos++;
 690                count--;
 691        }
 692finish:
 693        pos *= sizeof(reg);
 694        count *= sizeof(reg);
 695
 696        return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 697                                         38 * sizeof(reg), -1);
 698}
 699
 700static int fpregs32_get(struct task_struct *target,
 701                        const struct user_regset *regset,
 702                        unsigned int pos, unsigned int count,
 703                        void *kbuf, void __user *ubuf)
 704{
 705        const unsigned long *fpregs = task_thread_info(target)->fpregs;
 706        compat_ulong_t enabled;
 707        unsigned long fprs;
 708        compat_ulong_t fsr;
 709        int ret = 0;
 710
 711        if (target == current)
 712                save_and_clear_fpu();
 713
 714        fprs = task_thread_info(target)->fpsaved[0];
 715        if (fprs & FPRS_FEF) {
 716                fsr = task_thread_info(target)->xfsr[0];
 717                enabled = 1;
 718        } else {
 719                fsr = 0;
 720                enabled = 0;
 721        }
 722
 723        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 724                                  fpregs,
 725                                  0, 32 * sizeof(u32));
 726
 727        if (!ret)
 728                ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 729                                               32 * sizeof(u32),
 730                                               33 * sizeof(u32));
 731        if (!ret)
 732                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 733                                          &fsr,
 734                                          33 * sizeof(u32),
 735                                          34 * sizeof(u32));
 736
 737        if (!ret) {
 738                compat_ulong_t val;
 739
 740                val = (enabled << 8) | (8 << 16);
 741                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 742                                          &val,
 743                                          34 * sizeof(u32),
 744                                          35 * sizeof(u32));
 745        }
 746
 747        if (!ret)
 748                ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 749                                               35 * sizeof(u32), -1);
 750
 751        return ret;
 752}
 753
 754static int fpregs32_set(struct task_struct *target,
 755                        const struct user_regset *regset,
 756                        unsigned int pos, unsigned int count,
 757                        const void *kbuf, const void __user *ubuf)
 758{
 759        unsigned long *fpregs = task_thread_info(target)->fpregs;
 760        unsigned long fprs;
 761        int ret;
 762
 763        if (target == current)
 764                save_and_clear_fpu();
 765
 766        fprs = task_thread_info(target)->fpsaved[0];
 767
 768        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 769                                 fpregs,
 770                                 0, 32 * sizeof(u32));
 771        if (!ret)
 772                user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 773                                          32 * sizeof(u32),
 774                                          33 * sizeof(u32));
 775        if (!ret && count > 0) {
 776                compat_ulong_t fsr;
 777                unsigned long val;
 778
 779                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 780                                         &fsr,
 781                                         33 * sizeof(u32),
 782                                         34 * sizeof(u32));
 783                if (!ret) {
 784                        val = task_thread_info(target)->xfsr[0];
 785                        val &= 0xffffffff00000000UL;
 786                        val |= fsr;
 787                        task_thread_info(target)->xfsr[0] = val;
 788                }
 789        }
 790
 791        fprs |= (FPRS_FEF | FPRS_DL);
 792        task_thread_info(target)->fpsaved[0] = fprs;
 793
 794        if (!ret)
 795                ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 796                                                34 * sizeof(u32), -1);
 797        return ret;
 798}
 799
 800static const struct user_regset sparc32_regsets[] = {
 801        /* Format is:
 802         *      G0 --> G7
 803         *      O0 --> O7
 804         *      L0 --> L7
 805         *      I0 --> I7
 806         *      PSR, PC, nPC, Y, WIM, TBR
 807         */
 808        [REGSET_GENERAL] = {
 809                .core_note_type = NT_PRSTATUS,
 810                .n = 38,
 811                .size = sizeof(u32), .align = sizeof(u32),
 812                .get = genregs32_get, .set = genregs32_set
 813        },
 814        /* Format is:
 815         *      F0 --> F31
 816         *      empty 32-bit word
 817         *      FSR (32--bit word)
 818         *      FPU QUEUE COUNT (8-bit char)
 819         *      FPU QUEUE ENTRYSIZE (8-bit char)
 820         *      FPU ENABLED (8-bit char)
 821         *      empty 8-bit char
 822         *      FPU QUEUE (64 32-bit ints)
 823         */
 824        [REGSET_FP] = {
 825                .core_note_type = NT_PRFPREG,
 826                .n = 99,
 827                .size = sizeof(u32), .align = sizeof(u32),
 828                .get = fpregs32_get, .set = fpregs32_set
 829        },
 830};
 831
 832static const struct user_regset_view user_sparc32_view = {
 833        .name = "sparc", .e_machine = EM_SPARC,
 834        .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets)
 835};
 836#endif /* CONFIG_COMPAT */
 837
 838const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 839{
 840#ifdef CONFIG_COMPAT
 841        if (test_tsk_thread_flag(task, TIF_32BIT))
 842                return &user_sparc32_view;
 843#endif
 844        return &user_sparc64_view;
 845}
 846
 847#ifdef CONFIG_COMPAT
 848struct compat_fps {
 849        unsigned int regs[32];
 850        unsigned int fsr;
 851        unsigned int flags;
 852        unsigned int extra;
 853        unsigned int fpqd;
 854        struct compat_fq {
 855                unsigned int insnaddr;
 856                unsigned int insn;
 857        } fpq[16];
 858};
 859
 860long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
 861                        compat_ulong_t caddr, compat_ulong_t cdata)
 862{
 863        const struct user_regset_view *view = task_user_regset_view(current);
 864        compat_ulong_t caddr2 = task_pt_regs(current)->u_regs[UREG_I4];
 865        struct pt_regs32 __user *pregs;
 866        struct compat_fps __user *fps;
 867        unsigned long addr2 = caddr2;
 868        unsigned long addr = caddr;
 869        unsigned long data = cdata;
 870        int ret;
 871
 872        pregs = (struct pt_regs32 __user *) addr;
 873        fps = (struct compat_fps __user *) addr;
 874
 875        switch (request) {
 876        case PTRACE_PEEKUSR:
 877                ret = (addr != 0) ? -EIO : 0;
 878                break;
 879
 880        case PTRACE_GETREGS:
 881                ret = copy_regset_to_user(child, view, REGSET_GENERAL,
 882                                          32 * sizeof(u32),
 883                                          4 * sizeof(u32),
 884                                          &pregs->psr);
 885                if (!ret)
 886                        ret = copy_regset_to_user(child, view, REGSET_GENERAL,
 887                                                  1 * sizeof(u32),
 888                                                  15 * sizeof(u32),
 889                                                  &pregs->u_regs[0]);
 890                break;
 891
 892        case PTRACE_SETREGS:
 893                ret = copy_regset_from_user(child, view, REGSET_GENERAL,
 894                                            32 * sizeof(u32),
 895                                            4 * sizeof(u32),
 896                                            &pregs->psr);
 897                if (!ret)
 898                        ret = copy_regset_from_user(child, view, REGSET_GENERAL,
 899                                                    1 * sizeof(u32),
 900                                                    15 * sizeof(u32),
 901                                                    &pregs->u_regs[0]);
 902                break;
 903
 904        case PTRACE_GETFPREGS:
 905                ret = copy_regset_to_user(child, view, REGSET_FP,
 906                                          0 * sizeof(u32),
 907                                          32 * sizeof(u32),
 908                                          &fps->regs[0]);
 909                if (!ret)
 910                        ret = copy_regset_to_user(child, view, REGSET_FP,
 911                                                  33 * sizeof(u32),
 912                                                  1 * sizeof(u32),
 913                                                  &fps->fsr);
 914                if (!ret) {
 915                        if (__put_user(0, &fps->flags) ||
 916                            __put_user(0, &fps->extra) ||
 917                            __put_user(0, &fps->fpqd) ||
 918                            clear_user(&fps->fpq[0], 32 * sizeof(unsigned int)))
 919                                ret = -EFAULT;
 920                }
 921                break;
 922
 923        case PTRACE_SETFPREGS:
 924                ret = copy_regset_from_user(child, view, REGSET_FP,
 925                                            0 * sizeof(u32),
 926                                            32 * sizeof(u32),
 927                                            &fps->regs[0]);
 928                if (!ret)
 929                        ret = copy_regset_from_user(child, view, REGSET_FP,
 930                                                    33 * sizeof(u32),
 931                                                    1 * sizeof(u32),
 932                                                    &fps->fsr);
 933                break;
 934
 935        case PTRACE_READTEXT:
 936        case PTRACE_READDATA:
 937                ret = ptrace_readdata(child, addr,
 938                                      (char __user *)addr2, data);
 939                if (ret == data)
 940                        ret = 0;
 941                else if (ret >= 0)
 942                        ret = -EIO;
 943                break;
 944
 945        case PTRACE_WRITETEXT:
 946        case PTRACE_WRITEDATA:
 947                ret = ptrace_writedata(child, (char __user *) addr2,
 948                                       addr, data);
 949                if (ret == data)
 950                        ret = 0;
 951                else if (ret >= 0)
 952                        ret = -EIO;
 953                break;
 954
 955        default:
 956                if (request == PTRACE_SPARC_DETACH)
 957                        request = PTRACE_DETACH;
 958                ret = compat_ptrace_request(child, request, addr, data);
 959                break;
 960        }
 961
 962        return ret;
 963}
 964#endif /* CONFIG_COMPAT */
 965
 966struct fps {
 967        unsigned int regs[64];
 968        unsigned long fsr;
 969};
 970
 971long arch_ptrace(struct task_struct *child, long request,
 972                 unsigned long addr, unsigned long data)
 973{
 974        const struct user_regset_view *view = task_user_regset_view(current);
 975        unsigned long addr2 = task_pt_regs(current)->u_regs[UREG_I4];
 976        struct pt_regs __user *pregs;
 977        struct fps __user *fps;
 978        void __user *addr2p;
 979        int ret;
 980
 981        pregs = (struct pt_regs __user *) addr;
 982        fps = (struct fps __user *) addr;
 983        addr2p = (void __user *) addr2;
 984
 985        switch (request) {
 986        case PTRACE_PEEKUSR:
 987                ret = (addr != 0) ? -EIO : 0;
 988                break;
 989
 990        case PTRACE_GETREGS64:
 991                ret = copy_regset_to_user(child, view, REGSET_GENERAL,
 992                                          1 * sizeof(u64),
 993                                          15 * sizeof(u64),
 994                                          &pregs->u_regs[0]);
 995                if (!ret) {
 996                        /* XXX doesn't handle 'y' register correctly XXX */
 997                        ret = copy_regset_to_user(child, view, REGSET_GENERAL,
 998                                                  32 * sizeof(u64),
 999                                                  4 * sizeof(u64),
1000                                                  &pregs->tstate);
1001                }
1002                break;
1003
1004        case PTRACE_SETREGS64:
1005                ret = copy_regset_from_user(child, view, REGSET_GENERAL,
1006                                            1 * sizeof(u64),
1007                                            15 * sizeof(u64),
1008                                            &pregs->u_regs[0]);
1009                if (!ret) {
1010                        /* XXX doesn't handle 'y' register correctly XXX */
1011                        ret = copy_regset_from_user(child, view, REGSET_GENERAL,
1012                                                    32 * sizeof(u64),
1013                                                    4 * sizeof(u64),
1014                                                    &pregs->tstate);
1015                }
1016                break;
1017
1018        case PTRACE_GETFPREGS64:
1019                ret = copy_regset_to_user(child, view, REGSET_FP,
1020                                          0 * sizeof(u64),
1021                                          33 * sizeof(u64),
1022                                          fps);
1023                break;
1024
1025        case PTRACE_SETFPREGS64:
1026                ret = copy_regset_from_user(child, view, REGSET_FP,
1027                                          0 * sizeof(u64),
1028                                          33 * sizeof(u64),
1029                                          fps);
1030                break;
1031
1032        case PTRACE_READTEXT:
1033        case PTRACE_READDATA:
1034                ret = ptrace_readdata(child, addr, addr2p, data);
1035                if (ret == data)
1036                        ret = 0;
1037                else if (ret >= 0)
1038                        ret = -EIO;
1039                break;
1040
1041        case PTRACE_WRITETEXT:
1042        case PTRACE_WRITEDATA:
1043                ret = ptrace_writedata(child, addr2p, addr, data);
1044                if (ret == data)
1045                        ret = 0;
1046                else if (ret >= 0)
1047                        ret = -EIO;
1048                break;
1049
1050        default:
1051                if (request == PTRACE_SPARC_DETACH)
1052                        request = PTRACE_DETACH;
1053                ret = ptrace_request(child, request, addr, data);
1054                break;
1055        }
1056
1057        return ret;
1058}
1059
1060asmlinkage int syscall_trace_enter(struct pt_regs *regs)
1061{
1062        int ret = 0;
1063
1064        /* do the secure computing check first */
1065        secure_computing_strict(regs->u_regs[UREG_G1]);
1066
1067        if (test_thread_flag(TIF_SYSCALL_TRACE))
1068                ret = tracehook_report_syscall_entry(regs);
1069
1070        if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
1071                trace_sys_enter(regs, regs->u_regs[UREG_G1]);
1072
1073        audit_syscall_entry((test_thread_flag(TIF_32BIT) ?
1074                             AUDIT_ARCH_SPARC :
1075                             AUDIT_ARCH_SPARC64),
1076                            regs->u_regs[UREG_G1],
1077                            regs->u_regs[UREG_I0],
1078                            regs->u_regs[UREG_I1],
1079                            regs->u_regs[UREG_I2],
1080                            regs->u_regs[UREG_I3]);
1081
1082        return ret;
1083}
1084
1085asmlinkage void syscall_trace_leave(struct pt_regs *regs)
1086{
1087        audit_syscall_exit(regs);
1088
1089        if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
1090                trace_sys_exit(regs, regs->u_regs[UREG_G1]);
1091
1092        if (test_thread_flag(TIF_SYSCALL_TRACE))
1093                tracehook_report_syscall_exit(regs, 0);
1094}
1095