diff options
Diffstat (limited to 'security/nss/lib/freebl/blinit.c')
-rw-r--r-- | security/nss/lib/freebl/blinit.c | 147 |
1 files changed, 134 insertions, 13 deletions
diff --git a/security/nss/lib/freebl/blinit.c b/security/nss/lib/freebl/blinit.c index f369e62e7..959109b60 100644 --- a/security/nss/lib/freebl/blinit.c +++ b/security/nss/lib/freebl/blinit.c @@ -29,6 +29,7 @@ static PRBool arm_aes_support_ = PR_FALSE; static PRBool arm_sha1_support_ = PR_FALSE; static PRBool arm_sha2_support_ = PR_FALSE; static PRBool arm_pmull_support_ = PR_FALSE; +static PRBool ppc_crypto_support_ = PR_FALSE; #ifdef NSS_X86_OR_X64 /* @@ -92,23 +93,32 @@ CheckX86CPUSupport() #endif /* NSS_X86_OR_X64 */ /* clang-format off */ -#if (defined(__aarch64__) || defined(__arm__)) && !defined(__ANDROID__) +#if defined(__aarch64__) || defined(__arm__) #ifndef __has_include #define __has_include(x) 0 #endif #if (__has_include(<sys/auxv.h>) || defined(__linux__)) && \ defined(__GNUC__) && __GNUC__ >= 2 && defined(__ELF__) +/* This might be conflict with host compiler */ +#if !defined(__ANDROID__) #include <sys/auxv.h> +#endif extern unsigned long getauxval(unsigned long type) __attribute__((weak)); #else static unsigned long (*getauxval)(unsigned long) = NULL; -#define AT_HWCAP2 0 -#define AT_HWCAP 0 #endif /* defined(__GNUC__) && __GNUC__ >= 2 && defined(__ELF__)*/ -#endif /* (defined(__aarch64__) || defined(__arm__)) && !defined(__ANDROID__) */ + +#ifndef AT_HWCAP2 +#define AT_HWCAP2 26 +#endif +#ifndef AT_HWCAP +#define AT_HWCAP 16 +#endif + +#endif /* defined(__aarch64__) || defined(__arm__) */ /* clang-format on */ -#if defined(__aarch64__) && !defined(__ANDROID__) +#if defined(__aarch64__) // Defines from hwcap.h in Linux kernel - ARM64 #ifndef HWCAP_AES #define HWCAP_AES (1 << 3) @@ -128,19 +138,20 @@ CheckARMSupport() { char *disable_arm_neon = PR_GetEnvSecure("NSS_DISABLE_ARM_NEON"); char *disable_hw_aes = PR_GetEnvSecure("NSS_DISABLE_HW_AES"); + char *disable_pmull = PR_GetEnvSecure("NSS_DISABLE_PMULL"); if (getauxval) { long hwcaps = getauxval(AT_HWCAP); arm_aes_support_ = hwcaps & HWCAP_AES && disable_hw_aes == NULL; - arm_pmull_support_ = hwcaps & HWCAP_PMULL; + arm_pmull_support_ = hwcaps & HWCAP_PMULL && disable_pmull == NULL; arm_sha1_support_ = hwcaps & HWCAP_SHA1; arm_sha2_support_ = hwcaps & HWCAP_SHA2; } /* aarch64 must support NEON. */ arm_neon_support_ = disable_arm_neon == NULL; } -#endif /* defined(__aarch64__) && !defined(__ANDROID__) */ +#endif /* defined(__aarch64__) */ -#if defined(__arm__) && !defined(__ANDROID__) +#if defined(__arm__) // Defines from hwcap.h in Linux kernel - ARM /* * HWCAP flags - for elf_hwcap (in kernel) and AT_HWCAP @@ -165,23 +176,105 @@ CheckARMSupport() #define HWCAP2_SHA2 (1 << 3) #endif +PRBool +GetNeonSupport() +{ + char *disable_arm_neon = PR_GetEnvSecure("NSS_DISABLE_ARM_NEON"); + if (disable_arm_neon) { + return PR_FALSE; + } +#if defined(__ARM_NEON) || defined(__ARM_NEON__) + // Compiler generates NEON instruction as default option. + // If no getauxval, compiler generate NEON instruction by default, + // we should allow NOEN support. + return PR_TRUE; +#elif !defined(__ANDROID__) + // Android's cpu-features.c detects features by the following logic + // + // - Call getauxval(AT_HWCAP) + // - Parse /proc/self/auxv if getauxval is nothing or returns 0 + // - Parse /proc/cpuinfo if both cannot detect features + // + // But we don't use it for Android since Android document + // (https://developer.android.com/ndk/guides/cpu-features) says + // one problem with AT_HWCAP sometimes devices (Nexus 4 and emulator) + // are mistaken for IDIV. + if (getauxval) { + return (getauxval(AT_HWCAP) & HWCAP_NEON); + } +#endif /* defined(__ARM_NEON) || defined(__ARM_NEON__) */ + return PR_FALSE; +} + +#ifdef __linux__ +static long +ReadCPUInfoForHWCAP2() +{ + FILE *cpuinfo; + char buf[512]; + char *p; + long hwcap2 = 0; + + cpuinfo = fopen("/proc/cpuinfo", "r"); + if (!cpuinfo) { + return 0; + } + while (fgets(buf, 511, cpuinfo)) { + if (!memcmp(buf, "Features", 8)) { + p = strstr(buf, " aes"); + if (p && (p[4] == ' ' || p[4] == '\n')) { + hwcap2 |= HWCAP2_AES; + } + p = strstr(buf, " sha1"); + if (p && (p[5] == ' ' || p[5] == '\n')) { + hwcap2 |= HWCAP2_SHA1; + } + p = strstr(buf, " sha2"); + if (p && (p[5] == ' ' || p[5] == '\n')) { + hwcap2 |= HWCAP2_SHA2; + } + p = strstr(buf, " pmull"); + if (p && (p[6] == ' ' || p[6] == '\n')) { + hwcap2 |= HWCAP2_PMULL; + } + break; + } + } + + fclose(cpuinfo); + return hwcap2; +} +#endif /* __linux__ */ + void CheckARMSupport() { - char *disable_arm_neon = PR_GetEnvSecure("NSS_DISABLE_ARM_NEON"); char *disable_hw_aes = PR_GetEnvSecure("NSS_DISABLE_HW_AES"); if (getauxval) { + // Android's cpu-features.c uses AT_HWCAP2 for newer features. + // AT_HWCAP2 is implemented on newer devices / kernel, so we can trust + // it since cpu-features.c doesn't have workaround / fallback. + // Also, AT_HWCAP2 is supported by glibc 2.18+ on Linux/arm, If + // AT_HWCAP2 isn't supported by glibc or Linux kernel, getauxval will + // returns 0. long hwcaps = getauxval(AT_HWCAP2); +#ifdef __linux__ + if (!hwcaps) { + // Some ARMv8 devices may not implement AT_HWCAP2. So we also + // read /proc/cpuinfo if AT_HWCAP2 is 0. + hwcaps = ReadCPUInfoForHWCAP2(); + } +#endif arm_aes_support_ = hwcaps & HWCAP2_AES && disable_hw_aes == NULL; arm_pmull_support_ = hwcaps & HWCAP2_PMULL; arm_sha1_support_ = hwcaps & HWCAP2_SHA1; arm_sha2_support_ = hwcaps & HWCAP2_SHA2; - arm_neon_support_ = hwcaps & HWCAP_NEON && disable_arm_neon == NULL; } + arm_neon_support_ = GetNeonSupport(); } -#endif /* defined(__arm__) && !defined(__ANDROID__) */ +#endif /* defined(__arm__) */ -// Enable when Firefox can use it. +// Enable when Firefox can use it for Android API 16 and 17. // #if defined(__ANDROID__) && (defined(__arm__) || defined(__aarch64__)) // #include <cpu-features.h> // void @@ -256,14 +349,42 @@ arm_sha2_support() { return arm_sha2_support_; } +PRBool +ppc_crypto_support() +{ + return ppc_crypto_support_; +} + +#if defined(__powerpc__) + +#include <sys/auxv.h> + +// Defines from cputable.h in Linux kernel - PPC, letting us build on older kernels +#ifndef PPC_FEATURE2_VEC_CRYPTO +#define PPC_FEATURE2_VEC_CRYPTO 0x02000000 +#endif + +static void +CheckPPCSupport() +{ + char *disable_hw_crypto = PR_GetEnvSecure("NSS_DISABLE_PPC_GHASH"); + + long hwcaps = getauxval(AT_HWCAP2); + + ppc_crypto_support_ = hwcaps & PPC_FEATURE2_VEC_CRYPTO && disable_hw_crypto == NULL; +} + +#endif /* __powerpc__ */ static PRStatus FreeblInit(void) { #ifdef NSS_X86_OR_X64 CheckX86CPUSupport(); -#elif (defined(__aarch64__) || defined(__arm__)) && !defined(__ANDROID__) +#elif (defined(__aarch64__) || defined(__arm__)) CheckARMSupport(); +#elif (defined(__powerpc__)) + CheckPPCSupport(); #endif return PR_SUCCESS; } |