Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/sh/kernel/ptrace_32.c
10819 views
1
/*
2
* SuperH process tracing
3
*
4
* Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka
5
* Copyright (C) 2002 - 2009 Paul Mundt
6
*
7
* Audit support by Yuichi Nakamura <[email protected]>
8
*
9
* This file is subject to the terms and conditions of the GNU General Public
10
* License. See the file "COPYING" in the main directory of this archive
11
* for more details.
12
*/
13
#include <linux/kernel.h>
14
#include <linux/sched.h>
15
#include <linux/mm.h>
16
#include <linux/smp.h>
17
#include <linux/errno.h>
18
#include <linux/ptrace.h>
19
#include <linux/user.h>
20
#include <linux/security.h>
21
#include <linux/signal.h>
22
#include <linux/io.h>
23
#include <linux/audit.h>
24
#include <linux/seccomp.h>
25
#include <linux/tracehook.h>
26
#include <linux/elf.h>
27
#include <linux/regset.h>
28
#include <linux/hw_breakpoint.h>
29
#include <asm/uaccess.h>
30
#include <asm/pgtable.h>
31
#include <asm/system.h>
32
#include <asm/processor.h>
33
#include <asm/mmu_context.h>
34
#include <asm/syscalls.h>
35
#include <asm/fpu.h>
36
37
#define CREATE_TRACE_POINTS
38
#include <trace/events/syscalls.h>
39
40
/*
41
* This routine will get a word off of the process kernel stack.
42
*/
43
static inline int get_stack_long(struct task_struct *task, int offset)
44
{
45
unsigned char *stack;
46
47
stack = (unsigned char *)task_pt_regs(task);
48
stack += offset;
49
return (*((int *)stack));
50
}
51
52
/*
53
* This routine will put a word on the process kernel stack.
54
*/
55
static inline int put_stack_long(struct task_struct *task, int offset,
56
unsigned long data)
57
{
58
unsigned char *stack;
59
60
stack = (unsigned char *)task_pt_regs(task);
61
stack += offset;
62
*(unsigned long *) stack = data;
63
return 0;
64
}
65
66
void ptrace_triggered(struct perf_event *bp, int nmi,
67
struct perf_sample_data *data, struct pt_regs *regs)
68
{
69
struct perf_event_attr attr;
70
71
/*
72
* Disable the breakpoint request here since ptrace has defined a
73
* one-shot behaviour for breakpoint exceptions.
74
*/
75
attr = bp->attr;
76
attr.disabled = true;
77
modify_user_hw_breakpoint(bp, &attr);
78
}
79
80
static int set_single_step(struct task_struct *tsk, unsigned long addr)
81
{
82
struct thread_struct *thread = &tsk->thread;
83
struct perf_event *bp;
84
struct perf_event_attr attr;
85
86
bp = thread->ptrace_bps[0];
87
if (!bp) {
88
ptrace_breakpoint_init(&attr);
89
90
attr.bp_addr = addr;
91
attr.bp_len = HW_BREAKPOINT_LEN_2;
92
attr.bp_type = HW_BREAKPOINT_R;
93
94
bp = register_user_hw_breakpoint(&attr, ptrace_triggered, tsk);
95
if (IS_ERR(bp))
96
return PTR_ERR(bp);
97
98
thread->ptrace_bps[0] = bp;
99
} else {
100
int err;
101
102
attr = bp->attr;
103
attr.bp_addr = addr;
104
/* reenable breakpoint */
105
attr.disabled = false;
106
err = modify_user_hw_breakpoint(bp, &attr);
107
if (unlikely(err))
108
return err;
109
}
110
111
return 0;
112
}
113
114
void user_enable_single_step(struct task_struct *child)
115
{
116
unsigned long pc = get_stack_long(child, offsetof(struct pt_regs, pc));
117
118
set_tsk_thread_flag(child, TIF_SINGLESTEP);
119
120
if (ptrace_get_breakpoints(child) < 0)
121
return;
122
123
set_single_step(child, pc);
124
ptrace_put_breakpoints(child);
125
}
126
127
void user_disable_single_step(struct task_struct *child)
128
{
129
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
130
}
131
132
/*
133
* Called by kernel/ptrace.c when detaching..
134
*
135
* Make sure single step bits etc are not set.
136
*/
137
void ptrace_disable(struct task_struct *child)
138
{
139
user_disable_single_step(child);
140
}
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
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
151
regs->regs,
152
0, 16 * sizeof(unsigned long));
153
if (!ret)
154
/* PC, PR, SR, GBR, MACH, MACL, TRA */
155
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
156
&regs->pc,
157
offsetof(struct pt_regs, pc),
158
sizeof(struct pt_regs));
159
if (!ret)
160
ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
161
sizeof(struct pt_regs), -1);
162
163
return ret;
164
}
165
166
static int genregs_set(struct task_struct *target,
167
const struct user_regset *regset,
168
unsigned int pos, unsigned int count,
169
const void *kbuf, const void __user *ubuf)
170
{
171
struct pt_regs *regs = task_pt_regs(target);
172
int ret;
173
174
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
175
regs->regs,
176
0, 16 * sizeof(unsigned long));
177
if (!ret && count > 0)
178
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
179
&regs->pc,
180
offsetof(struct pt_regs, pc),
181
sizeof(struct pt_regs));
182
if (!ret)
183
ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
184
sizeof(struct pt_regs), -1);
185
186
return ret;
187
}
188
189
#ifdef CONFIG_SH_FPU
190
int fpregs_get(struct task_struct *target,
191
const struct user_regset *regset,
192
unsigned int pos, unsigned int count,
193
void *kbuf, void __user *ubuf)
194
{
195
int ret;
196
197
ret = init_fpu(target);
198
if (ret)
199
return ret;
200
201
if ((boot_cpu_data.flags & CPU_HAS_FPU))
202
return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
203
&target->thread.xstate->hardfpu, 0, -1);
204
205
return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
206
&target->thread.xstate->softfpu, 0, -1);
207
}
208
209
static int fpregs_set(struct task_struct *target,
210
const struct user_regset *regset,
211
unsigned int pos, unsigned int count,
212
const void *kbuf, const void __user *ubuf)
213
{
214
int ret;
215
216
ret = init_fpu(target);
217
if (ret)
218
return ret;
219
220
set_stopped_child_used_math(target);
221
222
if ((boot_cpu_data.flags & CPU_HAS_FPU))
223
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
224
&target->thread.xstate->hardfpu, 0, -1);
225
226
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
227
&target->thread.xstate->softfpu, 0, -1);
228
}
229
230
static int fpregs_active(struct task_struct *target,
231
const struct user_regset *regset)
232
{
233
return tsk_used_math(target) ? regset->n : 0;
234
}
235
#endif
236
237
#ifdef CONFIG_SH_DSP
238
static int dspregs_get(struct task_struct *target,
239
const struct user_regset *regset,
240
unsigned int pos, unsigned int count,
241
void *kbuf, void __user *ubuf)
242
{
243
const struct pt_dspregs *regs =
244
(struct pt_dspregs *)&target->thread.dsp_status.dsp_regs;
245
int ret;
246
247
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, regs,
248
0, sizeof(struct pt_dspregs));
249
if (!ret)
250
ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
251
sizeof(struct pt_dspregs), -1);
252
253
return ret;
254
}
255
256
static int dspregs_set(struct task_struct *target,
257
const struct user_regset *regset,
258
unsigned int pos, unsigned int count,
259
const void *kbuf, const void __user *ubuf)
260
{
261
struct pt_dspregs *regs =
262
(struct pt_dspregs *)&target->thread.dsp_status.dsp_regs;
263
int ret;
264
265
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, regs,
266
0, sizeof(struct pt_dspregs));
267
if (!ret)
268
ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
269
sizeof(struct pt_dspregs), -1);
270
271
return ret;
272
}
273
274
static int dspregs_active(struct task_struct *target,
275
const struct user_regset *regset)
276
{
277
struct pt_regs *regs = task_pt_regs(target);
278
279
return regs->sr & SR_DSP ? regset->n : 0;
280
}
281
#endif
282
283
const struct pt_regs_offset regoffset_table[] = {
284
REGS_OFFSET_NAME(0),
285
REGS_OFFSET_NAME(1),
286
REGS_OFFSET_NAME(2),
287
REGS_OFFSET_NAME(3),
288
REGS_OFFSET_NAME(4),
289
REGS_OFFSET_NAME(5),
290
REGS_OFFSET_NAME(6),
291
REGS_OFFSET_NAME(7),
292
REGS_OFFSET_NAME(8),
293
REGS_OFFSET_NAME(9),
294
REGS_OFFSET_NAME(10),
295
REGS_OFFSET_NAME(11),
296
REGS_OFFSET_NAME(12),
297
REGS_OFFSET_NAME(13),
298
REGS_OFFSET_NAME(14),
299
REGS_OFFSET_NAME(15),
300
REG_OFFSET_NAME(pc),
301
REG_OFFSET_NAME(pr),
302
REG_OFFSET_NAME(sr),
303
REG_OFFSET_NAME(gbr),
304
REG_OFFSET_NAME(mach),
305
REG_OFFSET_NAME(macl),
306
REG_OFFSET_NAME(tra),
307
REG_OFFSET_END,
308
};
309
310
/*
311
* These are our native regset flavours.
312
*/
313
enum sh_regset {
314
REGSET_GENERAL,
315
#ifdef CONFIG_SH_FPU
316
REGSET_FPU,
317
#endif
318
#ifdef CONFIG_SH_DSP
319
REGSET_DSP,
320
#endif
321
};
322
323
static const struct user_regset sh_regsets[] = {
324
/*
325
* Format is:
326
* R0 --> R15
327
* PC, PR, SR, GBR, MACH, MACL, TRA
328
*/
329
[REGSET_GENERAL] = {
330
.core_note_type = NT_PRSTATUS,
331
.n = ELF_NGREG,
332
.size = sizeof(long),
333
.align = sizeof(long),
334
.get = genregs_get,
335
.set = genregs_set,
336
},
337
338
#ifdef CONFIG_SH_FPU
339
[REGSET_FPU] = {
340
.core_note_type = NT_PRFPREG,
341
.n = sizeof(struct user_fpu_struct) / sizeof(long),
342
.size = sizeof(long),
343
.align = sizeof(long),
344
.get = fpregs_get,
345
.set = fpregs_set,
346
.active = fpregs_active,
347
},
348
#endif
349
350
#ifdef CONFIG_SH_DSP
351
[REGSET_DSP] = {
352
.n = sizeof(struct pt_dspregs) / sizeof(long),
353
.size = sizeof(long),
354
.align = sizeof(long),
355
.get = dspregs_get,
356
.set = dspregs_set,
357
.active = dspregs_active,
358
},
359
#endif
360
};
361
362
static const struct user_regset_view user_sh_native_view = {
363
.name = "sh",
364
.e_machine = EM_SH,
365
.regsets = sh_regsets,
366
.n = ARRAY_SIZE(sh_regsets),
367
};
368
369
const struct user_regset_view *task_user_regset_view(struct task_struct *task)
370
{
371
return &user_sh_native_view;
372
}
373
374
long arch_ptrace(struct task_struct *child, long request,
375
unsigned long addr, unsigned long data)
376
{
377
unsigned long __user *datap = (unsigned long __user *)data;
378
int ret;
379
380
switch (request) {
381
/* read the word at location addr in the USER area. */
382
case PTRACE_PEEKUSR: {
383
unsigned long tmp;
384
385
ret = -EIO;
386
if ((addr & 3) || addr < 0 ||
387
addr > sizeof(struct user) - 3)
388
break;
389
390
if (addr < sizeof(struct pt_regs))
391
tmp = get_stack_long(child, addr);
392
else if (addr >= offsetof(struct user, fpu) &&
393
addr < offsetof(struct user, u_fpvalid)) {
394
if (!tsk_used_math(child)) {
395
if (addr == offsetof(struct user, fpu.fpscr))
396
tmp = FPSCR_INIT;
397
else
398
tmp = 0;
399
} else {
400
unsigned long index;
401
ret = init_fpu(child);
402
if (ret)
403
break;
404
index = addr - offsetof(struct user, fpu);
405
tmp = ((unsigned long *)child->thread.xstate)
406
[index >> 2];
407
}
408
} else if (addr == offsetof(struct user, u_fpvalid))
409
tmp = !!tsk_used_math(child);
410
else if (addr == PT_TEXT_ADDR)
411
tmp = child->mm->start_code;
412
else if (addr == PT_DATA_ADDR)
413
tmp = child->mm->start_data;
414
else if (addr == PT_TEXT_END_ADDR)
415
tmp = child->mm->end_code;
416
else if (addr == PT_TEXT_LEN)
417
tmp = child->mm->end_code - child->mm->start_code;
418
else
419
tmp = 0;
420
ret = put_user(tmp, datap);
421
break;
422
}
423
424
case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
425
ret = -EIO;
426
if ((addr & 3) || addr < 0 ||
427
addr > sizeof(struct user) - 3)
428
break;
429
430
if (addr < sizeof(struct pt_regs))
431
ret = put_stack_long(child, addr, data);
432
else if (addr >= offsetof(struct user, fpu) &&
433
addr < offsetof(struct user, u_fpvalid)) {
434
unsigned long index;
435
ret = init_fpu(child);
436
if (ret)
437
break;
438
index = addr - offsetof(struct user, fpu);
439
set_stopped_child_used_math(child);
440
((unsigned long *)child->thread.xstate)
441
[index >> 2] = data;
442
ret = 0;
443
} else if (addr == offsetof(struct user, u_fpvalid)) {
444
conditional_stopped_child_used_math(data, child);
445
ret = 0;
446
}
447
break;
448
449
case PTRACE_GETREGS:
450
return copy_regset_to_user(child, &user_sh_native_view,
451
REGSET_GENERAL,
452
0, sizeof(struct pt_regs),
453
datap);
454
case PTRACE_SETREGS:
455
return copy_regset_from_user(child, &user_sh_native_view,
456
REGSET_GENERAL,
457
0, sizeof(struct pt_regs),
458
datap);
459
#ifdef CONFIG_SH_FPU
460
case PTRACE_GETFPREGS:
461
return copy_regset_to_user(child, &user_sh_native_view,
462
REGSET_FPU,
463
0, sizeof(struct user_fpu_struct),
464
datap);
465
case PTRACE_SETFPREGS:
466
return copy_regset_from_user(child, &user_sh_native_view,
467
REGSET_FPU,
468
0, sizeof(struct user_fpu_struct),
469
datap);
470
#endif
471
#ifdef CONFIG_SH_DSP
472
case PTRACE_GETDSPREGS:
473
return copy_regset_to_user(child, &user_sh_native_view,
474
REGSET_DSP,
475
0, sizeof(struct pt_dspregs),
476
datap);
477
case PTRACE_SETDSPREGS:
478
return copy_regset_from_user(child, &user_sh_native_view,
479
REGSET_DSP,
480
0, sizeof(struct pt_dspregs),
481
datap);
482
#endif
483
default:
484
ret = ptrace_request(child, request, addr, data);
485
break;
486
}
487
488
return ret;
489
}
490
491
static inline int audit_arch(void)
492
{
493
int arch = EM_SH;
494
495
#ifdef CONFIG_CPU_LITTLE_ENDIAN
496
arch |= __AUDIT_ARCH_LE;
497
#endif
498
499
return arch;
500
}
501
502
asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
503
{
504
long ret = 0;
505
506
secure_computing(regs->regs[0]);
507
508
if (test_thread_flag(TIF_SYSCALL_TRACE) &&
509
tracehook_report_syscall_entry(regs))
510
/*
511
* Tracing decided this syscall should not happen.
512
* We'll return a bogus call number to get an ENOSYS
513
* error, but leave the original number in regs->regs[0].
514
*/
515
ret = -1L;
516
517
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
518
trace_sys_enter(regs, regs->regs[0]);
519
520
if (unlikely(current->audit_context))
521
audit_syscall_entry(audit_arch(), regs->regs[3],
522
regs->regs[4], regs->regs[5],
523
regs->regs[6], regs->regs[7]);
524
525
return ret ?: regs->regs[0];
526
}
527
528
asmlinkage void do_syscall_trace_leave(struct pt_regs *regs)
529
{
530
int step;
531
532
if (unlikely(current->audit_context))
533
audit_syscall_exit(AUDITSC_RESULT(regs->regs[0]),
534
regs->regs[0]);
535
536
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
537
trace_sys_exit(regs, regs->regs[0]);
538
539
step = test_thread_flag(TIF_SINGLESTEP);
540
if (step || test_thread_flag(TIF_SYSCALL_TRACE))
541
tracehook_report_syscall_exit(regs, step);
542
}
543
544