diff options
Diffstat (limited to 'arch/blackfin/kernel/ptrace.c')
-rw-r--r-- | arch/blackfin/kernel/ptrace.c | 113 |
1 files changed, 90 insertions, 23 deletions
diff --git a/arch/blackfin/kernel/ptrace.c b/arch/blackfin/kernel/ptrace.c index 9536c1e2d1c7..92b4ca0b5af6 100644 --- a/arch/blackfin/kernel/ptrace.c +++ b/arch/blackfin/kernel/ptrace.c | |||
@@ -9,9 +9,11 @@ | |||
9 | #include <linux/sched.h> | 9 | #include <linux/sched.h> |
10 | #include <linux/mm.h> | 10 | #include <linux/mm.h> |
11 | #include <linux/smp.h> | 11 | #include <linux/smp.h> |
12 | #include <linux/elf.h> | ||
12 | #include <linux/errno.h> | 13 | #include <linux/errno.h> |
13 | #include <linux/ptrace.h> | 14 | #include <linux/ptrace.h> |
14 | #include <linux/user.h> | 15 | #include <linux/user.h> |
16 | #include <linux/regset.h> | ||
15 | #include <linux/signal.h> | 17 | #include <linux/signal.h> |
16 | #include <linux/uaccess.h> | 18 | #include <linux/uaccess.h> |
17 | 19 | ||
@@ -50,22 +52,6 @@ static inline struct pt_regs *task_pt_regs(struct task_struct *task) | |||
50 | } | 52 | } |
51 | 53 | ||
52 | /* | 54 | /* |
53 | * Get all user integer registers. | ||
54 | */ | ||
55 | static inline int ptrace_getregs(struct task_struct *tsk, void __user *uregs) | ||
56 | { | ||
57 | struct pt_regs regs; | ||
58 | memcpy(®s, task_pt_regs(tsk), sizeof(regs)); | ||
59 | regs.usp = tsk->thread.usp; | ||
60 | return copy_to_user(uregs, ®s, sizeof(struct pt_regs)) ? -EFAULT : 0; | ||
61 | } | ||
62 | |||
63 | /* Mapping from PT_xxx to the stack offset at which the register is | ||
64 | * saved. Notice that usp has no stack-slot and needs to be treated | ||
65 | * specially (see get_reg/put_reg below). | ||
66 | */ | ||
67 | |||
68 | /* | ||
69 | * Get contents of register REGNO in task TASK. | 55 | * Get contents of register REGNO in task TASK. |
70 | */ | 56 | */ |
71 | static inline long | 57 | static inline long |
@@ -170,6 +156,84 @@ static inline int is_user_addr_valid(struct task_struct *child, | |||
170 | return -EIO; | 156 | return -EIO; |
171 | } | 157 | } |
172 | 158 | ||
159 | /* | ||
160 | * retrieve the contents of Blackfin userspace general registers | ||
161 | */ | ||
162 | static int genregs_get(struct task_struct *target, | ||
163 | const struct user_regset *regset, | ||
164 | unsigned int pos, unsigned int count, | ||
165 | void *kbuf, void __user *ubuf) | ||
166 | { | ||
167 | struct pt_regs *regs = task_pt_regs(target); | ||
168 | int ret; | ||
169 | |||
170 | /* This sucks ... */ | ||
171 | regs->usp = target->thread.usp; | ||
172 | |||
173 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
174 | regs, 0, sizeof(*regs)); | ||
175 | if (ret < 0) | ||
176 | return ret; | ||
177 | |||
178 | return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
179 | sizeof(*regs), -1); | ||
180 | } | ||
181 | |||
182 | /* | ||
183 | * update the contents of the Blackfin userspace general registers | ||
184 | */ | ||
185 | static int genregs_set(struct task_struct *target, | ||
186 | const struct user_regset *regset, | ||
187 | unsigned int pos, unsigned int count, | ||
188 | const void *kbuf, const void __user *ubuf) | ||
189 | { | ||
190 | struct pt_regs *regs = task_pt_regs(target); | ||
191 | int ret; | ||
192 | |||
193 | /* Don't let people set SYSCFG (it's at the end of pt_regs) */ | ||
194 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
195 | regs, 0, PT_SYSCFG); | ||
196 | if (ret < 0) | ||
197 | return ret; | ||
198 | |||
199 | /* This sucks ... */ | ||
200 | target->thread.usp = regs->usp; | ||
201 | /* regs->retx = regs->pc; */ | ||
202 | |||
203 | return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
204 | PT_SYSCFG, -1); | ||
205 | } | ||
206 | |||
207 | /* | ||
208 | * Define the register sets available on the Blackfin under Linux | ||
209 | */ | ||
210 | enum bfin_regset { | ||
211 | REGSET_GENERAL, | ||
212 | }; | ||
213 | |||
214 | static const struct user_regset bfin_regsets[] = { | ||
215 | [REGSET_GENERAL] = { | ||
216 | .core_note_type = NT_PRSTATUS, | ||
217 | .n = sizeof(struct pt_regs) / sizeof(long), | ||
218 | .size = sizeof(long), | ||
219 | .align = sizeof(long), | ||
220 | .get = genregs_get, | ||
221 | .set = genregs_set, | ||
222 | }, | ||
223 | }; | ||
224 | |||
225 | static const struct user_regset_view user_bfin_native_view = { | ||
226 | .name = "Blackfin", | ||
227 | .e_machine = EM_BLACKFIN, | ||
228 | .regsets = bfin_regsets, | ||
229 | .n = ARRAY_SIZE(bfin_regsets), | ||
230 | }; | ||
231 | |||
232 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | ||
233 | { | ||
234 | return &user_bfin_native_view; | ||
235 | } | ||
236 | |||
173 | void ptrace_enable(struct task_struct *child) | 237 | void ptrace_enable(struct task_struct *child) |
174 | { | 238 | { |
175 | struct pt_regs *regs = task_pt_regs(child); | 239 | struct pt_regs *regs = task_pt_regs(child); |
@@ -327,15 +391,18 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
327 | break; | 391 | break; |
328 | 392 | ||
329 | case PTRACE_GETREGS: | 393 | case PTRACE_GETREGS: |
330 | /* Get all gp regs from the child. */ | 394 | pr_debug("ptrace: PTRACE_GETREGS\n"); |
331 | ret = ptrace_getregs(child, datap); | 395 | return copy_regset_to_user(child, &user_bfin_native_view, |
332 | break; | 396 | REGSET_GENERAL, |
397 | 0, sizeof(struct pt_regs), | ||
398 | (void __user *)data); | ||
333 | 399 | ||
334 | case PTRACE_SETREGS: | 400 | case PTRACE_SETREGS: |
335 | printk(KERN_WARNING "ptrace: SETREGS: **** NOT IMPLEMENTED ***\n"); | 401 | pr_debug("ptrace: PTRACE_SETREGS\n"); |
336 | /* Set all gp regs in the child. */ | 402 | return copy_regset_from_user(child, &user_bfin_native_view, |
337 | ret = 0; | 403 | REGSET_GENERAL, |
338 | break; | 404 | 0, sizeof(struct pt_regs), |
405 | (const void __user *)data); | ||
339 | 406 | ||
340 | default: | 407 | default: |
341 | ret = ptrace_request(child, request, addr, data); | 408 | ret = ptrace_request(child, request, addr, data); |