1 | /* 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 | #include <errno.h> |
19 | #include <fcntl.h> |
20 | #include <sys/types.h> |
21 | #include <unistd.h> |
22 | |
23 | #include <openssl/arm_arch.h> |
24 | #include <openssl/buf.h> |
25 | #include <openssl/mem.h> |
26 | |
27 | #include "cpu-arm-linux.h" |
28 | |
29 | #define AT_HWCAP 16 |
30 | #define AT_HWCAP2 26 |
31 | |
32 | // |getauxval| is not available on Android until API level 20. Link it as a weak |
33 | // symbol and use other methods as fallback. |
34 | unsigned long getauxval(unsigned long type) __attribute__((weak)); |
35 | |
36 | static int open_eintr(const char *path, int flags) { |
37 | int ret; |
38 | do { |
39 | ret = open(path, flags); |
40 | } while (ret < 0 && errno == EINTR); |
41 | return ret; |
42 | } |
43 | |
44 | static ssize_t read_eintr(int fd, void *out, size_t len) { |
45 | ssize_t ret; |
46 | do { |
47 | ret = read(fd, out, len); |
48 | } while (ret < 0 && errno == EINTR); |
49 | return ret; |
50 | } |
51 | |
52 | // read_full reads exactly |len| bytes from |fd| to |out|. On error or end of |
53 | // file, it returns zero. |
54 | static int read_full(int fd, void *out, size_t len) { |
55 | char *outp = out; |
56 | while (len > 0) { |
57 | ssize_t ret = read_eintr(fd, outp, len); |
58 | if (ret <= 0) { |
59 | return 0; |
60 | } |
61 | outp += ret; |
62 | len -= ret; |
63 | } |
64 | return 1; |
65 | } |
66 | |
67 | // read_file opens |path| and reads until end-of-file. On success, it returns |
68 | // one and sets |*out_ptr| and |*out_len| to a newly-allocated buffer with the |
69 | // contents. Otherwise, it returns zero. |
70 | static int read_file(char **out_ptr, size_t *out_len, const char *path) { |
71 | int fd = open_eintr(path, O_RDONLY); |
72 | if (fd < 0) { |
73 | return 0; |
74 | } |
75 | |
76 | static const size_t kReadSize = 1024; |
77 | int ret = 0; |
78 | size_t cap = kReadSize, len = 0; |
79 | char *buf = OPENSSL_malloc(cap); |
80 | if (buf == NULL) { |
81 | goto err; |
82 | } |
83 | |
84 | for (;;) { |
85 | if (cap - len < kReadSize) { |
86 | size_t new_cap = cap * 2; |
87 | if (new_cap < cap) { |
88 | goto err; |
89 | } |
90 | char *new_buf = OPENSSL_realloc(buf, new_cap); |
91 | if (new_buf == NULL) { |
92 | goto err; |
93 | } |
94 | buf = new_buf; |
95 | cap = new_cap; |
96 | } |
97 | |
98 | ssize_t bytes_read = read_eintr(fd, buf + len, kReadSize); |
99 | if (bytes_read < 0) { |
100 | goto err; |
101 | } |
102 | if (bytes_read == 0) { |
103 | break; |
104 | } |
105 | len += bytes_read; |
106 | } |
107 | |
108 | *out_ptr = buf; |
109 | *out_len = len; |
110 | ret = 1; |
111 | buf = NULL; |
112 | |
113 | err: |
114 | OPENSSL_free(buf); |
115 | close(fd); |
116 | return ret; |
117 | } |
118 | |
119 | // getauxval_proc behaves like |getauxval| but reads from /proc/self/auxv. |
120 | static unsigned long getauxval_proc(unsigned long type) { |
121 | int fd = open_eintr("/proc/self/auxv" , O_RDONLY); |
122 | if (fd < 0) { |
123 | return 0; |
124 | } |
125 | |
126 | struct { |
127 | unsigned long tag; |
128 | unsigned long value; |
129 | } entry; |
130 | |
131 | for (;;) { |
132 | if (!read_full(fd, &entry, sizeof(entry)) || |
133 | (entry.tag == 0 && entry.value == 0)) { |
134 | break; |
135 | } |
136 | if (entry.tag == type) { |
137 | close(fd); |
138 | return entry.value; |
139 | } |
140 | } |
141 | close(fd); |
142 | return 0; |
143 | } |
144 | |
145 | extern uint32_t OPENSSL_armcap_P; |
146 | |
147 | static int g_has_broken_neon, g_needs_hwcap2_workaround; |
148 | |
149 | void OPENSSL_cpuid_setup(void) { |
150 | char *cpuinfo_data; |
151 | size_t cpuinfo_len; |
152 | if (!read_file(&cpuinfo_data, &cpuinfo_len, "/proc/cpuinfo" )) { |
153 | return; |
154 | } |
155 | STRING_PIECE cpuinfo; |
156 | cpuinfo.data = cpuinfo_data; |
157 | cpuinfo.len = cpuinfo_len; |
158 | |
159 | // |getauxval| is not available on Android until API level 20. If it is |
160 | // unavailable, read from /proc/self/auxv as a fallback. This is unreadable |
161 | // on some versions of Android, so further fall back to /proc/cpuinfo. |
162 | // |
163 | // See |
164 | // https://android.googlesource.com/platform/ndk/+/882ac8f3392858991a0e1af33b4b7387ec856bd2 |
165 | // and b/13679666 (Google-internal) for details. |
166 | unsigned long hwcap = 0; |
167 | if (getauxval != NULL) { |
168 | hwcap = getauxval(AT_HWCAP); |
169 | } |
170 | if (hwcap == 0) { |
171 | hwcap = getauxval_proc(AT_HWCAP); |
172 | } |
173 | if (hwcap == 0) { |
174 | hwcap = crypto_get_arm_hwcap_from_cpuinfo(&cpuinfo); |
175 | } |
176 | |
177 | // Clear NEON support if known broken. |
178 | g_has_broken_neon = crypto_cpuinfo_has_broken_neon(&cpuinfo); |
179 | if (g_has_broken_neon) { |
180 | hwcap &= ~HWCAP_NEON; |
181 | } |
182 | |
183 | // Matching OpenSSL, only report other features if NEON is present. |
184 | if (hwcap & HWCAP_NEON) { |
185 | OPENSSL_armcap_P |= ARMV7_NEON; |
186 | |
187 | // Some ARMv8 Android devices don't expose AT_HWCAP2. Fall back to |
188 | // /proc/cpuinfo. See https://crbug.com/596156. |
189 | unsigned long hwcap2 = 0; |
190 | if (getauxval != NULL) { |
191 | hwcap2 = getauxval(AT_HWCAP2); |
192 | } |
193 | if (hwcap2 == 0) { |
194 | hwcap2 = crypto_get_arm_hwcap2_from_cpuinfo(&cpuinfo); |
195 | g_needs_hwcap2_workaround = hwcap2 != 0; |
196 | } |
197 | |
198 | if (hwcap2 & HWCAP2_AES) { |
199 | OPENSSL_armcap_P |= ARMV8_AES; |
200 | } |
201 | if (hwcap2 & HWCAP2_PMULL) { |
202 | OPENSSL_armcap_P |= ARMV8_PMULL; |
203 | } |
204 | if (hwcap2 & HWCAP2_SHA1) { |
205 | OPENSSL_armcap_P |= ARMV8_SHA1; |
206 | } |
207 | if (hwcap2 & HWCAP2_SHA2) { |
208 | OPENSSL_armcap_P |= ARMV8_SHA256; |
209 | } |
210 | } |
211 | |
212 | OPENSSL_free(cpuinfo_data); |
213 | } |
214 | |
215 | int CRYPTO_has_broken_NEON(void) { return g_has_broken_neon; } |
216 | |
217 | int CRYPTO_needs_hwcap2_workaround(void) { return g_needs_hwcap2_workaround; } |
218 | |
219 | #endif // OPENSSL_ARM && !OPENSSL_STATIC_ARMCAP |
220 | |