Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[ARM] 5387/1: Add ptrace VFP support on ARM

This patch adds ptrace support for setting and getting the VFP registers
using PTRACE_SETVFPREGS and PTRACE_GETVFPREGS. The user_vfp structure
defined in asm/user.h contains 32 double registers (to cover VFPv3 and
Neon hardware) and the FPSCR register.

Cc: Paul Brook <paul@codesourcery.com>
Cc: Daniel Jacobowitz <dan@codesourcery.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by

Catalin Marinas and committed by
Russell King
3d1228ea f373e8c0

+120 -4
+2
arch/arm/include/asm/ptrace.h
··· 27 27 /* PTRACE_SYSCALL is 24 */ 28 28 #define PTRACE_GETCRUNCHREGS 25 29 29 #define PTRACE_SETCRUNCHREGS 26 30 + #define PTRACE_GETVFPREGS 27 31 + #define PTRACE_SETVFPREGS 28 30 32 31 33 /* 32 34 * PSR bits
+2
arch/arm/include/asm/thread_info.h
··· 113 113 extern void iwmmxt_task_release(struct thread_info *); 114 114 extern void iwmmxt_task_switch(struct thread_info *); 115 115 116 + extern void vfp_sync_state(struct thread_info *thread); 117 + 116 118 #endif 117 119 118 120 /*
+9
arch/arm/include/asm/user.h
··· 81 81 #define HOST_TEXT_START_ADDR (u.start_code) 82 82 #define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG) 83 83 84 + /* 85 + * User specific VFP registers. If only VFPv2 is present, registers 16 to 31 86 + * are ignored by the ptrace system call. 87 + */ 88 + struct user_vfp { 89 + unsigned long long fpregs[32]; 90 + unsigned long fpscr; 91 + }; 92 + 84 93 #endif /* _ARM_USER_H */
+58
arch/arm/kernel/ptrace.c
··· 653 653 } 654 654 #endif 655 655 656 + #ifdef CONFIG_VFP 657 + /* 658 + * Get the child VFP state. 659 + */ 660 + static int ptrace_getvfpregs(struct task_struct *tsk, void __user *data) 661 + { 662 + struct thread_info *thread = task_thread_info(tsk); 663 + union vfp_state *vfp = &thread->vfpstate; 664 + struct user_vfp __user *ufp = data; 665 + 666 + vfp_sync_state(thread); 667 + 668 + /* copy the floating point registers */ 669 + if (copy_to_user(&ufp->fpregs, &vfp->hard.fpregs, 670 + sizeof(vfp->hard.fpregs))) 671 + return -EFAULT; 672 + 673 + /* copy the status and control register */ 674 + if (put_user(vfp->hard.fpscr, &ufp->fpscr)) 675 + return -EFAULT; 676 + 677 + return 0; 678 + } 679 + 680 + /* 681 + * Set the child VFP state. 682 + */ 683 + static int ptrace_setvfpregs(struct task_struct *tsk, void __user *data) 684 + { 685 + struct thread_info *thread = task_thread_info(tsk); 686 + union vfp_state *vfp = &thread->vfpstate; 687 + struct user_vfp __user *ufp = data; 688 + 689 + vfp_sync_state(thread); 690 + 691 + /* copy the floating point registers */ 692 + if (copy_from_user(&vfp->hard.fpregs, &ufp->fpregs, 693 + sizeof(vfp->hard.fpregs))) 694 + return -EFAULT; 695 + 696 + /* copy the status and control register */ 697 + if (get_user(vfp->hard.fpscr, &ufp->fpscr)) 698 + return -EFAULT; 699 + 700 + return 0; 701 + } 702 + #endif 703 + 656 704 long arch_ptrace(struct task_struct *child, long request, long addr, long data) 657 705 { 658 706 int ret; ··· 820 772 821 773 case PTRACE_SETCRUNCHREGS: 822 774 ret = ptrace_setcrunchregs(child, (void __user *)data); 775 + break; 776 + #endif 777 + 778 + #ifdef CONFIG_VFP 779 + case PTRACE_GETVFPREGS: 780 + ret = ptrace_getvfpregs(child, (void __user *)data); 781 + break; 782 + 783 + case PTRACE_SETVFPREGS: 784 + ret = ptrace_setvfpregs(child, (void __user *)data); 823 785 break; 824 786 #endif 825 787
-2
arch/arm/vfp/vfp.h
··· 377 377 u32 flags; 378 378 }; 379 379 380 - #if defined(CONFIG_SMP) || defined(CONFIG_PM) 381 380 extern void vfp_save_state(void *location, u32 fpexc); 382 - #endif
-2
arch/arm/vfp/vfphw.S
··· 172 172 @ retry the faulted instruction 173 173 ENDPROC(vfp_support_entry) 174 174 175 - #if defined(CONFIG_SMP) || defined(CONFIG_PM) 176 175 ENTRY(vfp_save_state) 177 176 @ Save the current VFP state 178 177 @ r0 - save location ··· 189 190 stmia r0, {r1, r2, r3, r12} @ save FPEXC, FPSCR, FPINST, FPINST2 190 191 mov pc, lr 191 192 ENDPROC(vfp_save_state) 192 - #endif 193 193 194 194 last_VFP_context_address: 195 195 .word last_VFP_context
+49
arch/arm/vfp/vfpmodule.c
··· 377 377 static inline void vfp_pm_init(void) { } 378 378 #endif /* CONFIG_PM */ 379 379 380 + /* 381 + * Synchronise the hardware VFP state of a thread other than current with the 382 + * saved one. This function is used by the ptrace mechanism. 383 + */ 384 + #ifdef CONFIG_SMP 385 + void vfp_sync_state(struct thread_info *thread) 386 + { 387 + /* 388 + * On SMP systems, the VFP state is automatically saved at every 389 + * context switch. We mark the thread VFP state as belonging to a 390 + * non-existent CPU so that the saved one will be reloaded when 391 + * needed. 392 + */ 393 + thread->vfpstate.hard.cpu = NR_CPUS; 394 + } 395 + #else 396 + void vfp_sync_state(struct thread_info *thread) 397 + { 398 + unsigned int cpu = get_cpu(); 399 + u32 fpexc = fmrx(FPEXC); 400 + 401 + /* 402 + * If VFP is enabled, the previous state was already saved and 403 + * last_VFP_context updated. 404 + */ 405 + if (fpexc & FPEXC_EN) 406 + goto out; 407 + 408 + if (!last_VFP_context[cpu]) 409 + goto out; 410 + 411 + /* 412 + * Save the last VFP state on this CPU. 413 + */ 414 + fmxr(FPEXC, fpexc | FPEXC_EN); 415 + vfp_save_state(last_VFP_context[cpu], fpexc); 416 + fmxr(FPEXC, fpexc); 417 + 418 + /* 419 + * Set the context to NULL to force a reload the next time the thread 420 + * uses the VFP. 421 + */ 422 + last_VFP_context[cpu] = NULL; 423 + 424 + out: 425 + put_cpu(); 426 + } 427 + #endif 428 + 380 429 #include <linux/smp.h> 381 430 382 431 /*