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 65618fdda0
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
This commit is contained in:
parent
dfb33cb0ef
commit
953a7d7c61
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user