Skip to content

Commit

Permalink
[ARM] 5387/1: Add ptrace VFP support on ARM
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Catalin Marinas authored and Russell King committed Feb 12, 2009
1 parent f373e8c commit 3d1228e
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 4 deletions.
2 changes: 2 additions & 0 deletions arch/arm/include/asm/ptrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
/* PTRACE_SYSCALL is 24 */
#define PTRACE_GETCRUNCHREGS 25
#define PTRACE_SETCRUNCHREGS 26
#define PTRACE_GETVFPREGS 27
#define PTRACE_SETVFPREGS 28

/*
* PSR bits
Expand Down
2 changes: 2 additions & 0 deletions arch/arm/include/asm/thread_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_switch(struct thread_info *);

extern void vfp_sync_state(struct thread_info *thread);

#endif

/*
Expand Down
9 changes: 9 additions & 0 deletions arch/arm/include/asm/user.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,13 @@ struct user{
#define HOST_TEXT_START_ADDR (u.start_code)
#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 */
58 changes: 58 additions & 0 deletions arch/arm/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,54 @@ static int ptrace_setcrunchregs(struct task_struct *tsk, void __user *ufp)
}
#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)
{
int ret;
Expand Down Expand Up @@ -775,6 +823,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
break;
#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:
ret = ptrace_request(child, request, addr, data);
break;
Expand Down
2 changes: 0 additions & 2 deletions arch/arm/vfp/vfp.h
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,4 @@ struct op {
u32 flags;
};

#if defined(CONFIG_SMP) || defined(CONFIG_PM)
extern void vfp_save_state(void *location, u32 fpexc);
#endif
2 changes: 0 additions & 2 deletions arch/arm/vfp/vfphw.S
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ process_exception:
@ retry the faulted instruction
ENDPROC(vfp_support_entry)

#if defined(CONFIG_SMP) || defined(CONFIG_PM)
ENTRY(vfp_save_state)
@ Save the current VFP state
@ r0 - save location
Expand All @@ -190,7 +189,6 @@ ENTRY(vfp_save_state)
stmia r0, {r1, r2, r3, r12} @ save FPEXC, FPSCR, FPINST, FPINST2
mov pc, lr
ENDPROC(vfp_save_state)
#endif

last_VFP_context_address:
.word last_VFP_context
Expand Down
49 changes: 49 additions & 0 deletions arch/arm/vfp/vfpmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,55 @@ static void vfp_pm_init(void)
static inline void vfp_pm_init(void) { }
#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>

/*
Expand Down

0 comments on commit 3d1228e

Please sign in to comment.