diff --git a/sys/arm/arm/machdep.c b/sys/arm/arm/machdep.c --- a/sys/arm/arm/machdep.c +++ b/sys/arm/arm/machdep.c @@ -380,6 +380,7 @@ thread0.td_pcb->pcb_fpflags = 0; thread0.td_pcb->pcb_vfpcpu = -1; thread0.td_pcb->pcb_vfpstate.fpscr = VFPSCR_DN; + thread0.td_pcb->pcb_vfpsaved = &thread0.td_pcb->pcb_vfpstate; thread0.td_frame = &proc0_tf; pcpup->pc_curpcb = thread0.td_pcb; } diff --git a/sys/arm/arm/vfp.c b/sys/arm/arm/vfp.c --- a/sys/arm/arm/vfp.c +++ b/sys/arm/arm/vfp.c @@ -34,6 +34,7 @@ #ifdef VFP #include #include +#include #include #include #include @@ -251,17 +252,45 @@ curpcb = curthread->td_pcb; cpu = PCPU_GET(cpuid); if (curpcb->pcb_vfpcpu != cpu || curthread != PCPU_GET(fpcurthread)) { - if (curpcb->pcb_vfpsaved == NULL) - curpcb->pcb_vfpsaved = &curpcb->pcb_vfpstate; vfp_restore(curpcb->pcb_vfpsaved); curpcb->pcb_vfpcpu = cpu; PCPU_SET(fpcurthread, curthread); } critical_exit(); + + KASSERT(curpcb->pcb_vfpsaved == &curpcb->pcb_vfpstate, + ("Kernel VFP state in use when entering userspace")); + return (0); } +/* + * Update the VFP state for a forked process or new thread. The PCB will + * have been copied from the old thread. + * The code is heavily based on arm64 logic. + */ +void +vfp_new_thread(struct thread *newtd, struct thread *oldtd, bool fork) +{ + struct pcb *newpcb; + + newpcb = newtd->td_pcb; + + /* Kernel threads start with clean VFP */ + if ((oldtd->td_pflags & TDP_KTHREAD) != 0) { + newpcb->pcb_fpflags &= + ~(PCB_FP_STARTED | PCB_FP_KERN | PCB_FP_NOSAVE); + } else { + MPASS((newpcb->pcb_fpflags & (PCB_FP_KERN|PCB_FP_NOSAVE)) == 0); + if (!fork) { + newpcb->pcb_fpflags &= ~PCB_FP_STARTED; + } + } + + newpcb->pcb_vfpsaved = &newpcb->pcb_vfpstate; + newpcb->pcb_vfpcpu = UINT_MAX; +} /* * Restore the given state to the VFP hardware. */ diff --git a/sys/arm/arm/vm_machdep.c b/sys/arm/arm/vm_machdep.c --- a/sys/arm/arm/vm_machdep.c +++ b/sys/arm/arm/vm_machdep.c @@ -137,9 +137,9 @@ pcb2->pcb_regs.sf_sp = STACKALIGN(td2->td_frame); pcb2->pcb_regs.sf_tpidrurw = (register_t)get_tls(); - pcb2->pcb_vfpcpu = -1; - pcb2->pcb_vfpsaved = &pcb2->pcb_vfpstate; - pcb2->pcb_vfpstate.fpscr = initial_fpscr; +#ifdef VFP + vfp_new_thread(td2, td1, true); +#endif tf = td2->td_frame; tf->tf_spsr &= ~PSR_C; @@ -216,6 +216,10 @@ td->td_frame->tf_spsr &= ~PSR_C; td->td_frame->tf_r0 = 0; +#ifdef VFP + vfp_new_thread(td, td0, false); +#endif + /* Setup to release spin count in fork_exit(). */ td->td_md.md_spinlock_count = 1; td->td_md.md_saved_cspr = PSR_SVC32_MODE; diff --git a/sys/arm/include/vfp.h b/sys/arm/include/vfp.h --- a/sys/arm/include/vfp.h +++ b/sys/arm/include/vfp.h @@ -157,6 +157,7 @@ void get_vfpcontext(struct thread *, mcontext_vfp_t *); void set_vfpcontext(struct thread *, mcontext_vfp_t *); void vfp_init(void); +void vfp_new_thread(struct thread*, struct thread*, bool); void vfp_store(struct vfp_state *, boolean_t); void vfp_discard(struct thread *); void vfp_restore_state(void);