aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCatalin Marinas <catalin.marinas@arm.com>2009-02-11 07:12:56 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2009-02-12 05:59:43 -0500
commit3d1228ead618b88e8606015cbabc49019981805d (patch)
tree5d44c936c12ff2e99732f535dd393af201e7fe67
parentf373e8c0639f1720d2d0fe414990f504e113c2ba (diff)
[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>
-rw-r--r--arch/arm/include/asm/ptrace.h2
-rw-r--r--arch/arm/include/asm/thread_info.h2
-rw-r--r--arch/arm/include/asm/user.h9
-rw-r--r--arch/arm/kernel/ptrace.c58
-rw-r--r--arch/arm/vfp/vfp.h2
-rw-r--r--arch/arm/vfp/vfphw.S2
-rw-r--r--arch/arm/vfp/vfpmodule.c49
7 files changed, 120 insertions, 4 deletions
diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h
index 73192618f1c2..236a06b9b7ce 100644
--- a/arch/arm/include/asm/ptrace.h
+++ b/arch/arm/include/asm/ptrace.h
@@ -27,6 +27,8 @@
27/* PTRACE_SYSCALL is 24 */ 27/* PTRACE_SYSCALL is 24 */
28#define PTRACE_GETCRUNCHREGS 25 28#define PTRACE_GETCRUNCHREGS 25
29#define PTRACE_SETCRUNCHREGS 26 29#define PTRACE_SETCRUNCHREGS 26
30#define PTRACE_GETVFPREGS 27
31#define PTRACE_SETVFPREGS 28
30 32
31/* 33/*
32 * PSR bits 34 * PSR bits
diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h
index 68b9ec82a37f..b9dc8a842573 100644
--- a/arch/arm/include/asm/thread_info.h
+++ b/arch/arm/include/asm/thread_info.h
@@ -113,6 +113,8 @@ extern void iwmmxt_task_restore(struct thread_info *, void *);
113extern void iwmmxt_task_release(struct thread_info *); 113extern void iwmmxt_task_release(struct thread_info *);
114extern void iwmmxt_task_switch(struct thread_info *); 114extern void iwmmxt_task_switch(struct thread_info *);
115 115
116extern void vfp_sync_state(struct thread_info *thread);
117
116#endif 118#endif
117 119
118/* 120/*
diff --git a/arch/arm/include/asm/user.h b/arch/arm/include/asm/user.h
index 825c1e7c582d..df95e050f9dd 100644
--- a/arch/arm/include/asm/user.h
+++ b/arch/arm/include/asm/user.h
@@ -81,4 +81,13 @@ struct user{
81#define HOST_TEXT_START_ADDR (u.start_code) 81#define HOST_TEXT_START_ADDR (u.start_code)
82#define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG) 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 */
88struct user_vfp {
89 unsigned long long fpregs[32];
90 unsigned long fpscr;
91};
92
84#endif /* _ARM_USER_H */ 93#endif /* _ARM_USER_H */
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index df653ea59250..89882a1d0187 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -653,6 +653,54 @@ static int ptrace_setcrunchregs(struct task_struct *tsk, void __user *ufp)
653} 653}
654#endif 654#endif
655 655
656#ifdef CONFIG_VFP
657/*
658 * Get the child VFP state.
659 */
660static 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 */
683static 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
656long arch_ptrace(struct task_struct *child, long request, long addr, long data) 704long arch_ptrace(struct task_struct *child, long request, long addr, long data)
657{ 705{
658 int ret; 706 int ret;
@@ -775,6 +823,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
775 break; 823 break;
776#endif 824#endif
777 825
826#ifdef CONFIG_VFP
827 case PTRACE_GETVFPREGS:
828 ret = ptrace_getvfpregs(child, (void __user *)data);
829 break;
830
831 case PTRACE_SETVFPREGS:
832 ret = ptrace_setvfpregs(child, (void __user *)data);
833 break;
834#endif
835
778 default: 836 default:
779 ret = ptrace_request(child, request, addr, data); 837 ret = ptrace_request(child, request, addr, data);
780 break; 838 break;
diff --git a/arch/arm/vfp/vfp.h b/arch/arm/vfp/vfp.h
index 8de86e4feada..c8c98dd44ad4 100644
--- a/arch/arm/vfp/vfp.h
+++ b/arch/arm/vfp/vfp.h
@@ -377,6 +377,4 @@ struct op {
377 u32 flags; 377 u32 flags;
378}; 378};
379 379
380#if defined(CONFIG_SMP) || defined(CONFIG_PM)
381extern void vfp_save_state(void *location, u32 fpexc); 380extern void vfp_save_state(void *location, u32 fpexc);
382#endif
diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S
index c92a08bd6a86..a5a4e57763c3 100644
--- a/arch/arm/vfp/vfphw.S
+++ b/arch/arm/vfp/vfphw.S
@@ -172,7 +172,6 @@ process_exception:
172 @ retry the faulted instruction 172 @ retry the faulted instruction
173ENDPROC(vfp_support_entry) 173ENDPROC(vfp_support_entry)
174 174
175#if defined(CONFIG_SMP) || defined(CONFIG_PM)
176ENTRY(vfp_save_state) 175ENTRY(vfp_save_state)
177 @ Save the current VFP state 176 @ Save the current VFP state
178 @ r0 - save location 177 @ r0 - save location
@@ -190,7 +189,6 @@ ENTRY(vfp_save_state)
190 stmia r0, {r1, r2, r3, r12} @ save FPEXC, FPSCR, FPINST, FPINST2 189 stmia r0, {r1, r2, r3, r12} @ save FPEXC, FPSCR, FPINST, FPINST2
191 mov pc, lr 190 mov pc, lr
192ENDPROC(vfp_save_state) 191ENDPROC(vfp_save_state)
193#endif
194 192
195last_VFP_context_address: 193last_VFP_context_address:
196 .word last_VFP_context 194 .word last_VFP_context
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
index 9f476a1be2ca..7e1239041b39 100644
--- a/arch/arm/vfp/vfpmodule.c
+++ b/arch/arm/vfp/vfpmodule.c
@@ -377,6 +377,55 @@ static void vfp_pm_init(void)
377static inline void vfp_pm_init(void) { } 377static inline void vfp_pm_init(void) { }
378#endif /* CONFIG_PM */ 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
385void 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
396void 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
424out:
425 put_cpu();
426}
427#endif
428
380#include <linux/smp.h> 429#include <linux/smp.h>
381 430
382/* 431/*