metag: ptrace
[linux-3.10.git] / arch / metag / kernel / ptrace.c
1 /*
2  *  Copyright (C) 2005-2012 Imagination Technologies Ltd.
3  *
4  * This file is subject to the terms and conditions of the GNU General
5  * Public License.  See the file COPYING in the main directory of
6  * this archive for more details.
7  */
8
9 #include <linux/kernel.h>
10 #include <linux/mm.h>
11 #include <linux/errno.h>
12 #include <linux/ptrace.h>
13 #include <linux/user.h>
14 #include <linux/regset.h>
15 #include <linux/tracehook.h>
16 #include <linux/elf.h>
17 #include <linux/uaccess.h>
18 #include <trace/syscall.h>
19
20 #define CREATE_TRACE_POINTS
21 #include <trace/events/syscalls.h>
22
23 /*
24  * user_regset definitions.
25  */
26
27 int metag_gp_regs_copyout(const struct pt_regs *regs,
28                           unsigned int pos, unsigned int count,
29                           void *kbuf, void __user *ubuf)
30 {
31         const void *ptr;
32         unsigned long data;
33         int ret;
34
35         /* D{0-1}.{0-7} */
36         ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
37                                   regs->ctx.DX, 0, 4*16);
38         if (ret)
39                 goto out;
40         /* A{0-1}.{0-1} */
41         ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
42                                   regs->ctx.AX, 4*16, 4*20);
43         if (ret)
44                 goto out;
45         /* A{0-1}.2 */
46         if (regs->ctx.SaveMask & TBICTX_XEXT_BIT)
47                 ptr = regs->ctx.Ext.Ctx.pExt;
48         else
49                 ptr = &regs->ctx.Ext.AX2;
50         ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
51                                   ptr, 4*20, 4*22);
52         if (ret)
53                 goto out;
54         /* A{0-1}.3 */
55         ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
56                                   &regs->ctx.AX3, 4*22, 4*24);
57         if (ret)
58                 goto out;
59         /* PC */
60         ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
61                                   &regs->ctx.CurrPC, 4*24, 4*25);
62         if (ret)
63                 goto out;
64         /* TXSTATUS */
65         data = (unsigned long)regs->ctx.Flags;
66         if (regs->ctx.SaveMask & TBICTX_CBUF_BIT)
67                 data |= USER_GP_REGS_STATUS_CATCH_BIT;
68         ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
69                                   &data, 4*25, 4*26);
70         if (ret)
71                 goto out;
72         /* TXRPT, TXBPOBITS, TXMODE */
73         ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
74                                   &regs->ctx.CurrRPT, 4*26, 4*29);
75         if (ret)
76                 goto out;
77         /* Padding */
78         ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
79                                        4*29, 4*30);
80 out:
81         return ret;
82 }
83
84 int metag_gp_regs_copyin(struct pt_regs *regs,
85                          unsigned int pos, unsigned int count,
86                          const void *kbuf, const void __user *ubuf)
87 {
88         void *ptr;
89         unsigned long data;
90         int ret;
91
92         /* D{0-1}.{0-7} */
93         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
94                                  regs->ctx.DX, 0, 4*16);
95         if (ret)
96                 goto out;
97         /* A{0-1}.{0-1} */
98         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
99                                  regs->ctx.AX, 4*16, 4*20);
100         if (ret)
101                 goto out;
102         /* A{0-1}.2 */
103         if (regs->ctx.SaveMask & TBICTX_XEXT_BIT)
104                 ptr = regs->ctx.Ext.Ctx.pExt;
105         else
106                 ptr = &regs->ctx.Ext.AX2;
107         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
108                                  ptr, 4*20, 4*22);
109         if (ret)
110                 goto out;
111         /* A{0-1}.3 */
112         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
113                                  &regs->ctx.AX3, 4*22, 4*24);
114         if (ret)
115                 goto out;
116         /* PC */
117         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
118                                  &regs->ctx.CurrPC, 4*24, 4*25);
119         if (ret)
120                 goto out;
121         /* TXSTATUS */
122         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
123                                  &data, 4*25, 4*26);
124         if (ret)
125                 goto out;
126         regs->ctx.Flags = data & 0xffff;
127         if (data & USER_GP_REGS_STATUS_CATCH_BIT)
128                 regs->ctx.SaveMask |= TBICTX_XCBF_BIT | TBICTX_CBUF_BIT;
129         else
130                 regs->ctx.SaveMask &= ~TBICTX_CBUF_BIT;
131         /* TXRPT, TXBPOBITS, TXMODE */
132         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
133                                  &regs->ctx.CurrRPT, 4*26, 4*29);
134 out:
135         return ret;
136 }
137
138 static int metag_gp_regs_get(struct task_struct *target,
139                              const struct user_regset *regset,
140                              unsigned int pos, unsigned int count,
141                              void *kbuf, void __user *ubuf)
142 {
143         const struct pt_regs *regs = task_pt_regs(target);
144         return metag_gp_regs_copyout(regs, pos, count, kbuf, ubuf);
145 }
146
147 static int metag_gp_regs_set(struct task_struct *target,
148                              const struct user_regset *regset,
149                              unsigned int pos, unsigned int count,
150                              const void *kbuf, const void __user *ubuf)
151 {
152         struct pt_regs *regs = task_pt_regs(target);
153         return metag_gp_regs_copyin(regs, pos, count, kbuf, ubuf);
154 }
155
156 int metag_cb_regs_copyout(const struct pt_regs *regs,
157                           unsigned int pos, unsigned int count,
158                           void *kbuf, void __user *ubuf)
159 {
160         int ret;
161
162         /* TXCATCH{0-3} */
163         if (regs->ctx.SaveMask & TBICTX_XCBF_BIT)
164                 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
165                                           regs->extcb0, 0, 4*4);
166         else
167                 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
168                                                0, 4*4);
169         return ret;
170 }
171
172 int metag_cb_regs_copyin(struct pt_regs *regs,
173                          unsigned int pos, unsigned int count,
174                          const void *kbuf, const void __user *ubuf)
175 {
176         int ret;
177
178         /* TXCATCH{0-3} */
179         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
180                                  regs->extcb0, 0, 4*4);
181         return ret;
182 }
183
184 static int metag_cb_regs_get(struct task_struct *target,
185                              const struct user_regset *regset,
186                              unsigned int pos, unsigned int count,
187                              void *kbuf, void __user *ubuf)
188 {
189         const struct pt_regs *regs = task_pt_regs(target);
190         return metag_cb_regs_copyout(regs, pos, count, kbuf, ubuf);
191 }
192
193 static int metag_cb_regs_set(struct task_struct *target,
194                              const struct user_regset *regset,
195                              unsigned int pos, unsigned int count,
196                              const void *kbuf, const void __user *ubuf)
197 {
198         struct pt_regs *regs = task_pt_regs(target);
199         return metag_cb_regs_copyin(regs, pos, count, kbuf, ubuf);
200 }
201
202 int metag_rp_state_copyout(const struct pt_regs *regs,
203                            unsigned int pos, unsigned int count,
204                            void *kbuf, void __user *ubuf)
205 {
206         unsigned long mask;
207         u64 *ptr;
208         int ret, i;
209
210         /* Empty read pipeline */
211         if (!(regs->ctx.SaveMask & TBICTX_CBRP_BIT)) {
212                 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
213                                                0, 4*13);
214                 goto out;
215         }
216
217         mask = (regs->ctx.CurrDIVTIME & TXDIVTIME_RPMASK_BITS) >>
218                 TXDIVTIME_RPMASK_S;
219
220         /* Read pipeline entries */
221         ptr = (void *)&regs->extcb0[1];
222         for (i = 0; i < 6; ++i, ++ptr) {
223                 if (mask & (1 << i))
224                         ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
225                                                   ptr, 8*i, 8*(i + 1));
226                 else
227                         ret = user_regset_copyout_zero(&pos, &count, &kbuf,
228                                                        &ubuf, 8*i, 8*(i + 1));
229                 if (ret)
230                         goto out;
231         }
232         /* Mask of entries */
233         ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
234                                   &mask, 4*12, 4*13);
235 out:
236         return ret;
237 }
238
239 int metag_rp_state_copyin(struct pt_regs *regs,
240                           unsigned int pos, unsigned int count,
241                           const void *kbuf, const void __user *ubuf)
242 {
243         struct user_rp_state rp;
244         unsigned long long *ptr;
245         int ret, i;
246
247         /* Read the entire pipeline before making any changes */
248         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
249                                  &rp, 0, 4*13);
250         if (ret)
251                 goto out;
252
253         /* Write pipeline entries */
254         ptr = (void *)&regs->extcb0[1];
255         for (i = 0; i < 6; ++i, ++ptr)
256                 if (rp.mask & (1 << i))
257                         *ptr = rp.entries[i];
258
259         /* Update RPMask in TXDIVTIME */
260         regs->ctx.CurrDIVTIME &= ~TXDIVTIME_RPMASK_BITS;
261         regs->ctx.CurrDIVTIME |= (rp.mask << TXDIVTIME_RPMASK_S)
262                                  & TXDIVTIME_RPMASK_BITS;
263
264         /* Set/clear flags to indicate catch/read pipeline state */
265         if (rp.mask)
266                 regs->ctx.SaveMask |= TBICTX_XCBF_BIT | TBICTX_CBRP_BIT;
267         else
268                 regs->ctx.SaveMask &= ~TBICTX_CBRP_BIT;
269 out:
270         return ret;
271 }
272
273 static int metag_rp_state_get(struct task_struct *target,
274                               const struct user_regset *regset,
275                               unsigned int pos, unsigned int count,
276                               void *kbuf, void __user *ubuf)
277 {
278         const struct pt_regs *regs = task_pt_regs(target);
279         return metag_rp_state_copyout(regs, pos, count, kbuf, ubuf);
280 }
281
282 static int metag_rp_state_set(struct task_struct *target,
283                               const struct user_regset *regset,
284                               unsigned int pos, unsigned int count,
285                               const void *kbuf, const void __user *ubuf)
286 {
287         struct pt_regs *regs = task_pt_regs(target);
288         return metag_rp_state_copyin(regs, pos, count, kbuf, ubuf);
289 }
290
291 enum metag_regset {
292         REGSET_GENERAL,
293         REGSET_CBUF,
294         REGSET_READPIPE,
295 };
296
297 static const struct user_regset metag_regsets[] = {
298         [REGSET_GENERAL] = {
299                 .core_note_type = NT_PRSTATUS,
300                 .n = ELF_NGREG,
301                 .size = sizeof(long),
302                 .align = sizeof(long long),
303                 .get = metag_gp_regs_get,
304                 .set = metag_gp_regs_set,
305         },
306         [REGSET_CBUF] = {
307                 .core_note_type = NT_METAG_CBUF,
308                 .n = sizeof(struct user_cb_regs) / sizeof(long),
309                 .size = sizeof(long),
310                 .align = sizeof(long long),
311                 .get = metag_cb_regs_get,
312                 .set = metag_cb_regs_set,
313         },
314         [REGSET_READPIPE] = {
315                 .core_note_type = NT_METAG_RPIPE,
316                 .n = sizeof(struct user_rp_state) / sizeof(long),
317                 .size = sizeof(long),
318                 .align = sizeof(long long),
319                 .get = metag_rp_state_get,
320                 .set = metag_rp_state_set,
321         },
322 };
323
324 static const struct user_regset_view user_metag_view = {
325         .name = "metag",
326         .e_machine = EM_METAG,
327         .regsets = metag_regsets,
328         .n = ARRAY_SIZE(metag_regsets)
329 };
330
331 const struct user_regset_view *task_user_regset_view(struct task_struct *task)
332 {
333         return &user_metag_view;
334 }
335
336 /*
337  * Called by kernel/ptrace.c when detaching..
338  *
339  * Make sure single step bits etc are not set.
340  */
341 void ptrace_disable(struct task_struct *child)
342 {
343         /* nothing to do.. */
344 }
345
346 long arch_ptrace(struct task_struct *child, long request, unsigned long addr,
347                  unsigned long data)
348 {
349         int ret;
350
351         switch (request) {
352         default:
353                 ret = ptrace_request(child, request, addr, data);
354                 break;
355         }
356
357         return ret;
358 }
359
360 int syscall_trace_enter(struct pt_regs *regs)
361 {
362         int ret = 0;
363
364         if (test_thread_flag(TIF_SYSCALL_TRACE))
365                 ret = tracehook_report_syscall_entry(regs);
366
367         if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
368                 trace_sys_enter(regs, regs->ctx.DX[0].U1);
369
370         return ret ? -1 : regs->ctx.DX[0].U1;
371 }
372
373 void syscall_trace_leave(struct pt_regs *regs)
374 {
375         if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
376                 trace_sys_exit(regs, regs->ctx.DX[0].U1);
377
378         if (test_thread_flag(TIF_SYSCALL_TRACE))
379                 tracehook_report_syscall_exit(regs, 0);
380 }