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