/* Sanitise and restore the current VFP state from the provided structures. */ int vfp_restore_user_hwstate(struct user_vfp __user *ufp, struct user_vfp_exc __user *ufp_exc) { struct thread_info *thread = current_thread_info(); struct vfp_hard_struct *hwstate = &thread->vfpstate.hard; unsigned long fpexc; int err = 0; /* Disable VFP to avoid corrupting the new thread state. */ vfp_flush_hwstate(thread); /* * Copy the floating point registers. There can be unused * registers see asm/hwcap.h for details. */ err |= __copy_from_user(&hwstate->fpregs, &ufp->fpregs, sizeof(hwstate->fpregs)); /* * Copy the status and control register. */ __get_user_error(hwstate->fpscr, &ufp->fpscr, err); /* * Sanitise and restore the exception registers. */ __get_user_error(fpexc, &ufp_exc->fpexc, err); /* Ensure the VFP is enabled. */ fpexc |= FPEXC_EN; /* Ensure FPINST2 is invalid and the exception flag is cleared. */ fpexc &= ~(FPEXC_EX | FPEXC_FP2V); hwstate->fpexc = fpexc; __get_user_error(hwstate->fpinst, &ufp_exc->fpinst, err); __get_user_error(hwstate->fpinst2, &ufp_exc->fpinst2, err); return err ? -EFAULT : 0; }
/* * For vfp_set() a read-modify-write is done on the VFP registers, * in order to avoid writing back a half-modified set of registers on * failure. */ static int vfp_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { int ret; struct thread_info *thread = task_thread_info(target); struct vfp_hard_struct new_vfp = thread->vfpstate.hard; const size_t user_fpregs_offset = offsetof(struct user_vfp, fpregs); const size_t user_fpscr_offset = offsetof(struct user_vfp, fpscr); ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &new_vfp.fpregs, user_fpregs_offset, user_fpregs_offset + sizeof(new_vfp.fpregs)); if (ret) return ret; ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, user_fpregs_offset + sizeof(new_vfp.fpregs), user_fpscr_offset); if (ret) return ret; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &new_vfp.fpscr, user_fpscr_offset, user_fpscr_offset + sizeof(new_vfp.fpscr)); if (ret) return ret; vfp_sync_hwstate(thread); thread->vfpstate.hard = new_vfp; vfp_flush_hwstate(thread); return 0; }