mirror of
https://github.com/google/cpu_features.git
synced 2025-07-02 13:43:18 +02:00
Add FreeBSD Arm64 support (#295)
* Add FreeBSD Arm64 detection Getting all the features is handled by reading /var/run/dmesg.boot. Feature detections were taken from the freebsd kernel code sys/arm64/arm64/identcpu.c * Add FreeBSD Arm64 tests * Add flagm, flagm2 and rng detection * Add HWCAP FreeBSD AArch64 * Update include to use linux hwcaps for powerpc * Add FreeBSD aarch64 impl * Separate Hwacps to freebsd and linux implementation * Add aarch64 midr_el1 implementation * Add detection hwcap cpuid to hwcaps.h * Add MIDR_EL1 tests
This commit is contained in:
143
src/hwcaps.c
143
src/hwcaps.c
@ -14,12 +14,7 @@
|
||||
|
||||
#include "internal/hwcaps.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cpu_features_macros.h"
|
||||
#include "internal/filesystem.h"
|
||||
#include "internal/string_view.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
static bool IsSet(const uint32_t mask, const uint32_t value) {
|
||||
if (mask == 0) return false;
|
||||
@ -31,139 +26,3 @@ bool CpuFeatures_IsHwCapsSet(const HardwareCapabilities hwcaps_mask,
|
||||
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
|
||||
|
54
src/hwcaps_freebsd.c
Normal file
54
src/hwcaps_freebsd.c
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2023 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 "cpu_features_macros.h"
|
||||
|
||||
#ifdef CPU_FEATURES_OS_FREEBSD
|
||||
|
||||
#include "internal/hwcaps.h"
|
||||
|
||||
#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
|
||||
|
||||
#ifdef HAVE_STRONG_ELF_AUX_INFO
|
||||
#include <stddef.h>
|
||||
#include <sys/auxv.h>
|
||||
|
||||
static unsigned long GetElfHwcapFromElfAuxInfo(int hwcap_type) {
|
||||
unsigned long hwcap;
|
||||
elf_aux_info(hwcap_type, &hwcap, sizeof(hwcap));
|
||||
return hwcap;
|
||||
}
|
||||
|
||||
HardwareCapabilities CpuFeatures_GetHardwareCapabilities(void) {
|
||||
HardwareCapabilities capabilities;
|
||||
capabilities.hwcaps = GetElfHwcapFromElfAuxInfo(AT_HWCAP);
|
||||
capabilities.hwcaps2 = GetElfHwcapFromElfAuxInfo(AT_HWCAP2);
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
const char *CpuFeatures_GetPlatformPointer(void) { return NULL; }
|
||||
|
||||
const char *CpuFeatures_GetBasePlatformPointer(void) { return NULL; }
|
||||
|
||||
#else
|
||||
#error "FreeBSD needs support for elf_aux_info"
|
||||
#endif // HAVE_STRONG_ELF_AUX_INFO
|
||||
|
||||
#endif // CPU_FEATURES_TEST
|
||||
#endif // CPU_FEATURES_OS_FREEBSD
|
161
src/hwcaps_linux_or_android.c
Normal file
161
src/hwcaps_linux_or_android.c
Normal file
@ -0,0 +1,161 @@
|
||||
// Copyright 2023 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 "cpu_features_macros.h"
|
||||
|
||||
#if defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cpu_features_macros.h"
|
||||
#include "internal/filesystem.h"
|
||||
#include "internal/hwcaps.h"
|
||||
#include "internal/string_view.h"
|
||||
|
||||
#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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
bool CpuFeatures_IsHwCapCpuidSupported() {
|
||||
return GetElfHwcapFromGetauxval(AARCH64_HWCAP_CPUID);
|
||||
}
|
||||
|
||||
#endif // CPU_FEATURES_TEST
|
||||
#endif // defined(CPU_FEATURES_OS_LINUX) || defined(CPU_FEATURES_OS_ANDROID)
|
40
src/impl_aarch64_cpuid.c
Normal file
40
src/impl_aarch64_cpuid.c
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2023 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 "cpu_features_macros.h"
|
||||
|
||||
#ifdef CPU_FEATURES_ARCH_AARCH64
|
||||
#if (defined(CPU_FEATURES_OS_FREEBSD) || defined(CPU_FEATURES_OS_LINUX) || \
|
||||
defined(CPU_FEATURES_OS_ANDROID))
|
||||
#if (defined(CPU_FEATURES_COMPILER_GCC) || defined(CPU_FEATURES_COMPILER_CLANG))
|
||||
|
||||
#include "internal/cpuid_aarch64.h"
|
||||
|
||||
#ifdef CPU_FEATURES_MOCK_CPUID_AARCH64
|
||||
// Implementation will be provided by test/cpuinfo_aarch64_test.cc.
|
||||
#else
|
||||
uint64_t GetMidrEl1(void) {
|
||||
uint64_t midr_el1;
|
||||
__asm("mrs %0, MIDR_EL1" : "=r"(midr_el1));
|
||||
return midr_el1;
|
||||
}
|
||||
#endif // CPU_FEATURES_MOCK_CPUID_AARCH64
|
||||
|
||||
#else
|
||||
#error "Unsupported compiler, aarch64 cpuid requires either GCC or Clang."
|
||||
#endif // (defined(CPU_FEATURES_COMPILER_GCC) ||
|
||||
// defined(CPU_FEATURES_COMPILER_CLANG))
|
||||
#endif // (defined(CPU_FEATURES_OS_FREEBSD) || defined(CPU_FEATURES_OS_LINUX)
|
||||
// || defined(CPU_FEATURES_OS_ANDROID))
|
||||
#endif // CPU_FEATURES_ARCH_AARCH64
|
46
src/impl_aarch64_freebsd.c
Normal file
46
src/impl_aarch64_freebsd.c
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2023 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 "cpu_features_macros.h"
|
||||
|
||||
#ifdef CPU_FEATURES_ARCH_AARCH64
|
||||
#ifdef CPU_FEATURES_OS_FREEBSD
|
||||
|
||||
#include "cpuinfo_aarch64.h"
|
||||
#include "impl_aarch64__base_implementation.inl"
|
||||
#include "internal/cpuid_aarch64.h"
|
||||
#include "internal/hwcaps.h"
|
||||
|
||||
static const Aarch64Info kEmptyAarch64Info;
|
||||
|
||||
Aarch64Info GetAarch64Info(void) {
|
||||
Aarch64Info info = kEmptyAarch64Info;
|
||||
const HardwareCapabilities hwcaps = CpuFeatures_GetHardwareCapabilities();
|
||||
for (size_t i = 0; i < AARCH64_LAST_; ++i) {
|
||||
if (CpuFeatures_IsHwCapsSet(kHardwareCapabilities[i], hwcaps)) {
|
||||
kSetters[i](&info.features, true);
|
||||
}
|
||||
}
|
||||
if (info.features.cpuid) {
|
||||
const uint64_t midr_el1 = GetMidrEl1();
|
||||
info.implementer = (int)ExtractBitRange(midr_el1, 31, 24);
|
||||
info.variant = (int)ExtractBitRange(midr_el1, 23, 20);
|
||||
info.part = (int)ExtractBitRange(midr_el1, 15, 4);
|
||||
info.revision = (int)ExtractBitRange(midr_el1, 3, 0);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
#endif // CPU_FEATURES_OS_FREEBSD
|
||||
#endif // CPU_FEATURES_ARCH_AARCH64
|
Reference in New Issue
Block a user