| Index: platform_tools/android/third_party/cpufeatures/cpu-features.c
|
| diff --git a/platform_tools/android/third_party/cpufeatures/cpu-features.c b/platform_tools/android/third_party/cpufeatures/cpu-features.c
|
| index 5b0a9d934350a617684e5bff68b46c8998596747..2d23efb8f99b73ff211a9392bd70f3b3f5585d36 100644
|
| --- a/platform_tools/android/third_party/cpufeatures/cpu-features.c
|
| +++ b/platform_tools/android/third_party/cpufeatures/cpu-features.c
|
| @@ -28,6 +28,8 @@
|
|
|
| /* ChangeLog for this library:
|
| *
|
| + * NDK r9?: Support for 64-bit CPUs (Intel, ARM & MIPS).
|
| + *
|
| * NDK r8d: Add android_setCpu().
|
| *
|
| * NDK r8c: Add new ARM CPU features: VFPv2, VFP_D32, VFP_FP16,
|
| @@ -56,16 +58,23 @@
|
| *
|
| * NDK r4: Initial release
|
| */
|
| -#include <sys/system_properties.h>
|
| -#ifdef __arm__
|
| -#include <machine/cpu-features.h>
|
| -#endif
|
| -#include <pthread.h>
|
| +
|
| +#if defined(__le32__) || defined(__le64__)
|
| +
|
| +// When users enter this, we should only provide interface and
|
| +// libportable will give the implementations.
|
| +
|
| +#else // !__le32__ && !__le64__
|
| +
|
| #include "cpu-features.h"
|
| +
|
| +#include <dlfcn.h>
|
| +#include <errno.h>
|
| +#include <fcntl.h>
|
| +#include <pthread.h>
|
| #include <stdio.h>
|
| #include <stdlib.h>
|
| -#include <fcntl.h>
|
| -#include <errno.h>
|
| +#include <sys/system_properties.h>
|
|
|
| static pthread_once_t g_once;
|
| static int g_inited;
|
| @@ -73,16 +82,12 @@ static AndroidCpuFamily g_cpuFamily;
|
| static uint64_t g_cpuFeatures;
|
| static int g_cpuCount;
|
|
|
| -static const int android_cpufeatures_debug = 0;
|
| -
|
| #ifdef __arm__
|
| -# define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_ARM
|
| -#elif defined __i386__
|
| -# define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_X86
|
| -#else
|
| -# define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_UNKNOWN
|
| +static uint32_t g_cpuIdArm;
|
| #endif
|
|
|
| +static const int android_cpufeatures_debug = 0;
|
| +
|
| #define D(...) \
|
| do { \
|
| if (android_cpufeatures_debug) { \
|
| @@ -123,22 +128,22 @@ get_file_size(const char* pathname)
|
|
|
| fd = open(pathname, O_RDONLY);
|
| if (fd < 0) {
|
| - D("Can't open %s: %s\n", pathname, strerror(errno));
|
| - return -1;
|
| + D("Can't open %s: %s\n", pathname, strerror(errno));
|
| + return -1;
|
| }
|
|
|
| for (;;) {
|
| - int ret = read(fd, buffer, sizeof buffer);
|
| - if (ret < 0) {
|
| - if (errno == EINTR)
|
| - continue;
|
| - D("Error while reading %s: %s\n", pathname, strerror(errno));
|
| - break;
|
| - }
|
| - if (ret == 0)
|
| - break;
|
| -
|
| - result += ret;
|
| + int ret = read(fd, buffer, sizeof buffer);
|
| + if (ret < 0) {
|
| + if (errno == EINTR)
|
| + continue;
|
| + D("Error while reading %s: %s\n", pathname, strerror(errno));
|
| + break;
|
| + }
|
| + if (ret == 0)
|
| + break;
|
| +
|
| + result += ret;
|
| }
|
| close(fd);
|
| return result;
|
| @@ -185,17 +190,16 @@ read_file(const char* pathname, char* buffer, size_t buffsize)
|
| * Return NULL if not found
|
| */
|
| static char*
|
| -extract_cpuinfo_field(char* buffer, int buflen, const char* field)
|
| +extract_cpuinfo_field(const char* buffer, int buflen, const char* field)
|
| {
|
| int fieldlen = strlen(field);
|
| - char* bufend = buffer + buflen;
|
| + const char* bufend = buffer + buflen;
|
| char* result = NULL;
|
| int len, ignore;
|
| const char *p, *q;
|
|
|
| /* Look for first field occurence, and ensures it starts the line. */
|
| p = buffer;
|
| - bufend = buffer + buflen;
|
| for (;;) {
|
| p = memmem(p, bufend-p, field, fieldlen);
|
| if (p == NULL)
|
| @@ -232,10 +236,6 @@ EXIT:
|
| return result;
|
| }
|
|
|
| -/* Like strlen(), but for constant string literals */
|
| -#define STRLEN_CONST(x) ((sizeof(x)-1)
|
| -
|
| -
|
| /* Checks that a space-separated list of items contains one given 'item'.
|
| * Returns 1 if found, 0 otherwise.
|
| */
|
| @@ -269,7 +269,7 @@ has_list_item(const char* list, const char* item)
|
| return 0;
|
| }
|
|
|
| -/* Parse an decimal integer starting from 'input', but not going further
|
| +/* Parse a number starting from 'input', but not going further
|
| * than 'limit'. Return the value into '*result'.
|
| *
|
| * NOTE: Does not skip over leading spaces, or deal with sign characters.
|
| @@ -280,15 +280,23 @@ has_list_item(const char* list, const char* item)
|
| * be <= 'limit').
|
| */
|
| static const char*
|
| -parse_decimal(const char* input, const char* limit, int* result)
|
| +parse_number(const char* input, const char* limit, int base, int* result)
|
| {
|
| const char* p = input;
|
| int val = 0;
|
| while (p < limit) {
|
| int d = (*p - '0');
|
| - if ((unsigned)d >= 10U)
|
| - break;
|
| - val = val*10 + d;
|
| + if ((unsigned)d >= 10U) {
|
| + d = (*p - 'a');
|
| + if ((unsigned)d >= 6U)
|
| + d = (*p - 'A');
|
| + if ((unsigned)d >= 6U)
|
| + break;
|
| + d += 10;
|
| + }
|
| + if (d >= base)
|
| + break;
|
| + val = val*base + d;
|
| p++;
|
| }
|
| if (p == input)
|
| @@ -298,6 +306,18 @@ parse_decimal(const char* input, const char* limit, int* result)
|
| return p;
|
| }
|
|
|
| +static const char*
|
| +parse_decimal(const char* input, const char* limit, int* result)
|
| +{
|
| + return parse_number(input, limit, 10, result);
|
| +}
|
| +
|
| +static const char*
|
| +parse_hexadecimal(const char* input, const char* limit, int* result)
|
| +{
|
| + return parse_number(input, limit, 16, result);
|
| +}
|
| +
|
| /* This small data type is used to represent a CPU list / mask, as read
|
| * from sysfs on Linux. See http://www.kernel.org/doc/Documentation/cputopology.txt
|
| *
|
| @@ -408,6 +428,18 @@ cpulist_read_from(CpuList* list, const char* filename)
|
|
|
| cpulist_parse(list, file, filelen);
|
| }
|
| +#if defined(__aarch64__)
|
| +// see <uapi/asm/hwcap.h> kernel header
|
| +#define HWCAP_FP (1 << 0)
|
| +#define HWCAP_ASIMD (1 << 1)
|
| +#define HWCAP_AES (1 << 3)
|
| +#define HWCAP_PMULL (1 << 4)
|
| +#define HWCAP_SHA1 (1 << 5)
|
| +#define HWCAP_SHA2 (1 << 6)
|
| +#define HWCAP_CRC32 (1 << 7)
|
| +#endif
|
| +
|
| +#if defined(__arm__)
|
|
|
| // See <asm/hwcap.h> kernel header.
|
| #define HWCAP_VFP (1 << 6)
|
| @@ -419,16 +451,78 @@ cpulist_read_from(CpuList* list, const char* filename)
|
| #define HWCAP_IDIVA (1 << 17)
|
| #define HWCAP_IDIVT (1 << 18)
|
|
|
| +// see <uapi/asm/hwcap.h> kernel header
|
| +#define HWCAP2_AES (1 << 0)
|
| +#define HWCAP2_PMULL (1 << 1)
|
| +#define HWCAP2_SHA1 (1 << 2)
|
| +#define HWCAP2_SHA2 (1 << 3)
|
| +#define HWCAP2_CRC32 (1 << 4)
|
| +
|
| +// This is the list of 32-bit ARMv7 optional features that are _always_
|
| +// supported by ARMv8 CPUs, as mandated by the ARM Architecture Reference
|
| +// Manual.
|
| +#define HWCAP_SET_FOR_ARMV8 \
|
| + ( HWCAP_VFP | \
|
| + HWCAP_NEON | \
|
| + HWCAP_VFPv3 | \
|
| + HWCAP_VFPv4 | \
|
| + HWCAP_IDIVA | \
|
| + HWCAP_IDIVT )
|
| +#endif
|
| +
|
| +#if defined(__arm__) || defined(__aarch64__)
|
| +
|
| #define AT_HWCAP 16
|
| +#define AT_HWCAP2 26
|
| +
|
| +// Probe the system's C library for a 'getauxval' function and call it if
|
| +// it exits, or return 0 for failure. This function is available since API
|
| +// level 20.
|
| +//
|
| +// This code does *NOT* check for '__ANDROID_API__ >= 20' to support the
|
| +// edge case where some NDK developers use headers for a platform that is
|
| +// newer than the one really targetted by their application.
|
| +// This is typically done to use newer native APIs only when running on more
|
| +// recent Android versions, and requires careful symbol management.
|
| +//
|
| +// Note that getauxval() can't really be re-implemented here, because
|
| +// its implementation does not parse /proc/self/auxv. Instead it depends
|
| +// on values that are passed by the kernel at process-init time to the
|
| +// C runtime initialization layer.
|
| +static uint32_t
|
| +get_elf_hwcap_from_getauxval(int hwcap_type) {
|
| + typedef unsigned long getauxval_func_t(unsigned long);
|
|
|
| -/* Read the ELF HWCAP flags by parsing /proc/self/auxv
|
| - */
|
| + dlerror();
|
| + void* libc_handle = dlopen("libc.so", RTLD_NOW);
|
| + if (!libc_handle) {
|
| + D("Could not dlopen() C library: %s\n", dlerror());
|
| + return 0;
|
| + }
|
| +
|
| + uint32_t ret = 0;
|
| + getauxval_func_t* func = (getauxval_func_t*)
|
| + dlsym(libc_handle, "getauxval");
|
| + if (!func) {
|
| + D("Could not find getauxval() in C library\n");
|
| + } else {
|
| + // Note: getauxval() returns 0 on failure. Doesn't touch errno.
|
| + ret = (uint32_t)(*func)(hwcap_type);
|
| + }
|
| + dlclose(libc_handle);
|
| + return ret;
|
| +}
|
| +#endif
|
| +
|
| +#if defined(__arm__)
|
| +// Parse /proc/self/auxv to extract the ELF HW capabilities bitmap for the
|
| +// current CPU. Note that this file is not accessible from regular
|
| +// application processes on some Android platform releases.
|
| +// On success, return new ELF hwcaps, or 0 on failure.
|
| static uint32_t
|
| -get_elf_hwcap(void)
|
| -{
|
| - uint32_t result = 0;
|
| +get_elf_hwcap_from_proc_self_auxv(void) {
|
| const char filepath[] = "/proc/self/auxv";
|
| - int fd = open(filepath, O_RDONLY);
|
| + int fd = TEMP_FAILURE_RETRY(open(filepath, O_RDONLY));
|
| if (fd < 0) {
|
| D("Could not open %s: %s\n", filepath, strerror(errno));
|
| return 0;
|
| @@ -436,11 +530,10 @@ get_elf_hwcap(void)
|
|
|
| struct { uint32_t tag; uint32_t value; } entry;
|
|
|
| + uint32_t result = 0;
|
| for (;;) {
|
| - int ret = read(fd, (char*)&entry, sizeof entry);
|
| + int ret = TEMP_FAILURE_RETRY(read(fd, (char*)&entry, sizeof entry));
|
| if (ret < 0) {
|
| - if (errno == EINTR)
|
| - continue;
|
| D("Error while reading %s: %s\n", filepath, strerror(errno));
|
| break;
|
| }
|
| @@ -456,6 +549,74 @@ get_elf_hwcap(void)
|
| return result;
|
| }
|
|
|
| +/* Compute the ELF HWCAP flags from the content of /proc/cpuinfo.
|
| + * This works by parsing the 'Features' line, which lists which optional
|
| + * features the device's CPU supports, on top of its reference
|
| + * architecture.
|
| + */
|
| +static uint32_t
|
| +get_elf_hwcap_from_proc_cpuinfo(const char* cpuinfo, int cpuinfo_len) {
|
| + uint32_t hwcaps = 0;
|
| + long architecture = 0;
|
| + char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture");
|
| + if (cpuArch) {
|
| + architecture = strtol(cpuArch, NULL, 10);
|
| + free(cpuArch);
|
| +
|
| + if (architecture >= 8L) {
|
| + // This is a 32-bit ARM binary running on a 64-bit ARM64 kernel.
|
| + // The 'Features' line only lists the optional features that the
|
| + // device's CPU supports, compared to its reference architecture
|
| + // which are of no use for this process.
|
| + D("Faking 32-bit ARM HWCaps on ARMv%ld CPU\n", architecture);
|
| + return HWCAP_SET_FOR_ARMV8;
|
| + }
|
| + }
|
| +
|
| + char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Features");
|
| + if (cpuFeatures != NULL) {
|
| + D("Found cpuFeatures = '%s'\n", cpuFeatures);
|
| +
|
| + if (has_list_item(cpuFeatures, "vfp"))
|
| + hwcaps |= HWCAP_VFP;
|
| + if (has_list_item(cpuFeatures, "vfpv3"))
|
| + hwcaps |= HWCAP_VFPv3;
|
| + if (has_list_item(cpuFeatures, "vfpv3d16"))
|
| + hwcaps |= HWCAP_VFPv3D16;
|
| + if (has_list_item(cpuFeatures, "vfpv4"))
|
| + hwcaps |= HWCAP_VFPv4;
|
| + if (has_list_item(cpuFeatures, "neon"))
|
| + hwcaps |= HWCAP_NEON;
|
| + if (has_list_item(cpuFeatures, "idiva"))
|
| + hwcaps |= HWCAP_IDIVA;
|
| + if (has_list_item(cpuFeatures, "idivt"))
|
| + hwcaps |= HWCAP_IDIVT;
|
| + if (has_list_item(cpuFeatures, "idiv"))
|
| + hwcaps |= HWCAP_IDIVA | HWCAP_IDIVT;
|
| + if (has_list_item(cpuFeatures, "iwmmxt"))
|
| + hwcaps |= HWCAP_IWMMXT;
|
| +
|
| + free(cpuFeatures);
|
| + }
|
| + return hwcaps;
|
| +}
|
| +
|
| +/* Check Houdini Binary Translator is installed on the system.
|
| + *
|
| + * If this function returns 1, get_elf_hwcap_from_getauxval() function
|
| + * will causes SIGSEGV while calling getauxval() function.
|
| + */
|
| +static int
|
| +has_houdini_binary_translator(void) {
|
| + int found = 0;
|
| + if (access("/system/lib/libhoudini.so", F_OK) != -1) {
|
| + D("Found Houdini binary translator\n");
|
| + found = 1;
|
| + }
|
| + return found;
|
| +}
|
| +#endif /* __arm__ */
|
| +
|
| /* Return the number of cpus present on a given device.
|
| *
|
| * To handle all weird kernel configurations, we need to compute the
|
| @@ -482,12 +643,19 @@ get_cpu_count(void)
|
| static void
|
| android_cpuInitFamily(void)
|
| {
|
| -#if defined(__ARM_ARCH__)
|
| +#if defined(__arm__)
|
| g_cpuFamily = ANDROID_CPU_FAMILY_ARM;
|
| #elif defined(__i386__)
|
| g_cpuFamily = ANDROID_CPU_FAMILY_X86;
|
| -#elif defined(_MIPS_ARCH)
|
| +#elif defined(__mips64)
|
| +/* Needs to be before __mips__ since the compiler defines both */
|
| + g_cpuFamily = ANDROID_CPU_FAMILY_MIPS64;
|
| +#elif defined(__mips__)
|
| g_cpuFamily = ANDROID_CPU_FAMILY_MIPS;
|
| +#elif defined(__aarch64__)
|
| + g_cpuFamily = ANDROID_CPU_FAMILY_ARM64;
|
| +#elif defined(__x86_64__)
|
| + g_cpuFamily = ANDROID_CPU_FAMILY_X86_64;
|
| #else
|
| g_cpuFamily = ANDROID_CPU_FAMILY_UNKNOWN;
|
| #endif
|
| @@ -532,11 +700,8 @@ android_cpuInit(void)
|
|
|
| D("found cpuCount = %d\n", g_cpuCount);
|
|
|
| -#ifdef __ARM_ARCH__
|
| +#ifdef __arm__
|
| {
|
| - char* features = NULL;
|
| - char* architecture = NULL;
|
| -
|
| /* Extract architecture from the "CPU Architecture" field.
|
| * The list is well-known, unlike the the output of
|
| * the 'Processor' field which can vary greatly.
|
| @@ -557,10 +722,7 @@ android_cpuInit(void)
|
| /* read the initial decimal number, ignore the rest */
|
| archNumber = strtol(cpuArch, &end, 10);
|
|
|
| - /* Here we assume that ARMv8 will be upwards compatible with v7
|
| - * in the future. Unfortunately, there is no 'Features' field to
|
| - * indicate that Thumb-2 is supported.
|
| - */
|
| + /* Note that ARMv8 is upwards compatible with ARMv7. */
|
| if (end > cpuArch && archNumber >= 7) {
|
| hasARMv7 = 1;
|
| }
|
| @@ -600,8 +762,25 @@ android_cpuInit(void)
|
| free(cpuArch);
|
| }
|
|
|
| + /* Check Houdini binary translator is installed */
|
| + int has_houdini = has_houdini_binary_translator();
|
| +
|
| /* Extract the list of CPU features from ELF hwcaps */
|
| - uint32_t hwcaps = get_elf_hwcap();
|
| + uint32_t hwcaps = 0;
|
| + if (!has_houdini) {
|
| + hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP);
|
| + }
|
| + if (!hwcaps) {
|
| + D("Parsing /proc/self/auxv to extract ELF hwcaps!\n");
|
| + hwcaps = get_elf_hwcap_from_proc_self_auxv();
|
| + }
|
| + if (!hwcaps) {
|
| + // Parsing /proc/self/auxv will fail from regular application
|
| + // processes on some Android platform versions, when this happens
|
| + // parse proc/cpuinfo instead.
|
| + D("Parsing /proc/cpuinfo to extract ELF hwcaps!\n");
|
| + hwcaps = get_elf_hwcap_from_proc_cpuinfo(cpuinfo, cpuinfo_len);
|
| + }
|
|
|
| if (hwcaps != 0) {
|
| int has_vfp = (hwcaps & HWCAP_VFP);
|
| @@ -618,9 +797,9 @@ android_cpuInit(void)
|
|
|
| // 'vfpv4' implies VFPv3|VFP_FMA|FP16
|
| if (has_vfpv4)
|
| - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3 |
|
| - ANDROID_CPU_ARM_FEATURE_VFP_FP16 |
|
| - ANDROID_CPU_ARM_FEATURE_VFP_FMA;
|
| + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3 |
|
| + ANDROID_CPU_ARM_FEATURE_VFP_FP16 |
|
| + ANDROID_CPU_ARM_FEATURE_VFP_FMA;
|
|
|
| // 'vfpv3' or 'vfpv3d16' imply VFPv3. Note that unlike GCC,
|
| // a value of 'vfpv3' doesn't necessarily mean that the D32
|
| @@ -628,45 +807,188 @@ android_cpuInit(void)
|
| // field that support D32 also support NEON, so this should
|
| // not be a problem in practice.
|
| if (has_vfpv3 || has_vfpv3d16)
|
| - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
|
| + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
|
|
|
| // 'vfp' is super ambiguous. Depending on the kernel, it can
|
| // either mean VFPv2 or VFPv3. Make it depend on ARMv7.
|
| if (has_vfp) {
|
| if (g_cpuFeatures & ANDROID_CPU_ARM_FEATURE_ARMv7)
|
| - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
|
| + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
|
| else
|
| - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv2;
|
| + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv2;
|
| }
|
|
|
| // Neon implies VFPv3|D32, and if vfpv4 is detected, NEON_FMA
|
| if (has_neon) {
|
| - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3 |
|
| - ANDROID_CPU_ARM_FEATURE_NEON |
|
| - ANDROID_CPU_ARM_FEATURE_VFP_D32;
|
| + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3 |
|
| + ANDROID_CPU_ARM_FEATURE_NEON |
|
| + ANDROID_CPU_ARM_FEATURE_VFP_D32;
|
| if (has_vfpv4)
|
| - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON_FMA;
|
| + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON_FMA;
|
| }
|
|
|
| // VFPv3 implies VFPv2 and ARMv7
|
| if (g_cpuFeatures & ANDROID_CPU_ARM_FEATURE_VFPv3)
|
| - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv2 |
|
| - ANDROID_CPU_ARM_FEATURE_ARMv7;
|
| -
|
| - // Note that some buggy kernels do not report these even when
|
| - // the CPU actually support the division instructions. However,
|
| - // assume that if 'vfpv4' is detected, then the CPU supports
|
| - // sdiv/udiv properly.
|
| - if (has_idiva || has_vfpv4)
|
| - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_ARM;
|
| - if (has_idivt || has_vfpv4)
|
| - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2;
|
| + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv2 |
|
| + ANDROID_CPU_ARM_FEATURE_ARMv7;
|
| +
|
| + if (has_idiva)
|
| + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_ARM;
|
| + if (has_idivt)
|
| + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2;
|
|
|
| if (has_iwmmxt)
|
| - g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_iWMMXt;
|
| + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_iWMMXt;
|
| + }
|
| +
|
| + /* Extract the list of CPU features from ELF hwcaps2 */
|
| + uint32_t hwcaps2 = 0;
|
| + if (!has_houdini) {
|
| + hwcaps2 = get_elf_hwcap_from_getauxval(AT_HWCAP2);
|
| + }
|
| + if (hwcaps2 != 0) {
|
| + int has_aes = (hwcaps2 & HWCAP2_AES);
|
| + int has_pmull = (hwcaps2 & HWCAP2_PMULL);
|
| + int has_sha1 = (hwcaps2 & HWCAP2_SHA1);
|
| + int has_sha2 = (hwcaps2 & HWCAP2_SHA2);
|
| + int has_crc32 = (hwcaps2 & HWCAP2_CRC32);
|
| +
|
| + if (has_aes)
|
| + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_AES;
|
| + if (has_pmull)
|
| + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_PMULL;
|
| + if (has_sha1)
|
| + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_SHA1;
|
| + if (has_sha2)
|
| + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_SHA2;
|
| + if (has_crc32)
|
| + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_CRC32;
|
| + }
|
| + /* Extract the cpuid value from various fields */
|
| + // The CPUID value is broken up in several entries in /proc/cpuinfo.
|
| + // This table is used to rebuild it from the entries.
|
| + static const struct CpuIdEntry {
|
| + const char* field;
|
| + char format;
|
| + char bit_lshift;
|
| + char bit_length;
|
| + } cpu_id_entries[] = {
|
| + { "CPU implementer", 'x', 24, 8 },
|
| + { "CPU variant", 'x', 20, 4 },
|
| + { "CPU part", 'x', 4, 12 },
|
| + { "CPU revision", 'd', 0, 4 },
|
| + };
|
| + size_t i;
|
| + D("Parsing /proc/cpuinfo to recover CPUID\n");
|
| + for (i = 0;
|
| + i < sizeof(cpu_id_entries)/sizeof(cpu_id_entries[0]);
|
| + ++i) {
|
| + const struct CpuIdEntry* entry = &cpu_id_entries[i];
|
| + char* value = extract_cpuinfo_field(cpuinfo,
|
| + cpuinfo_len,
|
| + entry->field);
|
| + if (value == NULL)
|
| + continue;
|
| +
|
| + D("field=%s value='%s'\n", entry->field, value);
|
| + char* value_end = value + strlen(value);
|
| + int val = 0;
|
| + const char* start = value;
|
| + const char* p;
|
| + if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) {
|
| + start += 2;
|
| + p = parse_hexadecimal(start, value_end, &val);
|
| + } else if (entry->format == 'x')
|
| + p = parse_hexadecimal(value, value_end, &val);
|
| + else
|
| + p = parse_decimal(value, value_end, &val);
|
| +
|
| + if (p > (const char*)start) {
|
| + val &= ((1 << entry->bit_length)-1);
|
| + val <<= entry->bit_lshift;
|
| + g_cpuIdArm |= (uint32_t) val;
|
| + }
|
| +
|
| + free(value);
|
| + }
|
| +
|
| + // Handle kernel configuration bugs that prevent the correct
|
| + // reporting of CPU features.
|
| + static const struct CpuFix {
|
| + uint32_t cpuid;
|
| + uint64_t or_flags;
|
| + } cpu_fixes[] = {
|
| + /* The Nexus 4 (Qualcomm Krait) kernel configuration
|
| + * forgets to report IDIV support. */
|
| + { 0x510006f2, ANDROID_CPU_ARM_FEATURE_IDIV_ARM |
|
| + ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 },
|
| + { 0x510006f3, ANDROID_CPU_ARM_FEATURE_IDIV_ARM |
|
| + ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 },
|
| + };
|
| + size_t n;
|
| + for (n = 0; n < sizeof(cpu_fixes)/sizeof(cpu_fixes[0]); ++n) {
|
| + const struct CpuFix* entry = &cpu_fixes[n];
|
| +
|
| + if (g_cpuIdArm == entry->cpuid)
|
| + g_cpuFeatures |= entry->or_flags;
|
| + }
|
| +
|
| + // Special case: The emulator-specific Android 4.2 kernel fails
|
| + // to report support for the 32-bit ARM IDIV instruction.
|
| + // Technically, this is a feature of the virtual CPU implemented
|
| + // by the emulator. Note that it could also support Thumb IDIV
|
| + // in the future, and this will have to be slightly updated.
|
| + char* hardware = extract_cpuinfo_field(cpuinfo,
|
| + cpuinfo_len,
|
| + "Hardware");
|
| + if (hardware) {
|
| + if (!strcmp(hardware, "Goldfish") &&
|
| + g_cpuIdArm == 0x4100c080 &&
|
| + (g_cpuFamily & ANDROID_CPU_ARM_FEATURE_ARMv7) != 0) {
|
| + g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_ARM;
|
| + }
|
| + free(hardware);
|
| + }
|
| + }
|
| +#endif /* __arm__ */
|
| +#ifdef __aarch64__
|
| + {
|
| + /* Extract the list of CPU features from ELF hwcaps */
|
| + uint32_t hwcaps = 0;
|
| + hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP);
|
| + if (hwcaps != 0) {
|
| + int has_fp = (hwcaps & HWCAP_FP);
|
| + int has_asimd = (hwcaps & HWCAP_ASIMD);
|
| + int has_aes = (hwcaps & HWCAP_AES);
|
| + int has_pmull = (hwcaps & HWCAP_PMULL);
|
| + int has_sha1 = (hwcaps & HWCAP_SHA1);
|
| + int has_sha2 = (hwcaps & HWCAP_SHA2);
|
| + int has_crc32 = (hwcaps & HWCAP_CRC32);
|
| +
|
| + if(has_fp == 0) {
|
| + D("ERROR: Floating-point unit missing, but is required by Android on AArch64 CPUs\n");
|
| + }
|
| + if(has_asimd == 0) {
|
| + D("ERROR: ASIMD unit missing, but is required by Android on AArch64 CPUs\n");
|
| + }
|
| +
|
| + if (has_fp)
|
| + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_FP;
|
| + if (has_asimd)
|
| + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_ASIMD;
|
| + if (has_aes)
|
| + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_AES;
|
| + if (has_pmull)
|
| + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_PMULL;
|
| + if (has_sha1)
|
| + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_SHA1;
|
| + if (has_sha2)
|
| + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_SHA2;
|
| + if (has_crc32)
|
| + g_cpuFeatures |= ANDROID_CPU_ARM64_FEATURE_CRC32;
|
| }
|
| }
|
| -#endif /* __ARM_ARCH__ */
|
| +#endif /* __aarch64__ */
|
|
|
| #ifdef __i386__
|
| int regs[4];
|
| @@ -741,6 +1063,25 @@ android_setCpu(int cpu_count, uint64_t cpu_features)
|
| return 1;
|
| }
|
|
|
| +#ifdef __arm__
|
| +uint32_t
|
| +android_getCpuIdArm(void)
|
| +{
|
| + pthread_once(&g_once, android_cpuInit);
|
| + return g_cpuIdArm;
|
| +}
|
| +
|
| +int
|
| +android_setCpuArm(int cpu_count, uint64_t cpu_features, uint32_t cpu_id)
|
| +{
|
| + if (!android_setCpu(cpu_count, cpu_features))
|
| + return 0;
|
| +
|
| + g_cpuIdArm = cpu_id;
|
| + return 1;
|
| +}
|
| +#endif /* __arm__ */
|
| +
|
| /*
|
| * Technical note: Making sense of ARM's FPU architecture versions.
|
| *
|
| @@ -925,3 +1266,5 @@ android_setCpu(int cpu_count, uint64_t cpu_features)
|
| * ARCH_NEON_FP16 (+EXT_FP16)
|
| *
|
| */
|
| +
|
| +#endif // defined(__le32__) || defined(__le64__)
|
|
|