diff options
author | djsollen <djsollen@google.com> | 2015-01-21 12:11:00 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-01-21 12:11:00 -0800 |
commit | eae7fc09c6a44d5fc610b79dc5ab0631ddd02354 (patch) | |
tree | e1190b21381df05cab9068c9b95de486527970af /platform_tools | |
parent | a1193e4b0e34a7e4e1bd33e9708d7341679f8321 (diff) |
Update the version of cpu-features that we use for testing.
Review URL: https://codereview.chromium.org/858233003
Diffstat (limited to 'platform_tools')
3 files changed, 544 insertions, 91 deletions
diff --git a/platform_tools/android/third_party/cpufeatures/README b/platform_tools/android/third_party/cpufeatures/README index 5815f0cbe8..d92522e040 100644 --- a/platform_tools/android/third_party/cpufeatures/README +++ b/platform_tools/android/third_party/cpufeatures/README @@ -1,5 +1,5 @@ The contents of this directory are directly copied from the Android NDK to avoid the need to have a dependency on the NDK directly. -NDK_REVISION: 8d (December 2012) -NDK_FILE_LOCAtION: ($NDK)/source/android/cpufeatures +NDK_REVISION: 10c (October 2014) +NDK_FILE_LOCAtION: ($NDK)/sources/android/cpufeatures diff --git a/platform_tools/android/third_party/cpufeatures/cpu-features.c b/platform_tools/android/third_party/cpufeatures/cpu-features.c index 5b0a9d9343..2d23efb8f9 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__) diff --git a/platform_tools/android/third_party/cpufeatures/cpu-features.h b/platform_tools/android/third_party/cpufeatures/cpu-features.h index f8553e88d5..e86cba882f 100644 --- a/platform_tools/android/third_party/cpufeatures/cpu-features.h +++ b/platform_tools/android/third_party/cpufeatures/cpu-features.h @@ -33,21 +33,48 @@ __BEGIN_DECLS +/* A list of valid values returned by android_getCpuFamily(). + * They describe the CPU Architecture of the current process. + */ typedef enum { ANDROID_CPU_FAMILY_UNKNOWN = 0, ANDROID_CPU_FAMILY_ARM, ANDROID_CPU_FAMILY_X86, ANDROID_CPU_FAMILY_MIPS, + ANDROID_CPU_FAMILY_ARM64, + ANDROID_CPU_FAMILY_X86_64, + ANDROID_CPU_FAMILY_MIPS64, ANDROID_CPU_FAMILY_MAX /* do not remove */ } AndroidCpuFamily; -/* Return family of the device's CPU */ -extern AndroidCpuFamily android_getCpuFamily(void); +/* Return the CPU family of the current process. + * + * Note that this matches the bitness of the current process. I.e. when + * running a 32-bit binary on a 64-bit capable CPU, this will return the + * 32-bit CPU family value. + */ +extern AndroidCpuFamily android_getCpuFamily(void); + +/* Return a bitmap describing a set of optional CPU features that are + * supported by the current device's CPU. The exact bit-flags returned + * depend on the value returned by android_getCpuFamily(). See the + * documentation for the ANDROID_CPU_*_FEATURE_* flags below for details. + * + * NOTE: This will return 0 for the following architectures that don't have + * optional features listed at the moment: + * + * ANDROID_CPU_FAMILY_MIPS + * ANDROID_CPU_FAMILY_ARM64 + * ANDROID_CPU_FAMILY_X86_64 + * ANDROID_CPU_FAMILY_MIPS64 + */ +extern uint64_t android_getCpuFeatures(void); -/* The list of feature flags for ARM CPUs that can be recognized by the - * library. Value details are: +/* The list of feature flags for ANDROID_CPU_FAMILY_ARM that can be + * recognized by the library (see note below for 64-bit ARM). Value details + * are: * * VFPv2: * CPU supports the VFPv2 instruction set. Many, but not all, ARMv6 CPUs @@ -103,6 +130,27 @@ extern AndroidCpuFamily android_getCpuFamily(void); * ARM CPU. This is only available on a few XScale-based CPU designs * sold by Marvell. Pretty rare in practice. * + * AES: + * CPU supports AES instructions. These instructions are only + * available for 32-bit applications running on ARMv8 CPU. + * + * CRC32: + * CPU supports CRC32 instructions. These instructions are only + * available for 32-bit applications running on ARMv8 CPU. + * + * SHA2: + * CPU supports SHA2 instructions. These instructions are only + * available for 32-bit applications running on ARMv8 CPU. + * + * SHA1: + * CPU supports SHA1 instructions. These instructions are only + * available for 32-bit applications running on ARMv8 CPU. + * + * PMULL: + * CPU supports 64-bit PMULL and PMULL2 instructions. These + * instructions are only available for 32-bit applications + * running on ARMv8 CPU. + * * If you want to tell the compiler to generate code that targets one of * the feature set above, you should probably use one of the following * flags (for more details, see technical note at the end of this file): @@ -150,6 +198,13 @@ extern AndroidCpuFamily android_getCpuFamily(void); * * -mcpu=iwmmxt * Allows the use of iWMMXt instrinsics with GCC. + * + * IMPORTANT NOTE: These flags should only be tested when + * android_getCpuFamily() returns ANDROID_CPU_FAMILY_ARM, i.e. this is a + * 32-bit process. + * + * When running a 64-bit ARM process on an ARMv8 CPU, + * android_getCpuFeatures() will return a different set of bitflags */ enum { ANDROID_CPU_ARM_FEATURE_ARMv7 = (1 << 0), @@ -164,18 +219,58 @@ enum { ANDROID_CPU_ARM_FEATURE_IDIV_ARM = (1 << 9), ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2 = (1 << 10), ANDROID_CPU_ARM_FEATURE_iWMMXt = (1 << 11), + ANDROID_CPU_ARM_FEATURE_AES = (1 << 12), + ANDROID_CPU_ARM_FEATURE_PMULL = (1 << 13), + ANDROID_CPU_ARM_FEATURE_SHA1 = (1 << 14), + ANDROID_CPU_ARM_FEATURE_SHA2 = (1 << 15), + ANDROID_CPU_ARM_FEATURE_CRC32 = (1 << 16), }; +/* The bit flags corresponding to the output of android_getCpuFeatures() + * when android_getCpuFamily() returns ANDROID_CPU_FAMILY_ARM64. Value details + * are: + * + * FP: + * CPU has Floating-point unit. + * + * ASIMD: + * CPU has Advanced SIMD unit. + * + * AES: + * CPU supports AES instructions. + * + * CRC32: + * CPU supports CRC32 instructions. + * + * SHA2: + * CPU supports SHA2 instructions. + * + * SHA1: + * CPU supports SHA1 instructions. + * + * PMULL: + * CPU supports 64-bit PMULL and PMULL2 instructions. + */ +enum { + ANDROID_CPU_ARM64_FEATURE_FP = (1 << 0), + ANDROID_CPU_ARM64_FEATURE_ASIMD = (1 << 1), + ANDROID_CPU_ARM64_FEATURE_AES = (1 << 2), + ANDROID_CPU_ARM64_FEATURE_PMULL = (1 << 3), + ANDROID_CPU_ARM64_FEATURE_SHA1 = (1 << 4), + ANDROID_CPU_ARM64_FEATURE_SHA2 = (1 << 5), + ANDROID_CPU_ARM64_FEATURE_CRC32 = (1 << 6), +}; +/* The bit flags corresponding to the output of android_getCpuFeatures() + * when android_getCpuFamily() returns ANDROID_CPU_FAMILY_X86. + */ enum { ANDROID_CPU_X86_FEATURE_SSSE3 = (1 << 0), ANDROID_CPU_X86_FEATURE_POPCNT = (1 << 1), ANDROID_CPU_X86_FEATURE_MOVBE = (1 << 2), }; -extern uint64_t android_getCpuFeatures(void); - /* Return the number of CPU cores detected on this device. */ -extern int android_getCpuCount(void); +extern int android_getCpuCount(void); /* The following is used to force the CPU count and features * mask in sandboxed processes. Under 4.1 and higher, these processes @@ -190,6 +285,21 @@ extern int android_getCpuCount(void); extern int android_setCpu(int cpu_count, uint64_t cpu_features); +#ifdef __arm__ +/* Retrieve the ARM 32-bit CPUID value from the kernel. + * Note that this cannot work on sandboxed processes under 4.1 and + * higher, unless you called android_setCpuArm() before. + */ +extern uint32_t android_getCpuIdArm(void); + +/* An ARM-specific variant of android_setCpu() that also allows you + * to set the ARM CPUID field. + */ +extern int android_setCpuArm(int cpu_count, + uint64_t cpu_features, + uint32_t cpu_id); +#endif + __END_DECLS #endif /* CPU_FEATURES_H */ |