diff options
Diffstat (limited to 'arch/arm/kernel/ptrace.c')
-rw-r--r-- | arch/arm/kernel/ptrace.c | 946 |
1 files changed, 472 insertions, 474 deletions
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index f99d489822d5..97260060bf26 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c | |||
@@ -19,13 +19,14 @@ | |||
19 | #include <linux/init.h> | 19 | #include <linux/init.h> |
20 | #include <linux/signal.h> | 20 | #include <linux/signal.h> |
21 | #include <linux/uaccess.h> | 21 | #include <linux/uaccess.h> |
22 | #include <linux/perf_event.h> | ||
23 | #include <linux/hw_breakpoint.h> | ||
24 | #include <linux/regset.h> | ||
22 | 25 | ||
23 | #include <asm/pgtable.h> | 26 | #include <asm/pgtable.h> |
24 | #include <asm/system.h> | 27 | #include <asm/system.h> |
25 | #include <asm/traps.h> | 28 | #include <asm/traps.h> |
26 | 29 | ||
27 | #include "ptrace.h" | ||
28 | |||
29 | #define REG_PC 15 | 30 | #define REG_PC 15 |
30 | #define REG_PSR 16 | 31 | #define REG_PSR 16 |
31 | /* | 32 | /* |
@@ -182,389 +183,12 @@ put_user_reg(struct task_struct *task, int offset, long data) | |||
182 | return ret; | 183 | return ret; |
183 | } | 184 | } |
184 | 185 | ||
185 | static inline int | ||
186 | read_u32(struct task_struct *task, unsigned long addr, u32 *res) | ||
187 | { | ||
188 | int ret; | ||
189 | |||
190 | ret = access_process_vm(task, addr, res, sizeof(*res), 0); | ||
191 | |||
192 | return ret == sizeof(*res) ? 0 : -EIO; | ||
193 | } | ||
194 | |||
195 | static inline int | ||
196 | read_instr(struct task_struct *task, unsigned long addr, u32 *res) | ||
197 | { | ||
198 | int ret; | ||
199 | |||
200 | if (addr & 1) { | ||
201 | u16 val; | ||
202 | ret = access_process_vm(task, addr & ~1, &val, sizeof(val), 0); | ||
203 | ret = ret == sizeof(val) ? 0 : -EIO; | ||
204 | *res = val; | ||
205 | } else { | ||
206 | u32 val; | ||
207 | ret = access_process_vm(task, addr & ~3, &val, sizeof(val), 0); | ||
208 | ret = ret == sizeof(val) ? 0 : -EIO; | ||
209 | *res = val; | ||
210 | } | ||
211 | return ret; | ||
212 | } | ||
213 | |||
214 | /* | ||
215 | * Get value of register `rn' (in the instruction) | ||
216 | */ | ||
217 | static unsigned long | ||
218 | ptrace_getrn(struct task_struct *child, unsigned long insn) | ||
219 | { | ||
220 | unsigned int reg = (insn >> 16) & 15; | ||
221 | unsigned long val; | ||
222 | |||
223 | val = get_user_reg(child, reg); | ||
224 | if (reg == 15) | ||
225 | val += 8; | ||
226 | |||
227 | return val; | ||
228 | } | ||
229 | |||
230 | /* | ||
231 | * Get value of operand 2 (in an ALU instruction) | ||
232 | */ | ||
233 | static unsigned long | ||
234 | ptrace_getaluop2(struct task_struct *child, unsigned long insn) | ||
235 | { | ||
236 | unsigned long val; | ||
237 | int shift; | ||
238 | int type; | ||
239 | |||
240 | if (insn & 1 << 25) { | ||
241 | val = insn & 255; | ||
242 | shift = (insn >> 8) & 15; | ||
243 | type = 3; | ||
244 | } else { | ||
245 | val = get_user_reg (child, insn & 15); | ||
246 | |||
247 | if (insn & (1 << 4)) | ||
248 | shift = (int)get_user_reg (child, (insn >> 8) & 15); | ||
249 | else | ||
250 | shift = (insn >> 7) & 31; | ||
251 | |||
252 | type = (insn >> 5) & 3; | ||
253 | } | ||
254 | |||
255 | switch (type) { | ||
256 | case 0: val <<= shift; break; | ||
257 | case 1: val >>= shift; break; | ||
258 | case 2: | ||
259 | val = (((signed long)val) >> shift); | ||
260 | break; | ||
261 | case 3: | ||
262 | val = (val >> shift) | (val << (32 - shift)); | ||
263 | break; | ||
264 | } | ||
265 | return val; | ||
266 | } | ||
267 | |||
268 | /* | ||
269 | * Get value of operand 2 (in a LDR instruction) | ||
270 | */ | ||
271 | static unsigned long | ||
272 | ptrace_getldrop2(struct task_struct *child, unsigned long insn) | ||
273 | { | ||
274 | unsigned long val; | ||
275 | int shift; | ||
276 | int type; | ||
277 | |||
278 | val = get_user_reg(child, insn & 15); | ||
279 | shift = (insn >> 7) & 31; | ||
280 | type = (insn >> 5) & 3; | ||
281 | |||
282 | switch (type) { | ||
283 | case 0: val <<= shift; break; | ||
284 | case 1: val >>= shift; break; | ||
285 | case 2: | ||
286 | val = (((signed long)val) >> shift); | ||
287 | break; | ||
288 | case 3: | ||
289 | val = (val >> shift) | (val << (32 - shift)); | ||
290 | break; | ||
291 | } | ||
292 | return val; | ||
293 | } | ||
294 | |||
295 | #define OP_MASK 0x01e00000 | ||
296 | #define OP_AND 0x00000000 | ||
297 | #define OP_EOR 0x00200000 | ||
298 | #define OP_SUB 0x00400000 | ||
299 | #define OP_RSB 0x00600000 | ||
300 | #define OP_ADD 0x00800000 | ||
301 | #define OP_ADC 0x00a00000 | ||
302 | #define OP_SBC 0x00c00000 | ||
303 | #define OP_RSC 0x00e00000 | ||
304 | #define OP_ORR 0x01800000 | ||
305 | #define OP_MOV 0x01a00000 | ||
306 | #define OP_BIC 0x01c00000 | ||
307 | #define OP_MVN 0x01e00000 | ||
308 | |||
309 | static unsigned long | ||
310 | get_branch_address(struct task_struct *child, unsigned long pc, unsigned long insn) | ||
311 | { | ||
312 | u32 alt = 0; | ||
313 | |||
314 | switch (insn & 0x0e000000) { | ||
315 | case 0x00000000: | ||
316 | case 0x02000000: { | ||
317 | /* | ||
318 | * data processing | ||
319 | */ | ||
320 | long aluop1, aluop2, ccbit; | ||
321 | |||
322 | if ((insn & 0x0fffffd0) == 0x012fff10) { | ||
323 | /* | ||
324 | * bx or blx | ||
325 | */ | ||
326 | alt = get_user_reg(child, insn & 15); | ||
327 | break; | ||
328 | } | ||
329 | |||
330 | |||
331 | if ((insn & 0xf000) != 0xf000) | ||
332 | break; | ||
333 | |||
334 | aluop1 = ptrace_getrn(child, insn); | ||
335 | aluop2 = ptrace_getaluop2(child, insn); | ||
336 | ccbit = get_user_reg(child, REG_PSR) & PSR_C_BIT ? 1 : 0; | ||
337 | |||
338 | switch (insn & OP_MASK) { | ||
339 | case OP_AND: alt = aluop1 & aluop2; break; | ||
340 | case OP_EOR: alt = aluop1 ^ aluop2; break; | ||
341 | case OP_SUB: alt = aluop1 - aluop2; break; | ||
342 | case OP_RSB: alt = aluop2 - aluop1; break; | ||
343 | case OP_ADD: alt = aluop1 + aluop2; break; | ||
344 | case OP_ADC: alt = aluop1 + aluop2 + ccbit; break; | ||
345 | case OP_SBC: alt = aluop1 - aluop2 + ccbit; break; | ||
346 | case OP_RSC: alt = aluop2 - aluop1 + ccbit; break; | ||
347 | case OP_ORR: alt = aluop1 | aluop2; break; | ||
348 | case OP_MOV: alt = aluop2; break; | ||
349 | case OP_BIC: alt = aluop1 & ~aluop2; break; | ||
350 | case OP_MVN: alt = ~aluop2; break; | ||
351 | } | ||
352 | break; | ||
353 | } | ||
354 | |||
355 | case 0x04000000: | ||
356 | case 0x06000000: | ||
357 | /* | ||
358 | * ldr | ||
359 | */ | ||
360 | if ((insn & 0x0010f000) == 0x0010f000) { | ||
361 | unsigned long base; | ||
362 | |||
363 | base = ptrace_getrn(child, insn); | ||
364 | if (insn & 1 << 24) { | ||
365 | long aluop2; | ||
366 | |||
367 | if (insn & 0x02000000) | ||
368 | aluop2 = ptrace_getldrop2(child, insn); | ||
369 | else | ||
370 | aluop2 = insn & 0xfff; | ||
371 | |||
372 | if (insn & 1 << 23) | ||
373 | base += aluop2; | ||
374 | else | ||
375 | base -= aluop2; | ||
376 | } | ||
377 | read_u32(child, base, &alt); | ||
378 | } | ||
379 | break; | ||
380 | |||
381 | case 0x08000000: | ||
382 | /* | ||
383 | * ldm | ||
384 | */ | ||
385 | if ((insn & 0x00108000) == 0x00108000) { | ||
386 | unsigned long base; | ||
387 | unsigned int nr_regs; | ||
388 | |||
389 | if (insn & (1 << 23)) { | ||
390 | nr_regs = hweight16(insn & 65535) << 2; | ||
391 | |||
392 | if (!(insn & (1 << 24))) | ||
393 | nr_regs -= 4; | ||
394 | } else { | ||
395 | if (insn & (1 << 24)) | ||
396 | nr_regs = -4; | ||
397 | else | ||
398 | nr_regs = 0; | ||
399 | } | ||
400 | |||
401 | base = ptrace_getrn(child, insn); | ||
402 | |||
403 | read_u32(child, base + nr_regs, &alt); | ||
404 | break; | ||
405 | } | ||
406 | break; | ||
407 | |||
408 | case 0x0a000000: { | ||
409 | /* | ||
410 | * bl or b | ||
411 | */ | ||
412 | signed long displ; | ||
413 | /* It's a branch/branch link: instead of trying to | ||
414 | * figure out whether the branch will be taken or not, | ||
415 | * we'll put a breakpoint at both locations. This is | ||
416 | * simpler, more reliable, and probably not a whole lot | ||
417 | * slower than the alternative approach of emulating the | ||
418 | * branch. | ||
419 | */ | ||
420 | displ = (insn & 0x00ffffff) << 8; | ||
421 | displ = (displ >> 6) + 8; | ||
422 | if (displ != 0 && displ != 4) | ||
423 | alt = pc + displ; | ||
424 | } | ||
425 | break; | ||
426 | } | ||
427 | |||
428 | return alt; | ||
429 | } | ||
430 | |||
431 | static int | ||
432 | swap_insn(struct task_struct *task, unsigned long addr, | ||
433 | void *old_insn, void *new_insn, int size) | ||
434 | { | ||
435 | int ret; | ||
436 | |||
437 | ret = access_process_vm(task, addr, old_insn, size, 0); | ||
438 | if (ret == size) | ||
439 | ret = access_process_vm(task, addr, new_insn, size, 1); | ||
440 | return ret; | ||
441 | } | ||
442 | |||
443 | static void | ||
444 | add_breakpoint(struct task_struct *task, struct debug_info *dbg, unsigned long addr) | ||
445 | { | ||
446 | int nr = dbg->nsaved; | ||
447 | |||
448 | if (nr < 2) { | ||
449 | u32 new_insn = BREAKINST_ARM; | ||
450 | int res; | ||
451 | |||
452 | res = swap_insn(task, addr, &dbg->bp[nr].insn, &new_insn, 4); | ||
453 | |||
454 | if (res == 4) { | ||
455 | dbg->bp[nr].address = addr; | ||
456 | dbg->nsaved += 1; | ||
457 | } | ||
458 | } else | ||
459 | printk(KERN_ERR "ptrace: too many breakpoints\n"); | ||
460 | } | ||
461 | |||
462 | /* | ||
463 | * Clear one breakpoint in the user program. We copy what the hardware | ||
464 | * does and use bit 0 of the address to indicate whether this is a Thumb | ||
465 | * breakpoint or an ARM breakpoint. | ||
466 | */ | ||
467 | static void clear_breakpoint(struct task_struct *task, struct debug_entry *bp) | ||
468 | { | ||
469 | unsigned long addr = bp->address; | ||
470 | union debug_insn old_insn; | ||
471 | int ret; | ||
472 | |||
473 | if (addr & 1) { | ||
474 | ret = swap_insn(task, addr & ~1, &old_insn.thumb, | ||
475 | &bp->insn.thumb, 2); | ||
476 | |||
477 | if (ret != 2 || old_insn.thumb != BREAKINST_THUMB) | ||
478 | printk(KERN_ERR "%s:%d: corrupted Thumb breakpoint at " | ||
479 | "0x%08lx (0x%04x)\n", task->comm, | ||
480 | task_pid_nr(task), addr, old_insn.thumb); | ||
481 | } else { | ||
482 | ret = swap_insn(task, addr & ~3, &old_insn.arm, | ||
483 | &bp->insn.arm, 4); | ||
484 | |||
485 | if (ret != 4 || old_insn.arm != BREAKINST_ARM) | ||
486 | printk(KERN_ERR "%s:%d: corrupted ARM breakpoint at " | ||
487 | "0x%08lx (0x%08x)\n", task->comm, | ||
488 | task_pid_nr(task), addr, old_insn.arm); | ||
489 | } | ||
490 | } | ||
491 | |||
492 | void ptrace_set_bpt(struct task_struct *child) | ||
493 | { | ||
494 | struct pt_regs *regs; | ||
495 | unsigned long pc; | ||
496 | u32 insn; | ||
497 | int res; | ||
498 | |||
499 | regs = task_pt_regs(child); | ||
500 | pc = instruction_pointer(regs); | ||
501 | |||
502 | if (thumb_mode(regs)) { | ||
503 | printk(KERN_WARNING "ptrace: can't handle thumb mode\n"); | ||
504 | return; | ||
505 | } | ||
506 | |||
507 | res = read_instr(child, pc, &insn); | ||
508 | if (!res) { | ||
509 | struct debug_info *dbg = &child->thread.debug; | ||
510 | unsigned long alt; | ||
511 | |||
512 | dbg->nsaved = 0; | ||
513 | |||
514 | alt = get_branch_address(child, pc, insn); | ||
515 | if (alt) | ||
516 | add_breakpoint(child, dbg, alt); | ||
517 | |||
518 | /* | ||
519 | * Note that we ignore the result of setting the above | ||
520 | * breakpoint since it may fail. When it does, this is | ||
521 | * not so much an error, but a forewarning that we may | ||
522 | * be receiving a prefetch abort shortly. | ||
523 | * | ||
524 | * If we don't set this breakpoint here, then we can | ||
525 | * lose control of the thread during single stepping. | ||
526 | */ | ||
527 | if (!alt || predicate(insn) != PREDICATE_ALWAYS) | ||
528 | add_breakpoint(child, dbg, pc + 4); | ||
529 | } | ||
530 | } | ||
531 | |||
532 | /* | ||
533 | * Ensure no single-step breakpoint is pending. Returns non-zero | ||
534 | * value if child was being single-stepped. | ||
535 | */ | ||
536 | void ptrace_cancel_bpt(struct task_struct *child) | ||
537 | { | ||
538 | int i, nsaved = child->thread.debug.nsaved; | ||
539 | |||
540 | child->thread.debug.nsaved = 0; | ||
541 | |||
542 | if (nsaved > 2) { | ||
543 | printk("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved); | ||
544 | nsaved = 2; | ||
545 | } | ||
546 | |||
547 | for (i = 0; i < nsaved; i++) | ||
548 | clear_breakpoint(child, &child->thread.debug.bp[i]); | ||
549 | } | ||
550 | |||
551 | void user_disable_single_step(struct task_struct *task) | ||
552 | { | ||
553 | task->ptrace &= ~PT_SINGLESTEP; | ||
554 | ptrace_cancel_bpt(task); | ||
555 | } | ||
556 | |||
557 | void user_enable_single_step(struct task_struct *task) | ||
558 | { | ||
559 | task->ptrace |= PT_SINGLESTEP; | ||
560 | } | ||
561 | |||
562 | /* | 186 | /* |
563 | * Called by kernel/ptrace.c when detaching.. | 187 | * Called by kernel/ptrace.c when detaching.. |
564 | */ | 188 | */ |
565 | void ptrace_disable(struct task_struct *child) | 189 | void ptrace_disable(struct task_struct *child) |
566 | { | 190 | { |
567 | user_disable_single_step(child); | 191 | /* Nothing to do. */ |
568 | } | 192 | } |
569 | 193 | ||
570 | /* | 194 | /* |
@@ -574,8 +198,6 @@ void ptrace_break(struct task_struct *tsk, struct pt_regs *regs) | |||
574 | { | 198 | { |
575 | siginfo_t info; | 199 | siginfo_t info; |
576 | 200 | ||
577 | ptrace_cancel_bpt(tsk); | ||
578 | |||
579 | info.si_signo = SIGTRAP; | 201 | info.si_signo = SIGTRAP; |
580 | info.si_errno = 0; | 202 | info.si_errno = 0; |
581 | info.si_code = TRAP_BRKPT; | 203 | info.si_code = TRAP_BRKPT; |
@@ -687,58 +309,6 @@ static int ptrace_write_user(struct task_struct *tsk, unsigned long off, | |||
687 | return put_user_reg(tsk, off >> 2, val); | 309 | return put_user_reg(tsk, off >> 2, val); |
688 | } | 310 | } |
689 | 311 | ||
690 | /* | ||
691 | * Get all user integer registers. | ||
692 | */ | ||
693 | static int ptrace_getregs(struct task_struct *tsk, void __user *uregs) | ||
694 | { | ||
695 | struct pt_regs *regs = task_pt_regs(tsk); | ||
696 | |||
697 | return copy_to_user(uregs, regs, sizeof(struct pt_regs)) ? -EFAULT : 0; | ||
698 | } | ||
699 | |||
700 | /* | ||
701 | * Set all user integer registers. | ||
702 | */ | ||
703 | static int ptrace_setregs(struct task_struct *tsk, void __user *uregs) | ||
704 | { | ||
705 | struct pt_regs newregs; | ||
706 | int ret; | ||
707 | |||
708 | ret = -EFAULT; | ||
709 | if (copy_from_user(&newregs, uregs, sizeof(struct pt_regs)) == 0) { | ||
710 | struct pt_regs *regs = task_pt_regs(tsk); | ||
711 | |||
712 | ret = -EINVAL; | ||
713 | if (valid_user_regs(&newregs)) { | ||
714 | *regs = newregs; | ||
715 | ret = 0; | ||
716 | } | ||
717 | } | ||
718 | |||
719 | return ret; | ||
720 | } | ||
721 | |||
722 | /* | ||
723 | * Get the child FPU state. | ||
724 | */ | ||
725 | static int ptrace_getfpregs(struct task_struct *tsk, void __user *ufp) | ||
726 | { | ||
727 | return copy_to_user(ufp, &task_thread_info(tsk)->fpstate, | ||
728 | sizeof(struct user_fp)) ? -EFAULT : 0; | ||
729 | } | ||
730 | |||
731 | /* | ||
732 | * Set the child FPU state. | ||
733 | */ | ||
734 | static int ptrace_setfpregs(struct task_struct *tsk, void __user *ufp) | ||
735 | { | ||
736 | struct thread_info *thread = task_thread_info(tsk); | ||
737 | thread->used_cp[1] = thread->used_cp[2] = 1; | ||
738 | return copy_from_user(&thread->fpstate, ufp, | ||
739 | sizeof(struct user_fp)) ? -EFAULT : 0; | ||
740 | } | ||
741 | |||
742 | #ifdef CONFIG_IWMMXT | 312 | #ifdef CONFIG_IWMMXT |
743 | 313 | ||
744 | /* | 314 | /* |
@@ -797,63 +367,454 @@ static int ptrace_setcrunchregs(struct task_struct *tsk, void __user *ufp) | |||
797 | } | 367 | } |
798 | #endif | 368 | #endif |
799 | 369 | ||
800 | #ifdef CONFIG_VFP | 370 | #ifdef CONFIG_HAVE_HW_BREAKPOINT |
801 | /* | 371 | /* |
802 | * Get the child VFP state. | 372 | * Convert a virtual register number into an index for a thread_info |
373 | * breakpoint array. Breakpoints are identified using positive numbers | ||
374 | * whilst watchpoints are negative. The registers are laid out as pairs | ||
375 | * of (address, control), each pair mapping to a unique hw_breakpoint struct. | ||
376 | * Register 0 is reserved for describing resource information. | ||
803 | */ | 377 | */ |
804 | static int ptrace_getvfpregs(struct task_struct *tsk, void __user *data) | 378 | static int ptrace_hbp_num_to_idx(long num) |
805 | { | 379 | { |
806 | struct thread_info *thread = task_thread_info(tsk); | 380 | if (num < 0) |
807 | union vfp_state *vfp = &thread->vfpstate; | 381 | num = (ARM_MAX_BRP << 1) - num; |
808 | struct user_vfp __user *ufp = data; | 382 | return (num - 1) >> 1; |
383 | } | ||
809 | 384 | ||
810 | vfp_sync_hwstate(thread); | 385 | /* |
386 | * Returns the virtual register number for the address of the | ||
387 | * breakpoint at index idx. | ||
388 | */ | ||
389 | static long ptrace_hbp_idx_to_num(int idx) | ||
390 | { | ||
391 | long mid = ARM_MAX_BRP << 1; | ||
392 | long num = (idx << 1) + 1; | ||
393 | return num > mid ? mid - num : num; | ||
394 | } | ||
395 | |||
396 | /* | ||
397 | * Handle hitting a HW-breakpoint. | ||
398 | */ | ||
399 | static void ptrace_hbptriggered(struct perf_event *bp, int unused, | ||
400 | struct perf_sample_data *data, | ||
401 | struct pt_regs *regs) | ||
402 | { | ||
403 | struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp); | ||
404 | long num; | ||
405 | int i; | ||
406 | siginfo_t info; | ||
407 | |||
408 | for (i = 0; i < ARM_MAX_HBP_SLOTS; ++i) | ||
409 | if (current->thread.debug.hbp[i] == bp) | ||
410 | break; | ||
811 | 411 | ||
812 | /* copy the floating point registers */ | 412 | num = (i == ARM_MAX_HBP_SLOTS) ? 0 : ptrace_hbp_idx_to_num(i); |
813 | if (copy_to_user(&ufp->fpregs, &vfp->hard.fpregs, | ||
814 | sizeof(vfp->hard.fpregs))) | ||
815 | return -EFAULT; | ||
816 | 413 | ||
817 | /* copy the status and control register */ | 414 | info.si_signo = SIGTRAP; |
818 | if (put_user(vfp->hard.fpscr, &ufp->fpscr)) | 415 | info.si_errno = (int)num; |
819 | return -EFAULT; | 416 | info.si_code = TRAP_HWBKPT; |
417 | info.si_addr = (void __user *)(bkpt->trigger); | ||
820 | 418 | ||
419 | force_sig_info(SIGTRAP, &info, current); | ||
420 | } | ||
421 | |||
422 | /* | ||
423 | * Set ptrace breakpoint pointers to zero for this task. | ||
424 | * This is required in order to prevent child processes from unregistering | ||
425 | * breakpoints held by their parent. | ||
426 | */ | ||
427 | void clear_ptrace_hw_breakpoint(struct task_struct *tsk) | ||
428 | { | ||
429 | memset(tsk->thread.debug.hbp, 0, sizeof(tsk->thread.debug.hbp)); | ||
430 | } | ||
431 | |||
432 | /* | ||
433 | * Unregister breakpoints from this task and reset the pointers in | ||
434 | * the thread_struct. | ||
435 | */ | ||
436 | void flush_ptrace_hw_breakpoint(struct task_struct *tsk) | ||
437 | { | ||
438 | int i; | ||
439 | struct thread_struct *t = &tsk->thread; | ||
440 | |||
441 | for (i = 0; i < ARM_MAX_HBP_SLOTS; i++) { | ||
442 | if (t->debug.hbp[i]) { | ||
443 | unregister_hw_breakpoint(t->debug.hbp[i]); | ||
444 | t->debug.hbp[i] = NULL; | ||
445 | } | ||
446 | } | ||
447 | } | ||
448 | |||
449 | static u32 ptrace_get_hbp_resource_info(void) | ||
450 | { | ||
451 | u8 num_brps, num_wrps, debug_arch, wp_len; | ||
452 | u32 reg = 0; | ||
453 | |||
454 | num_brps = hw_breakpoint_slots(TYPE_INST); | ||
455 | num_wrps = hw_breakpoint_slots(TYPE_DATA); | ||
456 | debug_arch = arch_get_debug_arch(); | ||
457 | wp_len = arch_get_max_wp_len(); | ||
458 | |||
459 | reg |= debug_arch; | ||
460 | reg <<= 8; | ||
461 | reg |= wp_len; | ||
462 | reg <<= 8; | ||
463 | reg |= num_wrps; | ||
464 | reg <<= 8; | ||
465 | reg |= num_brps; | ||
466 | |||
467 | return reg; | ||
468 | } | ||
469 | |||
470 | static struct perf_event *ptrace_hbp_create(struct task_struct *tsk, int type) | ||
471 | { | ||
472 | struct perf_event_attr attr; | ||
473 | |||
474 | ptrace_breakpoint_init(&attr); | ||
475 | |||
476 | /* Initialise fields to sane defaults. */ | ||
477 | attr.bp_addr = 0; | ||
478 | attr.bp_len = HW_BREAKPOINT_LEN_4; | ||
479 | attr.bp_type = type; | ||
480 | attr.disabled = 1; | ||
481 | |||
482 | return register_user_hw_breakpoint(&attr, ptrace_hbptriggered, tsk); | ||
483 | } | ||
484 | |||
485 | static int ptrace_gethbpregs(struct task_struct *tsk, long num, | ||
486 | unsigned long __user *data) | ||
487 | { | ||
488 | u32 reg; | ||
489 | int idx, ret = 0; | ||
490 | struct perf_event *bp; | ||
491 | struct arch_hw_breakpoint_ctrl arch_ctrl; | ||
492 | |||
493 | if (num == 0) { | ||
494 | reg = ptrace_get_hbp_resource_info(); | ||
495 | } else { | ||
496 | idx = ptrace_hbp_num_to_idx(num); | ||
497 | if (idx < 0 || idx >= ARM_MAX_HBP_SLOTS) { | ||
498 | ret = -EINVAL; | ||
499 | goto out; | ||
500 | } | ||
501 | |||
502 | bp = tsk->thread.debug.hbp[idx]; | ||
503 | if (!bp) { | ||
504 | reg = 0; | ||
505 | goto put; | ||
506 | } | ||
507 | |||
508 | arch_ctrl = counter_arch_bp(bp)->ctrl; | ||
509 | |||
510 | /* | ||
511 | * Fix up the len because we may have adjusted it | ||
512 | * to compensate for an unaligned address. | ||
513 | */ | ||
514 | while (!(arch_ctrl.len & 0x1)) | ||
515 | arch_ctrl.len >>= 1; | ||
516 | |||
517 | if (num & 0x1) | ||
518 | reg = bp->attr.bp_addr; | ||
519 | else | ||
520 | reg = encode_ctrl_reg(arch_ctrl); | ||
521 | } | ||
522 | |||
523 | put: | ||
524 | if (put_user(reg, data)) | ||
525 | ret = -EFAULT; | ||
526 | |||
527 | out: | ||
528 | return ret; | ||
529 | } | ||
530 | |||
531 | static int ptrace_sethbpregs(struct task_struct *tsk, long num, | ||
532 | unsigned long __user *data) | ||
533 | { | ||
534 | int idx, gen_len, gen_type, implied_type, ret = 0; | ||
535 | u32 user_val; | ||
536 | struct perf_event *bp; | ||
537 | struct arch_hw_breakpoint_ctrl ctrl; | ||
538 | struct perf_event_attr attr; | ||
539 | |||
540 | if (num == 0) | ||
541 | goto out; | ||
542 | else if (num < 0) | ||
543 | implied_type = HW_BREAKPOINT_RW; | ||
544 | else | ||
545 | implied_type = HW_BREAKPOINT_X; | ||
546 | |||
547 | idx = ptrace_hbp_num_to_idx(num); | ||
548 | if (idx < 0 || idx >= ARM_MAX_HBP_SLOTS) { | ||
549 | ret = -EINVAL; | ||
550 | goto out; | ||
551 | } | ||
552 | |||
553 | if (get_user(user_val, data)) { | ||
554 | ret = -EFAULT; | ||
555 | goto out; | ||
556 | } | ||
557 | |||
558 | bp = tsk->thread.debug.hbp[idx]; | ||
559 | if (!bp) { | ||
560 | bp = ptrace_hbp_create(tsk, implied_type); | ||
561 | if (IS_ERR(bp)) { | ||
562 | ret = PTR_ERR(bp); | ||
563 | goto out; | ||
564 | } | ||
565 | tsk->thread.debug.hbp[idx] = bp; | ||
566 | } | ||
567 | |||
568 | attr = bp->attr; | ||
569 | |||
570 | if (num & 0x1) { | ||
571 | /* Address */ | ||
572 | attr.bp_addr = user_val; | ||
573 | } else { | ||
574 | /* Control */ | ||
575 | decode_ctrl_reg(user_val, &ctrl); | ||
576 | ret = arch_bp_generic_fields(ctrl, &gen_len, &gen_type); | ||
577 | if (ret) | ||
578 | goto out; | ||
579 | |||
580 | if ((gen_type & implied_type) != gen_type) { | ||
581 | ret = -EINVAL; | ||
582 | goto out; | ||
583 | } | ||
584 | |||
585 | attr.bp_len = gen_len; | ||
586 | attr.bp_type = gen_type; | ||
587 | attr.disabled = !ctrl.enabled; | ||
588 | } | ||
589 | |||
590 | ret = modify_user_hw_breakpoint(bp, &attr); | ||
591 | out: | ||
592 | return ret; | ||
593 | } | ||
594 | #endif | ||
595 | |||
596 | /* regset get/set implementations */ | ||
597 | |||
598 | static int gpr_get(struct task_struct *target, | ||
599 | const struct user_regset *regset, | ||
600 | unsigned int pos, unsigned int count, | ||
601 | void *kbuf, void __user *ubuf) | ||
602 | { | ||
603 | struct pt_regs *regs = task_pt_regs(target); | ||
604 | |||
605 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
606 | regs, | ||
607 | 0, sizeof(*regs)); | ||
608 | } | ||
609 | |||
610 | static int gpr_set(struct task_struct *target, | ||
611 | const struct user_regset *regset, | ||
612 | unsigned int pos, unsigned int count, | ||
613 | const void *kbuf, const void __user *ubuf) | ||
614 | { | ||
615 | int ret; | ||
616 | struct pt_regs newregs; | ||
617 | |||
618 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
619 | &newregs, | ||
620 | 0, sizeof(newregs)); | ||
621 | if (ret) | ||
622 | return ret; | ||
623 | |||
624 | if (!valid_user_regs(&newregs)) | ||
625 | return -EINVAL; | ||
626 | |||
627 | *task_pt_regs(target) = newregs; | ||
821 | return 0; | 628 | return 0; |
822 | } | 629 | } |
823 | 630 | ||
631 | static int fpa_get(struct task_struct *target, | ||
632 | const struct user_regset *regset, | ||
633 | unsigned int pos, unsigned int count, | ||
634 | void *kbuf, void __user *ubuf) | ||
635 | { | ||
636 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
637 | &task_thread_info(target)->fpstate, | ||
638 | 0, sizeof(struct user_fp)); | ||
639 | } | ||
640 | |||
641 | static int fpa_set(struct task_struct *target, | ||
642 | const struct user_regset *regset, | ||
643 | unsigned int pos, unsigned int count, | ||
644 | const void *kbuf, const void __user *ubuf) | ||
645 | { | ||
646 | struct thread_info *thread = task_thread_info(target); | ||
647 | |||
648 | thread->used_cp[1] = thread->used_cp[2] = 1; | ||
649 | |||
650 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
651 | &thread->fpstate, | ||
652 | 0, sizeof(struct user_fp)); | ||
653 | } | ||
654 | |||
655 | #ifdef CONFIG_VFP | ||
824 | /* | 656 | /* |
825 | * Set the child VFP state. | 657 | * VFP register get/set implementations. |
658 | * | ||
659 | * With respect to the kernel, struct user_fp is divided into three chunks: | ||
660 | * 16 or 32 real VFP registers (d0-d15 or d0-31) | ||
661 | * These are transferred to/from the real registers in the task's | ||
662 | * vfp_hard_struct. The number of registers depends on the kernel | ||
663 | * configuration. | ||
664 | * | ||
665 | * 16 or 0 fake VFP registers (d16-d31 or empty) | ||
666 | * i.e., the user_vfp structure has space for 32 registers even if | ||
667 | * the kernel doesn't have them all. | ||
668 | * | ||
669 | * vfp_get() reads this chunk as zero where applicable | ||
670 | * vfp_set() ignores this chunk | ||
671 | * | ||
672 | * 1 word for the FPSCR | ||
673 | * | ||
674 | * The bounds-checking logic built into user_regset_copyout and friends | ||
675 | * means that we can make a simple sequence of calls to map the relevant data | ||
676 | * to/from the specified slice of the user regset structure. | ||
826 | */ | 677 | */ |
827 | static int ptrace_setvfpregs(struct task_struct *tsk, void __user *data) | 678 | static int vfp_get(struct task_struct *target, |
679 | const struct user_regset *regset, | ||
680 | unsigned int pos, unsigned int count, | ||
681 | void *kbuf, void __user *ubuf) | ||
828 | { | 682 | { |
829 | struct thread_info *thread = task_thread_info(tsk); | 683 | int ret; |
830 | union vfp_state *vfp = &thread->vfpstate; | 684 | struct thread_info *thread = task_thread_info(target); |
831 | struct user_vfp __user *ufp = data; | 685 | struct vfp_hard_struct const *vfp = &thread->vfpstate.hard; |
686 | const size_t user_fpregs_offset = offsetof(struct user_vfp, fpregs); | ||
687 | const size_t user_fpscr_offset = offsetof(struct user_vfp, fpscr); | ||
832 | 688 | ||
833 | vfp_sync_hwstate(thread); | 689 | vfp_sync_hwstate(thread); |
834 | 690 | ||
835 | /* copy the floating point registers */ | 691 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
836 | if (copy_from_user(&vfp->hard.fpregs, &ufp->fpregs, | 692 | &vfp->fpregs, |
837 | sizeof(vfp->hard.fpregs))) | 693 | user_fpregs_offset, |
838 | return -EFAULT; | 694 | user_fpregs_offset + sizeof(vfp->fpregs)); |
695 | if (ret) | ||
696 | return ret; | ||
839 | 697 | ||
840 | /* copy the status and control register */ | 698 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, |
841 | if (get_user(vfp->hard.fpscr, &ufp->fpscr)) | 699 | user_fpregs_offset + sizeof(vfp->fpregs), |
842 | return -EFAULT; | 700 | user_fpscr_offset); |
701 | if (ret) | ||
702 | return ret; | ||
843 | 703 | ||
704 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | ||
705 | &vfp->fpscr, | ||
706 | user_fpscr_offset, | ||
707 | user_fpscr_offset + sizeof(vfp->fpscr)); | ||
708 | } | ||
709 | |||
710 | /* | ||
711 | * For vfp_set() a read-modify-write is done on the VFP registers, | ||
712 | * in order to avoid writing back a half-modified set of registers on | ||
713 | * failure. | ||
714 | */ | ||
715 | static int vfp_set(struct task_struct *target, | ||
716 | const struct user_regset *regset, | ||
717 | unsigned int pos, unsigned int count, | ||
718 | const void *kbuf, const void __user *ubuf) | ||
719 | { | ||
720 | int ret; | ||
721 | struct thread_info *thread = task_thread_info(target); | ||
722 | struct vfp_hard_struct new_vfp = thread->vfpstate.hard; | ||
723 | const size_t user_fpregs_offset = offsetof(struct user_vfp, fpregs); | ||
724 | const size_t user_fpscr_offset = offsetof(struct user_vfp, fpscr); | ||
725 | |||
726 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
727 | &new_vfp.fpregs, | ||
728 | user_fpregs_offset, | ||
729 | user_fpregs_offset + sizeof(new_vfp.fpregs)); | ||
730 | if (ret) | ||
731 | return ret; | ||
732 | |||
733 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | ||
734 | user_fpregs_offset + sizeof(new_vfp.fpregs), | ||
735 | user_fpscr_offset); | ||
736 | if (ret) | ||
737 | return ret; | ||
738 | |||
739 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
740 | &new_vfp.fpscr, | ||
741 | user_fpscr_offset, | ||
742 | user_fpscr_offset + sizeof(new_vfp.fpscr)); | ||
743 | if (ret) | ||
744 | return ret; | ||
745 | |||
746 | vfp_sync_hwstate(thread); | ||
747 | thread->vfpstate.hard = new_vfp; | ||
844 | vfp_flush_hwstate(thread); | 748 | vfp_flush_hwstate(thread); |
845 | 749 | ||
846 | return 0; | 750 | return 0; |
847 | } | 751 | } |
752 | #endif /* CONFIG_VFP */ | ||
753 | |||
754 | enum arm_regset { | ||
755 | REGSET_GPR, | ||
756 | REGSET_FPR, | ||
757 | #ifdef CONFIG_VFP | ||
758 | REGSET_VFP, | ||
848 | #endif | 759 | #endif |
760 | }; | ||
849 | 761 | ||
850 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | 762 | static const struct user_regset arm_regsets[] = { |
763 | [REGSET_GPR] = { | ||
764 | .core_note_type = NT_PRSTATUS, | ||
765 | .n = ELF_NGREG, | ||
766 | .size = sizeof(u32), | ||
767 | .align = sizeof(u32), | ||
768 | .get = gpr_get, | ||
769 | .set = gpr_set | ||
770 | }, | ||
771 | [REGSET_FPR] = { | ||
772 | /* | ||
773 | * For the FPA regs in fpstate, the real fields are a mixture | ||
774 | * of sizes, so pretend that the registers are word-sized: | ||
775 | */ | ||
776 | .core_note_type = NT_PRFPREG, | ||
777 | .n = sizeof(struct user_fp) / sizeof(u32), | ||
778 | .size = sizeof(u32), | ||
779 | .align = sizeof(u32), | ||
780 | .get = fpa_get, | ||
781 | .set = fpa_set | ||
782 | }, | ||
783 | #ifdef CONFIG_VFP | ||
784 | [REGSET_VFP] = { | ||
785 | /* | ||
786 | * Pretend that the VFP regs are word-sized, since the FPSCR is | ||
787 | * a single word dangling at the end of struct user_vfp: | ||
788 | */ | ||
789 | .core_note_type = NT_ARM_VFP, | ||
790 | .n = ARM_VFPREGS_SIZE / sizeof(u32), | ||
791 | .size = sizeof(u32), | ||
792 | .align = sizeof(u32), | ||
793 | .get = vfp_get, | ||
794 | .set = vfp_set | ||
795 | }, | ||
796 | #endif /* CONFIG_VFP */ | ||
797 | }; | ||
798 | |||
799 | static const struct user_regset_view user_arm_view = { | ||
800 | .name = "arm", .e_machine = ELF_ARCH, .ei_osabi = ELF_OSABI, | ||
801 | .regsets = arm_regsets, .n = ARRAY_SIZE(arm_regsets) | ||
802 | }; | ||
803 | |||
804 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | ||
805 | { | ||
806 | return &user_arm_view; | ||
807 | } | ||
808 | |||
809 | long arch_ptrace(struct task_struct *child, long request, | ||
810 | unsigned long addr, unsigned long data) | ||
851 | { | 811 | { |
852 | int ret; | 812 | int ret; |
813 | unsigned long __user *datap = (unsigned long __user *) data; | ||
853 | 814 | ||
854 | switch (request) { | 815 | switch (request) { |
855 | case PTRACE_PEEKUSR: | 816 | case PTRACE_PEEKUSR: |
856 | ret = ptrace_read_user(child, addr, (unsigned long __user *)data); | 817 | ret = ptrace_read_user(child, addr, datap); |
857 | break; | 818 | break; |
858 | 819 | ||
859 | case PTRACE_POKEUSR: | 820 | case PTRACE_POKEUSR: |
@@ -861,34 +822,46 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
861 | break; | 822 | break; |
862 | 823 | ||
863 | case PTRACE_GETREGS: | 824 | case PTRACE_GETREGS: |
864 | ret = ptrace_getregs(child, (void __user *)data); | 825 | ret = copy_regset_to_user(child, |
826 | &user_arm_view, REGSET_GPR, | ||
827 | 0, sizeof(struct pt_regs), | ||
828 | datap); | ||
865 | break; | 829 | break; |
866 | 830 | ||
867 | case PTRACE_SETREGS: | 831 | case PTRACE_SETREGS: |
868 | ret = ptrace_setregs(child, (void __user *)data); | 832 | ret = copy_regset_from_user(child, |
833 | &user_arm_view, REGSET_GPR, | ||
834 | 0, sizeof(struct pt_regs), | ||
835 | datap); | ||
869 | break; | 836 | break; |
870 | 837 | ||
871 | case PTRACE_GETFPREGS: | 838 | case PTRACE_GETFPREGS: |
872 | ret = ptrace_getfpregs(child, (void __user *)data); | 839 | ret = copy_regset_to_user(child, |
840 | &user_arm_view, REGSET_FPR, | ||
841 | 0, sizeof(union fp_state), | ||
842 | datap); | ||
873 | break; | 843 | break; |
874 | 844 | ||
875 | case PTRACE_SETFPREGS: | 845 | case PTRACE_SETFPREGS: |
876 | ret = ptrace_setfpregs(child, (void __user *)data); | 846 | ret = copy_regset_from_user(child, |
847 | &user_arm_view, REGSET_FPR, | ||
848 | 0, sizeof(union fp_state), | ||
849 | datap); | ||
877 | break; | 850 | break; |
878 | 851 | ||
879 | #ifdef CONFIG_IWMMXT | 852 | #ifdef CONFIG_IWMMXT |
880 | case PTRACE_GETWMMXREGS: | 853 | case PTRACE_GETWMMXREGS: |
881 | ret = ptrace_getwmmxregs(child, (void __user *)data); | 854 | ret = ptrace_getwmmxregs(child, datap); |
882 | break; | 855 | break; |
883 | 856 | ||
884 | case PTRACE_SETWMMXREGS: | 857 | case PTRACE_SETWMMXREGS: |
885 | ret = ptrace_setwmmxregs(child, (void __user *)data); | 858 | ret = ptrace_setwmmxregs(child, datap); |
886 | break; | 859 | break; |
887 | #endif | 860 | #endif |
888 | 861 | ||
889 | case PTRACE_GET_THREAD_AREA: | 862 | case PTRACE_GET_THREAD_AREA: |
890 | ret = put_user(task_thread_info(child)->tp_value, | 863 | ret = put_user(task_thread_info(child)->tp_value, |
891 | (unsigned long __user *) data); | 864 | datap); |
892 | break; | 865 | break; |
893 | 866 | ||
894 | case PTRACE_SET_SYSCALL: | 867 | case PTRACE_SET_SYSCALL: |
@@ -898,21 +871,46 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
898 | 871 | ||
899 | #ifdef CONFIG_CRUNCH | 872 | #ifdef CONFIG_CRUNCH |
900 | case PTRACE_GETCRUNCHREGS: | 873 | case PTRACE_GETCRUNCHREGS: |
901 | ret = ptrace_getcrunchregs(child, (void __user *)data); | 874 | ret = ptrace_getcrunchregs(child, datap); |
902 | break; | 875 | break; |
903 | 876 | ||
904 | case PTRACE_SETCRUNCHREGS: | 877 | case PTRACE_SETCRUNCHREGS: |
905 | ret = ptrace_setcrunchregs(child, (void __user *)data); | 878 | ret = ptrace_setcrunchregs(child, datap); |
906 | break; | 879 | break; |
907 | #endif | 880 | #endif |
908 | 881 | ||
909 | #ifdef CONFIG_VFP | 882 | #ifdef CONFIG_VFP |
910 | case PTRACE_GETVFPREGS: | 883 | case PTRACE_GETVFPREGS: |
911 | ret = ptrace_getvfpregs(child, (void __user *)data); | 884 | ret = copy_regset_to_user(child, |
885 | &user_arm_view, REGSET_VFP, | ||
886 | 0, ARM_VFPREGS_SIZE, | ||
887 | datap); | ||
912 | break; | 888 | break; |
913 | 889 | ||
914 | case PTRACE_SETVFPREGS: | 890 | case PTRACE_SETVFPREGS: |
915 | ret = ptrace_setvfpregs(child, (void __user *)data); | 891 | ret = copy_regset_from_user(child, |
892 | &user_arm_view, REGSET_VFP, | ||
893 | 0, ARM_VFPREGS_SIZE, | ||
894 | datap); | ||
895 | break; | ||
896 | #endif | ||
897 | |||
898 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
899 | case PTRACE_GETHBPREGS: | ||
900 | if (ptrace_get_breakpoints(child) < 0) | ||
901 | return -ESRCH; | ||
902 | |||
903 | ret = ptrace_gethbpregs(child, addr, | ||
904 | (unsigned long __user *)data); | ||
905 | ptrace_put_breakpoints(child); | ||
906 | break; | ||
907 | case PTRACE_SETHBPREGS: | ||
908 | if (ptrace_get_breakpoints(child) < 0) | ||
909 | return -ESRCH; | ||
910 | |||
911 | ret = ptrace_sethbpregs(child, addr, | ||
912 | (unsigned long __user *)data); | ||
913 | ptrace_put_breakpoints(child); | ||
916 | break; | 914 | break; |
917 | #endif | 915 | #endif |
918 | 916 | ||