mirror of
https://github.com/google/cpu_features.git
synced 2025-04-28 15:33:37 +02:00

* Fix getauxval comment (API 18 not 20)
getauxval is available in Android starting with API 18, not 20.
The comment about __ANDROID_API__ appears to have been copied from the
NDK's cpufeatures, which always uses dlopen/dlsym and doesn't assume it
can directly call getauxval, even if __ANDROID_API__ is new enough.
With this project, though, when __ANDROID_API__ is 18 or up, the
CMakeLists.txt file would detect that getauxval is available and define
HAVE_STRONG_GETAUXVAL.
* Broaden Qualcomm Krait idiv workaround
Some Qualcomm Krait CPUs have IDIV support but the kernel doesn't
report it. Previously, this code looked for two CPUs:
- 0x510006F2 (0x51/'Q', variant 0, part 0x06f, rev 2)
- 0x510006F3 (0x51/'Q', variant 0, part 0x06f, rev 3)
This check misses my 2013 Nexus 7 device, which has this CPU ID:
- 0x511006f0 (0x51/'Q', variant 1, part 0x06f, rev 0)
My Nexus 7 device doesn't report idiv through AT_HWCAP or through
/proc/cpuinfo (AT_HWCAP is 0x1b0d7).
Expand the check to anything with:
- implementer 0x51
- architecture 7
- part 0x4d or 0x6f
Part 0x4d appears to be a dual-core Krait (e.g. see
https://crbug.com/341598#c43).
This new matching behavior is a subset of what the upstream kernel
does (patch[1] contributed by CodeAurora), and also closely matches the
behavior of pytorch/cpuinfo.
[1] 120ecfafab
170 lines
5.1 KiB
C
170 lines
5.1 KiB
C
// Copyright 2017 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "internal/hwcaps.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "cpu_features_macros.h"
|
|
#include "internal/filesystem.h"
|
|
#include "internal/string_view.h"
|
|
|
|
static bool IsSet(const uint32_t mask, const uint32_t value) {
|
|
if (mask == 0) return false;
|
|
return (value & mask) == mask;
|
|
}
|
|
|
|
bool CpuFeatures_IsHwCapsSet(const HardwareCapabilities hwcaps_mask,
|
|
const HardwareCapabilities hwcaps) {
|
|
return IsSet(hwcaps_mask.hwcaps, hwcaps.hwcaps) ||
|
|
IsSet(hwcaps_mask.hwcaps2, hwcaps.hwcaps2);
|
|
}
|
|
|
|
#ifdef CPU_FEATURES_TEST
|
|
// In test mode, hwcaps_for_testing will define the following functions.
|
|
HardwareCapabilities CpuFeatures_GetHardwareCapabilities(void);
|
|
const char* CpuFeatures_GetPlatformPointer(void);
|
|
const char* CpuFeatures_GetBasePlatformPointer(void);
|
|
#else
|
|
|
|
// Debug facilities
|
|
#if defined(NDEBUG)
|
|
#define D(...)
|
|
#else
|
|
#include <stdio.h>
|
|
#define D(...) \
|
|
do { \
|
|
printf(__VA_ARGS__); \
|
|
fflush(stdout); \
|
|
} while (0)
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Implementation of GetElfHwcapFromGetauxval
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define AT_HWCAP 16
|
|
#define AT_HWCAP2 26
|
|
#define AT_PLATFORM 15
|
|
#define AT_BASE_PLATFORM 24
|
|
|
|
#if defined(HAVE_STRONG_GETAUXVAL)
|
|
#include <sys/auxv.h>
|
|
static unsigned long GetElfHwcapFromGetauxval(uint32_t hwcap_type) {
|
|
return getauxval(hwcap_type);
|
|
}
|
|
#elif defined(HAVE_DLFCN_H)
|
|
// On Android we 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 18.
|
|
//
|
|
// 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.
|
|
|
|
#include <dlfcn.h>
|
|
|
|
typedef unsigned long getauxval_func_t(unsigned long);
|
|
|
|
static uint32_t GetElfHwcapFromGetauxval(uint32_t hwcap_type) {
|
|
uint32_t ret = 0;
|
|
void *libc_handle = NULL;
|
|
getauxval_func_t *func = NULL;
|
|
|
|
dlerror(); // Cleaning error state before calling dlopen.
|
|
libc_handle = dlopen("libc.so", RTLD_NOW);
|
|
if (!libc_handle) {
|
|
D("Could not dlopen() C library: %s\n", dlerror());
|
|
return 0;
|
|
}
|
|
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;
|
|
}
|
|
#else
|
|
#error "This platform does not provide hardware capabilities."
|
|
#endif
|
|
|
|
// Implementation of GetHardwareCapabilities for OS that provide
|
|
// GetElfHwcapFromGetauxval().
|
|
|
|
// Fallback when getauxval is not available, retrieves hwcaps from
|
|
// "/proc/self/auxv".
|
|
static uint32_t GetElfHwcapFromProcSelfAuxv(uint32_t hwcap_type) {
|
|
struct {
|
|
uint32_t tag;
|
|
uint32_t value;
|
|
} entry;
|
|
uint32_t result = 0;
|
|
const char filepath[] = "/proc/self/auxv";
|
|
const int fd = CpuFeatures_OpenFile(filepath);
|
|
if (fd < 0) {
|
|
D("Could not open %s\n", filepath);
|
|
return 0;
|
|
}
|
|
for (;;) {
|
|
const int ret = CpuFeatures_ReadFile(fd, (char *)&entry, sizeof entry);
|
|
if (ret < 0) {
|
|
D("Error while reading %s\n", filepath);
|
|
break;
|
|
}
|
|
// Detect end of list.
|
|
if (ret == 0 || (entry.tag == 0 && entry.value == 0)) {
|
|
break;
|
|
}
|
|
if (entry.tag == hwcap_type) {
|
|
result = entry.value;
|
|
break;
|
|
}
|
|
}
|
|
CpuFeatures_CloseFile(fd);
|
|
return result;
|
|
}
|
|
|
|
// Retrieves hardware capabilities by first trying to call getauxval, if not
|
|
// available falls back to reading "/proc/self/auxv".
|
|
static unsigned long GetHardwareCapabilitiesFor(uint32_t type) {
|
|
unsigned long hwcaps = GetElfHwcapFromGetauxval(type);
|
|
if (!hwcaps) {
|
|
D("Parsing /proc/self/auxv to extract ELF hwcaps!\n");
|
|
hwcaps = GetElfHwcapFromProcSelfAuxv(type);
|
|
}
|
|
return hwcaps;
|
|
}
|
|
|
|
HardwareCapabilities CpuFeatures_GetHardwareCapabilities(void) {
|
|
HardwareCapabilities capabilities;
|
|
capabilities.hwcaps = GetHardwareCapabilitiesFor(AT_HWCAP);
|
|
capabilities.hwcaps2 = GetHardwareCapabilitiesFor(AT_HWCAP2);
|
|
return capabilities;
|
|
}
|
|
|
|
const char *CpuFeatures_GetPlatformPointer(void) {
|
|
return (const char *)GetHardwareCapabilitiesFor(AT_PLATFORM);
|
|
}
|
|
|
|
const char *CpuFeatures_GetBasePlatformPointer(void) {
|
|
return (const char *)GetHardwareCapabilitiesFor(AT_BASE_PLATFORM);
|
|
}
|
|
|
|
#endif // CPU_FEATURES_TEST
|