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 - 2009  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/security.h>
  21#include <linux/signal.h>
  22#include <linux/io.h>
  23#include <linux/audit.h>
  24#include <linux/seccomp.h>
  25#include <linux/tracehook.h>
  26#include <linux/elf.h>
  27#include <linux/regset.h>
  28#include <linux/hw_breakpoint.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 ptrace_triggered(struct perf_event *bp, int nmi,
  67                      struct perf_sample_data *data, struct pt_regs *regs)
  68{
  69        struct perf_event_attr attr;
  70
  71        /*
  72         * Disable the breakpoint request here since ptrace has defined a
  73         * one-shot behaviour for breakpoint exceptions.
  74         */
  75        attr = bp->attr;
  76        attr.disabled = true;
  77        modify_user_hw_breakpoint(bp, &attr);
  78}
  79
  80static int set_single_step(struct task_struct *tsk, unsigned long addr)
  81{
  82        struct thread_struct *thread = &tsk->thread;
  83        struct perf_event *bp;
  84        struct perf_event_attr attr;
  85
  86        bp = thread->ptrace_bps[0];
  87        if (!bp) {
  88                ptrace_breakpoint_init(&attr);
  89
  90                attr.bp_addr = addr;
  91                attr.bp_len = HW_BREAKPOINT_LEN_2;
  92                attr.bp_type = HW_BREAKPOINT_R;
  93
  94                bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk);
  95                if (IS_ERR(bp))
  96                        return PTR_ERR(bp);
  97
  98                thread->ptrace_bps[0] = bp;
  99        } else {
 100                int err;
 101
 102                attr = bp->attr;
 103                attr.bp_addr = addr;
 104                err = modify_user_hw_breakpoint(bp, &attr);
 105                if (unlikely(err))
 106                        return err;
 107        }
 108
 109        return 0;
 110}
 111
 112void user_enable_single_step(struct task_struct *child)
 113{
 114        unsigned long pc = get_stack_long(child, offsetof(struct pt_regs, pc));
 115
 116        set_tsk_thread_flag(child, TIF_SINGLESTEP);
 117
 118        set_single_step(child, pc);
 119}
 120
 121void user_disable_single_step(struct task_struct *child)
 122{
 123        clear_tsk_thread_flag(child, TIF_SINGLESTEP);
 124}
 125
 126/*
 127 * Called by kernel/ptrace.c when detaching..
 128 *
 129 * Make sure single step bits etc are not set.
 130 */
 131void ptrace_disable(struct task_struct *child)
 132{
 133        user_disable_single_step(child);
 134}
 135
 136static int genregs_get(struct task_struct *target,
 137                       const struct user_regset *regset,
 138                       unsigned int pos, unsigned int count,
 139                       void *kbuf, void __user *ubuf)
 140{
 141        const struct pt_regs *regs = task_pt_regs(target);
 142        int ret;
 143
 144        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 145                                  regs->regs,
 146                                  0, 16 * sizeof(unsigned long));
 147        if (!ret)
 148                /* PC, PR, SR, GBR, MACH, MACL, TRA */
 149                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 150                                          &regs->pc,
 151                                          offsetof(struct pt_regs, pc),
 152                                          sizeof(struct pt_regs));
 153        if (!ret)
 154                ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 155                                               sizeof(struct pt_regs), -1);
 156
 157        return ret;
 158}
 159
 160static int genregs_set(struct task_struct *target,
 161                       const struct user_regset *regset,
 162                       unsigned int pos, unsigned int count,
 163                       const void *kbuf, const void __user *ubuf)
 164{
 165        struct pt_regs *regs = task_pt_regs(target);
 166        int ret;
 167
 168        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 169                                 regs->regs,
 170                                 0, 16 * sizeof(unsigned long));
 171        if (!ret && count > 0)
 172                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 173                                         &regs->pc,
 174                                         offsetof(struct pt_regs, pc),
 175                                         sizeof(struct pt_regs));
 176        if (!ret)
 177                ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 178                                                sizeof(struct pt_regs), -1);
 179
 180        return ret;
 181}
 182
 183#ifdef CONFIG_SH_FPU
 184int fpregs_get(struct task_struct *target,
 185               const struct user_regset *regset,
 186               unsigned int pos, unsigned int count,
 187               void *kbuf, void __user *ubuf)
 188{
 189        int ret;
 190
 191        ret = init_fpu(target);
 192        if (ret)
 193                return ret;
 194
 195        if ((boot_cpu_data.flags & CPU_HAS_FPU))
 196                return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 197                                           &target->thread.xstate->hardfpu, 0, -1);
 198
 199        return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 200                                   &target->thread.xstate->softfpu, 0, -1);
 201}
 202
 203static int fpregs_set(struct task_struct *target,
 204                       const struct user_regset *regset,
 205                       unsigned int pos, unsigned int count,
 206                       const void *kbuf, const void __user *ubuf)
 207{
 208        int ret;
 209
 210        ret = init_fpu(target);
 211        if (ret)
 212                return ret;
 213
 214        set_stopped_child_used_math(target);
 215
 216        if ((boot_cpu_data.flags & CPU_HAS_FPU))
 217                return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 218                                          &target->thread.xstate->hardfpu, 0, -1);
 219
 220        return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 221                                  &target->thread.xstate->softfpu, 0, -1);
 222}
 223
 224static int fpregs_active(struct task_struct *target,
 225                         const struct user_regset *regset)
 226{
 227        return tsk_used_math(target) ? regset->n : 0;
 228}
 229#endif
 230
 231#ifdef CONFIG_SH_DSP
 232static int dspregs_get(struct task_struct *target,
 233                       const struct user_regset *regset,
 234                       unsigned int pos, unsigned int count,
 235                       void *kbuf, void __user *ubuf)
 236{
 237        const struct pt_dspregs *regs =
 238                (struct pt_dspregs *)&target->thread.dsp_status.dsp_regs;
 239        int ret;
 240
 241        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, regs,
 242                                  0, sizeof(struct pt_dspregs));
 243        if (!ret)
 244                ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 245                                               sizeof(struct pt_dspregs), -1);
 246
 247        return ret;
 248}
 249
 250static int dspregs_set(struct task_struct *target,
 251                       const struct user_regset *regset,
 252                       unsigned int pos, unsigned int count,
 253                       const void *kbuf, const void __user *ubuf)
 254{
 255        struct pt_dspregs *regs =
 256                (struct pt_dspregs *)&target->thread.dsp_status.dsp_regs;
 257        int ret;
 258
 259        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, regs,
 260                                 0, sizeof(struct pt_dspregs));
 261        if (!ret)
 262                ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 263                                                sizeof(struct pt_dspregs), -1);
 264
 265        return ret;
 266}
 267
 268static int dspregs_active(struct task_struct *target,
 269                          const struct user_regset *regset)
 270{
 271        struct pt_regs *regs = task_pt_regs(target);
 272
 273        return regs->sr & SR_DSP ? regset->n : 0;
 274}
 275#endif
 276
 277/*
 278 * These are our native regset flavours.
 279 */
 280enum sh_regset {
 281        REGSET_GENERAL,
 282#ifdef CONFIG_SH_FPU
 283        REGSET_FPU,
 284#endif
 285#ifdef CONFIG_SH_DSP
 286        REGSET_DSP,
 287#endif
 288};
 289
 290static const struct user_regset sh_regsets[] = {
 291        /*
 292         * Format is:
 293         *      R0 --> R15
 294         *      PC, PR, SR, GBR, MACH, MACL, TRA
 295         */
 296        [REGSET_GENERAL] = {
 297                .core_note_type = NT_PRSTATUS,
 298                .n              = ELF_NGREG,
 299                .size           = sizeof(long),
 300                .align          = sizeof(long),
 301                .get            = genregs_get,
 302                .set            = genregs_set,
 303        },
 304
 305#ifdef CONFIG_SH_FPU
 306        [REGSET_FPU] = {
 307                .core_note_type = NT_PRFPREG,
 308                .n              = sizeof(struct user_fpu_struct) / sizeof(long),
 309                .size           = sizeof(long),
 310                .align          = sizeof(long),
 311                .get            = fpregs_get,
 312                .set            = fpregs_set,
 313                .active         = fpregs_active,
 314        },
 315#endif
 316
 317#ifdef CONFIG_SH_DSP
 318        [REGSET_DSP] = {
 319                .n              = sizeof(struct pt_dspregs) / sizeof(long),
 320                .size           = sizeof(long),
 321                .align          = sizeof(long),
 322                .get            = dspregs_get,
 323                .set            = dspregs_set,
 324                .active         = dspregs_active,
 325        },
 326#endif
 327};
 328
 329static const struct user_regset_view user_sh_native_view = {
 330        .name           = "sh",
 331        .e_machine      = EM_SH,
 332        .regsets        = sh_regsets,
 333        .n              = ARRAY_SIZE(sh_regsets),
 334};
 335
 336const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 337{
 338        return &user_sh_native_view;
 339}
 340
 341long arch_ptrace(struct task_struct *child, long request, long addr, long data)
 342{
 343        struct user * dummy = NULL;
 344        unsigned long __user *datap = (unsigned long __user *)data;
 345        int ret;
 346
 347        switch (request) {
 348        /* read the word at location addr in the USER area. */
 349        case PTRACE_PEEKUSR: {
 350                unsigned long tmp;
 351
 352                ret = -EIO;
 353                if ((addr & 3) || addr < 0 ||
 354                    addr > sizeof(struct user) - 3)
 355                        break;
 356
 357                if (addr < sizeof(struct pt_regs))
 358                        tmp = get_stack_long(child, addr);
 359                else if (addr >= (long) &dummy->fpu &&
 360                         addr < (long) &dummy->u_fpvalid) {
 361                        if (!tsk_used_math(child)) {
 362                                if (addr == (long)&dummy->fpu.fpscr)
 363                                        tmp = FPSCR_INIT;
 364                                else
 365                                        tmp = 0;
 366                        } else
 367                                tmp = ((long *)child->thread.xstate)
 368                                        [(addr - (long)&dummy->fpu) >> 2];
 369                } else if (addr == (long) &dummy->u_fpvalid)
 370                        tmp = !!tsk_used_math(child);
 371                else if (addr == PT_TEXT_ADDR)
 372                        tmp = child->mm->start_code;
 373                else if (addr == PT_DATA_ADDR)
 374                        tmp = child->mm->start_data;
 375                else if (addr == PT_TEXT_END_ADDR)
 376                        tmp = child->mm->end_code;
 377                else if (addr == PT_TEXT_LEN)
 378                        tmp = child->mm->end_code - child->mm->start_code;
 379                else
 380                        tmp = 0;
 381                ret = put_user(tmp, datap);
 382                break;
 383        }
 384
 385        case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
 386                ret = -EIO;
 387                if ((addr & 3) || addr < 0 ||
 388                    addr > sizeof(struct user) - 3)
 389                        break;
 390
 391                if (addr < sizeof(struct pt_regs))
 392                        ret = put_stack_long(child, addr, data);
 393                else if (addr >= (long) &dummy->fpu &&
 394                         addr < (long) &dummy->u_fpvalid) {
 395                        set_stopped_child_used_math(child);
 396                        ((long *)child->thread.xstate)
 397                                [(addr - (long)&dummy->fpu) >> 2] = data;
 398                        ret = 0;
 399                } else if (addr == (long) &dummy->u_fpvalid) {
 400                        conditional_stopped_child_used_math(data, child);
 401                        ret = 0;
 402                }
 403                break;
 404
 405        case PTRACE_GETREGS:
 406                return copy_regset_to_user(child, &user_sh_native_view,
 407                                           REGSET_GENERAL,
 408                                           0, sizeof(struct pt_regs),
 409                                           (void __user *)data);
 410        case PTRACE_SETREGS:
 411                return copy_regset_from_user(child, &user_sh_native_view,
 412                                             REGSET_GENERAL,
 413                                             0, sizeof(struct pt_regs),
 414                                             (const void __user *)data);
 415#ifdef CONFIG_SH_FPU
 416        case PTRACE_GETFPREGS:
 417                return copy_regset_to_user(child, &user_sh_native_view,
 418                                           REGSET_FPU,
 419                                           0, sizeof(struct user_fpu_struct),
 420                                           (void __user *)data);
 421        case PTRACE_SETFPREGS:
 422                return copy_regset_from_user(child, &user_sh_native_view,
 423                                             REGSET_FPU,
 424                                             0, sizeof(struct user_fpu_struct),
 425                                             (const void __user *)data);
 426#endif
 427#ifdef CONFIG_SH_DSP
 428        case PTRACE_GETDSPREGS:
 429                return copy_regset_to_user(child, &user_sh_native_view,
 430                                           REGSET_DSP,
 431                                           0, sizeof(struct pt_dspregs),
 432                                           (void __user *)data);
 433        case PTRACE_SETDSPREGS:
 434                return copy_regset_from_user(child, &user_sh_native_view,
 435                                             REGSET_DSP,
 436                                             0, sizeof(struct pt_dspregs),
 437                                             (const void __user *)data);
 438#endif
 439        default:
 440                ret = ptrace_request(child, request, addr, data);
 441                break;
 442        }
 443
 444        return ret;
 445}
 446
 447static inline int audit_arch(void)
 448{
 449        int arch = EM_SH;
 450
 451#ifdef CONFIG_CPU_LITTLE_ENDIAN
 452        arch |= __AUDIT_ARCH_LE;
 453#endif
 454
 455        return arch;
 456}
 457
 458asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
 459{
 460        long ret = 0;
 461
 462        secure_computing(regs->regs[0]);
 463
 464        if (test_thread_flag(TIF_SYSCALL_TRACE) &&
 465            tracehook_report_syscall_entry(regs))
 466                /*
 467                 * Tracing decided this syscall should not happen.
 468                 * We'll return a bogus call number to get an ENOSYS
 469                 * error, but leave the original number in regs->regs[0].
 470                 */
 471                ret = -1L;
 472
 473        if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
 474                trace_sys_enter(regs, regs->regs[0]);
 475
 476        if (unlikely(current->audit_context))
 477                audit_syscall_entry(audit_arch(), regs->regs[3],
 478                                    regs->regs[4], regs->regs[5],
 479                                    regs->regs[6], regs->regs[7]);
 480
 481        return ret ?: regs->regs[0];
 482}
 483
 484asmlinkage void do_syscall_trace_leave(struct pt_regs *regs)
 485{
 486        int step;
 487
 488        if (unlikely(current->audit_context))
 489                audit_syscall_exit(AUDITSC_RESULT(regs->regs[0]),
 490                                   regs->regs[0]);
 491
 492        if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
 493                trace_sys_exit(regs, regs->regs[0]);
 494
 495        step = test_thread_flag(TIF_SINGLESTEP);
 496        if (step || test_thread_flag(TIF_SYSCALL_TRACE))
 497                tracehook_report_syscall_exit(regs, step);
 498}
 499
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.