1 // Copyright 2016 The BoringSSL Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "internal.h"
16 
17 #if !defined(OPENSSL_NO_ASM) && defined(OPENSSL_ARM) && \
18     defined(OPENSSL_LINUX) && !defined(OPENSSL_STATIC_ARMCAP)
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <sys/auxv.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24 
25 #include <openssl/mem.h>
26 
27 #include "cpu_arm_linux.h"
28 
open_eintr(const char * path,int flags)29 static int open_eintr(const char *path, int flags) {
30   int ret;
31   do {
32     ret = open(path, flags);
33   } while (ret < 0 && errno == EINTR);
34   return ret;
35 }
36 
read_eintr(int fd,void * out,size_t len)37 static ssize_t read_eintr(int fd, void *out, size_t len) {
38   ssize_t ret;
39   do {
40     ret = read(fd, out, len);
41   } while (ret < 0 && errno == EINTR);
42   return ret;
43 }
44 
45 // read_file opens |path| and reads until end-of-file. On success, it returns
46 // one and sets |*out_ptr| and |*out_len| to a newly-allocated buffer with the
47 // contents. Otherwise, it returns zero.
read_file(char ** out_ptr,size_t * out_len,const char * path)48 static int read_file(char **out_ptr, size_t *out_len, const char *path) {
49   int fd = open_eintr(path, O_RDONLY);
50   if (fd < 0) {
51     return 0;
52   }
53 
54   static const size_t kReadSize = 1024;
55   int ret = 0;
56   size_t cap = kReadSize, len = 0;
57   char *buf = reinterpret_cast<char *>(OPENSSL_malloc(cap));
58   if (buf == NULL) {
59     goto err;
60   }
61 
62   for (;;) {
63     if (cap - len < kReadSize) {
64       size_t new_cap = cap * 2;
65       if (new_cap < cap) {
66         goto err;
67       }
68       char *new_buf = reinterpret_cast<char *>(OPENSSL_realloc(buf, new_cap));
69       if (new_buf == NULL) {
70         goto err;
71       }
72       buf = new_buf;
73       cap = new_cap;
74     }
75 
76     ssize_t bytes_read = read_eintr(fd, buf + len, kReadSize);
77     if (bytes_read < 0) {
78       goto err;
79     }
80     if (bytes_read == 0) {
81       break;
82     }
83     len += bytes_read;
84   }
85 
86   *out_ptr = buf;
87   *out_len = len;
88   ret = 1;
89   buf = NULL;
90 
91 err:
92   OPENSSL_free(buf);
93   close(fd);
94   return ret;
95 }
96 
97 static int g_needs_hwcap2_workaround;
98 
OPENSSL_cpuid_setup(void)99 void OPENSSL_cpuid_setup(void) {
100   // We ignore the return value of |read_file| and proceed with an empty
101   // /proc/cpuinfo on error. If |getauxval| works, we will still detect
102   // capabilities.
103   char *cpuinfo_data = NULL;
104   size_t cpuinfo_len = 0;
105   read_file(&cpuinfo_data, &cpuinfo_len, "/proc/cpuinfo");
106   STRING_PIECE cpuinfo;
107   cpuinfo.data = cpuinfo_data;
108   cpuinfo.len = cpuinfo_len;
109 
110   // Matching OpenSSL, only report other features if NEON is present.
111   unsigned long hwcap = getauxval(AT_HWCAP);
112   if (hwcap & CRYPTO_HWCAP_NEON) {
113 #if defined(HWCAP_ARM_NEON)
114       static_assert(HWCAP_ARM_NEON == CRYPTO_HWCAP_NEON,
115                     "CRYPTO_HWCAP values must match Linux");
116 #endif
117     OPENSSL_armcap_P |= ARMV7_NEON;
118 
119     // Some ARMv8 Android devices don't expose AT_HWCAP2. Fall back to
120     // /proc/cpuinfo. See https://crbug.com/boringssl/46. As of February 2021,
121     // this is now rare (see Chrome's Net.NeedsHWCAP2Workaround metric), but AES
122     // and PMULL extensions are very useful, so we still carry the workaround
123     // for now.
124     unsigned long hwcap2 = getauxval(AT_HWCAP2);
125     if (hwcap2 == 0) {
126       hwcap2 = crypto_get_arm_hwcap2_from_cpuinfo(&cpuinfo);
127       g_needs_hwcap2_workaround = hwcap2 != 0;
128     }
129 
130     // HWCAP2_* values, without the "CRYPTO_" prefix, are exposed through
131     // <sys/auxv.h> in some versions of glibc(>= 2.41). Assert that we don't
132     // diverge from those values.
133     if (hwcap2 & CRYPTO_HWCAP2_AES) {
134 #if defined(HWCAP2_AES)
135       static_assert(HWCAP2_AES == CRYPTO_HWCAP2_AES,
136                     "CRYPTO_HWCAP2 values must match Linux");
137 #endif
138       OPENSSL_armcap_P |= ARMV8_AES;
139     }
140     if (hwcap2 & CRYPTO_HWCAP2_PMULL) {
141 #if defined(HWCAP2_PMULL)
142       static_assert(HWCAP2_PMULL == CRYPTO_HWCAP2_PMULL,
143                     "CRYPTO_HWCAP2 values must match Linux");
144 #endif
145       OPENSSL_armcap_P |= ARMV8_PMULL;
146     }
147     if (hwcap2 & CRYPTO_HWCAP2_SHA1) {
148 #if defined(HWCAP2_SHA1)
149       static_assert(HWCAP2_SHA1 == CRYPTO_HWCAP2_SHA1,
150                     "CRYPTO_HWCAP2 values must match Linux");
151 #endif
152       OPENSSL_armcap_P |= ARMV8_SHA1;
153     }
154     if (hwcap2 & CRYPTO_HWCAP2_SHA2) {
155 #if defined(HWCAP2_SHA2)
156       static_assert(HWCAP2_SHA2 == CRYPTO_HWCAP2_SHA2,
157                     "CRYPTO_HWCAP2 values must match Linux");
158 #endif
159       OPENSSL_armcap_P |= ARMV8_SHA256;
160     }
161   }
162 
163   OPENSSL_free(cpuinfo_data);
164 }
165 
CRYPTO_has_broken_NEON(void)166 int CRYPTO_has_broken_NEON(void) { return 0; }
167 
CRYPTO_needs_hwcap2_workaround(void)168 int CRYPTO_needs_hwcap2_workaround(void) {
169   OPENSSL_init_cpuid();
170   return g_needs_hwcap2_workaround;
171 }
172 
173 #endif  // OPENSSL_ARM && OPENSSL_LINUX && !OPENSSL_STATIC_ARMCAP
174