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__) |