|
@@ -17,6 +17,8 @@
|
|
|
#include <linux/sched.h>
|
|
|
#include <linux/smp.h>
|
|
|
#include <linux/init.h>
|
|
|
+#include <linux/uaccess.h>
|
|
|
+#include <linux/user.h>
|
|
|
|
|
|
#include <asm/cp15.h>
|
|
|
#include <asm/cputype.h>
|
|
@@ -528,6 +530,103 @@ void vfp_flush_hwstate(struct thread_info *thread)
|
|
|
put_cpu();
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Save the current VFP state into the provided structures and prepare
|
|
|
+ * for entry into a new function (signal handler).
|
|
|
+ */
|
|
|
+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;
|
|
|
+
|
|
|
+ /* 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 for function
|
|
|
+ * entry.
|
|
|
+ */
|
|
|
+ hwstate->fpscr &= ~(FPSCR_LENGTH_MASK | FPSCR_STRIDE_MASK);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Disable VFP in the hwstate so that we can detect if it gets
|
|
|
+ * used.
|
|
|
+ */
|
|
|
+ hwstate->fpexc &= ~FPEXC_EN;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 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;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If VFP has been used, then disable it to avoid corrupting
|
|
|
+ * the new thread state.
|
|
|
+ */
|
|
|
+ if (hwstate->fpexc & FPEXC_EN)
|
|
|
+ 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;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* VFP hardware can lose all context when a CPU goes offline.
|
|
|
* As we will be running in SMP mode with CPU hotplug, we will save the
|