|
@@ -1,12 +1,14 @@
|
|
|
/*
|
|
|
- * linux/arch/sh/kernel/ptrace.c
|
|
|
+ * SuperH process tracing
|
|
|
*
|
|
|
- * Original x86 implementation:
|
|
|
- * By Ross Biro 1/23/92
|
|
|
- * edited by Linus Torvalds
|
|
|
+ * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka
|
|
|
+ * Copyright (C) 2002 - 2008 Paul Mundt
|
|
|
*
|
|
|
- * SuperH version: Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka
|
|
|
- * Audit support: Yuichi Nakamura <ynakam@hitachisoft.jp>
|
|
|
+ * Audit support by Yuichi Nakamura <ynakam@hitachisoft.jp>
|
|
|
+ *
|
|
|
+ * This file is subject to the terms and conditions of the GNU General Public
|
|
|
+ * License. See the file "COPYING" in the main directory of this archive
|
|
|
+ * for more details.
|
|
|
*/
|
|
|
#include <linux/kernel.h>
|
|
|
#include <linux/sched.h>
|
|
@@ -22,6 +24,8 @@
|
|
|
#include <linux/audit.h>
|
|
|
#include <linux/seccomp.h>
|
|
|
#include <linux/tracehook.h>
|
|
|
+#include <linux/elf.h>
|
|
|
+#include <linux/regset.h>
|
|
|
#include <asm/uaccess.h>
|
|
|
#include <asm/pgtable.h>
|
|
|
#include <asm/system.h>
|
|
@@ -29,11 +33,6 @@
|
|
|
#include <asm/mmu_context.h>
|
|
|
#include <asm/syscalls.h>
|
|
|
|
|
|
-/*
|
|
|
- * does not yet catch signals sent when the child dies.
|
|
|
- * in exit.c or in signal.c.
|
|
|
- */
|
|
|
-
|
|
|
/*
|
|
|
* This routine will get a word off of the process kernel stack.
|
|
|
*/
|
|
@@ -62,16 +61,12 @@ static inline int put_stack_long(struct task_struct *task, int offset,
|
|
|
|
|
|
void user_enable_single_step(struct task_struct *child)
|
|
|
{
|
|
|
- struct pt_regs *regs = task_pt_regs(child);
|
|
|
- long pc;
|
|
|
-
|
|
|
- pc = get_stack_long(child, (long)®s->pc);
|
|
|
-
|
|
|
/* Next scheduling will set up UBC */
|
|
|
if (child->thread.ubc_pc == 0)
|
|
|
ubc_usercnt += 1;
|
|
|
|
|
|
- child->thread.ubc_pc = pc;
|
|
|
+ child->thread.ubc_pc = get_stack_long(child,
|
|
|
+ offsetof(struct pt_regs, pc));
|
|
|
|
|
|
set_tsk_thread_flag(child, TIF_SINGLESTEP);
|
|
|
}
|
|
@@ -103,6 +98,83 @@ void ptrace_disable(struct task_struct *child)
|
|
|
user_disable_single_step(child);
|
|
|
}
|
|
|
|
|
|
+static int genregs_get(struct task_struct *target,
|
|
|
+ const struct user_regset *regset,
|
|
|
+ unsigned int pos, unsigned int count,
|
|
|
+ void *kbuf, void __user *ubuf)
|
|
|
+{
|
|
|
+ const struct pt_regs *regs = task_pt_regs(target);
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
|
|
|
+ regs->regs,
|
|
|
+ 0, 16 * sizeof(unsigned long));
|
|
|
+ if (!ret)
|
|
|
+ /* PC, PR, SR, GBR, MACH, MACL, TRA */
|
|
|
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
|
|
|
+ ®s->pc,
|
|
|
+ offsetof(struct pt_regs, pc),
|
|
|
+ sizeof(struct pt_regs));
|
|
|
+ if (!ret)
|
|
|
+ ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
|
|
|
+ sizeof(struct pt_regs), -1);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int genregs_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 = task_pt_regs(target);
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
+ regs->regs,
|
|
|
+ 0, 16 * sizeof(unsigned long));
|
|
|
+ if (!ret && count > 0)
|
|
|
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
|
|
|
+ ®s->pc,
|
|
|
+ offsetof(struct pt_regs, pc),
|
|
|
+ sizeof(struct pt_regs));
|
|
|
+ if (!ret)
|
|
|
+ ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
|
|
|
+ sizeof(struct pt_regs), -1);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * These are our native regset flavours.
|
|
|
+ */
|
|
|
+enum sh_regset {
|
|
|
+ REGSET_GENERAL,
|
|
|
+};
|
|
|
+
|
|
|
+static const struct user_regset sh_regsets[] = {
|
|
|
+ /*
|
|
|
+ * Format is:
|
|
|
+ * R0 --> R15
|
|
|
+ * PC, PR, SR, GBR, MACH, MACL, TRA
|
|
|
+ */
|
|
|
+ [REGSET_GENERAL] = {
|
|
|
+ .core_note_type = NT_PRSTATUS,
|
|
|
+ .n = ELF_NGREG,
|
|
|
+ .size = sizeof(long),
|
|
|
+ .align = sizeof(long),
|
|
|
+ .get = genregs_get,
|
|
|
+ .set = genregs_set,
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+static const struct user_regset_view user_sh_native_view = {
|
|
|
+ .name = "sh",
|
|
|
+ .e_machine = EM_SH,
|
|
|
+ .regsets = sh_regsets,
|
|
|
+ .n = ARRAY_SIZE(sh_regsets),
|
|
|
+};
|
|
|
+
|
|
|
long arch_ptrace(struct task_struct *child, long request, long addr, long data)
|
|
|
{
|
|
|
struct user * dummy = NULL;
|
|
@@ -159,6 +231,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
+ case PTRACE_GETREGS:
|
|
|
+ return copy_regset_to_user(child, &user_sh_native_view,
|
|
|
+ REGSET_GENERAL,
|
|
|
+ 0, sizeof(struct pt_regs),
|
|
|
+ (void __user *)data);
|
|
|
+ case PTRACE_SETREGS:
|
|
|
+ return copy_regset_from_user(child, &user_sh_native_view,
|
|
|
+ REGSET_GENERAL,
|
|
|
+ 0, sizeof(struct pt_regs),
|
|
|
+ (const void __user *)data);
|
|
|
#ifdef CONFIG_SH_DSP
|
|
|
case PTRACE_GETDSPREGS: {
|
|
|
unsigned long dp;
|