diff options
author | Vineet Gupta <vgupta@synopsys.com> | 2013-01-18 04:42:22 -0500 |
---|---|---|
committer | Vineet Gupta <vgupta@synopsys.com> | 2013-02-15 12:45:59 -0500 |
commit | 547f112571904da03589beb8434185294c77896a (patch) | |
tree | 0cf125fec3110091a208c0243173cfd150e332cd /arch/arc | |
parent | 080c37473eb671a037b3e9a315303851f0675be5 (diff) |
ARC: ptrace support
Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch/arc')
-rw-r--r-- | arch/arc/Kconfig | 1 | ||||
-rw-r--r-- | arch/arc/kernel/Makefile | 3 | ||||
-rw-r--r-- | arch/arc/kernel/entry.S | 68 | ||||
-rw-r--r-- | arch/arc/kernel/ptrace.c | 136 |
4 files changed, 206 insertions, 2 deletions
diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 03347cbde9bd..409b9378032e 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig | |||
@@ -22,6 +22,7 @@ config ARC | |||
22 | select GENERIC_PENDING_IRQ if SMP | 22 | select GENERIC_PENDING_IRQ if SMP |
23 | select GENERIC_SIGALTSTACK | 23 | select GENERIC_SIGALTSTACK |
24 | select GENERIC_SMP_IDLE_THREAD | 24 | select GENERIC_SMP_IDLE_THREAD |
25 | select HAVE_ARCH_TRACEHOOK | ||
25 | select HAVE_GENERIC_HARDIRQS | 26 | select HAVE_GENERIC_HARDIRQS |
26 | select HAVE_MEMBLOCK | 27 | select HAVE_MEMBLOCK |
27 | select IRQ_DOMAIN | 28 | select IRQ_DOMAIN |
diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile index c9ec0662a4ec..f4885891dabb 100644 --- a/arch/arc/kernel/Makefile +++ b/arch/arc/kernel/Makefile | |||
@@ -5,6 +5,9 @@ | |||
5 | # it under the terms of the GNU General Public License version 2 as | 5 | # it under the terms of the GNU General Public License version 2 as |
6 | # published by the Free Software Foundation. | 6 | # published by the Free Software Foundation. |
7 | 7 | ||
8 | # Pass UTS_MACHINE for user_regset definition | ||
9 | CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"' | ||
10 | |||
8 | obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o entry.o process.o | 11 | obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o entry.o process.o |
9 | obj-y += signal.o traps.o sys.o troubleshoot.o stacktrace.o clk.o | 12 | obj-y += signal.o traps.o sys.o troubleshoot.o stacktrace.o clk.o |
10 | obj-y += devtree.o | 13 | obj-y += devtree.o |
diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index 69d0d376e28b..76697aecd165 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S | |||
@@ -7,6 +7,13 @@ | |||
7 | * it under the terms of the GNU General Public License version 2 as | 7 | * it under the terms of the GNU General Public License version 2 as |
8 | * published by the Free Software Foundation. | 8 | * published by the Free Software Foundation. |
9 | * | 9 | * |
10 | * vineetg: Feb 2011 (ptrace low level code fixes) | ||
11 | * -traced syscall return code (r0) was not saved into pt_regs for restoring | ||
12 | * into user reg-file when traded task rets to user space. | ||
13 | * -syscalls needing arch-wrappers (mainly for passing sp as pt_regs) | ||
14 | * were not invoking post-syscall trace hook (jumping directly into | ||
15 | * ret_from_system_call) | ||
16 | * | ||
10 | * vineetg: Nov 2010: | 17 | * vineetg: Nov 2010: |
11 | * -Vector table jumps (@8 bytes) converted into branches (@4 bytes) | 18 | * -Vector table jumps (@8 bytes) converted into branches (@4 bytes) |
12 | * -To maintain the slot size of 8 bytes/vector, added nop, which is | 19 | * -To maintain the slot size of 8 bytes/vector, added nop, which is |
@@ -347,6 +354,50 @@ ARC_ENTRY EV_Extension | |||
347 | b ret_from_exception | 354 | b ret_from_exception |
348 | ARC_EXIT EV_Extension | 355 | ARC_EXIT EV_Extension |
349 | 356 | ||
357 | ;######################### System Call Tracing ######################### | ||
358 | |||
359 | tracesys: | ||
360 | ; save EFA in case tracer wants the PC of traced task | ||
361 | ; using ERET won't work since next-PC has already committed | ||
362 | lr r12, [efa] | ||
363 | GET_CURR_TASK_FIELD_PTR TASK_THREAD, r11 | ||
364 | st r12, [r11, THREAD_FAULT_ADDR] | ||
365 | |||
366 | ; PRE Sys Call Ptrace hook | ||
367 | mov r0, sp ; pt_regs needed | ||
368 | bl @syscall_trace_entry | ||
369 | |||
370 | ; Tracing code now returns the syscall num (orig or modif) | ||
371 | mov r8, r0 | ||
372 | |||
373 | ; Do the Sys Call as we normally would. | ||
374 | ; Validate the Sys Call number | ||
375 | cmp r8, NR_syscalls | ||
376 | mov.hi r0, -ENOSYS | ||
377 | bhi tracesys_exit | ||
378 | |||
379 | ; Restore the sys-call args. Mere invocation of the hook abv could have | ||
380 | ; clobbered them (since they are in scratch regs). The tracer could also | ||
381 | ; have deliberately changed the syscall args: r0-r7 | ||
382 | ld r0, [sp, PT_r0] | ||
383 | ld r1, [sp, PT_r1] | ||
384 | ld r2, [sp, PT_r2] | ||
385 | ld r3, [sp, PT_r3] | ||
386 | ld r4, [sp, PT_r4] | ||
387 | ld r5, [sp, PT_r5] | ||
388 | ld r6, [sp, PT_r6] | ||
389 | ld r7, [sp, PT_r7] | ||
390 | ld.as r9, [sys_call_table, r8] | ||
391 | jl [r9] ; Entry into Sys Call Handler | ||
392 | |||
393 | tracesys_exit: | ||
394 | st r0, [sp, PT_r0] ; sys call return value in pt_regs | ||
395 | |||
396 | ;POST Sys Call Ptrace Hook | ||
397 | bl @syscall_trace_exit | ||
398 | b ret_from_exception ; NOT ret_from_system_call at is saves r0 which | ||
399 | ; we'd done before calling post hook above | ||
400 | |||
350 | ;################### Break Point TRAP ########################## | 401 | ;################### Break Point TRAP ########################## |
351 | 402 | ||
352 | ; ======= (5b) Trap is due to Break-Point ========= | 403 | ; ======= (5b) Trap is due to Break-Point ========= |
@@ -412,6 +463,11 @@ ARC_ENTRY EV_Trap | |||
412 | ; Before doing anything, return from CPU Exception Mode | 463 | ; Before doing anything, return from CPU Exception Mode |
413 | FAKE_RET_FROM_EXCPN r11 | 464 | FAKE_RET_FROM_EXCPN r11 |
414 | 465 | ||
466 | ; If syscall tracing ongoing, invoke pre-pos-hooks | ||
467 | GET_CURR_THR_INFO_FLAGS r10 | ||
468 | btst r10, TIF_SYSCALL_TRACE | ||
469 | bnz tracesys ; this never comes back | ||
470 | |||
415 | ;============ This is normal System Call case ========== | 471 | ;============ This is normal System Call case ========== |
416 | ; Sys-call num shd not exceed the total system calls avail | 472 | ; Sys-call num shd not exceed the total system calls avail |
417 | cmp r8, NR_syscalls | 473 | cmp r8, NR_syscalls |
@@ -608,6 +664,10 @@ ARC_ENTRY sys_fork_wrapper | |||
608 | bl @sys_fork | 664 | bl @sys_fork |
609 | DISCARD_CALLEE_SAVED_USER | 665 | DISCARD_CALLEE_SAVED_USER |
610 | 666 | ||
667 | GET_CURR_THR_INFO_FLAGS r10 | ||
668 | btst r10, TIF_SYSCALL_TRACE | ||
669 | bnz tracesys_exit | ||
670 | |||
611 | b ret_from_system_call | 671 | b ret_from_system_call |
612 | ARC_EXIT sys_fork_wrapper | 672 | ARC_EXIT sys_fork_wrapper |
613 | 673 | ||
@@ -616,6 +676,10 @@ ARC_ENTRY sys_vfork_wrapper | |||
616 | bl @sys_vfork | 676 | bl @sys_vfork |
617 | DISCARD_CALLEE_SAVED_USER | 677 | DISCARD_CALLEE_SAVED_USER |
618 | 678 | ||
679 | GET_CURR_THR_INFO_FLAGS r10 | ||
680 | btst r10, TIF_SYSCALL_TRACE | ||
681 | bnz tracesys_exit | ||
682 | |||
619 | b ret_from_system_call | 683 | b ret_from_system_call |
620 | ARC_EXIT sys_vfork_wrapper | 684 | ARC_EXIT sys_vfork_wrapper |
621 | 685 | ||
@@ -624,5 +688,9 @@ ARC_ENTRY sys_clone_wrapper | |||
624 | bl @sys_clone | 688 | bl @sys_clone |
625 | DISCARD_CALLEE_SAVED_USER | 689 | DISCARD_CALLEE_SAVED_USER |
626 | 690 | ||
691 | GET_CURR_THR_INFO_FLAGS r10 | ||
692 | btst r10, TIF_SYSCALL_TRACE | ||
693 | bnz tracesys_exit | ||
694 | |||
627 | b ret_from_system_call | 695 | b ret_from_system_call |
628 | ARC_EXIT sys_clone_wrapper | 696 | ARC_EXIT sys_clone_wrapper |
diff --git a/arch/arc/kernel/ptrace.c b/arch/arc/kernel/ptrace.c index 1cf944a77f33..c6a81c58d0f3 100644 --- a/arch/arc/kernel/ptrace.c +++ b/arch/arc/kernel/ptrace.c | |||
@@ -7,6 +7,122 @@ | |||
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/ptrace.h> | 9 | #include <linux/ptrace.h> |
10 | #include <linux/tracehook.h> | ||
11 | #include <linux/regset.h> | ||
12 | #include <linux/unistd.h> | ||
13 | #include <linux/elf.h> | ||
14 | |||
15 | static struct callee_regs *task_callee_regs(struct task_struct *tsk) | ||
16 | { | ||
17 | struct callee_regs *tmp = (struct callee_regs *)tsk->thread.callee_reg; | ||
18 | return tmp; | ||
19 | } | ||
20 | |||
21 | static int genregs_get(struct task_struct *target, | ||
22 | const struct user_regset *regset, | ||
23 | unsigned int pos, unsigned int count, | ||
24 | void *kbuf, void __user *ubuf) | ||
25 | { | ||
26 | const struct pt_regs *ptregs = task_pt_regs(target); | ||
27 | const struct callee_regs *cregs = task_callee_regs(target); | ||
28 | int ret = 0; | ||
29 | unsigned int stop_pc_val; | ||
30 | |||
31 | #define REG_O_CHUNK(START, END, PTR) \ | ||
32 | if (!ret) \ | ||
33 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \ | ||
34 | offsetof(struct user_regs_struct, START), \ | ||
35 | offsetof(struct user_regs_struct, END)); | ||
36 | |||
37 | #define REG_O_ONE(LOC, PTR) \ | ||
38 | if (!ret) \ | ||
39 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \ | ||
40 | offsetof(struct user_regs_struct, LOC), \ | ||
41 | offsetof(struct user_regs_struct, LOC) + 4); | ||
42 | |||
43 | REG_O_CHUNK(scratch, callee, ptregs); | ||
44 | REG_O_CHUNK(callee, efa, cregs); | ||
45 | REG_O_CHUNK(efa, stop_pc, &target->thread.fault_address); | ||
46 | |||
47 | if (!ret) { | ||
48 | if (in_brkpt_trap(ptregs)) { | ||
49 | stop_pc_val = target->thread.fault_address; | ||
50 | pr_debug("\t\tstop_pc (brk-pt)\n"); | ||
51 | } else { | ||
52 | stop_pc_val = ptregs->ret; | ||
53 | pr_debug("\t\tstop_pc (others)\n"); | ||
54 | } | ||
55 | |||
56 | REG_O_ONE(stop_pc, &stop_pc_val); | ||
57 | } | ||
58 | |||
59 | return ret; | ||
60 | } | ||
61 | |||
62 | static int genregs_set(struct task_struct *target, | ||
63 | const struct user_regset *regset, | ||
64 | unsigned int pos, unsigned int count, | ||
65 | const void *kbuf, const void __user *ubuf) | ||
66 | { | ||
67 | const struct pt_regs *ptregs = task_pt_regs(target); | ||
68 | const struct callee_regs *cregs = task_callee_regs(target); | ||
69 | int ret = 0; | ||
70 | |||
71 | #define REG_IN_CHUNK(FIRST, NEXT, PTR) \ | ||
72 | if (!ret) \ | ||
73 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \ | ||
74 | (void *)(PTR), \ | ||
75 | offsetof(struct user_regs_struct, FIRST), \ | ||
76 | offsetof(struct user_regs_struct, NEXT)); | ||
77 | |||
78 | #define REG_IN_ONE(LOC, PTR) \ | ||
79 | if (!ret) \ | ||
80 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \ | ||
81 | (void *)(PTR), \ | ||
82 | offsetof(struct user_regs_struct, LOC), \ | ||
83 | offsetof(struct user_regs_struct, LOC) + 4); | ||
84 | |||
85 | #define REG_IGNORE_ONE(LOC) \ | ||
86 | if (!ret) \ | ||
87 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, \ | ||
88 | offsetof(struct user_regs_struct, LOC), \ | ||
89 | offsetof(struct user_regs_struct, LOC) + 4); | ||
90 | |||
91 | /* TBD: disallow updates to STATUS32, orig_r8 etc*/ | ||
92 | REG_IN_CHUNK(scratch, callee, ptregs); /* pt_regs[bta..orig_r8] */ | ||
93 | REG_IN_CHUNK(callee, efa, cregs); /* callee_regs[r25..r13] */ | ||
94 | REG_IGNORE_ONE(efa); /* efa update invalid */ | ||
95 | REG_IN_ONE(stop_pc, &ptregs->ret); /* stop_pc: PC update */ | ||
96 | |||
97 | return ret; | ||
98 | } | ||
99 | |||
100 | enum arc_getset { | ||
101 | REGSET_GENERAL, | ||
102 | }; | ||
103 | |||
104 | static const struct user_regset arc_regsets[] = { | ||
105 | [REGSET_GENERAL] = { | ||
106 | .core_note_type = NT_PRSTATUS, | ||
107 | .n = ELF_NGREG, | ||
108 | .size = sizeof(unsigned long), | ||
109 | .align = sizeof(unsigned long), | ||
110 | .get = genregs_get, | ||
111 | .set = genregs_set, | ||
112 | } | ||
113 | }; | ||
114 | |||
115 | static const struct user_regset_view user_arc_view = { | ||
116 | .name = UTS_MACHINE, | ||
117 | .e_machine = EM_ARCOMPACT, | ||
118 | .regsets = arc_regsets, | ||
119 | .n = ARRAY_SIZE(arc_regsets) | ||
120 | }; | ||
121 | |||
122 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | ||
123 | { | ||
124 | return &user_arc_view; | ||
125 | } | ||
10 | 126 | ||
11 | void ptrace_disable(struct task_struct *child) | 127 | void ptrace_disable(struct task_struct *child) |
12 | { | 128 | { |
@@ -16,11 +132,27 @@ long arch_ptrace(struct task_struct *child, long request, | |||
16 | unsigned long addr, unsigned long data) | 132 | unsigned long addr, unsigned long data) |
17 | { | 133 | { |
18 | int ret = -EIO; | 134 | int ret = -EIO; |
135 | |||
136 | pr_debug("REQ=%ld: ADDR =0x%lx, DATA=0x%lx)\n", request, addr, data); | ||
137 | |||
138 | switch (request) { | ||
139 | default: | ||
140 | ret = ptrace_request(child, request, addr, data); | ||
141 | break; | ||
142 | } | ||
143 | |||
19 | return ret; | 144 | return ret; |
20 | } | 145 | } |
21 | 146 | ||
147 | asmlinkage int syscall_trace_entry(struct pt_regs *regs) | ||
148 | { | ||
149 | if (tracehook_report_syscall_entry(regs)) | ||
150 | return ULONG_MAX; | ||
22 | 151 | ||
23 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | 152 | return regs->r8; |
153 | } | ||
154 | |||
155 | asmlinkage void syscall_trace_exit(struct pt_regs *regs) | ||
24 | { | 156 | { |
25 | return (const struct user_regset_view *)NULL; | 157 | tracehook_report_syscall_exit(regs, 0); |
26 | } | 158 | } |