linux/arch/blackfin/kernel/ptrace.c
<<
>>
Prefs
   1/*
   2 * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
   3 * these modifications are Copyright 2004-2010 Analog Devices Inc.
   4 *
   5 * Licensed under the GPL-2
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/sched.h>
  10#include <linux/mm.h>
  11#include <linux/smp.h>
  12#include <linux/elf.h>
  13#include <linux/errno.h>
  14#include <linux/ptrace.h>
  15#include <linux/user.h>
  16#include <linux/regset.h>
  17#include <linux/signal.h>
  18#include <linux/tracehook.h>
  19#include <linux/uaccess.h>
  20
  21#include <asm/page.h>
  22#include <asm/pgtable.h>
  23#include <asm/processor.h>
  24#include <asm/asm-offsets.h>
  25#include <asm/dma.h>
  26#include <asm/fixed_code.h>
  27#include <asm/cacheflush.h>
  28#include <asm/mem_map.h>
  29#include <asm/mmu_context.h>
  30
  31/*
  32 * does not yet catch signals sent when the child dies.
  33 * in exit.c or in signal.c.
  34 */
  35
  36/*
  37 * Get contents of register REGNO in task TASK.
  38 */
  39static inline long
  40get_reg(struct task_struct *task, unsigned long regno,
  41        unsigned long __user *datap)
  42{
  43        long tmp;
  44        struct pt_regs *regs = task_pt_regs(task);
  45
  46        if (regno & 3 || regno > PT_LAST_PSEUDO)
  47                return -EIO;
  48
  49        switch (regno) {
  50        case PT_TEXT_ADDR:
  51                tmp = task->mm->start_code;
  52                break;
  53        case PT_TEXT_END_ADDR:
  54                tmp = task->mm->end_code;
  55                break;
  56        case PT_DATA_ADDR:
  57                tmp = task->mm->start_data;
  58                break;
  59        case PT_USP:
  60                tmp = task->thread.usp;
  61                break;
  62        default:
  63                if (regno < sizeof(*regs)) {
  64                        void *reg_ptr = regs;
  65                        tmp = *(long *)(reg_ptr + regno);
  66                } else
  67                        return -EIO;
  68        }
  69
  70        return put_user(tmp, datap);
  71}
  72
  73/*
  74 * Write contents of register REGNO in task TASK.
  75 */
  76static inline int
  77put_reg(struct task_struct *task, unsigned long regno, unsigned long data)
  78{
  79        struct pt_regs *regs = task_pt_regs(task);
  80
  81        if (regno & 3 || regno > PT_LAST_PSEUDO)
  82                return -EIO;
  83
  84        switch (regno) {
  85        case PT_PC:
  86                /*********************************************************************/
  87                /* At this point the kernel is most likely in exception.             */
  88                /* The RETX register will be used to populate the pc of the process. */
  89                /*********************************************************************/
  90                regs->retx = data;
  91                regs->pc = data;
  92                break;
  93        case PT_RETX:
  94                break;          /* regs->retx = data; break; */
  95        case PT_USP:
  96                regs->usp = data;
  97                task->thread.usp = data;
  98                break;
  99        case PT_SYSCFG: /* don't let userspace screw with this */
 100                if ((data & ~1) != 0x6)
 101                        pr_warning("ptrace: ignore syscfg write of %#lx\n", data);
 102                break;          /* regs->syscfg = data; break; */
 103        default:
 104                if (regno < sizeof(*regs)) {
 105                        void *reg_offset = regs;
 106                        *(long *)(reg_offset + regno) = data;
 107                }
 108                /* Ignore writes to pseudo registers */
 109        }
 110
 111        return 0;
 112}
 113
 114/*
 115 * check that an address falls within the bounds of the target process's memory mappings
 116 */
 117int
 118is_user_addr_valid(struct task_struct *child, unsigned long start, unsigned long len)
 119{
 120        bool valid;
 121        struct vm_area_struct *vma;
 122        struct sram_list_struct *sraml;
 123
 124        /* overflow */
 125        if (start + len < start)
 126                return -EIO;
 127
 128        down_read(&child->mm->mmap_sem);
 129        vma = find_vma(child->mm, start);
 130        valid = vma && start >= vma->vm_start && start + len <= vma->vm_end;
 131        up_read(&child->mm->mmap_sem);
 132        if (valid)
 133                return 0;
 134
 135        for (sraml = child->mm->context.sram_list; sraml; sraml = sraml->next)
 136                if (start >= (unsigned long)sraml->addr
 137                    && start + len < (unsigned long)sraml->addr + sraml->length)
 138                        return 0;
 139
 140        if (start >= FIXED_CODE_START && start + len < FIXED_CODE_END)
 141                return 0;
 142
 143#ifdef CONFIG_APP_STACK_L1
 144        if (child->mm->context.l1_stack_save)
 145                if (start >= (unsigned long)l1_stack_base &&
 146                        start + len < (unsigned long)l1_stack_base + l1_stack_len)
 147                        return 0;
 148#endif
 149
 150        return -EIO;
 151}
 152
 153/*
 154 * retrieve the contents of Blackfin userspace general registers
 155 */
 156static int genregs_get(struct task_struct *target,
 157                       const struct user_regset *regset,
 158                       unsigned int pos, unsigned int count,
 159                       void *kbuf, void __user *ubuf)
 160{
 161        struct pt_regs *regs = task_pt_regs(target);
 162        int ret;
 163
 164        /* This sucks ... */
 165        regs->usp = target->thread.usp;
 166
 167        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 168                                  regs, 0, sizeof(*regs));
 169        if (ret < 0)
 170                return ret;
 171
 172        return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 173                                        sizeof(*regs), -1);
 174}
 175
 176/*
 177 * update the contents of the Blackfin userspace general registers
 178 */
 179static int genregs_set(struct task_struct *target,
 180                       const struct user_regset *regset,
 181                       unsigned int pos, unsigned int count,
 182                       const void *kbuf, const void __user *ubuf)
 183{
 184        struct pt_regs *regs = task_pt_regs(target);
 185        int ret;
 186
 187        /* Don't let people set SYSCFG (it's at the end of pt_regs) */
 188        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 189                                 regs, 0, PT_SYSCFG);
 190        if (ret < 0)
 191                return ret;
 192
 193        /* This sucks ... */
 194        target->thread.usp = regs->usp;
 195        /* regs->retx = regs->pc; */
 196
 197        return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 198                                        PT_SYSCFG, -1);
 199}
 200
 201/*
 202 * Define the register sets available on the Blackfin under Linux
 203 */
 204enum bfin_regset {
 205        REGSET_GENERAL,
 206};
 207
 208static const struct user_regset bfin_regsets[] = {
 209        [REGSET_GENERAL] = {
 210                .core_note_type = NT_PRSTATUS,
 211                .n              = sizeof(struct pt_regs) / sizeof(long),
 212                .size           = sizeof(long),
 213                .align          = sizeof(long),
 214                .get            = genregs_get,
 215                .set            = genregs_set,
 216        },
 217};
 218
 219static const struct user_regset_view user_bfin_native_view = {
 220        .name      = "Blackfin",
 221        .e_machine = EM_BLACKFIN,
 222        .regsets   = bfin_regsets,
 223        .n         = ARRAY_SIZE(bfin_regsets),
 224};
 225
 226const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 227{
 228        return &user_bfin_native_view;
 229}
 230
 231void user_enable_single_step(struct task_struct *child)
 232{
 233        struct pt_regs *regs = task_pt_regs(child);
 234        regs->syscfg |= SYSCFG_SSSTEP;
 235
 236        set_tsk_thread_flag(child, TIF_SINGLESTEP);
 237}
 238
 239void user_disable_single_step(struct task_struct *child)
 240{
 241        struct pt_regs *regs = task_pt_regs(child);
 242        regs->syscfg &= ~SYSCFG_SSSTEP;
 243
 244        clear_tsk_thread_flag(child, TIF_SINGLESTEP);
 245}
 246
 247long arch_ptrace(struct task_struct *child, long request,
 248                 unsigned long addr, unsigned long data)
 249{
 250        int ret;
 251        unsigned long __user *datap = (unsigned long __user *)data;
 252        void *paddr = (void *)addr;
 253
 254        switch (request) {
 255                /* when I and D space are separate, these will need to be fixed. */
 256        case PTRACE_PEEKDATA:
 257                pr_debug("ptrace: PEEKDATA\n");
 258                /* fall through */
 259        case PTRACE_PEEKTEXT:   /* read word at location addr. */
 260                {
 261                        unsigned long tmp = 0;
 262                        int copied = 0, to_copy = sizeof(tmp);
 263
 264                        ret = -EIO;
 265                        pr_debug("ptrace: PEEKTEXT at addr 0x%08lx + %i\n", addr, to_copy);
 266                        if (is_user_addr_valid(child, addr, to_copy) < 0)
 267                                break;
 268                        pr_debug("ptrace: user address is valid\n");
 269
 270                        switch (bfin_mem_access_type(addr, to_copy)) {
 271                        case BFIN_MEM_ACCESS_CORE:
 272                        case BFIN_MEM_ACCESS_CORE_ONLY:
 273                                copied = ptrace_access_vm(child, addr, &tmp,
 274                                                           to_copy, FOLL_FORCE);
 275                                if (copied)
 276                                        break;
 277
 278                                /* hrm, why didn't that work ... maybe no mapping */
 279                                if (addr >= FIXED_CODE_START &&
 280                                    addr + to_copy <= FIXED_CODE_END) {
 281                                        copy_from_user_page(0, 0, 0, &tmp, paddr, to_copy);
 282                                        copied = to_copy;
 283                                } else if (addr >= BOOT_ROM_START) {
 284                                        memcpy(&tmp, paddr, to_copy);
 285                                        copied = to_copy;
 286                                }
 287
 288                                break;
 289                        case BFIN_MEM_ACCESS_DMA:
 290                                if (safe_dma_memcpy(&tmp, paddr, to_copy))
 291                                        copied = to_copy;
 292                                break;
 293                        case BFIN_MEM_ACCESS_ITEST:
 294                                if (isram_memcpy(&tmp, paddr, to_copy))
 295                                        copied = to_copy;
 296                                break;
 297                        default:
 298                                copied = 0;
 299                                break;
 300                        }
 301
 302                        pr_debug("ptrace: copied size %d [0x%08lx]\n", copied, tmp);
 303                        if (copied == to_copy)
 304                                ret = put_user(tmp, datap);
 305                        break;
 306                }
 307
 308                /* when I and D space are separate, this will have to be fixed. */
 309        case PTRACE_POKEDATA:
 310                pr_debug("ptrace: PTRACE_PEEKDATA\n");
 311                /* fall through */
 312        case PTRACE_POKETEXT:   /* write the word at location addr. */
 313                {
 314                        int copied = 0, to_copy = sizeof(data);
 315
 316                        ret = -EIO;
 317                        pr_debug("ptrace: POKETEXT at addr 0x%08lx + %i bytes %lx\n",
 318                                 addr, to_copy, data);
 319                        if (is_user_addr_valid(child, addr, to_copy) < 0)
 320                                break;
 321                        pr_debug("ptrace: user address is valid\n");
 322
 323                        switch (bfin_mem_access_type(addr, to_copy)) {
 324                        case BFIN_MEM_ACCESS_CORE:
 325                        case BFIN_MEM_ACCESS_CORE_ONLY:
 326                                copied = ptrace_access_vm(child, addr, &data,
 327                                                           to_copy,
 328                                                           FOLL_FORCE | FOLL_WRITE);
 329                                break;
 330                        case BFIN_MEM_ACCESS_DMA:
 331                                if (safe_dma_memcpy(paddr, &data, to_copy))
 332                                        copied = to_copy;
 333                                break;
 334                        case BFIN_MEM_ACCESS_ITEST:
 335                                if (isram_memcpy(paddr, &data, to_copy))
 336                                        copied = to_copy;
 337                                break;
 338                        default:
 339                                copied = 0;
 340                                break;
 341                        }
 342
 343                        pr_debug("ptrace: copied size %d\n", copied);
 344                        if (copied == to_copy)
 345                                ret = 0;
 346                        break;
 347                }
 348
 349        case PTRACE_PEEKUSR:
 350                switch (addr) {
 351#ifdef CONFIG_BINFMT_ELF_FDPIC  /* backwards compat */
 352                case PT_FDPIC_EXEC:
 353                        request = PTRACE_GETFDPIC;
 354                        addr = PTRACE_GETFDPIC_EXEC;
 355                        goto case_default;
 356                case PT_FDPIC_INTERP:
 357                        request = PTRACE_GETFDPIC;
 358                        addr = PTRACE_GETFDPIC_INTERP;
 359                        goto case_default;
 360#endif
 361                default:
 362                        ret = get_reg(child, addr, datap);
 363                }
 364                pr_debug("ptrace: PEEKUSR reg %li with %#lx = %i\n", addr, data, ret);
 365                break;
 366
 367        case PTRACE_POKEUSR:
 368                ret = put_reg(child, addr, data);
 369                pr_debug("ptrace: POKEUSR reg %li with %li = %i\n", addr, data, ret);
 370                break;
 371
 372        case PTRACE_GETREGS:
 373                pr_debug("ptrace: PTRACE_GETREGS\n");
 374                return copy_regset_to_user(child, &user_bfin_native_view,
 375                                           REGSET_GENERAL,
 376                                           0, sizeof(struct pt_regs),
 377                                           datap);
 378
 379        case PTRACE_SETREGS:
 380                pr_debug("ptrace: PTRACE_SETREGS\n");
 381                return copy_regset_from_user(child, &user_bfin_native_view,
 382                                             REGSET_GENERAL,
 383                                             0, sizeof(struct pt_regs),
 384                                             datap);
 385
 386        case_default:
 387        default:
 388                ret = ptrace_request(child, request, addr, data);
 389                break;
 390        }
 391
 392        return ret;
 393}
 394
 395asmlinkage int syscall_trace_enter(struct pt_regs *regs)
 396{
 397        int ret = 0;
 398
 399        if (test_thread_flag(TIF_SYSCALL_TRACE))
 400                ret = tracehook_report_syscall_entry(regs);
 401
 402        return ret;
 403}
 404
 405asmlinkage void syscall_trace_leave(struct pt_regs *regs)
 406{
 407        int step;
 408
 409        step = test_thread_flag(TIF_SINGLESTEP);
 410        if (step || test_thread_flag(TIF_SYSCALL_TRACE))
 411                tracehook_report_syscall_exit(regs, step);
 412}
 413