diff options
Diffstat (limited to 'arch/sh/kernel/ptrace_64.c')
-rw-r--r-- | arch/sh/kernel/ptrace_64.c | 186 |
1 files changed, 185 insertions, 1 deletions
diff --git a/arch/sh/kernel/ptrace_64.c b/arch/sh/kernel/ptrace_64.c index e15b099c1f0..695097438f0 100644 --- a/arch/sh/kernel/ptrace_64.c +++ b/arch/sh/kernel/ptrace_64.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * arch/sh/kernel/ptrace_64.c | 2 | * arch/sh/kernel/ptrace_64.c |
3 | * | 3 | * |
4 | * Copyright (C) 2000, 2001 Paolo Alberelli | 4 | * Copyright (C) 2000, 2001 Paolo Alberelli |
5 | * Copyright (C) 2003 - 2007 Paul Mundt | 5 | * Copyright (C) 2003 - 2008 Paul Mundt |
6 | * | 6 | * |
7 | * Started from SH3/4 version: | 7 | * Started from SH3/4 version: |
8 | * SuperH version: Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka | 8 | * SuperH version: Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka |
@@ -29,6 +29,8 @@ | |||
29 | #include <linux/audit.h> | 29 | #include <linux/audit.h> |
30 | #include <linux/seccomp.h> | 30 | #include <linux/seccomp.h> |
31 | #include <linux/tracehook.h> | 31 | #include <linux/tracehook.h> |
32 | #include <linux/elf.h> | ||
33 | #include <linux/regset.h> | ||
32 | #include <asm/io.h> | 34 | #include <asm/io.h> |
33 | #include <asm/uaccess.h> | 35 | #include <asm/uaccess.h> |
34 | #include <asm/pgtable.h> | 36 | #include <asm/pgtable.h> |
@@ -137,6 +139,165 @@ void user_disable_single_step(struct task_struct *child) | |||
137 | regs->sr &= ~SR_SSTEP; | 139 | regs->sr &= ~SR_SSTEP; |
138 | } | 140 | } |
139 | 141 | ||
142 | static int genregs_get(struct task_struct *target, | ||
143 | const struct user_regset *regset, | ||
144 | unsigned int pos, unsigned int count, | ||
145 | void *kbuf, void __user *ubuf) | ||
146 | { | ||
147 | const struct pt_regs *regs = task_pt_regs(target); | ||
148 | int ret; | ||
149 | |||
150 | /* PC, SR, SYSCALL */ | ||
151 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
152 | ®s->pc, | ||
153 | 0, 3 * sizeof(unsigned long long)); | ||
154 | |||
155 | /* R1 -> R63 */ | ||
156 | if (!ret) | ||
157 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
158 | regs->regs, | ||
159 | offsetof(struct pt_regs, regs[0]), | ||
160 | 63 * sizeof(unsigned long long)); | ||
161 | /* TR0 -> TR7 */ | ||
162 | if (!ret) | ||
163 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
164 | regs->tregs, | ||
165 | offsetof(struct pt_regs, tregs[0]), | ||
166 | 8 * sizeof(unsigned long long)); | ||
167 | |||
168 | if (!ret) | ||
169 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
170 | sizeof(struct pt_regs), -1); | ||
171 | |||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | static int genregs_set(struct task_struct *target, | ||
176 | const struct user_regset *regset, | ||
177 | unsigned int pos, unsigned int count, | ||
178 | const void *kbuf, const void __user *ubuf) | ||
179 | { | ||
180 | struct pt_regs *regs = task_pt_regs(target); | ||
181 | int ret; | ||
182 | |||
183 | /* PC, SR, SYSCALL */ | ||
184 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
185 | ®s->pc, | ||
186 | 0, 3 * sizeof(unsigned long long)); | ||
187 | |||
188 | /* R1 -> R63 */ | ||
189 | if (!ret && count > 0) | ||
190 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
191 | regs->regs, | ||
192 | offsetof(struct pt_regs, regs[0]), | ||
193 | 63 * sizeof(unsigned long long)); | ||
194 | |||
195 | /* TR0 -> TR7 */ | ||
196 | if (!ret && count > 0) | ||
197 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
198 | regs->tregs, | ||
199 | offsetof(struct pt_regs, tregs[0]), | ||
200 | 8 * sizeof(unsigned long long)); | ||
201 | |||
202 | if (!ret) | ||
203 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
204 | sizeof(struct pt_regs), -1); | ||
205 | |||
206 | return ret; | ||
207 | } | ||
208 | |||
209 | #ifdef CONFIG_SH_FPU | ||
210 | int fpregs_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 | int ret; | ||
216 | |||
217 | ret = init_fpu(target); | ||
218 | if (ret) | ||
219 | return ret; | ||
220 | |||
221 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
222 | &target->thread.fpu.hard, 0, -1); | ||
223 | } | ||
224 | |||
225 | static int fpregs_set(struct task_struct *target, | ||
226 | const struct user_regset *regset, | ||
227 | unsigned int pos, unsigned int count, | ||
228 | const void *kbuf, const void __user *ubuf) | ||
229 | { | ||
230 | int ret; | ||
231 | |||
232 | ret = init_fpu(target); | ||
233 | if (ret) | ||
234 | return ret; | ||
235 | |||
236 | set_stopped_child_used_math(target); | ||
237 | |||
238 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
239 | &target->thread.fpu.hard, 0, -1); | ||
240 | } | ||
241 | |||
242 | static int fpregs_active(struct task_struct *target, | ||
243 | const struct user_regset *regset) | ||
244 | { | ||
245 | return tsk_used_math(target) ? regset->n : 0; | ||
246 | } | ||
247 | #endif | ||
248 | |||
249 | /* | ||
250 | * These are our native regset flavours. | ||
251 | */ | ||
252 | enum sh_regset { | ||
253 | REGSET_GENERAL, | ||
254 | #ifdef CONFIG_SH_FPU | ||
255 | REGSET_FPU, | ||
256 | #endif | ||
257 | }; | ||
258 | |||
259 | static const struct user_regset sh_regsets[] = { | ||
260 | /* | ||
261 | * Format is: | ||
262 | * PC, SR, SYSCALL, | ||
263 | * R1 --> R63, | ||
264 | * TR0 --> TR7, | ||
265 | */ | ||
266 | [REGSET_GENERAL] = { | ||
267 | .core_note_type = NT_PRSTATUS, | ||
268 | .n = ELF_NGREG, | ||
269 | .size = sizeof(long long), | ||
270 | .align = sizeof(long long), | ||
271 | .get = genregs_get, | ||
272 | .set = genregs_set, | ||
273 | }, | ||
274 | |||
275 | #ifdef CONFIG_SH_FPU | ||
276 | [REGSET_FPU] = { | ||
277 | .core_note_type = NT_PRFPREG, | ||
278 | .n = sizeof(struct user_fpu_struct) / | ||
279 | sizeof(long long), | ||
280 | .size = sizeof(long long), | ||
281 | .align = sizeof(long long), | ||
282 | .get = fpregs_get, | ||
283 | .set = fpregs_set, | ||
284 | .active = fpregs_active, | ||
285 | }, | ||
286 | #endif | ||
287 | }; | ||
288 | |||
289 | static const struct user_regset_view user_sh64_native_view = { | ||
290 | .name = "sh64", | ||
291 | .e_machine = EM_SH, | ||
292 | .regsets = sh_regsets, | ||
293 | .n = ARRAY_SIZE(sh_regsets), | ||
294 | }; | ||
295 | |||
296 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | ||
297 | { | ||
298 | return &user_sh64_native_view; | ||
299 | } | ||
300 | |||
140 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | 301 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) |
141 | { | 302 | { |
142 | int ret; | 303 | int ret; |
@@ -195,10 +356,33 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
195 | } | 356 | } |
196 | break; | 357 | break; |
197 | 358 | ||
359 | case PTRACE_GETREGS: | ||
360 | return copy_regset_to_user(child, &user_sh64_native_view, | ||
361 | REGSET_GENERAL, | ||
362 | 0, sizeof(struct pt_regs), | ||
363 | (void __user *)data); | ||
364 | case PTRACE_SETREGS: | ||
365 | return copy_regset_from_user(child, &user_sh64_native_view, | ||
366 | REGSET_GENERAL, | ||
367 | 0, sizeof(struct pt_regs), | ||
368 | (const void __user *)data); | ||
369 | #ifdef CONFIG_SH_FPU | ||
370 | case PTRACE_GETFPREGS: | ||
371 | return copy_regset_to_user(child, &user_sh64_native_view, | ||
372 | REGSET_FPU, | ||
373 | 0, sizeof(struct user_fpu_struct), | ||
374 | (void __user *)data); | ||
375 | case PTRACE_SETFPREGS: | ||
376 | return copy_regset_from_user(child, &user_sh64_native_view, | ||
377 | REGSET_FPU, | ||
378 | 0, sizeof(struct user_fpu_struct), | ||
379 | (const void __user *)data); | ||
380 | #endif | ||
198 | default: | 381 | default: |
199 | ret = ptrace_request(child, request, addr, data); | 382 | ret = ptrace_request(child, request, addr, data); |
200 | break; | 383 | break; |
201 | } | 384 | } |
385 | |||
202 | return ret; | 386 | return ret; |
203 | } | 387 | } |
204 | 388 | ||