diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index cb8f63a5..0b64a0b4 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -72,6 +72,7 @@ if (${ARCH} STREQUAL "arm") CRYPTO_ARCH_SOURCES cpu-arm.c + cpu-arm-asm.S ) endif() diff --git a/crypto/cpu-arm-asm.S b/crypto/cpu-arm-asm.S new file mode 100644 index 00000000..7941f5f0 --- /dev/null +++ b/crypto/cpu-arm-asm.S @@ -0,0 +1,32 @@ +# Copyright (c) 2014, Google Inc. +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +#if !defined(OPENSSL_NO_ASM) + +.syntax unified +.cpu cortex-a8 +.fpu neon +.text +.thumb +.align 2 +.global CRYPTO_arm_neon_probe +.hidden CRYPTO_arm_neon_probe +.type CRYPTO_arm_neon_probe, %function +.thumb_func +CRYPTO_arm_neon_probe: + vorr q1, q1, q1 + bx lr +.section .note.GNU-stack,"",%progbits + +#endif /* !OPENSSL_NO_ASM */ diff --git a/crypto/cpu-arm.c b/crypto/cpu-arm.c index 96392d8d..12098bdf 100644 --- a/crypto/cpu-arm.c +++ b/crypto/cpu-arm.c @@ -17,7 +17,8 @@ #if defined(OPENSSL_ARM) || defined(OPENSSL_AARCH64) #include -#include +#include +#include #include "arm_arch.h" @@ -35,7 +36,11 @@ char CRYPTO_is_NEON_capable(void) { return (OPENSSL_armcap_P & ARMV7_NEON) != 0; } +static char g_set_neon_called = 0; + void CRYPTO_set_NEON_capable(char neon_capable) { + g_set_neon_called = 1; + if (neon_capable) { OPENSSL_armcap_P |= ARMV7_NEON; } else { @@ -56,8 +61,66 @@ void CRYPTO_set_NEON_functional(char neon_functional) { } } +static sigjmp_buf sigill_jmp; + +static void sigill_handler(int signal) { + siglongjmp(sigill_jmp, signal); +} + +void CRYPTO_arm_neon_probe(); + +// probe_for_NEON returns 1 if a NEON instruction runs successfully. Because +// getauxval doesn't exist on Android until Jelly Bean, supporting NEON on +// older devices requires this. +static int probe_for_NEON() { + int supported = 0; + +#if !defined(OPENSSL_NO_ASM) + sigset_t sigmask; + sigfillset(&sigmask); + sigdelset(&sigmask, SIGILL); + sigdelset(&sigmask, SIGTRAP); + sigdelset(&sigmask, SIGFPE); + sigdelset(&sigmask, SIGBUS); + sigdelset(&sigmask, SIGSEGV); + + struct sigaction sigill_original_action, sigill_action; + memset(&sigill_action, 0, sizeof(sigill_action)); + sigill_action.sa_handler = sigill_handler; + sigill_action.sa_mask = sigmask; + + sigset_t original_sigmask; + sigprocmask(SIG_SETMASK, &sigmask, &original_sigmask); + sigaction(SIGILL, &sigill_action, &sigill_original_action); + + + if (sigsetjmp(sigill_jmp, 1 /* save signals */) == 0) { + // This function cannot be inline asm because GCC will refuse to compile + // inline NEON instructions unless building with -mfpu=neon, which would + // defeat the point of probing for support at runtime. + CRYPTO_arm_neon_probe(); + supported = 1; + } + // Note that Android up to and including Lollipop doesn't restore the signal + // mask correctly after returning from a sigsetjmp. So that would need to be + // set again here if more probes were added. + // See https://android-review.googlesource.com/#/c/127624/ + + sigaction(SIGILL, &sigill_original_action, NULL); + sigprocmask(SIG_SETMASK, &original_sigmask, NULL); +#endif + + return supported; +} + void OPENSSL_cpuid_setup(void) { if (getauxval == NULL) { + // |CRYPTO_is_NEON_capable| can be true even if |CRYPTO_set_NEON_capable| + // has never been called if the code was compiled with NEON support enabled + // (e.g. -mfpu=neon). + if (!g_set_neon_called && !CRYPTO_is_NEON_capable() && probe_for_NEON()) { + OPENSSL_armcap_P |= ARMV7_NEON; + } return; }