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:
parent
ab3da15643
commit
73c132c15d
|
@ -2197,6 +2197,13 @@ config NEON
|
|||
Say Y to include support code for NEON, the ARMv7 Advanced SIMD
|
||||
Extension.
|
||||
|
||||
config KERNEL_MODE_NEON
|
||||
bool "Support for NEON in kernel mode"
|
||||
default n
|
||||
depends on NEON
|
||||
help
|
||||
Say Y to include support for NEON in kernel mode.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Userspace binary formats"
|
||||
|
|
36
arch/arm/include/asm/neon.h
Normal file
36
arch/arm/include/asm/neon.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* linux/arch/arm/include/asm/neon.h
|
||||
*
|
||||
* Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <asm/hwcap.h>
|
||||
|
||||
#define cpu_has_neon() (!!(elf_hwcap & HWCAP_NEON))
|
||||
|
||||
#ifdef __ARM_NEON__
|
||||
|
||||
/*
|
||||
* If you are affected by the BUILD_BUG below, it probably means that you are
|
||||
* using NEON code /and/ calling the kernel_neon_begin() function from the same
|
||||
* compilation unit. To prevent issues that may arise from GCC reordering or
|
||||
* generating(1) NEON instructions outside of these begin/end functions, the
|
||||
* only supported way of using NEON code in the kernel is by isolating it in a
|
||||
* separate compilation unit, and calling it from another unit from inside a
|
||||
* kernel_neon_begin/kernel_neon_end pair.
|
||||
*
|
||||
* (1) Current GCC (4.7) might generate NEON instructions at O3 level if
|
||||
* -mpfu=neon is set.
|
||||
*/
|
||||
|
||||
#define kernel_neon_begin() \
|
||||
BUILD_BUG_ON_MSG(1, "kernel_neon_begin() called from NEON code")
|
||||
|
||||
#else
|
||||
void kernel_neon_begin(void);
|
||||
#endif
|
||||
void kernel_neon_end(void);
|
|
@ -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.
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue