linux/arch/sparc/kernel/ptrace_32.c
<<
>>
Prefs
   1/* ptrace.c: Sparc process tracing support.
   2 *
   3 * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net)
   4 *
   5 * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
   6 * and David Mosberger.
   7 *
   8 * Added Linux support -miguel (weird, eh?, the original code was meant
   9 * to emulate SunOS).
  10 */
  11
  12#include <linux/kernel.h>
  13#include <linux/sched.h>
  14#include <linux/mm.h>
  15#include <linux/errno.h>
  16#include <linux/ptrace.h>
  17#include <linux/user.h>
  18#include <linux/smp.h>
  19#include <linux/security.h>
  20#include <linux/signal.h>
  21#include <linux/regset.h>
  22#include <linux/elf.h>
  23#include <linux/tracehook.h>
  24
  25#include <asm/pgtable.h>
  26#include <asm/system.h>
  27#include <asm/uaccess.h>
  28
  29/* #define ALLOW_INIT_TRACING */
  30
  31/*
  32 * Called by kernel/ptrace.c when detaching..
  33 *
  34 * Make sure single step bits etc are not set.
  35 */
  36void ptrace_disable(struct task_struct *child)
  37{
  38        /* nothing to do */
  39}
  40
  41enum sparc_regset {
  42        REGSET_GENERAL,
  43        REGSET_FP,
  44};
  45
  46static int genregs32_get(struct task_struct *target,
  47                         const struct user_regset *regset,
  48                         unsigned int pos, unsigned int count,
  49                         void *kbuf, void __user *ubuf)
  50{
  51        const struct pt_regs *regs = target->thread.kregs;
  52        unsigned long __user *reg_window;
  53        unsigned long *k = kbuf;
  54        unsigned long __user *u = ubuf;
  55        unsigned long reg;
  56
  57        if (target == current)
  58                flush_user_windows();
  59
  60        pos /= sizeof(reg);
  61        count /= sizeof(reg);
  62
  63        if (kbuf) {
  64                for (; count > 0 && pos < 16; count--)
  65                        *k++ = regs->u_regs[pos++];
  66
  67                reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
  68                reg_window -= 16;
  69                for (; count > 0 && pos < 32; count--) {
  70                        if (get_user(*k++, &reg_window[pos++]))
  71                                return -EFAULT;
  72                }
  73        } else {
  74                for (; count > 0 && pos < 16; count--) {
  75                        if (put_user(regs->u_regs[pos++], u++))
  76                                return -EFAULT;
  77                }
  78
  79                reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
  80                reg_window -= 16;
  81                for (; count > 0 && pos < 32; count--) {
  82                        if (get_user(reg, &reg_window[pos++]) ||
  83                            put_user(reg, u++))
  84                                return -EFAULT;
  85                }
  86        }
  87        while (count > 0) {
  88                switch (pos) {
  89                case 32: /* PSR */
  90                        reg = regs->psr;
  91                        break;
  92                case 33: /* PC */
  93                        reg = regs->pc;
  94                        break;
  95                case 34: /* NPC */
  96                        reg = regs->npc;
  97                        break;
  98                case 35: /* Y */
  99                        reg = regs->y;
 100                        break;
 101                case 36: /* WIM */
 102                case 37: /* TBR */
 103                        reg = 0;
 104                        break;
 105                default:
 106                        goto finish;
 107                }
 108
 109                if (kbuf)
 110                        *k++ = reg;
 111                else if (put_user(reg, u++))
 112                        return -EFAULT;
 113                pos++;
 114                count--;
 115        }
 116finish:
 117        pos *= sizeof(reg);
 118        count *= sizeof(reg);
 119
 120        return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 121                                        38 * sizeof(reg), -1);
 122}
 123
 124static int genregs32_set(struct task_struct *target,
 125                         const struct user_regset *regset,
 126                         unsigned int pos, unsigned int count,
 127                         const void *kbuf, const void __user *ubuf)
 128{
 129        struct pt_regs *regs = target->thread.kregs;
 130        unsigned long __user *reg_window;
 131        const unsigned long *k = kbuf;
 132        const unsigned long __user *u = ubuf;
 133        unsigned long reg;
 134
 135        if (target == current)
 136                flush_user_windows();
 137
 138        pos /= sizeof(reg);
 139        count /= sizeof(reg);
 140
 141        if (kbuf) {
 142                for (; count > 0 && pos < 16; count--)
 143                        regs->u_regs[pos++] = *k++;
 144
 145                reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
 146                reg_window -= 16;
 147                for (; count > 0 && pos < 32; count--) {
 148                        if (put_user(*k++, &reg_window[pos++]))
 149                                return -EFAULT;
 150                }
 151        } else {
 152                for (; count > 0 && pos < 16; count--) {
 153                        if (get_user(reg, u++))
 154                                return -EFAULT;
 155                        regs->u_regs[pos++] = reg;
 156                }
 157
 158                reg_window = (unsigned long __user *) regs->u_regs[UREG_I6];
 159                reg_window -= 16;
 160                for (; count > 0 && pos < 32; count--) {
 161                        if (get_user(reg, u++) ||
 162                            put_user(reg, &reg_window[pos++]))
 163                                return -EFAULT;
 164                }
 165        }
 166        while (count > 0) {
 167                unsigned long psr;
 168
 169                if (kbuf)
 170                        reg = *k++;
 171                else if (get_user(reg, u++))
 172                        return -EFAULT;
 173
 174                switch (pos) {
 175                case 32: /* PSR */
 176                        psr = regs->psr;
 177                        psr &= ~(PSR_ICC | PSR_SYSCALL);
 178                        psr |= (reg & (PSR_ICC | PSR_SYSCALL));
 179                        regs->psr = psr;
 180                        break;
 181                case 33: /* PC */
 182                        regs->pc = reg;
 183                        break;
 184                case 34: /* NPC */
 185                        regs->npc = reg;
 186                        break;
 187                case 35: /* Y */
 188                        regs->y = reg;
 189                        break;
 190                case 36: /* WIM */
 191                case 37: /* TBR */
 192                        break;
 193                default:
 194                        goto finish;
 195                }
 196
 197                pos++;
 198                count--;
 199        }
 200finish:
 201        pos *= sizeof(reg);
 202        count *= sizeof(reg);
 203
 204        return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 205                                         38 * sizeof(reg), -1);
 206}
 207
 208static int fpregs32_get(struct task_struct *target,
 209                        const struct user_regset *regset,
 210                        unsigned int pos, unsigned int count,
 211                        void *kbuf, void __user *ubuf)
 212{
 213        const unsigned long *fpregs = target->thread.float_regs;
 214        int ret = 0;
 215
 216#if 0
 217        if (target == current)
 218                save_and_clear_fpu();
 219#endif
 220
 221        ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 222                                  fpregs,
 223                                  0, 32 * sizeof(u32));
 224
 225        if (!ret)
 226                ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 227                                               32 * sizeof(u32),
 228                                               33 * sizeof(u32));
 229        if (!ret)
 230                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 231                                          &target->thread.fsr,
 232                                          33 * sizeof(u32),
 233                                          34 * sizeof(u32));
 234
 235        if (!ret) {
 236                unsigned long val;
 237
 238                val = (1 << 8) | (8 << 16);
 239                ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
 240                                          &val,
 241                                          34 * sizeof(u32),
 242                                          35 * sizeof(u32));
 243        }
 244
 245        if (!ret)
 246                ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
 247                                               35 * sizeof(u32), -1);
 248
 249        return ret;
 250}
 251
 252static int fpregs32_set(struct task_struct *target,
 253                        const struct user_regset *regset,
 254                        unsigned int pos, unsigned int count,
 255                        const void *kbuf, const void __user *ubuf)
 256{
 257        unsigned long *fpregs = target->thread.float_regs;
 258        int ret;
 259
 260#if 0
 261        if (target == current)
 262                save_and_clear_fpu();
 263#endif
 264        ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 265                                 fpregs,
 266                                 0, 32 * sizeof(u32));
 267        if (!ret)
 268                user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 269                                          32 * sizeof(u32),
 270                                          33 * sizeof(u32));
 271        if (!ret && count > 0) {
 272                ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
 273                                         &target->thread.fsr,
 274                                         33 * sizeof(u32),
 275                                         34 * sizeof(u32));
 276        }
 277
 278        if (!ret)
 279                ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
 280                                                34 * sizeof(u32), -1);
 281        return ret;
 282}
 283
 284static const struct user_regset sparc32_regsets[] = {
 285        /* Format is:
 286         *      G0 --> G7
 287         *      O0 --> O7
 288         *      L0 --> L7
 289         *      I0 --> I7
 290         *      PSR, PC, nPC, Y, WIM, TBR
 291         */
 292        [REGSET_GENERAL] = {
 293                .core_note_type = NT_PRSTATUS,
 294                .n = 38,
 295                .size = sizeof(u32), .align = sizeof(u32),
 296                .get = genregs32_get, .set = genregs32_set
 297        },
 298        /* Format is:
 299         *      F0 --> F31
 300         *      empty 32-bit word
 301         *      FSR (32--bit word)
 302         *      FPU QUEUE COUNT (8-bit char)
 303         *      FPU QUEUE ENTRYSIZE (8-bit char)
 304         *      FPU ENABLED (8-bit char)
 305         *      empty 8-bit char
 306         *      FPU QUEUE (64 32-bit ints)
 307         */
 308        [REGSET_FP] = {
 309                .core_note_type = NT_PRFPREG,
 310                .n = 99,
 311                .size = sizeof(u32), .align = sizeof(u32),
 312                .get = fpregs32_get, .set = fpregs32_set
 313        },
 314};
 315
 316static const struct user_regset_view user_sparc32_view = {
 317        .name = "sparc", .e_machine = EM_SPARC,
 318        .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets)
 319};
 320
 321const struct user_regset_view *task_user_regset_view(struct task_struct *task)
 322{
 323        return &user_sparc32_view;
 324}
 325
 326struct fps {
 327        unsigned long regs[32];
 328        unsigned long fsr;
 329        unsigned long flags;
 330        unsigned long extra;
 331        unsigned long fpqd;
 332        struct fq {
 333                unsigned long *insnaddr;
 334                unsigned long insn;
 335        } fpq[16];
 336};
 337
 338long arch_ptrace(struct task_struct *child, long request,
 339                 unsigned long addr, unsigned long data)
 340{
 341        unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4];
 342        void __user *addr2p;
 343        const struct user_regset_view *view;
 344        struct pt_regs __user *pregs;
 345        struct fps __user *fps;
 346        int ret;
 347
 348        view = task_user_regset_view(current);
 349        addr2p = (void __user *) addr2;
 350        pregs = (struct pt_regs __user *) addr;
 351        fps = (struct fps __user *) addr;
 352
 353        switch(request) {
 354        case PTRACE_GETREGS: {
 355                ret = copy_regset_to_user(child, view, REGSET_GENERAL,
 356                                          32 * sizeof(u32),
 357                                          4 * sizeof(u32),
 358                                          &pregs->psr);
 359                if (!ret)
 360                        copy_regset_to_user(child, view, REGSET_GENERAL,
 361                                            1 * sizeof(u32),
 362                                            15 * sizeof(u32),
 363                                            &pregs->u_regs[0]);
 364                break;
 365        }
 366
 367        case PTRACE_SETREGS: {
 368                ret = copy_regset_from_user(child, view, REGSET_GENERAL,
 369                                            32 * sizeof(u32),
 370                                            4 * sizeof(u32),
 371                                            &pregs->psr);
 372                if (!ret)
 373                        copy_regset_from_user(child, view, REGSET_GENERAL,
 374                                              1 * sizeof(u32),
 375                                              15 * sizeof(u32),
 376                                              &pregs->u_regs[0]);
 377                break;
 378        }
 379
 380        case PTRACE_GETFPREGS: {
 381                ret = copy_regset_to_user(child, view, REGSET_FP,
 382                                          0 * sizeof(u32),
 383                                          32 * sizeof(u32),
 384                                          &fps->regs[0]);
 385                if (!ret)
 386                        ret = copy_regset_to_user(child, view, REGSET_FP,
 387                                                  33 * sizeof(u32),
 388                                                  1 * sizeof(u32),
 389                                                  &fps->fsr);
 390
 391                if (!ret) {
 392                        if (__put_user(0, &fps->fpqd) ||
 393                            __put_user(0, &fps->flags) ||
 394                            __put_user(0, &fps->extra) ||
 395                            clear_user(fps->fpq, sizeof(fps->fpq)))
 396                                ret = -EFAULT;
 397                }
 398                break;
 399        }
 400
 401        case PTRACE_SETFPREGS: {
 402                ret = copy_regset_from_user(child, view, REGSET_FP,
 403                                            0 * sizeof(u32),
 404                                            32 * sizeof(u32),
 405                                            &fps->regs[0]);
 406                if (!ret)
 407                        ret = copy_regset_from_user(child, view, REGSET_FP,
 408                                                    33 * sizeof(u32),
 409                                                    1 * sizeof(u32),
 410                                                    &fps->fsr);
 411                break;
 412        }
 413
 414        case PTRACE_READTEXT:
 415        case PTRACE_READDATA:
 416                ret = ptrace_readdata(child, addr, addr2p, data);
 417
 418                if (ret == data)
 419                        ret = 0;
 420                else if (ret >= 0)
 421                        ret = -EIO;
 422                break;
 423
 424        case PTRACE_WRITETEXT:
 425        case PTRACE_WRITEDATA:
 426                ret = ptrace_writedata(child, addr2p, addr, data);
 427
 428                if (ret == data)
 429                        ret = 0;
 430                else if (ret >= 0)
 431                        ret = -EIO;
 432                break;
 433
 434        default:
 435                if (request == PTRACE_SPARC_DETACH)
 436                        request = PTRACE_DETACH;
 437                ret = ptrace_request(child, request, addr, data);
 438                break;
 439        }
 440
 441        return ret;
 442}
 443
 444asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p)
 445{
 446        int ret = 0;
 447
 448        if (test_thread_flag(TIF_SYSCALL_TRACE)) {
 449                if (syscall_exit_p)
 450                        tracehook_report_syscall_exit(regs, 0);
 451                else
 452                        ret = tracehook_report_syscall_entry(regs);
 453        }
 454
 455        return ret;
 456}
 457