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