diff options
author | David Howells <dhowells@redhat.com> | 2009-06-11 08:05:24 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-11 12:01:26 -0400 |
commit | 4a3b98932270f5d69f2c081924e356325ed704d9 (patch) | |
tree | e2a8e37a37ed7e3be32c12ea233ea18c389549d0 /arch | |
parent | 24ceb7e8a6c5dd6e32ac3d43a2424542c97989f5 (diff) |
FRV: Implement new-style ptrace
Implement the new-style ptrace for FRV, including adding appropriate
tracehooks.
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/frv/Kconfig | 1 | ||||
-rw-r--r-- | arch/frv/include/asm/elf.h | 1 | ||||
-rw-r--r-- | arch/frv/include/asm/ptrace.h | 11 | ||||
-rw-r--r-- | arch/frv/include/asm/syscall.h | 123 | ||||
-rw-r--r-- | arch/frv/kernel/entry.S | 12 | ||||
-rw-r--r-- | arch/frv/kernel/ptrace.c | 378 | ||||
-rw-r--r-- | arch/frv/kernel/signal.c | 5 |
7 files changed, 367 insertions, 164 deletions
diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig index 9d1552a9ee2c..8a5bd7a9c6f5 100644 --- a/arch/frv/Kconfig +++ b/arch/frv/Kconfig | |||
@@ -6,6 +6,7 @@ config FRV | |||
6 | bool | 6 | bool |
7 | default y | 7 | default y |
8 | select HAVE_IDE | 8 | select HAVE_IDE |
9 | select HAVE_ARCH_TRACEHOOK | ||
9 | 10 | ||
10 | config ZONE_DMA | 11 | config ZONE_DMA |
11 | bool | 12 | bool |
diff --git a/arch/frv/include/asm/elf.h b/arch/frv/include/asm/elf.h index 7279ec07d62e..7bbf6e47f8c8 100644 --- a/arch/frv/include/asm/elf.h +++ b/arch/frv/include/asm/elf.h | |||
@@ -116,6 +116,7 @@ do { \ | |||
116 | } while(0) | 116 | } while(0) |
117 | 117 | ||
118 | #define USE_ELF_CORE_DUMP | 118 | #define USE_ELF_CORE_DUMP |
119 | #define CORE_DUMP_USE_REGSET | ||
119 | #define ELF_FDPIC_CORE_EFLAGS EF_FRV_FDPIC | 120 | #define ELF_FDPIC_CORE_EFLAGS EF_FRV_FDPIC |
120 | #define ELF_EXEC_PAGESIZE 16384 | 121 | #define ELF_EXEC_PAGESIZE 16384 |
121 | 122 | ||
diff --git a/arch/frv/include/asm/ptrace.h b/arch/frv/include/asm/ptrace.h index cf6934012b64..a54b535c9e49 100644 --- a/arch/frv/include/asm/ptrace.h +++ b/arch/frv/include/asm/ptrace.h | |||
@@ -65,6 +65,8 @@ | |||
65 | #ifdef __KERNEL__ | 65 | #ifdef __KERNEL__ |
66 | #ifndef __ASSEMBLY__ | 66 | #ifndef __ASSEMBLY__ |
67 | 67 | ||
68 | struct task_struct; | ||
69 | |||
68 | /* | 70 | /* |
69 | * we dedicate GR28 to keeping a pointer to the current exception frame | 71 | * we dedicate GR28 to keeping a pointer to the current exception frame |
70 | * - gr28 is destroyed on entry to the kernel from userspace | 72 | * - gr28 is destroyed on entry to the kernel from userspace |
@@ -73,11 +75,18 @@ register struct pt_regs *__frame asm("gr28"); | |||
73 | 75 | ||
74 | #define user_mode(regs) (!((regs)->psr & PSR_S)) | 76 | #define user_mode(regs) (!((regs)->psr & PSR_S)) |
75 | #define instruction_pointer(regs) ((regs)->pc) | 77 | #define instruction_pointer(regs) ((regs)->pc) |
78 | #define user_stack_pointer(regs) ((regs)->sp) | ||
76 | 79 | ||
77 | extern unsigned long user_stack(const struct pt_regs *); | 80 | extern unsigned long user_stack(const struct pt_regs *); |
78 | extern void show_regs(struct pt_regs *); | 81 | extern void show_regs(struct pt_regs *); |
79 | #define profile_pc(regs) ((regs)->pc) | 82 | #define profile_pc(regs) ((regs)->pc) |
80 | #endif | 83 | |
84 | #define task_pt_regs(task) ((task)->thread.frame0) | ||
85 | |||
86 | #define arch_has_single_step() (1) | ||
87 | extern void user_enable_single_step(struct task_struct *); | ||
88 | extern void user_disable_single_step(struct task_struct *); | ||
81 | 89 | ||
82 | #endif /* !__ASSEMBLY__ */ | 90 | #endif /* !__ASSEMBLY__ */ |
91 | #endif /* __KERNEL__ */ | ||
83 | #endif /* _ASM_PTRACE_H */ | 92 | #endif /* _ASM_PTRACE_H */ |
diff --git a/arch/frv/include/asm/syscall.h b/arch/frv/include/asm/syscall.h new file mode 100644 index 000000000000..70689eb29b98 --- /dev/null +++ b/arch/frv/include/asm/syscall.h | |||
@@ -0,0 +1,123 @@ | |||
1 | /* syscall parameter access functions | ||
2 | * | ||
3 | * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #ifndef _ASM_SYSCALL_H | ||
13 | #define _ASM_SYSCALL_H | ||
14 | |||
15 | #include <linux/err.h> | ||
16 | #include <asm/ptrace.h> | ||
17 | |||
18 | /* | ||
19 | * Get the system call number or -1 | ||
20 | */ | ||
21 | static inline long syscall_get_nr(struct task_struct *task, | ||
22 | struct pt_regs *regs) | ||
23 | { | ||
24 | return regs->syscallno; | ||
25 | } | ||
26 | |||
27 | /* | ||
28 | * Restore the clobbered GR8 register | ||
29 | * (1st syscall arg was overwritten with syscall return or error) | ||
30 | */ | ||
31 | static inline void syscall_rollback(struct task_struct *task, | ||
32 | struct pt_regs *regs) | ||
33 | { | ||
34 | regs->gr8 = regs->orig_gr8; | ||
35 | } | ||
36 | |||
37 | /* | ||
38 | * See if the syscall return value is an error, returning it if it is and 0 if | ||
39 | * not | ||
40 | */ | ||
41 | static inline long syscall_get_error(struct task_struct *task, | ||
42 | struct pt_regs *regs) | ||
43 | { | ||
44 | return IS_ERR_VALUE(regs->gr8) ? regs->gr8 : 0; | ||
45 | } | ||
46 | |||
47 | /* | ||
48 | * Get the syscall return value | ||
49 | */ | ||
50 | static inline long syscall_get_return_value(struct task_struct *task, | ||
51 | struct pt_regs *regs) | ||
52 | { | ||
53 | return regs->gr8; | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * Set the syscall return value | ||
58 | */ | ||
59 | static inline void syscall_set_return_value(struct task_struct *task, | ||
60 | struct pt_regs *regs, | ||
61 | int error, long val) | ||
62 | { | ||
63 | if (error) | ||
64 | regs->gr8 = -error; | ||
65 | else | ||
66 | regs->gr8 = val; | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * Retrieve the system call arguments | ||
71 | */ | ||
72 | static inline void syscall_get_arguments(struct task_struct *task, | ||
73 | struct pt_regs *regs, | ||
74 | unsigned int i, unsigned int n, | ||
75 | unsigned long *args) | ||
76 | { | ||
77 | /* | ||
78 | * Do this simply for now. If we need to start supporting | ||
79 | * fetching arguments from arbitrary indices, this will need some | ||
80 | * extra logic. Presently there are no in-tree users that depend | ||
81 | * on this behaviour. | ||
82 | */ | ||
83 | BUG_ON(i); | ||
84 | |||
85 | /* Argument pattern is: GR8, GR9, GR10, GR11, GR12, GR13 */ | ||
86 | switch (n) { | ||
87 | case 6: args[5] = regs->gr13; | ||
88 | case 5: args[4] = regs->gr12; | ||
89 | case 4: args[3] = regs->gr11; | ||
90 | case 3: args[2] = regs->gr10; | ||
91 | case 2: args[1] = regs->gr9; | ||
92 | case 1: args[0] = regs->gr8; | ||
93 | break; | ||
94 | default: | ||
95 | BUG(); | ||
96 | } | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * Alter the system call arguments | ||
101 | */ | ||
102 | static inline void syscall_set_arguments(struct task_struct *task, | ||
103 | struct pt_regs *regs, | ||
104 | unsigned int i, unsigned int n, | ||
105 | const unsigned long *args) | ||
106 | { | ||
107 | /* Same note as above applies */ | ||
108 | BUG_ON(i); | ||
109 | |||
110 | switch (n) { | ||
111 | case 6: regs->gr13 = args[5]; | ||
112 | case 5: regs->gr12 = args[4]; | ||
113 | case 4: regs->gr11 = args[3]; | ||
114 | case 3: regs->gr10 = args[2]; | ||
115 | case 2: regs->gr9 = args[1]; | ||
116 | case 1: regs->gr8 = args[0]; | ||
117 | break; | ||
118 | default: | ||
119 | BUG(); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | #endif /* _ASM_SYSCALL_H */ | ||
diff --git a/arch/frv/kernel/entry.S b/arch/frv/kernel/entry.S index 268dfbddee3b..356e0e327a89 100644 --- a/arch/frv/kernel/entry.S +++ b/arch/frv/kernel/entry.S | |||
@@ -1149,11 +1149,10 @@ __entry_work_notifysig: | |||
1149 | # perform syscall entry tracing | 1149 | # perform syscall entry tracing |
1150 | __syscall_trace_entry: | 1150 | __syscall_trace_entry: |
1151 | LEDS 0x6320 | 1151 | LEDS 0x6320 |
1152 | setlos.p #0,gr8 | 1152 | call syscall_trace_entry |
1153 | call do_syscall_trace | ||
1154 | 1153 | ||
1155 | ldi @(gr28,#REG_SYSCALLNO),gr7 | 1154 | lddi.p @(gr28,#REG_GR(8)) ,gr8 |
1156 | lddi @(gr28,#REG_GR(8)) ,gr8 | 1155 | ori gr8,#0,gr7 ; syscall_trace_entry() returned new syscallno |
1157 | lddi @(gr28,#REG_GR(10)),gr10 | 1156 | lddi @(gr28,#REG_GR(10)),gr10 |
1158 | lddi.p @(gr28,#REG_GR(12)),gr12 | 1157 | lddi.p @(gr28,#REG_GR(12)),gr12 |
1159 | 1158 | ||
@@ -1168,11 +1167,10 @@ __syscall_exit_work: | |||
1168 | beq icc0,#1,__entry_work_pending | 1167 | beq icc0,#1,__entry_work_pending |
1169 | 1168 | ||
1170 | movsg psr,gr23 | 1169 | movsg psr,gr23 |
1171 | andi gr23,#~PSR_PIL,gr23 ; could let do_syscall_trace() call schedule() | 1170 | andi gr23,#~PSR_PIL,gr23 ; could let syscall_trace_exit() call schedule() |
1172 | movgs gr23,psr | 1171 | movgs gr23,psr |
1173 | 1172 | ||
1174 | setlos.p #1,gr8 | 1173 | call syscall_trace_exit |
1175 | call do_syscall_trace | ||
1176 | bra __entry_resume_userspace | 1174 | bra __entry_resume_userspace |
1177 | 1175 | ||
1178 | __syscall_badsys: | 1176 | __syscall_badsys: |
diff --git a/arch/frv/kernel/ptrace.c b/arch/frv/kernel/ptrace.c index 6b15e5da311a..60eeed3694c0 100644 --- a/arch/frv/kernel/ptrace.c +++ b/arch/frv/kernel/ptrace.c | |||
@@ -19,6 +19,9 @@ | |||
19 | #include <linux/user.h> | 19 | #include <linux/user.h> |
20 | #include <linux/security.h> | 20 | #include <linux/security.h> |
21 | #include <linux/signal.h> | 21 | #include <linux/signal.h> |
22 | #include <linux/regset.h> | ||
23 | #include <linux/elf.h> | ||
24 | #include <linux/tracehook.h> | ||
22 | 25 | ||
23 | #include <asm/uaccess.h> | 26 | #include <asm/uaccess.h> |
24 | #include <asm/page.h> | 27 | #include <asm/page.h> |
@@ -33,6 +36,169 @@ | |||
33 | */ | 36 | */ |
34 | 37 | ||
35 | /* | 38 | /* |
39 | * retrieve the contents of FRV userspace general registers | ||
40 | */ | ||
41 | static int genregs_get(struct task_struct *target, | ||
42 | const struct user_regset *regset, | ||
43 | unsigned int pos, unsigned int count, | ||
44 | void *kbuf, void __user *ubuf) | ||
45 | { | ||
46 | const struct user_int_regs *iregs = &target->thread.user->i; | ||
47 | int ret; | ||
48 | |||
49 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
50 | iregs, 0, sizeof(*iregs)); | ||
51 | if (ret < 0) | ||
52 | return ret; | ||
53 | |||
54 | return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
55 | sizeof(*iregs), -1); | ||
56 | } | ||
57 | |||
58 | /* | ||
59 | * update the contents of the FRV userspace general registers | ||
60 | */ | ||
61 | static int genregs_set(struct task_struct *target, | ||
62 | const struct user_regset *regset, | ||
63 | unsigned int pos, unsigned int count, | ||
64 | const void *kbuf, const void __user *ubuf) | ||
65 | { | ||
66 | struct user_int_regs *iregs = &target->thread.user->i; | ||
67 | unsigned int offs_gr0, offs_gr1; | ||
68 | int ret; | ||
69 | |||
70 | /* not allowed to set PSR or __status */ | ||
71 | if (pos < offsetof(struct user_int_regs, psr) + sizeof(long) && | ||
72 | pos + count > offsetof(struct user_int_regs, psr)) | ||
73 | return -EIO; | ||
74 | |||
75 | if (pos < offsetof(struct user_int_regs, __status) + sizeof(long) && | ||
76 | pos + count > offsetof(struct user_int_regs, __status)) | ||
77 | return -EIO; | ||
78 | |||
79 | /* set the control regs */ | ||
80 | offs_gr0 = offsetof(struct user_int_regs, gr[0]); | ||
81 | offs_gr1 = offsetof(struct user_int_regs, gr[1]); | ||
82 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
83 | iregs, 0, offs_gr0); | ||
84 | if (ret < 0) | ||
85 | return ret; | ||
86 | |||
87 | /* skip GR0/TBR */ | ||
88 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
89 | offs_gr0, offs_gr1); | ||
90 | if (ret < 0) | ||
91 | return ret; | ||
92 | |||
93 | /* set the general regs */ | ||
94 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
95 | &iregs->gr[1], offs_gr1, sizeof(*iregs)); | ||
96 | if (ret < 0) | ||
97 | return ret; | ||
98 | |||
99 | return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
100 | sizeof(*iregs), -1); | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | * retrieve the contents of FRV userspace FP/Media registers | ||
105 | */ | ||
106 | static int fpmregs_get(struct task_struct *target, | ||
107 | const struct user_regset *regset, | ||
108 | unsigned int pos, unsigned int count, | ||
109 | void *kbuf, void __user *ubuf) | ||
110 | { | ||
111 | const struct user_fpmedia_regs *fpregs = &target->thread.user->f; | ||
112 | int ret; | ||
113 | |||
114 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
115 | fpregs, 0, sizeof(*fpregs)); | ||
116 | if (ret < 0) | ||
117 | return ret; | ||
118 | |||
119 | return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
120 | sizeof(*fpregs), -1); | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | * update the contents of the FRV userspace FP/Media registers | ||
125 | */ | ||
126 | static int fpmregs_set(struct task_struct *target, | ||
127 | const struct user_regset *regset, | ||
128 | unsigned int pos, unsigned int count, | ||
129 | const void *kbuf, const void __user *ubuf) | ||
130 | { | ||
131 | struct user_fpmedia_regs *fpregs = &target->thread.user->f; | ||
132 | int ret; | ||
133 | |||
134 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
135 | fpregs, 0, sizeof(*fpregs)); | ||
136 | if (ret < 0) | ||
137 | return ret; | ||
138 | |||
139 | return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
140 | sizeof(*fpregs), -1); | ||
141 | } | ||
142 | |||
143 | /* | ||
144 | * determine if the FP/Media registers have actually been used | ||
145 | */ | ||
146 | static int fpmregs_active(struct task_struct *target, | ||
147 | const struct user_regset *regset) | ||
148 | { | ||
149 | return tsk_used_math(target) ? regset->n : 0; | ||
150 | } | ||
151 | |||
152 | /* | ||
153 | * Define the register sets available on the FRV under Linux | ||
154 | */ | ||
155 | enum frv_regset { | ||
156 | REGSET_GENERAL, | ||
157 | REGSET_FPMEDIA, | ||
158 | }; | ||
159 | |||
160 | static const struct user_regset frv_regsets[] = { | ||
161 | /* | ||
162 | * General register format is: | ||
163 | * PSR, ISR, CCR, CCCR, LR, LCR, PC, (STATUS), SYSCALLNO, ORIG_G8 | ||
164 | * GNER0-1, IACC0, TBR, GR1-63 | ||
165 | */ | ||
166 | [REGSET_GENERAL] = { | ||
167 | .core_note_type = NT_PRSTATUS, | ||
168 | .n = ELF_NGREG, | ||
169 | .size = sizeof(long), | ||
170 | .align = sizeof(long), | ||
171 | .get = genregs_get, | ||
172 | .set = genregs_set, | ||
173 | }, | ||
174 | /* | ||
175 | * FPU/Media register format is: | ||
176 | * FR0-63, FNER0-1, MSR0-1, ACC0-7, ACCG0-8, FSR | ||
177 | */ | ||
178 | [REGSET_FPMEDIA] = { | ||
179 | .core_note_type = NT_PRFPREG, | ||
180 | .n = sizeof(struct user_fpmedia_regs) / sizeof(long), | ||
181 | .size = sizeof(long), | ||
182 | .align = sizeof(long), | ||
183 | .get = fpmregs_get, | ||
184 | .set = fpmregs_set, | ||
185 | .active = fpmregs_active, | ||
186 | }, | ||
187 | }; | ||
188 | |||
189 | static const struct user_regset_view user_frv_native_view = { | ||
190 | .name = "frv", | ||
191 | .e_machine = EM_FRV, | ||
192 | .regsets = frv_regsets, | ||
193 | .n = ARRAY_SIZE(frv_regsets), | ||
194 | }; | ||
195 | |||
196 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | ||
197 | { | ||
198 | return &user_frv_native_view; | ||
199 | } | ||
200 | |||
201 | /* | ||
36 | * Get contents of register REGNO in task TASK. | 202 | * Get contents of register REGNO in task TASK. |
37 | */ | 203 | */ |
38 | static inline long get_reg(struct task_struct *task, int regno) | 204 | static inline long get_reg(struct task_struct *task, int regno) |
@@ -69,40 +235,23 @@ static inline int put_reg(struct task_struct *task, int regno, | |||
69 | } | 235 | } |
70 | 236 | ||
71 | /* | 237 | /* |
72 | * check that an address falls within the bounds of the target process's memory | ||
73 | * mappings | ||
74 | */ | ||
75 | static inline int is_user_addr_valid(struct task_struct *child, | ||
76 | unsigned long start, unsigned long len) | ||
77 | { | ||
78 | #ifdef CONFIG_MMU | ||
79 | if (start >= PAGE_OFFSET || len > PAGE_OFFSET - start) | ||
80 | return -EIO; | ||
81 | return 0; | ||
82 | #else | ||
83 | struct vm_area_struct *vma; | ||
84 | |||
85 | vma = find_vma(child->mm, start); | ||
86 | if (vma && start >= vma->vm_start && start + len <= vma->vm_end) | ||
87 | return 0; | ||
88 | |||
89 | return -EIO; | ||
90 | #endif | ||
91 | } | ||
92 | |||
93 | /* | ||
94 | * Called by kernel/ptrace.c when detaching.. | 238 | * Called by kernel/ptrace.c when detaching.. |
95 | * | 239 | * |
96 | * Control h/w single stepping | 240 | * Control h/w single stepping |
97 | */ | 241 | */ |
98 | void ptrace_disable(struct task_struct *child) | 242 | void user_enable_single_step(struct task_struct *child) |
243 | { | ||
244 | child->thread.frame0->__status |= REG__STATUS_STEP; | ||
245 | } | ||
246 | |||
247 | void user_disable_single_step(struct task_struct *child) | ||
99 | { | 248 | { |
100 | child->thread.frame0->__status &= ~REG__STATUS_STEP; | 249 | child->thread.frame0->__status &= ~REG__STATUS_STEP; |
101 | } | 250 | } |
102 | 251 | ||
103 | void ptrace_enable(struct task_struct *child) | 252 | void ptrace_disable(struct task_struct *child) |
104 | { | 253 | { |
105 | child->thread.frame0->__status |= REG__STATUS_STEP; | 254 | user_disable_single_step(child); |
106 | } | 255 | } |
107 | 256 | ||
108 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | 257 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) |
@@ -111,15 +260,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
111 | int ret; | 260 | int ret; |
112 | 261 | ||
113 | switch (request) { | 262 | switch (request) { |
114 | /* when I and D space are separate, these will need to be fixed. */ | ||
115 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | ||
116 | case PTRACE_PEEKDATA: | ||
117 | ret = -EIO; | ||
118 | if (is_user_addr_valid(child, addr, sizeof(tmp)) < 0) | ||
119 | break; | ||
120 | ret = generic_ptrace_peekdata(child, addr, data); | ||
121 | break; | ||
122 | |||
123 | /* read the word at location addr in the USER area. */ | 263 | /* read the word at location addr in the USER area. */ |
124 | case PTRACE_PEEKUSR: { | 264 | case PTRACE_PEEKUSR: { |
125 | tmp = 0; | 265 | tmp = 0; |
@@ -163,15 +303,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
163 | break; | 303 | break; |
164 | } | 304 | } |
165 | 305 | ||
166 | /* when I and D space are separate, this will have to be fixed. */ | ||
167 | case PTRACE_POKETEXT: /* write the word at location addr. */ | ||
168 | case PTRACE_POKEDATA: | ||
169 | ret = -EIO; | ||
170 | if (is_user_addr_valid(child, addr, sizeof(tmp)) < 0) | ||
171 | break; | ||
172 | ret = generic_ptrace_pokedata(child, addr, data); | ||
173 | break; | ||
174 | |||
175 | case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ | 306 | case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ |
176 | ret = -EIO; | 307 | ret = -EIO; |
177 | if ((addr & 3) || addr < 0) | 308 | if ((addr & 3) || addr < 0) |
@@ -179,7 +310,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
179 | 310 | ||
180 | ret = 0; | 311 | ret = 0; |
181 | switch (addr >> 2) { | 312 | switch (addr >> 2) { |
182 | case 0 ... PT__END-1: | 313 | case 0 ... PT__END - 1: |
183 | ret = put_reg(child, addr >> 2, data); | 314 | ret = put_reg(child, addr >> 2, data); |
184 | break; | 315 | break; |
185 | 316 | ||
@@ -189,95 +320,29 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
189 | } | 320 | } |
190 | break; | 321 | break; |
191 | 322 | ||
192 | case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ | 323 | case PTRACE_GETREGS: /* Get all integer regs from the child. */ |
193 | case PTRACE_CONT: /* restart after signal. */ | 324 | return copy_regset_to_user(child, &user_frv_native_view, |
194 | ret = -EIO; | 325 | REGSET_GENERAL, |
195 | if (!valid_signal(data)) | 326 | 0, sizeof(child->thread.user->i), |
196 | break; | 327 | (void __user *)data); |
197 | if (request == PTRACE_SYSCALL) | 328 | |
198 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | 329 | case PTRACE_SETREGS: /* Set all integer regs in the child. */ |
199 | else | 330 | return copy_regset_from_user(child, &user_frv_native_view, |
200 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | 331 | REGSET_GENERAL, |
201 | child->exit_code = data; | 332 | 0, sizeof(child->thread.user->i), |
202 | ptrace_disable(child); | 333 | (const void __user *)data); |
203 | wake_up_process(child); | 334 | |
204 | ret = 0; | 335 | case PTRACE_GETFPREGS: /* Get the child FP/Media state. */ |
205 | break; | 336 | return copy_regset_to_user(child, &user_frv_native_view, |
206 | 337 | REGSET_FPMEDIA, | |
207 | /* make the child exit. Best I can do is send it a sigkill. | 338 | 0, sizeof(child->thread.user->f), |
208 | * perhaps it should be put in the status that it wants to | 339 | (void __user *)data); |
209 | * exit. | 340 | |
210 | */ | 341 | case PTRACE_SETFPREGS: /* Set the child FP/Media state. */ |
211 | case PTRACE_KILL: | 342 | return copy_regset_from_user(child, &user_frv_native_view, |
212 | ret = 0; | 343 | REGSET_FPMEDIA, |
213 | if (child->exit_state == EXIT_ZOMBIE) /* already dead */ | 344 | 0, sizeof(child->thread.user->f), |
214 | break; | 345 | (const void __user *)data); |
215 | child->exit_code = SIGKILL; | ||
216 | clear_tsk_thread_flag(child, TIF_SINGLESTEP); | ||
217 | ptrace_disable(child); | ||
218 | wake_up_process(child); | ||
219 | break; | ||
220 | |||
221 | case PTRACE_SINGLESTEP: /* set the trap flag. */ | ||
222 | ret = -EIO; | ||
223 | if (!valid_signal(data)) | ||
224 | break; | ||
225 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
226 | ptrace_enable(child); | ||
227 | child->exit_code = data; | ||
228 | wake_up_process(child); | ||
229 | ret = 0; | ||
230 | break; | ||
231 | |||
232 | case PTRACE_DETACH: /* detach a process that was attached. */ | ||
233 | ret = ptrace_detach(child, data); | ||
234 | break; | ||
235 | |||
236 | case PTRACE_GETREGS: { /* Get all integer regs from the child. */ | ||
237 | int i; | ||
238 | for (i = 0; i < PT__GPEND; i++) { | ||
239 | tmp = get_reg(child, i); | ||
240 | if (put_user(tmp, (unsigned long *) data)) { | ||
241 | ret = -EFAULT; | ||
242 | break; | ||
243 | } | ||
244 | data += sizeof(long); | ||
245 | } | ||
246 | ret = 0; | ||
247 | break; | ||
248 | } | ||
249 | |||
250 | case PTRACE_SETREGS: { /* Set all integer regs in the child. */ | ||
251 | int i; | ||
252 | for (i = 0; i < PT__GPEND; i++) { | ||
253 | if (get_user(tmp, (unsigned long *) data)) { | ||
254 | ret = -EFAULT; | ||
255 | break; | ||
256 | } | ||
257 | put_reg(child, i, tmp); | ||
258 | data += sizeof(long); | ||
259 | } | ||
260 | ret = 0; | ||
261 | break; | ||
262 | } | ||
263 | |||
264 | case PTRACE_GETFPREGS: { /* Get the child FP/Media state. */ | ||
265 | ret = 0; | ||
266 | if (copy_to_user((void *) data, | ||
267 | &child->thread.user->f, | ||
268 | sizeof(child->thread.user->f))) | ||
269 | ret = -EFAULT; | ||
270 | break; | ||
271 | } | ||
272 | |||
273 | case PTRACE_SETFPREGS: { /* Set the child FP/Media state. */ | ||
274 | ret = 0; | ||
275 | if (copy_from_user(&child->thread.user->f, | ||
276 | (void *) data, | ||
277 | sizeof(child->thread.user->f))) | ||
278 | ret = -EFAULT; | ||
279 | break; | ||
280 | } | ||
281 | 346 | ||
282 | case PTRACE_GETFDPIC: | 347 | case PTRACE_GETFDPIC: |
283 | tmp = 0; | 348 | tmp = 0; |
@@ -300,35 +365,36 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
300 | break; | 365 | break; |
301 | 366 | ||
302 | default: | 367 | default: |
303 | ret = -EIO; | 368 | ret = ptrace_request(child, request, addr, data); |
304 | break; | 369 | break; |
305 | } | 370 | } |
306 | return ret; | 371 | return ret; |
307 | } | 372 | } |
308 | 373 | ||
309 | asmlinkage void do_syscall_trace(int leaving) | 374 | /* |
375 | * handle tracing of system call entry | ||
376 | * - return the revised system call number or ULONG_MAX to cause ENOSYS | ||
377 | */ | ||
378 | asmlinkage unsigned long syscall_trace_entry(void) | ||
310 | { | 379 | { |
311 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | 380 | __frame->__status |= REG__STATUS_SYSC_ENTRY; |
312 | return; | 381 | if (tracehook_report_syscall_entry(__frame)) { |
313 | 382 | /* tracing decided this syscall should not happen, so | |
314 | if (!(current->ptrace & PT_PTRACED)) | 383 | * We'll return a bogus call number to get an ENOSYS |
315 | return; | 384 | * error, but leave the original number in |
316 | 385 | * __frame->syscallno | |
317 | /* we need to indicate entry or exit to strace */ | 386 | */ |
318 | if (leaving) | 387 | return ULONG_MAX; |
319 | __frame->__status |= REG__STATUS_SYSC_EXIT; | 388 | } |
320 | else | ||
321 | __frame->__status |= REG__STATUS_SYSC_ENTRY; | ||
322 | 389 | ||
323 | ptrace_notify(SIGTRAP); | 390 | return __frame->syscallno; |
391 | } | ||
324 | 392 | ||
325 | /* | 393 | /* |
326 | * this isn't the same as continuing with a signal, but it will do | 394 | * handle tracing of system call exit |
327 | * for normal use. strace only continues with a signal if the | 395 | */ |
328 | * stopping signal is not SIGTRAP. -brl | 396 | asmlinkage void syscall_trace_exit(void) |
329 | */ | 397 | { |
330 | if (current->exit_code) { | 398 | __frame->__status |= REG__STATUS_SYSC_EXIT; |
331 | send_sig(current->exit_code, current, 1); | 399 | tracehook_report_syscall_exit(__frame, 0); |
332 | current->exit_code = 0; | ||
333 | } | ||
334 | } | 400 | } |
diff --git a/arch/frv/kernel/signal.c b/arch/frv/kernel/signal.c index 7ae290a161de..4a7a62c6e783 100644 --- a/arch/frv/kernel/signal.c +++ b/arch/frv/kernel/signal.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/unistd.h> | 21 | #include <linux/unistd.h> |
22 | #include <linux/personality.h> | 22 | #include <linux/personality.h> |
23 | #include <linux/freezer.h> | 23 | #include <linux/freezer.h> |
24 | #include <linux/tracehook.h> | ||
24 | #include <asm/ucontext.h> | 25 | #include <asm/ucontext.h> |
25 | #include <asm/uaccess.h> | 26 | #include <asm/uaccess.h> |
26 | #include <asm/cacheflush.h> | 27 | #include <asm/cacheflush.h> |
@@ -516,6 +517,9 @@ static void do_signal(void) | |||
516 | * clear the TIF_RESTORE_SIGMASK flag */ | 517 | * clear the TIF_RESTORE_SIGMASK flag */ |
517 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | 518 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) |
518 | clear_thread_flag(TIF_RESTORE_SIGMASK); | 519 | clear_thread_flag(TIF_RESTORE_SIGMASK); |
520 | |||
521 | tracehook_signal_handler(signr, &info, &ka, __frame, | ||
522 | test_thread_flag(TIF_SINGLESTEP)); | ||
519 | } | 523 | } |
520 | 524 | ||
521 | return; | 525 | return; |
@@ -567,6 +571,7 @@ asmlinkage void do_notify_resume(__u32 thread_info_flags) | |||
567 | /* deal with notification on about to resume userspace execution */ | 571 | /* deal with notification on about to resume userspace execution */ |
568 | if (thread_info_flags & _TIF_NOTIFY_RESUME) { | 572 | if (thread_info_flags & _TIF_NOTIFY_RESUME) { |
569 | clear_thread_flag(TIF_NOTIFY_RESUME); | 573 | clear_thread_flag(TIF_NOTIFY_RESUME); |
574 | tracehook_notify_resume(__frame); | ||
570 | } | 575 | } |
571 | 576 | ||
572 | } /* end do_notify_resume() */ | 577 | } /* end do_notify_resume() */ |