| /* | 
 |  * Copyright (C) 2018 The Android Open Source Project | 
 |  * All rights reserved. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions | 
 |  * are met: | 
 |  *  * Redistributions of source code must retain the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer. | 
 |  *  * Redistributions in binary form must reproduce the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer in | 
 |  *    the documentation and/or other materials provided with the | 
 |  *    distribution. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
 |  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
 |  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | 
 |  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | 
 |  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | 
 |  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | 
 |  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS | 
 |  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | 
 |  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 
 |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | 
 |  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
 |  * SUCH DAMAGE. | 
 |  */ | 
 |  | 
 | #include <fcntl.h> | 
 | #include <private/bionic_ifuncs.h> | 
 | #include <sys/syscall.h> | 
 |  | 
 | extern "C" { | 
 |  | 
 | enum CpuVariant { | 
 |   kUnknown = 0, | 
 |   kGeneric, | 
 |   kCortexA7, | 
 |   kCortexA9, | 
 |   kCortexA53, | 
 |   kCortexA55, | 
 |   kKrait, | 
 |   kKryo, | 
 | }; | 
 |  | 
 | static constexpr int MAX_CPU_NAME_LEN = 12; | 
 | struct CpuVariantNames { | 
 |   alignas(alignof(int)) char name[MAX_CPU_NAME_LEN]; | 
 |   CpuVariant variant; | 
 | }; | 
 |  | 
 | static constexpr CpuVariantNames cpu_variant_names[] = { | 
 |     {"cortex-a76", kCortexA55}, | 
 |     {"kryo385", kCortexA55}, | 
 |     {"cortex-a75", kCortexA55}, | 
 |     {"kryo", kKryo}, | 
 |     {"cortex-a73", kCortexA55}, | 
 |     {"cortex-a55", kCortexA55}, | 
 |     {"cortex-a53", kCortexA53}, | 
 |     {"krait", kKrait}, | 
 |     {"cortex-a9", kCortexA9}, | 
 |     {"cortex-a7", kCortexA7}, | 
 |     // kUnknown indicates the end of this array. | 
 |     {"", kUnknown}, | 
 | }; | 
 |  | 
 | static long ifunc_open(const char* pathname) { | 
 |   register long r0 __asm__("r0") = AT_FDCWD; | 
 |   register long r1 __asm__("r1") = reinterpret_cast<long>(pathname); | 
 |   register long r2 __asm__("r2") = O_RDONLY; | 
 |   register long r3 __asm__("r3") = 0; | 
 |   register long r7 __asm__("r7") = __NR_openat; | 
 |   __asm__ volatile("swi #0" | 
 |                    : "=r"(r0) | 
 |                    : "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r7)); | 
 |   return r0; | 
 | } | 
 |  | 
 | static ssize_t ifunc_read(int fd, void* buf, size_t count) { | 
 |   register long r0 __asm__("r0") = fd; | 
 |   register long r1 __asm__("r1") = reinterpret_cast<long>(buf); | 
 |   register long r2 __asm__("r2") = count; | 
 |   register long r7 __asm__("r7") = __NR_read; | 
 |   __asm__ volatile("swi #0" | 
 |                    : "=r"(r0) | 
 |                    : "r"(r0), "r"(r1), "r"(r2), "r"(r7) | 
 |                    : "memory"); | 
 |   return r0; | 
 | } | 
 |  | 
 | static int ifunc_close(int fd) { | 
 |   register long r0 __asm__("r0") = fd; | 
 |   register long r7 __asm__("r7") = __NR_close; | 
 |   __asm__ volatile("swi #0" : "=r"(r0) : "r"(r0), "r"(r7)); | 
 |   return r0; | 
 | } | 
 |  | 
 | static bool is_same_name(const char* a, const char* b) { | 
 |   static_assert(MAX_CPU_NAME_LEN % sizeof(int) == 0, ""); | 
 |   const int* ia = reinterpret_cast<const int*>(a); | 
 |   const int* ib = reinterpret_cast<const int*>(b); | 
 |   for (size_t i = 0; i < MAX_CPU_NAME_LEN / sizeof(int); ++i) { | 
 |     if (ia[i] != ib[i]) { | 
 |       return false; | 
 |     } | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | static CpuVariant init_cpu_variant() { | 
 |   int fd = ifunc_open("/dev/cpu_variant:arm"); | 
 |   if (fd < 0) return kGeneric; | 
 |  | 
 |   alignas(alignof(int)) char name[MAX_CPU_NAME_LEN] = {}; | 
 |  | 
 |   int bytes_read, total_read = 0; | 
 |   while (total_read < MAX_CPU_NAME_LEN - 1 && | 
 |          (bytes_read = ifunc_read(fd, name + total_read, | 
 |                                   MAX_CPU_NAME_LEN - 1 - total_read)) > 0) { | 
 |     total_read += bytes_read; | 
 |   } | 
 |   ifunc_close(fd); | 
 |  | 
 |   if (bytes_read != 0) { | 
 |     // The file is too big. We haven't reach the end. Or maybe there is an | 
 |     // error when reading. | 
 |     return kGeneric; | 
 |   } | 
 |   name[total_read] = 0; | 
 |  | 
 |   const CpuVariantNames* cpu_variant = cpu_variant_names; | 
 |   while (cpu_variant->variant != kUnknown) { | 
 |     if (is_same_name(cpu_variant->name, name)) { | 
 |       return cpu_variant->variant; | 
 |     } | 
 |     cpu_variant++; | 
 |   } | 
 |   return kGeneric; | 
 | } | 
 |  | 
 | static CpuVariant get_cpu_variant() { | 
 |   static CpuVariant cpu_variant = kUnknown; | 
 |   if (cpu_variant == kUnknown) { | 
 |     cpu_variant = init_cpu_variant(); | 
 |   } | 
 |   return cpu_variant; | 
 | } | 
 |  | 
 | DEFINE_IFUNC_FOR(memmove) { | 
 |   RETURN_FUNC(memmove_func_t, memmove_a15); | 
 | } | 
 | MEMMOVE_SHIM() | 
 |  | 
 | DEFINE_IFUNC_FOR(memcpy) { | 
 |   return memmove_resolver(hwcap); | 
 | } | 
 | MEMCPY_SHIM() | 
 |  | 
 | // On arm32, __memcpy() is not publicly exposed, but gets called by memmove() | 
 | // in cases where the copy is known to be overlap-safe. | 
 | typedef void* __memcpy_func_t(void*, const void*, size_t); | 
 | DEFINE_IFUNC_FOR(__memcpy) { | 
 |   switch (get_cpu_variant()) { | 
 |     case kCortexA7: | 
 |       RETURN_FUNC(__memcpy_func_t, __memcpy_a7); | 
 |     case kCortexA9: | 
 |       RETURN_FUNC(__memcpy_func_t, __memcpy_a9); | 
 |     case kKrait: | 
 |       RETURN_FUNC(__memcpy_func_t, __memcpy_krait); | 
 |     case kCortexA53: | 
 |       RETURN_FUNC(__memcpy_func_t, __memcpy_a53); | 
 |     case kCortexA55: | 
 |       RETURN_FUNC(__memcpy_func_t, __memcpy_a55); | 
 |     case kKryo: | 
 |       RETURN_FUNC(__memcpy_func_t, __memcpy_kryo); | 
 |     default: | 
 |       RETURN_FUNC(__memcpy_func_t, __memcpy_a15); | 
 |   } | 
 | } | 
 | DEFINE_STATIC_SHIM(void* __memcpy(void* dst, const void* src, size_t n) { | 
 |   FORWARD(__memcpy)(dst, src, n); | 
 | }) | 
 |  | 
 | DEFINE_IFUNC_FOR(__memset_chk) { | 
 |   switch (get_cpu_variant()) { | 
 |     case kCortexA7: | 
 |     case kCortexA53: | 
 |     case kCortexA55: | 
 |     case kKryo: | 
 |       RETURN_FUNC(__memset_chk_func_t, __memset_chk_a7); | 
 |     case kCortexA9: | 
 |       RETURN_FUNC(__memset_chk_func_t, __memset_chk_a9); | 
 |     case kKrait: | 
 |       RETURN_FUNC(__memset_chk_func_t, __memset_chk_krait); | 
 |     default: | 
 |       RETURN_FUNC(__memset_chk_func_t, __memset_chk_a15); | 
 |   } | 
 | } | 
 | __MEMSET_CHK_SHIM() | 
 |  | 
 | DEFINE_IFUNC_FOR(memset) { | 
 |   switch (get_cpu_variant()) { | 
 |     case kCortexA7: | 
 |     case kCortexA53: | 
 |     case kCortexA55: | 
 |     case kKryo: | 
 |       RETURN_FUNC(memset_func_t, memset_a7); | 
 |     case kCortexA9: | 
 |       RETURN_FUNC(memset_func_t, memset_a9); | 
 |     case kKrait: | 
 |       RETURN_FUNC(memset_func_t, memset_krait); | 
 |     default: | 
 |       RETURN_FUNC(memset_func_t, memset_a15); | 
 |   } | 
 | } | 
 | MEMSET_SHIM() | 
 |  | 
 | DEFINE_IFUNC_FOR(strcpy) { | 
 |   switch (get_cpu_variant()) { | 
 |     case kCortexA9: | 
 |       RETURN_FUNC(strcpy_func_t, strcpy_a9); | 
 |     default: | 
 |       RETURN_FUNC(strcpy_func_t, strcpy_a15); | 
 |   } | 
 | } | 
 | STRCPY_SHIM() | 
 |  | 
 | DEFINE_IFUNC_FOR(__strcpy_chk) { | 
 |   switch (get_cpu_variant()) { | 
 |     case kCortexA7: | 
 |       RETURN_FUNC(__strcpy_chk_func_t, __strcpy_chk_a7); | 
 |     case kCortexA9: | 
 |       RETURN_FUNC(__strcpy_chk_func_t, __strcpy_chk_a9); | 
 |     case kKrait: | 
 |     case kKryo: | 
 |       RETURN_FUNC(__strcpy_chk_func_t, __strcpy_chk_krait); | 
 |     case kCortexA53: | 
 |       RETURN_FUNC(__strcpy_chk_func_t, __strcpy_chk_a53); | 
 |     case kCortexA55: | 
 |       RETURN_FUNC(__strcpy_chk_func_t, __strcpy_chk_a55); | 
 |     default: | 
 |       RETURN_FUNC(__strcpy_chk_func_t, __strcpy_chk_a15); | 
 |   } | 
 | } | 
 | __STRCPY_CHK_SHIM() | 
 |  | 
 | DEFINE_IFUNC_FOR(stpcpy) { | 
 |   switch (get_cpu_variant()) { | 
 |     case kCortexA9: | 
 |       RETURN_FUNC(stpcpy_func_t, stpcpy_a9); | 
 |     default: | 
 |       RETURN_FUNC(stpcpy_func_t, stpcpy_a15); | 
 |   } | 
 | } | 
 | STPCPY_SHIM() | 
 |  | 
 | DEFINE_IFUNC_FOR(strcat) { | 
 |   switch (get_cpu_variant()) { | 
 |     case kCortexA9: | 
 |       RETURN_FUNC(strcat_func_t, strcat_a9); | 
 |     default: | 
 |       RETURN_FUNC(strcat_func_t, strcat_a15); | 
 |   } | 
 | } | 
 | STRCAT_SHIM() | 
 |  | 
 | DEFINE_IFUNC_FOR(__strcat_chk) { | 
 |   switch (get_cpu_variant()) { | 
 |     case kCortexA7: | 
 |       RETURN_FUNC(__strcat_chk_func_t, __strcat_chk_a7); | 
 |     case kCortexA9: | 
 |       RETURN_FUNC(__strcat_chk_func_t, __strcat_chk_a9); | 
 |     case kKrait: | 
 |     case kKryo: | 
 |       RETURN_FUNC(__strcat_chk_func_t, __strcat_chk_krait); | 
 |     case kCortexA53: | 
 |       RETURN_FUNC(__strcat_chk_func_t, __strcat_chk_a53); | 
 |     case kCortexA55: | 
 |       RETURN_FUNC(__strcat_chk_func_t, __strcat_chk_a55); | 
 |     default: | 
 |       RETURN_FUNC(__strcat_chk_func_t, __strcat_chk_a15); | 
 |   } | 
 | } | 
 | __STRCAT_CHK_SHIM() | 
 |  | 
 | DEFINE_IFUNC_FOR(strcmp) { | 
 |   RETURN_FUNC(strcmp_func_t, strcmp_a15); | 
 | } | 
 | STRCMP_SHIM() | 
 |  | 
 | DEFINE_IFUNC_FOR(strlen) { | 
 |   switch (get_cpu_variant()) { | 
 |     case kCortexA9: | 
 |       RETURN_FUNC(strlen_func_t, strlen_a9); | 
 |     default: | 
 |       RETURN_FUNC(strlen_func_t, strlen_a15); | 
 |   } | 
 | } | 
 | STRLEN_SHIM() | 
 |  | 
 | }  // extern "C" |