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 <linux/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), FOLL_FORCE);
 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), FOLL_FORCE);
 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),
 157                        FOLL_FORCE | FOLL_WRITE);
 158
 159        return copied != sizeof(val) ? -EIO : 0;
 160}
 161
 162static int
 163write_tsk_long(struct task_struct *child,
 164                unsigned long addr, unsigned long val)
 165{
 166        int copied;
 167
 168        copied = access_process_vm(child, addr, &val, sizeof(val),
 169                        FOLL_FORCE | FOLL_WRITE);
 170
 171        return copied != sizeof(val) ? -EIO : 0;
 172}
 173
 174void user_enable_single_step(struct task_struct *child)
 175{
 176        /* far_epc is the target of branch */
 177        unsigned int epc, far_epc = 0;
 178        unsigned long epc_insn, far_epc_insn;
 179        int ninsn_type;                 /* next insn type 0=16b, 1=32b */
 180        unsigned int tmp, tmp2;
 181        struct pt_regs *regs = task_pt_regs(child);
 182        child->thread.single_step = 1;
 183        child->thread.ss_nextcnt = 1;
 184        epc = regs->cp0_epc;
 185
 186        read_tsk_long(child, epc, &epc_insn);
 187
 188        if (is_16bitinsn(epc_insn)) {
 189                if ((epc_insn & J16M) == J16) {
 190                        tmp = epc_insn & 0xFFE;
 191                        epc = (epc & 0xFFFFF000) | tmp;
 192                } else if ((epc_insn & B16M) == B16) {
 193                        child->thread.ss_nextcnt = 2;
 194                        tmp = (epc_insn & 0xFF) << 1;
 195                        tmp = tmp << 23;
 196                        tmp = (unsigned int)((int) tmp >> 23);
 197                        far_epc = epc + tmp;
 198                        epc += 2;
 199                } else if ((epc_insn & BR16M) == BR16) {
 200                        child->thread.ss_nextcnt = 2;
 201                        tmp = (epc_insn >> 4) & 0xF;
 202                        far_epc = regs->regs[tmp];
 203                        epc += 2;
 204                } else
 205                        epc += 2;
 206        } else {
 207                if ((epc_insn & J32M) == J32) {
 208                        tmp = epc_insn & 0x03FFFFFE;
 209                        tmp2 = tmp & 0x7FFF;
 210                        tmp = (((tmp >> 16) & 0x3FF) << 15) | tmp2;
 211                        epc = (epc & 0xFFC00000) | tmp;
 212                } else if ((epc_insn & B32M) == B32) {
 213                        child->thread.ss_nextcnt = 2;
 214                        tmp = epc_insn & 0x03FFFFFE;    /* discard LK bit */
 215                        tmp2 = tmp & 0x3FF;
 216                        tmp = (((tmp >> 16) & 0x3FF) << 10) | tmp2; /* 20bit */
 217                        tmp = tmp << 12;
 218                        tmp = (unsigned int)((int) tmp >> 12);
 219                        far_epc = epc + tmp;
 220                        epc += 4;
 221                } else if ((epc_insn & BR32M) == BR32) {
 222                        child->thread.ss_nextcnt = 2;
 223                        tmp = (epc_insn >> 16) & 0x1F;
 224                        far_epc = regs->regs[tmp];
 225                        epc += 4;
 226                } else
 227                        epc += 4;
 228        }
 229
 230        if (child->thread.ss_nextcnt == 1) {
 231                read_tsk_long(child, epc, &epc_insn);
 232
 233                if (is_16bitinsn(epc_insn)) {
 234                        write_tsk_short(child, epc, SINGLESTEP16_INSN);
 235                        ninsn_type = 0;
 236                } else {
 237                        write_tsk_long(child, epc, SINGLESTEP32_INSN);
 238                        ninsn_type = 1;
 239                }
 240
 241                if (ninsn_type == 0) {  /* 16bits */
 242                        child->thread.insn1_type = 0;
 243                        child->thread.addr1 = epc;
 244                         /* the insn may have 32bit data */
 245                        child->thread.insn1 = (short)epc_insn;
 246                } else {
 247                        child->thread.insn1_type = 1;
 248                        child->thread.addr1 = epc;
 249                        child->thread.insn1 = epc_insn;
 250                }
 251        } else {
 252                /* branch! have two target child->thread.ss_nextcnt=2 */
 253                read_tsk_long(child, epc, &epc_insn);
 254                read_tsk_long(child, far_epc, &far_epc_insn);
 255                if (is_16bitinsn(epc_insn)) {
 256                        write_tsk_short(child, epc, SINGLESTEP16_INSN);
 257                        ninsn_type = 0;
 258                } else {
 259                        write_tsk_long(child, epc, SINGLESTEP32_INSN);
 260                        ninsn_type = 1;
 261                }
 262
 263                if (ninsn_type == 0) {  /* 16bits */
 264                        child->thread.insn1_type = 0;
 265                        child->thread.addr1 = epc;
 266                         /* the insn may have 32bit data */
 267                        child->thread.insn1 = (short)epc_insn;
 268                } else {
 269                        child->thread.insn1_type = 1;
 270                        child->thread.addr1 = epc;
 271                        child->thread.insn1 = epc_insn;
 272                }
 273
 274                if (is_16bitinsn(far_epc_insn)) {
 275                        write_tsk_short(child, far_epc, SINGLESTEP16_INSN);
 276                        ninsn_type = 0;
 277                } else {
 278                        write_tsk_long(child, far_epc, SINGLESTEP32_INSN);
 279                        ninsn_type = 1;
 280                }
 281
 282                if (ninsn_type == 0) {  /* 16bits */
 283                        child->thread.insn2_type = 0;
 284                        child->thread.addr2 = far_epc;
 285                         /* the insn may have 32bit data */
 286                        child->thread.insn2 = (short)far_epc_insn;
 287                } else {
 288                        child->thread.insn2_type = 1;
 289                        child->thread.addr2 = far_epc;
 290                        child->thread.insn2 = far_epc_insn;
 291                }
 292        }
 293}
 294
 295void user_disable_single_step(struct task_struct *child)
 296{
 297        if (child->thread.insn1_type == 0)
 298                write_tsk_short(child, child->thread.addr1,
 299                                child->thread.insn1);
 300
 301        if (child->thread.insn1_type == 1)
 302                write_tsk_long(child, child->thread.addr1,
 303                                child->thread.insn1);
 304
 305        if (child->thread.ss_nextcnt == 2) {    /* branch */
 306                if (child->thread.insn1_type == 0)
 307                        write_tsk_short(child, child->thread.addr1,
 308                                        child->thread.insn1);
 309                if (child->thread.insn1_type == 1)
 310                        write_tsk_long(child, child->thread.addr1,
 311                                        child->thread.insn1);
 312                if (child->thread.insn2_type == 0)
 313                        write_tsk_short(child, child->thread.addr2,
 314                                        child->thread.insn2);
 315                if (child->thread.insn2_type == 1)
 316                        write_tsk_long(child, child->thread.addr2,
 317                                        child->thread.insn2);
 318        }
 319
 320        child->thread.single_step = 0;
 321        child->thread.ss_nextcnt = 0;
 322}
 323
 324void ptrace_disable(struct task_struct *child)
 325{
 326        user_disable_single_step(child);
 327}
 328
 329long
 330arch_ptrace(struct task_struct *child, long request,
 331            unsigned long addr, unsigned long data)
 332{
 333        int ret;
 334        unsigned long __user *datap = (void __user *)data;
 335
 336        switch (request) {
 337        case PTRACE_GETREGS:
 338                ret = copy_regset_to_user(child, &user_score_native_view,
 339                                                REGSET_GENERAL,
 340                                                0, sizeof(struct pt_regs),
 341                                                datap);
 342                break;
 343
 344        case PTRACE_SETREGS:
 345                ret = copy_regset_from_user(child, &user_score_native_view,
 346                                                REGSET_GENERAL,
 347                                                0, sizeof(struct pt_regs),
 348                                                datap);
 349                break;
 350
 351        default:
 352                ret = ptrace_request(child, request, addr, data);
 353                break;
 354        }
 355
 356        return ret;
 357}
 358
 359/*
 360 * Notification of system call entry/exit
 361 * - triggered by current->work.syscall_trace
 362 */
 363asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit)
 364{
 365        if (!(current->ptrace & PT_PTRACED))
 366                return;
 367
 368        if (!test_thread_flag(TIF_SYSCALL_TRACE))
 369                return;
 370
 371        /* The 0x80 provides a way for the tracing parent to distinguish
 372           between a syscall stop and SIGTRAP delivery. */
 373        ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ?
 374                        0x80 : 0));
 375
 376        /*
 377         * this isn't the same as continuing with a signal, but it will do
 378         * for normal use.  strace only continues with a signal if the
 379         * stopping signal is not SIGTRAP.  -brl
 380         */
 381        if (current->exit_code) {
 382                send_sig(current->exit_code, current, 1);
 383                current->exit_code = 0;
 384        }
 385}
 386