ARM: add support for kernel mode NEON
In order to safely support the use of NEON instructions in kernel mode, some precautions need to be taken: - the userland context that may be present in the registers (even if the NEON/VFP is currently disabled) must be stored under the correct task (which may not be 'current' in the UP case), - to avoid having to keep track of additional vfpstates for the kernel side, disallow the use of NEON in interrupt context and run with preemption disabled, - after use, re-enable preemption and re-enable the lazy restore machinery by disabling the NEON/VFP unit. This patch adds the functions kernel_neon_begin() and kernel_neon_end() which take care of the above. It also adds the Kconfig symbol KERNEL_MODE_NEON to enable it. Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Acked-by: Nicolas Pitre <nico@linaro.org>
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/user.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <asm/cp15.h>
|
||||
#include <asm/cputype.h>
|
||||
@@ -668,6 +669,52 @@ void vfp_kmode_exception(void)
|
||||
pr_crit("BUG: FP instruction issued in kernel mode with FP unit disabled\n");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KERNEL_MODE_NEON
|
||||
|
||||
/*
|
||||
* Kernel-side NEON support functions
|
||||
*/
|
||||
void kernel_neon_begin(void)
|
||||
{
|
||||
struct thread_info *thread = current_thread_info();
|
||||
unsigned int cpu;
|
||||
u32 fpexc;
|
||||
|
||||
/*
|
||||
* Kernel mode NEON is only allowed outside of interrupt context
|
||||
* with preemption disabled. This will make sure that the kernel
|
||||
* mode NEON register contents never need to be preserved.
|
||||
*/
|
||||
BUG_ON(in_interrupt());
|
||||
cpu = get_cpu();
|
||||
|
||||
fpexc = fmrx(FPEXC) | FPEXC_EN;
|
||||
fmxr(FPEXC, fpexc);
|
||||
|
||||
/*
|
||||
* Save the userland NEON/VFP state. Under UP,
|
||||
* the owner could be a task other than 'current'
|
||||
*/
|
||||
if (vfp_state_in_hw(cpu, thread))
|
||||
vfp_save_state(&thread->vfpstate, fpexc);
|
||||
#ifndef CONFIG_SMP
|
||||
else if (vfp_current_hw_state[cpu] != NULL)
|
||||
vfp_save_state(vfp_current_hw_state[cpu], fpexc);
|
||||
#endif
|
||||
vfp_current_hw_state[cpu] = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(kernel_neon_begin);
|
||||
|
||||
void kernel_neon_end(void)
|
||||
{
|
||||
/* Disable the NEON/VFP unit. */
|
||||
fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
|
||||
put_cpu();
|
||||
}
|
||||
EXPORT_SYMBOL(kernel_neon_end);
|
||||
|
||||
#endif /* CONFIG_KERNEL_MODE_NEON */
|
||||
|
||||
/*
|
||||
* VFP support code initialisation.
|
||||
*/
|
||||
|
Reference in New Issue
Block a user