diff options
author | Paul Mundt <lethal@linux-sh.org> | 2008-09-12 06:52:36 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2008-09-12 06:52:36 -0400 |
commit | 934135c19d8a1be435bae75aefc09b8ae1698b16 (patch) | |
tree | 825be606c643ed792a2973263cade41d05171cde /arch/sh/kernel | |
parent | 09558748464a9afafe2848a3ad4cfd509c9b0fb6 (diff) |
sh: ptrace: Introduce user_regset interface for gp regs.
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh/kernel')
-rw-r--r-- | arch/sh/kernel/ptrace_32.c | 116 |
1 files changed, 99 insertions, 17 deletions
diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c index 84bf3420597c..5e3ba10255cd 100644 --- a/arch/sh/kernel/ptrace_32.c +++ b/arch/sh/kernel/ptrace_32.c | |||
@@ -1,12 +1,14 @@ | |||
1 | /* | 1 | /* |
2 | * linux/arch/sh/kernel/ptrace.c | 2 | * SuperH process tracing |
3 | * | 3 | * |
4 | * Original x86 implementation: | 4 | * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka |
5 | * By Ross Biro 1/23/92 | 5 | * Copyright (C) 2002 - 2008 Paul Mundt |
6 | * edited by Linus Torvalds | ||
7 | * | 6 | * |
8 | * SuperH version: Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka | 7 | * Audit support by Yuichi Nakamura <ynakam@hitachisoft.jp> |
9 | * Audit support: Yuichi Nakamura <ynakam@hitachisoft.jp> | 8 | * |
9 | * This file is subject to the terms and conditions of the GNU General Public | ||
10 | * License. See the file "COPYING" in the main directory of this archive | ||
11 | * for more details. | ||
10 | */ | 12 | */ |
11 | #include <linux/kernel.h> | 13 | #include <linux/kernel.h> |
12 | #include <linux/sched.h> | 14 | #include <linux/sched.h> |
@@ -22,6 +24,8 @@ | |||
22 | #include <linux/audit.h> | 24 | #include <linux/audit.h> |
23 | #include <linux/seccomp.h> | 25 | #include <linux/seccomp.h> |
24 | #include <linux/tracehook.h> | 26 | #include <linux/tracehook.h> |
27 | #include <linux/elf.h> | ||
28 | #include <linux/regset.h> | ||
25 | #include <asm/uaccess.h> | 29 | #include <asm/uaccess.h> |
26 | #include <asm/pgtable.h> | 30 | #include <asm/pgtable.h> |
27 | #include <asm/system.h> | 31 | #include <asm/system.h> |
@@ -30,11 +34,6 @@ | |||
30 | #include <asm/syscalls.h> | 34 | #include <asm/syscalls.h> |
31 | 35 | ||
32 | /* | 36 | /* |
33 | * does not yet catch signals sent when the child dies. | ||
34 | * in exit.c or in signal.c. | ||
35 | */ | ||
36 | |||
37 | /* | ||
38 | * This routine will get a word off of the process kernel stack. | 37 | * This routine will get a word off of the process kernel stack. |
39 | */ | 38 | */ |
40 | static inline int get_stack_long(struct task_struct *task, int offset) | 39 | static inline int get_stack_long(struct task_struct *task, int offset) |
@@ -62,16 +61,12 @@ static inline int put_stack_long(struct task_struct *task, int offset, | |||
62 | 61 | ||
63 | void user_enable_single_step(struct task_struct *child) | 62 | void user_enable_single_step(struct task_struct *child) |
64 | { | 63 | { |
65 | struct pt_regs *regs = task_pt_regs(child); | ||
66 | long pc; | ||
67 | |||
68 | pc = get_stack_long(child, (long)®s->pc); | ||
69 | |||
70 | /* Next scheduling will set up UBC */ | 64 | /* Next scheduling will set up UBC */ |
71 | if (child->thread.ubc_pc == 0) | 65 | if (child->thread.ubc_pc == 0) |
72 | ubc_usercnt += 1; | 66 | ubc_usercnt += 1; |
73 | 67 | ||
74 | child->thread.ubc_pc = pc; | 68 | child->thread.ubc_pc = get_stack_long(child, |
69 | offsetof(struct pt_regs, pc)); | ||
75 | 70 | ||
76 | set_tsk_thread_flag(child, TIF_SINGLESTEP); | 71 | set_tsk_thread_flag(child, TIF_SINGLESTEP); |
77 | } | 72 | } |
@@ -103,6 +98,83 @@ void ptrace_disable(struct task_struct *child) | |||
103 | user_disable_single_step(child); | 98 | user_disable_single_step(child); |
104 | } | 99 | } |
105 | 100 | ||
101 | static int genregs_get(struct task_struct *target, | ||
102 | const struct user_regset *regset, | ||
103 | unsigned int pos, unsigned int count, | ||
104 | void *kbuf, void __user *ubuf) | ||
105 | { | ||
106 | const struct pt_regs *regs = task_pt_regs(target); | ||
107 | int ret; | ||
108 | |||
109 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
110 | regs->regs, | ||
111 | 0, 16 * sizeof(unsigned long)); | ||
112 | if (!ret) | ||
113 | /* PC, PR, SR, GBR, MACH, MACL, TRA */ | ||
114 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
115 | ®s->pc, | ||
116 | offsetof(struct pt_regs, pc), | ||
117 | sizeof(struct pt_regs)); | ||
118 | if (!ret) | ||
119 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
120 | sizeof(struct pt_regs), -1); | ||
121 | |||
122 | return ret; | ||
123 | } | ||
124 | |||
125 | static int genregs_set(struct task_struct *target, | ||
126 | const struct user_regset *regset, | ||
127 | unsigned int pos, unsigned int count, | ||
128 | const void *kbuf, const void __user *ubuf) | ||
129 | { | ||
130 | struct pt_regs *regs = task_pt_regs(target); | ||
131 | int ret; | ||
132 | |||
133 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
134 | regs->regs, | ||
135 | 0, 16 * sizeof(unsigned long)); | ||
136 | if (!ret && count > 0) | ||
137 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
138 | ®s->pc, | ||
139 | offsetof(struct pt_regs, pc), | ||
140 | sizeof(struct pt_regs)); | ||
141 | if (!ret) | ||
142 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
143 | sizeof(struct pt_regs), -1); | ||
144 | |||
145 | return ret; | ||
146 | } | ||
147 | |||
148 | /* | ||
149 | * These are our native regset flavours. | ||
150 | */ | ||
151 | enum sh_regset { | ||
152 | REGSET_GENERAL, | ||
153 | }; | ||
154 | |||
155 | static const struct user_regset sh_regsets[] = { | ||
156 | /* | ||
157 | * Format is: | ||
158 | * R0 --> R15 | ||
159 | * PC, PR, SR, GBR, MACH, MACL, TRA | ||
160 | */ | ||
161 | [REGSET_GENERAL] = { | ||
162 | .core_note_type = NT_PRSTATUS, | ||
163 | .n = ELF_NGREG, | ||
164 | .size = sizeof(long), | ||
165 | .align = sizeof(long), | ||
166 | .get = genregs_get, | ||
167 | .set = genregs_set, | ||
168 | }, | ||
169 | }; | ||
170 | |||
171 | static const struct user_regset_view user_sh_native_view = { | ||
172 | .name = "sh", | ||
173 | .e_machine = EM_SH, | ||
174 | .regsets = sh_regsets, | ||
175 | .n = ARRAY_SIZE(sh_regsets), | ||
176 | }; | ||
177 | |||
106 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | 178 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) |
107 | { | 179 | { |
108 | struct user * dummy = NULL; | 180 | struct user * dummy = NULL; |
@@ -159,6 +231,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
159 | } | 231 | } |
160 | break; | 232 | break; |
161 | 233 | ||
234 | case PTRACE_GETREGS: | ||
235 | return copy_regset_to_user(child, &user_sh_native_view, | ||
236 | REGSET_GENERAL, | ||
237 | 0, sizeof(struct pt_regs), | ||
238 | (void __user *)data); | ||
239 | case PTRACE_SETREGS: | ||
240 | return copy_regset_from_user(child, &user_sh_native_view, | ||
241 | REGSET_GENERAL, | ||
242 | 0, sizeof(struct pt_regs), | ||
243 | (const void __user *)data); | ||
162 | #ifdef CONFIG_SH_DSP | 244 | #ifdef CONFIG_SH_DSP |
163 | case PTRACE_GETDSPREGS: { | 245 | case PTRACE_GETDSPREGS: { |
164 | unsigned long dp; | 246 | unsigned long dp; |