linux/arch/xtensa/kernel/ptrace.c
<<
>>
Prefs
   1// TODO some minor issues
   2/*
   3 * This file is subject to the terms and conditions of the GNU General Public
   4 * License.  See the file "COPYING" in the main directory of this archive
   5 * for more details.
   6 *
   7 * Copyright (C) 2001 - 2007  Tensilica Inc.
   8 *
   9 * Joe Taylor   <joe@tensilica.com, joetylr@yahoo.com>
  10 * Chris Zankel <chris@zankel.net>
  11 * Scott Foehner<sfoehner@yahoo.com>,
  12 * Kevin Chea
  13 * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca>
  14 */
  15
  16#include <linux/kernel.h>
  17#include <linux/sched.h>
  18#include <linux/mm.h>
  19#include <linux/errno.h>
  20#include <linux/ptrace.h>
  21#include <linux/smp.h>
  22#include <linux/security.h>
  23#include <linux/signal.h>
  24
  25#include <asm/pgtable.h>
  26#include <asm/page.h>
  27#include <asm/uaccess.h>
  28#include <asm/ptrace.h>
  29#include <asm/elf.h>
  30#include <asm/coprocessor.h>
  31
  32
  33void user_enable_single_step(struct task_struct *child)
  34{
  35        child->ptrace |= PT_SINGLESTEP;
  36}
  37
  38void user_disable_single_step(struct task_struct *child)
  39{
  40        child->ptrace &= ~PT_SINGLESTEP;
  41}
  42
  43/*
  44 * Called by kernel/ptrace.c when detaching to disable single stepping.
  45 */
  46
  47void ptrace_disable(struct task_struct *child)
  48{
  49        /* Nothing to do.. */
  50}
  51
  52int ptrace_getregs(struct task_struct *child, void __user *uregs)
  53{
  54        struct pt_regs *regs = task_pt_regs(child);
  55        xtensa_gregset_t __user *gregset = uregs;
  56        unsigned long wb = regs->windowbase;
  57        int i;
  58
  59        if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t)))
  60                return -EIO;
  61
  62        __put_user(regs->pc, &gregset->pc);
  63        __put_user(regs->ps & ~(1 << PS_EXCM_BIT), &gregset->ps);
  64        __put_user(regs->lbeg, &gregset->lbeg);
  65        __put_user(regs->lend, &gregset->lend);
  66        __put_user(regs->lcount, &gregset->lcount);
  67        __put_user(regs->windowstart, &gregset->windowstart);
  68        __put_user(regs->windowbase, &gregset->windowbase);
  69        __put_user(regs->threadptr, &gregset->threadptr);
  70
  71        for (i = 0; i < XCHAL_NUM_AREGS; i++)
  72                __put_user(regs->areg[i],
  73                                gregset->a + ((wb * 4 + i) % XCHAL_NUM_AREGS));
  74
  75        return 0;
  76}
  77
  78int ptrace_setregs(struct task_struct *child, void __user *uregs)
  79{
  80        struct pt_regs *regs = task_pt_regs(child);
  81        xtensa_gregset_t *gregset = uregs;
  82        const unsigned long ps_mask = PS_CALLINC_MASK | PS_OWB_MASK;
  83        unsigned long ps;
  84        unsigned long wb, ws;
  85
  86        if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t)))
  87                return -EIO;
  88
  89        __get_user(regs->pc, &gregset->pc);
  90        __get_user(ps, &gregset->ps);
  91        __get_user(regs->lbeg, &gregset->lbeg);
  92        __get_user(regs->lend, &gregset->lend);
  93        __get_user(regs->lcount, &gregset->lcount);
  94        __get_user(ws, &gregset->windowstart);
  95        __get_user(wb, &gregset->windowbase);
  96        __get_user(regs->threadptr, &gregset->threadptr);
  97
  98        regs->ps = (regs->ps & ~ps_mask) | (ps & ps_mask) | (1 << PS_EXCM_BIT);
  99
 100        if (wb >= XCHAL_NUM_AREGS / 4)
 101                return -EFAULT;
 102
 103        if (wb != regs->windowbase || ws != regs->windowstart) {
 104                unsigned long rotws, wmask;
 105
 106                rotws = (((ws | (ws << WSBITS)) >> wb) &
 107                                ((1 << WSBITS) - 1)) & ~1;
 108                wmask = ((rotws ? WSBITS + 1 - ffs(rotws) : 0) << 4) |
 109                        (rotws & 0xF) | 1;
 110                regs->windowbase = wb;
 111                regs->windowstart = ws;
 112                regs->wmask = wmask;
 113        }
 114
 115        if (wb != 0 &&  __copy_from_user(regs->areg + XCHAL_NUM_AREGS - wb * 4,
 116                                gregset->a, wb * 16))
 117                return -EFAULT;
 118
 119        if (__copy_from_user(regs->areg, gregset->a + wb * 4,
 120                                (WSBITS - wb) * 16))
 121                return -EFAULT;
 122
 123        return 0;
 124}
 125
 126
 127int ptrace_getxregs(struct task_struct *child, void __user *uregs)
 128{
 129        struct pt_regs *regs = task_pt_regs(child);
 130        struct thread_info *ti = task_thread_info(child);
 131        elf_xtregs_t __user *xtregs = uregs;
 132        int ret = 0;
 133
 134        if (!access_ok(VERIFY_WRITE, uregs, sizeof(elf_xtregs_t)))
 135                return -EIO;
 136
 137#if XTENSA_HAVE_COPROCESSORS
 138        /* Flush all coprocessor registers to memory. */
 139        coprocessor_flush_all(ti);
 140        ret |= __copy_to_user(&xtregs->cp0, &ti->xtregs_cp,
 141                              sizeof(xtregs_coprocessor_t));
 142#endif
 143        ret |= __copy_to_user(&xtregs->opt, &regs->xtregs_opt,
 144                              sizeof(xtregs->opt));
 145        ret |= __copy_to_user(&xtregs->user,&ti->xtregs_user,
 146                              sizeof(xtregs->user));
 147
 148        return ret ? -EFAULT : 0;
 149}
 150
 151int ptrace_setxregs(struct task_struct *child, void __user *uregs)
 152{
 153        struct thread_info *ti = task_thread_info(child);
 154        struct pt_regs *regs = task_pt_regs(child);
 155        elf_xtregs_t *xtregs = uregs;
 156        int ret = 0;
 157
 158        if (!access_ok(VERIFY_READ, uregs, sizeof(elf_xtregs_t)))
 159                return -EFAULT;
 160
 161#if XTENSA_HAVE_COPROCESSORS
 162        /* Flush all coprocessors before we overwrite them. */
 163        coprocessor_flush_all(ti);
 164        coprocessor_release_all(ti);
 165
 166        ret |= __copy_from_user(&ti->xtregs_cp, &xtregs->cp0,
 167                                sizeof(xtregs_coprocessor_t));
 168#endif
 169        ret |= __copy_from_user(&regs->xtregs_opt, &xtregs->opt,
 170                                sizeof(xtregs->opt));
 171        ret |= __copy_from_user(&ti->xtregs_user, &xtregs->user,
 172                                sizeof(xtregs->user));
 173
 174        return ret ? -EFAULT : 0;
 175}
 176
 177int ptrace_peekusr(struct task_struct *child, long regno, long __user *ret)
 178{
 179        struct pt_regs *regs;
 180        unsigned long tmp;
 181
 182        regs = task_pt_regs(child);
 183        tmp = 0;  /* Default return value. */
 184
 185        switch(regno) {
 186
 187                case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
 188                        tmp = regs->areg[regno - REG_AR_BASE];
 189                        break;
 190
 191                case REG_A_BASE ... REG_A_BASE + 15:
 192                        tmp = regs->areg[regno - REG_A_BASE];
 193                        break;
 194
 195                case REG_PC:
 196                        tmp = regs->pc;
 197                        break;
 198
 199                case REG_PS:
 200                        /* Note:  PS.EXCM is not set while user task is running;
 201                         * its being set in regs is for exception handling
 202                         * convenience.  */
 203                        tmp = (regs->ps & ~(1 << PS_EXCM_BIT));
 204                        break;
 205
 206                case REG_WB:
 207                        break;          /* tmp = 0 */
 208
 209                case REG_WS:
 210                {
 211                        unsigned long wb = regs->windowbase;
 212                        unsigned long ws = regs->windowstart;
 213                        tmp = ((ws>>wb) | (ws<<(WSBITS-wb))) & ((1<<WSBITS)-1);
 214                        break;
 215                }
 216                case REG_LBEG:
 217                        tmp = regs->lbeg;
 218                        break;
 219
 220                case REG_LEND:
 221                        tmp = regs->lend;
 222                        break;
 223
 224                case REG_LCOUNT:
 225                        tmp = regs->lcount;
 226                        break;
 227
 228                case REG_SAR:
 229                        tmp = regs->sar;
 230                        break;
 231
 232                case SYSCALL_NR:
 233                        tmp = regs->syscall;
 234                        break;
 235
 236                default:
 237                        return -EIO;
 238        }
 239        return put_user(tmp, ret);
 240}
 241
 242int ptrace_pokeusr(struct task_struct *child, long regno, long val)
 243{
 244        struct pt_regs *regs;
 245        regs = task_pt_regs(child);
 246
 247        switch (regno) {
 248                case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
 249                        regs->areg[regno - REG_AR_BASE] = val;
 250                        break;
 251
 252                case REG_A_BASE ... REG_A_BASE + 15:
 253                        regs->areg[regno - REG_A_BASE] = val;
 254                        break;
 255
 256                case REG_PC:
 257                        regs->pc = val;
 258                        break;
 259
 260                case SYSCALL_NR:
 261                        regs->syscall = val;
 262                        break;
 263
 264                default:
 265                        return -EIO;
 266        }
 267        return 0;
 268}
 269
 270long arch_ptrace(struct task_struct *child, long request,
 271                 unsigned long addr, unsigned long data)
 272{
 273        int ret = -EPERM;
 274        void __user *datap = (void __user *) data;
 275
 276        switch (request) {
 277        case PTRACE_PEEKTEXT:   /* read word at location addr. */
 278        case PTRACE_PEEKDATA:
 279                ret = generic_ptrace_peekdata(child, addr, data);
 280                break;
 281
 282        case PTRACE_PEEKUSR:    /* read register specified by addr. */
 283                ret = ptrace_peekusr(child, addr, datap);
 284                break;
 285
 286        case PTRACE_POKETEXT:   /* write the word at location addr. */
 287        case PTRACE_POKEDATA:
 288                ret = generic_ptrace_pokedata(child, addr, data);
 289                break;
 290
 291        case PTRACE_POKEUSR:    /* write register specified by addr. */
 292                ret = ptrace_pokeusr(child, addr, data);
 293                break;
 294
 295        case PTRACE_GETREGS:
 296                ret = ptrace_getregs(child, datap);
 297                break;
 298
 299        case PTRACE_SETREGS:
 300                ret = ptrace_setregs(child, datap);
 301                break;
 302
 303        case PTRACE_GETXTREGS:
 304                ret = ptrace_getxregs(child, datap);
 305                break;
 306
 307        case PTRACE_SETXTREGS:
 308                ret = ptrace_setxregs(child, datap);
 309                break;
 310
 311        default:
 312                ret = ptrace_request(child, request, addr, data);
 313                break;
 314        }
 315
 316        return ret;
 317}
 318
 319void do_syscall_trace(void)
 320{
 321        /*
 322         * The 0x80 provides a way for the tracing parent to distinguish
 323         * between a syscall stop and SIGTRAP delivery
 324         */
 325        ptrace_notify(SIGTRAP|((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
 326
 327        /*
 328         * this isn't the same as continuing with a signal, but it will do
 329         * for normal use.  strace only continues with a signal if the
 330         * stopping signal is not SIGTRAP.  -brl
 331         */
 332        if (current->exit_code) {
 333                send_sig(current->exit_code, current, 1);
 334                current->exit_code = 0;
 335        }
 336}
 337
 338void do_syscall_trace_enter(struct pt_regs *regs)
 339{
 340        if (test_thread_flag(TIF_SYSCALL_TRACE)
 341                        && (current->ptrace & PT_PTRACED))
 342                do_syscall_trace();
 343
 344#if 0
 345        audit_syscall_entry(current, AUDIT_ARCH_XTENSA..);
 346#endif
 347}
 348
 349void do_syscall_trace_leave(struct pt_regs *regs)
 350{
 351        if ((test_thread_flag(TIF_SYSCALL_TRACE))
 352                        && (current->ptrace & PT_PTRACED))
 353                do_syscall_trace();
 354}
 355