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