diff options
Diffstat (limited to 'arch/sparc/kernel/ptrace.c')
-rw-r--r-- | arch/sparc/kernel/ptrace.c | 813 |
1 files changed, 340 insertions, 473 deletions
diff --git a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace.c index 7452269bba2a..5b54f11f4e59 100644 --- a/arch/sparc/kernel/ptrace.c +++ b/arch/sparc/kernel/ptrace.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* ptrace.c: Sparc process tracing support. | 1 | /* ptrace.c: Sparc process tracing support. |
2 | * | 2 | * |
3 | * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu) | 3 | * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net) |
4 | * | 4 | * |
5 | * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, | 5 | * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, |
6 | * and David Mosberger. | 6 | * and David Mosberger. |
@@ -19,389 +19,343 @@ | |||
19 | #include <linux/smp_lock.h> | 19 | #include <linux/smp_lock.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> | ||
22 | 24 | ||
23 | #include <asm/pgtable.h> | 25 | #include <asm/pgtable.h> |
24 | #include <asm/system.h> | 26 | #include <asm/system.h> |
25 | #include <asm/uaccess.h> | 27 | #include <asm/uaccess.h> |
26 | 28 | ||
27 | #define MAGIC_CONSTANT 0x80000000 | 29 | /* #define ALLOW_INIT_TRACING */ |
28 | |||
29 | 30 | ||
30 | /* Returning from ptrace is a bit tricky because the syscall return | 31 | /* |
31 | * low level code assumes any value returned which is negative and | 32 | * Called by kernel/ptrace.c when detaching.. |
32 | * is a valid errno will mean setting the condition codes to indicate | 33 | * |
33 | * an error return. This doesn't work, so we have this hook. | 34 | * Make sure single step bits etc are not set. |
34 | */ | 35 | */ |
35 | static inline void pt_error_return(struct pt_regs *regs, unsigned long error) | 36 | void ptrace_disable(struct task_struct *child) |
36 | { | 37 | { |
37 | regs->u_regs[UREG_I0] = error; | 38 | /* nothing to do */ |
38 | regs->psr |= PSR_C; | ||
39 | regs->pc = regs->npc; | ||
40 | regs->npc += 4; | ||
41 | } | 39 | } |
42 | 40 | ||
43 | static inline void pt_succ_return(struct pt_regs *regs, unsigned long value) | 41 | enum sparc_regset { |
44 | { | 42 | REGSET_GENERAL, |
45 | regs->u_regs[UREG_I0] = value; | 43 | REGSET_FP, |
46 | regs->psr &= ~PSR_C; | 44 | }; |
47 | regs->pc = regs->npc; | ||
48 | regs->npc += 4; | ||
49 | } | ||
50 | 45 | ||
51 | static void | 46 | static int genregs32_get(struct task_struct *target, |
52 | pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long __user *addr) | 47 | const struct user_regset *regset, |
48 | unsigned int pos, unsigned int count, | ||
49 | void *kbuf, void __user *ubuf) | ||
53 | { | 50 | { |
54 | if (put_user(value, addr)) { | 51 | const struct pt_regs *regs = target->thread.kregs; |
55 | pt_error_return(regs, EFAULT); | 52 | unsigned long __user *reg_window; |
56 | return; | 53 | unsigned long *k = kbuf; |
54 | unsigned long __user *u = ubuf; | ||
55 | unsigned long reg; | ||
56 | |||
57 | if (target == current) | ||
58 | flush_user_windows(); | ||
59 | |||
60 | pos /= sizeof(reg); | ||
61 | count /= sizeof(reg); | ||
62 | |||
63 | if (kbuf) { | ||
64 | for (; count > 0 && pos < 16; count--) | ||
65 | *k++ = regs->u_regs[pos++]; | ||
66 | |||
67 | reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; | ||
68 | for (; count > 0 && pos < 32; count--) { | ||
69 | if (get_user(*k++, ®_window[pos++])) | ||
70 | return -EFAULT; | ||
71 | } | ||
72 | } else { | ||
73 | for (; count > 0 && pos < 16; count--) { | ||
74 | if (put_user(regs->u_regs[pos++], u++)) | ||
75 | return -EFAULT; | ||
76 | } | ||
77 | |||
78 | reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; | ||
79 | for (; count > 0 && pos < 32; count--) { | ||
80 | if (get_user(reg, ®_window[pos++]) || | ||
81 | put_user(reg, u++)) | ||
82 | return -EFAULT; | ||
83 | } | ||
57 | } | 84 | } |
58 | regs->u_regs[UREG_I0] = 0; | 85 | while (count > 0) { |
59 | regs->psr &= ~PSR_C; | 86 | switch (pos) { |
60 | regs->pc = regs->npc; | 87 | case 32: /* PSR */ |
61 | regs->npc += 4; | 88 | reg = regs->psr; |
62 | } | 89 | break; |
90 | case 33: /* PC */ | ||
91 | reg = regs->pc; | ||
92 | break; | ||
93 | case 34: /* NPC */ | ||
94 | reg = regs->npc; | ||
95 | break; | ||
96 | case 35: /* Y */ | ||
97 | reg = regs->y; | ||
98 | break; | ||
99 | case 36: /* WIM */ | ||
100 | case 37: /* TBR */ | ||
101 | reg = 0; | ||
102 | break; | ||
103 | default: | ||
104 | goto finish; | ||
105 | } | ||
63 | 106 | ||
64 | static void | 107 | if (kbuf) |
65 | pt_os_succ_return (struct pt_regs *regs, unsigned long val, long __user *addr) | 108 | *k++ = reg; |
66 | { | 109 | else if (put_user(reg, u++)) |
67 | if (current->personality == PER_SUNOS) | 110 | return -EFAULT; |
68 | pt_succ_return (regs, val); | 111 | pos++; |
69 | else | 112 | count--; |
70 | pt_succ_return_linux (regs, val, addr); | 113 | } |
114 | finish: | ||
115 | pos *= sizeof(reg); | ||
116 | count *= sizeof(reg); | ||
117 | |||
118 | return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
119 | 38 * sizeof(reg), -1); | ||
71 | } | 120 | } |
72 | 121 | ||
73 | /* Fuck me gently with a chainsaw... */ | 122 | static int genregs32_set(struct task_struct *target, |
74 | static inline void read_sunos_user(struct pt_regs *regs, unsigned long offset, | 123 | const struct user_regset *regset, |
75 | struct task_struct *tsk, long __user *addr) | 124 | unsigned int pos, unsigned int count, |
125 | const void *kbuf, const void __user *ubuf) | ||
76 | { | 126 | { |
77 | struct pt_regs *cregs = tsk->thread.kregs; | 127 | struct pt_regs *regs = target->thread.kregs; |
78 | struct thread_info *t = task_thread_info(tsk); | 128 | unsigned long __user *reg_window; |
79 | int v; | 129 | const unsigned long *k = kbuf; |
80 | 130 | const unsigned long __user *u = ubuf; | |
81 | if(offset >= 1024) | 131 | unsigned long reg; |
82 | offset -= 1024; /* whee... */ | 132 | |
83 | if(offset & ((sizeof(unsigned long) - 1))) { | 133 | if (target == current) |
84 | pt_error_return(regs, EIO); | 134 | flush_user_windows(); |
85 | return; | 135 | |
86 | } | 136 | pos /= sizeof(reg); |
87 | if(offset >= 16 && offset < 784) { | 137 | count /= sizeof(reg); |
88 | offset -= 16; offset >>= 2; | 138 | |
89 | pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr); | 139 | if (kbuf) { |
90 | return; | 140 | for (; count > 0 && pos < 16; count--) |
91 | } | 141 | regs->u_regs[pos++] = *k++; |
92 | if(offset >= 784 && offset < 832) { | 142 | |
93 | offset -= 784; offset >>= 2; | 143 | reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; |
94 | pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr); | 144 | for (; count > 0 && pos < 32; count--) { |
95 | return; | 145 | if (put_user(*k++, ®_window[pos++])) |
96 | } | 146 | return -EFAULT; |
97 | switch(offset) { | 147 | } |
98 | case 0: | 148 | } else { |
99 | v = t->ksp; | 149 | for (; count > 0 && pos < 16; count--) { |
100 | break; | 150 | if (get_user(reg, u++)) |
101 | case 4: | 151 | return -EFAULT; |
102 | v = t->kpc; | 152 | regs->u_regs[pos++] = reg; |
103 | break; | 153 | } |
104 | case 8: | ||
105 | v = t->kpsr; | ||
106 | break; | ||
107 | case 12: | ||
108 | v = t->uwinmask; | ||
109 | break; | ||
110 | case 832: | ||
111 | v = t->w_saved; | ||
112 | break; | ||
113 | case 896: | ||
114 | v = cregs->u_regs[UREG_I0]; | ||
115 | break; | ||
116 | case 900: | ||
117 | v = cregs->u_regs[UREG_I1]; | ||
118 | break; | ||
119 | case 904: | ||
120 | v = cregs->u_regs[UREG_I2]; | ||
121 | break; | ||
122 | case 908: | ||
123 | v = cregs->u_regs[UREG_I3]; | ||
124 | break; | ||
125 | case 912: | ||
126 | v = cregs->u_regs[UREG_I4]; | ||
127 | break; | ||
128 | case 916: | ||
129 | v = cregs->u_regs[UREG_I5]; | ||
130 | break; | ||
131 | case 920: | ||
132 | v = cregs->u_regs[UREG_I6]; | ||
133 | break; | ||
134 | case 924: | ||
135 | if(tsk->thread.flags & MAGIC_CONSTANT) | ||
136 | v = cregs->u_regs[UREG_G1]; | ||
137 | else | ||
138 | v = 0; | ||
139 | break; | ||
140 | case 940: | ||
141 | v = cregs->u_regs[UREG_I0]; | ||
142 | break; | ||
143 | case 944: | ||
144 | v = cregs->u_regs[UREG_I1]; | ||
145 | break; | ||
146 | 154 | ||
147 | case 948: | 155 | reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; |
148 | /* Isn't binary compatibility _fun_??? */ | 156 | for (; count > 0 && pos < 32; count--) { |
149 | if(cregs->psr & PSR_C) | 157 | if (get_user(reg, u++) || |
150 | v = cregs->u_regs[UREG_I0] << 24; | 158 | put_user(reg, ®_window[pos++])) |
151 | else | 159 | return -EFAULT; |
152 | v = 0; | 160 | } |
153 | break; | 161 | } |
162 | while (count > 0) { | ||
163 | unsigned long psr; | ||
164 | |||
165 | if (kbuf) | ||
166 | reg = *k++; | ||
167 | else if (get_user(reg, u++)) | ||
168 | return -EFAULT; | ||
169 | |||
170 | switch (pos) { | ||
171 | case 32: /* PSR */ | ||
172 | psr = regs->psr; | ||
173 | psr &= ~PSR_ICC; | ||
174 | psr |= (reg & PSR_ICC); | ||
175 | regs->psr = psr; | ||
176 | break; | ||
177 | case 33: /* PC */ | ||
178 | regs->pc = reg; | ||
179 | break; | ||
180 | case 34: /* NPC */ | ||
181 | regs->npc = reg; | ||
182 | break; | ||
183 | case 35: /* Y */ | ||
184 | regs->y = reg; | ||
185 | break; | ||
186 | case 36: /* WIM */ | ||
187 | case 37: /* TBR */ | ||
188 | break; | ||
189 | default: | ||
190 | goto finish; | ||
191 | } | ||
154 | 192 | ||
155 | /* Rest of them are completely unsupported. */ | 193 | pos++; |
156 | default: | 194 | count--; |
157 | printk("%s [%d]: Wants to read user offset %ld\n", | ||
158 | current->comm, task_pid_nr(current), offset); | ||
159 | pt_error_return(regs, EIO); | ||
160 | return; | ||
161 | } | 195 | } |
162 | if (current->personality == PER_SUNOS) | 196 | finish: |
163 | pt_succ_return (regs, v); | 197 | pos *= sizeof(reg); |
164 | else | 198 | count *= sizeof(reg); |
165 | pt_succ_return_linux (regs, v, addr); | 199 | |
166 | return; | 200 | return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, |
201 | 38 * sizeof(reg), -1); | ||
167 | } | 202 | } |
168 | 203 | ||
169 | static inline void write_sunos_user(struct pt_regs *regs, unsigned long offset, | 204 | static int fpregs32_get(struct task_struct *target, |
170 | struct task_struct *tsk) | 205 | const struct user_regset *regset, |
206 | unsigned int pos, unsigned int count, | ||
207 | void *kbuf, void __user *ubuf) | ||
171 | { | 208 | { |
172 | struct pt_regs *cregs = tsk->thread.kregs; | 209 | const unsigned long *fpregs = target->thread.float_regs; |
173 | struct thread_info *t = task_thread_info(tsk); | 210 | int ret = 0; |
174 | unsigned long value = regs->u_regs[UREG_I3]; | ||
175 | |||
176 | if(offset >= 1024) | ||
177 | offset -= 1024; /* whee... */ | ||
178 | if(offset & ((sizeof(unsigned long) - 1))) | ||
179 | goto failure; | ||
180 | if(offset >= 16 && offset < 784) { | ||
181 | offset -= 16; offset >>= 2; | ||
182 | *(((unsigned long *)(&t->reg_window[0]))+offset) = value; | ||
183 | goto success; | ||
184 | } | ||
185 | if(offset >= 784 && offset < 832) { | ||
186 | offset -= 784; offset >>= 2; | ||
187 | *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset) = value; | ||
188 | goto success; | ||
189 | } | ||
190 | switch(offset) { | ||
191 | case 896: | ||
192 | cregs->u_regs[UREG_I0] = value; | ||
193 | break; | ||
194 | case 900: | ||
195 | cregs->u_regs[UREG_I1] = value; | ||
196 | break; | ||
197 | case 904: | ||
198 | cregs->u_regs[UREG_I2] = value; | ||
199 | break; | ||
200 | case 908: | ||
201 | cregs->u_regs[UREG_I3] = value; | ||
202 | break; | ||
203 | case 912: | ||
204 | cregs->u_regs[UREG_I4] = value; | ||
205 | break; | ||
206 | case 916: | ||
207 | cregs->u_regs[UREG_I5] = value; | ||
208 | break; | ||
209 | case 920: | ||
210 | cregs->u_regs[UREG_I6] = value; | ||
211 | break; | ||
212 | case 924: | ||
213 | cregs->u_regs[UREG_I7] = value; | ||
214 | break; | ||
215 | case 940: | ||
216 | cregs->u_regs[UREG_I0] = value; | ||
217 | break; | ||
218 | case 944: | ||
219 | cregs->u_regs[UREG_I1] = value; | ||
220 | break; | ||
221 | 211 | ||
222 | /* Rest of them are completely unsupported or "no-touch". */ | 212 | #if 0 |
223 | default: | 213 | if (target == current) |
224 | printk("%s [%d]: Wants to write user offset %ld\n", | 214 | save_and_clear_fpu(); |
225 | current->comm, task_pid_nr(current), offset); | 215 | #endif |
226 | goto failure; | 216 | |
217 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
218 | fpregs, | ||
219 | 0, 32 * sizeof(u32)); | ||
220 | |||
221 | if (!ret) | ||
222 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | ||
223 | 32 * sizeof(u32), | ||
224 | 33 * sizeof(u32)); | ||
225 | if (!ret) | ||
226 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
227 | &target->thread.fsr, | ||
228 | 33 * sizeof(u32), | ||
229 | 34 * sizeof(u32)); | ||
230 | |||
231 | if (!ret) { | ||
232 | unsigned long val; | ||
233 | |||
234 | val = (1 << 8) | (8 << 16); | ||
235 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
236 | &val, | ||
237 | 34 * sizeof(u32), | ||
238 | 35 * sizeof(u32)); | ||
227 | } | 239 | } |
228 | success: | ||
229 | pt_succ_return(regs, 0); | ||
230 | return; | ||
231 | failure: | ||
232 | pt_error_return(regs, EIO); | ||
233 | return; | ||
234 | } | ||
235 | 240 | ||
236 | /* #define ALLOW_INIT_TRACING */ | 241 | if (!ret) |
237 | /* #define DEBUG_PTRACE */ | 242 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, |
238 | 243 | 35 * sizeof(u32), -1); | |
239 | #ifdef DEBUG_PTRACE | ||
240 | char *pt_rq [] = { | ||
241 | /* 0 */ "TRACEME", "PEEKTEXT", "PEEKDATA", "PEEKUSR", | ||
242 | /* 4 */ "POKETEXT", "POKEDATA", "POKEUSR", "CONT", | ||
243 | /* 8 */ "KILL", "SINGLESTEP", "SUNATTACH", "SUNDETACH", | ||
244 | /* 12 */ "GETREGS", "SETREGS", "GETFPREGS", "SETFPREGS", | ||
245 | /* 16 */ "READDATA", "WRITEDATA", "READTEXT", "WRITETEXT", | ||
246 | /* 20 */ "GETFPAREGS", "SETFPAREGS", "unknown", "unknown", | ||
247 | /* 24 */ "SYSCALL", "" | ||
248 | }; | ||
249 | #endif | ||
250 | 244 | ||
251 | /* | 245 | return ret; |
252 | * Called by kernel/ptrace.c when detaching.. | ||
253 | * | ||
254 | * Make sure single step bits etc are not set. | ||
255 | */ | ||
256 | void ptrace_disable(struct task_struct *child) | ||
257 | { | ||
258 | /* nothing to do */ | ||
259 | } | 246 | } |
260 | 247 | ||
261 | asmlinkage void do_ptrace(struct pt_regs *regs) | 248 | static int fpregs32_set(struct task_struct *target, |
249 | const struct user_regset *regset, | ||
250 | unsigned int pos, unsigned int count, | ||
251 | const void *kbuf, const void __user *ubuf) | ||
262 | { | 252 | { |
263 | unsigned long request = regs->u_regs[UREG_I0]; | 253 | unsigned long *fpregs = target->thread.float_regs; |
264 | unsigned long pid = regs->u_regs[UREG_I1]; | ||
265 | unsigned long addr = regs->u_regs[UREG_I2]; | ||
266 | unsigned long data = regs->u_regs[UREG_I3]; | ||
267 | unsigned long addr2 = regs->u_regs[UREG_I4]; | ||
268 | struct task_struct *child; | ||
269 | int ret; | 254 | int ret; |
270 | 255 | ||
271 | lock_kernel(); | 256 | #if 0 |
272 | #ifdef DEBUG_PTRACE | 257 | if (target == current) |
273 | { | 258 | save_and_clear_fpu(); |
274 | char *s; | ||
275 | |||
276 | if ((request >= 0) && (request <= 24)) | ||
277 | s = pt_rq [request]; | ||
278 | else | ||
279 | s = "unknown"; | ||
280 | |||
281 | if (request == PTRACE_POKEDATA && data == 0x91d02001){ | ||
282 | printk ("do_ptrace: breakpoint pid=%d, addr=%08lx addr2=%08lx\n", | ||
283 | pid, addr, addr2); | ||
284 | } else | ||
285 | printk("do_ptrace: rq=%s(%d) pid=%d addr=%08lx data=%08lx addr2=%08lx\n", | ||
286 | s, (int) request, (int) pid, addr, data, addr2); | ||
287 | } | ||
288 | #endif | 259 | #endif |
289 | 260 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
290 | if (request == PTRACE_TRACEME) { | 261 | fpregs, |
291 | ret = ptrace_traceme(); | 262 | 0, 32 * sizeof(u32)); |
292 | if (ret < 0) | 263 | if (!ret) |
293 | pt_error_return(regs, -ret); | 264 | user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, |
294 | else | 265 | 32 * sizeof(u32), |
295 | pt_succ_return(regs, 0); | 266 | 33 * sizeof(u32)); |
296 | goto out; | 267 | if (!ret && count > 0) { |
268 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
269 | &target->thread.fsr, | ||
270 | 33 * sizeof(u32), | ||
271 | 34 * sizeof(u32)); | ||
297 | } | 272 | } |
298 | 273 | ||
299 | child = ptrace_get_task_struct(pid); | 274 | if (!ret) |
300 | if (IS_ERR(child)) { | 275 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, |
301 | ret = PTR_ERR(child); | 276 | 34 * sizeof(u32), -1); |
302 | pt_error_return(regs, -ret); | 277 | return ret; |
303 | goto out; | 278 | } |
304 | } | ||
305 | 279 | ||
306 | if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH) | 280 | static const struct user_regset sparc32_regsets[] = { |
307 | || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) { | 281 | /* Format is: |
308 | if (ptrace_attach(child)) { | 282 | * G0 --> G7 |
309 | pt_error_return(regs, EPERM); | 283 | * O0 --> O7 |
310 | goto out_tsk; | 284 | * L0 --> L7 |
311 | } | 285 | * I0 --> I7 |
312 | pt_succ_return(regs, 0); | 286 | * PSR, PC, nPC, Y, WIM, TBR |
313 | goto out_tsk; | 287 | */ |
314 | } | 288 | [REGSET_GENERAL] = { |
289 | .core_note_type = NT_PRSTATUS, | ||
290 | .n = 38 * sizeof(u32), | ||
291 | .size = sizeof(u32), .align = sizeof(u32), | ||
292 | .get = genregs32_get, .set = genregs32_set | ||
293 | }, | ||
294 | /* Format is: | ||
295 | * F0 --> F31 | ||
296 | * empty 32-bit word | ||
297 | * FSR (32--bit word) | ||
298 | * FPU QUEUE COUNT (8-bit char) | ||
299 | * FPU QUEUE ENTRYSIZE (8-bit char) | ||
300 | * FPU ENABLED (8-bit char) | ||
301 | * empty 8-bit char | ||
302 | * FPU QUEUE (64 32-bit ints) | ||
303 | */ | ||
304 | [REGSET_FP] = { | ||
305 | .core_note_type = NT_PRFPREG, | ||
306 | .n = 99 * sizeof(u32), | ||
307 | .size = sizeof(u32), .align = sizeof(u32), | ||
308 | .get = fpregs32_get, .set = fpregs32_set | ||
309 | }, | ||
310 | }; | ||
315 | 311 | ||
316 | ret = ptrace_check_attach(child, request == PTRACE_KILL); | 312 | static const struct user_regset_view user_sparc32_view = { |
317 | if (ret < 0) { | 313 | .name = "sparc", .e_machine = EM_SPARC, |
318 | pt_error_return(regs, -ret); | 314 | .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) |
319 | goto out_tsk; | 315 | }; |
320 | } | ||
321 | 316 | ||
322 | switch(request) { | 317 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) |
323 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | 318 | { |
324 | case PTRACE_PEEKDATA: { | 319 | return &user_sparc32_view; |
325 | unsigned long tmp; | 320 | } |
326 | |||
327 | if (access_process_vm(child, addr, | ||
328 | &tmp, sizeof(tmp), 0) == sizeof(tmp)) | ||
329 | pt_os_succ_return(regs, tmp, (long __user *)data); | ||
330 | else | ||
331 | pt_error_return(regs, EIO); | ||
332 | goto out_tsk; | ||
333 | } | ||
334 | 321 | ||
335 | case PTRACE_PEEKUSR: | 322 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) |
336 | read_sunos_user(regs, addr, child, (long __user *) data); | 323 | { |
337 | goto out_tsk; | 324 | unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4]; |
338 | 325 | const struct user_regset_view *view; | |
339 | case PTRACE_POKEUSR: | 326 | int ret; |
340 | write_sunos_user(regs, addr, child); | 327 | |
341 | goto out_tsk; | 328 | view = task_user_regset_view(child); |
342 | |||
343 | case PTRACE_POKETEXT: /* write the word at location addr. */ | ||
344 | case PTRACE_POKEDATA: { | ||
345 | if (access_process_vm(child, addr, | ||
346 | &data, sizeof(data), 1) == sizeof(data)) | ||
347 | pt_succ_return(regs, 0); | ||
348 | else | ||
349 | pt_error_return(regs, EIO); | ||
350 | goto out_tsk; | ||
351 | } | ||
352 | 329 | ||
330 | switch(request) { | ||
353 | case PTRACE_GETREGS: { | 331 | case PTRACE_GETREGS: { |
354 | struct pt_regs __user *pregs = (struct pt_regs __user *) addr; | 332 | struct pt_regs __user *pregs = (struct pt_regs __user *) addr; |
355 | struct pt_regs *cregs = child->thread.kregs; | ||
356 | int rval; | ||
357 | 333 | ||
358 | if (!access_ok(VERIFY_WRITE, pregs, sizeof(struct pt_regs))) { | 334 | ret = copy_regset_to_user(child, view, REGSET_GENERAL, |
359 | rval = -EFAULT; | 335 | 32 * sizeof(u32), |
360 | pt_error_return(regs, -rval); | 336 | 4 * sizeof(u32), |
361 | goto out_tsk; | 337 | &pregs->psr); |
362 | } | 338 | if (!ret) |
363 | __put_user(cregs->psr, (&pregs->psr)); | 339 | copy_regset_to_user(child, view, REGSET_GENERAL, |
364 | __put_user(cregs->pc, (&pregs->pc)); | 340 | 1 * sizeof(u32), |
365 | __put_user(cregs->npc, (&pregs->npc)); | 341 | 15 * sizeof(u32), |
366 | __put_user(cregs->y, (&pregs->y)); | 342 | &pregs->u_regs[0]); |
367 | for(rval = 1; rval < 16; rval++) | 343 | break; |
368 | __put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1])); | ||
369 | pt_succ_return(regs, 0); | ||
370 | #ifdef DEBUG_PTRACE | ||
371 | printk ("PC=%x nPC=%x o7=%x\n", cregs->pc, cregs->npc, cregs->u_regs [15]); | ||
372 | #endif | ||
373 | goto out_tsk; | ||
374 | } | 344 | } |
375 | 345 | ||
376 | case PTRACE_SETREGS: { | 346 | case PTRACE_SETREGS: { |
377 | struct pt_regs __user *pregs = (struct pt_regs __user *) addr; | 347 | struct pt_regs __user *pregs = (struct pt_regs __user *) addr; |
378 | struct pt_regs *cregs = child->thread.kregs; | 348 | |
379 | unsigned long psr, pc, npc, y; | 349 | ret = copy_regset_from_user(child, view, REGSET_GENERAL, |
380 | int i; | 350 | 32 * sizeof(u32), |
381 | 351 | 4 * sizeof(u32), | |
382 | /* Must be careful, tracing process can only set certain | 352 | &pregs->psr); |
383 | * bits in the psr. | 353 | if (!ret) |
384 | */ | 354 | copy_regset_from_user(child, view, REGSET_GENERAL, |
385 | if (!access_ok(VERIFY_READ, pregs, sizeof(struct pt_regs))) { | 355 | 1 * sizeof(u32), |
386 | pt_error_return(regs, EFAULT); | 356 | 15 * sizeof(u32), |
387 | goto out_tsk; | 357 | &pregs->u_regs[0]); |
388 | } | 358 | break; |
389 | __get_user(psr, (&pregs->psr)); | ||
390 | __get_user(pc, (&pregs->pc)); | ||
391 | __get_user(npc, (&pregs->npc)); | ||
392 | __get_user(y, (&pregs->y)); | ||
393 | psr &= PSR_ICC; | ||
394 | cregs->psr &= ~PSR_ICC; | ||
395 | cregs->psr |= psr; | ||
396 | if (!((pc | npc) & 3)) { | ||
397 | cregs->pc = pc; | ||
398 | cregs->npc =npc; | ||
399 | } | ||
400 | cregs->y = y; | ||
401 | for(i = 1; i < 16; i++) | ||
402 | __get_user(cregs->u_regs[i], (&pregs->u_regs[i-1])); | ||
403 | pt_succ_return(regs, 0); | ||
404 | goto out_tsk; | ||
405 | } | 359 | } |
406 | 360 | ||
407 | case PTRACE_GETFPREGS: { | 361 | case PTRACE_GETFPREGS: { |
@@ -417,26 +371,25 @@ asmlinkage void do_ptrace(struct pt_regs *regs) | |||
417 | } fpq[16]; | 371 | } fpq[16]; |
418 | }; | 372 | }; |
419 | struct fps __user *fps = (struct fps __user *) addr; | 373 | struct fps __user *fps = (struct fps __user *) addr; |
420 | int i; | ||
421 | 374 | ||
422 | if (!access_ok(VERIFY_WRITE, fps, sizeof(struct fps))) { | 375 | ret = copy_regset_to_user(child, view, REGSET_FP, |
423 | i = -EFAULT; | 376 | 0 * sizeof(u32), |
424 | pt_error_return(regs, -i); | 377 | 32 * sizeof(u32), |
425 | goto out_tsk; | 378 | &fps->regs[0]); |
426 | } | 379 | if (!ret) |
427 | for(i = 0; i < 32; i++) | 380 | ret = copy_regset_to_user(child, view, REGSET_FP, |
428 | __put_user(child->thread.float_regs[i], (&fps->regs[i])); | 381 | 33 * sizeof(u32), |
429 | __put_user(child->thread.fsr, (&fps->fsr)); | 382 | 1 * sizeof(u32), |
430 | __put_user(child->thread.fpqdepth, (&fps->fpqd)); | 383 | &fps->fsr); |
431 | __put_user(0, (&fps->flags)); | 384 | |
432 | __put_user(0, (&fps->extra)); | 385 | if (!ret) { |
433 | for(i = 0; i < 16; i++) { | 386 | if (__put_user(0, &fps->fpqd) || |
434 | __put_user(child->thread.fpqueue[i].insn_addr, | 387 | __put_user(0, &fps->flags) || |
435 | (&fps->fpq[i].insnaddr)); | 388 | __put_user(0, &fps->extra) || |
436 | __put_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn)); | 389 | clear_user(fps->fpq, sizeof(fps->fpq))) |
390 | ret = -EFAULT; | ||
437 | } | 391 | } |
438 | pt_succ_return(regs, 0); | 392 | break; |
439 | goto out_tsk; | ||
440 | } | 393 | } |
441 | 394 | ||
442 | case PTRACE_SETFPREGS: { | 395 | case PTRACE_SETFPREGS: { |
@@ -452,137 +405,55 @@ asmlinkage void do_ptrace(struct pt_regs *regs) | |||
452 | } fpq[16]; | 405 | } fpq[16]; |
453 | }; | 406 | }; |
454 | struct fps __user *fps = (struct fps __user *) addr; | 407 | struct fps __user *fps = (struct fps __user *) addr; |
455 | int i; | ||
456 | 408 | ||
457 | if (!access_ok(VERIFY_READ, fps, sizeof(struct fps))) { | 409 | ret = copy_regset_from_user(child, view, REGSET_FP, |
458 | i = -EFAULT; | 410 | 0 * sizeof(u32), |
459 | pt_error_return(regs, -i); | 411 | 32 * sizeof(u32), |
460 | goto out_tsk; | 412 | &fps->regs[0]); |
461 | } | 413 | if (!ret) |
462 | copy_from_user(&child->thread.float_regs[0], &fps->regs[0], (32 * sizeof(unsigned long))); | 414 | ret = copy_regset_from_user(child, view, REGSET_FP, |
463 | __get_user(child->thread.fsr, (&fps->fsr)); | 415 | 33 * sizeof(u32), |
464 | __get_user(child->thread.fpqdepth, (&fps->fpqd)); | 416 | 1 * sizeof(u32), |
465 | for(i = 0; i < 16; i++) { | 417 | &fps->fsr); |
466 | __get_user(child->thread.fpqueue[i].insn_addr, | 418 | break; |
467 | (&fps->fpq[i].insnaddr)); | ||
468 | __get_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn)); | ||
469 | } | ||
470 | pt_succ_return(regs, 0); | ||
471 | goto out_tsk; | ||
472 | } | 419 | } |
473 | 420 | ||
474 | case PTRACE_READTEXT: | 421 | case PTRACE_READTEXT: |
475 | case PTRACE_READDATA: { | 422 | case PTRACE_READDATA: |
476 | int res = ptrace_readdata(child, addr, | 423 | ret = ptrace_readdata(child, addr, |
477 | (void __user *) addr2, data); | 424 | (void __user *) addr2, data); |
478 | 425 | ||
479 | if (res == data) { | 426 | if (ret == data) |
480 | pt_succ_return(regs, 0); | 427 | ret = 0; |
481 | goto out_tsk; | 428 | else if (ret >= 0) |
482 | } | 429 | ret = -EIO; |
483 | /* Partial read is an IO failure */ | 430 | break; |
484 | if (res >= 0) | ||
485 | res = -EIO; | ||
486 | pt_error_return(regs, -res); | ||
487 | goto out_tsk; | ||
488 | } | ||
489 | 431 | ||
490 | case PTRACE_WRITETEXT: | 432 | case PTRACE_WRITETEXT: |
491 | case PTRACE_WRITEDATA: { | 433 | case PTRACE_WRITEDATA: |
492 | int res = ptrace_writedata(child, (void __user *) addr2, | 434 | ret = ptrace_writedata(child, (void __user *) addr2, |
493 | addr, data); | 435 | addr, data); |
494 | 436 | ||
495 | if (res == data) { | 437 | if (ret == data) |
496 | pt_succ_return(regs, 0); | 438 | ret = 0; |
497 | goto out_tsk; | 439 | else if (ret >= 0) |
498 | } | 440 | ret = -EIO; |
499 | /* Partial write is an IO failure */ | 441 | break; |
500 | if (res >= 0) | ||
501 | res = -EIO; | ||
502 | pt_error_return(regs, -res); | ||
503 | goto out_tsk; | ||
504 | } | ||
505 | |||
506 | case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */ | ||
507 | addr = 1; | ||
508 | |||
509 | case PTRACE_CONT: { /* restart after signal. */ | ||
510 | if (!valid_signal(data)) { | ||
511 | pt_error_return(regs, EIO); | ||
512 | goto out_tsk; | ||
513 | } | ||
514 | |||
515 | if (request == PTRACE_SYSCALL) | ||
516 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
517 | else | ||
518 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
519 | |||
520 | child->exit_code = data; | ||
521 | #ifdef DEBUG_PTRACE | ||
522 | printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n", | ||
523 | child->comm, child->pid, child->exit_code, | ||
524 | child->thread.kregs->pc, | ||
525 | child->thread.kregs->npc); | ||
526 | #endif | ||
527 | wake_up_process(child); | ||
528 | pt_succ_return(regs, 0); | ||
529 | goto out_tsk; | ||
530 | } | ||
531 | |||
532 | /* | ||
533 | * make the child exit. Best I can do is send it a sigkill. | ||
534 | * perhaps it should be put in the status that it wants to | ||
535 | * exit. | ||
536 | */ | ||
537 | case PTRACE_KILL: { | ||
538 | if (child->exit_state == EXIT_ZOMBIE) { /* already dead */ | ||
539 | pt_succ_return(regs, 0); | ||
540 | goto out_tsk; | ||
541 | } | ||
542 | wake_up_process(child); | ||
543 | child->exit_code = SIGKILL; | ||
544 | pt_succ_return(regs, 0); | ||
545 | goto out_tsk; | ||
546 | } | ||
547 | 442 | ||
548 | case PTRACE_SUNDETACH: { /* detach a process that was attached. */ | 443 | default: |
549 | int err = ptrace_detach(child, data); | 444 | ret = ptrace_request(child, request, addr, data); |
550 | if (err) { | 445 | break; |
551 | pt_error_return(regs, EIO); | ||
552 | goto out_tsk; | ||
553 | } | ||
554 | pt_succ_return(regs, 0); | ||
555 | goto out_tsk; | ||
556 | } | 446 | } |
557 | 447 | ||
558 | /* PTRACE_DUMPCORE unsupported... */ | 448 | return ret; |
559 | |||
560 | default: { | ||
561 | int err = ptrace_request(child, request, addr, data); | ||
562 | if (err) | ||
563 | pt_error_return(regs, -err); | ||
564 | else | ||
565 | pt_succ_return(regs, 0); | ||
566 | goto out_tsk; | ||
567 | } | ||
568 | } | ||
569 | out_tsk: | ||
570 | if (child) | ||
571 | put_task_struct(child); | ||
572 | out: | ||
573 | unlock_kernel(); | ||
574 | } | 449 | } |
575 | 450 | ||
576 | asmlinkage void syscall_trace(void) | 451 | asmlinkage void syscall_trace(void) |
577 | { | 452 | { |
578 | #ifdef DEBUG_PTRACE | ||
579 | printk("%s [%d]: syscall_trace\n", current->comm, current->pid); | ||
580 | #endif | ||
581 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | 453 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) |
582 | return; | 454 | return; |
583 | if (!(current->ptrace & PT_PTRACED)) | 455 | if (!(current->ptrace & PT_PTRACED)) |
584 | return; | 456 | return; |
585 | current->thread.flags ^= MAGIC_CONSTANT; | ||
586 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) | 457 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) |
587 | ? 0x80 : 0)); | 458 | ? 0x80 : 0)); |
588 | /* | 459 | /* |
@@ -590,10 +461,6 @@ asmlinkage void syscall_trace(void) | |||
590 | * for normal use. strace only continues with a signal if the | 461 | * for normal use. strace only continues with a signal if the |
591 | * stopping signal is not SIGTRAP. -brl | 462 | * stopping signal is not SIGTRAP. -brl |
592 | */ | 463 | */ |
593 | #ifdef DEBUG_PTRACE | ||
594 | printk("%s [%d]: syscall_trace exit= %x\n", current->comm, | ||
595 | current->pid, current->exit_code); | ||
596 | #endif | ||
597 | if (current->exit_code) { | 464 | if (current->exit_code) { |
598 | send_sig (current->exit_code, current, 1); | 465 | send_sig (current->exit_code, current, 1); |
599 | current->exit_code = 0; | 466 | current->exit_code = 0; |