1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *  * Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in
12  *    the documentation and/or other materials provided with the
13  *    distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <fcntl.h>
30 #include <sys/syscall.h>
31 
32 #include <private/bionic_ifuncs.h>
33 
34 extern "C" {
35 
36 enum CpuVariant {
37     kUnknown = 0,
38     kGeneric,
39     kCortexA7,
40     kCortexA9,
41     kCortexA53,
42     kCortexA55,
43     kKrait,
44     kKryo,
45 };
46 
47 static constexpr int MAX_CPU_NAME_LEN = 12;
48 struct CpuVariantNames {
49     alignas(alignof(int)) char name[MAX_CPU_NAME_LEN];
50     CpuVariant variant;
51 };
52 
53 static constexpr CpuVariantNames cpu_variant_names[] = {
54     {"cortex-a76", kCortexA55},
55     {"kryo385", kCortexA55},
56     {"cortex-a75", kCortexA55},
57     {"kryo", kKryo},
58     {"cortex-a73", kCortexA55},
59     {"cortex-a55", kCortexA55},
60     {"cortex-a53", kCortexA53},
61     {"krait", kKrait},
62     {"cortex-a9", kCortexA9},
63     {"cortex-a7", kCortexA7},
64     // kUnknown indicates the end of this array.
65     {"", kUnknown},
66 };
67 
ifunc_open(const char * pathname)68 static long ifunc_open(const char* pathname) {
69     register long r0 __asm__("r0") = AT_FDCWD;
70     register long r1 __asm__("r1") = reinterpret_cast<long>(pathname);
71     register long r2 __asm__("r2") = O_RDONLY;
72     register long r3 __asm__("r3") = 0;
73     register long r7 __asm__("r7") = __NR_openat;
74     __asm__ volatile("swi #0" : "=r"(r0) : "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r7));
75     return r0;
76 }
77 
ifunc_read(int fd,void * buf,size_t count)78 static ssize_t ifunc_read(int fd, void* buf, size_t count) {
79     register long r0 __asm__("r0") = fd;
80     register long r1 __asm__("r1") = reinterpret_cast<long>(buf);
81     register long r2 __asm__("r2") = count;
82     register long r7 __asm__("r7") = __NR_read;
83     __asm__ volatile("swi #0" : "=r"(r0) : "r"(r0), "r"(r1), "r"(r2), "r"(r7) : "memory");
84     return r0;
85 }
86 
ifunc_close(int fd)87 static int ifunc_close(int fd) {
88     register long r0 __asm__("r0") = fd;
89     register long r7 __asm__("r7") = __NR_close;
90     __asm__ volatile("swi #0" : "=r"(r0) : "r"(r0), "r"(r7));
91     return r0;
92 }
93 
is_same_name(const char * a,const char * b)94 static bool is_same_name(const char* a, const char* b) {
95     static_assert(MAX_CPU_NAME_LEN % sizeof(int) == 0, "");
96     const int* ia = reinterpret_cast<const int*>(a);
97     const int* ib = reinterpret_cast<const int*>(b);
98     for (size_t i = 0; i < MAX_CPU_NAME_LEN / sizeof(int); ++i) {
99         if (ia[i] != ib[i]) {
100             return false;
101         }
102     }
103     return true;
104 }
105 
init_cpu_variant()106 static CpuVariant init_cpu_variant() {
107     int fd = ifunc_open("/dev/cpu_variant:arm");
108     if (fd < 0) return kGeneric;
109 
110     alignas(alignof(int)) char name[MAX_CPU_NAME_LEN] = {};
111 
112     int bytes_read, total_read = 0;
113     while (total_read < MAX_CPU_NAME_LEN - 1 &&
114            (bytes_read = ifunc_read(fd, name + total_read,
115                                     MAX_CPU_NAME_LEN - 1 - total_read)) > 0) {
116         total_read += bytes_read;
117     }
118     ifunc_close(fd);
119 
120     if (bytes_read != 0) {
121         // The file is too big. We haven't reach the end. Or maybe there is an
122         // error when reading.
123         return kGeneric;
124     }
125     name[total_read] = 0;
126 
127     const CpuVariantNames* cpu_variant = cpu_variant_names;
128     while (cpu_variant->variant != kUnknown) {
129         if (is_same_name(cpu_variant->name, name)) {
130             return cpu_variant->variant;
131         }
132         cpu_variant++;
133     }
134     return kGeneric;
135 }
136 
get_cpu_variant()137 static CpuVariant get_cpu_variant() {
138     static CpuVariant cpu_variant = kUnknown;
139     if (cpu_variant == kUnknown) {
140         cpu_variant = init_cpu_variant();
141     }
142     return cpu_variant;
143 }
144 
145 typedef void* memmove_func(void* __dst, const void* __src, size_t __n);
DEFINE_IFUNC_FOR(memmove)146 DEFINE_IFUNC_FOR(memmove) {
147     RETURN_FUNC(memmove_func, memmove_a15);
148 }
149 
150 typedef void* memcpy_func(void*, const void*, size_t);
DEFINE_IFUNC_FOR(memcpy)151 DEFINE_IFUNC_FOR(memcpy) {
152     return memmove_resolver(hwcap);
153 }
154 
155 typedef void* __memcpy_func(void*, const void*, size_t);
DEFINE_IFUNC_FOR(__memcpy)156 DEFINE_IFUNC_FOR(__memcpy) {
157     switch(get_cpu_variant()) {
158         case kCortexA7:
159             RETURN_FUNC(__memcpy_func, __memcpy_a7);
160         case kCortexA9:
161             RETURN_FUNC(__memcpy_func, __memcpy_a9);
162         case kKrait:
163             RETURN_FUNC(__memcpy_func, __memcpy_krait);
164         case kCortexA53:
165             RETURN_FUNC(__memcpy_func, __memcpy_a53);
166         case kCortexA55:
167             RETURN_FUNC(__memcpy_func, __memcpy_a55);
168         case kKryo:
169             RETURN_FUNC(__memcpy_func, __memcpy_kryo);
170         default:
171             RETURN_FUNC(__memcpy_func, __memcpy_a15);
172     }
173 }
174 
175 typedef void* __memset_chk_func(void* s, int c, size_t n, size_t n2);
DEFINE_IFUNC_FOR(__memset_chk)176 DEFINE_IFUNC_FOR(__memset_chk) {
177     switch(get_cpu_variant()) {
178         case kCortexA7:
179         case kCortexA53:
180         case kCortexA55:
181         case kKryo:
182             RETURN_FUNC(__memset_chk_func, __memset_chk_a7);
183         case kCortexA9:
184             RETURN_FUNC(__memset_chk_func, __memset_chk_a9);
185         case kKrait:
186             RETURN_FUNC(__memset_chk_func, __memset_chk_krait);
187         default:
188             RETURN_FUNC(__memset_chk_func, __memset_chk_a15);
189     }
190 }
191 
192 typedef void* memset_func(void* __dst, int __ch, size_t __n);
DEFINE_IFUNC_FOR(memset)193 DEFINE_IFUNC_FOR(memset) {
194     switch(get_cpu_variant()) {
195         case kCortexA7:
196         case kCortexA53:
197         case kCortexA55:
198         case kKryo:
199              RETURN_FUNC(memset_func, memset_a7);
200         case kCortexA9:
201              RETURN_FUNC(memset_func, memset_a9);
202         case kKrait:
203              RETURN_FUNC(memset_func, memset_krait);
204         default:
205              RETURN_FUNC(memset_func, memset_a15);
206     }
207 }
208 
209 typedef char* strcpy_func(char* __dst, const char* __src);
DEFINE_IFUNC_FOR(strcpy)210 DEFINE_IFUNC_FOR(strcpy) {
211     switch(get_cpu_variant()) {
212         case kCortexA9:
213             RETURN_FUNC(strcpy_func, strcpy_a9);
214         default:
215             RETURN_FUNC(strcpy_func, strcpy_a15);
216     }
217 }
218 
219 typedef char* __strcpy_chk_func(char* dst, const char* src, size_t dst_len);
DEFINE_IFUNC_FOR(__strcpy_chk)220 DEFINE_IFUNC_FOR(__strcpy_chk) {
221     switch(get_cpu_variant()) {
222         case kCortexA7:
223             RETURN_FUNC(__strcpy_chk_func, __strcpy_chk_a7);
224         case kCortexA9:
225             RETURN_FUNC(__strcpy_chk_func, __strcpy_chk_a9);
226         case kKrait:
227         case kKryo:
228             RETURN_FUNC(__strcpy_chk_func, __strcpy_chk_krait);
229         case kCortexA53:
230             RETURN_FUNC(__strcpy_chk_func, __strcpy_chk_a53);
231         case kCortexA55:
232             RETURN_FUNC(__strcpy_chk_func, __strcpy_chk_a55);
233         default:
234             RETURN_FUNC(__strcpy_chk_func, __strcpy_chk_a15);
235     }
236 }
237 
238 typedef char* stpcpy_func(char* __dst, const char* __src);
DEFINE_IFUNC_FOR(stpcpy)239 DEFINE_IFUNC_FOR(stpcpy) {
240     switch(get_cpu_variant()) {
241         case kCortexA9:
242             RETURN_FUNC(stpcpy_func, stpcpy_a9);
243         default:
244             RETURN_FUNC(stpcpy_func, stpcpy_a15);
245     }
246 }
247 
248 typedef char* strcat_func(char* __dst, const char* __src);
DEFINE_IFUNC_FOR(strcat)249 DEFINE_IFUNC_FOR(strcat) {
250     switch(get_cpu_variant()) {
251         case kCortexA9:
252             RETURN_FUNC(strcat_func, strcat_a9);
253         default:
254             RETURN_FUNC(strcat_func, strcat_a15);
255     }
256 }
257 
258 typedef char* __strcat_chk_func(char* dst, const char* src, size_t dst_buf_size);
DEFINE_IFUNC_FOR(__strcat_chk)259 DEFINE_IFUNC_FOR(__strcat_chk) {
260     switch(get_cpu_variant()) {
261         case kCortexA7:
262             RETURN_FUNC(__strcat_chk_func, __strcat_chk_a7);
263         case kCortexA9:
264             RETURN_FUNC(__strcat_chk_func, __strcat_chk_a9);
265         case kKrait:
266         case kKryo:
267             RETURN_FUNC(__strcat_chk_func, __strcat_chk_krait);
268         case kCortexA53:
269             RETURN_FUNC(__strcat_chk_func, __strcat_chk_a53);
270         case kCortexA55:
271             RETURN_FUNC(__strcat_chk_func, __strcat_chk_a55);
272         default:
273             RETURN_FUNC(__strcat_chk_func, __strcat_chk_a15);
274     }
275 }
276 
277 typedef int strcmp_func(const char* __lhs, const char* __rhs);
DEFINE_IFUNC_FOR(strcmp)278 DEFINE_IFUNC_FOR(strcmp) {
279     RETURN_FUNC(strcmp_func, strcmp_a15);
280 }
281 
282 typedef size_t strlen_func(const char* __s);
DEFINE_IFUNC_FOR(strlen)283 DEFINE_IFUNC_FOR(strlen) {
284     switch(get_cpu_variant()) {
285         case kCortexA9:
286             RETURN_FUNC(strlen_func, strlen_a9);
287         default:
288             RETURN_FUNC(strlen_func, strlen_a15);
289     }
290 }
291 
292 }  // extern "C"
293