linux/arch/score/kernel/ptrace.c
<<
>>
Prefs
   1/*
   2 * arch/score/kernel/ptrace.c
   3 *
   4 * Score Processor version.
   5 *
   6 * Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
   7 *  Chen Liqin <liqin.chen@sunplusct.com>
   8 *  Lennox Wu <lennox.wu@sunplusct.com>
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License as published by
  12 * the Free Software Foundation; either version 2 of the License, or
  13 * (at your option) any later version.
  14 *
  15 * This program is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 * GNU General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License
  21 * along with this program; if not, see the file COPYING, or write
  22 * to the Free Software Foundation, Inc.,
  23 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  24 */
  25
  26#include <linux/elf.h>
  27#include <linux/kernel.h>
  28#include <linux/mm.h>
  29#include <linux/ptrace.h>
  30#include <linux/regset.h>
  31
  32#include <asm/uaccess.h>
  33
  34/*
  35 * retrieve the contents of SCORE userspace general registers
  36 */
  37static int genregs_get(struct task_struct *target,
  38                       const struct user_regset *regset,
  39                       unsigned int pos, unsigned int count,
  40                       void *kbuf, void __user *ubuf)
  41{
  42        const struct pt_regs *regs = task_pt_regs(target);
  43        int ret;
  44
  45        /* skip 9 * sizeof(unsigned long) not use for pt_regs */
  46        ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
  47                                        0, offsetof(struct pt_regs, regs));
  48
  49        /* r0 - r31, cel, ceh, sr0, sr1, sr2, epc, ema, psr, ecr, condition */
  50        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
  51                                  regs->regs,
  52                                  offsetof(struct pt_regs, regs),
  53                                  offsetof(struct pt_regs, cp0_condition));
  54
  55        if (!ret)
  56                ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
  57                                                sizeof(struct pt_regs), -1);
  58
  59        return ret;
  60}
  61
  62/*
  63 * update the contents of the SCORE userspace general registers
  64 */
  65static int genregs_set(struct task_struct *target,
  66                       const struct user_regset *regset,
  67                       unsigned int pos, unsigned int count,
  68                       const void *kbuf, const void __user *ubuf)
  69{
  70        struct pt_regs *regs = task_pt_regs(target);
  71        int ret;
  72
  73        /* skip 9 * sizeof(unsigned long) */
  74        ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
  75                                        0, offsetof(struct pt_regs, regs));
  76
  77        /* r0 - r31, cel, ceh, sr0, sr1, sr2, epc, ema, psr, ecr, condition */
  78        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
  79                                  regs->regs,
  80                                  offsetof(struct pt_regs, regs),
  81                                  offsetof(struct pt_regs, cp0_condition));
  82
  83        if (!ret)
  84                ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
  85                                                sizeof(struct pt_regs), -1);
  86
  87        return ret;
  88}
  89
  90/*
  91 * Define the register sets available on the score7 under Linux
  92 */
  93enum score7_regset {
  94        REGSET_GENERAL,
  95};
  96
  97static const struct user_regset score7_regsets[] = {
  98        [REGSET_GENERAL] = {
  99                .core_note_type = NT_PRSTATUS,
 100                .n              = ELF_NGREG,
 101                .size           = sizeof(long),
 102                .align          = sizeof(long),
 103                .get            = genregs_get,
 104                .set            = genregs_set,
 105        },
 106};
 107
 108static const struct user_regset_view user_score_native_view = {
 109        .name           = "score7",
 110        .e_machine      = EM_SCORE7,
 111        .regsets        = score7_regsets,
 112        .n              = ARRAY_SIZE(score7_regsets),
 113};
 114
 115const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 116{
 117        return &user_score_native_view;
 118}
 119
 120static int is_16bitinsn(unsigned long insn)
 121{
 122        if ((insn & INSN32_MASK) == INSN32_MASK)
 123                return 0;
 124        else
 125                return 1;
 126}
 127
 128int
 129read_tsk_long(struct task_struct *child,
 130                unsigned long addr, unsigned long *res)
 131{
 132        int copied;
 133
 134        copied = access_process_vm(child, addr, res, sizeof(*res), 0);
 135
 136        return copied != sizeof(*res) ? -EIO : 0;
 137}
 138
 139int
 140read_tsk_short(struct task_struct *child,
 141                unsigned long addr, unsigned short *res)
 142{
 143        int copied;
 144
 145        copied = access_process_vm(child, addr, res, sizeof(*res), 0);
 146
 147        return copied != sizeof(*res) ? -EIO : 0;
 148}
 149
 150static int
 151write_tsk_short(struct task_struct *child,
 152                unsigned long addr, unsigned short val)
 153{
 154        int copied;
 155
 156        copied = access_process_vm(child, addr, &val, sizeof(val), 1);
 157
 158        return copied != sizeof(val) ? -EIO : 0;
 159}
 160
 161static int
 162write_tsk_long(struct task_struct *child,
 163                unsigned long addr, unsigned long val)
 164{
 165        int copied;
 166
 167        copied = access_process_vm(child, addr, &val, sizeof(val), 1);
 168
 169        return copied != sizeof(val) ? -EIO : 0;
 170}
 171
 172void user_enable_single_step(struct task_struct *child)
 173{
 174        /* far_epc is the target of branch */
 175        unsigned int epc, far_epc = 0;
 176        unsigned long epc_insn, far_epc_insn;
 177        int ninsn_type;                 /* next insn type 0=16b, 1=32b */
 178        unsigned int tmp, tmp2;
 179        struct pt_regs *regs = task_pt_regs(child);
 180        child->thread.single_step = 1;
 181        child->thread.ss_nextcnt = 1;
 182        epc = regs->cp0_epc;
 183
 184        read_tsk_long(child, epc, &epc_insn);
 185
 186        if (is_16bitinsn(epc_insn)) {
 187                if ((epc_insn & J16M) == J16) {
 188                        tmp = epc_insn & 0xFFE;
 189                        epc = (epc & 0xFFFFF000) | tmp;
 190                } else if ((epc_insn & B16M) == B16) {
 191                        child->thread.ss_nextcnt = 2;
 192                        tmp = (epc_insn & 0xFF) << 1;
 193                        tmp = tmp << 23;
 194                        tmp = (unsigned int)((int) tmp >> 23);
 195                        far_epc = epc + tmp;
 196                        epc += 2;
 197                } else if ((epc_insn & BR16M) == BR16) {
 198                        child->thread.ss_nextcnt = 2;
 199                        tmp = (epc_insn >> 4) & 0xF;
 200                        far_epc = regs->regs[tmp];
 201                        epc += 2;
 202                } else
 203                        epc += 2;
 204        } else {
 205                if ((epc_insn & J32M) == J32) {
 206                        tmp = epc_insn & 0x03FFFFFE;
 207                        tmp2 = tmp & 0x7FFF;
 208                        tmp = (((tmp >> 16) & 0x3FF) << 15) | tmp2;
 209                        epc = (epc & 0xFFC00000) | tmp;
 210                } else if ((epc_insn & B32M) == B32) {
 211                        child->thread.ss_nextcnt = 2;
 212                        tmp = epc_insn & 0x03FFFFFE;    /* discard LK bit */
 213                        tmp2 = tmp & 0x3FF;
 214                        tmp = (((tmp >> 16) & 0x3FF) << 10) | tmp2; /* 20bit */
 215                        tmp = tmp << 12;
 216                        tmp = (unsigned int)((int) tmp >> 12);
 217                        far_epc = epc + tmp;
 218                        epc += 4;
 219                } else if ((epc_insn & BR32M) == BR32) {
 220                        child->thread.ss_nextcnt = 2;
 221                        tmp = (epc_insn >> 16) & 0x1F;
 222                        far_epc = regs->regs[tmp];
 223                        epc += 4;
 224                } else
 225                        epc += 4;
 226        }
 227
 228        if (child->thread.ss_nextcnt == 1) {
 229                read_tsk_long(child, epc, &epc_insn);
 230
 231                if (is_16bitinsn(epc_insn)) {
 232                        write_tsk_short(child, epc, SINGLESTEP16_INSN);
 233                        ninsn_type = 0;
 234                } else {
 235                        write_tsk_long(child, epc, SINGLESTEP32_INSN);
 236                        ninsn_type = 1;
 237                }
 238
 239                if (ninsn_type == 0) {  /* 16bits */
 240                        child->thread.insn1_type = 0;
 241                        child->thread.addr1 = epc;
 242                         /* the insn may have 32bit data */
 243                        child->thread.insn1 = (short)epc_insn;
 244                } else {
 245                        child->thread.insn1_type = 1;
 246                        child->thread.addr1 = epc;
 247                        child->thread.insn1 = epc_insn;
 248                }
 249        } else {
 250                /* branch! have two target child->thread.ss_nextcnt=2 */
 251                read_tsk_long(child, epc, &epc_insn);
 252                read_tsk_long(child, far_epc, &far_epc_insn);
 253                if (is_16bitinsn(epc_insn)) {
 254                        write_tsk_short(child, epc, SINGLESTEP16_INSN);
 255                        ninsn_type = 0;
 256                } else {
 257                        write_tsk_long(child, epc, SINGLESTEP32_INSN);
 258                        ninsn_type = 1;
 259                }
 260
 261                if (ninsn_type == 0) {  /* 16bits */
 262                        child->thread.insn1_type = 0;
 263                        child->thread.addr1 = epc;
 264                         /* the insn may have 32bit data */
 265                        child->thread.insn1 = (short)epc_insn;
 266                } else {
 267                        child->thread.insn1_type = 1;
 268                        child->thread.addr1 = epc;
 269                        child->thread.insn1 = epc_insn;
 270                }
 271
 272                if (is_16bitinsn(far_epc_insn)) {
 273                        write_tsk_short(child, far_epc, SINGLESTEP16_INSN);
 274                        ninsn_type = 0;
 275                } else {
 276                        write_tsk_long(child, far_epc, SINGLESTEP32_INSN);
 277                        ninsn_type = 1;
 278                }
 279
 280                if (ninsn_type == 0) {  /* 16bits */
 281                        child->thread.insn2_type = 0;
 282                        child->thread.addr2 = far_epc;
 283                         /* the insn may have 32bit data */
 284                        child->thread.insn2 = (short)far_epc_insn;
 285                } else {
 286                        child->thread.insn2_type = 1;
 287                        child->thread.addr2 = far_epc;
 288                        child->thread.insn2 = far_epc_insn;
 289                }
 290        }
 291}
 292
 293void user_disable_single_step(struct task_struct *child)
 294{
 295        if (child->thread.insn1_type == 0)
 296                write_tsk_short(child, child->thread.addr1,
 297                                child->thread.insn1);
 298
 299        if (child->thread.insn1_type == 1)
 300                write_tsk_long(child, child->thread.addr1,
 301                                child->thread.insn1);
 302
 303        if (child->thread.ss_nextcnt == 2) {    /* branch */
 304                if (child->thread.insn1_type == 0)
 305                        write_tsk_short(child, child->thread.addr1,
 306                                        child->thread.insn1);
 307                if (child->thread.insn1_type == 1)
 308                        write_tsk_long(child, child->thread.addr1,
 309                                        child->thread.insn1);
 310                if (child->thread.insn2_type == 0)
 311                        write_tsk_short(child, child->thread.addr2,
 312                                        child->thread.insn2);
 313                if (child->thread.insn2_type == 1)
 314                        write_tsk_long(child, child->thread.addr2,
 315                                        child->thread.insn2);
 316        }
 317
 318        child->thread.single_step = 0;
 319        child->thread.ss_nextcnt = 0;
 320}
 321
 322void ptrace_disable(struct task_struct *child)
 323{
 324        user_disable_single_step(child);
 325}
 326
 327long
 328arch_ptrace(struct task_struct *child, long request, long addr, long data)
 329{
 330        int ret;
 331        unsigned long __user *datap = (void __user *)data;
 332
 333        switch (request) {
 334        case PTRACE_GETREGS:
 335                ret = copy_regset_to_user(child, &user_score_native_view,
 336                                                REGSET_GENERAL,
 337                                                0, sizeof(struct pt_regs),
 338                                                (void __user *)datap);
 339                break;
 340
 341        case PTRACE_SETREGS:
 342                ret = copy_regset_from_user(child, &user_score_native_view,
 343                                                REGSET_GENERAL,
 344                                                0, sizeof(struct pt_regs),
 345                                                (const void __user *)datap);
 346                break;
 347
 348        default:
 349                ret = ptrace_request(child, request, addr, data);
 350                break;
 351        }
 352
 353        return ret;
 354}
 355
 356/*
 357 * Notification of system call entry/exit
 358 * - triggered by current->work.syscall_trace
 359 */
 360asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit)
 361{
 362        if (!(current->ptrace & PT_PTRACED))
 363                return;
 364
 365        if (!test_thread_flag(TIF_SYSCALL_TRACE))
 366                return;
 367
 368        /* The 0x80 provides a way for the tracing parent to distinguish
 369           between a syscall stop and SIGTRAP delivery. */
 370        ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ?
 371                        0x80 : 0));
 372
 373        /*
 374         * this isn't the same as continuing with a signal, but it will do
 375         * for normal use.  strace only continues with a signal if the
 376         * stopping signal is not SIGTRAP.  -brl
 377         */
 378        if (current->exit_code) {
 379                send_sig(current->exit_code, current, 1);
 380                current->exit_code = 0;
 381        }
 382}
 383