|
@@ -494,3 +494,135 @@ static int vfp_cpu_pm_notifier(struct notifier_block *self, unsigned long cmd,
|
|
|
}
|
|
|
return NOTIFY_OK;
|
|
|
}
|
|
|
+
|
|
|
+static struct notifier_block vfp_cpu_pm_notifier_block = {
|
|
|
+ .notifier_call = vfp_cpu_pm_notifier,
|
|
|
+};
|
|
|
+
|
|
|
+static void vfp_pm_init(void)
|
|
|
+{
|
|
|
+ cpu_pm_register_notifier(&vfp_cpu_pm_notifier_block);
|
|
|
+}
|
|
|
+
|
|
|
+#else
|
|
|
+static inline void vfp_pm_init(void) { }
|
|
|
+#endif /* CONFIG_CPU_PM */
|
|
|
+
|
|
|
+/*
|
|
|
+ * Ensure that the VFP state stored in 'thread->vfpstate' is up to date
|
|
|
+ * with the hardware state.
|
|
|
+ */
|
|
|
+void vfp_sync_hwstate(struct thread_info *thread)
|
|
|
+{
|
|
|
+ unsigned int cpu = get_cpu();
|
|
|
+
|
|
|
+ if (vfp_state_in_hw(cpu, thread)) {
|
|
|
+ u32 fpexc = fmrx(FPEXC);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Save the last VFP state on this CPU.
|
|
|
+ */
|
|
|
+ fmxr(FPEXC, fpexc | FPEXC_EN);
|
|
|
+ vfp_save_state(&thread->vfpstate, fpexc | FPEXC_EN);
|
|
|
+ fmxr(FPEXC, fpexc);
|
|
|
+ }
|
|
|
+
|
|
|
+ put_cpu();
|
|
|
+}
|
|
|
+
|
|
|
+/* Ensure that the thread reloads the hardware VFP state on the next use. */
|
|
|
+void vfp_flush_hwstate(struct thread_info *thread)
|
|
|
+{
|
|
|
+ unsigned int cpu = get_cpu();
|
|
|
+
|
|
|
+ vfp_force_reload(cpu, 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);
|
|
|
+ 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;
|
|
|
+
|
|
|
+ /* 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;
|
|
|
+}
|