Commit 953a7d7c authored by Alex Richardson's avatar Alex Richardson
Browse files

Arch64: Clear VFP state on execve()

I noticed that many of the math-related tests were failing on AArch64.
After a lot of debugging, I noticed that the floating point exception flags
were not being reset when starting a new process. This change resets the
VFP inside exec_setregs() to ensure no VFP register state is leaked from
parent processes to children.

This commit also moves the clearing of fpcr that was added in 65618fdd
from fork() to execve() since that makes more sense: fork() can retain
current register values, but execve() should result in a well-defined
clean state.

Reviewed By:	andrew
MFC after:	1 week
Differential Revision: https://reviews.freebsd.org/D29060
parent dfb33cb0
......@@ -51,6 +51,9 @@ __FBSDID("$FreeBSD$");
#include <sys/vnode.h>
#include <machine/elf.h>
#ifdef VFP
#include <machine/vfp.h>
#endif
#include <compat/freebsd32/freebsd32_util.h>
......@@ -251,6 +254,10 @@ freebsd32_setregs(struct thread *td, struct image_params *imgp,
tf->tf_x[14] = imgp->entry_addr;
tf->tf_elr = imgp->entry_addr;
tf->tf_spsr = PSR_M_32;
#ifdef VFP
vfp_reset_state(td, td->td_pcb);
#endif
}
void
......
......@@ -552,6 +552,7 @@ void
exec_setregs(struct thread *td, struct image_params *imgp, uintptr_t stack)
{
struct trapframe *tf = td->td_frame;
struct pcb *pcb = td->td_pcb;
memset(tf, 0, sizeof(struct trapframe));
......@@ -559,6 +560,12 @@ exec_setregs(struct thread *td, struct image_params *imgp, uintptr_t stack)
tf->tf_sp = STACKALIGN(stack);
tf->tf_lr = imgp->entry_addr;
tf->tf_elr = imgp->entry_addr;
#ifdef VFP
vfp_reset_state(td, pcb);
#endif
/* TODO: Shouldn't we also reset pcb_dbg_regs? */
}
/* Sanity check these are the same size, they will be memcpy'd to and fro */
......
......@@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
#ifdef VFP
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/limits.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/pcpu.h>
......@@ -199,6 +200,26 @@ vfp_save_state(struct thread *td, struct pcb *pcb)
critical_exit();
}
/*
* Reset the FP state to avoid leaking state from the parent process across
* execve() (and to ensure that we get a consistent floating point environment
* in every new process).
*/
void
vfp_reset_state(struct thread *td, struct pcb *pcb)
{
critical_enter();
bzero(&pcb->pcb_fpustate.vfp_regs, sizeof(pcb->pcb_fpustate.vfp_regs));
KASSERT(pcb->pcb_fpusaved == &pcb->pcb_fpustate,
("pcb_fpusaved should point to pcb_fpustate."));
pcb->pcb_fpustate.vfp_fpcr = initial_fpcr;
pcb->pcb_fpustate.vfp_fpsr = 0;
pcb->pcb_vfpcpu = UINT_MAX;
pcb->pcb_fpflags = 0;
vfp_discard(td);
critical_exit();
}
void
vfp_restore_state(void)
{
......
......@@ -108,7 +108,6 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags)
td2->td_pcb->pcb_sp = (uintptr_t)td2->td_frame;
td2->td_pcb->pcb_fpusaved = &td2->td_pcb->pcb_fpustate;
td2->td_pcb->pcb_vfpcpu = UINT_MAX;
td2->td_pcb->pcb_fpusaved->vfp_fpcr = initial_fpcr;
/* Setup to release spin count in fork_exit(). */
td2->td_md.md_spinlock_count = 1;
......
......@@ -68,6 +68,7 @@ struct thread;
void vfp_init(void);
void vfp_discard(struct thread *);
void vfp_reset_state(struct thread *, struct pcb *);
void vfp_restore_state(void);
void vfp_save_state(struct thread *, struct pcb *);
......
......@@ -57,6 +57,10 @@ __FBSDID("$FreeBSD$");
#include <compat/linux/linux_util.h>
#include <compat/linux/linux_vdso.h>
#ifdef VFP
#include <machine/vfp.h>
#endif
MODULE_VERSION(linux64elf, 1);
const char *linux_kplatform;
......@@ -360,6 +364,10 @@ linux_exec_setregs(struct thread *td, struct image_params *imgp,
regs->tf_lr = 0xffffffffffffffff;
#endif
regs->tf_elr = imgp->entry_addr;
#ifdef VFP
vfp_reset_state(td, td->td_pcb);
#endif
}
int
......
Supports Markdown
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