Commit | Line | Data |
---|---|---|
5a0015d6 CZ |
1 | // TODO some minor issues |
2 | /* | |
3 | * This file is subject to the terms and conditions of the GNU General Public | |
4 | * License. See the file "COPYING" in the main directory of this archive | |
5 | * for more details. | |
6 | * | |
c658eac6 | 7 | * Copyright (C) 2001 - 2007 Tensilica Inc. |
5a0015d6 CZ |
8 | * |
9 | * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com> | |
10 | * Chris Zankel <chris@zankel.net> | |
11 | * Scott Foehner<sfoehner@yahoo.com>, | |
12 | * Kevin Chea | |
13 | * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca> | |
14 | */ | |
15 | ||
5a0015d6 CZ |
16 | #include <linux/kernel.h> |
17 | #include <linux/sched.h> | |
18 | #include <linux/mm.h> | |
19 | #include <linux/errno.h> | |
20 | #include <linux/ptrace.h> | |
21 | #include <linux/smp.h> | |
5a0015d6 | 22 | #include <linux/security.h> |
0ee23b50 | 23 | #include <linux/signal.h> |
5a0015d6 CZ |
24 | |
25 | #include <asm/pgtable.h> | |
26 | #include <asm/page.h> | |
27 | #include <asm/system.h> | |
28 | #include <asm/uaccess.h> | |
29 | #include <asm/ptrace.h> | |
30 | #include <asm/elf.h> | |
c658eac6 | 31 | #include <asm/coprocessor.h> |
5a0015d6 CZ |
32 | |
33 | /* | |
c658eac6 | 34 | * Called by kernel/ptrace.c when detaching to disable single stepping. |
5a0015d6 CZ |
35 | */ |
36 | ||
37 | void ptrace_disable(struct task_struct *child) | |
38 | { | |
39 | /* Nothing to do.. */ | |
40 | } | |
41 | ||
c658eac6 | 42 | int ptrace_getregs(struct task_struct *child, void __user *uregs) |
5a0015d6 | 43 | { |
c658eac6 CZ |
44 | struct pt_regs *regs = task_pt_regs(child); |
45 | xtensa_gregset_t __user *gregset = uregs; | |
c658eac6 | 46 | unsigned long wm = regs->wmask; |
42086cec CZ |
47 | unsigned long wb = regs->windowbase; |
48 | int live, i; | |
c658eac6 CZ |
49 | |
50 | if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t))) | |
51 | return -EIO; | |
52 | ||
42086cec CZ |
53 | __put_user(regs->pc, &gregset->pc); |
54 | __put_user(regs->ps & ~(1 << PS_EXCM_BIT), &gregset->ps); | |
55 | __put_user(regs->lbeg, &gregset->lbeg); | |
56 | __put_user(regs->lend, &gregset->lend); | |
57 | __put_user(regs->lcount, &gregset->lcount); | |
58 | __put_user(regs->windowstart, &gregset->windowstart); | |
59 | __put_user(regs->windowbase, &gregset->windowbase); | |
c658eac6 CZ |
60 | |
61 | live = (wm & 2) ? 4 : (wm & 4) ? 8 : (wm & 8) ? 12 : 16; | |
c658eac6 | 62 | |
42086cec CZ |
63 | for (i = 0; i < live; i++) |
64 | __put_user(regs->areg[i],gregset->a+((wb*4+i)%XCHAL_NUM_AREGS)); | |
65 | for (i = XCHAL_NUM_AREGS - (wm >> 4) * 4; i < XCHAL_NUM_AREGS; i++) | |
66 | __put_user(regs->areg[i],gregset->a+((wb*4+i)%XCHAL_NUM_AREGS)); | |
67 | ||
68 | return 0; | |
c658eac6 | 69 | } |
5a0015d6 | 70 | |
c658eac6 CZ |
71 | int ptrace_setregs(struct task_struct *child, void __user *uregs) |
72 | { | |
73 | struct pt_regs *regs = task_pt_regs(child); | |
74 | xtensa_gregset_t *gregset = uregs; | |
75 | const unsigned long ps_mask = PS_CALLINC_MASK | PS_OWB_MASK; | |
c658eac6 | 76 | unsigned long ps; |
42086cec | 77 | unsigned long wb; |
c658eac6 CZ |
78 | |
79 | if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t))) | |
80 | return -EIO; | |
81 | ||
42086cec CZ |
82 | __get_user(regs->pc, &gregset->pc); |
83 | __get_user(ps, &gregset->ps); | |
84 | __get_user(regs->lbeg, &gregset->lbeg); | |
85 | __get_user(regs->lend, &gregset->lend); | |
86 | __get_user(regs->lcount, &gregset->lcount); | |
87 | __get_user(regs->windowstart, &gregset->windowstart); | |
88 | __get_user(wb, &gregset->windowbase); | |
c658eac6 CZ |
89 | |
90 | regs->ps = (regs->ps & ~ps_mask) | (ps & ps_mask) | (1 << PS_EXCM_BIT); | |
91 | ||
42086cec CZ |
92 | if (wb >= XCHAL_NUM_AREGS / 4) |
93 | return -EFAULT; | |
c658eac6 | 94 | |
42086cec CZ |
95 | regs->windowbase = wb; |
96 | ||
97 | if (wb != 0 && __copy_from_user(regs->areg + XCHAL_NUM_AREGS - wb * 4, | |
98 | gregset->a, wb * 16)) | |
99 | return -EFAULT; | |
100 | ||
101 | if (__copy_from_user(regs->areg, gregset->a + wb*4, (WSBITS-wb) * 16)) | |
102 | return -EFAULT; | |
103 | ||
104 | return 0; | |
c658eac6 | 105 | } |
5a0015d6 | 106 | |
5a0015d6 | 107 | |
c658eac6 CZ |
108 | int ptrace_getxregs(struct task_struct *child, void __user *uregs) |
109 | { | |
110 | struct pt_regs *regs = task_pt_regs(child); | |
111 | struct thread_info *ti = task_thread_info(child); | |
112 | elf_xtregs_t __user *xtregs = uregs; | |
113 | int ret = 0; | |
114 | ||
115 | if (!access_ok(VERIFY_WRITE, uregs, sizeof(elf_xtregs_t))) | |
116 | return -EIO; | |
117 | ||
118 | #if XTENSA_HAVE_COPROCESSORS | |
119 | /* Flush all coprocessor registers to memory. */ | |
120 | coprocessor_flush_all(ti); | |
121 | ret |= __copy_to_user(&xtregs->cp0, &ti->xtregs_cp, | |
122 | sizeof(xtregs_coprocessor_t)); | |
123 | #endif | |
124 | ret |= __copy_to_user(&xtregs->opt, ®s->xtregs_opt, | |
125 | sizeof(xtregs->opt)); | |
126 | ret |= __copy_to_user(&xtregs->user,&ti->xtregs_user, | |
127 | sizeof(xtregs->user)); | |
5a0015d6 | 128 | |
c658eac6 CZ |
129 | return ret ? -EFAULT : 0; |
130 | } | |
131 | ||
132 | int ptrace_setxregs(struct task_struct *child, void __user *uregs) | |
133 | { | |
134 | struct thread_info *ti = task_thread_info(child); | |
135 | struct pt_regs *regs = task_pt_regs(child); | |
136 | elf_xtregs_t *xtregs = uregs; | |
137 | int ret = 0; | |
138 | ||
139 | #if XTENSA_HAVE_COPROCESSORS | |
140 | /* Flush all coprocessors before we overwrite them. */ | |
141 | coprocessor_flush_all(ti); | |
142 | coprocessor_release_all(ti); | |
143 | ||
144 | ret |= __copy_from_user(&ti->xtregs_cp, &xtregs->cp0, | |
145 | sizeof(xtregs_coprocessor_t)); | |
146 | #endif | |
147 | ret |= __copy_from_user(®s->xtregs_opt, &xtregs->opt, | |
148 | sizeof(xtregs->opt)); | |
149 | ret |= __copy_from_user(&ti->xtregs_user, &xtregs->user, | |
150 | sizeof(xtregs->user)); | |
151 | ||
152 | return ret ? -EFAULT : 0; | |
153 | } | |
154 | ||
155 | int ptrace_peekusr(struct task_struct *child, long regno, long __user *ret) | |
156 | { | |
157 | struct pt_regs *regs; | |
158 | unsigned long tmp; | |
159 | ||
160 | regs = task_pt_regs(child); | |
161 | tmp = 0; /* Default return value. */ | |
5a0015d6 | 162 | |
c658eac6 | 163 | switch(regno) { |
5a0015d6 CZ |
164 | |
165 | case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: | |
c658eac6 | 166 | tmp = regs->areg[regno - REG_AR_BASE]; |
5a0015d6 | 167 | break; |
c658eac6 | 168 | |
5a0015d6 | 169 | case REG_A_BASE ... REG_A_BASE + 15: |
c658eac6 | 170 | tmp = regs->areg[regno - REG_A_BASE]; |
5a0015d6 | 171 | break; |
c658eac6 | 172 | |
5a0015d6 CZ |
173 | case REG_PC: |
174 | tmp = regs->pc; | |
175 | break; | |
c658eac6 | 176 | |
5a0015d6 CZ |
177 | case REG_PS: |
178 | /* Note: PS.EXCM is not set while user task is running; | |
179 | * its being set in regs is for exception handling | |
180 | * convenience. */ | |
173d6681 | 181 | tmp = (regs->ps & ~(1 << PS_EXCM_BIT)); |
5a0015d6 | 182 | break; |
c658eac6 | 183 | |
5a0015d6 | 184 | case REG_WB: |
c658eac6 CZ |
185 | break; /* tmp = 0 */ |
186 | ||
5a0015d6 | 187 | case REG_WS: |
c658eac6 CZ |
188 | { |
189 | unsigned long wb = regs->windowbase; | |
190 | unsigned long ws = regs->windowstart; | |
191 | tmp = ((ws>>wb) | (ws<<(WSBITS-wb))) & ((1<<WSBITS)-1); | |
5a0015d6 | 192 | break; |
c658eac6 | 193 | } |
5a0015d6 CZ |
194 | case REG_LBEG: |
195 | tmp = regs->lbeg; | |
196 | break; | |
c658eac6 | 197 | |
5a0015d6 CZ |
198 | case REG_LEND: |
199 | tmp = regs->lend; | |
200 | break; | |
c658eac6 | 201 | |
5a0015d6 CZ |
202 | case REG_LCOUNT: |
203 | tmp = regs->lcount; | |
204 | break; | |
c658eac6 | 205 | |
5a0015d6 CZ |
206 | case REG_SAR: |
207 | tmp = regs->sar; | |
208 | break; | |
c658eac6 | 209 | |
5a0015d6 CZ |
210 | case SYSCALL_NR: |
211 | tmp = regs->syscall; | |
212 | break; | |
5a0015d6 | 213 | |
c658eac6 CZ |
214 | default: |
215 | return -EIO; | |
216 | } | |
217 | return put_user(tmp, ret); | |
218 | } | |
5a0015d6 | 219 | |
c658eac6 CZ |
220 | int ptrace_pokeusr(struct task_struct *child, long regno, long val) |
221 | { | |
222 | struct pt_regs *regs; | |
223 | regs = task_pt_regs(child); | |
5a0015d6 | 224 | |
c658eac6 | 225 | switch (regno) { |
5a0015d6 | 226 | case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: |
c658eac6 | 227 | regs->areg[regno - REG_AR_BASE] = val; |
5a0015d6 | 228 | break; |
c658eac6 | 229 | |
5a0015d6 | 230 | case REG_A_BASE ... REG_A_BASE + 15: |
c658eac6 | 231 | regs->areg[regno - REG_A_BASE] = val; |
5a0015d6 | 232 | break; |
c658eac6 | 233 | |
5a0015d6 | 234 | case REG_PC: |
c658eac6 | 235 | regs->pc = val; |
5a0015d6 | 236 | break; |
c658eac6 | 237 | |
5a0015d6 | 238 | case SYSCALL_NR: |
c658eac6 | 239 | regs->syscall = val; |
5a0015d6 | 240 | break; |
5a0015d6 CZ |
241 | |
242 | default: | |
c658eac6 CZ |
243 | return -EIO; |
244 | } | |
245 | return 0; | |
246 | } | |
247 | ||
248 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |
249 | { | |
250 | int ret = -EPERM; | |
251 | ||
252 | switch (request) { | |
253 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | |
254 | case PTRACE_PEEKDATA: | |
255 | ret = generic_ptrace_peekdata(child, addr, data); | |
256 | break; | |
257 | ||
258 | case PTRACE_PEEKUSR: /* read register specified by addr. */ | |
259 | ret = ptrace_peekusr(child, addr, (void __user *) data); | |
260 | break; | |
261 | ||
262 | case PTRACE_POKETEXT: /* write the word at location addr. */ | |
263 | case PTRACE_POKEDATA: | |
264 | ret = generic_ptrace_pokedata(child, addr, data); | |
265 | break; | |
266 | ||
267 | case PTRACE_POKEUSR: /* write register specified by addr. */ | |
268 | ret = ptrace_pokeusr(child, addr, data); | |
5a0015d6 | 269 | break; |
5a0015d6 CZ |
270 | |
271 | /* continue and stop at next (return from) syscall */ | |
c658eac6 | 272 | |
5a0015d6 CZ |
273 | case PTRACE_SYSCALL: |
274 | case PTRACE_CONT: /* restart after signal. */ | |
275 | { | |
276 | ret = -EIO; | |
0ee23b50 | 277 | if (!valid_signal(data)) |
5a0015d6 CZ |
278 | break; |
279 | if (request == PTRACE_SYSCALL) | |
280 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | |
281 | else | |
282 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | |
283 | child->exit_code = data; | |
284 | /* Make sure the single step bit is not set. */ | |
285 | child->ptrace &= ~PT_SINGLESTEP; | |
286 | wake_up_process(child); | |
287 | ret = 0; | |
288 | break; | |
289 | } | |
290 | ||
291 | /* | |
292 | * make the child exit. Best I can do is send it a sigkill. | |
293 | * perhaps it should be put in the status that it wants to | |
294 | * exit. | |
295 | */ | |
296 | case PTRACE_KILL: | |
297 | ret = 0; | |
d742eae8 | 298 | if (child->exit_state == EXIT_ZOMBIE) /* already dead */ |
5a0015d6 CZ |
299 | break; |
300 | child->exit_code = SIGKILL; | |
301 | child->ptrace &= ~PT_SINGLESTEP; | |
302 | wake_up_process(child); | |
303 | break; | |
304 | ||
305 | case PTRACE_SINGLESTEP: | |
306 | ret = -EIO; | |
0ee23b50 | 307 | if (!valid_signal(data)) |
5a0015d6 CZ |
308 | break; |
309 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | |
310 | child->ptrace |= PT_SINGLESTEP; | |
311 | child->exit_code = data; | |
312 | wake_up_process(child); | |
313 | ret = 0; | |
314 | break; | |
315 | ||
316 | case PTRACE_GETREGS: | |
c658eac6 | 317 | ret = ptrace_getregs(child, (void __user *) data); |
5a0015d6 | 318 | break; |
5a0015d6 CZ |
319 | |
320 | case PTRACE_SETREGS: | |
c658eac6 | 321 | ret = ptrace_setregs(child, (void __user *) data); |
5a0015d6 | 322 | break; |
5a0015d6 | 323 | |
c658eac6 CZ |
324 | case PTRACE_GETXTREGS: |
325 | ret = ptrace_getxregs(child, (void __user *) data); | |
5a0015d6 | 326 | break; |
5a0015d6 | 327 | |
c658eac6 CZ |
328 | case PTRACE_SETXTREGS: |
329 | ret = ptrace_setxregs(child, (void __user *) data); | |
5a0015d6 CZ |
330 | break; |
331 | ||
5a0015d6 CZ |
332 | default: |
333 | ret = ptrace_request(child, request, addr, data); | |
c658eac6 | 334 | break; |
5a0015d6 | 335 | } |
c658eac6 | 336 | |
5a0015d6 CZ |
337 | return ret; |
338 | } | |
339 | ||
340 | void do_syscall_trace(void) | |
341 | { | |
5a0015d6 CZ |
342 | /* |
343 | * The 0x80 provides a way for the tracing parent to distinguish | |
344 | * between a syscall stop and SIGTRAP delivery | |
345 | */ | |
346 | ptrace_notify(SIGTRAP|((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0)); | |
347 | ||
348 | /* | |
349 | * this isn't the same as continuing with a signal, but it will do | |
350 | * for normal use. strace only continues with a signal if the | |
351 | * stopping signal is not SIGTRAP. -brl | |
352 | */ | |
353 | if (current->exit_code) { | |
354 | send_sig(current->exit_code, current, 1); | |
355 | current->exit_code = 0; | |
356 | } | |
357 | } | |
fc4fb2ad CZ |
358 | |
359 | void do_syscall_trace_enter(struct pt_regs *regs) | |
360 | { | |
361 | if (test_thread_flag(TIF_SYSCALL_TRACE) | |
362 | && (current->ptrace & PT_PTRACED)) | |
363 | do_syscall_trace(); | |
364 | ||
365 | #if 0 | |
366 | if (unlikely(current->audit_context)) | |
367 | audit_syscall_entry(current, AUDIT_ARCH_XTENSA..); | |
368 | #endif | |
369 | } | |
370 | ||
371 | void do_syscall_trace_leave(struct pt_regs *regs) | |
372 | { | |
373 | if ((test_thread_flag(TIF_SYSCALL_TRACE)) | |
374 | && (current->ptrace & PT_PTRACED)) | |
375 | do_syscall_trace(); | |
376 | } | |
377 |