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        struct vm_area_struct *vma;
 121        struct sram_list_struct *sraml;
 122
 123        /* overflow */
 124        if (start + len < start)
 125                return -EIO;
 126
 127        vma = find_vma(child->mm, start);
 128        if (vma && start >= vma->vm_start && start + len <= vma->vm_end)
 129                        return 0;
 130
 131        for (sraml = child->mm->context.sram_list; sraml; sraml = sraml->next)
 132                if (start >= (unsigned long)sraml->addr
 133                    && start + len < (unsigned long)sraml->addr + sraml->length)
 134                        return 0;
 135
 136        if (start >= FIXED_CODE_START && start + len < FIXED_CODE_END)
 137                return 0;
 138
 139#ifdef CONFIG_APP_STACK_L1
 140        if (child->mm->context.l1_stack_save)
 141                if (start >= (unsigned long)l1_stack_base &&
 142                        start + len < (unsigned long)l1_stack_base + l1_stack_len)
 143                        return 0;
 144#endif
 145
 146        return -EIO;
 147}
 148
 149/*
 150 * retrieve the contents of Blackfin userspace general registers
 151 */
 152static int genregs_get(struct task_struct *target,
 153                       const struct user_regset *regset,
 154                       unsigned int pos, unsigned int count,
 155                       void *kbuf, void __user *ubuf)
 156{
 157        struct pt_regs *regs = task_pt_regs(target);
 158        int ret;
 159
 160        /* This sucks ... */
 161        regs->usp = target->thread.usp;
 162
 163        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 164                                  regs, 0, sizeof(*regs));
 165        if (ret < 0)
 166                return ret;
 167
 168        return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 169                                        sizeof(*regs), -1);
 170}
 171
 172/*
 173 * update the contents of the Blackfin userspace general registers
 174 */
 175static int genregs_set(struct task_struct *target,
 176                       const struct user_regset *regset,
 177                       unsigned int pos, unsigned int count,
 178                       const void *kbuf, const void __user *ubuf)
 179{
 180        struct pt_regs *regs = task_pt_regs(target);
 181        int ret;
 182
 183        /* Don't let people set SYSCFG (it's at the end of pt_regs) */
 184        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 185                                 regs, 0, PT_SYSCFG);
 186        if (ret < 0)
 187                return ret;
 188
 189        /* This sucks ... */
 190        target->thread.usp = regs->usp;
 191        /* regs->retx = regs->pc; */
 192
 193        return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 194                                        PT_SYSCFG, -1);
 195}
 196
 197/*
 198 * Define the register sets available on the Blackfin under Linux
 199 */
 200enum bfin_regset {
 201        REGSET_GENERAL,
 202};
 203
 204static const struct user_regset bfin_regsets[] = {
 205        [REGSET_GENERAL] = {
 206                .core_note_type = NT_PRSTATUS,
 207                .n              = sizeof(struct pt_regs) / sizeof(long),
 208                .size           = sizeof(long),
 209                .align          = sizeof(long),
 210                .get            = genregs_get,
 211                .set            = genregs_set,
 212        },
 213};
 214
 215static const struct user_regset_view user_bfin_native_view = {
 216        .name      = "Blackfin",
 217        .e_machine = EM_BLACKFIN,
 218        .regsets   = bfin_regsets,
 219        .n         = ARRAY_SIZE(bfin_regsets),
 220};
 221
 222const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 223{
 224        return &user_bfin_native_view;
 225}
 226
 227void user_enable_single_step(struct task_struct *child)
 228{
 229        struct pt_regs *regs = task_pt_regs(child);
 230        regs->syscfg |= SYSCFG_SSSTEP;
 231
 232        set_tsk_thread_flag(child, TIF_SINGLESTEP);
 233}
 234
 235void user_disable_single_step(struct task_struct *child)
 236{
 237        struct pt_regs *regs = task_pt_regs(child);
 238        regs->syscfg &= ~SYSCFG_SSSTEP;
 239
 240        clear_tsk_thread_flag(child, TIF_SINGLESTEP);
 241}
 242
 243long arch_ptrace(struct task_struct *child, long request,
 244                 unsigned long addr, unsigned long data)
 245{
 246        int ret;
 247        unsigned long __user *datap = (unsigned long __user *)data;
 248        void *paddr = (void *)addr;
 249
 250        switch (request) {
 251                /* when I and D space are separate, these will need to be fixed. */
 252        case PTRACE_PEEKDATA:
 253                pr_debug("ptrace: PEEKDATA\n");
 254                /* fall through */
 255        case PTRACE_PEEKTEXT:   /* read word at location addr. */
 256                {
 257                        unsigned long tmp = 0;
 258                        int copied = 0, to_copy = sizeof(tmp);
 259
 260                        ret = -EIO;
 261                        pr_debug("ptrace: PEEKTEXT at addr 0x%08lx + %i\n", addr, to_copy);
 262                        if (is_user_addr_valid(child, addr, to_copy) < 0)
 263                                break;
 264                        pr_debug("ptrace: user address is valid\n");
 265
 266                        switch (bfin_mem_access_type(addr, to_copy)) {
 267                        case BFIN_MEM_ACCESS_CORE:
 268                        case BFIN_MEM_ACCESS_CORE_ONLY:
 269                                copied = access_process_vm(child, addr, &tmp,
 270                                                           to_copy, 0);
 271                                if (copied)
 272                                        break;
 273
 274                                /* hrm, why didn't that work ... maybe no mapping */
 275                                if (addr >= FIXED_CODE_START &&
 276                                    addr + to_copy <= FIXED_CODE_END) {
 277                                        copy_from_user_page(0, 0, 0, &tmp, paddr, to_copy);
 278                                        copied = to_copy;
 279                                } else if (addr >= BOOT_ROM_START) {
 280                                        memcpy(&tmp, paddr, to_copy);
 281                                        copied = to_copy;
 282                                }
 283
 284                                break;
 285                        case BFIN_MEM_ACCESS_DMA:
 286                                if (safe_dma_memcpy(&tmp, paddr, to_copy))
 287                                        copied = to_copy;
 288                                break;
 289                        case BFIN_MEM_ACCESS_ITEST:
 290                                if (isram_memcpy(&tmp, paddr, to_copy))
 291                                        copied = to_copy;
 292                                break;
 293                        default:
 294                                copied = 0;
 295                                break;
 296                        }
 297
 298                        pr_debug("ptrace: copied size %d [0x%08lx]\n", copied, tmp);
 299                        if (copied == to_copy)
 300                                ret = put_user(tmp, datap);
 301                        break;
 302                }
 303
 304                /* when I and D space are separate, this will have to be fixed. */
 305        case PTRACE_POKEDATA:
 306                pr_debug("ptrace: PTRACE_PEEKDATA\n");
 307                /* fall through */
 308        case PTRACE_POKETEXT:   /* write the word at location addr. */
 309                {
 310                        int copied = 0, to_copy = sizeof(data);
 311
 312                        ret = -EIO;
 313                        pr_debug("ptrace: POKETEXT at addr 0x%08lx + %i bytes %lx\n",
 314                                 addr, to_copy, data);
 315                        if (is_user_addr_valid(child, addr, to_copy) < 0)
 316                                break;
 317                        pr_debug("ptrace: user address is valid\n");
 318
 319                        switch (bfin_mem_access_type(addr, to_copy)) {
 320                        case BFIN_MEM_ACCESS_CORE:
 321                        case BFIN_MEM_ACCESS_CORE_ONLY:
 322                                copied = access_process_vm(child, addr, &data,
 323                                                           to_copy, 1);
 324                                break;
 325                        case BFIN_MEM_ACCESS_DMA:
 326                                if (safe_dma_memcpy(paddr, &data, to_copy))
 327                                        copied = to_copy;
 328                                break;
 329                        case BFIN_MEM_ACCESS_ITEST:
 330                                if (isram_memcpy(paddr, &data, to_copy))
 331                                        copied = to_copy;
 332                                break;
 333                        default:
 334                                copied = 0;
 335                                break;
 336                        }
 337
 338                        pr_debug("ptrace: copied size %d\n", copied);
 339                        if (copied == to_copy)
 340                                ret = 0;
 341                        break;
 342                }
 343
 344        case PTRACE_PEEKUSR:
 345                switch (addr) {
 346#ifdef CONFIG_BINFMT_ELF_FDPIC  /* backwards compat */
 347                case PT_FDPIC_EXEC:
 348                        request = PTRACE_GETFDPIC;
 349                        addr = PTRACE_GETFDPIC_EXEC;
 350                        goto case_default;
 351                case PT_FDPIC_INTERP:
 352                        request = PTRACE_GETFDPIC;
 353                        addr = PTRACE_GETFDPIC_INTERP;
 354                        goto case_default;
 355#endif
 356                default:
 357                        ret = get_reg(child, addr, datap);
 358                }
 359                pr_debug("ptrace: PEEKUSR reg %li with %#lx = %i\n", addr, data, ret);
 360                break;
 361
 362        case PTRACE_POKEUSR:
 363                ret = put_reg(child, addr, data);
 364                pr_debug("ptrace: POKEUSR reg %li with %li = %i\n", addr, data, ret);
 365                break;
 366
 367        case PTRACE_GETREGS:
 368                pr_debug("ptrace: PTRACE_GETREGS\n");
 369                return copy_regset_to_user(child, &user_bfin_native_view,
 370                                           REGSET_GENERAL,
 371                                           0, sizeof(struct pt_regs),
 372                                           datap);
 373
 374        case PTRACE_SETREGS:
 375                pr_debug("ptrace: PTRACE_SETREGS\n");
 376                return copy_regset_from_user(child, &user_bfin_native_view,
 377                                             REGSET_GENERAL,
 378                                             0, sizeof(struct pt_regs),
 379                                             datap);
 380
 381        case_default:
 382        default:
 383                ret = ptrace_request(child, request, addr, data);
 384                break;
 385        }
 386
 387        return ret;
 388}
 389
 390asmlinkage int syscall_trace_enter(struct pt_regs *regs)
 391{
 392        int ret = 0;
 393
 394        if (test_thread_flag(TIF_SYSCALL_TRACE))
 395                ret = tracehook_report_syscall_entry(regs);
 396
 397        return ret;
 398}
 399
 400asmlinkage void syscall_trace_leave(struct pt_regs *regs)
 401{
 402        int step;
 403
 404        step = test_thread_flag(TIF_SINGLESTEP);
 405        if (step || test_thread_flag(TIF_SYSCALL_TRACE))
 406                tracehook_report_syscall_exit(regs, step);
 407}
 408