|
|
|
@ -27,6 +27,10 @@ |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/* ChangeLog for this library:
|
|
|
|
|
* |
|
|
|
|
* NDK r10e?: Add MIPS MSA feature. |
|
|
|
|
* |
|
|
|
|
* NDK r10: Support for 64-bit CPUs (Intel, ARM & MIPS). |
|
|
|
|
* |
|
|
|
|
* NDK r8d: Add android_setCpu(). |
|
|
|
|
* |
|
|
|
@ -56,16 +60,17 @@ |
|
|
|
|
* |
|
|
|
|
* NDK r4: Initial release |
|
|
|
|
*/ |
|
|
|
|
#include <sys/system_properties.h> |
|
|
|
|
#ifdef __arm__ |
|
|
|
|
#include <machine/cpu-features.h> |
|
|
|
|
#endif |
|
|
|
|
#include <pthread.h> |
|
|
|
|
|
|
|
|
|
#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> |
|
|
|
|
#include <unistd.h> |
|
|
|
|
|
|
|
|
|
static pthread_once_t g_once; |
|
|
|
|
static int g_inited; |
|
|
|
@ -73,16 +78,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) { \
|
|
|
|
@ -109,6 +110,25 @@ static __inline__ void x86_cpuid(int func, int values[4]) |
|
|
|
|
values[2] = c; |
|
|
|
|
values[3] = d; |
|
|
|
|
} |
|
|
|
|
#elif defined(__x86_64__) |
|
|
|
|
static __inline__ void x86_cpuid(int func, int values[4]) |
|
|
|
|
{ |
|
|
|
|
int64_t a, b, c, d; |
|
|
|
|
/* We need to preserve ebx since we're compiling PIC code */ |
|
|
|
|
/* this means we can't use "=b" for the second output register */ |
|
|
|
|
__asm__ __volatile__ ( \
|
|
|
|
|
"push %%rbx\n" |
|
|
|
|
"cpuid\n" \
|
|
|
|
|
"mov %%rbx, %1\n" |
|
|
|
|
"pop %%rbx\n" |
|
|
|
|
: "=a" (a), "=r" (b), "=c" (c), "=d" (d) \
|
|
|
|
|
: "a" (func) \
|
|
|
|
|
); |
|
|
|
|
values[0] = a; |
|
|
|
|
values[1] = b; |
|
|
|
|
values[2] = c; |
|
|
|
|
values[3] = d; |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
/* Get the size of a file by reading it until the end. This is needed
|
|
|
|
@ -118,7 +138,8 @@ static __inline__ void x86_cpuid(int func, int values[4]) |
|
|
|
|
static int |
|
|
|
|
get_file_size(const char* pathname) |
|
|
|
|
{ |
|
|
|
|
int fd, ret, result = 0; |
|
|
|
|
|
|
|
|
|
int fd, result = 0; |
|
|
|
|
char buffer[256]; |
|
|
|
|
|
|
|
|
|
fd = open(pathname, O_RDONLY); |
|
|
|
@ -178,6 +199,7 @@ read_file(const char* pathname, char* buffer, size_t buffsize) |
|
|
|
|
return count; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifdef __arm__ |
|
|
|
|
/* Extract the content of a the first occurence of a given field in
|
|
|
|
|
* the content of /proc/cpuinfo and return it as a heap-allocated |
|
|
|
|
* string that must be freed by the caller. |
|
|
|
@ -190,12 +212,11 @@ extract_cpuinfo_field(const char* buffer, int buflen, const char* field) |
|
|
|
|
int fieldlen = strlen(field); |
|
|
|
|
const char* bufend = buffer + buflen; |
|
|
|
|
char* result = NULL; |
|
|
|
|
int len, ignore; |
|
|
|
|
int len; |
|
|
|
|
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 +253,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. |
|
|
|
|
*/ |
|
|
|
@ -268,8 +285,9 @@ has_list_item(const char* list, const char* item) |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
#endif /* __arm__ */ |
|
|
|
|
|
|
|
|
|
/* 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 +298,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 +324,20 @@ 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); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifdef __arm__ |
|
|
|
|
static const char* |
|
|
|
|
parse_hexadecimal(const char* input, const char* limit, int* result) |
|
|
|
|
{ |
|
|
|
|
return parse_number(input, limit, 16, result); |
|
|
|
|
} |
|
|
|
|
#endif /* __arm__ */ |
|
|
|
|
|
|
|
|
|
/* 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 +448,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,27 +471,84 @@ 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(__mips__) |
|
|
|
|
// see <uapi/asm/hwcap.h> kernel header
|
|
|
|
|
#define HWCAP_MIPS_R6 (1 << 0) |
|
|
|
|
#define HWCAP_MIPS_MSA (1 << 1) |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#if defined(__arm__) || defined(__aarch64__) || defined(__mips__) |
|
|
|
|
|
|
|
|
|
#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); |
|
|
|
|
|
|
|
|
|
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__) |
|
|
|
|
/* Compute the ELF HWCAP flags.
|
|
|
|
|
*/ |
|
|
|
|
// 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(const char* cpuinfo, int cpuinfo_len) |
|
|
|
|
{ |
|
|
|
|
/* IMPORTANT:
|
|
|
|
|
* Accessing /proc/self/auxv doesn't work anymore on all |
|
|
|
|
* platform versions. More specifically, when running inside |
|
|
|
|
* a regular application process, most of /proc/self/ will be |
|
|
|
|
* non-readable, including /proc/self/auxv. This doesn't |
|
|
|
|
* happen however if the application is debuggable, or when |
|
|
|
|
* running under the "shell" UID, which is why this was not |
|
|
|
|
* detected appropriately. |
|
|
|
|
*/ |
|
|
|
|
#if 0 |
|
|
|
|
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; |
|
|
|
@ -447,11 +556,10 @@ get_elf_hwcap(const char* cpuinfo, int cpuinfo_len) |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
@ -465,12 +573,33 @@ get_elf_hwcap(const char* cpuinfo, int cpuinfo_len) |
|
|
|
|
} |
|
|
|
|
close(fd); |
|
|
|
|
return result; |
|
|
|
|
#else |
|
|
|
|
// Recreate ELF hwcaps by parsing /proc/cpuinfo Features tag.
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* 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); |
|
|
|
|
|
|
|
|
@ -496,7 +625,6 @@ get_elf_hwcap(const char* cpuinfo, int cpuinfo_len) |
|
|
|
|
free(cpuFeatures); |
|
|
|
|
} |
|
|
|
|
return hwcaps; |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
#endif /* __arm__ */ |
|
|
|
|
|
|
|
|
@ -526,12 +654,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 |
|
|
|
@ -576,11 +711,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. |
|
|
|
@ -601,10 +733,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; |
|
|
|
|
} |
|
|
|
@ -645,7 +774,19 @@ android_cpuInit(void) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Extract the list of CPU features from ELF hwcaps */ |
|
|
|
|
uint32_t hwcaps = get_elf_hwcap(cpuinfo, cpuinfo_len); |
|
|
|
|
uint32_t hwcaps = 0; |
|
|
|
|
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); |
|
|
|
@ -697,22 +838,163 @@ android_cpuInit(void) |
|
|
|
|
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) |
|
|
|
|
if (has_idiva) |
|
|
|
|
g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_ARM; |
|
|
|
|
if (has_idivt || has_vfpv4) |
|
|
|
|
if (has_idivt) |
|
|
|
|
g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_IDIV_THUMB2; |
|
|
|
|
|
|
|
|
|
if (has_iwmmxt) |
|
|
|
|
g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_iWMMXt; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Extract the list of CPU features from ELF hwcaps2 */ |
|
|
|
|
uint32_t hwcaps2 = 0; |
|
|
|
|
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_ARCH__ */ |
|
|
|
|
#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"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifdef __i386__ |
|
|
|
|
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 /* __aarch64__ */ |
|
|
|
|
|
|
|
|
|
#if defined(__i386__) || defined(__x86_64__) |
|
|
|
|
int regs[4]; |
|
|
|
|
|
|
|
|
|
/* According to http://en.wikipedia.org/wiki/CPUID */ |
|
|
|
@ -732,10 +1014,50 @@ android_cpuInit(void) |
|
|
|
|
if ((regs[2] & (1 << 23)) != 0) { |
|
|
|
|
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_POPCNT; |
|
|
|
|
} |
|
|
|
|
if ((regs[2] & (1 << 19)) != 0) { |
|
|
|
|
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSE4_1; |
|
|
|
|
} |
|
|
|
|
if ((regs[2] & (1 << 20)) != 0) { |
|
|
|
|
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSE4_2; |
|
|
|
|
} |
|
|
|
|
if (vendorIsIntel && (regs[2] & (1 << 22)) != 0) { |
|
|
|
|
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_MOVBE; |
|
|
|
|
} |
|
|
|
|
if ((regs[2] & (1 << 25)) != 0) { |
|
|
|
|
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AES_NI; |
|
|
|
|
} |
|
|
|
|
if ((regs[2] & (1 << 28)) != 0) { |
|
|
|
|
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AVX; |
|
|
|
|
} |
|
|
|
|
if ((regs[2] & (1 << 30)) != 0) { |
|
|
|
|
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_RDRAND; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
x86_cpuid(7, regs); |
|
|
|
|
if ((regs[1] & (1 << 5)) != 0) { |
|
|
|
|
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_AVX2; |
|
|
|
|
} |
|
|
|
|
if ((regs[1] & (1 << 29)) != 0) { |
|
|
|
|
g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SHA_NI; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif |
|
|
|
|
#if defined( __mips__) |
|
|
|
|
{ /* MIPS and MIPS64 */ |
|
|
|
|
/* 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_r6 = (hwcaps & HWCAP_MIPS_R6); |
|
|
|
|
int has_msa = (hwcaps & HWCAP_MIPS_MSA); |
|
|
|
|
if (has_r6) |
|
|
|
|
g_cpuFeatures |= ANDROID_CPU_MIPS_FEATURE_R6; |
|
|
|
|
if (has_msa) |
|
|
|
|
g_cpuFeatures |= ANDROID_CPU_MIPS_FEATURE_MSA; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
#endif /* __mips__ */ |
|
|
|
|
|
|
|
|
|
free(cpuinfo); |
|
|
|
|
} |
|
|
|
@ -785,6 +1107,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. |
|
|
|
|
* |