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 = access_process_vm(child, addr, &tmp,
 274                                                           to_copy, 0);
 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 = access_process_vm(child, addr, &data,
 327                                                           to_copy, 1);
 328                                break;
 329                        case BFIN_MEM_ACCESS_DMA:
 330                                if (safe_dma_memcpy(paddr, &data, to_copy))
 331                                        copied = to_copy;
 332                                break;
 333                        case BFIN_MEM_ACCESS_ITEST:
 334                                if (isram_memcpy(paddr, &data, to_copy))
 335                                        copied = to_copy;
 336                                break;
 337                        default:
 338                                copied = 0;
 339                                break;
 340                        }
 341
 342                        pr_debug("ptrace: copied size %d\n", copied);
 343                        if (copied == to_copy)
 344                                ret = 0;
 345                        break;
 346                }
 347
 348        case PTRACE_PEEKUSR:
 349                switch (addr) {
 350#ifdef CONFIG_BINFMT_ELF_FDPIC  /* backwards compat */
 351                case PT_FDPIC_EXEC:
 352                        request = PTRACE_GETFDPIC;
 353                        addr = PTRACE_GETFDPIC_EXEC;
 354                        goto case_default;
 355                case PT_FDPIC_INTERP:
 356                        request = PTRACE_GETFDPIC;
 357                        addr = PTRACE_GETFDPIC_INTERP;
 358                        goto case_default;
 359#endif
 360                default:
 361                        ret = get_reg(child, addr, datap);
 362                }
 363                pr_debug("ptrace: PEEKUSR reg %li with %#lx = %i\n", addr, data, ret);
 364                break;
 365
 366        case PTRACE_POKEUSR:
 367                ret = put_reg(child, addr, data);
 368                pr_debug("ptrace: POKEUSR reg %li with %li = %i\n", addr, data, ret);
 369                break;
 370
 371        case PTRACE_GETREGS:
 372                pr_debug("ptrace: PTRACE_GETREGS\n");
 373                return copy_regset_to_user(child, &user_bfin_native_view,
 374                                           REGSET_GENERAL,
 375                                           0, sizeof(struct pt_regs),
 376                                           datap);
 377
 378        case PTRACE_SETREGS:
 379                pr_debug("ptrace: PTRACE_SETREGS\n");
 380                return copy_regset_from_user(child, &user_bfin_native_view,
 381                                             REGSET_GENERAL,
 382                                             0, sizeof(struct pt_regs),
 383                                             datap);
 384
 385        case_default:
 386        default:
 387                ret = ptrace_request(child, request, addr, data);
 388                break;
 389        }
 390
 391        return ret;
 392}
 393
 394asmlinkage int syscall_trace_enter(struct pt_regs *regs)
 395{
 396        int ret = 0;
 397
 398        if (test_thread_flag(TIF_SYSCALL_TRACE))
 399                ret = tracehook_report_syscall_entry(regs);
 400
 401        return ret;
 402}
 403
 404asmlinkage void syscall_trace_leave(struct pt_regs *regs)
 405{
 406        int step;
 407
 408        step = test_thread_flag(TIF_SINGLESTEP);
 409        if (step || test_thread_flag(TIF_SYSCALL_TRACE))
 410                tracehook_report_syscall_exit(regs, step);
 411}
 412