summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/freebl/blinit.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/freebl/blinit.c')
-rw-r--r--security/nss/lib/freebl/blinit.c147
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;
}