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 | 32 | |
6d75ca10 CH |
33 | |
34 | void user_enable_single_step(struct task_struct *child) | |
35 | { | |
36 | child->ptrace |= PT_SINGLESTEP; | |
37 | } | |
38 | ||
39 | void user_disable_single_step(struct task_struct *child) | |
40 | { | |
41 | child->ptrace &= ~PT_SINGLESTEP; | |
42 | } | |
43 | ||
5a0015d6 | 44 | /* |
c658eac6 | 45 | * Called by kernel/ptrace.c when detaching to disable single stepping. |
5a0015d6 CZ |
46 | */ |
47 | ||
48 | void ptrace_disable(struct task_struct *child) | |
49 | { | |
50 | /* Nothing to do.. */ | |
51 | } | |
52 | ||
c658eac6 | 53 | int ptrace_getregs(struct task_struct *child, void __user *uregs) |
5a0015d6 | 54 | { |
c658eac6 CZ |
55 | struct pt_regs *regs = task_pt_regs(child); |
56 | xtensa_gregset_t __user *gregset = uregs; | |
c658eac6 | 57 | unsigned long wm = regs->wmask; |
42086cec CZ |
58 | unsigned long wb = regs->windowbase; |
59 | int live, i; | |
c658eac6 CZ |
60 | |
61 | if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t))) | |
62 | return -EIO; | |
63 | ||
42086cec CZ |
64 | __put_user(regs->pc, &gregset->pc); |
65 | __put_user(regs->ps & ~(1 << PS_EXCM_BIT), &gregset->ps); | |
66 | __put_user(regs->lbeg, &gregset->lbeg); | |
67 | __put_user(regs->lend, &gregset->lend); | |
68 | __put_user(regs->lcount, &gregset->lcount); | |
69 | __put_user(regs->windowstart, &gregset->windowstart); | |
70 | __put_user(regs->windowbase, &gregset->windowbase); | |
c658eac6 CZ |
71 | |
72 | live = (wm & 2) ? 4 : (wm & 4) ? 8 : (wm & 8) ? 12 : 16; | |
c658eac6 | 73 | |
42086cec CZ |
74 | for (i = 0; i < live; i++) |
75 | __put_user(regs->areg[i],gregset->a+((wb*4+i)%XCHAL_NUM_AREGS)); | |
76 | for (i = XCHAL_NUM_AREGS - (wm >> 4) * 4; i < XCHAL_NUM_AREGS; i++) | |
77 | __put_user(regs->areg[i],gregset->a+((wb*4+i)%XCHAL_NUM_AREGS)); | |
78 | ||
79 | return 0; | |
c658eac6 | 80 | } |
5a0015d6 | 81 | |
c658eac6 CZ |
82 | int ptrace_setregs(struct task_struct *child, void __user *uregs) |
83 | { | |
84 | struct pt_regs *regs = task_pt_regs(child); | |
85 | xtensa_gregset_t *gregset = uregs; | |
86 | const unsigned long ps_mask = PS_CALLINC_MASK | PS_OWB_MASK; | |
c658eac6 | 87 | unsigned long ps; |
42086cec | 88 | unsigned long wb; |
c658eac6 CZ |
89 | |
90 | if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t))) | |
91 | return -EIO; | |
92 | ||
42086cec CZ |
93 | __get_user(regs->pc, &gregset->pc); |
94 | __get_user(ps, &gregset->ps); | |
95 | __get_user(regs->lbeg, &gregset->lbeg); | |
96 | __get_user(regs->lend, &gregset->lend); | |
97 | __get_user(regs->lcount, &gregset->lcount); | |
98 | __get_user(regs->windowstart, &gregset->windowstart); | |
99 | __get_user(wb, &gregset->windowbase); | |
c658eac6 CZ |
100 | |
101 | regs->ps = (regs->ps & ~ps_mask) | (ps & ps_mask) | (1 << PS_EXCM_BIT); | |
102 | ||
42086cec CZ |
103 | if (wb >= XCHAL_NUM_AREGS / 4) |
104 | return -EFAULT; | |
c658eac6 | 105 | |
42086cec CZ |
106 | regs->windowbase = wb; |
107 | ||
108 | if (wb != 0 && __copy_from_user(regs->areg + XCHAL_NUM_AREGS - wb * 4, | |
109 | gregset->a, wb * 16)) | |
110 | return -EFAULT; | |
111 | ||
112 | if (__copy_from_user(regs->areg, gregset->a + wb*4, (WSBITS-wb) * 16)) | |
113 | return -EFAULT; | |
114 | ||
115 | return 0; | |
c658eac6 | 116 | } |
5a0015d6 | 117 | |
5a0015d6 | 118 | |
c658eac6 CZ |
119 | int ptrace_getxregs(struct task_struct *child, void __user *uregs) |
120 | { | |
121 | struct pt_regs *regs = task_pt_regs(child); | |
122 | struct thread_info *ti = task_thread_info(child); | |
123 | elf_xtregs_t __user *xtregs = uregs; | |
124 | int ret = 0; | |
125 | ||
126 | if (!access_ok(VERIFY_WRITE, uregs, sizeof(elf_xtregs_t))) | |
127 | return -EIO; | |
128 | ||
129 | #if XTENSA_HAVE_COPROCESSORS | |
130 | /* Flush all coprocessor registers to memory. */ | |
131 | coprocessor_flush_all(ti); | |
132 | ret |= __copy_to_user(&xtregs->cp0, &ti->xtregs_cp, | |
133 | sizeof(xtregs_coprocessor_t)); | |
134 | #endif | |
135 | ret |= __copy_to_user(&xtregs->opt, ®s->xtregs_opt, | |
136 | sizeof(xtregs->opt)); | |
137 | ret |= __copy_to_user(&xtregs->user,&ti->xtregs_user, | |
138 | sizeof(xtregs->user)); | |
5a0015d6 | 139 | |
c658eac6 CZ |
140 | return ret ? -EFAULT : 0; |
141 | } | |
142 | ||
143 | int ptrace_setxregs(struct task_struct *child, void __user *uregs) | |
144 | { | |
145 | struct thread_info *ti = task_thread_info(child); | |
146 | struct pt_regs *regs = task_pt_regs(child); | |
147 | elf_xtregs_t *xtregs = uregs; | |
148 | int ret = 0; | |
149 | ||
150 | #if XTENSA_HAVE_COPROCESSORS | |
151 | /* Flush all coprocessors before we overwrite them. */ | |
152 | coprocessor_flush_all(ti); | |
153 | coprocessor_release_all(ti); | |
154 | ||
155 | ret |= __copy_from_user(&ti->xtregs_cp, &xtregs->cp0, | |
156 | sizeof(xtregs_coprocessor_t)); | |
157 | #endif | |
158 | ret |= __copy_from_user(®s->xtregs_opt, &xtregs->opt, | |
159 | sizeof(xtregs->opt)); | |
160 | ret |= __copy_from_user(&ti->xtregs_user, &xtregs->user, | |
161 | sizeof(xtregs->user)); | |
162 | ||
163 | return ret ? -EFAULT : 0; | |
164 | } | |
165 | ||
166 | int ptrace_peekusr(struct task_struct *child, long regno, long __user *ret) | |
167 | { | |
168 | struct pt_regs *regs; | |
169 | unsigned long tmp; | |
170 | ||
171 | regs = task_pt_regs(child); | |
172 | tmp = 0; /* Default return value. */ | |
5a0015d6 | 173 | |
c658eac6 | 174 | switch(regno) { |
5a0015d6 CZ |
175 | |
176 | case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: | |
c658eac6 | 177 | tmp = regs->areg[regno - REG_AR_BASE]; |
5a0015d6 | 178 | break; |
c658eac6 | 179 | |
5a0015d6 | 180 | case REG_A_BASE ... REG_A_BASE + 15: |
c658eac6 | 181 | tmp = regs->areg[regno - REG_A_BASE]; |
5a0015d6 | 182 | break; |
c658eac6 | 183 | |
5a0015d6 CZ |
184 | case REG_PC: |
185 | tmp = regs->pc; | |
186 | break; | |
c658eac6 | 187 | |
5a0015d6 CZ |
188 | case REG_PS: |
189 | /* Note: PS.EXCM is not set while user task is running; | |
190 | * its being set in regs is for exception handling | |
191 | * convenience. */ | |
173d6681 | 192 | tmp = (regs->ps & ~(1 << PS_EXCM_BIT)); |
5a0015d6 | 193 | break; |
c658eac6 | 194 | |
5a0015d6 | 195 | case REG_WB: |
c658eac6 CZ |
196 | break; /* tmp = 0 */ |
197 | ||
5a0015d6 | 198 | case REG_WS: |
c658eac6 CZ |
199 | { |
200 | unsigned long wb = regs->windowbase; | |
201 | unsigned long ws = regs->windowstart; | |
202 | tmp = ((ws>>wb) | (ws<<(WSBITS-wb))) & ((1<<WSBITS)-1); | |
5a0015d6 | 203 | break; |
c658eac6 | 204 | } |
5a0015d6 CZ |
205 | case REG_LBEG: |
206 | tmp = regs->lbeg; | |
207 | break; | |
c658eac6 | 208 | |
5a0015d6 CZ |
209 | case REG_LEND: |
210 | tmp = regs->lend; | |
211 | break; | |
c658eac6 | 212 | |
5a0015d6 CZ |
213 | case REG_LCOUNT: |
214 | tmp = regs->lcount; | |
215 | break; | |
c658eac6 | 216 | |
5a0015d6 CZ |
217 | case REG_SAR: |
218 | tmp = regs->sar; | |
219 | break; | |
c658eac6 | 220 | |
5a0015d6 CZ |
221 | case SYSCALL_NR: |
222 | tmp = regs->syscall; | |
223 | break; | |
5a0015d6 | 224 | |
c658eac6 CZ |
225 | default: |
226 | return -EIO; | |
227 | } | |
228 | return put_user(tmp, ret); | |
229 | } | |
5a0015d6 | 230 | |
c658eac6 CZ |
231 | int ptrace_pokeusr(struct task_struct *child, long regno, long val) |
232 | { | |
233 | struct pt_regs *regs; | |
234 | regs = task_pt_regs(child); | |
5a0015d6 | 235 | |
c658eac6 | 236 | switch (regno) { |
5a0015d6 | 237 | case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: |
c658eac6 | 238 | regs->areg[regno - REG_AR_BASE] = val; |
5a0015d6 | 239 | break; |
c658eac6 | 240 | |
5a0015d6 | 241 | case REG_A_BASE ... REG_A_BASE + 15: |
c658eac6 | 242 | regs->areg[regno - REG_A_BASE] = val; |
5a0015d6 | 243 | break; |
c658eac6 | 244 | |
5a0015d6 | 245 | case REG_PC: |
c658eac6 | 246 | regs->pc = val; |
5a0015d6 | 247 | break; |
c658eac6 | 248 | |
5a0015d6 | 249 | case SYSCALL_NR: |
c658eac6 | 250 | regs->syscall = val; |
5a0015d6 | 251 | break; |
5a0015d6 CZ |
252 | |
253 | default: | |
c658eac6 CZ |
254 | return -EIO; |
255 | } | |
256 | return 0; | |
257 | } | |
258 | ||
9b05a69e NK |
259 | long arch_ptrace(struct task_struct *child, long request, |
260 | unsigned long addr, unsigned long data) | |
c658eac6 CZ |
261 | { |
262 | int ret = -EPERM; | |
263 | ||
264 | switch (request) { | |
265 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | |
266 | case PTRACE_PEEKDATA: | |
267 | ret = generic_ptrace_peekdata(child, addr, data); | |
268 | break; | |
269 | ||
270 | case PTRACE_PEEKUSR: /* read register specified by addr. */ | |
271 | ret = ptrace_peekusr(child, addr, (void __user *) data); | |
272 | break; | |
273 | ||
274 | case PTRACE_POKETEXT: /* write the word at location addr. */ | |
275 | case PTRACE_POKEDATA: | |
276 | ret = generic_ptrace_pokedata(child, addr, data); | |
277 | break; | |
278 | ||
279 | case PTRACE_POKEUSR: /* write register specified by addr. */ | |
280 | ret = ptrace_pokeusr(child, addr, data); | |
5a0015d6 | 281 | break; |
5a0015d6 | 282 | |
5a0015d6 | 283 | case PTRACE_GETREGS: |
c658eac6 | 284 | ret = ptrace_getregs(child, (void __user *) data); |
5a0015d6 | 285 | break; |
5a0015d6 CZ |
286 | |
287 | case PTRACE_SETREGS: | |
c658eac6 | 288 | ret = ptrace_setregs(child, (void __user *) data); |
5a0015d6 | 289 | break; |
5a0015d6 | 290 | |
c658eac6 CZ |
291 | case PTRACE_GETXTREGS: |
292 | ret = ptrace_getxregs(child, (void __user *) data); | |
5a0015d6 | 293 | break; |
5a0015d6 | 294 | |
c658eac6 CZ |
295 | case PTRACE_SETXTREGS: |
296 | ret = ptrace_setxregs(child, (void __user *) data); | |
5a0015d6 CZ |
297 | break; |
298 | ||
5a0015d6 CZ |
299 | default: |
300 | ret = ptrace_request(child, request, addr, data); | |
c658eac6 | 301 | break; |
5a0015d6 | 302 | } |
c658eac6 | 303 | |
5a0015d6 CZ |
304 | return ret; |
305 | } | |
306 | ||
307 | void do_syscall_trace(void) | |
308 | { | |
5a0015d6 CZ |
309 | /* |
310 | * The 0x80 provides a way for the tracing parent to distinguish | |
311 | * between a syscall stop and SIGTRAP delivery | |
312 | */ | |
313 | ptrace_notify(SIGTRAP|((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0)); | |
314 | ||
315 | /* | |
316 | * this isn't the same as continuing with a signal, but it will do | |
317 | * for normal use. strace only continues with a signal if the | |
318 | * stopping signal is not SIGTRAP. -brl | |
319 | */ | |
320 | if (current->exit_code) { | |
321 | send_sig(current->exit_code, current, 1); | |
322 | current->exit_code = 0; | |
323 | } | |
324 | } | |
fc4fb2ad CZ |
325 | |
326 | void do_syscall_trace_enter(struct pt_regs *regs) | |
327 | { | |
328 | if (test_thread_flag(TIF_SYSCALL_TRACE) | |
329 | && (current->ptrace & PT_PTRACED)) | |
330 | do_syscall_trace(); | |
331 | ||
332 | #if 0 | |
333 | if (unlikely(current->audit_context)) | |
334 | audit_syscall_entry(current, AUDIT_ARCH_XTENSA..); | |
335 | #endif | |
336 | } | |
337 | ||
338 | void do_syscall_trace_leave(struct pt_regs *regs) | |
339 | { | |
340 | if ((test_thread_flag(TIF_SYSCALL_TRACE)) | |
341 | && (current->ptrace & PT_PTRACED)) | |
342 | do_syscall_trace(); | |
343 | } | |
344 |