static void vfp_thread_copy(struct thread_info *thread) { struct thread_info *parent = current_thread_info(); vfp_sync_hwstate(parent); thread->vfpstate = parent->vfpstate; }
/* * VFP register get/set implementations. * * With respect to the kernel, struct user_fp is divided into three chunks: * 16 or 32 real VFP registers (d0-d15 or d0-31) * These are transferred to/from the real registers in the task's * vfp_hard_struct. The number of registers depends on the kernel * configuration. * * 16 or 0 fake VFP registers (d16-d31 or empty) * i.e., the user_vfp structure has space for 32 registers even if * the kernel doesn't have them all. * * vfp_get() reads this chunk as zero where applicable * vfp_set() ignores this chunk * * 1 word for the FPSCR * * The bounds-checking logic built into user_regset_copyout and friends * means that we can make a simple sequence of calls to map the relevant data * to/from the specified slice of the user regset structure. */ static int vfp_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { int ret; struct thread_info *thread = task_thread_info(target); struct vfp_hard_struct const *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); vfp_sync_hwstate(thread); ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &vfp->fpregs, user_fpregs_offset, user_fpregs_offset + sizeof(vfp->fpregs)); if (ret) return ret; ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, user_fpregs_offset + sizeof(vfp->fpregs), user_fpscr_offset); if (ret) return ret; return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &vfp->fpscr, user_fpscr_offset, user_fpscr_offset + sizeof(vfp->fpscr)); }
int vfp_preserve_user_clear_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; int err = 0; vfp_sync_hwstate(thread); err |= __copy_to_user(&ufp->fpregs, &hwstate->fpregs, sizeof(hwstate->fpregs)); __put_user_error(hwstate->fpscr, &ufp->fpscr, err); __put_user_error(hwstate->fpexc, &ufp_exc->fpexc, err); __put_user_error(hwstate->fpinst, &ufp_exc->fpinst, err); __put_user_error(hwstate->fpinst2, &ufp_exc->fpinst2, err); if (err) return -EFAULT; vfp_flush_hwstate(thread); hwstate->fpscr &= ~(FPSCR_LENGTH_MASK | FPSCR_STRIDE_MASK); return 0; }
static int preserve_vfp_context(struct vfp_sigframe __user *frame) { struct thread_info *thread = current_thread_info(); struct vfp_hard_struct *h = &thread->vfpstate.hard; const unsigned long magic = VFP_MAGIC; const unsigned long size = VFP_STORAGE_SIZE; int err = 0; vfp_sync_hwstate(thread); __put_user_error(magic, &frame->magic, err); __put_user_error(size, &frame->size, err); /* * Copy the floating point registers. There can be unused * registers see asm/hwcap.h for details. */ err |= __copy_to_user(&frame->ufp.fpregs, &h->fpregs, sizeof(h->fpregs)); /* * Copy the status and control register. */ __put_user_error(h->fpscr, &frame->ufp.fpscr, err); /* * Copy the exception registers. */ __put_user_error(h->fpexc, &frame->ufp_exc.fpexc, err); __put_user_error(h->fpinst, &frame->ufp_exc.fpinst, err); __put_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err); return err ? -EFAULT : 0; }
static void vfp_thread_copy(struct thread_info *thread) { struct thread_info *parent = current_thread_info(); vfp_sync_hwstate(parent); thread->vfpstate = parent->vfpstate; #ifdef CONFIG_SMP thread->vfpstate.hard.cpu = NR_CPUS; #endif }
/* * Save the current VFP state into the provided structures and prepare * for entry into a new function (signal handler). */ int vfp_preserve_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; int err = 0; /* Ensure that the saved hwstate is up-to-date. */ vfp_sync_hwstate(thread); /* * Copy the floating point registers. There can be unused * registers see asm/hwcap.h for details. */ err |= __copy_to_user(&ufp->fpregs, &hwstate->fpregs, sizeof(hwstate->fpregs)); /* * Copy the status and control register. */ __put_user_error(hwstate->fpscr, &ufp->fpscr, err); /* * Copy the exception registers. */ __put_user_error(hwstate->fpexc, &ufp_exc->fpexc, err); __put_user_error(hwstate->fpinst, &ufp_exc->fpinst, err); __put_user_error(hwstate->fpinst2, &ufp_exc->fpinst2, err); if (err) return -EFAULT; /* Ensure that VFP is disabled. */ vfp_flush_hwstate(thread); /* * As per the PCS, clear the length and stride bits before entry * to the signal handler. */ hwstate->fpscr &= ~(FPSCR_LENGTH_MASK | FPSCR_STRIDE_MASK); /* * Disable VFP in the hwstate so that we can detect if it was * used by the signal handler. */ hwstate->fpexc &= ~FPEXC_EN; return 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; */ struct vfp_hard_struct new_vfp; const size_t user_fpregs_offset = offsetof(struct user_vfp, fpregs); const size_t user_fpscr_offset = offsetof(struct user_vfp, fpscr); vfp_sync_hwstate(thread); new_vfp = thread->vfpstate.hard; 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); thread->vfpstate.hard = new_vfp; return 0; }