aboutsummaryrefslogtreecommitdiffhomepage
path: root/platform_tools
diff options
context:
space:
mode:
authorGravatar djsollen <djsollen@google.com>2015-01-21 12:11:00 -0800
committerGravatar Commit bot <commit-bot@chromium.org>2015-01-21 12:11:00 -0800
commiteae7fc09c6a44d5fc610b79dc5ab0631ddd02354 (patch)
treee1190b21381df05cab9068c9b95de486527970af /platform_tools
parenta1193e4b0e34a7e4e1bd33e9708d7341679f8321 (diff)
Update the version of cpu-features that we use for testing.
Diffstat (limited to 'platform_tools')
-rw-r--r--platform_tools/android/third_party/cpufeatures/README4
-rw-r--r--platform_tools/android/third_party/cpufeatures/cpu-features.c507
-rw-r--r--platform_tools/android/third_party/cpufeatures/cpu-features.h124
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 */