From 953a7d7c61f3b2f5351dfe668510ec782ae282e8 Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Tue, 9 Mar 2021 19:11:40 +0000 Subject: [PATCH] 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 65618fdda0f27 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 --- sys/arm64/arm64/elf32_machdep.c | 7 +++++++ sys/arm64/arm64/machdep.c | 7 +++++++ sys/arm64/arm64/vfp.c | 21 +++++++++++++++++++++ sys/arm64/arm64/vm_machdep.c | 1 - sys/arm64/include/vfp.h | 1 + sys/arm64/linux/linux_sysvec.c | 8 ++++++++ 6 files changed, 44 insertions(+), 1 deletion(-) diff --git a/sys/arm64/arm64/elf32_machdep.c b/sys/arm64/arm64/elf32_machdep.c index 916633650d69..84b62caf8590 100644 --- a/sys/arm64/arm64/elf32_machdep.c +++ b/sys/arm64/arm64/elf32_machdep.c @@ -51,6 +51,9 @@ __FBSDID("$FreeBSD$"); #include #include +#ifdef VFP +#include +#endif #include @@ -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 diff --git a/sys/arm64/arm64/machdep.c b/sys/arm64/arm64/machdep.c index 73b06beeba7e..91f0a31ebe36 100644 --- a/sys/arm64/arm64/machdep.c +++ b/sys/arm64/arm64/machdep.c @@ -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 */ diff --git a/sys/arm64/arm64/vfp.c b/sys/arm64/arm64/vfp.c index 23c782c6dccb..5fa420e668c1 100644 --- a/sys/arm64/arm64/vfp.c +++ b/sys/arm64/arm64/vfp.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #ifdef VFP #include #include +#include #include #include #include @@ -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) { diff --git a/sys/arm64/arm64/vm_machdep.c b/sys/arm64/arm64/vm_machdep.c index 9a496b40ec98..0193048e2697 100644 --- a/sys/arm64/arm64/vm_machdep.c +++ b/sys/arm64/arm64/vm_machdep.c @@ -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; diff --git a/sys/arm64/include/vfp.h b/sys/arm64/include/vfp.h index b0ba01a2a319..3632e5eaa396 100644 --- a/sys/arm64/include/vfp.h +++ b/sys/arm64/include/vfp.h @@ -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 *); diff --git a/sys/arm64/linux/linux_sysvec.c b/sys/arm64/linux/linux_sysvec.c index 1d628ffe6ecb..67feacfa876b 100644 --- a/sys/arm64/linux/linux_sysvec.c +++ b/sys/arm64/linux/linux_sysvec.c @@ -57,6 +57,10 @@ __FBSDID("$FreeBSD$"); #include #include +#ifdef VFP +#include +#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