GNU Linux-libre 4.14.295-gnu1
[releases.git] / arch / sparc / kernel / ptrace_32.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* ptrace.c: Sparc process tracing support.
3  *
4  * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net)
5  *
6  * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
7  * and David Mosberger.
8  *
9  * Added Linux support -miguel (weird, eh?, the original code was meant
10  * to emulate SunOS).
11  */
12
13 #include <linux/kernel.h>
14 #include <linux/sched.h>
15 #include <linux/mm.h>
16 #include <linux/errno.h>
17 #include <linux/ptrace.h>
18 #include <linux/user.h>
19 #include <linux/smp.h>
20 #include <linux/security.h>
21 #include <linux/signal.h>
22 #include <linux/regset.h>
23 #include <linux/elf.h>
24 #include <linux/tracehook.h>
25
26 #include <asm/pgtable.h>
27 #include <linux/uaccess.h>
28 #include <asm/cacheflush.h>
29
30 #include "kernel.h"
31
32 /* #define ALLOW_INIT_TRACING */
33
34 /*
35  * Called by kernel/ptrace.c when detaching..
36  *
37  * Make sure single step bits etc are not set.
38  */
39 void ptrace_disable(struct task_struct *child)
40 {
41         /* nothing to do */
42 }
43
44 enum sparc_regset {
45         REGSET_GENERAL,
46         REGSET_FP,
47 };
48
49 static int regwindow32_get(struct task_struct *target,
50                            const struct pt_regs *regs,
51                            u32 *uregs)
52 {
53         unsigned long reg_window = regs->u_regs[UREG_I6];
54         int size = 16 * sizeof(u32);
55
56         if (target == current) {
57                 if (copy_from_user(uregs, (void __user *)reg_window, size))
58                         return -EFAULT;
59         } else {
60                 if (access_process_vm(target, reg_window, uregs, size,
61                                       FOLL_FORCE) != size)
62                         return -EFAULT;
63         }
64         return 0;
65 }
66
67 static int regwindow32_set(struct task_struct *target,
68                            const struct pt_regs *regs,
69                            u32 *uregs)
70 {
71         unsigned long reg_window = regs->u_regs[UREG_I6];
72         int size = 16 * sizeof(u32);
73
74         if (target == current) {
75                 if (copy_to_user((void __user *)reg_window, uregs, size))
76                         return -EFAULT;
77         } else {
78                 if (access_process_vm(target, reg_window, uregs, size,
79                                       FOLL_FORCE | FOLL_WRITE) != size)
80                         return -EFAULT;
81         }
82         return 0;
83 }
84
85 static int genregs32_get(struct task_struct *target,
86                          const struct user_regset *regset,
87                          unsigned int pos, unsigned int count,
88                          void *kbuf, void __user *ubuf)
89 {
90         const struct pt_regs *regs = target->thread.kregs;
91         u32 uregs[16];
92         int ret;
93
94         if (target == current)
95                 flush_user_windows();
96
97         ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
98                                   regs->u_regs,
99                                   0, 16 * sizeof(u32));
100         if (ret || !count)
101                 return ret;
102
103         if (pos < 32 * sizeof(u32)) {
104                 if (regwindow32_get(target, regs, uregs))
105                         return -EFAULT;
106                 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
107                                           uregs,
108                                           16 * sizeof(u32), 32 * sizeof(u32));
109                 if (ret || !count)
110                         return ret;
111         }
112
113         uregs[0] = regs->psr;
114         uregs[1] = regs->pc;
115         uregs[2] = regs->npc;
116         uregs[3] = regs->y;
117         uregs[4] = 0;   /* WIM */
118         uregs[5] = 0;   /* TBR */
119         return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
120                                   uregs,
121                                   32 * sizeof(u32), 38 * sizeof(u32));
122 }
123
124 static int genregs32_set(struct task_struct *target,
125                          const struct user_regset *regset,
126                          unsigned int pos, unsigned int count,
127                          const void *kbuf, const void __user *ubuf)
128 {
129         struct pt_regs *regs = target->thread.kregs;
130         u32 uregs[16];
131         u32 psr;
132         int ret;
133
134         if (target == current)
135                 flush_user_windows();
136
137         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
138                                  regs->u_regs,
139                                  0, 16 * sizeof(u32));
140         if (ret || !count)
141                 return ret;
142
143         if (pos < 32 * sizeof(u32)) {
144                 if (regwindow32_get(target, regs, uregs))
145                         return -EFAULT;
146                 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
147                                          uregs,
148                                          16 * sizeof(u32), 32 * sizeof(u32));
149                 if (ret)
150                         return ret;
151                 if (regwindow32_set(target, regs, uregs))
152                         return -EFAULT;
153                 if (!count)
154                         return 0;
155         }
156         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
157                                  &psr,
158                                  32 * sizeof(u32), 33 * sizeof(u32));
159         if (ret)
160                 return ret;
161         regs->psr = (regs->psr & ~(PSR_ICC | PSR_SYSCALL)) |
162                     (psr & (PSR_ICC | PSR_SYSCALL));
163         if (!count)
164                 return 0;
165         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
166                                  &regs->pc,
167                                  33 * sizeof(u32), 34 * sizeof(u32));
168         if (ret || !count)
169                 return ret;
170         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
171                                  &regs->npc,
172                                  34 * sizeof(u32), 35 * sizeof(u32));
173         if (ret || !count)
174                 return ret;
175         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
176                                  &regs->y,
177                                  35 * sizeof(u32), 36 * sizeof(u32));
178         if (ret || !count)
179                 return ret;
180         return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
181                                          36 * sizeof(u32), 38 * sizeof(u32));
182 }
183
184 static int fpregs32_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 unsigned long *fpregs = target->thread.float_regs;
190         int ret = 0;
191
192 #if 0
193         if (target == current)
194                 save_and_clear_fpu();
195 #endif
196
197         ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
198                                   fpregs,
199                                   0, 32 * sizeof(u32));
200
201         if (!ret)
202                 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
203                                                32 * sizeof(u32),
204                                                33 * sizeof(u32));
205         if (!ret)
206                 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
207                                           &target->thread.fsr,
208                                           33 * sizeof(u32),
209                                           34 * sizeof(u32));
210
211         if (!ret) {
212                 unsigned long val;
213
214                 val = (1 << 8) | (8 << 16);
215                 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
216                                           &val,
217                                           34 * sizeof(u32),
218                                           35 * sizeof(u32));
219         }
220
221         if (!ret)
222                 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
223                                                35 * sizeof(u32), -1);
224
225         return ret;
226 }
227
228 static int fpregs32_set(struct task_struct *target,
229                         const struct user_regset *regset,
230                         unsigned int pos, unsigned int count,
231                         const void *kbuf, const void __user *ubuf)
232 {
233         unsigned long *fpregs = target->thread.float_regs;
234         int ret;
235
236 #if 0
237         if (target == current)
238                 save_and_clear_fpu();
239 #endif
240         ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
241                                  fpregs,
242                                  0, 32 * sizeof(u32));
243         if (!ret)
244                 user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
245                                           32 * sizeof(u32),
246                                           33 * sizeof(u32));
247         if (!ret && count > 0) {
248                 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
249                                          &target->thread.fsr,
250                                          33 * sizeof(u32),
251                                          34 * sizeof(u32));
252         }
253
254         if (!ret)
255                 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
256                                                 34 * sizeof(u32), -1);
257         return ret;
258 }
259
260 static const struct user_regset sparc32_regsets[] = {
261         /* Format is:
262          *      G0 --> G7
263          *      O0 --> O7
264          *      L0 --> L7
265          *      I0 --> I7
266          *      PSR, PC, nPC, Y, WIM, TBR
267          */
268         [REGSET_GENERAL] = {
269                 .core_note_type = NT_PRSTATUS,
270                 .n = 38,
271                 .size = sizeof(u32), .align = sizeof(u32),
272                 .get = genregs32_get, .set = genregs32_set
273         },
274         /* Format is:
275          *      F0 --> F31
276          *      empty 32-bit word
277          *      FSR (32--bit word)
278          *      FPU QUEUE COUNT (8-bit char)
279          *      FPU QUEUE ENTRYSIZE (8-bit char)
280          *      FPU ENABLED (8-bit char)
281          *      empty 8-bit char
282          *      FPU QUEUE (64 32-bit ints)
283          */
284         [REGSET_FP] = {
285                 .core_note_type = NT_PRFPREG,
286                 .n = 99,
287                 .size = sizeof(u32), .align = sizeof(u32),
288                 .get = fpregs32_get, .set = fpregs32_set
289         },
290 };
291
292 static const struct user_regset_view user_sparc32_view = {
293         .name = "sparc", .e_machine = EM_SPARC,
294         .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets)
295 };
296
297 const struct user_regset_view *task_user_regset_view(struct task_struct *task)
298 {
299         return &user_sparc32_view;
300 }
301
302 struct fps {
303         unsigned long regs[32];
304         unsigned long fsr;
305         unsigned long flags;
306         unsigned long extra;
307         unsigned long fpqd;
308         struct fq {
309                 unsigned long *insnaddr;
310                 unsigned long insn;
311         } fpq[16];
312 };
313
314 long arch_ptrace(struct task_struct *child, long request,
315                  unsigned long addr, unsigned long data)
316 {
317         unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4];
318         void __user *addr2p;
319         const struct user_regset_view *view;
320         struct pt_regs __user *pregs;
321         struct fps __user *fps;
322         int ret;
323
324         view = task_user_regset_view(current);
325         addr2p = (void __user *) addr2;
326         pregs = (struct pt_regs __user *) addr;
327         fps = (struct fps __user *) addr;
328
329         switch(request) {
330         case PTRACE_GETREGS: {
331                 ret = copy_regset_to_user(child, view, REGSET_GENERAL,
332                                           32 * sizeof(u32),
333                                           4 * sizeof(u32),
334                                           &pregs->psr);
335                 if (!ret)
336                         copy_regset_to_user(child, view, REGSET_GENERAL,
337                                             1 * sizeof(u32),
338                                             15 * sizeof(u32),
339                                             &pregs->u_regs[0]);
340                 break;
341         }
342
343         case PTRACE_SETREGS: {
344                 ret = copy_regset_from_user(child, view, REGSET_GENERAL,
345                                             32 * sizeof(u32),
346                                             4 * sizeof(u32),
347                                             &pregs->psr);
348                 if (!ret)
349                         copy_regset_from_user(child, view, REGSET_GENERAL,
350                                               1 * sizeof(u32),
351                                               15 * sizeof(u32),
352                                               &pregs->u_regs[0]);
353                 break;
354         }
355
356         case PTRACE_GETFPREGS: {
357                 ret = copy_regset_to_user(child, view, REGSET_FP,
358                                           0 * sizeof(u32),
359                                           32 * sizeof(u32),
360                                           &fps->regs[0]);
361                 if (!ret)
362                         ret = copy_regset_to_user(child, view, REGSET_FP,
363                                                   33 * sizeof(u32),
364                                                   1 * sizeof(u32),
365                                                   &fps->fsr);
366
367                 if (!ret) {
368                         if (__put_user(0, &fps->fpqd) ||
369                             __put_user(0, &fps->flags) ||
370                             __put_user(0, &fps->extra) ||
371                             clear_user(fps->fpq, sizeof(fps->fpq)))
372                                 ret = -EFAULT;
373                 }
374                 break;
375         }
376
377         case PTRACE_SETFPREGS: {
378                 ret = copy_regset_from_user(child, view, REGSET_FP,
379                                             0 * sizeof(u32),
380                                             32 * sizeof(u32),
381                                             &fps->regs[0]);
382                 if (!ret)
383                         ret = copy_regset_from_user(child, view, REGSET_FP,
384                                                     33 * sizeof(u32),
385                                                     1 * sizeof(u32),
386                                                     &fps->fsr);
387                 break;
388         }
389
390         case PTRACE_READTEXT:
391         case PTRACE_READDATA:
392                 ret = ptrace_readdata(child, addr, addr2p, data);
393
394                 if (ret == data)
395                         ret = 0;
396                 else if (ret >= 0)
397                         ret = -EIO;
398                 break;
399
400         case PTRACE_WRITETEXT:
401         case PTRACE_WRITEDATA:
402                 ret = ptrace_writedata(child, addr2p, addr, data);
403
404                 if (ret == data)
405                         ret = 0;
406                 else if (ret >= 0)
407                         ret = -EIO;
408                 break;
409
410         default:
411                 if (request == PTRACE_SPARC_DETACH)
412                         request = PTRACE_DETACH;
413                 ret = ptrace_request(child, request, addr, data);
414                 break;
415         }
416
417         return ret;
418 }
419
420 asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p)
421 {
422         int ret = 0;
423
424         if (test_thread_flag(TIF_SYSCALL_TRACE)) {
425                 if (syscall_exit_p)
426                         tracehook_report_syscall_exit(regs, 0);
427                 else
428                         ret = tracehook_report_syscall_entry(regs);
429         }
430
431         return ret;
432 }