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 | ||
c91e02bd MF |
16 | #include <linux/errno.h> |
17 | #include <linux/hw_breakpoint.h> | |
5a0015d6 | 18 | #include <linux/kernel.h> |
5a0015d6 | 19 | #include <linux/mm.h> |
c91e02bd | 20 | #include <linux/perf_event.h> |
5a0015d6 | 21 | #include <linux/ptrace.h> |
c91e02bd | 22 | #include <linux/sched.h> |
5a0015d6 | 23 | #include <linux/security.h> |
0ee23b50 | 24 | #include <linux/signal.h> |
c91e02bd | 25 | #include <linux/smp.h> |
5a0015d6 | 26 | |
c91e02bd MF |
27 | #include <asm/coprocessor.h> |
28 | #include <asm/elf.h> | |
5a0015d6 | 29 | #include <asm/page.h> |
c91e02bd | 30 | #include <asm/pgtable.h> |
5a0015d6 | 31 | #include <asm/ptrace.h> |
c91e02bd | 32 | #include <asm/uaccess.h> |
5a0015d6 | 33 | |
6d75ca10 CH |
34 | |
35 | void user_enable_single_step(struct task_struct *child) | |
36 | { | |
37 | child->ptrace |= PT_SINGLESTEP; | |
38 | } | |
39 | ||
40 | void user_disable_single_step(struct task_struct *child) | |
41 | { | |
42 | child->ptrace &= ~PT_SINGLESTEP; | |
43 | } | |
44 | ||
5a0015d6 | 45 | /* |
c658eac6 | 46 | * Called by kernel/ptrace.c when detaching to disable single stepping. |
5a0015d6 CZ |
47 | */ |
48 | ||
49 | void ptrace_disable(struct task_struct *child) | |
50 | { | |
51 | /* Nothing to do.. */ | |
52 | } | |
53 | ||
c658eac6 | 54 | int ptrace_getregs(struct task_struct *child, void __user *uregs) |
5a0015d6 | 55 | { |
c658eac6 CZ |
56 | struct pt_regs *regs = task_pt_regs(child); |
57 | xtensa_gregset_t __user *gregset = uregs; | |
42086cec | 58 | unsigned long wb = regs->windowbase; |
4b2bb03f | 59 | int 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); | |
c50842df | 71 | __put_user(regs->threadptr, &gregset->threadptr); |
c658eac6 | 72 | |
4b2bb03f MF |
73 | for (i = 0; i < XCHAL_NUM_AREGS; i++) |
74 | __put_user(regs->areg[i], | |
75 | gregset->a + ((wb * 4 + i) % XCHAL_NUM_AREGS)); | |
42086cec CZ |
76 | |
77 | return 0; | |
c658eac6 | 78 | } |
5a0015d6 | 79 | |
c658eac6 CZ |
80 | int ptrace_setregs(struct task_struct *child, void __user *uregs) |
81 | { | |
82 | struct pt_regs *regs = task_pt_regs(child); | |
83 | xtensa_gregset_t *gregset = uregs; | |
84 | const unsigned long ps_mask = PS_CALLINC_MASK | PS_OWB_MASK; | |
c658eac6 | 85 | unsigned long ps; |
4b2bb03f | 86 | unsigned long wb, ws; |
c658eac6 CZ |
87 | |
88 | if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t))) | |
89 | return -EIO; | |
90 | ||
42086cec CZ |
91 | __get_user(regs->pc, &gregset->pc); |
92 | __get_user(ps, &gregset->ps); | |
93 | __get_user(regs->lbeg, &gregset->lbeg); | |
94 | __get_user(regs->lend, &gregset->lend); | |
95 | __get_user(regs->lcount, &gregset->lcount); | |
4b2bb03f | 96 | __get_user(ws, &gregset->windowstart); |
42086cec | 97 | __get_user(wb, &gregset->windowbase); |
c50842df | 98 | __get_user(regs->threadptr, &gregset->threadptr); |
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 | |
4b2bb03f MF |
105 | if (wb != regs->windowbase || ws != regs->windowstart) { |
106 | unsigned long rotws, wmask; | |
107 | ||
108 | rotws = (((ws | (ws << WSBITS)) >> wb) & | |
109 | ((1 << WSBITS) - 1)) & ~1; | |
110 | wmask = ((rotws ? WSBITS + 1 - ffs(rotws) : 0) << 4) | | |
111 | (rotws & 0xF) | 1; | |
112 | regs->windowbase = wb; | |
113 | regs->windowstart = ws; | |
114 | regs->wmask = wmask; | |
115 | } | |
42086cec CZ |
116 | |
117 | if (wb != 0 && __copy_from_user(regs->areg + XCHAL_NUM_AREGS - wb * 4, | |
4b2bb03f | 118 | gregset->a, wb * 16)) |
42086cec CZ |
119 | return -EFAULT; |
120 | ||
4b2bb03f MF |
121 | if (__copy_from_user(regs->areg, gregset->a + wb * 4, |
122 | (WSBITS - wb) * 16)) | |
42086cec CZ |
123 | return -EFAULT; |
124 | ||
125 | return 0; | |
c658eac6 | 126 | } |
5a0015d6 | 127 | |
5a0015d6 | 128 | |
c658eac6 CZ |
129 | int ptrace_getxregs(struct task_struct *child, void __user *uregs) |
130 | { | |
131 | struct pt_regs *regs = task_pt_regs(child); | |
132 | struct thread_info *ti = task_thread_info(child); | |
133 | elf_xtregs_t __user *xtregs = uregs; | |
134 | int ret = 0; | |
135 | ||
136 | if (!access_ok(VERIFY_WRITE, uregs, sizeof(elf_xtregs_t))) | |
137 | return -EIO; | |
138 | ||
139 | #if XTENSA_HAVE_COPROCESSORS | |
140 | /* Flush all coprocessor registers to memory. */ | |
141 | coprocessor_flush_all(ti); | |
142 | ret |= __copy_to_user(&xtregs->cp0, &ti->xtregs_cp, | |
143 | sizeof(xtregs_coprocessor_t)); | |
144 | #endif | |
145 | ret |= __copy_to_user(&xtregs->opt, ®s->xtregs_opt, | |
146 | sizeof(xtregs->opt)); | |
147 | ret |= __copy_to_user(&xtregs->user,&ti->xtregs_user, | |
148 | sizeof(xtregs->user)); | |
5a0015d6 | 149 | |
c658eac6 CZ |
150 | return ret ? -EFAULT : 0; |
151 | } | |
152 | ||
153 | int ptrace_setxregs(struct task_struct *child, void __user *uregs) | |
154 | { | |
155 | struct thread_info *ti = task_thread_info(child); | |
156 | struct pt_regs *regs = task_pt_regs(child); | |
157 | elf_xtregs_t *xtregs = uregs; | |
158 | int ret = 0; | |
159 | ||
0d0138eb DR |
160 | if (!access_ok(VERIFY_READ, uregs, sizeof(elf_xtregs_t))) |
161 | return -EFAULT; | |
162 | ||
c658eac6 CZ |
163 | #if XTENSA_HAVE_COPROCESSORS |
164 | /* Flush all coprocessors before we overwrite them. */ | |
165 | coprocessor_flush_all(ti); | |
166 | coprocessor_release_all(ti); | |
167 | ||
c4c4594b | 168 | ret |= __copy_from_user(&ti->xtregs_cp, &xtregs->cp0, |
c658eac6 CZ |
169 | sizeof(xtregs_coprocessor_t)); |
170 | #endif | |
171 | ret |= __copy_from_user(®s->xtregs_opt, &xtregs->opt, | |
172 | sizeof(xtregs->opt)); | |
173 | ret |= __copy_from_user(&ti->xtregs_user, &xtregs->user, | |
174 | sizeof(xtregs->user)); | |
175 | ||
176 | return ret ? -EFAULT : 0; | |
177 | } | |
178 | ||
179 | int ptrace_peekusr(struct task_struct *child, long regno, long __user *ret) | |
180 | { | |
181 | struct pt_regs *regs; | |
182 | unsigned long tmp; | |
183 | ||
184 | regs = task_pt_regs(child); | |
185 | tmp = 0; /* Default return value. */ | |
5a0015d6 | 186 | |
c658eac6 | 187 | switch(regno) { |
5a0015d6 CZ |
188 | |
189 | case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: | |
c658eac6 | 190 | tmp = regs->areg[regno - REG_AR_BASE]; |
5a0015d6 | 191 | break; |
c658eac6 | 192 | |
5a0015d6 | 193 | case REG_A_BASE ... REG_A_BASE + 15: |
c658eac6 | 194 | tmp = regs->areg[regno - REG_A_BASE]; |
5a0015d6 | 195 | break; |
c658eac6 | 196 | |
5a0015d6 CZ |
197 | case REG_PC: |
198 | tmp = regs->pc; | |
199 | break; | |
c658eac6 | 200 | |
5a0015d6 CZ |
201 | case REG_PS: |
202 | /* Note: PS.EXCM is not set while user task is running; | |
203 | * its being set in regs is for exception handling | |
204 | * convenience. */ | |
173d6681 | 205 | tmp = (regs->ps & ~(1 << PS_EXCM_BIT)); |
5a0015d6 | 206 | break; |
c658eac6 | 207 | |
5a0015d6 | 208 | case REG_WB: |
c658eac6 CZ |
209 | break; /* tmp = 0 */ |
210 | ||
5a0015d6 | 211 | case REG_WS: |
c658eac6 CZ |
212 | { |
213 | unsigned long wb = regs->windowbase; | |
214 | unsigned long ws = regs->windowstart; | |
215 | tmp = ((ws>>wb) | (ws<<(WSBITS-wb))) & ((1<<WSBITS)-1); | |
5a0015d6 | 216 | break; |
c658eac6 | 217 | } |
5a0015d6 CZ |
218 | case REG_LBEG: |
219 | tmp = regs->lbeg; | |
220 | break; | |
c658eac6 | 221 | |
5a0015d6 CZ |
222 | case REG_LEND: |
223 | tmp = regs->lend; | |
224 | break; | |
c658eac6 | 225 | |
5a0015d6 CZ |
226 | case REG_LCOUNT: |
227 | tmp = regs->lcount; | |
228 | break; | |
c658eac6 | 229 | |
5a0015d6 CZ |
230 | case REG_SAR: |
231 | tmp = regs->sar; | |
232 | break; | |
c658eac6 | 233 | |
5a0015d6 CZ |
234 | case SYSCALL_NR: |
235 | tmp = regs->syscall; | |
236 | break; | |
5a0015d6 | 237 | |
c658eac6 CZ |
238 | default: |
239 | return -EIO; | |
240 | } | |
241 | return put_user(tmp, ret); | |
242 | } | |
5a0015d6 | 243 | |
c658eac6 CZ |
244 | int ptrace_pokeusr(struct task_struct *child, long regno, long val) |
245 | { | |
246 | struct pt_regs *regs; | |
247 | regs = task_pt_regs(child); | |
5a0015d6 | 248 | |
c658eac6 | 249 | switch (regno) { |
5a0015d6 | 250 | case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: |
c658eac6 | 251 | regs->areg[regno - REG_AR_BASE] = val; |
5a0015d6 | 252 | break; |
c658eac6 | 253 | |
5a0015d6 | 254 | case REG_A_BASE ... REG_A_BASE + 15: |
c658eac6 | 255 | regs->areg[regno - REG_A_BASE] = val; |
5a0015d6 | 256 | break; |
c658eac6 | 257 | |
5a0015d6 | 258 | case REG_PC: |
c658eac6 | 259 | regs->pc = val; |
5a0015d6 | 260 | break; |
c658eac6 | 261 | |
5a0015d6 | 262 | case SYSCALL_NR: |
c658eac6 | 263 | regs->syscall = val; |
5a0015d6 | 264 | break; |
5a0015d6 CZ |
265 | |
266 | default: | |
c658eac6 CZ |
267 | return -EIO; |
268 | } | |
269 | return 0; | |
270 | } | |
271 | ||
c91e02bd MF |
272 | #ifdef CONFIG_HAVE_HW_BREAKPOINT |
273 | static void ptrace_hbptriggered(struct perf_event *bp, | |
274 | struct perf_sample_data *data, | |
275 | struct pt_regs *regs) | |
276 | { | |
277 | int i; | |
278 | siginfo_t info; | |
279 | struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp); | |
280 | ||
281 | if (bp->attr.bp_type & HW_BREAKPOINT_X) { | |
282 | for (i = 0; i < XCHAL_NUM_IBREAK; ++i) | |
283 | if (current->thread.ptrace_bp[i] == bp) | |
284 | break; | |
285 | i <<= 1; | |
286 | } else { | |
287 | for (i = 0; i < XCHAL_NUM_DBREAK; ++i) | |
288 | if (current->thread.ptrace_wp[i] == bp) | |
289 | break; | |
290 | i = (i << 1) | 1; | |
291 | } | |
292 | ||
293 | info.si_signo = SIGTRAP; | |
294 | info.si_errno = i; | |
295 | info.si_code = TRAP_HWBKPT; | |
296 | info.si_addr = (void __user *)bkpt->address; | |
297 | ||
298 | force_sig_info(SIGTRAP, &info, current); | |
299 | } | |
300 | ||
301 | static struct perf_event *ptrace_hbp_create(struct task_struct *tsk, int type) | |
302 | { | |
303 | struct perf_event_attr attr; | |
304 | ||
305 | ptrace_breakpoint_init(&attr); | |
306 | ||
307 | /* Initialise fields to sane defaults. */ | |
308 | attr.bp_addr = 0; | |
309 | attr.bp_len = 1; | |
310 | attr.bp_type = type; | |
311 | attr.disabled = 1; | |
312 | ||
313 | return register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL, | |
314 | tsk); | |
315 | } | |
316 | ||
317 | /* | |
318 | * Address bit 0 choose instruction (0) or data (1) break register, bits | |
319 | * 31..1 are the register number. | |
320 | * Both PTRACE_GETHBPREGS and PTRACE_SETHBPREGS transfer two 32-bit words: | |
321 | * address (0) and control (1). | |
322 | * Instruction breakpoint contorl word is 0 to clear breakpoint, 1 to set. | |
323 | * Data breakpoint control word bit 31 is 'trigger on store', bit 30 is | |
324 | * 'trigger on load, bits 29..0 are length. Length 0 is used to clear a | |
325 | * breakpoint. To set a breakpoint length must be a power of 2 in the range | |
326 | * 1..64 and the address must be length-aligned. | |
327 | */ | |
328 | ||
329 | static long ptrace_gethbpregs(struct task_struct *child, long addr, | |
330 | long __user *datap) | |
331 | { | |
332 | struct perf_event *bp; | |
333 | u32 user_data[2] = {0}; | |
334 | bool dbreak = addr & 1; | |
335 | unsigned idx = addr >> 1; | |
336 | ||
337 | if ((!dbreak && idx >= XCHAL_NUM_IBREAK) || | |
338 | (dbreak && idx >= XCHAL_NUM_DBREAK)) | |
339 | return -EINVAL; | |
340 | ||
341 | if (dbreak) | |
342 | bp = child->thread.ptrace_wp[idx]; | |
343 | else | |
344 | bp = child->thread.ptrace_bp[idx]; | |
345 | ||
346 | if (bp) { | |
347 | user_data[0] = bp->attr.bp_addr; | |
348 | user_data[1] = bp->attr.disabled ? 0 : bp->attr.bp_len; | |
349 | if (dbreak) { | |
350 | if (bp->attr.bp_type & HW_BREAKPOINT_R) | |
351 | user_data[1] |= DBREAKC_LOAD_MASK; | |
352 | if (bp->attr.bp_type & HW_BREAKPOINT_W) | |
353 | user_data[1] |= DBREAKC_STOR_MASK; | |
354 | } | |
355 | } | |
356 | ||
357 | if (copy_to_user(datap, user_data, sizeof(user_data))) | |
358 | return -EFAULT; | |
359 | ||
360 | return 0; | |
361 | } | |
362 | ||
363 | static long ptrace_sethbpregs(struct task_struct *child, long addr, | |
364 | long __user *datap) | |
365 | { | |
366 | struct perf_event *bp; | |
367 | struct perf_event_attr attr; | |
368 | u32 user_data[2]; | |
369 | bool dbreak = addr & 1; | |
370 | unsigned idx = addr >> 1; | |
371 | int bp_type = 0; | |
372 | ||
373 | if ((!dbreak && idx >= XCHAL_NUM_IBREAK) || | |
374 | (dbreak && idx >= XCHAL_NUM_DBREAK)) | |
375 | return -EINVAL; | |
376 | ||
377 | if (copy_from_user(user_data, datap, sizeof(user_data))) | |
378 | return -EFAULT; | |
379 | ||
380 | if (dbreak) { | |
381 | bp = child->thread.ptrace_wp[idx]; | |
382 | if (user_data[1] & DBREAKC_LOAD_MASK) | |
383 | bp_type |= HW_BREAKPOINT_R; | |
384 | if (user_data[1] & DBREAKC_STOR_MASK) | |
385 | bp_type |= HW_BREAKPOINT_W; | |
386 | } else { | |
387 | bp = child->thread.ptrace_bp[idx]; | |
388 | bp_type = HW_BREAKPOINT_X; | |
389 | } | |
390 | ||
391 | if (!bp) { | |
392 | bp = ptrace_hbp_create(child, | |
393 | bp_type ? bp_type : HW_BREAKPOINT_RW); | |
394 | if (IS_ERR(bp)) | |
395 | return PTR_ERR(bp); | |
396 | if (dbreak) | |
397 | child->thread.ptrace_wp[idx] = bp; | |
398 | else | |
399 | child->thread.ptrace_bp[idx] = bp; | |
400 | } | |
401 | ||
402 | attr = bp->attr; | |
403 | attr.bp_addr = user_data[0]; | |
404 | attr.bp_len = user_data[1] & ~(DBREAKC_LOAD_MASK | DBREAKC_STOR_MASK); | |
405 | attr.bp_type = bp_type; | |
406 | attr.disabled = !attr.bp_len; | |
407 | ||
408 | return modify_user_hw_breakpoint(bp, &attr); | |
409 | } | |
410 | #endif | |
411 | ||
9b05a69e NK |
412 | long arch_ptrace(struct task_struct *child, long request, |
413 | unsigned long addr, unsigned long data) | |
c658eac6 CZ |
414 | { |
415 | int ret = -EPERM; | |
5ef45079 | 416 | void __user *datap = (void __user *) data; |
c658eac6 CZ |
417 | |
418 | switch (request) { | |
419 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | |
420 | case PTRACE_PEEKDATA: | |
421 | ret = generic_ptrace_peekdata(child, addr, data); | |
422 | break; | |
423 | ||
424 | case PTRACE_PEEKUSR: /* read register specified by addr. */ | |
5ef45079 | 425 | ret = ptrace_peekusr(child, addr, datap); |
c658eac6 CZ |
426 | break; |
427 | ||
428 | case PTRACE_POKETEXT: /* write the word at location addr. */ | |
429 | case PTRACE_POKEDATA: | |
430 | ret = generic_ptrace_pokedata(child, addr, data); | |
431 | break; | |
432 | ||
433 | case PTRACE_POKEUSR: /* write register specified by addr. */ | |
434 | ret = ptrace_pokeusr(child, addr, data); | |
5a0015d6 | 435 | break; |
5a0015d6 | 436 | |
5a0015d6 | 437 | case PTRACE_GETREGS: |
5ef45079 | 438 | ret = ptrace_getregs(child, datap); |
5a0015d6 | 439 | break; |
5a0015d6 CZ |
440 | |
441 | case PTRACE_SETREGS: | |
5ef45079 | 442 | ret = ptrace_setregs(child, datap); |
5a0015d6 | 443 | break; |
5a0015d6 | 444 | |
c658eac6 | 445 | case PTRACE_GETXTREGS: |
5ef45079 | 446 | ret = ptrace_getxregs(child, datap); |
5a0015d6 | 447 | break; |
5a0015d6 | 448 | |
c658eac6 | 449 | case PTRACE_SETXTREGS: |
5ef45079 | 450 | ret = ptrace_setxregs(child, datap); |
5a0015d6 | 451 | break; |
c91e02bd MF |
452 | #ifdef CONFIG_HAVE_HW_BREAKPOINT |
453 | case PTRACE_GETHBPREGS: | |
454 | ret = ptrace_gethbpregs(child, addr, datap); | |
455 | break; | |
5a0015d6 | 456 | |
c91e02bd MF |
457 | case PTRACE_SETHBPREGS: |
458 | ret = ptrace_sethbpregs(child, addr, datap); | |
459 | break; | |
460 | #endif | |
5a0015d6 CZ |
461 | default: |
462 | ret = ptrace_request(child, request, addr, data); | |
c658eac6 | 463 | break; |
5a0015d6 | 464 | } |
c658eac6 | 465 | |
5a0015d6 CZ |
466 | return ret; |
467 | } | |
468 | ||
469 | void do_syscall_trace(void) | |
470 | { | |
5a0015d6 CZ |
471 | /* |
472 | * The 0x80 provides a way for the tracing parent to distinguish | |
473 | * between a syscall stop and SIGTRAP delivery | |
474 | */ | |
475 | ptrace_notify(SIGTRAP|((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0)); | |
476 | ||
477 | /* | |
478 | * this isn't the same as continuing with a signal, but it will do | |
479 | * for normal use. strace only continues with a signal if the | |
480 | * stopping signal is not SIGTRAP. -brl | |
481 | */ | |
482 | if (current->exit_code) { | |
483 | send_sig(current->exit_code, current, 1); | |
484 | current->exit_code = 0; | |
485 | } | |
486 | } | |
fc4fb2ad CZ |
487 | |
488 | void do_syscall_trace_enter(struct pt_regs *regs) | |
489 | { | |
490 | if (test_thread_flag(TIF_SYSCALL_TRACE) | |
491 | && (current->ptrace & PT_PTRACED)) | |
492 | do_syscall_trace(); | |
493 | ||
494 | #if 0 | |
91397401 | 495 | audit_syscall_entry(...); |
fc4fb2ad CZ |
496 | #endif |
497 | } | |
498 | ||
499 | void do_syscall_trace_leave(struct pt_regs *regs) | |
500 | { | |
501 | if ((test_thread_flag(TIF_SYSCALL_TRACE)) | |
502 | && (current->ptrace & PT_PTRACED)) | |
503 | do_syscall_trace(); | |
504 | } |