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