Commit 3d1228ea authored by Catalin Marinas's avatar Catalin Marinas Committed by Russell King

[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: default avatarCatalin Marinas <catalin.marinas@arm.com>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent f373e8c0
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
/* PTRACE_SYSCALL is 24 */ /* PTRACE_SYSCALL is 24 */
#define PTRACE_GETCRUNCHREGS 25 #define PTRACE_GETCRUNCHREGS 25
#define PTRACE_SETCRUNCHREGS 26 #define PTRACE_SETCRUNCHREGS 26
#define PTRACE_GETVFPREGS 27
#define PTRACE_SETVFPREGS 28
/* /*
* PSR bits * PSR bits
......
...@@ -113,6 +113,8 @@ extern void iwmmxt_task_restore(struct thread_info *, void *); ...@@ -113,6 +113,8 @@ extern void iwmmxt_task_restore(struct thread_info *, void *);
extern void iwmmxt_task_release(struct thread_info *); extern void iwmmxt_task_release(struct thread_info *);
extern void iwmmxt_task_switch(struct thread_info *); extern void iwmmxt_task_switch(struct thread_info *);
extern void vfp_sync_state(struct thread_info *thread);
#endif #endif
/* /*
......
...@@ -81,4 +81,13 @@ struct user{ ...@@ -81,4 +81,13 @@ struct user{
#define HOST_TEXT_START_ADDR (u.start_code) #define HOST_TEXT_START_ADDR (u.start_code)
#define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG) #define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG)
/*
* User specific VFP registers. If only VFPv2 is present, registers 16 to 31
* are ignored by the ptrace system call.
*/
struct user_vfp {
unsigned long long fpregs[32];
unsigned long fpscr;
};
#endif /* _ARM_USER_H */ #endif /* _ARM_USER_H */
...@@ -653,6 +653,54 @@ static int ptrace_setcrunchregs(struct task_struct *tsk, void __user *ufp) ...@@ -653,6 +653,54 @@ static int ptrace_setcrunchregs(struct task_struct *tsk, void __user *ufp)
} }
#endif #endif
#ifdef CONFIG_VFP
/*
* Get the child VFP state.
*/
static int ptrace_getvfpregs(struct task_struct *tsk, void __user *data)
{
struct thread_info *thread = task_thread_info(tsk);
union vfp_state *vfp = &thread->vfpstate;
struct user_vfp __user *ufp = data;
vfp_sync_state(thread);
/* copy the floating point registers */
if (copy_to_user(&ufp->fpregs, &vfp->hard.fpregs,
sizeof(vfp->hard.fpregs)))
return -EFAULT;
/* copy the status and control register */
if (put_user(vfp->hard.fpscr, &ufp->fpscr))
return -EFAULT;
return 0;
}
/*
* Set the child VFP state.
*/
static int ptrace_setvfpregs(struct task_struct *tsk, void __user *data)
{
struct thread_info *thread = task_thread_info(tsk);
union vfp_state *vfp = &thread->vfpstate;
struct user_vfp __user *ufp = data;
vfp_sync_state(thread);
/* copy the floating point registers */
if (copy_from_user(&vfp->hard.fpregs, &ufp->fpregs,
sizeof(vfp->hard.fpregs)))
return -EFAULT;
/* copy the status and control register */
if (get_user(vfp->hard.fpscr, &ufp->fpscr))
return -EFAULT;
return 0;
}
#endif
long arch_ptrace(struct task_struct *child, long request, long addr, long data) long arch_ptrace(struct task_struct *child, long request, long addr, long data)
{ {
int ret; int ret;
...@@ -775,6 +823,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) ...@@ -775,6 +823,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
break; break;
#endif #endif
#ifdef CONFIG_VFP
case PTRACE_GETVFPREGS:
ret = ptrace_getvfpregs(child, (void __user *)data);
break;
case PTRACE_SETVFPREGS:
ret = ptrace_setvfpregs(child, (void __user *)data);
break;
#endif
default: default:
ret = ptrace_request(child, request, addr, data); ret = ptrace_request(child, request, addr, data);
break; break;
......
...@@ -377,6 +377,4 @@ struct op { ...@@ -377,6 +377,4 @@ struct op {
u32 flags; u32 flags;
}; };
#if defined(CONFIG_SMP) || defined(CONFIG_PM)
extern void vfp_save_state(void *location, u32 fpexc); extern void vfp_save_state(void *location, u32 fpexc);
#endif
...@@ -172,7 +172,6 @@ process_exception: ...@@ -172,7 +172,6 @@ process_exception:
@ retry the faulted instruction @ retry the faulted instruction
ENDPROC(vfp_support_entry) ENDPROC(vfp_support_entry)
#if defined(CONFIG_SMP) || defined(CONFIG_PM)
ENTRY(vfp_save_state) ENTRY(vfp_save_state)
@ Save the current VFP state @ Save the current VFP state
@ r0 - save location @ r0 - save location
...@@ -190,7 +189,6 @@ ENTRY(vfp_save_state) ...@@ -190,7 +189,6 @@ ENTRY(vfp_save_state)
stmia r0, {r1, r2, r3, r12} @ save FPEXC, FPSCR, FPINST, FPINST2 stmia r0, {r1, r2, r3, r12} @ save FPEXC, FPSCR, FPINST, FPINST2
mov pc, lr mov pc, lr
ENDPROC(vfp_save_state) ENDPROC(vfp_save_state)
#endif
last_VFP_context_address: last_VFP_context_address:
.word last_VFP_context .word last_VFP_context
......
...@@ -377,6 +377,55 @@ static void vfp_pm_init(void) ...@@ -377,6 +377,55 @@ static void vfp_pm_init(void)
static inline void vfp_pm_init(void) { } static inline void vfp_pm_init(void) { }
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
/*
* Synchronise the hardware VFP state of a thread other than current with the
* saved one. This function is used by the ptrace mechanism.
*/
#ifdef CONFIG_SMP
void vfp_sync_state(struct thread_info *thread)
{
/*
* On SMP systems, the VFP state is automatically saved at every
* context switch. We mark the thread VFP state as belonging to a
* non-existent CPU so that the saved one will be reloaded when
* needed.
*/
thread->vfpstate.hard.cpu = NR_CPUS;
}
#else
void vfp_sync_state(struct thread_info *thread)
{
unsigned int cpu = get_cpu();
u32 fpexc = fmrx(FPEXC);
/*
* If VFP is enabled, the previous state was already saved and
* last_VFP_context updated.
*/
if (fpexc & FPEXC_EN)
goto out;
if (!last_VFP_context[cpu])
goto out;
/*
* Save the last VFP state on this CPU.
*/
fmxr(FPEXC, fpexc | FPEXC_EN);
vfp_save_state(last_VFP_context[cpu], fpexc);
fmxr(FPEXC, fpexc);
/*
* Set the context to NULL to force a reload the next time the thread
* uses the VFP.
*/
last_VFP_context[cpu] = NULL;
out:
put_cpu();
}
#endif
#include <linux/smp.h> #include <linux/smp.h>
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment