linux/arch/sh/kernel/ptrace_32.c
<<
>>
Prefs
   1/*
   2 * SuperH process tracing
   3 *
   4 * Copyright (C) 1999, 2000  Kaz Kojima & Niibe Yutaka
   5 * Copyright (C) 2002 - 2008  Paul Mundt
   6 *
   7 * Audit support by Yuichi Nakamura <ynakam@hitachisoft.jp>
   8 *
   9 * This file is subject to the terms and conditions of the GNU General Public
  10 * License.  See the file "COPYING" in the main directory of this archive
  11 * for more details.
  12 */
  13#include <linux/kernel.h>
  14#include <linux/sched.h>
  15#include <linux/mm.h>
  16#include <linux/smp.h>
  17#include <linux/errno.h>
  18#include <linux/ptrace.h>
  19#include <linux/user.h>
  20#include <linux/slab.h>
  21#include <linux/security.h>
  22#include <linux/signal.h>
  23#include <linux/io.h>
  24#include <linux/audit.h>
  25#include <linux/seccomp.h>
  26#include <linux/tracehook.h>
  27#include <linux/elf.h>
  28#include <linux/regset.h>
  29#include <asm/uaccess.h>
  30#include <asm/pgtable.h>
  31#include <asm/system.h>
  32#include <asm/processor.h>
  33#include <asm/mmu_context.h>
  34#include <asm/syscalls.h>
  35#include <asm/fpu.h>
  36
  37#define CREATE_TRACE_POINTS
  38#include <trace/events/syscalls.h>
  39
  40/*
  41 * This routine will get a word off of the process kernel stack.
  42 */
  43static inline int get_stack_long(struct task_struct *task, int offset)
  44{
  45        unsigned char *stack;
  46
  47        stack = (unsigned char *)task_pt_regs(task);
  48        stack += offset;
  49        return (*((int *)stack));
  50}
  51
  52/*
  53 * This routine will put a word on the process kernel stack.
  54 */
  55static inline int put_stack_long(struct task_struct *task, int offset,
  56                                 unsigned long data)
  57{
  58        unsigned char *stack;
  59
  60        stack = (unsigned char *)task_pt_regs(task);
  61        stack += offset;
  62        *(unsigned long *) stack = data;
  63        return 0;
  64}
  65
  66void user_enable_single_step(struct task_struct *child)
  67{
  68        /* Next scheduling will set up UBC */
  69        if (child->thread.ubc_pc == 0)
  70                ubc_usercnt += 1;
  71
  72        child->thread.ubc_pc = get_stack_long(child,
  73                                offsetof(struct pt_regs, pc));
  74
  75        set_tsk_thread_flag(child, TIF_SINGLESTEP);
  76}
  77
  78void user_disable_single_step(struct task_struct *child)
  79{
  80        clear_tsk_thread_flag(child, TIF_SINGLESTEP);
  81
  82        /*
  83         * Ensure the UBC is not programmed at the next context switch.
  84         *
  85         * Normally this is not needed but there are sequences such as
  86         * singlestep, signal delivery, and continue that leave the
  87         * ubc_pc non-zero leading to spurious SIGTRAPs.
  88         */
  89        if (child->thread.ubc_pc != 0) {
  90                ubc_usercnt -= 1;
  91                child->thread.ubc_pc = 0;
  92        }
  93}
  94
  95/*
  96 * Called by kernel/ptrace.c when detaching..
  97 *
  98 * Make sure single step bits etc are not set.
  99 */
 100void ptrace_disable(struct task_struct *child)
 101{
 102        user_disable_single_step(child);
 103}
 104
 105static int genregs_get(struct task_struct *target,
 106                       const struct user_regset *regset,
 107                       unsigned int pos, unsigned int count,
 108                       void *kbuf, void __user *ubuf)
 109{
 110        const struct pt_regs *regs = task_pt_regs(target);
 111        int ret;
 112
 113        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 114                                  regs->regs,
 115                                  0, 16 * sizeof(unsigned long));
 116        if (!ret)
 117                /* PC, PR, SR, GBR, MACH, MACL, TRA */
 118                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 119                                          &regs->pc,
 120                                          offsetof(struct pt_regs, pc),
 121                                          sizeof(struct pt_regs));
 122        if (!ret)
 123                ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 124                                               sizeof(struct pt_regs), -1);
 125
 126        return ret;
 127}
 128
 129static int genregs_set(struct task_struct *target,
 130                       const struct user_regset *regset,
 131                       unsigned int pos, unsigned int count,
 132                       const void *kbuf, const void __user *ubuf)
 133{
 134        struct pt_regs *regs = task_pt_regs(target);
 135        int ret;
 136
 137        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 138                                 regs->regs,
 139                                 0, 16 * sizeof(unsigned long));
 140        if (!ret && count > 0)
 141                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 142                                         &regs->pc,
 143                                         offsetof(struct pt_regs, pc),
 144                                         sizeof(struct pt_regs));
 145        if (!ret)
 146                ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 147                                                sizeof(struct pt_regs), -1);
 148
 149        return ret;
 150}
 151
 152#ifdef CONFIG_SH_FPU
 153int fpregs_get(struct task_struct *target,
 154               const struct user_regset *regset,
 155               unsigned int pos, unsigned int count,
 156               void *kbuf, void __user *ubuf)
 157{
 158        int ret;
 159
 160        ret = init_fpu(target);
 161        if (ret)
 162                return ret;
 163
 164        if ((boot_cpu_data.flags & CPU_HAS_FPU))
 165                return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 166                                           &target->thread.fpu.hard, 0, -1);
 167
 168        return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 169                                   &target->thread.fpu.soft, 0, -1);
 170}
 171
 172static int fpregs_set(struct task_struct *target,
 173                       const struct user_regset *regset,
 174                       unsigned int pos, unsigned int count,
 175                       const void *kbuf, const void __user *ubuf)
 176{
 177        int ret;
 178
 179        ret = init_fpu(target);
 180        if (ret)
 181                return ret;
 182
 183        set_stopped_child_used_math(target);
 184
 185        if ((boot_cpu_data.flags & CPU_HAS_FPU))
 186                return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 187                                          &target->thread.fpu.hard, 0, -1);
 188
 189        return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 190                                  &target->thread.fpu.soft, 0, -1);
 191}
 192
 193static int fpregs_active(struct task_struct *target,
 194                         const struct user_regset *regset)
 195{
 196        return tsk_used_math(target) ? regset->n : 0;
 197}
 198#endif
 199
 200#ifdef CONFIG_SH_DSP
 201static int dspregs_get(struct task_struct *target,
 202                       const struct user_regset *regset,
 203                       unsigned int pos, unsigned int count,
 204                       void *kbuf, void __user *ubuf)
 205{
 206        const struct pt_dspregs *regs =
 207                (struct pt_dspregs *)&target->thread.dsp_status.dsp_regs;
 208        int ret;
 209
 210        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, regs,
 211                                  0, sizeof(struct pt_dspregs));
 212        if (!ret)
 213                ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 214                                               sizeof(struct pt_dspregs), -1);
 215
 216        return ret;
 217}
 218
 219static int dspregs_set(struct task_struct *target,
 220                       const struct user_regset *regset,
 221                       unsigned int pos, unsigned int count,
 222                       const void *kbuf, const void __user *ubuf)
 223{
 224        struct pt_dspregs *regs =
 225                (struct pt_dspregs *)&target->thread.dsp_status.dsp_regs;
 226        int ret;
 227
 228        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, regs,
 229                                 0, sizeof(struct pt_dspregs));
 230        if (!ret)
 231                ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 232                                                sizeof(struct pt_dspregs), -1);
 233
 234        return ret;
 235}
 236
 237static int dspregs_active(struct task_struct *target,
 238                          const struct user_regset *regset)
 239{
 240        struct pt_regs *regs = task_pt_regs(target);
 241
 242        return regs->sr & SR_DSP ? regset->n : 0;
 243}
 244#endif
 245
 246/*
 247 * These are our native regset flavours.
 248 */
 249enum sh_regset {
 250        REGSET_GENERAL,
 251#ifdef CONFIG_SH_FPU
 252        REGSET_FPU,
 253#endif
 254#ifdef CONFIG_SH_DSP
 255        REGSET_DSP,
 256#endif
 257};
 258
 259static const struct user_regset sh_regsets[] = {
 260        /*
 261         * Format is:
 262         *      R0 --> R15
 263         *      PC, PR, SR, GBR, MACH, MACL, TRA
 264         */
 265        [REGSET_GENERAL] = {
 266                .core_note_type = NT_PRSTATUS,
 267                .n              = ELF_NGREG,
 268                .size           = sizeof(long),
 269                .align          = sizeof(long),
 270                .get            = genregs_get,
 271                .set            = genregs_set,
 272        },
 273
 274#ifdef CONFIG_SH_FPU
 275        [REGSET_FPU] = {
 276                .core_note_type = NT_PRFPREG,
 277                .n              = sizeof(struct user_fpu_struct) / sizeof(long),
 278                .size           = sizeof(long),
 279                .align          = sizeof(long),
 280                .get            = fpregs_get,
 281                .set            = fpregs_set,
 282                .active         = fpregs_active,
 283        },
 284#endif
 285
 286#ifdef CONFIG_SH_DSP
 287        [REGSET_DSP] = {
 288                .n              = sizeof(struct pt_dspregs) / sizeof(long),
 289                .size           = sizeof(long),
 290                .align          = sizeof(long),
 291                .get            = dspregs_get,
 292                .set            = dspregs_set,
 293                .active         = dspregs_active,
 294        },
 295#endif
 296};
 297
 298static const struct user_regset_view user_sh_native_view = {
 299        .name           = "sh",
 300        .e_machine      = EM_SH,
 301        .regsets        = sh_regsets,
 302        .n              = ARRAY_SIZE(sh_regsets),
 303};
 304
 305const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 306{
 307        return &user_sh_native_view;
 308}
 309
 310long arch_ptrace(struct task_struct *child, long request, long addr, long data)
 311{
 312        struct user * dummy = NULL;
 313        unsigned long __user *datap = (unsigned long __user *)data;
 314        int ret;
 315
 316        switch (request) {
 317        /* read the word at location addr in the USER area. */
 318        case PTRACE_PEEKUSR: {
 319                unsigned long tmp;
 320
 321                ret = -EIO;
 322                if ((addr & 3) || addr < 0 ||
 323                    addr > sizeof(struct user) - 3)
 324                        break;
 325
 326                if (addr < sizeof(struct pt_regs))
 327                        tmp = get_stack_long(child, addr);
 328                else if (addr >= (long) &dummy->fpu &&
 329                         addr < (long) &dummy->u_fpvalid) {
 330                        if (!tsk_used_math(child)) {
 331                                if (addr == (long)&dummy->fpu.fpscr)
 332                                        tmp = FPSCR_INIT;
 333                                else
 334                                        tmp = 0;
 335                        } else
 336                                tmp = ((long *)&child->thread.fpu)
 337                                        [(addr - (long)&dummy->fpu) >> 2];
 338                } else if (addr == (long) &dummy->u_fpvalid)
 339                        tmp = !!tsk_used_math(child);
 340                else if (addr == PT_TEXT_ADDR)
 341                        tmp = child->mm->start_code;
 342                else if (addr == PT_DATA_ADDR)
 343                        tmp = child->mm->start_data;
 344                else if (addr == PT_TEXT_END_ADDR)
 345                        tmp = child->mm->end_code;
 346                else if (addr == PT_TEXT_LEN)
 347                        tmp = child->mm->end_code - child->mm->start_code;
 348                else
 349                        tmp = 0;
 350                ret = put_user(tmp, datap);
 351                break;
 352        }
 353
 354        case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
 355                ret = -EIO;
 356                if ((addr & 3) || addr < 0 ||
 357                    addr > sizeof(struct user) - 3)
 358                        break;
 359
 360                if (addr < sizeof(struct pt_regs))
 361                        ret = put_stack_long(child, addr, data);
 362                else if (addr >= (long) &dummy->fpu &&
 363                         addr < (long) &dummy->u_fpvalid) {
 364                        set_stopped_child_used_math(child);
 365                        ((long *)&child->thread.fpu)
 366                                [(addr - (long)&dummy->fpu) >> 2] = data;
 367                        ret = 0;
 368                } else if (addr == (long) &dummy->u_fpvalid) {
 369                        conditional_stopped_child_used_math(data, child);
 370                        ret = 0;
 371                }
 372                break;
 373
 374        case PTRACE_GETREGS:
 375                return copy_regset_to_user(child, &user_sh_native_view,
 376                                           REGSET_GENERAL,
 377                                           0, sizeof(struct pt_regs),
 378                                           (void __user *)data);
 379        case PTRACE_SETREGS:
 380                return copy_regset_from_user(child, &user_sh_native_view,
 381                                             REGSET_GENERAL,
 382                                             0, sizeof(struct pt_regs),
 383                                             (const void __user *)data);
 384#ifdef CONFIG_SH_FPU
 385        case PTRACE_GETFPREGS:
 386                return copy_regset_to_user(child, &user_sh_native_view,
 387                                           REGSET_FPU,
 388                                           0, sizeof(struct user_fpu_struct),
 389                                           (void __user *)data);
 390        case PTRACE_SETFPREGS:
 391                return copy_regset_from_user(child, &user_sh_native_view,
 392                                             REGSET_FPU,
 393                                             0, sizeof(struct user_fpu_struct),
 394                                             (const void __user *)data);
 395#endif
 396#ifdef CONFIG_SH_DSP
 397        case PTRACE_GETDSPREGS:
 398                return copy_regset_to_user(child, &user_sh_native_view,
 399                                           REGSET_DSP,
 400                                           0, sizeof(struct pt_dspregs),
 401                                           (void __user *)data);
 402        case PTRACE_SETDSPREGS:
 403                return copy_regset_from_user(child, &user_sh_native_view,
 404                                             REGSET_DSP,
 405                                             0, sizeof(struct pt_dspregs),
 406                                             (const void __user *)data);
 407#endif
 408#ifdef CONFIG_BINFMT_ELF_FDPIC
 409        case PTRACE_GETFDPIC: {
 410                unsigned long tmp = 0;
 411
 412                switch (addr) {
 413                case PTRACE_GETFDPIC_EXEC:
 414                        tmp = child->mm->context.exec_fdpic_loadmap;
 415                        break;
 416                case PTRACE_GETFDPIC_INTERP:
 417                        tmp = child->mm->context.interp_fdpic_loadmap;
 418                        break;
 419                default:
 420                        break;
 421                }
 422
 423                ret = 0;
 424                if (put_user(tmp, datap)) {
 425                        ret = -EFAULT;
 426                        break;
 427                }
 428                break;
 429        }
 430#endif
 431        default:
 432                ret = ptrace_request(child, request, addr, data);
 433                break;
 434        }
 435
 436        return ret;
 437}
 438
 439static inline int audit_arch(void)
 440{
 441        int arch = EM_SH;
 442
 443#ifdef CONFIG_CPU_LITTLE_ENDIAN
 444        arch |= __AUDIT_ARCH_LE;
 445#endif
 446
 447        return arch;
 448}
 449
 450asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
 451{
 452        long ret = 0;
 453
 454        secure_computing(regs->regs[0]);
 455
 456        if (test_thread_flag(TIF_SYSCALL_TRACE) &&
 457            tracehook_report_syscall_entry(regs))
 458                /*
 459                 * Tracing decided this syscall should not happen.
 460                 * We'll return a bogus call number to get an ENOSYS
 461                 * error, but leave the original number in regs->regs[0].
 462                 */
 463                ret = -1L;
 464
 465        if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
 466                trace_sys_enter(regs, regs->regs[0]);
 467
 468        if (unlikely(current->audit_context))
 469                audit_syscall_entry(audit_arch(), regs->regs[3],
 470                                    regs->regs[4], regs->regs[5],
 471                                    regs->regs[6], regs->regs[7]);
 472
 473        return ret ?: regs->regs[0];
 474}
 475
 476asmlinkage void do_syscall_trace_leave(struct pt_regs *regs)
 477{
 478        int step;
 479
 480        if (unlikely(current->audit_context))
 481                audit_syscall_exit(AUDITSC_RESULT(regs->regs[0]),
 482                                   regs->regs[0]);
 483
 484        if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
 485                trace_sys_exit(regs, regs->regs[0]);
 486
 487        step = test_thread_flag(TIF_SINGLESTEP);
 488        if (step || test_thread_flag(TIF_SYSCALL_TRACE))
 489                tracehook_report_syscall_exit(regs, step);
 490}
 491