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