123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- // SPDX-License-Identifier: GPL-2.0
- /* ptrace.c: Sparc process tracing support.
- *
- * Copyright (C) 1996, 2008 David S. Miller ([email protected])
- *
- * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
- * and David Mosberger.
- *
- * Added Linux support -miguel (weird, eh?, the original code was meant
- * to emulate SunOS).
- */
- #include <linux/kernel.h>
- #include <linux/sched.h>
- #include <linux/mm.h>
- #include <linux/errno.h>
- #include <linux/ptrace.h>
- #include <linux/user.h>
- #include <linux/smp.h>
- #include <linux/security.h>
- #include <linux/signal.h>
- #include <linux/regset.h>
- #include <linux/elf.h>
- #include <linux/uaccess.h>
- #include <asm/cacheflush.h>
- #include "kernel.h"
- /* #define ALLOW_INIT_TRACING */
- /*
- * Called by kernel/ptrace.c when detaching..
- *
- * Make sure single step bits etc are not set.
- */
- void ptrace_disable(struct task_struct *child)
- {
- /* nothing to do */
- }
- enum sparc_regset {
- REGSET_GENERAL,
- REGSET_FP,
- };
- static int regwindow32_get(struct task_struct *target,
- const struct pt_regs *regs,
- u32 *uregs)
- {
- unsigned long reg_window = regs->u_regs[UREG_I6];
- int size = 16 * sizeof(u32);
- if (target == current) {
- if (copy_from_user(uregs, (void __user *)reg_window, size))
- return -EFAULT;
- } else {
- if (access_process_vm(target, reg_window, uregs, size,
- FOLL_FORCE) != size)
- return -EFAULT;
- }
- return 0;
- }
- static int regwindow32_set(struct task_struct *target,
- const struct pt_regs *regs,
- u32 *uregs)
- {
- unsigned long reg_window = regs->u_regs[UREG_I6];
- int size = 16 * sizeof(u32);
- if (target == current) {
- if (copy_to_user((void __user *)reg_window, uregs, size))
- return -EFAULT;
- } else {
- if (access_process_vm(target, reg_window, uregs, size,
- FOLL_FORCE | FOLL_WRITE) != size)
- return -EFAULT;
- }
- return 0;
- }
- static int genregs32_get(struct task_struct *target,
- const struct user_regset *regset,
- struct membuf to)
- {
- const struct pt_regs *regs = target->thread.kregs;
- u32 uregs[16];
- if (target == current)
- flush_user_windows();
- membuf_write(&to, regs->u_regs, 16 * sizeof(u32));
- if (!to.left)
- return 0;
- if (regwindow32_get(target, regs, uregs))
- return -EFAULT;
- membuf_write(&to, uregs, 16 * sizeof(u32));
- membuf_store(&to, regs->psr);
- membuf_store(&to, regs->pc);
- membuf_store(&to, regs->npc);
- membuf_store(&to, regs->y);
- return membuf_zero(&to, 2 * sizeof(u32));
- }
- static int genregs32_set(struct task_struct *target,
- const struct user_regset *regset,
- unsigned int pos, unsigned int count,
- const void *kbuf, const void __user *ubuf)
- {
- struct pt_regs *regs = target->thread.kregs;
- u32 uregs[16];
- u32 psr;
- int ret;
- if (target == current)
- flush_user_windows();
- ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- regs->u_regs,
- 0, 16 * sizeof(u32));
- if (ret || !count)
- return ret;
- if (regwindow32_get(target, regs, uregs))
- return -EFAULT;
- ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- uregs,
- 16 * sizeof(u32), 32 * sizeof(u32));
- if (ret)
- return ret;
- if (regwindow32_set(target, regs, uregs))
- return -EFAULT;
- if (!count)
- return 0;
- ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- &psr,
- 32 * sizeof(u32), 33 * sizeof(u32));
- if (ret)
- return ret;
- regs->psr = (regs->psr & ~(PSR_ICC | PSR_SYSCALL)) |
- (psr & (PSR_ICC | PSR_SYSCALL));
- if (!count)
- return 0;
- ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- ®s->pc,
- 33 * sizeof(u32), 34 * sizeof(u32));
- if (ret || !count)
- return ret;
- ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- ®s->npc,
- 34 * sizeof(u32), 35 * sizeof(u32));
- if (ret || !count)
- return ret;
- ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- ®s->y,
- 35 * sizeof(u32), 36 * sizeof(u32));
- if (ret || !count)
- return ret;
- return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
- 36 * sizeof(u32), 38 * sizeof(u32));
- }
- static int fpregs32_get(struct task_struct *target,
- const struct user_regset *regset,
- struct membuf to)
- {
- #if 0
- if (target == current)
- save_and_clear_fpu();
- #endif
- membuf_write(&to, target->thread.float_regs, 32 * sizeof(u32));
- membuf_zero(&to, sizeof(u32));
- membuf_write(&to, &target->thread.fsr, sizeof(u32));
- membuf_store(&to, (u32)((1 << 8) | (8 << 16)));
- return membuf_zero(&to, 64 * sizeof(u32));
- }
- static int fpregs32_set(struct task_struct *target,
- const struct user_regset *regset,
- unsigned int pos, unsigned int count,
- const void *kbuf, const void __user *ubuf)
- {
- unsigned long *fpregs = target->thread.float_regs;
- int ret;
- #if 0
- if (target == current)
- save_and_clear_fpu();
- #endif
- ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- fpregs,
- 0, 32 * sizeof(u32));
- if (!ret)
- user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
- 32 * sizeof(u32),
- 33 * sizeof(u32));
- if (!ret)
- ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- &target->thread.fsr,
- 33 * sizeof(u32),
- 34 * sizeof(u32));
- if (!ret)
- ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
- 34 * sizeof(u32), -1);
- return ret;
- }
- static const struct user_regset sparc32_regsets[] = {
- /* Format is:
- * G0 --> G7
- * O0 --> O7
- * L0 --> L7
- * I0 --> I7
- * PSR, PC, nPC, Y, WIM, TBR
- */
- [REGSET_GENERAL] = {
- .core_note_type = NT_PRSTATUS,
- .n = 38,
- .size = sizeof(u32), .align = sizeof(u32),
- .regset_get = genregs32_get, .set = genregs32_set
- },
- /* Format is:
- * F0 --> F31
- * empty 32-bit word
- * FSR (32--bit word)
- * FPU QUEUE COUNT (8-bit char)
- * FPU QUEUE ENTRYSIZE (8-bit char)
- * FPU ENABLED (8-bit char)
- * empty 8-bit char
- * FPU QUEUE (64 32-bit ints)
- */
- [REGSET_FP] = {
- .core_note_type = NT_PRFPREG,
- .n = 99,
- .size = sizeof(u32), .align = sizeof(u32),
- .regset_get = fpregs32_get, .set = fpregs32_set
- },
- };
- static int getregs_get(struct task_struct *target,
- const struct user_regset *regset,
- struct membuf to)
- {
- const struct pt_regs *regs = target->thread.kregs;
- if (target == current)
- flush_user_windows();
- membuf_store(&to, regs->psr);
- membuf_store(&to, regs->pc);
- membuf_store(&to, regs->npc);
- membuf_store(&to, regs->y);
- return membuf_write(&to, regs->u_regs + 1, 15 * sizeof(u32));
- }
- static int setregs_set(struct task_struct *target,
- const struct user_regset *regset,
- unsigned int pos, unsigned int count,
- const void *kbuf, const void __user *ubuf)
- {
- struct pt_regs *regs = target->thread.kregs;
- u32 v[4];
- int ret;
- if (target == current)
- flush_user_windows();
- ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- v,
- 0, 4 * sizeof(u32));
- if (ret)
- return ret;
- regs->psr = (regs->psr & ~(PSR_ICC | PSR_SYSCALL)) |
- (v[0] & (PSR_ICC | PSR_SYSCALL));
- regs->pc = v[1];
- regs->npc = v[2];
- regs->y = v[3];
- return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- regs->u_regs + 1,
- 4 * sizeof(u32) , 19 * sizeof(u32));
- }
- static int getfpregs_get(struct task_struct *target,
- const struct user_regset *regset,
- struct membuf to)
- {
- #if 0
- if (target == current)
- save_and_clear_fpu();
- #endif
- membuf_write(&to, &target->thread.float_regs, 32 * sizeof(u32));
- membuf_write(&to, &target->thread.fsr, sizeof(u32));
- return membuf_zero(&to, 35 * sizeof(u32));
- }
- static int setfpregs_set(struct task_struct *target,
- const struct user_regset *regset,
- unsigned int pos, unsigned int count,
- const void *kbuf, const void __user *ubuf)
- {
- unsigned long *fpregs = target->thread.float_regs;
- int ret;
- #if 0
- if (target == current)
- save_and_clear_fpu();
- #endif
- ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- fpregs,
- 0, 32 * sizeof(u32));
- if (ret)
- return ret;
- return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- &target->thread.fsr,
- 32 * sizeof(u32),
- 33 * sizeof(u32));
- }
- static const struct user_regset ptrace32_regsets[] = {
- [REGSET_GENERAL] = {
- .n = 19, .size = sizeof(u32),
- .regset_get = getregs_get, .set = setregs_set,
- },
- [REGSET_FP] = {
- .n = 68, .size = sizeof(u32),
- .regset_get = getfpregs_get, .set = setfpregs_set,
- },
- };
- static const struct user_regset_view ptrace32_view = {
- .regsets = ptrace32_regsets, .n = ARRAY_SIZE(ptrace32_regsets)
- };
- static const struct user_regset_view user_sparc32_view = {
- .name = "sparc", .e_machine = EM_SPARC,
- .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets)
- };
- const struct user_regset_view *task_user_regset_view(struct task_struct *task)
- {
- return &user_sparc32_view;
- }
- struct fps {
- unsigned long regs[32];
- unsigned long fsr;
- unsigned long flags;
- unsigned long extra;
- unsigned long fpqd;
- struct fq {
- unsigned long *insnaddr;
- unsigned long insn;
- } fpq[16];
- };
- long arch_ptrace(struct task_struct *child, long request,
- unsigned long addr, unsigned long data)
- {
- unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4];
- void __user *addr2p;
- struct pt_regs __user *pregs;
- struct fps __user *fps;
- int ret;
- addr2p = (void __user *) addr2;
- pregs = (struct pt_regs __user *) addr;
- fps = (struct fps __user *) addr;
- switch(request) {
- case PTRACE_GETREGS: {
- ret = copy_regset_to_user(child, &ptrace32_view,
- REGSET_GENERAL, 0,
- 19 * sizeof(u32),
- pregs);
- break;
- }
- case PTRACE_SETREGS: {
- ret = copy_regset_from_user(child, &ptrace32_view,
- REGSET_GENERAL, 0,
- 19 * sizeof(u32),
- pregs);
- break;
- }
- case PTRACE_GETFPREGS: {
- ret = copy_regset_to_user(child, &ptrace32_view,
- REGSET_FP, 0,
- 68 * sizeof(u32),
- fps);
- break;
- }
- case PTRACE_SETFPREGS: {
- ret = copy_regset_from_user(child, &ptrace32_view,
- REGSET_FP, 0,
- 33 * sizeof(u32),
- fps);
- break;
- }
- case PTRACE_READTEXT:
- case PTRACE_READDATA:
- ret = ptrace_readdata(child, addr, addr2p, data);
- if (ret == data)
- ret = 0;
- else if (ret >= 0)
- ret = -EIO;
- break;
- case PTRACE_WRITETEXT:
- case PTRACE_WRITEDATA:
- ret = ptrace_writedata(child, addr2p, addr, data);
- if (ret == data)
- ret = 0;
- else if (ret >= 0)
- ret = -EIO;
- break;
- default:
- if (request == PTRACE_SPARC_DETACH)
- request = PTRACE_DETACH;
- ret = ptrace_request(child, request, addr, data);
- break;
- }
- return ret;
- }
- asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p)
- {
- int ret = 0;
- if (test_thread_flag(TIF_SYSCALL_TRACE)) {
- if (syscall_exit_p)
- ptrace_report_syscall_exit(regs, 0);
- else
- ret = ptrace_report_syscall_entry(regs);
- }
- return ret;
- }
|