sparc32: fix register window handling in genregs32_[gs]et()
[linux-block.git] / arch / sparc / kernel / ptrace_32.c
CommitLineData
b2441318 1// SPDX-License-Identifier: GPL-2.0
1da177e4
LT
2/* ptrace.c: Sparc process tracing support.
3 *
8e3fe806 4 * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net)
1da177e4
LT
5 *
6 * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
7 * and David Mosberger.
8 *
5b2afff2 9 * Added Linux support -miguel (weird, eh?, the original code was meant
1da177e4
LT
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>
1da177e4 20#include <linux/security.h>
7ed20e1a 21#include <linux/signal.h>
8e3fe806
DM
22#include <linux/regset.h>
23#include <linux/elf.h>
1c133b4b 24#include <linux/tracehook.h>
1da177e4
LT
25
26#include <asm/pgtable.h>
7c0f6ba6 27#include <linux/uaccess.h>
d550bbd4 28#include <asm/cacheflush.h>
1da177e4 29
c8c8782d
SR
30#include "kernel.h"
31
1da177e4 32/* #define ALLOW_INIT_TRACING */
1da177e4
LT
33
34/*
35 * Called by kernel/ptrace.c when detaching..
36 *
37 * Make sure single step bits etc are not set.
38 */
39void ptrace_disable(struct task_struct *child)
40{
41 /* nothing to do */
42}
43
8e3fe806
DM
44enum sparc_regset {
45 REGSET_GENERAL,
46 REGSET_FP,
47};
48
cf51e129
AV
49static 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
67static 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
8e3fe806
DM
85static 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;
cf51e129
AV
91 u32 uregs[16];
92 int ret;
8e3fe806
DM
93
94 if (target == current)
95 flush_user_windows();
96
cf51e129
AV
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;
8e3fe806 102
cf51e129
AV
103 if (pos < 32 * sizeof(u32)) {
104 if (regwindow32_get(target, regs, uregs))
8e3fe806 105 return -EFAULT;
cf51e129
AV
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;
8e3fe806 111 }
8e3fe806 112
cf51e129
AV
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));
8e3fe806
DM
122}
123
124static 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;
cf51e129
AV
130 u32 uregs[16];
131 u32 psr;
132 int ret;
8e3fe806
DM
133
134 if (target == current)
135 flush_user_windows();
136
cf51e129
AV
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;
8e3fe806 142
cf51e129
AV
143 if (pos < 32 * sizeof(u32)) {
144 if (regwindow32_get(target, regs, uregs))
8e3fe806 145 return -EFAULT;
cf51e129
AV
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;
8e3fe806 155 }
cf51e129
AV
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->y,
172 34 * sizeof(u32), 35 * sizeof(u32));
173 if (ret || !count)
174 return ret;
8e3fe806 175 return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
cf51e129 176 35 * sizeof(u32), 38 * sizeof(u32));
8e3fe806
DM
177}
178
179static int fpregs32_get(struct task_struct *target,
180 const struct user_regset *regset,
181 unsigned int pos, unsigned int count,
182 void *kbuf, void __user *ubuf)
183{
184 const unsigned long *fpregs = target->thread.float_regs;
185 int ret = 0;
186
187#if 0
188 if (target == current)
189 save_and_clear_fpu();
190#endif
191
192 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
193 fpregs,
194 0, 32 * sizeof(u32));
195
196 if (!ret)
197 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
198 32 * sizeof(u32),
199 33 * sizeof(u32));
200 if (!ret)
201 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
202 &target->thread.fsr,
203 33 * sizeof(u32),
204 34 * sizeof(u32));
205
206 if (!ret) {
207 unsigned long val;
208
209 val = (1 << 8) | (8 << 16);
210 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
211 &val,
212 34 * sizeof(u32),
213 35 * sizeof(u32));
214 }
215
216 if (!ret)
217 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
218 35 * sizeof(u32), -1);
219
220 return ret;
221}
222
223static int fpregs32_set(struct task_struct *target,
224 const struct user_regset *regset,
225 unsigned int pos, unsigned int count,
226 const void *kbuf, const void __user *ubuf)
227{
228 unsigned long *fpregs = target->thread.float_regs;
229 int ret;
230
231#if 0
232 if (target == current)
233 save_and_clear_fpu();
234#endif
235 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
236 fpregs,
237 0, 32 * sizeof(u32));
238 if (!ret)
239 user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
240 32 * sizeof(u32),
241 33 * sizeof(u32));
242 if (!ret && count > 0) {
243 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
244 &target->thread.fsr,
245 33 * sizeof(u32),
246 34 * sizeof(u32));
247 }
248
249 if (!ret)
250 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
251 34 * sizeof(u32), -1);
252 return ret;
253}
254
255static const struct user_regset sparc32_regsets[] = {
256 /* Format is:
257 * G0 --> G7
258 * O0 --> O7
259 * L0 --> L7
260 * I0 --> I7
261 * PSR, PC, nPC, Y, WIM, TBR
262 */
263 [REGSET_GENERAL] = {
264 .core_note_type = NT_PRSTATUS,
7d4ee289 265 .n = 38,
8e3fe806
DM
266 .size = sizeof(u32), .align = sizeof(u32),
267 .get = genregs32_get, .set = genregs32_set
268 },
269 /* Format is:
270 * F0 --> F31
271 * empty 32-bit word
272 * FSR (32--bit word)
273 * FPU QUEUE COUNT (8-bit char)
274 * FPU QUEUE ENTRYSIZE (8-bit char)
275 * FPU ENABLED (8-bit char)
276 * empty 8-bit char
277 * FPU QUEUE (64 32-bit ints)
278 */
279 [REGSET_FP] = {
280 .core_note_type = NT_PRFPREG,
7d4ee289 281 .n = 99,
8e3fe806
DM
282 .size = sizeof(u32), .align = sizeof(u32),
283 .get = fpregs32_get, .set = fpregs32_set
284 },
285};
286
287static const struct user_regset_view user_sparc32_view = {
288 .name = "sparc", .e_machine = EM_SPARC,
289 .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets)
290};
291
292const struct user_regset_view *task_user_regset_view(struct task_struct *task)
293{
294 return &user_sparc32_view;
295}
296
a9384e23
NK
297struct fps {
298 unsigned long regs[32];
299 unsigned long fsr;
300 unsigned long flags;
301 unsigned long extra;
302 unsigned long fpqd;
303 struct fq {
304 unsigned long *insnaddr;
305 unsigned long insn;
306 } fpq[16];
307};
308
9b05a69e
NK
309long arch_ptrace(struct task_struct *child, long request,
310 unsigned long addr, unsigned long data)
1da177e4 311{
9775369e 312 unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4];
a9384e23 313 void __user *addr2p;
d256eb8d 314 const struct user_regset_view *view;
a9384e23
NK
315 struct pt_regs __user *pregs;
316 struct fps __user *fps;
d256eb8d
DM
317 int ret;
318
d786a4a6 319 view = task_user_regset_view(current);
a9384e23
NK
320 addr2p = (void __user *) addr2;
321 pregs = (struct pt_regs __user *) addr;
322 fps = (struct fps __user *) addr;
1da177e4
LT
323
324 switch(request) {
1da177e4 325 case PTRACE_GETREGS: {
d256eb8d
DM
326 ret = copy_regset_to_user(child, view, REGSET_GENERAL,
327 32 * sizeof(u32),
328 4 * sizeof(u32),
329 &pregs->psr);
330 if (!ret)
331 copy_regset_to_user(child, view, REGSET_GENERAL,
332 1 * sizeof(u32),
333 15 * sizeof(u32),
334 &pregs->u_regs[0]);
9775369e 335 break;
1da177e4
LT
336 }
337
338 case PTRACE_SETREGS: {
d256eb8d
DM
339 ret = copy_regset_from_user(child, view, REGSET_GENERAL,
340 32 * sizeof(u32),
341 4 * sizeof(u32),
342 &pregs->psr);
343 if (!ret)
344 copy_regset_from_user(child, view, REGSET_GENERAL,
345 1 * sizeof(u32),
346 15 * sizeof(u32),
347 &pregs->u_regs[0]);
9775369e 348 break;
1da177e4
LT
349 }
350
351 case PTRACE_GETFPREGS: {
d256eb8d
DM
352 ret = copy_regset_to_user(child, view, REGSET_FP,
353 0 * sizeof(u32),
354 32 * sizeof(u32),
355 &fps->regs[0]);
356 if (!ret)
357 ret = copy_regset_to_user(child, view, REGSET_FP,
358 33 * sizeof(u32),
359 1 * sizeof(u32),
360 &fps->fsr);
361
362 if (!ret) {
363 if (__put_user(0, &fps->fpqd) ||
364 __put_user(0, &fps->flags) ||
365 __put_user(0, &fps->extra) ||
366 clear_user(fps->fpq, sizeof(fps->fpq)))
367 ret = -EFAULT;
1da177e4 368 }
9775369e 369 break;
1da177e4
LT
370 }
371
372 case PTRACE_SETFPREGS: {
d256eb8d
DM
373 ret = copy_regset_from_user(child, view, REGSET_FP,
374 0 * sizeof(u32),
375 32 * sizeof(u32),
376 &fps->regs[0]);
377 if (!ret)
378 ret = copy_regset_from_user(child, view, REGSET_FP,
379 33 * sizeof(u32),
380 1 * sizeof(u32),
381 &fps->fsr);
9775369e 382 break;
1da177e4
LT
383 }
384
385 case PTRACE_READTEXT:
9775369e 386 case PTRACE_READDATA:
a9384e23 387 ret = ptrace_readdata(child, addr, addr2p, data);
9775369e
DM
388
389 if (ret == data)
390 ret = 0;
391 else if (ret >= 0)
392 ret = -EIO;
393 break;
1da177e4
LT
394
395 case PTRACE_WRITETEXT:
9775369e 396 case PTRACE_WRITEDATA:
a9384e23 397 ret = ptrace_writedata(child, addr2p, addr, data);
9775369e
DM
398
399 if (ret == data)
400 ret = 0;
401 else if (ret >= 0)
402 ret = -EIO;
403 break;
1da177e4 404
9775369e 405 default:
986bef85
DM
406 if (request == PTRACE_SPARC_DETACH)
407 request = PTRACE_DETACH;
9775369e
DM
408 ret = ptrace_request(child, request, addr, data);
409 break;
1da177e4
LT
410 }
411
9775369e 412 return ret;
1da177e4
LT
413}
414
1c133b4b 415asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p)
1da177e4 416{
1c133b4b
DM
417 int ret = 0;
418
419 if (test_thread_flag(TIF_SYSCALL_TRACE)) {
420 if (syscall_exit_p)
421 tracehook_report_syscall_exit(regs, 0);
422 else
423 ret = tracehook_report_syscall_entry(regs);
1da177e4 424 }
1c133b4b
DM
425
426 return ret;
1da177e4 427}