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