blob: a5f1f8ac3353e3a4e876927dc58ba767f599c8d4 [file] [log] [blame]
David Benjamin4969cc92016-04-22 15:02:23 -04001/* Copyright (c) 2016, Google Inc.
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15#include <openssl/cpu.h>
16
17#if defined(OPENSSL_ARM) && !defined(OPENSSL_STATIC_ARMCAP)
18
19#include <errno.h>
20#include <fcntl.h>
21#include <string.h>
22#include <sys/types.h>
23#include <unistd.h>
24
25#include <openssl/arm_arch.h>
26#include <openssl/buf.h>
27#include <openssl/mem.h>
28
29#include "internal.h"
30
31
32#define AT_HWCAP 16
33#define AT_HWCAP2 26
34
35#define HWCAP_NEON (1 << 12)
36
Robert Sloan8f860b12017-08-28 07:37:06 -070037// See /usr/include/asm/hwcap.h on an ARM installation for the source of
38// these values.
David Benjamin4969cc92016-04-22 15:02:23 -040039#define HWCAP2_AES (1 << 0)
40#define HWCAP2_PMULL (1 << 1)
41#define HWCAP2_SHA1 (1 << 2)
42#define HWCAP2_SHA2 (1 << 3)
43
Robert Sloan8f860b12017-08-28 07:37:06 -070044// |getauxval| is not available on Android until API level 20. Link it as a weak
45// symbol and use other methods as fallback.
David Benjamin4969cc92016-04-22 15:02:23 -040046unsigned long getauxval(unsigned long type) __attribute__((weak));
47
48static int open_eintr(const char *path, int flags) {
49 int ret;
50 do {
51 ret = open(path, flags);
52 } while (ret < 0 && errno == EINTR);
53 return ret;
54}
55
56static ssize_t read_eintr(int fd, void *out, size_t len) {
57 ssize_t ret;
58 do {
59 ret = read(fd, out, len);
60 } while (ret < 0 && errno == EINTR);
61 return ret;
62}
63
Robert Sloan8f860b12017-08-28 07:37:06 -070064// read_full reads exactly |len| bytes from |fd| to |out|. On error or end of
65// file, it returns zero.
David Benjamin4969cc92016-04-22 15:02:23 -040066static int read_full(int fd, void *out, size_t len) {
67 char *outp = out;
68 while (len > 0) {
69 ssize_t ret = read_eintr(fd, outp, len);
70 if (ret <= 0) {
71 return 0;
72 }
73 outp += ret;
74 len -= ret;
75 }
76 return 1;
77}
78
Robert Sloan8f860b12017-08-28 07:37:06 -070079// read_file opens |path| and reads until end-of-file. On success, it returns
80// one and sets |*out_ptr| and |*out_len| to a newly-allocated buffer with the
81// contents. Otherwise, it returns zero.
David Benjamin4969cc92016-04-22 15:02:23 -040082static int read_file(char **out_ptr, size_t *out_len, const char *path) {
83 int fd = open_eintr(path, O_RDONLY);
84 if (fd < 0) {
85 return 0;
86 }
87
88 static const size_t kReadSize = 1024;
89 int ret = 0;
90 size_t cap = kReadSize, len = 0;
91 char *buf = OPENSSL_malloc(cap);
92 if (buf == NULL) {
93 goto err;
94 }
95
96 for (;;) {
97 if (cap - len < kReadSize) {
98 size_t new_cap = cap * 2;
99 if (new_cap < cap) {
100 goto err;
101 }
102 char *new_buf = OPENSSL_realloc(buf, new_cap);
103 if (new_buf == NULL) {
104 goto err;
105 }
106 buf = new_buf;
107 cap = new_cap;
108 }
109
110 ssize_t bytes_read = read_eintr(fd, buf + len, kReadSize);
111 if (bytes_read < 0) {
112 goto err;
113 }
114 if (bytes_read == 0) {
115 break;
116 }
117 len += bytes_read;
118 }
119
120 *out_ptr = buf;
121 *out_len = len;
122 ret = 1;
123 buf = NULL;
124
125err:
126 OPENSSL_free(buf);
127 close(fd);
128 return ret;
129}
130
Robert Sloan8f860b12017-08-28 07:37:06 -0700131// getauxval_proc behaves like |getauxval| but reads from /proc/self/auxv.
David Benjamin4969cc92016-04-22 15:02:23 -0400132static unsigned long getauxval_proc(unsigned long type) {
133 int fd = open_eintr("/proc/self/auxv", O_RDONLY);
134 if (fd < 0) {
135 return 0;
136 }
137
138 struct {
139 unsigned long tag;
140 unsigned long value;
141 } entry;
142
143 for (;;) {
144 if (!read_full(fd, &entry, sizeof(entry)) ||
145 (entry.tag == 0 && entry.value == 0)) {
146 break;
147 }
148 if (entry.tag == type) {
149 close(fd);
150 return entry.value;
151 }
152 }
153 close(fd);
154 return 0;
155}
156
157typedef struct {
158 const char *data;
159 size_t len;
160} STRING_PIECE;
161
162static int STRING_PIECE_equals(const STRING_PIECE *a, const char *b) {
163 size_t b_len = strlen(b);
Robert Sloan69939df2017-01-09 10:53:07 -0800164 return a->len == b_len && OPENSSL_memcmp(a->data, b, b_len) == 0;
David Benjamin4969cc92016-04-22 15:02:23 -0400165}
166
Robert Sloan8f860b12017-08-28 07:37:06 -0700167// STRING_PIECE_split finds the first occurence of |sep| in |in| and, if found,
168// sets |*out_left| and |*out_right| to |in| split before and after it. It
169// returns one if |sep| was found and zero otherwise.
David Benjamin4969cc92016-04-22 15:02:23 -0400170static int STRING_PIECE_split(STRING_PIECE *out_left, STRING_PIECE *out_right,
171 const STRING_PIECE *in, char sep) {
Robert Sloan69939df2017-01-09 10:53:07 -0800172 const char *p = OPENSSL_memchr(in->data, sep, in->len);
David Benjamin4969cc92016-04-22 15:02:23 -0400173 if (p == NULL) {
174 return 0;
175 }
Robert Sloan8f860b12017-08-28 07:37:06 -0700176 // |out_left| or |out_right| may alias |in|, so make a copy.
David Benjamin4969cc92016-04-22 15:02:23 -0400177 STRING_PIECE in_copy = *in;
178 out_left->data = in_copy.data;
179 out_left->len = p - in_copy.data;
180 out_right->data = in_copy.data + out_left->len + 1;
181 out_right->len = in_copy.len - out_left->len - 1;
182 return 1;
183}
184
Robert Sloan8f860b12017-08-28 07:37:06 -0700185// STRING_PIECE_trim removes leading and trailing whitespace from |s|.
David Benjamin4969cc92016-04-22 15:02:23 -0400186static void STRING_PIECE_trim(STRING_PIECE *s) {
187 while (s->len != 0 && (s->data[0] == ' ' || s->data[0] == '\t')) {
188 s->data++;
189 s->len--;
190 }
191 while (s->len != 0 &&
192 (s->data[s->len - 1] == ' ' || s->data[s->len - 1] == '\t')) {
193 s->len--;
194 }
195}
196
Robert Sloan8f860b12017-08-28 07:37:06 -0700197// extract_cpuinfo_field extracts a /proc/cpuinfo field named |field| from
198// |in|. If found, it sets |*out| to the value and returns one. Otherwise, it
199// returns zero.
David Benjamin4969cc92016-04-22 15:02:23 -0400200static int extract_cpuinfo_field(STRING_PIECE *out, const STRING_PIECE *in,
201 const char *field) {
Robert Sloan8f860b12017-08-28 07:37:06 -0700202 // Process |in| one line at a time.
David Benjamin4969cc92016-04-22 15:02:23 -0400203 STRING_PIECE remaining = *in, line;
204 while (STRING_PIECE_split(&line, &remaining, &remaining, '\n')) {
205 STRING_PIECE key, value;
206 if (!STRING_PIECE_split(&key, &value, &line, ':')) {
207 continue;
208 }
209 STRING_PIECE_trim(&key);
210 if (STRING_PIECE_equals(&key, field)) {
211 STRING_PIECE_trim(&value);
212 *out = value;
213 return 1;
214 }
215 }
216
217 return 0;
218}
219
220static int cpuinfo_field_equals(const STRING_PIECE *cpuinfo, const char *field,
221 const char *value) {
222 STRING_PIECE extracted;
223 return extract_cpuinfo_field(&extracted, cpuinfo, field) &&
224 STRING_PIECE_equals(&extracted, value);
225}
226
Robert Sloan8f860b12017-08-28 07:37:06 -0700227// has_list_item treats |list| as a space-separated list of items and returns
228// one if |item| is contained in |list| and zero otherwise.
David Benjamin4969cc92016-04-22 15:02:23 -0400229static int has_list_item(const STRING_PIECE *list, const char *item) {
230 STRING_PIECE remaining = *list, feature;
231 while (STRING_PIECE_split(&feature, &remaining, &remaining, ' ')) {
232 if (STRING_PIECE_equals(&feature, item)) {
233 return 1;
234 }
235 }
236 return 0;
237}
238
239static unsigned long get_hwcap_cpuinfo(const STRING_PIECE *cpuinfo) {
240 if (cpuinfo_field_equals(cpuinfo, "CPU architecture", "8")) {
Robert Sloan8f860b12017-08-28 07:37:06 -0700241 // This is a 32-bit ARM binary running on a 64-bit kernel. NEON is always
242 // available on ARMv8. Linux omits required features, so reading the
243 // "Features" line does not work. (For simplicity, use strict equality. We
244 // assume everything running on future ARM architectures will have a
245 // working |getauxval|.)
David Benjamin4969cc92016-04-22 15:02:23 -0400246 return HWCAP_NEON;
247 }
248
249 STRING_PIECE features;
250 if (extract_cpuinfo_field(&features, cpuinfo, "Features") &&
251 has_list_item(&features, "neon")) {
252 return HWCAP_NEON;
253 }
254 return 0;
255}
256
257static unsigned long get_hwcap2_cpuinfo(const STRING_PIECE *cpuinfo) {
258 STRING_PIECE features;
259 if (!extract_cpuinfo_field(&features, cpuinfo, "Features")) {
260 return 0;
261 }
262
263 unsigned long ret = 0;
264 if (has_list_item(&features, "aes")) {
265 ret |= HWCAP2_AES;
266 }
267 if (has_list_item(&features, "pmull")) {
268 ret |= HWCAP2_PMULL;
269 }
270 if (has_list_item(&features, "sha1")) {
271 ret |= HWCAP2_SHA1;
272 }
273 if (has_list_item(&features, "sha2")) {
274 ret |= HWCAP2_SHA2;
275 }
276 return ret;
277}
278
Robert Sloan8f860b12017-08-28 07:37:06 -0700279// has_broken_neon returns one if |in| matches a CPU known to have a broken
280// NEON unit. See https://crbug.com/341598.
David Benjamin4969cc92016-04-22 15:02:23 -0400281static int has_broken_neon(const STRING_PIECE *cpuinfo) {
282 return cpuinfo_field_equals(cpuinfo, "CPU implementer", "0x51") &&
283 cpuinfo_field_equals(cpuinfo, "CPU architecture", "7") &&
284 cpuinfo_field_equals(cpuinfo, "CPU variant", "0x1") &&
285 cpuinfo_field_equals(cpuinfo, "CPU part", "0x04d") &&
286 cpuinfo_field_equals(cpuinfo, "CPU revision", "0");
287}
288
289extern uint32_t OPENSSL_armcap_P;
290
291static int g_has_broken_neon;
292
293void OPENSSL_cpuid_setup(void) {
294 char *cpuinfo_data;
295 size_t cpuinfo_len;
296 if (!read_file(&cpuinfo_data, &cpuinfo_len, "/proc/cpuinfo")) {
297 return;
298 }
299 STRING_PIECE cpuinfo;
300 cpuinfo.data = cpuinfo_data;
301 cpuinfo.len = cpuinfo_len;
302
Robert Sloan8f860b12017-08-28 07:37:06 -0700303 // |getauxval| is not available on Android until API level 20. If it is
304 // unavailable, read from /proc/self/auxv as a fallback. This is unreadable
305 // on some versions of Android, so further fall back to /proc/cpuinfo.
306 //
307 // See
308 // https://android.googlesource.com/platform/ndk/+/882ac8f3392858991a0e1af33b4b7387ec856bd2
309 // and b/13679666 (Google-internal) for details.
David Benjamin4969cc92016-04-22 15:02:23 -0400310 unsigned long hwcap = 0;
311 if (getauxval != NULL) {
312 hwcap = getauxval(AT_HWCAP);
313 }
314 if (hwcap == 0) {
315 hwcap = getauxval_proc(AT_HWCAP);
316 }
317 if (hwcap == 0) {
318 hwcap = get_hwcap_cpuinfo(&cpuinfo);
319 }
320
Robert Sloan8f860b12017-08-28 07:37:06 -0700321 // Clear NEON support if known broken.
David Benjamin4969cc92016-04-22 15:02:23 -0400322 g_has_broken_neon = has_broken_neon(&cpuinfo);
323 if (g_has_broken_neon) {
324 hwcap &= ~HWCAP_NEON;
325 }
326
Robert Sloan8f860b12017-08-28 07:37:06 -0700327 // Matching OpenSSL, only report other features if NEON is present.
David Benjamin4969cc92016-04-22 15:02:23 -0400328 if (hwcap & HWCAP_NEON) {
329 OPENSSL_armcap_P |= ARMV7_NEON;
330
Robert Sloan8f860b12017-08-28 07:37:06 -0700331 // Some ARMv8 Android devices don't expose AT_HWCAP2. Fall back to
332 // /proc/cpuinfo. See https://crbug.com/596156.
David Benjamin4969cc92016-04-22 15:02:23 -0400333 unsigned long hwcap2 = 0;
334 if (getauxval != NULL) {
335 hwcap2 = getauxval(AT_HWCAP2);
336 }
337 if (hwcap2 == 0) {
338 hwcap2 = get_hwcap2_cpuinfo(&cpuinfo);
339 }
340
341 if (hwcap2 & HWCAP2_AES) {
342 OPENSSL_armcap_P |= ARMV8_AES;
343 }
344 if (hwcap2 & HWCAP2_PMULL) {
345 OPENSSL_armcap_P |= ARMV8_PMULL;
346 }
347 if (hwcap2 & HWCAP2_SHA1) {
348 OPENSSL_armcap_P |= ARMV8_SHA1;
349 }
350 if (hwcap2 & HWCAP2_SHA2) {
351 OPENSSL_armcap_P |= ARMV8_SHA256;
352 }
353 }
354
355 OPENSSL_free(cpuinfo_data);
356}
357
358int CRYPTO_has_broken_NEON(void) { return g_has_broken_neon; }
359
Robert Sloan8f860b12017-08-28 07:37:06 -0700360#endif // OPENSSL_ARM && !OPENSSL_STATIC_ARMCAP