3rdparty: Update CPUInfo to latest

Allows building on FreeBSD.
This commit is contained in:
JordanTheToaster 2024-06-09 13:37:45 +01:00 committed by Connor McLaughlin
parent e2a4d8f1e6
commit b011e91abd
69 changed files with 10237 additions and 7950 deletions

View File

@ -67,6 +67,9 @@ ENDIF()
# -- [ Determine target processor # -- [ Determine target processor
SET(CPUINFO_TARGET_PROCESSOR "${CMAKE_SYSTEM_PROCESSOR}") SET(CPUINFO_TARGET_PROCESSOR "${CMAKE_SYSTEM_PROCESSOR}")
IF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD" AND CPUINFO_TARGET_PROCESSOR STREQUAL "amd64")
SET(CPUINFO_TARGET_PROCESSOR "AMD64")
ENDIF()
IF(IS_APPLE_OS AND CMAKE_OSX_ARCHITECTURES MATCHES "^(x86_64|arm64.*)$") IF(IS_APPLE_OS AND CMAKE_OSX_ARCHITECTURES MATCHES "^(x86_64|arm64.*)$")
SET(CPUINFO_TARGET_PROCESSOR "${CMAKE_OSX_ARCHITECTURES}") SET(CPUINFO_TARGET_PROCESSOR "${CMAKE_OSX_ARCHITECTURES}")
ELSEIF(CMAKE_GENERATOR MATCHES "^Visual Studio " AND CMAKE_VS_PLATFORM_NAME) ELSEIF(CMAKE_GENERATOR MATCHES "^Visual Studio " AND CMAKE_VS_PLATFORM_NAME)
@ -105,7 +108,7 @@ IF(NOT CMAKE_SYSTEM_NAME)
"Target operating system is not specified. " "Target operating system is not specified. "
"cpuinfo will compile, but cpuinfo_initialize() will always fail.") "cpuinfo will compile, but cpuinfo_initialize() will always fail.")
SET(CPUINFO_SUPPORTED_PLATFORM FALSE) SET(CPUINFO_SUPPORTED_PLATFORM FALSE)
ELSEIF(NOT CMAKE_SYSTEM_NAME MATCHES "^(Windows|WindowsStore|CYGWIN|MSYS|Darwin|Linux|Android)$") ELSEIF(NOT CMAKE_SYSTEM_NAME MATCHES "^(Windows|WindowsStore|CYGWIN|MSYS|Darwin|Linux|Android|FreeBSD)$")
IF(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14" AND NOT IS_APPLE_OS) IF(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14" AND NOT IS_APPLE_OS)
MESSAGE(WARNING MESSAGE(WARNING
"Target operating system \"${CMAKE_SYSTEM_NAME}\" is not supported in cpuinfo. " "Target operating system \"${CMAKE_SYSTEM_NAME}\" is not supported in cpuinfo. "
@ -178,6 +181,8 @@ IF(CPUINFO_SUPPORTED_PLATFORM)
LIST(APPEND CPUINFO_SRCS src/x86/mach/init.c) LIST(APPEND CPUINFO_SRCS src/x86/mach/init.c)
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "^(Windows|WindowsStore|CYGWIN|MSYS)$") ELSEIF(CMAKE_SYSTEM_NAME MATCHES "^(Windows|WindowsStore|CYGWIN|MSYS)$")
LIST(APPEND CPUINFO_SRCS src/x86/windows/init.c) LIST(APPEND CPUINFO_SRCS src/x86/windows/init.c)
ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
LIST(APPEND CPUINFO_SRCS src/x86/freebsd/init.c)
ENDIF() ENDIF()
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "^Windows" AND CPUINFO_TARGET_PROCESSOR MATCHES "^(ARM64|arm64)$") ELSEIF(CMAKE_SYSTEM_NAME MATCHES "^Windows" AND CPUINFO_TARGET_PROCESSOR MATCHES "^(ARM64|arm64)$")
LIST(APPEND CPUINFO_SRCS LIST(APPEND CPUINFO_SRCS
@ -234,9 +239,11 @@ IF(CPUINFO_SUPPORTED_PLATFORM)
src/linux/processors.c) src/linux/processors.c)
ELSEIF(IS_APPLE_OS) ELSEIF(IS_APPLE_OS)
LIST(APPEND CPUINFO_SRCS src/mach/topology.c) LIST(APPEND CPUINFO_SRCS src/mach/topology.c)
ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
LIST(APPEND CPUINFO_SRCS src/freebsd/topology.c)
ENDIF() ENDIF()
IF(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Android") IF(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Android" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
SET(CMAKE_THREAD_PREFER_PTHREAD TRUE) SET(CMAKE_THREAD_PREFER_PTHREAD TRUE)
SET(THREADS_PREFER_PTHREAD_FLAG TRUE) SET(THREADS_PREFER_PTHREAD_FLAG TRUE)
FIND_PACKAGE(Threads REQUIRED) FIND_PACKAGE(Threads REQUIRED)
@ -301,6 +308,9 @@ IF(CPUINFO_SUPPORTED_PLATFORM)
TARGET_LINK_LIBRARIES(cpuinfo_internals PUBLIC ${CMAKE_THREAD_LIBS_INIT}) TARGET_LINK_LIBRARIES(cpuinfo_internals PUBLIC ${CMAKE_THREAD_LIBS_INIT})
TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE _GNU_SOURCE=1) TARGET_COMPILE_DEFINITIONS(cpuinfo PRIVATE _GNU_SOURCE=1)
TARGET_COMPILE_DEFINITIONS(cpuinfo_internals PRIVATE _GNU_SOURCE=1) TARGET_COMPILE_DEFINITIONS(cpuinfo_internals PRIVATE _GNU_SOURCE=1)
ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
TARGET_LINK_LIBRARIES(cpuinfo PUBLIC ${CMAKE_THREAD_LIBS_INIT})
TARGET_LINK_LIBRARIES(cpuinfo_internals PUBLIC ${CMAKE_THREAD_LIBS_INIT})
ENDIF() ENDIF()
ELSE() ELSE()
TARGET_COMPILE_DEFINITIONS(cpuinfo INTERFACE CPUINFO_SUPPORTED_PLATFORM=0) TARGET_COMPILE_DEFINITIONS(cpuinfo INTERFACE CPUINFO_SUPPORTED_PLATFORM=0)

View File

@ -7,37 +7,35 @@
#include <cpuinfo.h> #include <cpuinfo.h>
#if defined(__linux__) #if defined(__linux__)
#include <sys/types.h> #include <sys/types.h>
#endif #endif
#if !defined(CPUINFO_MOCK) || !(CPUINFO_MOCK) #if !defined(CPUINFO_MOCK) || !(CPUINFO_MOCK)
#error This header is intended only for test use #error This header is intended only for test use
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
void CPUINFO_ABI cpuinfo_set_fpsid(uint32_t fpsid); void CPUINFO_ABI cpuinfo_set_fpsid(uint32_t fpsid);
void CPUINFO_ABI cpuinfo_set_wcid(uint32_t wcid); void CPUINFO_ABI cpuinfo_set_wcid(uint32_t wcid);
#endif /* CPUINFO_ARCH_ARM */ #endif /* CPUINFO_ARCH_ARM */
#if CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64 #if CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64
struct cpuinfo_mock_cpuid { struct cpuinfo_mock_cpuid {
uint32_t input_eax; uint32_t input_eax;
uint32_t input_ecx; uint32_t input_ecx;
uint32_t eax; uint32_t eax;
uint32_t ebx; uint32_t ebx;
uint32_t ecx; uint32_t ecx;
uint32_t edx; uint32_t edx;
}; };
void CPUINFO_ABI cpuinfo_mock_set_cpuid(struct cpuinfo_mock_cpuid* dump, size_t entries); void CPUINFO_ABI cpuinfo_mock_set_cpuid(struct cpuinfo_mock_cpuid* dump, size_t entries);
void CPUINFO_ABI cpuinfo_mock_get_cpuid(uint32_t eax, uint32_t regs[4]); void CPUINFO_ABI cpuinfo_mock_get_cpuid(uint32_t eax, uint32_t regs[4]);
void CPUINFO_ABI cpuinfo_mock_get_cpuidex(uint32_t eax, uint32_t ecx, uint32_t regs[4]); void CPUINFO_ABI cpuinfo_mock_get_cpuidex(uint32_t eax, uint32_t ecx, uint32_t regs[4]);
#endif /* CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64 */ #endif /* CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64 */
struct cpuinfo_mock_file { struct cpuinfo_mock_file {
@ -53,22 +51,22 @@ struct cpuinfo_mock_property {
}; };
#if defined(__linux__) #if defined(__linux__)
void CPUINFO_ABI cpuinfo_mock_filesystem(struct cpuinfo_mock_file* files); void CPUINFO_ABI cpuinfo_mock_filesystem(struct cpuinfo_mock_file* files);
int CPUINFO_ABI cpuinfo_mock_open(const char* path, int oflag); int CPUINFO_ABI cpuinfo_mock_open(const char* path, int oflag);
int CPUINFO_ABI cpuinfo_mock_close(int fd); int CPUINFO_ABI cpuinfo_mock_close(int fd);
ssize_t CPUINFO_ABI cpuinfo_mock_read(int fd, void* buffer, size_t capacity); ssize_t CPUINFO_ABI cpuinfo_mock_read(int fd, void* buffer, size_t capacity);
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
void CPUINFO_ABI cpuinfo_set_hwcap(uint32_t hwcap); void CPUINFO_ABI cpuinfo_set_hwcap(uint32_t hwcap);
#endif #endif
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
void CPUINFO_ABI cpuinfo_set_hwcap2(uint32_t hwcap2); void CPUINFO_ABI cpuinfo_set_hwcap2(uint32_t hwcap2);
#endif #endif
#endif #endif
#if defined(__ANDROID__) #if defined(__ANDROID__)
void CPUINFO_ABI cpuinfo_mock_android_properties(struct cpuinfo_mock_property* properties); void CPUINFO_ABI cpuinfo_mock_android_properties(struct cpuinfo_mock_property* properties);
void CPUINFO_ABI cpuinfo_mock_gl_renderer(const char* renderer); void CPUINFO_ABI cpuinfo_mock_gl_renderer(const char* renderer);
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus

File diff suppressed because it is too large Load Diff

View File

@ -6,13 +6,13 @@
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#ifdef __linux__ #ifdef __linux__
#include <linux/api.h> #include <linux/api.h>
#include <unistd.h> #include <sys/syscall.h>
#include <sys/syscall.h> #include <unistd.h>
#if !defined(__NR_getcpu) #if !defined(__NR_getcpu)
#include <asm-generic/unistd.h> #include <asm-generic/unistd.h>
#endif #endif
#endif #endif
bool cpuinfo_is_initialized = false; bool cpuinfo_is_initialized = false;
@ -21,57 +21,54 @@ struct cpuinfo_processor* cpuinfo_processors = NULL;
struct cpuinfo_core* cpuinfo_cores = NULL; struct cpuinfo_core* cpuinfo_cores = NULL;
struct cpuinfo_cluster* cpuinfo_clusters = NULL; struct cpuinfo_cluster* cpuinfo_clusters = NULL;
struct cpuinfo_package* cpuinfo_packages = NULL; struct cpuinfo_package* cpuinfo_packages = NULL;
struct cpuinfo_cache* cpuinfo_cache[cpuinfo_cache_level_max] = { NULL }; struct cpuinfo_cache* cpuinfo_cache[cpuinfo_cache_level_max] = {NULL};
uint32_t cpuinfo_processors_count = 0; uint32_t cpuinfo_processors_count = 0;
uint32_t cpuinfo_cores_count = 0; uint32_t cpuinfo_cores_count = 0;
uint32_t cpuinfo_clusters_count = 0; uint32_t cpuinfo_clusters_count = 0;
uint32_t cpuinfo_packages_count = 0; uint32_t cpuinfo_packages_count = 0;
uint32_t cpuinfo_cache_count[cpuinfo_cache_level_max] = { 0 }; uint32_t cpuinfo_cache_count[cpuinfo_cache_level_max] = {0};
uint32_t cpuinfo_max_cache_size = 0; uint32_t cpuinfo_max_cache_size = 0;
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 \ #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
|| CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 struct cpuinfo_uarch_info* cpuinfo_uarchs = NULL;
struct cpuinfo_uarch_info* cpuinfo_uarchs = NULL; uint32_t cpuinfo_uarchs_count = 0;
uint32_t cpuinfo_uarchs_count = 0;
#else #else
struct cpuinfo_uarch_info cpuinfo_global_uarch = { cpuinfo_uarch_unknown }; struct cpuinfo_uarch_info cpuinfo_global_uarch = {cpuinfo_uarch_unknown};
#endif #endif
#ifdef __linux__ #ifdef __linux__
uint32_t cpuinfo_linux_cpu_max = 0; uint32_t cpuinfo_linux_cpu_max = 0;
const struct cpuinfo_processor** cpuinfo_linux_cpu_to_processor_map = NULL; const struct cpuinfo_processor** cpuinfo_linux_cpu_to_processor_map = NULL;
const struct cpuinfo_core** cpuinfo_linux_cpu_to_core_map = NULL; const struct cpuinfo_core** cpuinfo_linux_cpu_to_core_map = NULL;
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 \ #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
|| CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map = NULL;
const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map = NULL; #endif
#endif
#endif #endif
const struct cpuinfo_processor* cpuinfo_get_processors(void) { const struct cpuinfo_processor* cpuinfo_get_processors(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processors"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processors");
} }
return cpuinfo_processors; return cpuinfo_processors;
} }
const struct cpuinfo_core* cpuinfo_get_cores(void) { const struct cpuinfo_core* cpuinfo_get_cores(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "core"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "core");
} }
return cpuinfo_cores; return cpuinfo_cores;
} }
const struct cpuinfo_cluster* cpuinfo_get_clusters(void) { const struct cpuinfo_cluster* cpuinfo_get_clusters(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "clusters"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "clusters");
} }
return cpuinfo_clusters; return cpuinfo_clusters;
} }
const struct cpuinfo_package* cpuinfo_get_packages(void) { const struct cpuinfo_package* cpuinfo_get_packages(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "packages"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "packages");
} }
return cpuinfo_packages; return cpuinfo_packages;
@ -81,49 +78,48 @@ const struct cpuinfo_uarch_info* cpuinfo_get_uarchs() {
if (!cpuinfo_is_initialized) { if (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarchs"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarchs");
} }
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 \ #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
|| CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 return cpuinfo_uarchs;
return cpuinfo_uarchs; #else
#else return &cpuinfo_global_uarch;
return &cpuinfo_global_uarch; #endif
#endif
} }
const struct cpuinfo_processor* cpuinfo_get_processor(uint32_t index) { const struct cpuinfo_processor* cpuinfo_get_processor(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processor"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processor");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_processors_count) { if CPUINFO_UNLIKELY (index >= cpuinfo_processors_count) {
return NULL; return NULL;
} }
return &cpuinfo_processors[index]; return &cpuinfo_processors[index];
} }
const struct cpuinfo_core* cpuinfo_get_core(uint32_t index) { const struct cpuinfo_core* cpuinfo_get_core(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "core"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "core");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_cores_count) { if CPUINFO_UNLIKELY (index >= cpuinfo_cores_count) {
return NULL; return NULL;
} }
return &cpuinfo_cores[index]; return &cpuinfo_cores[index];
} }
const struct cpuinfo_cluster* cpuinfo_get_cluster(uint32_t index) { const struct cpuinfo_cluster* cpuinfo_get_cluster(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "cluster"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "cluster");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_clusters_count) { if CPUINFO_UNLIKELY (index >= cpuinfo_clusters_count) {
return NULL; return NULL;
} }
return &cpuinfo_clusters[index]; return &cpuinfo_clusters[index];
} }
const struct cpuinfo_package* cpuinfo_get_package(uint32_t index) { const struct cpuinfo_package* cpuinfo_get_package(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "package"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "package");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_packages_count) { if CPUINFO_UNLIKELY (index >= cpuinfo_packages_count) {
return NULL; return NULL;
} }
return &cpuinfo_packages[index]; return &cpuinfo_packages[index];
@ -133,43 +129,42 @@ const struct cpuinfo_uarch_info* cpuinfo_get_uarch(uint32_t index) {
if (!cpuinfo_is_initialized) { if (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarch"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarch");
} }
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 \ #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
|| CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 if CPUINFO_UNLIKELY (index >= cpuinfo_uarchs_count) {
if CPUINFO_UNLIKELY(index >= cpuinfo_uarchs_count) { return NULL;
return NULL; }
} return &cpuinfo_uarchs[index];
return &cpuinfo_uarchs[index]; #else
#else if CPUINFO_UNLIKELY (index != 0) {
if CPUINFO_UNLIKELY(index != 0) { return NULL;
return NULL; }
} return &cpuinfo_global_uarch;
return &cpuinfo_global_uarch; #endif
#endif
} }
uint32_t cpuinfo_get_processors_count(void) { uint32_t cpuinfo_get_processors_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processors_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processors_count");
} }
return cpuinfo_processors_count; return cpuinfo_processors_count;
} }
uint32_t cpuinfo_get_cores_count(void) { uint32_t cpuinfo_get_cores_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "cores_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "cores_count");
} }
return cpuinfo_cores_count; return cpuinfo_cores_count;
} }
uint32_t cpuinfo_get_clusters_count(void) { uint32_t cpuinfo_get_clusters_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "clusters_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "clusters_count");
} }
return cpuinfo_clusters_count; return cpuinfo_clusters_count;
} }
uint32_t cpuinfo_get_packages_count(void) { uint32_t cpuinfo_get_packages_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "packages_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "packages_count");
} }
return cpuinfo_packages_count; return cpuinfo_packages_count;
@ -179,239 +174,243 @@ uint32_t cpuinfo_get_uarchs_count(void) {
if (!cpuinfo_is_initialized) { if (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarchs_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarchs_count");
} }
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 \ #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
|| CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 return cpuinfo_uarchs_count;
return cpuinfo_uarchs_count; #else
#else return 1;
return 1; #endif
#endif
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1i_caches(void) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1i_caches(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_caches"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_caches");
} }
return cpuinfo_cache[cpuinfo_cache_level_1i]; return cpuinfo_cache[cpuinfo_cache_level_1i];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1d_caches(void) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1d_caches(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_caches"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_caches");
} }
return cpuinfo_cache[cpuinfo_cache_level_1d]; return cpuinfo_cache[cpuinfo_cache_level_1d];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l2_caches(void) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l2_caches(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_caches"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_caches");
} }
return cpuinfo_cache[cpuinfo_cache_level_2]; return cpuinfo_cache[cpuinfo_cache_level_2];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l3_caches(void) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l3_caches(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_caches"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_caches");
} }
return cpuinfo_cache[cpuinfo_cache_level_3]; return cpuinfo_cache[cpuinfo_cache_level_3];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l4_caches(void) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l4_caches(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_caches"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_caches");
} }
return cpuinfo_cache[cpuinfo_cache_level_4]; return cpuinfo_cache[cpuinfo_cache_level_4];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1i_cache(uint32_t index) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1i_cache(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_cache"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_cache");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_cache_count[cpuinfo_cache_level_1i]) { if CPUINFO_UNLIKELY (index >= cpuinfo_cache_count[cpuinfo_cache_level_1i]) {
return NULL; return NULL;
} }
return &cpuinfo_cache[cpuinfo_cache_level_1i][index]; return &cpuinfo_cache[cpuinfo_cache_level_1i][index];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1d_cache(uint32_t index) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1d_cache(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_cache"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_cache");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_cache_count[cpuinfo_cache_level_1d]) { if CPUINFO_UNLIKELY (index >= cpuinfo_cache_count[cpuinfo_cache_level_1d]) {
return NULL; return NULL;
} }
return &cpuinfo_cache[cpuinfo_cache_level_1d][index]; return &cpuinfo_cache[cpuinfo_cache_level_1d][index];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l2_cache(uint32_t index) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l2_cache(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_cache"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_cache");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_cache_count[cpuinfo_cache_level_2]) { if CPUINFO_UNLIKELY (index >= cpuinfo_cache_count[cpuinfo_cache_level_2]) {
return NULL; return NULL;
} }
return &cpuinfo_cache[cpuinfo_cache_level_2][index]; return &cpuinfo_cache[cpuinfo_cache_level_2][index];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l3_cache(uint32_t index) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l3_cache(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_cache"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_cache");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_cache_count[cpuinfo_cache_level_3]) { if CPUINFO_UNLIKELY (index >= cpuinfo_cache_count[cpuinfo_cache_level_3]) {
return NULL; return NULL;
} }
return &cpuinfo_cache[cpuinfo_cache_level_3][index]; return &cpuinfo_cache[cpuinfo_cache_level_3][index];
} }
const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l4_cache(uint32_t index) { const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l4_cache(uint32_t index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_cache"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_cache");
} }
if CPUINFO_UNLIKELY(index >= cpuinfo_cache_count[cpuinfo_cache_level_4]) { if CPUINFO_UNLIKELY (index >= cpuinfo_cache_count[cpuinfo_cache_level_4]) {
return NULL; return NULL;
} }
return &cpuinfo_cache[cpuinfo_cache_level_4][index]; return &cpuinfo_cache[cpuinfo_cache_level_4][index];
} }
uint32_t CPUINFO_ABI cpuinfo_get_l1i_caches_count(void) { uint32_t CPUINFO_ABI cpuinfo_get_l1i_caches_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_caches_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_caches_count");
} }
return cpuinfo_cache_count[cpuinfo_cache_level_1i]; return cpuinfo_cache_count[cpuinfo_cache_level_1i];
} }
uint32_t CPUINFO_ABI cpuinfo_get_l1d_caches_count(void) { uint32_t CPUINFO_ABI cpuinfo_get_l1d_caches_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_caches_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_caches_count");
} }
return cpuinfo_cache_count[cpuinfo_cache_level_1d]; return cpuinfo_cache_count[cpuinfo_cache_level_1d];
} }
uint32_t CPUINFO_ABI cpuinfo_get_l2_caches_count(void) { uint32_t CPUINFO_ABI cpuinfo_get_l2_caches_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_caches_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_caches_count");
} }
return cpuinfo_cache_count[cpuinfo_cache_level_2]; return cpuinfo_cache_count[cpuinfo_cache_level_2];
} }
uint32_t CPUINFO_ABI cpuinfo_get_l3_caches_count(void) { uint32_t CPUINFO_ABI cpuinfo_get_l3_caches_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_caches_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_caches_count");
} }
return cpuinfo_cache_count[cpuinfo_cache_level_3]; return cpuinfo_cache_count[cpuinfo_cache_level_3];
} }
uint32_t CPUINFO_ABI cpuinfo_get_l4_caches_count(void) { uint32_t CPUINFO_ABI cpuinfo_get_l4_caches_count(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_caches_count"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_caches_count");
} }
return cpuinfo_cache_count[cpuinfo_cache_level_4]; return cpuinfo_cache_count[cpuinfo_cache_level_4];
} }
uint32_t CPUINFO_ABI cpuinfo_get_max_cache_size(void) { uint32_t CPUINFO_ABI cpuinfo_get_max_cache_size(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "max_cache_size"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "max_cache_size");
} }
return cpuinfo_max_cache_size; return cpuinfo_max_cache_size;
} }
const struct cpuinfo_processor* CPUINFO_ABI cpuinfo_get_current_processor(void) { const struct cpuinfo_processor* CPUINFO_ABI cpuinfo_get_current_processor(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_processor"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_processor");
} }
#ifdef __linux__ #ifdef __linux__
/* Initializing this variable silences a MemorySanitizer error. */ /* Initializing this variable silences a MemorySanitizer error. */
unsigned cpu = 0; unsigned cpu = 0;
if CPUINFO_UNLIKELY(syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) { if CPUINFO_UNLIKELY (syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
return 0; return 0;
} }
if CPUINFO_UNLIKELY((uint32_t) cpu >= cpuinfo_linux_cpu_max) { if CPUINFO_UNLIKELY ((uint32_t)cpu >= cpuinfo_linux_cpu_max) {
return 0; return 0;
} }
return cpuinfo_linux_cpu_to_processor_map[cpu]; return cpuinfo_linux_cpu_to_processor_map[cpu];
#else #else
return NULL; return NULL;
#endif #endif
} }
const struct cpuinfo_core* CPUINFO_ABI cpuinfo_get_current_core(void) { const struct cpuinfo_core* CPUINFO_ABI cpuinfo_get_current_core(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_core"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_core");
} }
#ifdef __linux__ #ifdef __linux__
/* Initializing this variable silences a MemorySanitizer error. */ /* Initializing this variable silences a MemorySanitizer error. */
unsigned cpu = 0; unsigned cpu = 0;
if CPUINFO_UNLIKELY(syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) { if CPUINFO_UNLIKELY (syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
return 0; return 0;
} }
if CPUINFO_UNLIKELY((uint32_t) cpu >= cpuinfo_linux_cpu_max) { if CPUINFO_UNLIKELY ((uint32_t)cpu >= cpuinfo_linux_cpu_max) {
return 0; return 0;
} }
return cpuinfo_linux_cpu_to_core_map[cpu]; return cpuinfo_linux_cpu_to_core_map[cpu];
#else #else
return NULL; return NULL;
#endif #endif
} }
uint32_t CPUINFO_ABI cpuinfo_get_current_uarch_index(void) { uint32_t CPUINFO_ABI cpuinfo_get_current_uarch_index(void) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_uarch_index"); cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_uarch_index");
} }
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 \ #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
|| CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 #ifdef __linux__
#ifdef __linux__ if (cpuinfo_linux_cpu_to_uarch_index_map == NULL) {
if (cpuinfo_linux_cpu_to_uarch_index_map == NULL) { /* Special case: avoid syscall on systems with only a single
/* Special case: avoid syscall on systems with only a single type of cores */ * type of cores
return 0; */
}
/* General case */
/* Initializing this variable silences a MemorySanitizer error. */
unsigned cpu = 0;
if CPUINFO_UNLIKELY(syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
return 0;
}
if CPUINFO_UNLIKELY((uint32_t) cpu >= cpuinfo_linux_cpu_max) {
return 0;
}
return cpuinfo_linux_cpu_to_uarch_index_map[cpu];
#else
/* Fallback: pretend to be on the big core. */
return 0;
#endif
#else
/* Only ARM/ARM64/RISCV processors may include cores of different types in the same package. */
return 0; return 0;
#endif }
/* General case */
/* Initializing this variable silences a MemorySanitizer error. */
unsigned cpu = 0;
if CPUINFO_UNLIKELY (syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
return 0;
}
if CPUINFO_UNLIKELY ((uint32_t)cpu >= cpuinfo_linux_cpu_max) {
return 0;
}
return cpuinfo_linux_cpu_to_uarch_index_map[cpu];
#else
/* Fallback: pretend to be on the big core. */
return 0;
#endif
#else
/* Only ARM/ARM64/RISCV processors may include cores of different types
* in the same package. */
return 0;
#endif
} }
uint32_t CPUINFO_ABI cpuinfo_get_current_uarch_index_with_default(uint32_t default_uarch_index) { uint32_t CPUINFO_ABI cpuinfo_get_current_uarch_index_with_default(uint32_t default_uarch_index) {
if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) {
cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_uarch_index_with_default"); cpuinfo_log_fatal(
"cpuinfo_get_%s called before cpuinfo is initialized", "current_uarch_index_with_default");
} }
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 \ #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
|| CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 #ifdef __linux__
#ifdef __linux__ if (cpuinfo_linux_cpu_to_uarch_index_map == NULL) {
if (cpuinfo_linux_cpu_to_uarch_index_map == NULL) { /* Special case: avoid syscall on systems with only a single
/* Special case: avoid syscall on systems with only a single type of cores */ * type of cores
return 0; */
}
/* General case */
/* Initializing this variable silences a MemorySanitizer error. */
unsigned cpu = 0;
if CPUINFO_UNLIKELY(syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
return default_uarch_index;
}
if CPUINFO_UNLIKELY((uint32_t) cpu >= cpuinfo_linux_cpu_max) {
return default_uarch_index;
}
return cpuinfo_linux_cpu_to_uarch_index_map[cpu];
#else
/* Fallback: no API to query current core, use default uarch index. */
return default_uarch_index;
#endif
#else
/* Only ARM/ARM64/RISCV processors may include cores of different types in the same package. */
return 0; return 0;
#endif }
/* General case */
/* Initializing this variable silences a MemorySanitizer error. */
unsigned cpu = 0;
if CPUINFO_UNLIKELY (syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
return default_uarch_index;
}
if CPUINFO_UNLIKELY ((uint32_t)cpu >= cpuinfo_linux_cpu_max) {
return default_uarch_index;
}
return cpuinfo_linux_cpu_to_uarch_index_map[cpu];
#else
/* Fallback: no API to query current core, use default uarch index. */
return default_uarch_index;
#endif
#else
/* Only ARM/ARM64/RISCV processors may include cores of different types
* in the same package. */
return 0;
#endif
} }

View File

@ -1,9 +1,9 @@
#pragma once #pragma once
#include <cpuinfo.h>
#include <cpuinfo/common.h>
#include <arm/api.h> #include <arm/api.h>
#include <arm/linux/api.h> #include <arm/linux/api.h>
#include <cpuinfo.h>
#include <cpuinfo/common.h>
enum cpuinfo_android_chipset_property { enum cpuinfo_android_chipset_property {
cpuinfo_android_chipset_property_proc_cpuinfo_hardware = 0, cpuinfo_android_chipset_property_proc_cpuinfo_hardware = 0,

View File

@ -1,42 +1,42 @@
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <stddef.h>
#include <string.h> #include <string.h>
#include <sys/system_properties.h> #include <sys/system_properties.h>
#include <linux/api.h>
#include <arm/android/api.h> #include <arm/android/api.h>
#include <arm/linux/api.h> #include <arm/linux/api.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <linux/api.h>
#if CPUINFO_MOCK #if CPUINFO_MOCK
#include <cpuinfo-mock.h> #include <cpuinfo-mock.h>
static struct cpuinfo_mock_property* cpuinfo_mock_properties = NULL; static struct cpuinfo_mock_property* cpuinfo_mock_properties = NULL;
void CPUINFO_ABI cpuinfo_mock_android_properties(struct cpuinfo_mock_property* properties) { void CPUINFO_ABI cpuinfo_mock_android_properties(struct cpuinfo_mock_property* properties) {
cpuinfo_log_info("Android properties mocking enabled"); cpuinfo_log_info("Android properties mocking enabled");
cpuinfo_mock_properties = properties; cpuinfo_mock_properties = properties;
} }
static int cpuinfo_android_property_get(const char* key, char* value) { static int cpuinfo_android_property_get(const char* key, char* value) {
if (cpuinfo_mock_properties != NULL) { if (cpuinfo_mock_properties != NULL) {
for (const struct cpuinfo_mock_property* prop = cpuinfo_mock_properties; prop->key != NULL; prop++) { for (const struct cpuinfo_mock_property* prop = cpuinfo_mock_properties; prop->key != NULL; prop++) {
if (strncmp(key, prop->key, CPUINFO_BUILD_PROP_NAME_MAX) == 0) { if (strncmp(key, prop->key, CPUINFO_BUILD_PROP_NAME_MAX) == 0) {
strncpy(value, prop->value, CPUINFO_BUILD_PROP_VALUE_MAX); strncpy(value, prop->value, CPUINFO_BUILD_PROP_VALUE_MAX);
return (int) strnlen(prop->value, CPUINFO_BUILD_PROP_VALUE_MAX); return (int)strnlen(prop->value, CPUINFO_BUILD_PROP_VALUE_MAX);
}
} }
} }
*value = '\0';
return 0;
} }
*value = '\0';
return 0;
}
#else #else
static inline int cpuinfo_android_property_get(const char* key, char* value) { static inline int cpuinfo_android_property_get(const char* key, char* value) {
return __system_property_get(key, value); return __system_property_get(key, value);
} }
#endif #endif
void cpuinfo_arm_android_parse_properties(struct cpuinfo_android_properties properties[restrict static 1]) { void cpuinfo_arm_android_parse_properties(struct cpuinfo_android_properties properties[restrict static 1]) {
@ -50,18 +50,17 @@ void cpuinfo_arm_android_parse_properties(struct cpuinfo_android_properties prop
const int ro_mediatek_platform_length = const int ro_mediatek_platform_length =
cpuinfo_android_property_get("ro.mediatek.platform", properties->ro_mediatek_platform); cpuinfo_android_property_get("ro.mediatek.platform", properties->ro_mediatek_platform);
cpuinfo_log_debug("read ro.mediatek.platform = \"%.*s\"", cpuinfo_log_debug(
ro_mediatek_platform_length, properties->ro_mediatek_platform); "read ro.mediatek.platform = \"%.*s\"", ro_mediatek_platform_length, properties->ro_mediatek_platform);
const int ro_arch_length = const int ro_arch_length = cpuinfo_android_property_get("ro.arch", properties->ro_arch);
cpuinfo_android_property_get("ro.arch", properties->ro_arch);
cpuinfo_log_debug("read ro.arch = \"%.*s\"", ro_arch_length, properties->ro_arch); cpuinfo_log_debug("read ro.arch = \"%.*s\"", ro_arch_length, properties->ro_arch);
const int ro_chipname_length = const int ro_chipname_length = cpuinfo_android_property_get("ro.chipname", properties->ro_chipname);
cpuinfo_android_property_get("ro.chipname", properties->ro_chipname);
cpuinfo_log_debug("read ro.chipname = \"%.*s\"", ro_chipname_length, properties->ro_chipname); cpuinfo_log_debug("read ro.chipname = \"%.*s\"", ro_chipname_length, properties->ro_chipname);
const int ro_hardware_chipname_length = const int ro_hardware_chipname_length =
cpuinfo_android_property_get("ro.hardware.chipname", properties->ro_hardware_chipname); cpuinfo_android_property_get("ro.hardware.chipname", properties->ro_hardware_chipname);
cpuinfo_log_debug("read ro.hardware.chipname = \"%.*s\"", ro_hardware_chipname_length, properties->ro_hardware_chipname); cpuinfo_log_debug(
"read ro.hardware.chipname = \"%.*s\"", ro_hardware_chipname_length, properties->ro_hardware_chipname);
} }

View File

@ -80,45 +80,47 @@ struct cpuinfo_arm_chipset {
#define CPUINFO_ARM_CHIPSET_NAME_MAX CPUINFO_PACKAGE_NAME_MAX #define CPUINFO_ARM_CHIPSET_NAME_MAX CPUINFO_PACKAGE_NAME_MAX
#ifndef __cplusplus #ifndef __cplusplus
CPUINFO_INTERNAL void cpuinfo_arm_chipset_to_string( CPUINFO_INTERNAL void cpuinfo_arm_chipset_to_string(
const struct cpuinfo_arm_chipset chipset[restrict static 1], const struct cpuinfo_arm_chipset chipset[restrict static 1],
char name[restrict static CPUINFO_ARM_CHIPSET_NAME_MAX]); char name[restrict static CPUINFO_ARM_CHIPSET_NAME_MAX]);
CPUINFO_INTERNAL void cpuinfo_arm_fixup_chipset( CPUINFO_INTERNAL void cpuinfo_arm_fixup_chipset(
struct cpuinfo_arm_chipset chipset[restrict static 1], uint32_t cores, uint32_t max_cpu_freq_max); struct cpuinfo_arm_chipset chipset[restrict static 1],
uint32_t cores,
uint32_t max_cpu_freq_max);
CPUINFO_INTERNAL void cpuinfo_arm_decode_vendor_uarch( CPUINFO_INTERNAL void cpuinfo_arm_decode_vendor_uarch(
uint32_t midr, uint32_t midr,
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
bool has_vfpv4, bool has_vfpv4,
#endif #endif
enum cpuinfo_vendor vendor[restrict static 1], enum cpuinfo_vendor vendor[restrict static 1],
enum cpuinfo_uarch uarch[restrict static 1]); enum cpuinfo_uarch uarch[restrict static 1]);
CPUINFO_INTERNAL void cpuinfo_arm_decode_cache( CPUINFO_INTERNAL void cpuinfo_arm_decode_cache(
enum cpuinfo_uarch uarch, enum cpuinfo_uarch uarch,
uint32_t cluster_cores, uint32_t cluster_cores,
uint32_t midr, uint32_t midr,
const struct cpuinfo_arm_chipset chipset[restrict static 1], const struct cpuinfo_arm_chipset chipset[restrict static 1],
uint32_t cluster_id, uint32_t cluster_id,
uint32_t arch_version, uint32_t arch_version,
struct cpuinfo_cache l1i[restrict static 1], struct cpuinfo_cache l1i[restrict static 1],
struct cpuinfo_cache l1d[restrict static 1], struct cpuinfo_cache l1d[restrict static 1],
struct cpuinfo_cache l2[restrict static 1], struct cpuinfo_cache l2[restrict static 1],
struct cpuinfo_cache l3[restrict static 1]); struct cpuinfo_cache l3[restrict static 1]);
CPUINFO_INTERNAL uint32_t cpuinfo_arm_compute_max_cache_size( CPUINFO_INTERNAL uint32_t
const struct cpuinfo_processor processor[restrict static 1]); cpuinfo_arm_compute_max_cache_size(const struct cpuinfo_processor processor[restrict static 1]);
#else /* defined(__cplusplus) */ #else /* defined(__cplusplus) */
CPUINFO_INTERNAL void cpuinfo_arm_decode_cache( CPUINFO_INTERNAL void cpuinfo_arm_decode_cache(
enum cpuinfo_uarch uarch, enum cpuinfo_uarch uarch,
uint32_t cluster_cores, uint32_t cluster_cores,
uint32_t midr, uint32_t midr,
const struct cpuinfo_arm_chipset chipset[1], const struct cpuinfo_arm_chipset chipset[1],
uint32_t cluster_id, uint32_t cluster_id,
uint32_t arch_version, uint32_t arch_version,
struct cpuinfo_cache l1i[1], struct cpuinfo_cache l1i[1],
struct cpuinfo_cache l1d[1], struct cpuinfo_cache l1d[1],
struct cpuinfo_cache l2[1], struct cpuinfo_cache l2[1],
struct cpuinfo_cache l3[1]); struct cpuinfo_cache l3[1]);
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +1,27 @@
#include <stdint.h> #include <stdint.h>
#if CPUINFO_MOCK #if CPUINFO_MOCK
#include <cpuinfo-mock.h> #include <cpuinfo-mock.h>
#endif #endif
#include <arm/linux/api.h> #include <arm/linux/api.h>
#include <arm/linux/cp.h> #include <arm/linux/cp.h>
#include <arm/midr.h> #include <arm/midr.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#if CPUINFO_MOCK #if CPUINFO_MOCK
uint32_t cpuinfo_arm_fpsid = 0; uint32_t cpuinfo_arm_fpsid = 0;
uint32_t cpuinfo_arm_mvfr0 = 0; uint32_t cpuinfo_arm_mvfr0 = 0;
uint32_t cpuinfo_arm_wcid = 0; uint32_t cpuinfo_arm_wcid = 0;
void cpuinfo_set_fpsid(uint32_t fpsid) { void cpuinfo_set_fpsid(uint32_t fpsid) {
cpuinfo_arm_fpsid = fpsid; cpuinfo_arm_fpsid = fpsid;
} }
void cpuinfo_set_wcid(uint32_t wcid) { void cpuinfo_set_wcid(uint32_t wcid) {
cpuinfo_arm_wcid = wcid; cpuinfo_arm_wcid = wcid;
} }
#endif #endif
void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo( void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
uint32_t features, uint32_t features,
uint32_t features2, uint32_t features2,
@ -31,27 +29,27 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
uint32_t architecture_version, uint32_t architecture_version,
uint32_t architecture_flags, uint32_t architecture_flags,
const struct cpuinfo_arm_chipset chipset[restrict static 1], const struct cpuinfo_arm_chipset chipset[restrict static 1],
struct cpuinfo_arm_isa isa[restrict static 1]) struct cpuinfo_arm_isa isa[restrict static 1]) {
{
if (architecture_version < 8) { if (architecture_version < 8) {
const uint32_t armv8_features2_mask = CPUINFO_ARM_LINUX_FEATURE2_AES | CPUINFO_ARM_LINUX_FEATURE2_PMULL | const uint32_t armv8_features2_mask = CPUINFO_ARM_LINUX_FEATURE2_AES |
CPUINFO_ARM_LINUX_FEATURE2_SHA1 | CPUINFO_ARM_LINUX_FEATURE2_SHA2 | CPUINFO_ARM_LINUX_FEATURE2_CRC32; CPUINFO_ARM_LINUX_FEATURE2_PMULL | CPUINFO_ARM_LINUX_FEATURE2_SHA1 |
CPUINFO_ARM_LINUX_FEATURE2_SHA2 | CPUINFO_ARM_LINUX_FEATURE2_CRC32;
if (features2 & armv8_features2_mask) { if (features2 & armv8_features2_mask) {
architecture_version = 8; architecture_version = 8;
} }
} }
if (architecture_version >= 8) { if (architecture_version >= 8) {
/* /*
* ARMv7 code running on ARMv8: IDIV, VFP, NEON are always supported, * ARMv7 code running on ARMv8: IDIV, VFP, NEON are always
* but may be not reported in /proc/cpuinfo features. * supported, but may be not reported in /proc/cpuinfo features.
*/ */
isa->armv5e = true; isa->armv5e = true;
isa->armv6 = true; isa->armv6 = true;
isa->armv6k = true; isa->armv6k = true;
isa->armv7 = true; isa->armv7 = true;
isa->armv7mp = true; isa->armv7mp = true;
isa->armv8 = true; isa->armv8 = true;
isa->thumb = true; isa->thumb = true;
isa->thumb2 = true; isa->thumb2 = true;
isa->idiv = true; isa->idiv = true;
isa->vfpv3 = true; isa->vfpv3 = true;
@ -61,8 +59,10 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
isa->neon = true; isa->neon = true;
/* /*
* NEON FP16 compute extension and VQRDMLAH/VQRDMLSH instructions are not indicated in /proc/cpuinfo. * NEON FP16 compute extension and VQRDMLAH/VQRDMLSH
* Use a MIDR-based heuristic to whitelist processors known to support it: * instructions are not indicated in /proc/cpuinfo. Use a
* MIDR-based heuristic to whitelist processors known to support
* it:
* - Processors with Cortex-A55 cores * - Processors with Cortex-A55 cores
* - Processors with Cortex-A75 cores * - Processors with Cortex-A75 cores
* - Processors with Cortex-A76 cores * - Processors with Cortex-A76 cores
@ -82,8 +82,10 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
* - Neoverse V2 cores * - Neoverse V2 cores
*/ */
if (chipset->series == cpuinfo_arm_chipset_series_samsung_exynos && chipset->model == 9810) { if (chipset->series == cpuinfo_arm_chipset_series_samsung_exynos && chipset->model == 9810) {
/* Only little cores of Exynos 9810 support FP16 & RDM */ /* Only little cores of Exynos 9810 support FP16 & RDM
cpuinfo_log_warning("FP16 arithmetics and RDM disabled: only little cores in Exynos 9810 support these extensions"); */
cpuinfo_log_warning(
"FP16 arithmetics and RDM disabled: only little cores in Exynos 9810 support these extensions");
} else { } else {
switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) { switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
case UINT32_C(0x4100D050): /* Cortex-A55 */ case UINT32_C(0x4100D050): /* Cortex-A55 */
@ -102,11 +104,16 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
case UINT32_C(0x4100D4D0): /* Cortex-A715 */ case UINT32_C(0x4100D4D0): /* Cortex-A715 */
case UINT32_C(0x4100D4E0): /* Cortex-X3 */ case UINT32_C(0x4100D4E0): /* Cortex-X3 */
case UINT32_C(0x4100D4F0): /* Neoverse V2 */ case UINT32_C(0x4100D4F0): /* Neoverse V2 */
case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */ case UINT32_C(0x4800D400): /* Cortex-A76
case UINT32_C(0x51008020): /* Kryo 385 Gold (Cortex-A75) */ (HiSilicon) */
case UINT32_C(0x51008030): /* Kryo 385 Silver (Cortex-A55) */ case UINT32_C(0x51008020): /* Kryo 385 Gold
case UINT32_C(0x51008040): /* Kryo 485 Gold (Cortex-A76) */ (Cortex-A75) */
case UINT32_C(0x51008050): /* Kryo 485 Silver (Cortex-A55) */ case UINT32_C(0x51008030): /* Kryo 385 Silver
(Cortex-A55) */
case UINT32_C(0x51008040): /* Kryo 485 Gold
(Cortex-A76) */
case UINT32_C(0x51008050): /* Kryo 485 Silver
(Cortex-A55) */
case UINT32_C(0x53000030): /* Exynos M4 */ case UINT32_C(0x53000030): /* Exynos M4 */
case UINT32_C(0x53000040): /* Exynos M5 */ case UINT32_C(0x53000040): /* Exynos M5 */
isa->fp16arith = true; isa->fp16arith = true;
@ -117,7 +124,8 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
/* /*
* NEON VDOT instructions are not indicated in /proc/cpuinfo. * NEON VDOT instructions are not indicated in /proc/cpuinfo.
* Use a MIDR-based heuristic to whitelist processors known to support it: * Use a MIDR-based heuristic to whitelist processors known to
* support it:
* - Processors with Cortex-A76 cores * - Processors with Cortex-A76 cores
* - Processors with Cortex-A77 cores * - Processors with Cortex-A77 cores
* - Processors with Cortex-A78 cores * - Processors with Cortex-A78 cores
@ -135,7 +143,8 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
* - Neoverse V2 cores * - Neoverse V2 cores
*/ */
if (chipset->series == cpuinfo_arm_chipset_series_spreadtrum_sc && chipset->model == 9863) { if (chipset->series == cpuinfo_arm_chipset_series_spreadtrum_sc && chipset->model == 9863) {
cpuinfo_log_warning("VDOT instructions disabled: cause occasional SIGILL on Spreadtrum SC9863A"); cpuinfo_log_warning(
"VDOT instructions disabled: cause occasional SIGILL on Spreadtrum SC9863A");
} else if (chipset->series == cpuinfo_arm_chipset_series_unisoc_t && chipset->model == 310) { } else if (chipset->series == cpuinfo_arm_chipset_series_unisoc_t && chipset->model == 310) {
cpuinfo_log_warning("VDOT instructions disabled: cause occasional SIGILL on Unisoc T310"); cpuinfo_log_warning("VDOT instructions disabled: cause occasional SIGILL on Unisoc T310");
} else { } else {
@ -154,41 +163,52 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
case UINT32_C(0x4100D4D0): /* Cortex-A715 */ case UINT32_C(0x4100D4D0): /* Cortex-A715 */
case UINT32_C(0x4100D4E0): /* Cortex-X3 */ case UINT32_C(0x4100D4E0): /* Cortex-X3 */
case UINT32_C(0x4100D4F0): /* Neoverse V2 */ case UINT32_C(0x4100D4F0): /* Neoverse V2 */
case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */ case UINT32_C(0x4800D400): /* Cortex-A76
case UINT32_C(0x51008040): /* Kryo 485 Gold (Cortex-A76) */ (HiSilicon) */
case UINT32_C(0x51008050): /* Kryo 485 Silver (Cortex-A55) */ case UINT32_C(0x51008040): /* Kryo 485 Gold
(Cortex-A76) */
case UINT32_C(0x51008050): /* Kryo 485 Silver
(Cortex-A55) */
case UINT32_C(0x53000030): /* Exynos M4 */ case UINT32_C(0x53000030): /* Exynos M4 */
case UINT32_C(0x53000040): /* Exynos M5 */ case UINT32_C(0x53000040): /* Exynos M5 */
isa->dot = true; isa->dot = true;
break; break;
case UINT32_C(0x4100D050): /* Cortex A55: revision 1 or later only */ case UINT32_C(0x4100D050): /* Cortex A55: revision 1
or later only */
isa->dot = !!(midr_get_variant(midr) >= 1); isa->dot = !!(midr_get_variant(midr) >= 1);
break; break;
case UINT32_C(0x4100D0A0): /* Cortex A75: revision 2 or later only */ case UINT32_C(0x4100D0A0): /* Cortex A75: revision 2
or later only */
isa->dot = !!(midr_get_variant(midr) >= 2); isa->dot = !!(midr_get_variant(midr) >= 2);
break; break;
} }
} }
} else { } else {
/* ARMv7 or lower: use feature flags to detect optional features */ /* ARMv7 or lower: use feature flags to detect optional features
*/
/* /*
* ARM11 (ARM 1136/1156/1176/11 MPCore) processors can report v7 architecture * ARM11 (ARM 1136/1156/1176/11 MPCore) processors can report v7
* even though they support only ARMv6 instruction set. * architecture even though they support only ARMv6 instruction
* set.
*/ */
if (architecture_version == 7 && midr_is_arm11(midr)) { if (architecture_version == 7 && midr_is_arm11(midr)) {
cpuinfo_log_warning("kernel-reported architecture ARMv7 ignored due to mismatch with processor microarchitecture (ARM11)"); cpuinfo_log_warning(
"kernel-reported architecture ARMv7 ignored due to mismatch with processor microarchitecture (ARM11)");
architecture_version = 6; architecture_version = 6;
} }
if (architecture_version < 7) { if (architecture_version < 7) {
const uint32_t armv7_features_mask = CPUINFO_ARM_LINUX_FEATURE_VFPV3 | CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | CPUINFO_ARM_LINUX_FEATURE_VFPD32 | const uint32_t armv7_features_mask = CPUINFO_ARM_LINUX_FEATURE_VFPV3 |
CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON | CPUINFO_ARM_LINUX_FEATURE_IDIVT | CPUINFO_ARM_LINUX_FEATURE_IDIVA; CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | CPUINFO_ARM_LINUX_FEATURE_VFPD32 |
CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON |
CPUINFO_ARM_LINUX_FEATURE_IDIVT | CPUINFO_ARM_LINUX_FEATURE_IDIVA;
if (features & armv7_features_mask) { if (features & armv7_features_mask) {
architecture_version = 7; architecture_version = 7;
} }
} }
if ((architecture_version >= 6) || (features & CPUINFO_ARM_LINUX_FEATURE_EDSP) || (architecture_flags & CPUINFO_ARM_LINUX_ARCH_E)) { if ((architecture_version >= 6) || (features & CPUINFO_ARM_LINUX_FEATURE_EDSP) ||
(architecture_flags & CPUINFO_ARM_LINUX_ARCH_E)) {
isa->armv5e = true; isa->armv5e = true;
} }
if (architecture_version >= 6) { if (architecture_version >= 6) {
@ -199,13 +219,16 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
isa->armv7 = true; isa->armv7 = true;
/* /*
* ARMv7 MP extension (PLDW instruction) is not indicated in /proc/cpuinfo. * ARMv7 MP extension (PLDW instruction) is not
* Use heuristic list of supporting processors: * indicated in /proc/cpuinfo. Use heuristic list of
* - Processors supporting UDIV/SDIV instructions ("idiva" + "idivt" features in /proc/cpuinfo) * supporting processors:
* - Processors supporting UDIV/SDIV instructions
* ("idiva" + "idivt" features in /proc/cpuinfo)
* - Cortex-A5 * - Cortex-A5
* - Cortex-A9 * - Cortex-A9
* - Dual-Core Scorpion * - Dual-Core Scorpion
* - Krait (supports UDIV/SDIV, but kernels may not report it in /proc/cpuinfo) * - Krait (supports UDIV/SDIV, but kernels may not
* report it in /proc/cpuinfo)
* *
* TODO: check single-core Qualcomm Scorpion. * TODO: check single-core Qualcomm Scorpion.
*/ */
@ -218,31 +241,35 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
isa->armv7mp = true; isa->armv7mp = true;
break; break;
default: default:
/* In practice IDIV instruction implies ARMv7+MP ISA */ /* In practice IDIV instruction implies
isa->armv7mp = (features & CPUINFO_ARM_LINUX_FEATURE_IDIV) == CPUINFO_ARM_LINUX_FEATURE_IDIV; * ARMv7+MP ISA */
isa->armv7mp = (features & CPUINFO_ARM_LINUX_FEATURE_IDIV) ==
CPUINFO_ARM_LINUX_FEATURE_IDIV;
break; break;
} }
} }
if (features & CPUINFO_ARM_LINUX_FEATURE_IWMMXT) { if (features & CPUINFO_ARM_LINUX_FEATURE_IWMMXT) {
#if !defined(__ARM_ARCH_8A__) && !(defined(__ARM_ARCH) && (__ARM_ARCH >= 8)) #if !defined(__ARM_ARCH_8A__) && !(defined(__ARM_ARCH) && (__ARM_ARCH >= 8))
const uint32_t wcid = read_wcid(); const uint32_t wcid = read_wcid();
cpuinfo_log_debug("WCID = 0x%08"PRIx32, wcid); cpuinfo_log_debug("WCID = 0x%08" PRIx32, wcid);
const uint32_t coprocessor_type = (wcid >> 8) & UINT32_C(0xFF); const uint32_t coprocessor_type = (wcid >> 8) & UINT32_C(0xFF);
if (coprocessor_type >= 0x10) { if (coprocessor_type >= 0x10) {
isa->wmmx = true; isa->wmmx = true;
if (coprocessor_type >= 0x20) { if (coprocessor_type >= 0x20) {
isa->wmmx2 = true; isa->wmmx2 = true;
}
} else {
cpuinfo_log_warning("WMMX ISA disabled: OS reported iwmmxt feature, "
"but WCID coprocessor type 0x%"PRIx32" indicates no WMMX support",
coprocessor_type);
} }
#else } else {
cpuinfo_log_warning("WMMX ISA disabled: OS reported iwmmxt feature, " cpuinfo_log_warning(
"but there is no iWMMXt coprocessor"); "WMMX ISA disabled: OS reported iwmmxt feature, "
#endif "but WCID coprocessor type 0x%" PRIx32 " indicates no WMMX support",
coprocessor_type);
}
#else
cpuinfo_log_warning(
"WMMX ISA disabled: OS reported iwmmxt feature, "
"but there is no iWMMXt coprocessor");
#endif
} }
if ((features & CPUINFO_ARM_LINUX_FEATURE_THUMB) || (architecture_flags & CPUINFO_ARM_LINUX_ARCH_T)) { if ((features & CPUINFO_ARM_LINUX_FEATURE_THUMB) || (architecture_flags & CPUINFO_ARM_LINUX_ARCH_T)) {
@ -263,35 +290,39 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
isa->jazelle = true; isa->jazelle = true;
} }
/* Qualcomm Krait may have buggy kernel configuration that doesn't report IDIV */ /* Qualcomm Krait may have buggy kernel configuration that
if ((features & CPUINFO_ARM_LINUX_FEATURE_IDIV) == CPUINFO_ARM_LINUX_FEATURE_IDIV || midr_is_krait(midr)) { * doesn't report IDIV */
if ((features & CPUINFO_ARM_LINUX_FEATURE_IDIV) == CPUINFO_ARM_LINUX_FEATURE_IDIV ||
midr_is_krait(midr)) {
isa->idiv = true; isa->idiv = true;
} }
const uint32_t vfp_mask = \ const uint32_t vfp_mask = CPUINFO_ARM_LINUX_FEATURE_VFP | CPUINFO_ARM_LINUX_FEATURE_VFPV3 |
CPUINFO_ARM_LINUX_FEATURE_VFP | CPUINFO_ARM_LINUX_FEATURE_VFPV3 | CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | \ CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | CPUINFO_ARM_LINUX_FEATURE_VFPD32 |
CPUINFO_ARM_LINUX_FEATURE_VFPD32 | CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON; CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON;
if (features & vfp_mask) { if (features & vfp_mask) {
const uint32_t vfpv3_mask = CPUINFO_ARM_LINUX_FEATURE_VFPV3 | CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | \ const uint32_t vfpv3_mask = CPUINFO_ARM_LINUX_FEATURE_VFPV3 |
CPUINFO_ARM_LINUX_FEATURE_VFPD32 | CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON; CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | CPUINFO_ARM_LINUX_FEATURE_VFPD32 |
CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON;
if ((architecture_version >= 7) || (features & vfpv3_mask)) { if ((architecture_version >= 7) || (features & vfpv3_mask)) {
isa->vfpv3 = true; isa->vfpv3 = true;
const uint32_t d32_mask = CPUINFO_ARM_LINUX_FEATURE_VFPD32 | CPUINFO_ARM_LINUX_FEATURE_NEON; const uint32_t d32_mask =
CPUINFO_ARM_LINUX_FEATURE_VFPD32 | CPUINFO_ARM_LINUX_FEATURE_NEON;
if (features & d32_mask) { if (features & d32_mask) {
isa->d32 = true; isa->d32 = true;
} }
} else { } else {
#if defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_8A__) || defined(__ARM_ARCH) && (__ARM_ARCH >= 7) #if defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_8A__) || defined(__ARM_ARCH) && (__ARM_ARCH >= 7)
isa->vfpv3 = true; isa->vfpv3 = true;
#else #else
const uint32_t fpsid = read_fpsid(); const uint32_t fpsid = read_fpsid();
cpuinfo_log_debug("FPSID = 0x%08"PRIx32, fpsid); cpuinfo_log_debug("FPSID = 0x%08" PRIx32, fpsid);
const uint32_t subarchitecture = (fpsid >> 16) & UINT32_C(0x7F); const uint32_t subarchitecture = (fpsid >> 16) & UINT32_C(0x7F);
if (subarchitecture >= 0x01) { if (subarchitecture >= 0x01) {
isa->vfpv2 = true; isa->vfpv2 = true;
} }
#endif #endif
} }
} }
if (features & CPUINFO_ARM_LINUX_FEATURE_NEON) { if (features & CPUINFO_ARM_LINUX_FEATURE_NEON) {
@ -300,8 +331,9 @@ void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
/* /*
* There is no separate feature flag for FP16 support. * There is no separate feature flag for FP16 support.
* VFPv4 implies VFPv3-FP16 support (and in practice, NEON-HP as well). * VFPv4 implies VFPv3-FP16 support (and in practice, NEON-HP as
* Additionally, ARM Cortex-A9 and Qualcomm Scorpion support FP16. * well). Additionally, ARM Cortex-A9 and Qualcomm Scorpion
* support FP16.
*/ */
if ((features & CPUINFO_ARM_LINUX_FEATURE_VFPV4) || midr_is_cortex_a9(midr) || midr_is_scorpion(midr)) { if ((features & CPUINFO_ARM_LINUX_FEATURE_VFPV4) || midr_is_cortex_a9(midr) || midr_is_scorpion(midr)) {
isa->fp16 = true; isa->fp16 = true;

View File

@ -3,14 +3,12 @@
#include <arm/linux/api.h> #include <arm/linux/api.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo( void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
uint32_t features, uint32_t features,
uint32_t features2, uint32_t features2,
uint32_t midr, uint32_t midr,
const struct cpuinfo_arm_chipset chipset[restrict static 1], const struct cpuinfo_arm_chipset chipset[restrict static 1],
struct cpuinfo_arm_isa isa[restrict static 1]) struct cpuinfo_arm_isa isa[restrict static 1]) {
{
if (features & CPUINFO_ARM_LINUX_FEATURE_AES) { if (features & CPUINFO_ARM_LINUX_FEATURE_AES) {
isa->aes = true; isa->aes = true;
} }
@ -31,8 +29,10 @@ void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
} }
/* /*
* Some phones ship with an old kernel configuration that doesn't report NEON FP16 compute extension and SQRDMLAH/SQRDMLSH/UQRDMLAH/UQRDMLSH instructions. * Some phones ship with an old kernel configuration that doesn't report
* Use a MIDR-based heuristic to whitelist processors known to support it: * NEON FP16 compute extension and SQRDMLAH/SQRDMLSH/UQRDMLAH/UQRDMLSH
* instructions. Use a MIDR-based heuristic to whitelist processors
* known to support it:
* - Processors with Cortex-A55 cores * - Processors with Cortex-A55 cores
* - Processors with Cortex-A65 cores * - Processors with Cortex-A65 cores
* - Processors with Cortex-A75 cores * - Processors with Cortex-A75 cores
@ -46,8 +46,10 @@ void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
* - Neoverse V2 cores * - Neoverse V2 cores
*/ */
if (chipset->series == cpuinfo_arm_chipset_series_samsung_exynos && chipset->model == 9810) { if (chipset->series == cpuinfo_arm_chipset_series_samsung_exynos && chipset->model == 9810) {
/* Exynos 9810 reports that it supports FP16 compute, but in fact only little cores do */ /* Exynos 9810 reports that it supports FP16 compute, but in
cpuinfo_log_warning("FP16 arithmetics and RDM disabled: only little cores in Exynos 9810 support these extensions"); * fact only little cores do */
cpuinfo_log_warning(
"FP16 arithmetics and RDM disabled: only little cores in Exynos 9810 support these extensions");
} else { } else {
const uint32_t fp16arith_mask = CPUINFO_ARM_LINUX_FEATURE_FPHP | CPUINFO_ARM_LINUX_FEATURE_ASIMDHP; const uint32_t fp16arith_mask = CPUINFO_ARM_LINUX_FEATURE_FPHP | CPUINFO_ARM_LINUX_FEATURE_ASIMDHP;
switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) { switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
@ -75,9 +77,11 @@ void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
if ((features & fp16arith_mask) == fp16arith_mask) { if ((features & fp16arith_mask) == fp16arith_mask) {
isa->fp16arith = true; isa->fp16arith = true;
} else if (features & CPUINFO_ARM_LINUX_FEATURE_FPHP) { } else if (features & CPUINFO_ARM_LINUX_FEATURE_FPHP) {
cpuinfo_log_warning("FP16 arithmetics disabled: detected support only for scalar operations"); cpuinfo_log_warning(
"FP16 arithmetics disabled: detected support only for scalar operations");
} else if (features & CPUINFO_ARM_LINUX_FEATURE_ASIMDHP) { } else if (features & CPUINFO_ARM_LINUX_FEATURE_ASIMDHP) {
cpuinfo_log_warning("FP16 arithmetics disabled: detected support only for SIMD operations"); cpuinfo_log_warning(
"FP16 arithmetics disabled: detected support only for SIMD operations");
} }
if (features & CPUINFO_ARM_LINUX_FEATURE_ASIMDRDM) { if (features & CPUINFO_ARM_LINUX_FEATURE_ASIMDRDM) {
isa->rdm = true; isa->rdm = true;
@ -90,8 +94,9 @@ void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
} }
/* /*
* Many phones ship with an old kernel configuration that doesn't report UDOT/SDOT instructions. * Many phones ship with an old kernel configuration that doesn't report
* Use a MIDR-based heuristic to whitelist processors known to support it. * UDOT/SDOT instructions. Use a MIDR-based heuristic to whitelist
* processors known to support it.
*/ */
switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) { switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
case UINT32_C(0x4100D060): /* Cortex-A65 */ case UINT32_C(0x4100D060): /* Cortex-A65 */
@ -137,8 +142,9 @@ void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SVE2) { if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SVE2) {
isa->sve2 = true; isa->sve2 = true;
} }
// SVEBF16 is set iff SVE and BF16 are both supported, but the SVEBF16 feature flag // SVEBF16 is set iff SVE and BF16 are both supported, but the SVEBF16
// was added in Linux kernel before the BF16 feature flag, so we check for either. // feature flag was added in Linux kernel before the BF16 feature flag,
// so we check for either.
if (features2 & (CPUINFO_ARM_LINUX_FEATURE2_BF16 | CPUINFO_ARM_LINUX_FEATURE2_SVEBF16)) { if (features2 & (CPUINFO_ARM_LINUX_FEATURE2_BF16 | CPUINFO_ARM_LINUX_FEATURE2_SVEBF16)) {
isa->bf16 = true; isa->bf16 = true;
} }
@ -146,4 +152,3 @@ void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
isa->fhm = true; isa->fhm = true;
} }
} }

View File

@ -3,38 +3,40 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <arm/api.h>
#include <arm/midr.h>
#include <cpuinfo.h> #include <cpuinfo.h>
#include <cpuinfo/common.h> #include <cpuinfo/common.h>
#include <arm/midr.h>
#include <arm/api.h>
#include <linux/api.h> #include <linux/api.h>
/* No hard limit in the kernel, maximum length observed on non-rogue kernels is 64 */ /* No hard limit in the kernel, maximum length observed on non-rogue kernels is
* 64 */
#define CPUINFO_HARDWARE_VALUE_MAX 64 #define CPUINFO_HARDWARE_VALUE_MAX 64
/* No hard limit in the kernel, maximum length on Raspberry Pi is 8. Add 1 symbol to detect overly large revision strings */ /* No hard limit in the kernel, maximum length on Raspberry Pi is 8. Add 1
* symbol to detect overly large revision strings */
#define CPUINFO_REVISION_VALUE_MAX 9 #define CPUINFO_REVISION_VALUE_MAX 9
#ifdef __ANDROID__ #ifdef __ANDROID__
/* As per include/sys/system_properties.h in Android NDK */ /* As per include/sys/system_properties.h in Android NDK */
#define CPUINFO_BUILD_PROP_NAME_MAX 32 #define CPUINFO_BUILD_PROP_NAME_MAX 32
#define CPUINFO_BUILD_PROP_VALUE_MAX 92 #define CPUINFO_BUILD_PROP_VALUE_MAX 92
struct cpuinfo_android_properties { struct cpuinfo_android_properties {
char proc_cpuinfo_hardware[CPUINFO_HARDWARE_VALUE_MAX]; char proc_cpuinfo_hardware[CPUINFO_HARDWARE_VALUE_MAX];
char ro_product_board[CPUINFO_BUILD_PROP_VALUE_MAX]; char ro_product_board[CPUINFO_BUILD_PROP_VALUE_MAX];
char ro_board_platform[CPUINFO_BUILD_PROP_VALUE_MAX]; char ro_board_platform[CPUINFO_BUILD_PROP_VALUE_MAX];
char ro_mediatek_platform[CPUINFO_BUILD_PROP_VALUE_MAX]; char ro_mediatek_platform[CPUINFO_BUILD_PROP_VALUE_MAX];
char ro_arch[CPUINFO_BUILD_PROP_VALUE_MAX]; char ro_arch[CPUINFO_BUILD_PROP_VALUE_MAX];
char ro_chipname[CPUINFO_BUILD_PROP_VALUE_MAX]; char ro_chipname[CPUINFO_BUILD_PROP_VALUE_MAX];
char ro_hardware_chipname[CPUINFO_BUILD_PROP_VALUE_MAX]; char ro_hardware_chipname[CPUINFO_BUILD_PROP_VALUE_MAX];
}; };
#endif #endif
#define CPUINFO_ARM_LINUX_ARCH_T UINT32_C(0x00000001) #define CPUINFO_ARM_LINUX_ARCH_T UINT32_C(0x00000001)
#define CPUINFO_ARM_LINUX_ARCH_E UINT32_C(0x00000002) #define CPUINFO_ARM_LINUX_ARCH_E UINT32_C(0x00000002)
#define CPUINFO_ARM_LINUX_ARCH_J UINT32_C(0x00000004) #define CPUINFO_ARM_LINUX_ARCH_J UINT32_C(0x00000004)
#define CPUINFO_ARM_LINUX_ARCH_TE UINT32_C(0x00000003) #define CPUINFO_ARM_LINUX_ARCH_TE UINT32_C(0x00000003)
#define CPUINFO_ARM_LINUX_ARCH_TEJ UINT32_C(0x00000007) #define CPUINFO_ARM_LINUX_ARCH_TEJ UINT32_C(0x00000007)
struct cpuinfo_arm_linux_proc_cpuinfo_cache { struct cpuinfo_arm_linux_proc_cpuinfo_cache {
@ -49,116 +51,118 @@ struct cpuinfo_arm_linux_proc_cpuinfo_cache {
}; };
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
/* arch/arm/include/uapi/asm/hwcap.h */ /* arch/arm/include/uapi/asm/hwcap.h */
#define CPUINFO_ARM_LINUX_FEATURE_SWP UINT32_C(0x00000001) #define CPUINFO_ARM_LINUX_FEATURE_SWP UINT32_C(0x00000001)
#define CPUINFO_ARM_LINUX_FEATURE_HALF UINT32_C(0x00000002) #define CPUINFO_ARM_LINUX_FEATURE_HALF UINT32_C(0x00000002)
#define CPUINFO_ARM_LINUX_FEATURE_THUMB UINT32_C(0x00000004) #define CPUINFO_ARM_LINUX_FEATURE_THUMB UINT32_C(0x00000004)
#define CPUINFO_ARM_LINUX_FEATURE_26BIT UINT32_C(0x00000008) #define CPUINFO_ARM_LINUX_FEATURE_26BIT UINT32_C(0x00000008)
#define CPUINFO_ARM_LINUX_FEATURE_FASTMULT UINT32_C(0x00000010) #define CPUINFO_ARM_LINUX_FEATURE_FASTMULT UINT32_C(0x00000010)
#define CPUINFO_ARM_LINUX_FEATURE_FPA UINT32_C(0x00000020) #define CPUINFO_ARM_LINUX_FEATURE_FPA UINT32_C(0x00000020)
#define CPUINFO_ARM_LINUX_FEATURE_VFP UINT32_C(0x00000040) #define CPUINFO_ARM_LINUX_FEATURE_VFP UINT32_C(0x00000040)
#define CPUINFO_ARM_LINUX_FEATURE_EDSP UINT32_C(0x00000080) #define CPUINFO_ARM_LINUX_FEATURE_EDSP UINT32_C(0x00000080)
#define CPUINFO_ARM_LINUX_FEATURE_JAVA UINT32_C(0x00000100) #define CPUINFO_ARM_LINUX_FEATURE_JAVA UINT32_C(0x00000100)
#define CPUINFO_ARM_LINUX_FEATURE_IWMMXT UINT32_C(0x00000200) #define CPUINFO_ARM_LINUX_FEATURE_IWMMXT UINT32_C(0x00000200)
#define CPUINFO_ARM_LINUX_FEATURE_CRUNCH UINT32_C(0x00000400) #define CPUINFO_ARM_LINUX_FEATURE_CRUNCH UINT32_C(0x00000400)
#define CPUINFO_ARM_LINUX_FEATURE_THUMBEE UINT32_C(0x00000800) #define CPUINFO_ARM_LINUX_FEATURE_THUMBEE UINT32_C(0x00000800)
#define CPUINFO_ARM_LINUX_FEATURE_NEON UINT32_C(0x00001000) #define CPUINFO_ARM_LINUX_FEATURE_NEON UINT32_C(0x00001000)
#define CPUINFO_ARM_LINUX_FEATURE_VFPV3 UINT32_C(0x00002000) #define CPUINFO_ARM_LINUX_FEATURE_VFPV3 UINT32_C(0x00002000)
#define CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 UINT32_C(0x00004000) /* Also set for VFPv4 with 16 double-precision registers */ #define CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 \
#define CPUINFO_ARM_LINUX_FEATURE_TLS UINT32_C(0x00008000) UINT32_C(0x00004000) /* Also set for VFPv4 with 16 double-precision \
#define CPUINFO_ARM_LINUX_FEATURE_VFPV4 UINT32_C(0x00010000) registers */
#define CPUINFO_ARM_LINUX_FEATURE_IDIVA UINT32_C(0x00020000) #define CPUINFO_ARM_LINUX_FEATURE_TLS UINT32_C(0x00008000)
#define CPUINFO_ARM_LINUX_FEATURE_IDIVT UINT32_C(0x00040000) #define CPUINFO_ARM_LINUX_FEATURE_VFPV4 UINT32_C(0x00010000)
#define CPUINFO_ARM_LINUX_FEATURE_IDIV UINT32_C(0x00060000) #define CPUINFO_ARM_LINUX_FEATURE_IDIVA UINT32_C(0x00020000)
#define CPUINFO_ARM_LINUX_FEATURE_VFPD32 UINT32_C(0x00080000) #define CPUINFO_ARM_LINUX_FEATURE_IDIVT UINT32_C(0x00040000)
#define CPUINFO_ARM_LINUX_FEATURE_LPAE UINT32_C(0x00100000) #define CPUINFO_ARM_LINUX_FEATURE_IDIV UINT32_C(0x00060000)
#define CPUINFO_ARM_LINUX_FEATURE_EVTSTRM UINT32_C(0x00200000) #define CPUINFO_ARM_LINUX_FEATURE_VFPD32 UINT32_C(0x00080000)
#define CPUINFO_ARM_LINUX_FEATURE_LPAE UINT32_C(0x00100000)
#define CPUINFO_ARM_LINUX_FEATURE_EVTSTRM UINT32_C(0x00200000)
#define CPUINFO_ARM_LINUX_FEATURE2_AES UINT32_C(0x00000001) #define CPUINFO_ARM_LINUX_FEATURE2_AES UINT32_C(0x00000001)
#define CPUINFO_ARM_LINUX_FEATURE2_PMULL UINT32_C(0x00000002) #define CPUINFO_ARM_LINUX_FEATURE2_PMULL UINT32_C(0x00000002)
#define CPUINFO_ARM_LINUX_FEATURE2_SHA1 UINT32_C(0x00000004) #define CPUINFO_ARM_LINUX_FEATURE2_SHA1 UINT32_C(0x00000004)
#define CPUINFO_ARM_LINUX_FEATURE2_SHA2 UINT32_C(0x00000008) #define CPUINFO_ARM_LINUX_FEATURE2_SHA2 UINT32_C(0x00000008)
#define CPUINFO_ARM_LINUX_FEATURE2_CRC32 UINT32_C(0x00000010) #define CPUINFO_ARM_LINUX_FEATURE2_CRC32 UINT32_C(0x00000010)
#elif CPUINFO_ARCH_ARM64 #elif CPUINFO_ARCH_ARM64
/* arch/arm64/include/uapi/asm/hwcap.h */ /* arch/arm64/include/uapi/asm/hwcap.h */
#define CPUINFO_ARM_LINUX_FEATURE_FP UINT32_C(0x00000001) #define CPUINFO_ARM_LINUX_FEATURE_FP UINT32_C(0x00000001)
#define CPUINFO_ARM_LINUX_FEATURE_ASIMD UINT32_C(0x00000002) #define CPUINFO_ARM_LINUX_FEATURE_ASIMD UINT32_C(0x00000002)
#define CPUINFO_ARM_LINUX_FEATURE_EVTSTRM UINT32_C(0x00000004) #define CPUINFO_ARM_LINUX_FEATURE_EVTSTRM UINT32_C(0x00000004)
#define CPUINFO_ARM_LINUX_FEATURE_AES UINT32_C(0x00000008) #define CPUINFO_ARM_LINUX_FEATURE_AES UINT32_C(0x00000008)
#define CPUINFO_ARM_LINUX_FEATURE_PMULL UINT32_C(0x00000010) #define CPUINFO_ARM_LINUX_FEATURE_PMULL UINT32_C(0x00000010)
#define CPUINFO_ARM_LINUX_FEATURE_SHA1 UINT32_C(0x00000020) #define CPUINFO_ARM_LINUX_FEATURE_SHA1 UINT32_C(0x00000020)
#define CPUINFO_ARM_LINUX_FEATURE_SHA2 UINT32_C(0x00000040) #define CPUINFO_ARM_LINUX_FEATURE_SHA2 UINT32_C(0x00000040)
#define CPUINFO_ARM_LINUX_FEATURE_CRC32 UINT32_C(0x00000080) #define CPUINFO_ARM_LINUX_FEATURE_CRC32 UINT32_C(0x00000080)
#define CPUINFO_ARM_LINUX_FEATURE_ATOMICS UINT32_C(0x00000100) #define CPUINFO_ARM_LINUX_FEATURE_ATOMICS UINT32_C(0x00000100)
#define CPUINFO_ARM_LINUX_FEATURE_FPHP UINT32_C(0x00000200) #define CPUINFO_ARM_LINUX_FEATURE_FPHP UINT32_C(0x00000200)
#define CPUINFO_ARM_LINUX_FEATURE_ASIMDHP UINT32_C(0x00000400) #define CPUINFO_ARM_LINUX_FEATURE_ASIMDHP UINT32_C(0x00000400)
#define CPUINFO_ARM_LINUX_FEATURE_CPUID UINT32_C(0x00000800) #define CPUINFO_ARM_LINUX_FEATURE_CPUID UINT32_C(0x00000800)
#define CPUINFO_ARM_LINUX_FEATURE_ASIMDRDM UINT32_C(0x00001000) #define CPUINFO_ARM_LINUX_FEATURE_ASIMDRDM UINT32_C(0x00001000)
#define CPUINFO_ARM_LINUX_FEATURE_JSCVT UINT32_C(0x00002000) #define CPUINFO_ARM_LINUX_FEATURE_JSCVT UINT32_C(0x00002000)
#define CPUINFO_ARM_LINUX_FEATURE_FCMA UINT32_C(0x00004000) #define CPUINFO_ARM_LINUX_FEATURE_FCMA UINT32_C(0x00004000)
#define CPUINFO_ARM_LINUX_FEATURE_LRCPC UINT32_C(0x00008000) #define CPUINFO_ARM_LINUX_FEATURE_LRCPC UINT32_C(0x00008000)
#define CPUINFO_ARM_LINUX_FEATURE_DCPOP UINT32_C(0x00010000) #define CPUINFO_ARM_LINUX_FEATURE_DCPOP UINT32_C(0x00010000)
#define CPUINFO_ARM_LINUX_FEATURE_SHA3 UINT32_C(0x00020000) #define CPUINFO_ARM_LINUX_FEATURE_SHA3 UINT32_C(0x00020000)
#define CPUINFO_ARM_LINUX_FEATURE_SM3 UINT32_C(0x00040000) #define CPUINFO_ARM_LINUX_FEATURE_SM3 UINT32_C(0x00040000)
#define CPUINFO_ARM_LINUX_FEATURE_SM4 UINT32_C(0x00080000) #define CPUINFO_ARM_LINUX_FEATURE_SM4 UINT32_C(0x00080000)
#define CPUINFO_ARM_LINUX_FEATURE_ASIMDDP UINT32_C(0x00100000) #define CPUINFO_ARM_LINUX_FEATURE_ASIMDDP UINT32_C(0x00100000)
#define CPUINFO_ARM_LINUX_FEATURE_SHA512 UINT32_C(0x00200000) #define CPUINFO_ARM_LINUX_FEATURE_SHA512 UINT32_C(0x00200000)
#define CPUINFO_ARM_LINUX_FEATURE_SVE UINT32_C(0x00400000) #define CPUINFO_ARM_LINUX_FEATURE_SVE UINT32_C(0x00400000)
#define CPUINFO_ARM_LINUX_FEATURE_ASIMDFHM UINT32_C(0x00800000) #define CPUINFO_ARM_LINUX_FEATURE_ASIMDFHM UINT32_C(0x00800000)
#define CPUINFO_ARM_LINUX_FEATURE_DIT UINT32_C(0x01000000) #define CPUINFO_ARM_LINUX_FEATURE_DIT UINT32_C(0x01000000)
#define CPUINFO_ARM_LINUX_FEATURE_USCAT UINT32_C(0x02000000) #define CPUINFO_ARM_LINUX_FEATURE_USCAT UINT32_C(0x02000000)
#define CPUINFO_ARM_LINUX_FEATURE_ILRCPC UINT32_C(0x04000000) #define CPUINFO_ARM_LINUX_FEATURE_ILRCPC UINT32_C(0x04000000)
#define CPUINFO_ARM_LINUX_FEATURE_FLAGM UINT32_C(0x08000000) #define CPUINFO_ARM_LINUX_FEATURE_FLAGM UINT32_C(0x08000000)
#define CPUINFO_ARM_LINUX_FEATURE_SSBS UINT32_C(0x10000000) #define CPUINFO_ARM_LINUX_FEATURE_SSBS UINT32_C(0x10000000)
#define CPUINFO_ARM_LINUX_FEATURE_SB UINT32_C(0x20000000) #define CPUINFO_ARM_LINUX_FEATURE_SB UINT32_C(0x20000000)
#define CPUINFO_ARM_LINUX_FEATURE_PACA UINT32_C(0x40000000) #define CPUINFO_ARM_LINUX_FEATURE_PACA UINT32_C(0x40000000)
#define CPUINFO_ARM_LINUX_FEATURE_PACG UINT32_C(0x80000000) #define CPUINFO_ARM_LINUX_FEATURE_PACG UINT32_C(0x80000000)
#define CPUINFO_ARM_LINUX_FEATURE2_DCPODP UINT32_C(0x00000001) #define CPUINFO_ARM_LINUX_FEATURE2_DCPODP UINT32_C(0x00000001)
#define CPUINFO_ARM_LINUX_FEATURE2_SVE2 UINT32_C(0x00000002) #define CPUINFO_ARM_LINUX_FEATURE2_SVE2 UINT32_C(0x00000002)
#define CPUINFO_ARM_LINUX_FEATURE2_SVEAES UINT32_C(0x00000004) #define CPUINFO_ARM_LINUX_FEATURE2_SVEAES UINT32_C(0x00000004)
#define CPUINFO_ARM_LINUX_FEATURE2_SVEPMULL UINT32_C(0x00000008) #define CPUINFO_ARM_LINUX_FEATURE2_SVEPMULL UINT32_C(0x00000008)
#define CPUINFO_ARM_LINUX_FEATURE2_SVEBITPERM UINT32_C(0x00000010) #define CPUINFO_ARM_LINUX_FEATURE2_SVEBITPERM UINT32_C(0x00000010)
#define CPUINFO_ARM_LINUX_FEATURE2_SVESHA3 UINT32_C(0x00000020) #define CPUINFO_ARM_LINUX_FEATURE2_SVESHA3 UINT32_C(0x00000020)
#define CPUINFO_ARM_LINUX_FEATURE2_SVESM4 UINT32_C(0x00000040) #define CPUINFO_ARM_LINUX_FEATURE2_SVESM4 UINT32_C(0x00000040)
#define CPUINFO_ARM_LINUX_FEATURE2_FLAGM2 UINT32_C(0x00000080) #define CPUINFO_ARM_LINUX_FEATURE2_FLAGM2 UINT32_C(0x00000080)
#define CPUINFO_ARM_LINUX_FEATURE2_FRINT UINT32_C(0x00000100) #define CPUINFO_ARM_LINUX_FEATURE2_FRINT UINT32_C(0x00000100)
#define CPUINFO_ARM_LINUX_FEATURE2_SVEI8MM UINT32_C(0x00000200) #define CPUINFO_ARM_LINUX_FEATURE2_SVEI8MM UINT32_C(0x00000200)
#define CPUINFO_ARM_LINUX_FEATURE2_SVEF32MM UINT32_C(0x00000400) #define CPUINFO_ARM_LINUX_FEATURE2_SVEF32MM UINT32_C(0x00000400)
#define CPUINFO_ARM_LINUX_FEATURE2_SVEF64MM UINT32_C(0x00000800) #define CPUINFO_ARM_LINUX_FEATURE2_SVEF64MM UINT32_C(0x00000800)
#define CPUINFO_ARM_LINUX_FEATURE2_SVEBF16 UINT32_C(0x00001000) #define CPUINFO_ARM_LINUX_FEATURE2_SVEBF16 UINT32_C(0x00001000)
#define CPUINFO_ARM_LINUX_FEATURE2_I8MM UINT32_C(0x00002000) #define CPUINFO_ARM_LINUX_FEATURE2_I8MM UINT32_C(0x00002000)
#define CPUINFO_ARM_LINUX_FEATURE2_BF16 UINT32_C(0x00004000) #define CPUINFO_ARM_LINUX_FEATURE2_BF16 UINT32_C(0x00004000)
#define CPUINFO_ARM_LINUX_FEATURE2_DGH UINT32_C(0x00008000) #define CPUINFO_ARM_LINUX_FEATURE2_DGH UINT32_C(0x00008000)
#define CPUINFO_ARM_LINUX_FEATURE2_RNG UINT32_C(0x00010000) #define CPUINFO_ARM_LINUX_FEATURE2_RNG UINT32_C(0x00010000)
#define CPUINFO_ARM_LINUX_FEATURE2_BTI UINT32_C(0x00020000) #define CPUINFO_ARM_LINUX_FEATURE2_BTI UINT32_C(0x00020000)
#endif #endif
#define CPUINFO_ARM_LINUX_VALID_ARCHITECTURE UINT32_C(0x00010000) #define CPUINFO_ARM_LINUX_VALID_ARCHITECTURE UINT32_C(0x00010000)
#define CPUINFO_ARM_LINUX_VALID_IMPLEMENTER UINT32_C(0x00020000) #define CPUINFO_ARM_LINUX_VALID_IMPLEMENTER UINT32_C(0x00020000)
#define CPUINFO_ARM_LINUX_VALID_VARIANT UINT32_C(0x00040000) #define CPUINFO_ARM_LINUX_VALID_VARIANT UINT32_C(0x00040000)
#define CPUINFO_ARM_LINUX_VALID_PART UINT32_C(0x00080000) #define CPUINFO_ARM_LINUX_VALID_PART UINT32_C(0x00080000)
#define CPUINFO_ARM_LINUX_VALID_REVISION UINT32_C(0x00100000) #define CPUINFO_ARM_LINUX_VALID_REVISION UINT32_C(0x00100000)
#define CPUINFO_ARM_LINUX_VALID_PROCESSOR UINT32_C(0x00200000) #define CPUINFO_ARM_LINUX_VALID_PROCESSOR UINT32_C(0x00200000)
#define CPUINFO_ARM_LINUX_VALID_FEATURES UINT32_C(0x00400000) #define CPUINFO_ARM_LINUX_VALID_FEATURES UINT32_C(0x00400000)
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
#define CPUINFO_ARM_LINUX_VALID_ICACHE_SIZE UINT32_C(0x01000000) #define CPUINFO_ARM_LINUX_VALID_ICACHE_SIZE UINT32_C(0x01000000)
#define CPUINFO_ARM_LINUX_VALID_ICACHE_SETS UINT32_C(0x02000000) #define CPUINFO_ARM_LINUX_VALID_ICACHE_SETS UINT32_C(0x02000000)
#define CPUINFO_ARM_LINUX_VALID_ICACHE_WAYS UINT32_C(0x04000000) #define CPUINFO_ARM_LINUX_VALID_ICACHE_WAYS UINT32_C(0x04000000)
#define CPUINFO_ARM_LINUX_VALID_ICACHE_LINE UINT32_C(0x08000000) #define CPUINFO_ARM_LINUX_VALID_ICACHE_LINE UINT32_C(0x08000000)
#define CPUINFO_ARM_LINUX_VALID_DCACHE_SIZE UINT32_C(0x10000000) #define CPUINFO_ARM_LINUX_VALID_DCACHE_SIZE UINT32_C(0x10000000)
#define CPUINFO_ARM_LINUX_VALID_DCACHE_SETS UINT32_C(0x20000000) #define CPUINFO_ARM_LINUX_VALID_DCACHE_SETS UINT32_C(0x20000000)
#define CPUINFO_ARM_LINUX_VALID_DCACHE_WAYS UINT32_C(0x40000000) #define CPUINFO_ARM_LINUX_VALID_DCACHE_WAYS UINT32_C(0x40000000)
#define CPUINFO_ARM_LINUX_VALID_DCACHE_LINE UINT32_C(0x80000000) #define CPUINFO_ARM_LINUX_VALID_DCACHE_LINE UINT32_C(0x80000000)
#endif #endif
#define CPUINFO_ARM_LINUX_VALID_INFO UINT32_C(0x007F0000) #define CPUINFO_ARM_LINUX_VALID_INFO UINT32_C(0x007F0000)
#define CPUINFO_ARM_LINUX_VALID_MIDR UINT32_C(0x003F0000) #define CPUINFO_ARM_LINUX_VALID_MIDR UINT32_C(0x003F0000)
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
#define CPUINFO_ARM_LINUX_VALID_ICACHE UINT32_C(0x0F000000) #define CPUINFO_ARM_LINUX_VALID_ICACHE UINT32_C(0x0F000000)
#define CPUINFO_ARM_LINUX_VALID_DCACHE UINT32_C(0xF0000000) #define CPUINFO_ARM_LINUX_VALID_DCACHE UINT32_C(0xF0000000)
#define CPUINFO_ARM_LINUX_VALID_CACHE_LINE UINT32_C(0x88000000) #define CPUINFO_ARM_LINUX_VALID_CACHE_LINE UINT32_C(0x88000000)
#endif #endif
struct cpuinfo_arm_linux_processor { struct cpuinfo_arm_linux_processor {
@ -178,13 +182,15 @@ struct cpuinfo_arm_linux_processor {
uint32_t uarch_index; uint32_t uarch_index;
/** /**
* ID of the physical package which includes this logical processor. * ID of the physical package which includes this logical processor.
* The value is parsed from /sys/devices/system/cpu/cpu<N>/topology/physical_package_id * The value is parsed from
* /sys/devices/system/cpu/cpu<N>/topology/physical_package_id
*/ */
uint32_t package_id; uint32_t package_id;
/** /**
* Minimum processor ID on the package which includes this logical processor. * Minimum processor ID on the package which includes this logical
* This value can serve as an ID for the cluster of logical processors: it is the * processor. This value can serve as an ID for the cluster of logical
* same for all logical processors on the same package. * processors: it is the same for all logical processors on the same
* package.
*/ */
uint32_t package_leader_id; uint32_t package_leader_id;
/** /**
@ -193,14 +199,16 @@ struct cpuinfo_arm_linux_processor {
uint32_t package_processor_count; uint32_t package_processor_count;
/** /**
* Maximum frequency, in kHZ. * Maximum frequency, in kHZ.
* The value is parsed from /sys/devices/system/cpu/cpu<N>/cpufreq/cpuinfo_max_freq * The value is parsed from
* If failed to read or parse the file, the value is 0. * /sys/devices/system/cpu/cpu<N>/cpufreq/cpuinfo_max_freq If failed to
* read or parse the file, the value is 0.
*/ */
uint32_t max_frequency; uint32_t max_frequency;
/** /**
* Minimum frequency, in kHZ. * Minimum frequency, in kHZ.
* The value is parsed from /sys/devices/system/cpu/cpu<N>/cpufreq/cpuinfo_min_freq * The value is parsed from
* If failed to read or parse the file, the value is 0. * /sys/devices/system/cpu/cpu<N>/cpufreq/cpuinfo_min_freq If failed to
* read or parse the file, the value is 0.
*/ */
uint32_t min_frequency; uint32_t min_frequency;
/** Linux processor ID */ /** Linux processor ID */
@ -216,8 +224,7 @@ struct cpuinfo_arm_linux_cluster {
/* Returns true if the two processors do belong to the same cluster */ /* Returns true if the two processors do belong to the same cluster */
static inline bool cpuinfo_arm_linux_processor_equals( static inline bool cpuinfo_arm_linux_processor_equals(
struct cpuinfo_arm_linux_processor processor_i[restrict static 1], struct cpuinfo_arm_linux_processor processor_i[restrict static 1],
struct cpuinfo_arm_linux_processor processor_j[restrict static 1]) struct cpuinfo_arm_linux_processor processor_j[restrict static 1]) {
{
const uint32_t joint_flags = processor_i->flags & processor_j->flags; const uint32_t joint_flags = processor_i->flags & processor_j->flags;
bool same_max_frequency = false; bool same_max_frequency = false;
@ -251,11 +258,11 @@ static inline bool cpuinfo_arm_linux_processor_equals(
return same_max_frequency && same_min_frequency; return same_max_frequency && same_min_frequency;
} }
/* Returns true if the two processors certainly don't belong to the same cluster */ /* Returns true if the two processors certainly don't belong to the same cluster
*/
static inline bool cpuinfo_arm_linux_processor_not_equals( static inline bool cpuinfo_arm_linux_processor_not_equals(
struct cpuinfo_arm_linux_processor processor_i[restrict static 1], struct cpuinfo_arm_linux_processor processor_i[restrict static 1],
struct cpuinfo_arm_linux_processor processor_j[restrict static 1]) struct cpuinfo_arm_linux_processor processor_j[restrict static 1]) {
{
const uint32_t joint_flags = processor_i->flags & processor_j->flags; const uint32_t joint_flags = processor_i->flags & processor_j->flags;
if (joint_flags & CPUINFO_LINUX_FLAG_MAX_FREQUENCY) { if (joint_flags & CPUINFO_LINUX_FLAG_MAX_FREQUENCY) {
@ -286,79 +293,73 @@ CPUINFO_INTERNAL bool cpuinfo_arm_linux_parse_proc_cpuinfo(
struct cpuinfo_arm_linux_processor processors[restrict static max_processors_count]); struct cpuinfo_arm_linux_processor processors[restrict static max_processors_count]);
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
CPUINFO_INTERNAL bool cpuinfo_arm_linux_hwcap_from_getauxval( CPUINFO_INTERNAL bool cpuinfo_arm_linux_hwcap_from_getauxval(
uint32_t hwcap[restrict static 1], uint32_t hwcap[restrict static 1],
uint32_t hwcap2[restrict static 1]); uint32_t hwcap2[restrict static 1]);
CPUINFO_INTERNAL bool cpuinfo_arm_linux_hwcap_from_procfs( CPUINFO_INTERNAL bool cpuinfo_arm_linux_hwcap_from_procfs(
uint32_t hwcap[restrict static 1], uint32_t hwcap[restrict static 1],
uint32_t hwcap2[restrict static 1]); uint32_t hwcap2[restrict static 1]);
CPUINFO_INTERNAL void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo( CPUINFO_INTERNAL void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
uint32_t features, uint32_t features,
uint32_t features2, uint32_t features2,
uint32_t midr, uint32_t midr,
uint32_t architecture_version, uint32_t architecture_version,
uint32_t architecture_flags, uint32_t architecture_flags,
const struct cpuinfo_arm_chipset chipset[restrict static 1], const struct cpuinfo_arm_chipset chipset[restrict static 1],
struct cpuinfo_arm_isa isa[restrict static 1]); struct cpuinfo_arm_isa isa[restrict static 1]);
#elif CPUINFO_ARCH_ARM64 #elif CPUINFO_ARCH_ARM64
CPUINFO_INTERNAL void cpuinfo_arm_linux_hwcap_from_getauxval( CPUINFO_INTERNAL void cpuinfo_arm_linux_hwcap_from_getauxval(
uint32_t hwcap[restrict static 1], uint32_t hwcap[restrict static 1],
uint32_t hwcap2[restrict static 1]); uint32_t hwcap2[restrict static 1]);
CPUINFO_INTERNAL void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo( CPUINFO_INTERNAL void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
uint32_t features, uint32_t features,
uint32_t features2, uint32_t features2,
uint32_t midr, uint32_t midr,
const struct cpuinfo_arm_chipset chipset[restrict static 1], const struct cpuinfo_arm_chipset chipset[restrict static 1],
struct cpuinfo_arm_isa isa[restrict static 1]); struct cpuinfo_arm_isa isa[restrict static 1]);
#endif #endif
#if defined(__ANDROID__) #if defined(__ANDROID__)
CPUINFO_INTERNAL struct cpuinfo_arm_chipset CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset(
cpuinfo_arm_android_decode_chipset( const struct cpuinfo_android_properties properties[restrict static 1],
const struct cpuinfo_android_properties properties[restrict static 1], uint32_t cores,
uint32_t cores, uint32_t max_cpu_freq_max);
uint32_t max_cpu_freq_max);
#else #else
CPUINFO_INTERNAL struct cpuinfo_arm_chipset CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_linux_decode_chipset(
cpuinfo_arm_linux_decode_chipset( const char hardware[restrict static CPUINFO_HARDWARE_VALUE_MAX],
const char hardware[restrict static CPUINFO_HARDWARE_VALUE_MAX], const char revision[restrict static CPUINFO_REVISION_VALUE_MAX],
const char revision[restrict static CPUINFO_REVISION_VALUE_MAX], uint32_t cores,
uint32_t cores, uint32_t max_cpu_freq_max);
uint32_t max_cpu_freq_max);
#endif #endif
CPUINFO_INTERNAL struct cpuinfo_arm_chipset CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_linux_decode_chipset_from_proc_cpuinfo_hardware(
cpuinfo_arm_linux_decode_chipset_from_proc_cpuinfo_hardware( const char proc_cpuinfo_hardware[restrict static CPUINFO_HARDWARE_VALUE_MAX],
const char proc_cpuinfo_hardware[restrict static CPUINFO_HARDWARE_VALUE_MAX], uint32_t cores,
uint32_t cores, uint32_t max_cpu_freq_max, bool is_tegra); uint32_t max_cpu_freq_max,
bool is_tegra);
#ifdef __ANDROID__ #ifdef __ANDROID__
CPUINFO_INTERNAL struct cpuinfo_arm_chipset CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_product_board(
cpuinfo_arm_android_decode_chipset_from_ro_product_board( const char ro_product_board[restrict static CPUINFO_BUILD_PROP_VALUE_MAX],
const char ro_product_board[restrict static CPUINFO_BUILD_PROP_VALUE_MAX], uint32_t cores,
uint32_t cores, uint32_t max_cpu_freq_max); uint32_t max_cpu_freq_max);
CPUINFO_INTERNAL struct cpuinfo_arm_chipset CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_board_platform(
cpuinfo_arm_android_decode_chipset_from_ro_board_platform( const char ro_board_platform[restrict static CPUINFO_BUILD_PROP_VALUE_MAX],
const char ro_board_platform[restrict static CPUINFO_BUILD_PROP_VALUE_MAX], uint32_t cores,
uint32_t cores, uint32_t max_cpu_freq_max); uint32_t max_cpu_freq_max);
CPUINFO_INTERNAL struct cpuinfo_arm_chipset CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_mediatek_platform(
cpuinfo_arm_android_decode_chipset_from_ro_mediatek_platform( const char ro_mediatek_platform[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]);
const char ro_mediatek_platform[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]); CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_arch(
CPUINFO_INTERNAL struct cpuinfo_arm_chipset const char ro_arch[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]);
cpuinfo_arm_android_decode_chipset_from_ro_arch( CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_chipname(
const char ro_arch[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]); const char ro_chipname[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]);
CPUINFO_INTERNAL struct cpuinfo_arm_chipset CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_hardware_chipname(
cpuinfo_arm_android_decode_chipset_from_ro_chipname( const char ro_hardware_chipname[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]);
const char ro_chipname[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]);
CPUINFO_INTERNAL struct cpuinfo_arm_chipset
cpuinfo_arm_android_decode_chipset_from_ro_hardware_chipname(
const char ro_hardware_chipname[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]);
#else #else
CPUINFO_INTERNAL struct cpuinfo_arm_chipset CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_linux_decode_chipset_from_proc_cpuinfo_revision(
cpuinfo_arm_linux_decode_chipset_from_proc_cpuinfo_revision( const char proc_cpuinfo_revision[restrict static CPUINFO_REVISION_VALUE_MAX]);
const char proc_cpuinfo_revision[restrict static CPUINFO_REVISION_VALUE_MAX]);
#endif #endif
CPUINFO_INTERNAL bool cpuinfo_arm_linux_detect_core_clusters_by_heuristic( CPUINFO_INTERNAL bool cpuinfo_arm_linux_detect_core_clusters_by_heuristic(

File diff suppressed because it is too large Load Diff

View File

@ -1,63 +1,74 @@
#include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <cpuinfo.h>
#include <arm/linux/api.h> #include <arm/linux/api.h>
#include <cpuinfo.h>
#if defined(__ANDROID__) #if defined(__ANDROID__)
#include <arm/android/api.h> #include <arm/android/api.h>
#endif #endif
#include <arm/api.h> #include <arm/api.h>
#include <arm/midr.h> #include <arm/midr.h>
#include <linux/api.h>
#include <cpuinfo/internal-api.h> #include <cpuinfo/internal-api.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <linux/api.h>
static inline bool bitmask_all(uint32_t bitfield, uint32_t mask) { static inline bool bitmask_all(uint32_t bitfield, uint32_t mask) {
return (bitfield & mask) == mask; return (bitfield & mask) == mask;
} }
/* /*
* Assigns logical processors to clusters of cores using heuristic based on the typical configuration of clusters for * Assigns logical processors to clusters of cores using heuristic based on the
* 5, 6, 8, and 10 cores: * typical configuration of clusters for 5, 6, 8, and 10 cores:
* - 5 cores (ARM32 Android only): 2 clusters of 4+1 cores * - 5 cores (ARM32 Android only): 2 clusters of 4+1 cores
* - 6 cores: 2 clusters of 4+2 cores * - 6 cores: 2 clusters of 4+2 cores
* - 8 cores: 2 clusters of 4+4 cores * - 8 cores: 2 clusters of 4+4 cores
* - 10 cores: 3 clusters of 4+4+2 cores * - 10 cores: 3 clusters of 4+4+2 cores
* *
* The function must be called after parsing OS-provided information on core clusters. * The function must be called after parsing OS-provided information on core
* Its purpose is to detect clusters of cores when OS-provided information is lacking or incomplete, i.e. * clusters. Its purpose is to detect clusters of cores when OS-provided
* - Linux kernel is not configured to report information in sysfs topology leaf. * information is lacking or incomplete, i.e.
* - Linux kernel reports topology information only for online cores, and only cores on one cluster are online, e.g.: * - Linux kernel is not configured to report information in sysfs topology
* - Exynos 8890 has 8 cores in 4+4 clusters, but only the first cluster of 4 cores is reported, and cluster * leaf.
* configuration of logical processors 4-7 is not reported (all remaining processors 4-7 form cluster 1) * - Linux kernel reports topology information only for online cores, and only
* - MT6797 has 10 cores in 4+4+2, but only the first cluster of 4 cores is reported, and cluster configuration * cores on one cluster are online, e.g.:
* of logical processors 4-9 is not reported (processors 4-7 form cluster 1, and processors 8-9 form cluster 2). * - Exynos 8890 has 8 cores in 4+4 clusters, but only the first cluster of 4
* cores is reported, and cluster configuration of logical processors 4-7 is not
* reported (all remaining processors 4-7 form cluster 1)
* - MT6797 has 10 cores in 4+4+2, but only the first cluster of 4 cores is
* reported, and cluster configuration of logical processors 4-9 is not reported
* (processors 4-7 form cluster 1, and processors 8-9 form cluster 2).
* *
* Heuristic assignment of processors to the above pre-defined clusters fails if such assignment would contradict * Heuristic assignment of processors to the above pre-defined clusters fails if
* information provided by the operating system: * such assignment would contradict information provided by the operating
* - Any of the OS-reported processor clusters is different than the corresponding heuristic cluster. * system:
* - Processors in a heuristic cluster have no OS-provided cluster siblings information, but have known and different * - Any of the OS-reported processor clusters is different than the
* minimum/maximum frequency. * corresponding heuristic cluster.
* - Processors in a heuristic cluster have no OS-provided cluster siblings information, but have known and different * - Processors in a heuristic cluster have no OS-provided cluster siblings
* MIDR components. * information, but have known and different minimum/maximum frequency.
* - Processors in a heuristic cluster have no OS-provided cluster siblings
* information, but have known and different MIDR components.
* *
* If the heuristic assignment of processors to clusters of cores fails, all processors' clusters are unchanged. * If the heuristic assignment of processors to clusters of cores fails, all
* processors' clusters are unchanged.
* *
* @param usable_processors - number of processors in the @p processors array with CPUINFO_LINUX_FLAG_VALID flags. * @param usable_processors - number of processors in the @p processors array
* with CPUINFO_LINUX_FLAG_VALID flags.
* @param max_processors - number of elements in the @p processors array. * @param max_processors - number of elements in the @p processors array.
* @param[in,out] processors - processor descriptors with pre-parsed POSSIBLE and PRESENT flags, minimum/maximum * @param[in,out] processors - processor descriptors with pre-parsed POSSIBLE
* frequency, MIDR information, and core cluster (package siblings list) information. * and PRESENT flags, minimum/maximum frequency, MIDR information, and core
* cluster (package siblings list) information.
* *
* @retval true if the heuristic successfully assigned all processors into clusters of cores. * @retval true if the heuristic successfully assigned all processors into
* @retval false if known details about processors contradict the heuristic configuration of core clusters. * clusters of cores.
* @retval false if known details about processors contradict the heuristic
* configuration of core clusters.
*/ */
bool cpuinfo_arm_linux_detect_core_clusters_by_heuristic( bool cpuinfo_arm_linux_detect_core_clusters_by_heuristic(
uint32_t usable_processors, uint32_t usable_processors,
uint32_t max_processors, uint32_t max_processors,
struct cpuinfo_arm_linux_processor processors[restrict static max_processors]) struct cpuinfo_arm_linux_processor processors[restrict static max_processors]) {
{
uint32_t cluster_processors[3]; uint32_t cluster_processors[3];
switch (usable_processors) { switch (usable_processors) {
case 10: case 10:
@ -76,8 +87,9 @@ bool cpuinfo_arm_linux_detect_core_clusters_by_heuristic(
#if defined(__ANDROID__) && CPUINFO_ARCH_ARM #if defined(__ANDROID__) && CPUINFO_ARCH_ARM
case 5: case 5:
/* /*
* The only processor with 5 cores is Leadcore L1860C (ARMv7, mobile), * The only processor with 5 cores is Leadcore L1860C
* but this configuration is not too unreasonable for a virtualized ARM server. * (ARMv7, mobile), but this configuration is not too
* unreasonable for a virtualized ARM server.
*/ */
cluster_processors[0] = 4; cluster_processors[0] = 4;
cluster_processors[1] = 1; cluster_processors[1] = 1;
@ -89,7 +101,8 @@ bool cpuinfo_arm_linux_detect_core_clusters_by_heuristic(
/* /*
* Assignment of processors to core clusters is done in two passes: * Assignment of processors to core clusters is done in two passes:
* 1. Verify that the clusters proposed by heuristic are compatible with known details about processors. * 1. Verify that the clusters proposed by heuristic are compatible with
* known details about processors.
* 2. If verification passed, update core clusters for the processors. * 2. If verification passed, update core clusters for the processors.
*/ */
@ -100,16 +113,22 @@ bool cpuinfo_arm_linux_detect_core_clusters_by_heuristic(
for (uint32_t i = 0; i < max_processors; i++) { for (uint32_t i = 0; i < max_processors; i++) {
if (bitmask_all(processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) { if (bitmask_all(processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
if (expected_cluster_processors == 0) { if (expected_cluster_processors == 0) {
/* Expect this processor to start a new cluster */ /* Expect this processor to start a new cluster
*/
expected_cluster_exists = !!(processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER); expected_cluster_exists = !!(processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER);
if (expected_cluster_exists) { if (expected_cluster_exists) {
if (processors[i].package_leader_id != i) { if (processors[i].package_leader_id != i) {
cpuinfo_log_debug( cpuinfo_log_debug(
"heuristic detection of core clusters failed: " "heuristic detection of core clusters failed: "
"processor %"PRIu32" is expected to start a new cluster #%"PRIu32" with %"PRIu32" cores, " "processor %" PRIu32
"but system siblings lists reported it as a sibling of processor %"PRIu32, " is expected to start a new cluster #%" PRIu32 " with %" PRIu32
i, cluster, cluster_processors[cluster], processors[i].package_leader_id); " cores, "
"but system siblings lists reported it as a sibling of processor %" PRIu32,
i,
cluster,
cluster_processors[cluster],
processors[i].package_leader_id);
return false; return false;
} }
} else { } else {
@ -119,48 +138,73 @@ bool cpuinfo_arm_linux_detect_core_clusters_by_heuristic(
cluster_start = i; cluster_start = i;
expected_cluster_processors = cluster_processors[cluster++]; expected_cluster_processors = cluster_processors[cluster++];
} else { } else {
/* Expect this processor to belong to the same cluster as processor */ /* Expect this processor to belong to the same
* cluster as processor */
if (expected_cluster_exists) { if (expected_cluster_exists) {
/* /*
* The cluster suggested by the heuristic was already parsed from system siblings lists. * The cluster suggested by the
* For all processors we expect in the cluster, check that: * heuristic was already parsed from
* - They have pre-assigned cluster from siblings lists (CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER flag). * system siblings lists. For all
* - They were assigned to the same cluster based on siblings lists * processors we expect in the cluster,
* (package_leader_id points to the first processor in the cluster). * check that:
* - They have pre-assigned cluster from
* siblings lists
* (CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER
* flag).
* - They were assigned to the same
* cluster based on siblings lists
* (package_leader_id points to the
* first processor in the cluster).
*/ */
if ((processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER) == 0) { if ((processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER) == 0) {
cpuinfo_log_debug( cpuinfo_log_debug(
"heuristic detection of core clusters failed: " "heuristic detection of core clusters failed: "
"processor %"PRIu32" is expected to belong to the cluster of processor %"PRIu32", " "processor %" PRIu32
"but system siblings lists did not report it as a sibling of processor %"PRIu32, " is expected to belong to the cluster of processor %" PRIu32
i, cluster_start, cluster_start); ", "
"but system siblings lists did not report it as a sibling of processor %" PRIu32,
i,
cluster_start,
cluster_start);
return false; return false;
} }
if (processors[i].package_leader_id != cluster_start) { if (processors[i].package_leader_id != cluster_start) {
cpuinfo_log_debug( cpuinfo_log_debug(
"heuristic detection of core clusters failed: " "heuristic detection of core clusters failed: "
"processor %"PRIu32" is expected to belong to the cluster of processor %"PRIu32", " "processor %" PRIu32
"but system siblings lists reported it to belong to the cluster of processor %"PRIu32, " is expected to belong to the cluster of processor %" PRIu32
i, cluster_start, cluster_start); ", "
"but system siblings lists reported it to belong to the cluster of processor %" PRIu32,
i,
cluster_start,
cluster_start);
return false; return false;
} }
} else { } else {
/* /*
* The cluster suggest by the heuristic was not parsed from system siblings lists. * The cluster suggest by the heuristic
* For all processors we expect in the cluster, check that: * was not parsed from system siblings
* - They have no pre-assigned cluster from siblings lists. * lists. For all processors we expect
* - If their min/max CPU frequency is known, it is the same. * in the cluster, check that:
* - If any part of their MIDR (Implementer, Variant, Part, Revision) is known, it is the same. * - They have no pre-assigned cluster
* from siblings lists.
* - If their min/max CPU frequency is
* known, it is the same.
* - If any part of their MIDR
* (Implementer, Variant, Part,
* Revision) is known, it is the same.
*/ */
if (processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER) { if (processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER) {
cpuinfo_log_debug( cpuinfo_log_debug(
"heuristic detection of core clusters failed: " "heuristic detection of core clusters failed: "
"processor %"PRIu32" is expected to be unassigned to any cluster, " "processor %" PRIu32
"but system siblings lists reported it to belong to the cluster of processor %"PRIu32, " is expected to be unassigned to any cluster, "
i, processors[i].package_leader_id); "but system siblings lists reported it to belong to the cluster of processor %" PRIu32,
i,
processors[i].package_leader_id);
return false; return false;
} }
@ -169,8 +213,13 @@ bool cpuinfo_arm_linux_detect_core_clusters_by_heuristic(
if (cluster_min_frequency != processors[i].min_frequency) { if (cluster_min_frequency != processors[i].min_frequency) {
cpuinfo_log_debug( cpuinfo_log_debug(
"heuristic detection of core clusters failed: " "heuristic detection of core clusters failed: "
"minimum frequency of processor %"PRIu32" (%"PRIu32" KHz) is different than of its expected cluster (%"PRIu32" KHz)", "minimum frequency of processor %" PRIu32
i, processors[i].min_frequency, cluster_min_frequency); " (%" PRIu32
" KHz) is different than of its expected cluster (%" PRIu32
" KHz)",
i,
processors[i].min_frequency,
cluster_min_frequency);
return false; return false;
} }
} else { } else {
@ -184,8 +233,13 @@ bool cpuinfo_arm_linux_detect_core_clusters_by_heuristic(
if (cluster_max_frequency != processors[i].max_frequency) { if (cluster_max_frequency != processors[i].max_frequency) {
cpuinfo_log_debug( cpuinfo_log_debug(
"heuristic detection of core clusters failed: " "heuristic detection of core clusters failed: "
"maximum frequency of processor %"PRIu32" (%"PRIu32" KHz) is different than of its expected cluster (%"PRIu32" KHz)", "maximum frequency of processor %" PRIu32
i, processors[i].max_frequency, cluster_max_frequency); " (%" PRIu32
" KHz) is different than of its expected cluster (%" PRIu32
" KHz)",
i,
processors[i].max_frequency,
cluster_max_frequency);
return false; return false;
} }
} else { } else {
@ -196,41 +250,61 @@ bool cpuinfo_arm_linux_detect_core_clusters_by_heuristic(
if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_IMPLEMENTER) { if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_IMPLEMENTER) {
if (cluster_flags & CPUINFO_ARM_LINUX_VALID_IMPLEMENTER) { if (cluster_flags & CPUINFO_ARM_LINUX_VALID_IMPLEMENTER) {
if ((cluster_midr & CPUINFO_ARM_MIDR_IMPLEMENTER_MASK) != (processors[i].midr & CPUINFO_ARM_MIDR_IMPLEMENTER_MASK)) { if ((cluster_midr & CPUINFO_ARM_MIDR_IMPLEMENTER_MASK) !=
(processors[i].midr & CPUINFO_ARM_MIDR_IMPLEMENTER_MASK)) {
cpuinfo_log_debug( cpuinfo_log_debug(
"heuristic detection of core clusters failed: " "heuristic detection of core clusters failed: "
"CPU Implementer of processor %"PRIu32" (0x%02"PRIx32") is different than of its expected cluster (0x%02"PRIx32")", "CPU Implementer of processor %" PRIu32
i, midr_get_implementer(processors[i].midr), midr_get_implementer(cluster_midr)); " (0x%02" PRIx32
") is different than of its expected cluster (0x%02" PRIx32
")",
i,
midr_get_implementer(processors[i].midr),
midr_get_implementer(cluster_midr));
return false; return false;
} }
} else { } else {
cluster_midr = midr_copy_implementer(cluster_midr, processors[i].midr); cluster_midr =
midr_copy_implementer(cluster_midr, processors[i].midr);
cluster_flags |= CPUINFO_ARM_LINUX_VALID_IMPLEMENTER; cluster_flags |= CPUINFO_ARM_LINUX_VALID_IMPLEMENTER;
} }
} }
if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_VARIANT) { if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_VARIANT) {
if (cluster_flags & CPUINFO_ARM_LINUX_VALID_VARIANT) { if (cluster_flags & CPUINFO_ARM_LINUX_VALID_VARIANT) {
if ((cluster_midr & CPUINFO_ARM_MIDR_VARIANT_MASK) != (processors[i].midr & CPUINFO_ARM_MIDR_VARIANT_MASK)) { if ((cluster_midr & CPUINFO_ARM_MIDR_VARIANT_MASK) !=
(processors[i].midr & CPUINFO_ARM_MIDR_VARIANT_MASK)) {
cpuinfo_log_debug( cpuinfo_log_debug(
"heuristic detection of core clusters failed: " "heuristic detection of core clusters failed: "
"CPU Variant of processor %"PRIu32" (0x%"PRIx32") is different than of its expected cluster (0x%"PRIx32")", "CPU Variant of processor %" PRIu32
i, midr_get_variant(processors[i].midr), midr_get_variant(cluster_midr)); " (0x%" PRIx32
") is different than of its expected cluster (0x%" PRIx32
")",
i,
midr_get_variant(processors[i].midr),
midr_get_variant(cluster_midr));
return false; return false;
} }
} else { } else {
cluster_midr = midr_copy_variant(cluster_midr, processors[i].midr); cluster_midr =
midr_copy_variant(cluster_midr, processors[i].midr);
cluster_flags |= CPUINFO_ARM_LINUX_VALID_VARIANT; cluster_flags |= CPUINFO_ARM_LINUX_VALID_VARIANT;
} }
} }
if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_PART) { if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_PART) {
if (cluster_flags & CPUINFO_ARM_LINUX_VALID_PART) { if (cluster_flags & CPUINFO_ARM_LINUX_VALID_PART) {
if ((cluster_midr & CPUINFO_ARM_MIDR_PART_MASK) != (processors[i].midr & CPUINFO_ARM_MIDR_PART_MASK)) { if ((cluster_midr & CPUINFO_ARM_MIDR_PART_MASK) !=
(processors[i].midr & CPUINFO_ARM_MIDR_PART_MASK)) {
cpuinfo_log_debug( cpuinfo_log_debug(
"heuristic detection of core clusters failed: " "heuristic detection of core clusters failed: "
"CPU Part of processor %"PRIu32" (0x%03"PRIx32") is different than of its expected cluster (0x%03"PRIx32")", "CPU Part of processor %" PRIu32
i, midr_get_part(processors[i].midr), midr_get_part(cluster_midr)); " (0x%03" PRIx32
") is different than of its expected cluster (0x%03" PRIx32
")",
i,
midr_get_part(processors[i].midr),
midr_get_part(cluster_midr));
return false; return false;
} }
} else { } else {
@ -241,15 +315,22 @@ bool cpuinfo_arm_linux_detect_core_clusters_by_heuristic(
if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_REVISION) { if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_REVISION) {
if (cluster_flags & CPUINFO_ARM_LINUX_VALID_REVISION) { if (cluster_flags & CPUINFO_ARM_LINUX_VALID_REVISION) {
if ((cluster_midr & CPUINFO_ARM_MIDR_REVISION_MASK) != (processors[i].midr & CPUINFO_ARM_MIDR_REVISION_MASK)) { if ((cluster_midr & CPUINFO_ARM_MIDR_REVISION_MASK) !=
(processors[i].midr & CPUINFO_ARM_MIDR_REVISION_MASK)) {
cpuinfo_log_debug( cpuinfo_log_debug(
"heuristic detection of core clusters failed: " "heuristic detection of core clusters failed: "
"CPU Revision of processor %"PRIu32" (0x%"PRIx32") is different than of its expected cluster (0x%"PRIx32")", "CPU Revision of processor %" PRIu32
i, midr_get_revision(cluster_midr), midr_get_revision(processors[i].midr)); " (0x%" PRIx32
") is different than of its expected cluster (0x%" PRIx32
")",
i,
midr_get_revision(cluster_midr),
midr_get_revision(processors[i].midr));
return false; return false;
} }
} else { } else {
cluster_midr = midr_copy_revision(cluster_midr, processors[i].midr); cluster_midr =
midr_copy_revision(cluster_midr, processors[i].midr);
cluster_flags |= CPUINFO_ARM_LINUX_VALID_REVISION; cluster_flags |= CPUINFO_ARM_LINUX_VALID_REVISION;
} }
} }
@ -265,16 +346,21 @@ bool cpuinfo_arm_linux_detect_core_clusters_by_heuristic(
for (uint32_t i = 0; i < max_processors; i++) { for (uint32_t i = 0; i < max_processors; i++) {
if (bitmask_all(processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) { if (bitmask_all(processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
if (expected_cluster_processors == 0) { if (expected_cluster_processors == 0) {
/* Expect this processor to start a new cluster */ /* Expect this processor to start a new cluster
*/
cluster_start = i; cluster_start = i;
expected_cluster_processors = cluster_processors[cluster++]; expected_cluster_processors = cluster_processors[cluster++];
} else { } else {
/* Expect this processor to belong to the same cluster as processor */ /* Expect this processor to belong to the same
* cluster as processor */
if (!(processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER)) { if (!(processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER)) {
cpuinfo_log_debug("assigned processor %"PRIu32" to cluster of processor %"PRIu32" based on heuristic", cpuinfo_log_debug(
i, cluster_start); "assigned processor %" PRIu32 " to cluster of processor %" PRIu32
" based on heuristic",
i,
cluster_start);
} }
processors[i].package_leader_id = cluster_start; processors[i].package_leader_id = cluster_start;
@ -291,38 +377,49 @@ bool cpuinfo_arm_linux_detect_core_clusters_by_heuristic(
* - Clusters detected from OS-provided information are unchanged: * - Clusters detected from OS-provided information are unchanged:
* - Processors assigned to these clusters stay assigned to the same clusters * - Processors assigned to these clusters stay assigned to the same clusters
* - No new processors are added to these clusters * - No new processors are added to these clusters
* - Processors without pre-assigned cluster are clustered in one sequential scan: * - Processors without pre-assigned cluster are clustered in one sequential
* - If known details (min/max frequency, MIDR components) of a processor are compatible with a preceding * scan:
* processor, without pre-assigned cluster, the processor is assigned to the cluster of the preceding processor. * - If known details (min/max frequency, MIDR components) of a processor are
* - If known details (min/max frequency, MIDR components) of a processor are not compatible with a preceding * compatible with a preceding processor, without pre-assigned cluster, the
* processor, the processor is assigned to a newly created cluster. * processor is assigned to the cluster of the preceding processor.
* - If known details (min/max frequency, MIDR components) of a processor are
* not compatible with a preceding processor, the processor is assigned to a
* newly created cluster.
* *
* The function must be called after parsing OS-provided information on core clusters, and usually is called only * The function must be called after parsing OS-provided information on core
* if heuristic assignment of processors to clusters (cpuinfo_arm_linux_cluster_processors_by_heuristic) failed. * clusters, and usually is called only if heuristic assignment of processors to
* clusters (cpuinfo_arm_linux_cluster_processors_by_heuristic) failed.
* *
* Its purpose is to detect clusters of cores when OS-provided information is lacking or incomplete, i.e. * Its purpose is to detect clusters of cores when OS-provided information is
* - Linux kernel is not configured to report information in sysfs topology leaf. * lacking or incomplete, i.e.
* - Linux kernel reports topology information only for online cores, and all cores on some of the clusters are offline. * - Linux kernel is not configured to report information in sysfs topology
* leaf.
* - Linux kernel reports topology information only for online cores, and all
* cores on some of the clusters are offline.
* *
* Sequential assignment of processors to clusters always succeeds, and upon exit, all usable processors in the * Sequential assignment of processors to clusters always succeeds, and upon
* exit, all usable processors in the
* @p processors array have cluster information. * @p processors array have cluster information.
* *
* @param max_processors - number of elements in the @p processors array. * @param max_processors - number of elements in the @p processors array.
* @param[in,out] processors - processor descriptors with pre-parsed POSSIBLE and PRESENT flags, minimum/maximum * @param[in,out] processors - processor descriptors with pre-parsed POSSIBLE
* frequency, MIDR information, and core cluster (package siblings list) information. * and PRESENT flags, minimum/maximum frequency, MIDR information, and core
* cluster (package siblings list) information.
* *
* @retval true if the heuristic successfully assigned all processors into clusters of cores. * @retval true if the heuristic successfully assigned all processors into
* @retval false if known details about processors contradict the heuristic configuration of core clusters. * clusters of cores.
* @retval false if known details about processors contradict the heuristic
* configuration of core clusters.
*/ */
void cpuinfo_arm_linux_detect_core_clusters_by_sequential_scan( void cpuinfo_arm_linux_detect_core_clusters_by_sequential_scan(
uint32_t max_processors, uint32_t max_processors,
struct cpuinfo_arm_linux_processor processors[restrict static max_processors]) struct cpuinfo_arm_linux_processor processors[restrict static max_processors]) {
{
uint32_t cluster_flags = 0; uint32_t cluster_flags = 0;
uint32_t cluster_processors = 0; uint32_t cluster_processors = 0;
uint32_t cluster_start, cluster_midr, cluster_max_frequency, cluster_min_frequency; uint32_t cluster_start, cluster_midr, cluster_max_frequency, cluster_min_frequency;
for (uint32_t i = 0; i < max_processors; i++) { for (uint32_t i = 0; i < max_processors; i++) {
if ((processors[i].flags & (CPUINFO_LINUX_FLAG_VALID | CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER)) == CPUINFO_LINUX_FLAG_VALID) { if ((processors[i].flags & (CPUINFO_LINUX_FLAG_VALID | CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER)) ==
CPUINFO_LINUX_FLAG_VALID) {
if (cluster_processors == 0) { if (cluster_processors == 0) {
goto new_cluster; goto new_cluster;
} }
@ -331,9 +428,14 @@ void cpuinfo_arm_linux_detect_core_clusters_by_sequential_scan(
if (cluster_flags & CPUINFO_LINUX_FLAG_MIN_FREQUENCY) { if (cluster_flags & CPUINFO_LINUX_FLAG_MIN_FREQUENCY) {
if (cluster_min_frequency != processors[i].min_frequency) { if (cluster_min_frequency != processors[i].min_frequency) {
cpuinfo_log_info( cpuinfo_log_info(
"minimum frequency of processor %"PRIu32" (%"PRIu32" KHz) is different than of preceding cluster (%"PRIu32" KHz); " "minimum frequency of processor %" PRIu32 " (%" PRIu32
"processor %"PRIu32" starts to a new cluster", " KHz) is different than of preceding cluster (%" PRIu32
i, processors[i].min_frequency, cluster_min_frequency, i); " KHz); "
"processor %" PRIu32 " starts to a new cluster",
i,
processors[i].min_frequency,
cluster_min_frequency,
i);
goto new_cluster; goto new_cluster;
} }
} else { } else {
@ -346,9 +448,14 @@ void cpuinfo_arm_linux_detect_core_clusters_by_sequential_scan(
if (cluster_flags & CPUINFO_LINUX_FLAG_MAX_FREQUENCY) { if (cluster_flags & CPUINFO_LINUX_FLAG_MAX_FREQUENCY) {
if (cluster_max_frequency != processors[i].max_frequency) { if (cluster_max_frequency != processors[i].max_frequency) {
cpuinfo_log_debug( cpuinfo_log_debug(
"maximum frequency of processor %"PRIu32" (%"PRIu32" KHz) is different than of preceding cluster (%"PRIu32" KHz); " "maximum frequency of processor %" PRIu32 " (%" PRIu32
"processor %"PRIu32" starts a new cluster", " KHz) is different than of preceding cluster (%" PRIu32
i, processors[i].max_frequency, cluster_max_frequency, i); " KHz); "
"processor %" PRIu32 " starts a new cluster",
i,
processors[i].max_frequency,
cluster_max_frequency,
i);
goto new_cluster; goto new_cluster;
} }
} else { } else {
@ -359,11 +466,17 @@ void cpuinfo_arm_linux_detect_core_clusters_by_sequential_scan(
if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_IMPLEMENTER) { if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_IMPLEMENTER) {
if (cluster_flags & CPUINFO_ARM_LINUX_VALID_IMPLEMENTER) { if (cluster_flags & CPUINFO_ARM_LINUX_VALID_IMPLEMENTER) {
if ((cluster_midr & CPUINFO_ARM_MIDR_IMPLEMENTER_MASK) != (processors[i].midr & CPUINFO_ARM_MIDR_IMPLEMENTER_MASK)) { if ((cluster_midr & CPUINFO_ARM_MIDR_IMPLEMENTER_MASK) !=
(processors[i].midr & CPUINFO_ARM_MIDR_IMPLEMENTER_MASK)) {
cpuinfo_log_debug( cpuinfo_log_debug(
"CPU Implementer of processor %"PRIu32" (0x%02"PRIx32") is different than of preceding cluster (0x%02"PRIx32"); " "CPU Implementer of processor %" PRIu32 " (0x%02" PRIx32
"processor %"PRIu32" starts to a new cluster", ") is different than of preceding cluster (0x%02" PRIx32
i, midr_get_implementer(processors[i].midr), midr_get_implementer(cluster_midr), i); "); "
"processor %" PRIu32 " starts to a new cluster",
i,
midr_get_implementer(processors[i].midr),
midr_get_implementer(cluster_midr),
i);
goto new_cluster; goto new_cluster;
} }
} else { } else {
@ -374,11 +487,17 @@ void cpuinfo_arm_linux_detect_core_clusters_by_sequential_scan(
if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_VARIANT) { if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_VARIANT) {
if (cluster_flags & CPUINFO_ARM_LINUX_VALID_VARIANT) { if (cluster_flags & CPUINFO_ARM_LINUX_VALID_VARIANT) {
if ((cluster_midr & CPUINFO_ARM_MIDR_VARIANT_MASK) != (processors[i].midr & CPUINFO_ARM_MIDR_VARIANT_MASK)) { if ((cluster_midr & CPUINFO_ARM_MIDR_VARIANT_MASK) !=
(processors[i].midr & CPUINFO_ARM_MIDR_VARIANT_MASK)) {
cpuinfo_log_debug( cpuinfo_log_debug(
"CPU Variant of processor %"PRIu32" (0x%"PRIx32") is different than of its expected cluster (0x%"PRIx32")" "CPU Variant of processor %" PRIu32 " (0x%" PRIx32
"processor %"PRIu32" starts to a new cluster", ") is different than of its expected cluster (0x%" PRIx32
i, midr_get_variant(processors[i].midr), midr_get_variant(cluster_midr), i); ")"
"processor %" PRIu32 " starts to a new cluster",
i,
midr_get_variant(processors[i].midr),
midr_get_variant(cluster_midr),
i);
goto new_cluster; goto new_cluster;
} }
} else { } else {
@ -389,11 +508,17 @@ void cpuinfo_arm_linux_detect_core_clusters_by_sequential_scan(
if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_PART) { if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_PART) {
if (cluster_flags & CPUINFO_ARM_LINUX_VALID_PART) { if (cluster_flags & CPUINFO_ARM_LINUX_VALID_PART) {
if ((cluster_midr & CPUINFO_ARM_MIDR_PART_MASK) != (processors[i].midr & CPUINFO_ARM_MIDR_PART_MASK)) { if ((cluster_midr & CPUINFO_ARM_MIDR_PART_MASK) !=
(processors[i].midr & CPUINFO_ARM_MIDR_PART_MASK)) {
cpuinfo_log_debug( cpuinfo_log_debug(
"CPU Part of processor %"PRIu32" (0x%03"PRIx32") is different than of its expected cluster (0x%03"PRIx32")" "CPU Part of processor %" PRIu32 " (0x%03" PRIx32
"processor %"PRIu32" starts to a new cluster", ") is different than of its expected cluster (0x%03" PRIx32
i, midr_get_part(processors[i].midr), midr_get_part(cluster_midr), i); ")"
"processor %" PRIu32 " starts to a new cluster",
i,
midr_get_part(processors[i].midr),
midr_get_part(cluster_midr),
i);
goto new_cluster; goto new_cluster;
} }
} else { } else {
@ -404,11 +529,17 @@ void cpuinfo_arm_linux_detect_core_clusters_by_sequential_scan(
if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_REVISION) { if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_REVISION) {
if (cluster_flags & CPUINFO_ARM_LINUX_VALID_REVISION) { if (cluster_flags & CPUINFO_ARM_LINUX_VALID_REVISION) {
if ((cluster_midr & CPUINFO_ARM_MIDR_REVISION_MASK) != (processors[i].midr & CPUINFO_ARM_MIDR_REVISION_MASK)) { if ((cluster_midr & CPUINFO_ARM_MIDR_REVISION_MASK) !=
(processors[i].midr & CPUINFO_ARM_MIDR_REVISION_MASK)) {
cpuinfo_log_debug( cpuinfo_log_debug(
"CPU Revision of processor %"PRIu32" (0x%"PRIx32") is different than of its expected cluster (0x%"PRIx32")" "CPU Revision of processor %" PRIu32 " (0x%" PRIx32
"processor %"PRIu32" starts to a new cluster", ") is different than of its expected cluster (0x%" PRIx32
i, midr_get_revision(cluster_midr), midr_get_revision(processors[i].midr), i); ")"
"processor %" PRIu32 " starts to a new cluster",
i,
midr_get_revision(cluster_midr),
midr_get_revision(processors[i].midr),
i);
goto new_cluster; goto new_cluster;
} }
} else { } else {
@ -417,21 +548,26 @@ void cpuinfo_arm_linux_detect_core_clusters_by_sequential_scan(
} }
} }
/* All checks passed, attach processor to the preceding cluster */ /* All checks passed, attach processor to the preceding
* cluster */
cluster_processors++; cluster_processors++;
processors[i].package_leader_id = cluster_start; processors[i].package_leader_id = cluster_start;
processors[i].flags |= CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER; processors[i].flags |= CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER;
cpuinfo_log_debug("assigned processor %"PRIu32" to preceding cluster of processor %"PRIu32, i, cluster_start); cpuinfo_log_debug(
"assigned processor %" PRIu32 " to preceding cluster of processor %" PRIu32,
i,
cluster_start);
continue; continue;
new_cluster: new_cluster:
/* Create a new cluster starting with processor i */ /* Create a new cluster starting with processor i */
cluster_start = i; cluster_start = i;
processors[i].package_leader_id = i; processors[i].package_leader_id = i;
processors[i].flags |= CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER; processors[i].flags |= CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER;
cluster_processors = 1; cluster_processors = 1;
/* Copy known information from processor to cluster, and set the flags accordingly */ /* Copy known information from processor to cluster, and
* set the flags accordingly */
cluster_flags = 0; cluster_flags = 0;
if (processors[i].flags & CPUINFO_LINUX_FLAG_MIN_FREQUENCY) { if (processors[i].flags & CPUINFO_LINUX_FLAG_MIN_FREQUENCY) {
cluster_min_frequency = processors[i].min_frequency; cluster_min_frequency = processors[i].min_frequency;
@ -463,27 +599,30 @@ new_cluster:
/* /*
* Counts the number of logical processors in each core cluster. * Counts the number of logical processors in each core cluster.
* This function should be called after all processors are assigned to core clusters. * This function should be called after all processors are assigned to core
* clusters.
* *
* @param max_processors - number of elements in the @p processors array. * @param max_processors - number of elements in the @p processors array.
* @param[in,out] processors - processor descriptors with pre-parsed POSSIBLE and PRESENT flags, * @param[in,out] processors - processor descriptors with pre-parsed POSSIBLE
* and decoded core cluster (package_leader_id) information. * and PRESENT flags, and decoded core cluster (package_leader_id) information.
* The function expects the value of processors[i].package_processor_count to be zero. * The function expects the value of
* Upon return, processors[i].package_processor_count will contain the number of logical * processors[i].package_processor_count to be zero. Upon return,
* processors[i].package_processor_count will contain the number of logical
* processors in the respective core cluster. * processors in the respective core cluster.
*/ */
void cpuinfo_arm_linux_count_cluster_processors( void cpuinfo_arm_linux_count_cluster_processors(
uint32_t max_processors, uint32_t max_processors,
struct cpuinfo_arm_linux_processor processors[restrict static max_processors]) struct cpuinfo_arm_linux_processor processors[restrict static max_processors]) {
{ /* First pass: accumulate the number of processors at the group leader's
/* First pass: accumulate the number of processors at the group leader's package_processor_count */ * package_processor_count */
for (uint32_t i = 0; i < max_processors; i++) { for (uint32_t i = 0; i < max_processors; i++) {
if (bitmask_all(processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) { if (bitmask_all(processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
const uint32_t package_leader_id = processors[i].package_leader_id; const uint32_t package_leader_id = processors[i].package_leader_id;
processors[package_leader_id].package_processor_count += 1; processors[package_leader_id].package_processor_count += 1;
} }
} }
/* Second pass: copy the package_processor_count from the group leader processor */ /* Second pass: copy the package_processor_count from the group leader
* processor */
for (uint32_t i = 0; i < max_processors; i++) { for (uint32_t i = 0; i < max_processors; i++) {
if (bitmask_all(processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) { if (bitmask_all(processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
const uint32_t package_leader_id = processors[i].package_leader_id; const uint32_t package_leader_id = processors[i].package_leader_id;

View File

@ -1,50 +1,49 @@
#include <stdint.h> #include <stdint.h>
#if CPUINFO_MOCK #if CPUINFO_MOCK
extern uint32_t cpuinfo_arm_fpsid; extern uint32_t cpuinfo_arm_fpsid;
extern uint32_t cpuinfo_arm_mvfr0; extern uint32_t cpuinfo_arm_mvfr0;
extern uint32_t cpuinfo_arm_wcid; extern uint32_t cpuinfo_arm_wcid;
static inline uint32_t read_fpsid(void) { static inline uint32_t read_fpsid(void) {
return cpuinfo_arm_fpsid; return cpuinfo_arm_fpsid;
} }
static inline uint32_t read_mvfr0(void) { static inline uint32_t read_mvfr0(void) {
return cpuinfo_arm_mvfr0; return cpuinfo_arm_mvfr0;
} }
static inline uint32_t read_wcid(void) { static inline uint32_t read_wcid(void) {
return cpuinfo_arm_wcid; return cpuinfo_arm_wcid;
} }
#else #else
#if !defined(__ARM_ARCH_7A__) && !defined(__ARM_ARCH_8A__) && !(defined(__ARM_ARCH) && (__ARM_ARCH >= 7)) #if !defined(__ARM_ARCH_7A__) && !defined(__ARM_ARCH_8A__) && !(defined(__ARM_ARCH) && (__ARM_ARCH >= 7))
/* /*
* CoProcessor 10 is inaccessible from user mode since ARMv7, * CoProcessor 10 is inaccessible from user mode since ARMv7,
* and clang refuses to compile inline assembly when targeting ARMv7+ * and clang refuses to compile inline assembly when targeting ARMv7+
*/ */
static inline uint32_t read_fpsid(void) { static inline uint32_t read_fpsid(void) {
uint32_t fpsid; uint32_t fpsid;
__asm__ __volatile__("MRC p10, 0x7, %[fpsid], cr0, cr0, 0" : [fpsid] "=r" (fpsid)); __asm__ __volatile__("MRC p10, 0x7, %[fpsid], cr0, cr0, 0" : [fpsid] "=r"(fpsid));
return fpsid; return fpsid;
} }
static inline uint32_t read_mvfr0(void) { static inline uint32_t read_mvfr0(void) {
uint32_t mvfr0; uint32_t mvfr0;
__asm__ __volatile__("MRC p10, 0x7, %[mvfr0], cr7, cr0, 0" : [mvfr0] "=r" (mvfr0)); __asm__ __volatile__("MRC p10, 0x7, %[mvfr0], cr7, cr0, 0" : [mvfr0] "=r"(mvfr0));
return mvfr0; return mvfr0;
} }
#endif #endif
#if !defined(__ARM_ARCH_8A__) && !(defined(__ARM_ARCH) && (__ARM_ARCH >= 8)) #if !defined(__ARM_ARCH_8A__) && !(defined(__ARM_ARCH) && (__ARM_ARCH >= 8))
/* /*
* In ARMv8, AArch32 state supports only conceptual coprocessors CP10, CP11, CP14, and CP15. * In ARMv8, AArch32 state supports only conceptual coprocessors CP10, CP11,
* AArch64 does not support the concept of coprocessors. * CP14, and CP15. AArch64 does not support the concept of coprocessors. and
* and clang refuses to compile inline assembly when targeting ARMv8+ * clang refuses to compile inline assembly when targeting ARMv8+
*/ */
static inline uint32_t read_wcid(void) { static inline uint32_t read_wcid(void) {
uint32_t wcid; uint32_t wcid;
__asm__ __volatile__("MRC p1, 0, %[wcid], c0, c0" : [wcid] "=r" (wcid)); __asm__ __volatile__("MRC p1, 0, %[wcid], c0, c0" : [wcid] "=r"(wcid));
return wcid; return wcid;
} }
#endif #endif
#endif #endif

View File

@ -1,26 +1,22 @@
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <stddef.h>
#include <string.h> #include <string.h>
#include <linux/api.h>
#include <arm/linux/api.h> #include <arm/linux/api.h>
#include <arm/midr.h> #include <arm/midr.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <linux/api.h>
/* /*
* Size, in chars, of the on-stack buffer used for parsing lines of /proc/cpuinfo. * Size, in chars, of the on-stack buffer used for parsing lines of
* This is also the limit on the length of a single line. * /proc/cpuinfo. This is also the limit on the length of a single line.
*/ */
#define BUFFER_SIZE 1024 #define BUFFER_SIZE 1024
static uint32_t parse_processor_number(const char* processor_start, const char* processor_end) {
static uint32_t parse_processor_number( const size_t processor_length = (size_t)(processor_end - processor_start);
const char* processor_start,
const char* processor_end)
{
const size_t processor_length = (size_t) (processor_end - processor_start);
if (processor_length == 0) { if (processor_length == 0) {
cpuinfo_log_warning("Processor number in /proc/cpuinfo is ignored: string is empty"); cpuinfo_log_warning("Processor number in /proc/cpuinfo is ignored: string is empty");
@ -29,10 +25,12 @@ static uint32_t parse_processor_number(
uint32_t processor_number = 0; uint32_t processor_number = 0;
for (const char* digit_ptr = processor_start; digit_ptr != processor_end; digit_ptr++) { for (const char* digit_ptr = processor_start; digit_ptr != processor_end; digit_ptr++) {
const uint32_t digit = (uint32_t) (*digit_ptr - '0'); const uint32_t digit = (uint32_t)(*digit_ptr - '0');
if (digit > 10) { if (digit > 10) {
cpuinfo_log_warning("non-decimal suffix %.*s in /proc/cpuinfo processor number is ignored", cpuinfo_log_warning(
(int) (processor_end - digit_ptr), digit_ptr); "non-decimal suffix %.*s in /proc/cpuinfo processor number is ignored",
(int)(processor_end - digit_ptr),
digit_ptr);
break; break;
} }
@ -45,42 +43,48 @@ static uint32_t parse_processor_number(
/* /*
* Full list of ARM features reported in /proc/cpuinfo: * Full list of ARM features reported in /proc/cpuinfo:
* *
* * swp - support for SWP instruction (deprecated in ARMv7, can be removed in future) * * swp - support for SWP instruction (deprecated in ARMv7, can be removed
* * half - support for half-word loads and stores. These instruction are part of ARMv4, *in future)
* so no need to check it on supported CPUs. * * half - support for half-word loads and stores. These instruction are
* * thumb - support for 16-bit Thumb instruction set. Note that BX instruction is detected *part of ARMv4, so no need to check it on supported CPUs.
* by ARMv4T architecture, not by this flag. * * thumb - support for 16-bit Thumb instruction set. Note that BX
* * 26bit - old CPUs merged 26-bit PC and program status register (flags) into 32-bit PC *instruction is detected by ARMv4T architecture, not by this flag.
* and had special instructions for working with packed PC. Now it is all deprecated. * * 26bit - old CPUs merged 26-bit PC and program status register (flags)
* * fastmult - most old ARM CPUs could only compute 2 bits of multiplication result per clock *into 32-bit PC and had special instructions for working with packed PC. Now it
* cycle, but CPUs with M suffix (e.g. ARM7TDMI) could compute 4 bits per cycle. *is all deprecated.
* Of course, now it makes no sense. * * fastmult - most old ARM CPUs could only compute 2 bits of
* * fpa - floating point accelerator available. On original ARM ABI all floating-point operations *multiplication result per clock cycle, but CPUs with M suffix (e.g. ARM7TDMI)
* generated FPA instructions. If FPA was not available, these instructions generated *could compute 4 bits per cycle. Of course, now it makes no sense.
* "illegal operation" interrupts, and the OS processed them by emulating the FPA instructions. * * fpa - floating point accelerator available. On original ARM ABI all
* Debian used this ABI before it switched to EABI. Now FPA is deprecated. *floating-point operations generated FPA instructions. If FPA was not
* * vfp - vector floating point instructions. Available on most modern CPUs (as part of VFPv3). *available, these instructions generated "illegal operation" interrupts, and
* Required by Android ARMv7A ABI and by Ubuntu on ARM. *the OS processed them by emulating the FPA instructions. Debian used this ABI
*before it switched to EABI. Now FPA is deprecated.
* * vfp - vector floating point instructions. Available on most modern
*CPUs (as part of VFPv3). Required by Android ARMv7A ABI and by Ubuntu on ARM.
* Note: there is no flag for VFPv2. * Note: there is no flag for VFPv2.
* * edsp - V5E instructions: saturating add/sub and 16-bit x 16-bit -> 32/64-bit multiplications. * * edsp - V5E instructions: saturating add/sub and 16-bit x 16-bit ->
* Required on Android, supported by all CPUs in production. *32/64-bit multiplications. Required on Android, supported by all CPUs in
*production.
* * java - Jazelle extension. Supported on most CPUs. * * java - Jazelle extension. Supported on most CPUs.
* * iwmmxt - Intel/Marvell Wireless MMX instructions. 64-bit integer SIMD. * * iwmmxt - Intel/Marvell Wireless MMX instructions. 64-bit integer SIMD.
* Supported on XScale (Since PXA270) and Sheeva (PJ1, PJ4) architectures. * Supported on XScale (Since PXA270) and Sheeva (PJ1, PJ4)
* Note that there is no flag for WMMX2 instructions. *architectures. Note that there is no flag for WMMX2 instructions.
* * crunch - Maverick Crunch instructions. Junk. * * crunch - Maverick Crunch instructions. Junk.
* * thumbee - ThumbEE instructions. Almost no documentation is available. * * thumbee - ThumbEE instructions. Almost no documentation is available.
* * neon - NEON instructions (aka Advanced SIMD). MVFR1 register gives more * * neon - NEON instructions (aka Advanced SIMD). MVFR1 register gives
* fine-grained information on particular supported features, but *more fine-grained information on particular supported features, but the Linux
* the Linux kernel exports only a single flag for all of them. *kernel exports only a single flag for all of them. According to ARMv7A docs it
* According to ARMv7A docs it also implies the availability of VFPv3 *also implies the availability of VFPv3 (with 32 double-precision registers
* (with 32 double-precision registers d0-d31). *d0-d31).
* * vfpv3 - VFPv3 instructions. Available on most modern CPUs. Augment VFPv2 by * * vfpv3 - VFPv3 instructions. Available on most modern CPUs. Augment
* conversion to/from integers and load constant instructions. *VFPv2 by conversion to/from integers and load constant instructions. Required
* Required by Android ARMv7A ABI and by Ubuntu on ARM. *by Android ARMv7A ABI and by Ubuntu on ARM.
* * vfpv3d16 - VFPv3 instructions with only 16 double-precision registers (d0-d15). * * vfpv3d16 - VFPv3 instructions with only 16 double-precision registers
*(d0-d15).
* * tls - software thread ID registers. * * tls - software thread ID registers.
* Used by kernel (and likely libc) for efficient implementation of TLS. * Used by kernel (and likely libc) for efficient implementation of
*TLS.
* * vfpv4 - fused multiply-add instructions. * * vfpv4 - fused multiply-add instructions.
* * idiva - DIV instructions available in ARM mode. * * idiva - DIV instructions available in ARM mode.
* * idivt - DIV instructions available in Thumb mode. * * idivt - DIV instructions available in Thumb mode.
@ -93,15 +97,15 @@ static uint32_t parse_processor_number(
* * sha2 - SHA2 instructions. * * sha2 - SHA2 instructions.
* * crc32 - CRC32 instructions. * * crc32 - CRC32 instructions.
* *
* /proc/cpuinfo on ARM is populated in file arch/arm/kernel/setup.c in Linux kernel * /proc/cpuinfo on ARM is populated in file arch/arm/kernel/setup.c in
* Note that some devices may use patched Linux kernels with different feature names. *Linux kernel Note that some devices may use patched Linux kernels with
* However, the names above were checked on a large number of /proc/cpuinfo listings. *different feature names. However, the names above were checked on a large
*number of /proc/cpuinfo listings.
*/ */
static void parse_features( static void parse_features(
const char* features_start, const char* features_start,
const char* features_end, const char* features_end,
struct cpuinfo_arm_linux_processor processor[restrict static 1]) struct cpuinfo_arm_linux_processor processor[restrict static 1]) {
{
const char* feature_start = features_start; const char* feature_start = features_start;
const char* feature_end; const char* feature_end;
@ -115,7 +119,7 @@ static void parse_features(
break; break;
} }
} }
const size_t feature_length = (size_t) (feature_end - feature_start); const size_t feature_length = (size_t)(feature_end - feature_start);
switch (feature_length) { switch (feature_length) {
case 2: case 2:
@ -126,8 +130,9 @@ static void parse_features(
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
} else if (memcmp(feature_start, "wp", feature_length) == 0) { } else if (memcmp(feature_start, "wp", feature_length) == 0) {
/* /*
* Some AArch64 kernels, including the one on Nexus 5X, * Some AArch64 kernels, including the
* erroneously report "swp" as "wp" to AArch32 programs * one on Nexus 5X, erroneously report
* "swp" as "wp" to AArch32 programs
*/ */
processor->features |= CPUINFO_ARM_LINUX_FEATURE_SWP; processor->features |= CPUINFO_ARM_LINUX_FEATURE_SWP;
#endif #endif
@ -137,11 +142,11 @@ static void parse_features(
break; break;
case 3: case 3:
if (memcmp(feature_start, "aes", feature_length) == 0) { if (memcmp(feature_start, "aes", feature_length) == 0) {
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_AES; processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_AES;
#elif CPUINFO_ARCH_ARM64 #elif CPUINFO_ARCH_ARM64
processor->features |= CPUINFO_ARM_LINUX_FEATURE_AES; processor->features |= CPUINFO_ARM_LINUX_FEATURE_AES;
#endif #endif
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
} else if (memcmp(feature_start, "swp", feature_length) == 0) { } else if (memcmp(feature_start, "swp", feature_length) == 0) {
processor->features |= CPUINFO_ARM_LINUX_FEATURE_SWP; processor->features |= CPUINFO_ARM_LINUX_FEATURE_SWP;
@ -158,29 +163,29 @@ static void parse_features(
break; break;
case 4: case 4:
if (memcmp(feature_start, "sha1", feature_length) == 0) { if (memcmp(feature_start, "sha1", feature_length) == 0) {
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_SHA1; processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_SHA1;
#elif CPUINFO_ARCH_ARM64 #elif CPUINFO_ARCH_ARM64
processor->features |= CPUINFO_ARM_LINUX_FEATURE_SHA1; processor->features |= CPUINFO_ARM_LINUX_FEATURE_SHA1;
#endif #endif
} else if (memcmp(feature_start, "sha2", feature_length) == 0) { } else if (memcmp(feature_start, "sha2", feature_length) == 0) {
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_SHA2; processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_SHA2;
#elif CPUINFO_ARCH_ARM64 #elif CPUINFO_ARCH_ARM64
processor->features |= CPUINFO_ARM_LINUX_FEATURE_SHA2; processor->features |= CPUINFO_ARM_LINUX_FEATURE_SHA2;
#endif #endif
} else if (memcmp(feature_start, "fphp", feature_length) == 0) { } else if (memcmp(feature_start, "fphp", feature_length) == 0) {
#if CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM64
processor->features |= CPUINFO_ARM_LINUX_FEATURE_FPHP; processor->features |= CPUINFO_ARM_LINUX_FEATURE_FPHP;
#endif #endif
} else if (memcmp(feature_start, "fcma", feature_length) == 0) { } else if (memcmp(feature_start, "fcma", feature_length) == 0) {
#if CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM64
processor->features |= CPUINFO_ARM_LINUX_FEATURE_FCMA; processor->features |= CPUINFO_ARM_LINUX_FEATURE_FCMA;
#endif #endif
} else if (memcmp(feature_start, "i8mm", feature_length) == 0) { } else if (memcmp(feature_start, "i8mm", feature_length) == 0) {
#if CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM64
processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_I8MM; processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_I8MM;
#endif #endif
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
} else if (memcmp(feature_start, "half", feature_length) == 0) { } else if (memcmp(feature_start, "half", feature_length) == 0) {
processor->features |= CPUINFO_ARM_LINUX_FEATURE_HALF; processor->features |= CPUINFO_ARM_LINUX_FEATURE_HALF;
@ -194,8 +199,9 @@ static void parse_features(
processor->features |= CPUINFO_ARM_LINUX_FEATURE_LPAE; processor->features |= CPUINFO_ARM_LINUX_FEATURE_LPAE;
} else if (memcmp(feature_start, "tlsi", feature_length) == 0) { } else if (memcmp(feature_start, "tlsi", feature_length) == 0) {
/* /*
* Some AArch64 kernels, including the one on Nexus 5X, * Some AArch64 kernels, including the
* erroneously report "tls" as "tlsi" to AArch32 programs * one on Nexus 5X, erroneously report
* "tls" as "tlsi" to AArch32 programs
*/ */
processor->features |= CPUINFO_ARM_LINUX_FEATURE_TLS; processor->features |= CPUINFO_ARM_LINUX_FEATURE_TLS;
#endif /* CPUINFO_ARCH_ARM */ #endif /* CPUINFO_ARCH_ARM */
@ -205,33 +211,33 @@ static void parse_features(
break; break;
case 5: case 5:
if (memcmp(feature_start, "pmull", feature_length) == 0) { if (memcmp(feature_start, "pmull", feature_length) == 0) {
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_PMULL; processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_PMULL;
#elif CPUINFO_ARCH_ARM64 #elif CPUINFO_ARCH_ARM64
processor->features |= CPUINFO_ARM_LINUX_FEATURE_PMULL; processor->features |= CPUINFO_ARM_LINUX_FEATURE_PMULL;
#endif #endif
} else if (memcmp(feature_start, "crc32", feature_length) == 0) { } else if (memcmp(feature_start, "crc32", feature_length) == 0) {
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_CRC32; processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_CRC32;
#elif CPUINFO_ARCH_ARM64 #elif CPUINFO_ARCH_ARM64
processor->features |= CPUINFO_ARM_LINUX_FEATURE_CRC32; processor->features |= CPUINFO_ARM_LINUX_FEATURE_CRC32;
#endif #endif
} else if (memcmp(feature_start, "asimd", feature_length) == 0) { } else if (memcmp(feature_start, "asimd", feature_length) == 0) {
#if CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM64
processor->features |= CPUINFO_ARM_LINUX_FEATURE_ASIMD; processor->features |= CPUINFO_ARM_LINUX_FEATURE_ASIMD;
#endif #endif
} else if (memcmp(feature_start, "cpuid", feature_length) == 0) { } else if (memcmp(feature_start, "cpuid", feature_length) == 0) {
#if CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM64
processor->features |= CPUINFO_ARM_LINUX_FEATURE_CPUID; processor->features |= CPUINFO_ARM_LINUX_FEATURE_CPUID;
#endif #endif
} else if (memcmp(feature_start, "jscvt", feature_length) == 0) { } else if (memcmp(feature_start, "jscvt", feature_length) == 0) {
#if CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM64
processor->features |= CPUINFO_ARM_LINUX_FEATURE_JSCVT; processor->features |= CPUINFO_ARM_LINUX_FEATURE_JSCVT;
#endif #endif
} else if (memcmp(feature_start, "lrcpc", feature_length) == 0) { } else if (memcmp(feature_start, "lrcpc", feature_length) == 0) {
#if CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM64
processor->features |= CPUINFO_ARM_LINUX_FEATURE_LRCPC; processor->features |= CPUINFO_ARM_LINUX_FEATURE_LRCPC;
#endif #endif
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
} else if (memcmp(feature_start, "thumb", feature_length) == 0) { } else if (memcmp(feature_start, "thumb", feature_length) == 0) {
processor->features |= CPUINFO_ARM_LINUX_FEATURE_THUMB; processor->features |= CPUINFO_ARM_LINUX_FEATURE_THUMB;
@ -249,7 +255,7 @@ static void parse_features(
} else { } else {
goto unexpected; goto unexpected;
} }
break; break;
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
case 6: case 6:
if (memcmp(feature_start, "iwmmxt", feature_length) == 0) { if (memcmp(feature_start, "iwmmxt", feature_length) == 0) {
@ -267,13 +273,13 @@ static void parse_features(
if (memcmp(feature_start, "evtstrm", feature_length) == 0) { if (memcmp(feature_start, "evtstrm", feature_length) == 0) {
processor->features |= CPUINFO_ARM_LINUX_FEATURE_EVTSTRM; processor->features |= CPUINFO_ARM_LINUX_FEATURE_EVTSTRM;
} else if (memcmp(feature_start, "atomics", feature_length) == 0) { } else if (memcmp(feature_start, "atomics", feature_length) == 0) {
#if CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM64
processor->features |= CPUINFO_ARM_LINUX_FEATURE_ATOMICS; processor->features |= CPUINFO_ARM_LINUX_FEATURE_ATOMICS;
#endif #endif
} else if (memcmp(feature_start, "asimdhp", feature_length) == 0) { } else if (memcmp(feature_start, "asimdhp", feature_length) == 0) {
#if CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM64
processor->features |= CPUINFO_ARM_LINUX_FEATURE_ASIMDHP; processor->features |= CPUINFO_ARM_LINUX_FEATURE_ASIMDHP;
#endif #endif
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
} else if (memcmp(feature_start, "thumbee", feature_length) == 0) { } else if (memcmp(feature_start, "thumbee", feature_length) == 0) {
processor->features |= CPUINFO_ARM_LINUX_FEATURE_THUMBEE; processor->features |= CPUINFO_ARM_LINUX_FEATURE_THUMBEE;
@ -284,13 +290,13 @@ static void parse_features(
break; break;
case 8: case 8:
if (memcmp(feature_start, "asimdrdm", feature_length) == 0) { if (memcmp(feature_start, "asimdrdm", feature_length) == 0) {
#if CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM64
processor->features |= CPUINFO_ARM_LINUX_FEATURE_ASIMDRDM; processor->features |= CPUINFO_ARM_LINUX_FEATURE_ASIMDRDM;
#endif #endif
} else if (memcmp(feature_start, "asimdfhm", feature_length) == 0) { } else if (memcmp(feature_start, "asimdfhm", feature_length) == 0) {
#if CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM64
processor->features |= CPUINFO_ARM_LINUX_FEATURE_ASIMDFHM; processor->features |= CPUINFO_ARM_LINUX_FEATURE_ASIMDFHM;
#endif #endif
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
} else if (memcmp(feature_start, "fastmult", feature_length) == 0) { } else if (memcmp(feature_start, "fastmult", feature_length) == 0) {
processor->features |= CPUINFO_ARM_LINUX_FEATURE_FASTMULT; processor->features |= CPUINFO_ARM_LINUX_FEATURE_FASTMULT;
@ -303,8 +309,10 @@ static void parse_features(
break; break;
default: default:
unexpected: unexpected:
cpuinfo_log_warning("unexpected /proc/cpuinfo feature \"%.*s\" is ignored", cpuinfo_log_warning(
(int) feature_length, feature_start); "unexpected /proc/cpuinfo feature \"%.*s\" is ignored",
(int)feature_length,
feature_start);
break; break;
} }
feature_start = feature_end; feature_start = feature_end;
@ -319,10 +327,10 @@ static void parse_features(
static void parse_cpu_architecture( static void parse_cpu_architecture(
const char* cpu_architecture_start, const char* cpu_architecture_start,
const char* cpu_architecture_end, const char* cpu_architecture_end,
struct cpuinfo_arm_linux_processor processor[restrict static 1]) struct cpuinfo_arm_linux_processor processor[restrict static 1]) {
{ const size_t cpu_architecture_length = (size_t)(cpu_architecture_end - cpu_architecture_start);
const size_t cpu_architecture_length = (size_t) (cpu_architecture_end - cpu_architecture_start); /* Early AArch64 kernels report "CPU architecture: AArch64" instead of a
/* Early AArch64 kernels report "CPU architecture: AArch64" instead of a numeric value 8 */ * numeric value 8 */
if (cpu_architecture_length == 7) { if (cpu_architecture_length == 7) {
if (memcmp(cpu_architecture_start, "AArch64", cpu_architecture_length) == 0) { if (memcmp(cpu_architecture_start, "AArch64", cpu_architecture_length) == 0) {
processor->midr = midr_set_architecture(processor->midr, UINT32_C(0xF)); processor->midr = midr_set_architecture(processor->midr, UINT32_C(0xF));
@ -332,7 +340,6 @@ static void parse_cpu_architecture(
} }
} }
uint32_t architecture = 0; uint32_t architecture = 0;
const char* cpu_architecture_ptr = cpu_architecture_start; const char* cpu_architecture_ptr = cpu_architecture_start;
for (; cpu_architecture_ptr != cpu_architecture_end; cpu_architecture_ptr++) { for (; cpu_architecture_ptr != cpu_architecture_end; cpu_architecture_ptr++) {
@ -347,8 +354,10 @@ static void parse_cpu_architecture(
} }
if (cpu_architecture_ptr == cpu_architecture_start) { if (cpu_architecture_ptr == cpu_architecture_start) {
cpuinfo_log_warning("CPU architecture %.*s in /proc/cpuinfo is ignored due to non-digit at the beginning of the string", cpuinfo_log_warning(
(int) cpu_architecture_length, cpu_architecture_start); "CPU architecture %.*s in /proc/cpuinfo is ignored due to non-digit at the beginning of the string",
(int)cpu_architecture_length,
cpu_architecture_start);
} else { } else {
if (architecture != 0) { if (architecture != 0) {
processor->architecture_version = architecture; processor->architecture_version = architecture;
@ -370,17 +379,22 @@ static void parse_cpu_architecture(
#endif /* CPUINFO_ARCH_ARM */ #endif /* CPUINFO_ARCH_ARM */
case ' ': case ' ':
case '\t': case '\t':
/* Ignore whitespace at the end */ /* Ignore whitespace at the end
*/
break; break;
default: default:
cpuinfo_log_warning("skipped unknown architectural feature '%c' for ARMv%"PRIu32, cpuinfo_log_warning(
feature, architecture); "skipped unknown architectural feature '%c' for ARMv%" PRIu32,
feature,
architecture);
break; break;
} }
} }
} else { } else {
cpuinfo_log_warning("CPU architecture %.*s in /proc/cpuinfo is ignored due to invalid value (0)", cpuinfo_log_warning(
(int) cpu_architecture_length, cpu_architecture_start); "CPU architecture %.*s in /proc/cpuinfo is ignored due to invalid value (0)",
(int)cpu_architecture_length,
cpu_architecture_start);
} }
} }
@ -391,9 +405,12 @@ static void parse_cpu_architecture(
midr_architecture = UINT32_C(0x7); /* ARMv6 */ midr_architecture = UINT32_C(0x7); /* ARMv6 */
break; break;
case 5: case 5:
if ((processor->architecture_flags & CPUINFO_ARM_LINUX_ARCH_TEJ) == CPUINFO_ARM_LINUX_ARCH_TEJ) { if ((processor->architecture_flags & CPUINFO_ARM_LINUX_ARCH_TEJ) ==
CPUINFO_ARM_LINUX_ARCH_TEJ) {
midr_architecture = UINT32_C(0x6); /* ARMv5TEJ */ midr_architecture = UINT32_C(0x6); /* ARMv5TEJ */
} else if ((processor->architecture_flags & CPUINFO_ARM_LINUX_ARCH_TE) == CPUINFO_ARM_LINUX_ARCH_TE) { } else if (
(processor->architecture_flags & CPUINFO_ARM_LINUX_ARCH_TE) ==
CPUINFO_ARM_LINUX_ARCH_TE) {
midr_architecture = UINT32_C(0x5); /* ARMv5TE */ midr_architecture = UINT32_C(0x5); /* ARMv5TE */
} else { } else {
midr_architecture = UINT32_C(0x4); /* ARMv5T */ midr_architecture = UINT32_C(0x4); /* ARMv5T */
@ -407,9 +424,8 @@ static void parse_cpu_architecture(
static void parse_cpu_part( static void parse_cpu_part(
const char* cpu_part_start, const char* cpu_part_start,
const char* cpu_part_end, const char* cpu_part_end,
struct cpuinfo_arm_linux_processor processor[restrict static 1]) struct cpuinfo_arm_linux_processor processor[restrict static 1]) {
{ const size_t cpu_part_length = (size_t)(cpu_part_end - cpu_part_start);
const size_t cpu_part_length = (size_t) (cpu_part_end - cpu_part_start);
/* /*
* CPU part should contain hex prefix (0x) and one to three hex digits. * CPU part should contain hex prefix (0x) and one to three hex digits.
@ -419,32 +435,42 @@ static void parse_cpu_part(
* Main ID Register (MIDR) assigns only a 12-bit value for CPU part. * Main ID Register (MIDR) assigns only a 12-bit value for CPU part.
*/ */
if (cpu_part_length < 3 || cpu_part_length > 5) { if (cpu_part_length < 3 || cpu_part_length > 5) {
cpuinfo_log_warning("CPU part %.*s in /proc/cpuinfo is ignored due to unexpected length (%zu)", cpuinfo_log_warning(
(int) cpu_part_length, cpu_part_start, cpu_part_length); "CPU part %.*s in /proc/cpuinfo is ignored due to unexpected length (%zu)",
(int)cpu_part_length,
cpu_part_start,
cpu_part_length);
return; return;
} }
/* Verify the presence of hex prefix */ /* Verify the presence of hex prefix */
if (cpu_part_start[0] != '0' || cpu_part_start[1] != 'x') { if (cpu_part_start[0] != '0' || cpu_part_start[1] != 'x') {
cpuinfo_log_warning("CPU part %.*s in /proc/cpuinfo is ignored due to lack of 0x prefix", cpuinfo_log_warning(
(int) cpu_part_length, cpu_part_start); "CPU part %.*s in /proc/cpuinfo is ignored due to lack of 0x prefix",
(int)cpu_part_length,
cpu_part_start);
return; return;
} }
/* Verify that characters after hex prefix are hexadecimal digits and decode them */ /* Verify that characters after hex prefix are hexadecimal digits and
* decode them */
uint32_t cpu_part = 0; uint32_t cpu_part = 0;
for (const char* digit_ptr = cpu_part_start + 2; digit_ptr != cpu_part_end; digit_ptr++) { for (const char* digit_ptr = cpu_part_start + 2; digit_ptr != cpu_part_end; digit_ptr++) {
const char digit_char = *digit_ptr; const char digit_char = *digit_ptr;
uint32_t digit; uint32_t digit;
if (digit_char >= '0' && digit_char <= '9') { if (digit_char >= '0' && digit_char <= '9') {
digit = digit_char - '0'; digit = digit_char - '0';
} else if ((uint32_t) (digit_char - 'A') < 6) { } else if ((uint32_t)(digit_char - 'A') < 6) {
digit = 10 + (digit_char - 'A'); digit = 10 + (digit_char - 'A');
} else if ((uint32_t) (digit_char - 'a') < 6) { } else if ((uint32_t)(digit_char - 'a') < 6) {
digit = 10 + (digit_char - 'a'); digit = 10 + (digit_char - 'a');
} else { } else {
cpuinfo_log_warning("CPU part %.*s in /proc/cpuinfo is ignored due to unexpected non-hex character %c at offset %zu", cpuinfo_log_warning(
(int) cpu_part_length, cpu_part_start, digit_char, (size_t) (digit_ptr - cpu_part_start)); "CPU part %.*s in /proc/cpuinfo is ignored due to unexpected non-hex character %c at offset %zu",
(int)cpu_part_length,
cpu_part_start,
digit_char,
(size_t)(digit_ptr - cpu_part_start));
return; return;
} }
cpu_part = cpu_part * 16 + digit; cpu_part = cpu_part * 16 + digit;
@ -457,8 +483,7 @@ static void parse_cpu_part(
static void parse_cpu_implementer( static void parse_cpu_implementer(
const char* cpu_implementer_start, const char* cpu_implementer_start,
const char* cpu_implementer_end, const char* cpu_implementer_end,
struct cpuinfo_arm_linux_processor processor[restrict static 1]) struct cpuinfo_arm_linux_processor processor[restrict static 1]) {
{
const size_t cpu_implementer_length = cpu_implementer_end - cpu_implementer_start; const size_t cpu_implementer_length = cpu_implementer_end - cpu_implementer_start;
/* /*
@ -466,39 +491,50 @@ static void parse_cpu_implementer(
* I have never seen single hex digit as a value of this field, * I have never seen single hex digit as a value of this field,
* but I don't think it is impossible in future. * but I don't think it is impossible in future.
* Value can not contain more than two hex digits since * Value can not contain more than two hex digits since
* Main ID Register (MIDR) assigns only an 8-bit value for CPU implementer. * Main ID Register (MIDR) assigns only an 8-bit value for CPU
* implementer.
*/ */
switch (cpu_implementer_length) { switch (cpu_implementer_length) {
case 3: case 3:
case 4: case 4:
break; break;
default: default:
cpuinfo_log_warning("CPU implementer %.*s in /proc/cpuinfo is ignored due to unexpected length (%zu)", cpuinfo_log_warning(
(int) cpu_implementer_length, cpu_implementer_start, cpu_implementer_length); "CPU implementer %.*s in /proc/cpuinfo is ignored due to unexpected length (%zu)",
return; (int)cpu_implementer_length,
cpu_implementer_start,
cpu_implementer_length);
return;
} }
/* Verify the presence of hex prefix */ /* Verify the presence of hex prefix */
if (cpu_implementer_start[0] != '0' || cpu_implementer_start[1] != 'x') { if (cpu_implementer_start[0] != '0' || cpu_implementer_start[1] != 'x') {
cpuinfo_log_warning("CPU implementer %.*s in /proc/cpuinfo is ignored due to lack of 0x prefix", cpuinfo_log_warning(
(int) cpu_implementer_length, cpu_implementer_start); "CPU implementer %.*s in /proc/cpuinfo is ignored due to lack of 0x prefix",
(int)cpu_implementer_length,
cpu_implementer_start);
return; return;
} }
/* Verify that characters after hex prefix are hexadecimal digits and decode them */ /* Verify that characters after hex prefix are hexadecimal digits and
* decode them */
uint32_t cpu_implementer = 0; uint32_t cpu_implementer = 0;
for (const char* digit_ptr = cpu_implementer_start + 2; digit_ptr != cpu_implementer_end; digit_ptr++) { for (const char* digit_ptr = cpu_implementer_start + 2; digit_ptr != cpu_implementer_end; digit_ptr++) {
const char digit_char = *digit_ptr; const char digit_char = *digit_ptr;
uint32_t digit; uint32_t digit;
if (digit_char >= '0' && digit_char <= '9') { if (digit_char >= '0' && digit_char <= '9') {
digit = digit_char - '0'; digit = digit_char - '0';
} else if ((uint32_t) (digit_char - 'A') < 6) { } else if ((uint32_t)(digit_char - 'A') < 6) {
digit = 10 + (digit_char - 'A'); digit = 10 + (digit_char - 'A');
} else if ((uint32_t) (digit_char - 'a') < 6) { } else if ((uint32_t)(digit_char - 'a') < 6) {
digit = 10 + (digit_char - 'a'); digit = 10 + (digit_char - 'a');
} else { } else {
cpuinfo_log_warning("CPU implementer %.*s in /proc/cpuinfo is ignored due to unexpected non-hex character '%c' at offset %zu", cpuinfo_log_warning(
(int) cpu_implementer_length, cpu_implementer_start, digit_char, (size_t) (digit_ptr - cpu_implementer_start)); "CPU implementer %.*s in /proc/cpuinfo is ignored due to unexpected non-hex character '%c' at offset %zu",
(int)cpu_implementer_length,
cpu_implementer_start,
digit_char,
(size_t)(digit_ptr - cpu_implementer_start));
return; return;
} }
cpu_implementer = cpu_implementer * 16 + digit; cpu_implementer = cpu_implementer * 16 + digit;
@ -511,8 +547,7 @@ static void parse_cpu_implementer(
static void parse_cpu_variant( static void parse_cpu_variant(
const char* cpu_variant_start, const char* cpu_variant_start,
const char* cpu_variant_end, const char* cpu_variant_end,
struct cpuinfo_arm_linux_processor processor[restrict static 1]) struct cpuinfo_arm_linux_processor processor[restrict static 1]) {
{
const size_t cpu_variant_length = cpu_variant_end - cpu_variant_start; const size_t cpu_variant_length = cpu_variant_end - cpu_variant_start;
/* /*
@ -521,30 +556,39 @@ static void parse_cpu_variant(
* Main ID Register (MIDR) assigns only a 4-bit value for CPU variant. * Main ID Register (MIDR) assigns only a 4-bit value for CPU variant.
*/ */
if (cpu_variant_length != 3) { if (cpu_variant_length != 3) {
cpuinfo_log_warning("CPU variant %.*s in /proc/cpuinfo is ignored due to unexpected length (%zu)", cpuinfo_log_warning(
(int) cpu_variant_length, cpu_variant_start, cpu_variant_length); "CPU variant %.*s in /proc/cpuinfo is ignored due to unexpected length (%zu)",
(int)cpu_variant_length,
cpu_variant_start,
cpu_variant_length);
return; return;
} }
/* Skip if there is no hex prefix (0x) */ /* Skip if there is no hex prefix (0x) */
if (cpu_variant_start[0] != '0' || cpu_variant_start[1] != 'x') { if (cpu_variant_start[0] != '0' || cpu_variant_start[1] != 'x') {
cpuinfo_log_warning("CPU variant %.*s in /proc/cpuinfo is ignored due to lack of 0x prefix", cpuinfo_log_warning(
(int) cpu_variant_length, cpu_variant_start); "CPU variant %.*s in /proc/cpuinfo is ignored due to lack of 0x prefix",
(int)cpu_variant_length,
cpu_variant_start);
return; return;
} }
/* Check if the value after hex prefix is indeed a hex digit and decode it. */ /* Check if the value after hex prefix is indeed a hex digit and decode
* it. */
const char digit_char = cpu_variant_start[2]; const char digit_char = cpu_variant_start[2];
uint32_t cpu_variant; uint32_t cpu_variant;
if ((uint32_t) (digit_char - '0') < 10) { if ((uint32_t)(digit_char - '0') < 10) {
cpu_variant = (uint32_t) (digit_char - '0'); cpu_variant = (uint32_t)(digit_char - '0');
} else if ((uint32_t) (digit_char - 'A') < 6) { } else if ((uint32_t)(digit_char - 'A') < 6) {
cpu_variant = 10 + (uint32_t) (digit_char - 'A'); cpu_variant = 10 + (uint32_t)(digit_char - 'A');
} else if ((uint32_t) (digit_char - 'a') < 6) { } else if ((uint32_t)(digit_char - 'a') < 6) {
cpu_variant = 10 + (uint32_t) (digit_char - 'a'); cpu_variant = 10 + (uint32_t)(digit_char - 'a');
} else { } else {
cpuinfo_log_warning("CPU variant %.*s in /proc/cpuinfo is ignored due to unexpected non-hex character '%c'", cpuinfo_log_warning(
(int) cpu_variant_length, cpu_variant_start, digit_char); "CPU variant %.*s in /proc/cpuinfo is ignored due to unexpected non-hex character '%c'",
(int)cpu_variant_length,
cpu_variant_start,
digit_char);
return; return;
} }
@ -555,17 +599,20 @@ static void parse_cpu_variant(
static void parse_cpu_revision( static void parse_cpu_revision(
const char* cpu_revision_start, const char* cpu_revision_start,
const char* cpu_revision_end, const char* cpu_revision_end,
struct cpuinfo_arm_linux_processor processor[restrict static 1]) struct cpuinfo_arm_linux_processor processor[restrict static 1]) {
{
uint32_t cpu_revision = 0; uint32_t cpu_revision = 0;
for (const char* digit_ptr = cpu_revision_start; digit_ptr != cpu_revision_end; digit_ptr++) { for (const char* digit_ptr = cpu_revision_start; digit_ptr != cpu_revision_end; digit_ptr++) {
const uint32_t digit = (uint32_t) (*digit_ptr - '0'); const uint32_t digit = (uint32_t)(*digit_ptr - '0');
/* Verify that the character in CPU revision is a decimal digit */ /* Verify that the character in CPU revision is a decimal digit
*/
if (digit >= 10) { if (digit >= 10) {
cpuinfo_log_warning("CPU revision %.*s in /proc/cpuinfo is ignored due to unexpected non-digit character '%c' at offset %zu", cpuinfo_log_warning(
(int) (cpu_revision_end - cpu_revision_start), cpu_revision_start, "CPU revision %.*s in /proc/cpuinfo is ignored due to unexpected non-digit character '%c' at offset %zu",
*digit_ptr, (size_t) (digit_ptr - cpu_revision_start)); (int)(cpu_revision_end - cpu_revision_start),
cpu_revision_start,
*digit_ptr,
(size_t)(digit_ptr - cpu_revision_start));
return; return;
} }
@ -598,15 +645,18 @@ static void parse_cache_number(
const char* number_name, const char* number_name,
uint32_t number_ptr[restrict static 1], uint32_t number_ptr[restrict static 1],
uint32_t flags[restrict static 1], uint32_t flags[restrict static 1],
uint32_t number_mask) uint32_t number_mask) {
{
uint32_t number = 0; uint32_t number = 0;
for (const char* digit_ptr = number_start; digit_ptr != number_end; digit_ptr++) { for (const char* digit_ptr = number_start; digit_ptr != number_end; digit_ptr++) {
const uint32_t digit = *digit_ptr - '0'; const uint32_t digit = *digit_ptr - '0';
if (digit >= 10) { if (digit >= 10) {
cpuinfo_log_warning("%s %.*s in /proc/cpuinfo is ignored due to unexpected non-digit character '%c' at offset %zu", cpuinfo_log_warning(
number_name, (int) (number_end - number_start), number_start, "%s %.*s in /proc/cpuinfo is ignored due to unexpected non-digit character '%c' at offset %zu",
*digit_ptr, (size_t) (digit_ptr - number_start)); number_name,
(int)(number_end - number_start),
number_start,
*digit_ptr,
(size_t)(digit_ptr - number_start));
return; return;
} }
@ -614,11 +664,15 @@ static void parse_cache_number(
} }
if (number == 0) { if (number == 0) {
cpuinfo_log_warning("%s %.*s in /proc/cpuinfo is ignored due to invalid value of zero reported by the kernel", cpuinfo_log_warning(
number_name, (int) (number_end - number_start), number_start); "%s %.*s in /proc/cpuinfo is ignored due to invalid value of zero reported by the kernel",
number_name,
(int)(number_end - number_start),
number_start);
} }
/* If the number specifies a cache line size, verify that is a reasonable power of 2 */ /* If the number specifies a cache line size, verify that is a
* reasonable power of 2 */
if (number_mask & CPUINFO_ARM_LINUX_VALID_CACHE_LINE) { if (number_mask & CPUINFO_ARM_LINUX_VALID_CACHE_LINE) {
switch (number) { switch (number) {
case 16: case 16:
@ -627,8 +681,11 @@ static void parse_cache_number(
case 128: case 128:
break; break;
default: default:
cpuinfo_log_warning("invalid %s %.*s is ignored: a value of 16, 32, 64, or 128 expected", cpuinfo_log_warning(
number_name, (int) (number_end - number_start), number_start); "invalid %s %.*s is ignored: a value of 16, 32, 64, or 128 expected",
number_name,
(int)(number_end - number_start),
number_start);
} }
} }
@ -658,12 +715,9 @@ struct proc_cpuinfo_parser_state {
* processor : 1 * processor : 1
* BogoMIPS : 1363.33 * BogoMIPS : 1363.33
* *
* Features : swp half thumb fastmult vfp edsp thumbee neon vfpv3 * Features : swp half thumb fastmult vfp edsp thumbee neon
* CPU implementer : 0x41 *vfpv3 CPU implementer : 0x41 CPU architecture: 7 CPU variant : 0x2 CPU
* CPU architecture: 7 *part : 0xc09 CPU revision : 10
* CPU variant : 0x2
* CPU part : 0xc09
* CPU revision : 10
* *
* Hardware : OMAP4 Panda board * Hardware : OMAP4 Panda board
* Revision : 0020 * Revision : 0020
@ -673,8 +727,7 @@ static bool parse_line(
const char* line_start, const char* line_start,
const char* line_end, const char* line_end,
struct proc_cpuinfo_parser_state state[restrict static 1], struct proc_cpuinfo_parser_state state[restrict static 1],
uint64_t line_number) uint64_t line_number) {
{
/* Empty line. Skip. */ /* Empty line. Skip. */
if (line_start == line_end) { if (line_start == line_end) {
return true; return true;
@ -689,8 +742,10 @@ static bool parse_line(
} }
/* Skip line if no ':' separator was found. */ /* Skip line if no ':' separator was found. */
if (separator == line_end) { if (separator == line_end) {
cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: key/value separator ':' not found", cpuinfo_log_debug(
(int) (line_end - line_start), line_start); "Line %.*s in /proc/cpuinfo is ignored: key/value separator ':' not found",
(int)(line_end - line_start),
line_start);
return true; return true;
} }
@ -703,8 +758,10 @@ static bool parse_line(
} }
/* Skip line if key contains nothing but spaces. */ /* Skip line if key contains nothing but spaces. */
if (key_end == line_start) { if (key_end == line_start) {
cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: key contains only spaces", cpuinfo_log_debug(
(int) (line_end - line_start), line_start); "Line %.*s in /proc/cpuinfo is ignored: key contains only spaces",
(int)(line_end - line_start),
line_start);
return true; return true;
} }
@ -717,8 +774,10 @@ static bool parse_line(
} }
/* Value part contains nothing but spaces. Skip line. */ /* Value part contains nothing but spaces. Skip line. */
if (value_start == line_end) { if (value_start == line_end) {
cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: value contains only spaces", cpuinfo_log_debug(
(int) (line_end - line_start), line_start); "Line %.*s in /proc/cpuinfo is ignored: value contains only spaces",
(int)(line_end - line_start),
line_start);
return true; return true;
} }
@ -730,10 +789,10 @@ static bool parse_line(
} }
} }
const uint32_t processor_index = state->processor_index; const uint32_t processor_index = state->processor_index;
const uint32_t max_processors_count = state->max_processors_count; const uint32_t max_processors_count = state->max_processors_count;
struct cpuinfo_arm_linux_processor* processors = state->processors; struct cpuinfo_arm_linux_processor* processors = state->processors;
struct cpuinfo_arm_linux_processor* processor = &state->dummy_processor; struct cpuinfo_arm_linux_processor* processor = &state->dummy_processor;
if (processor_index < max_processors_count) { if (processor_index < max_processors_count) {
processor = &processors[processor_index]; processor = &processors[processor_index];
} }
@ -745,21 +804,37 @@ static bool parse_line(
/* Usually contains just zeros, useless */ /* Usually contains just zeros, useless */
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
} else if (memcmp(line_start, "I size", key_length) == 0) { } else if (memcmp(line_start, "I size", key_length) == 0) {
parse_cache_number(value_start, value_end, parse_cache_number(
"instruction cache size", &processor->proc_cpuinfo_cache.i_size, value_start,
&processor->flags, CPUINFO_ARM_LINUX_VALID_ICACHE_SIZE); value_end,
"instruction cache size",
&processor->proc_cpuinfo_cache.i_size,
&processor->flags,
CPUINFO_ARM_LINUX_VALID_ICACHE_SIZE);
} else if (memcmp(line_start, "I sets", key_length) == 0) { } else if (memcmp(line_start, "I sets", key_length) == 0) {
parse_cache_number(value_start, value_end, parse_cache_number(
"instruction cache sets", &processor->proc_cpuinfo_cache.i_sets, value_start,
&processor->flags, CPUINFO_ARM_LINUX_VALID_ICACHE_SETS); value_end,
"instruction cache sets",
&processor->proc_cpuinfo_cache.i_sets,
&processor->flags,
CPUINFO_ARM_LINUX_VALID_ICACHE_SETS);
} else if (memcmp(line_start, "D size", key_length) == 0) { } else if (memcmp(line_start, "D size", key_length) == 0) {
parse_cache_number(value_start, value_end, parse_cache_number(
"data cache size", &processor->proc_cpuinfo_cache.d_size, value_start,
&processor->flags, CPUINFO_ARM_LINUX_VALID_DCACHE_SIZE); value_end,
"data cache size",
&processor->proc_cpuinfo_cache.d_size,
&processor->flags,
CPUINFO_ARM_LINUX_VALID_DCACHE_SIZE);
} else if (memcmp(line_start, "D sets", key_length) == 0) { } else if (memcmp(line_start, "D sets", key_length) == 0) {
parse_cache_number(value_start, value_end, parse_cache_number(
"data cache sets", &processor->proc_cpuinfo_cache.d_sets, value_start,
&processor->flags, CPUINFO_ARM_LINUX_VALID_DCACHE_SETS); value_end,
"data cache sets",
&processor->proc_cpuinfo_cache.d_sets,
&processor->flags,
CPUINFO_ARM_LINUX_VALID_DCACHE_SETS);
#endif /* CPUINFO_ARCH_ARM */ #endif /* CPUINFO_ARCH_ARM */
} else { } else {
goto unknown; goto unknown;
@ -768,13 +843,21 @@ static bool parse_line(
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
case 7: case 7:
if (memcmp(line_start, "I assoc", key_length) == 0) { if (memcmp(line_start, "I assoc", key_length) == 0) {
parse_cache_number(value_start, value_end, parse_cache_number(
"instruction cache associativity", &processor->proc_cpuinfo_cache.i_assoc, value_start,
&processor->flags, CPUINFO_ARM_LINUX_VALID_ICACHE_WAYS); value_end,
"instruction cache associativity",
&processor->proc_cpuinfo_cache.i_assoc,
&processor->flags,
CPUINFO_ARM_LINUX_VALID_ICACHE_WAYS);
} else if (memcmp(line_start, "D assoc", key_length) == 0) { } else if (memcmp(line_start, "D assoc", key_length) == 0) {
parse_cache_number(value_start, value_end, parse_cache_number(
"data cache associativity", &processor->proc_cpuinfo_cache.d_assoc, value_start,
&processor->flags, CPUINFO_ARM_LINUX_VALID_DCACHE_WAYS); value_end,
"data cache associativity",
&processor->proc_cpuinfo_cache.d_assoc,
&processor->flags,
CPUINFO_ARM_LINUX_VALID_DCACHE_WAYS);
} else { } else {
goto unknown; goto unknown;
} }
@ -790,27 +873,33 @@ static bool parse_line(
} else if (memcmp(line_start, "Hardware", key_length) == 0) { } else if (memcmp(line_start, "Hardware", key_length) == 0) {
size_t value_length = value_end - value_start; size_t value_length = value_end - value_start;
if (value_length > CPUINFO_HARDWARE_VALUE_MAX) { if (value_length > CPUINFO_HARDWARE_VALUE_MAX) {
cpuinfo_log_info( cpuinfo_log_warning(
"length of Hardware value \"%.*s\" in /proc/cpuinfo exceeds limit (%d): truncating to the limit", "length of Hardware value \"%.*s\" in /proc/cpuinfo exceeds limit (%d): truncating to the limit",
(int) value_length, value_start, CPUINFO_HARDWARE_VALUE_MAX); (int)value_length,
value_start,
CPUINFO_HARDWARE_VALUE_MAX);
value_length = CPUINFO_HARDWARE_VALUE_MAX; value_length = CPUINFO_HARDWARE_VALUE_MAX;
} else { } else {
state->hardware[value_length] = '\0'; state->hardware[value_length] = '\0';
} }
memcpy(state->hardware, value_start, value_length); memcpy(state->hardware, value_start, value_length);
cpuinfo_log_debug("parsed /proc/cpuinfo Hardware = \"%.*s\"", (int) value_length, value_start); cpuinfo_log_debug(
"parsed /proc/cpuinfo Hardware = \"%.*s\"", (int)value_length, value_start);
} else if (memcmp(line_start, "Revision", key_length) == 0) { } else if (memcmp(line_start, "Revision", key_length) == 0) {
size_t value_length = value_end - value_start; size_t value_length = value_end - value_start;
if (value_length > CPUINFO_REVISION_VALUE_MAX) { if (value_length > CPUINFO_REVISION_VALUE_MAX) {
cpuinfo_log_info( cpuinfo_log_warning(
"length of Revision value \"%.*s\" in /proc/cpuinfo exceeds limit (%d): truncating to the limit", "length of Revision value \"%.*s\" in /proc/cpuinfo exceeds limit (%d): truncating to the limit",
(int) value_length, value_start, CPUINFO_REVISION_VALUE_MAX); (int)value_length,
value_start,
CPUINFO_REVISION_VALUE_MAX);
value_length = CPUINFO_REVISION_VALUE_MAX; value_length = CPUINFO_REVISION_VALUE_MAX;
} else { } else {
state->revision[value_length] = '\0'; state->revision[value_length] = '\0';
} }
memcpy(state->revision, value_start, value_length); memcpy(state->revision, value_start, value_length);
cpuinfo_log_debug("parsed /proc/cpuinfo Revision = \"%.*s\"", (int) value_length, value_start); cpuinfo_log_debug(
"parsed /proc/cpuinfo Revision = \"%.*s\"", (int)value_length, value_start);
} else { } else {
goto unknown; goto unknown;
} }
@ -819,28 +908,39 @@ static bool parse_line(
if (memcmp(line_start, "processor", key_length) == 0) { if (memcmp(line_start, "processor", key_length) == 0) {
const uint32_t new_processor_index = parse_processor_number(value_start, value_end); const uint32_t new_processor_index = parse_processor_number(value_start, value_end);
if (new_processor_index < processor_index) { if (new_processor_index < processor_index) {
/* Strange: decreasing processor number */ /* Strange: decreasing processor number
*/
cpuinfo_log_warning( cpuinfo_log_warning(
"unexpectedly low processor number %"PRIu32" following processor %"PRIu32" in /proc/cpuinfo", "unexpectedly low processor number %" PRIu32
new_processor_index, processor_index); " following processor %" PRIu32 " in /proc/cpuinfo",
new_processor_index,
processor_index);
} else if (new_processor_index > processor_index + 1) { } else if (new_processor_index > processor_index + 1) {
/* Strange, but common: skipped processor $(processor_index + 1) */ /* Strange, but common: skipped
cpuinfo_log_info( * processor $(processor_index + 1) */
"unexpectedly high processor number %"PRIu32" following processor %"PRIu32" in /proc/cpuinfo", cpuinfo_log_warning(
new_processor_index, processor_index); "unexpectedly high processor number %" PRIu32
" following processor %" PRIu32 " in /proc/cpuinfo",
new_processor_index,
processor_index);
} }
if (new_processor_index < max_processors_count) { if (new_processor_index < max_processors_count) {
/* Record that the processor was mentioned in /proc/cpuinfo */ /* Record that the processor was
* mentioned in /proc/cpuinfo */
processors[new_processor_index].flags |= CPUINFO_ARM_LINUX_VALID_PROCESSOR; processors[new_processor_index].flags |= CPUINFO_ARM_LINUX_VALID_PROCESSOR;
} else { } else {
/* Log and ignore processor */ /* Log and ignore processor */
cpuinfo_log_warning("processor %"PRIu32" in /proc/cpuinfo is ignored: index exceeds system limit %"PRIu32, cpuinfo_log_warning(
new_processor_index, max_processors_count - 1); "processor %" PRIu32
" in /proc/cpuinfo is ignored: index exceeds system limit %" PRIu32,
new_processor_index,
max_processors_count - 1);
} }
state->processor_index = new_processor_index; state->processor_index = new_processor_index;
return true; return true;
} else if (memcmp(line_start, "Processor", key_length) == 0) { } else if (memcmp(line_start, "Processor", key_length) == 0) {
/* TODO: parse to fix misreported architecture, similar to Android's cpufeatures */ /* TODO: parse to fix misreported architecture,
* similar to Android's cpufeatures */
} else { } else {
goto unknown; goto unknown;
} }
@ -862,13 +962,21 @@ static bool parse_line(
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
case 13: case 13:
if (memcmp(line_start, "I line length", key_length) == 0) { if (memcmp(line_start, "I line length", key_length) == 0) {
parse_cache_number(value_start, value_end, parse_cache_number(
"instruction cache line size", &processor->proc_cpuinfo_cache.i_line_length, value_start,
&processor->flags, CPUINFO_ARM_LINUX_VALID_ICACHE_LINE); value_end,
"instruction cache line size",
&processor->proc_cpuinfo_cache.i_line_length,
&processor->flags,
CPUINFO_ARM_LINUX_VALID_ICACHE_LINE);
} else if (memcmp(line_start, "D line length", key_length) == 0) { } else if (memcmp(line_start, "D line length", key_length) == 0) {
parse_cache_number(value_start, value_end, parse_cache_number(
"data cache line size", &processor->proc_cpuinfo_cache.d_line_length, value_start,
&processor->flags, CPUINFO_ARM_LINUX_VALID_DCACHE_LINE); value_end,
"data cache line size",
&processor->proc_cpuinfo_cache.d_line_length,
&processor->flags,
CPUINFO_ARM_LINUX_VALID_DCACHE_LINE);
} else { } else {
goto unknown; goto unknown;
} }
@ -892,8 +1000,7 @@ static bool parse_line(
break; break;
default: default:
unknown: unknown:
cpuinfo_log_debug("unknown /proc/cpuinfo key: %.*s", (int) key_length, line_start); cpuinfo_log_debug("unknown /proc/cpuinfo key: %.*s", (int)key_length, line_start);
} }
return true; return true;
} }
@ -902,8 +1009,7 @@ bool cpuinfo_arm_linux_parse_proc_cpuinfo(
char hardware[restrict static CPUINFO_HARDWARE_VALUE_MAX], char hardware[restrict static CPUINFO_HARDWARE_VALUE_MAX],
char revision[restrict static CPUINFO_REVISION_VALUE_MAX], char revision[restrict static CPUINFO_REVISION_VALUE_MAX],
uint32_t max_processors_count, uint32_t max_processors_count,
struct cpuinfo_arm_linux_processor processors[restrict static max_processors_count]) struct cpuinfo_arm_linux_processor processors[restrict static max_processors_count]) {
{
hardware[0] = '\0'; hardware[0] = '\0';
struct proc_cpuinfo_parser_state state = { struct proc_cpuinfo_parser_state state = {
.hardware = hardware, .hardware = hardware,
@ -912,6 +1018,6 @@ bool cpuinfo_arm_linux_parse_proc_cpuinfo(
.max_processors_count = max_processors_count, .max_processors_count = max_processors_count,
.processors = processors, .processors = processors,
}; };
return cpuinfo_linux_parse_multiline_file("/proc/cpuinfo", BUFFER_SIZE, return cpuinfo_linux_parse_multiline_file(
(cpuinfo_line_callback) parse_line, &state); "/proc/cpuinfo", BUFFER_SIZE, (cpuinfo_line_callback)parse_line, &state);
} }

View File

@ -1,163 +1,154 @@
#include <limits.h> #include <limits.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <elf.h> #include <elf.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#if CPUINFO_MOCK #if CPUINFO_MOCK
#include <cpuinfo-mock.h> #include <cpuinfo-mock.h>
#endif #endif
#include <cpuinfo.h>
#include <arm/linux/api.h> #include <arm/linux/api.h>
#include <cpuinfo.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#if CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_ARM && \ #if CPUINFO_ARCH_ARM64 || \
defined(__GLIBC__) && defined(__GLIBC_MINOR__) && (__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 16) CPUINFO_ARCH_ARM && defined(__GLIBC__) && defined(__GLIBC_MINOR__) && \
#include <sys/auxv.h> (__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 16)
#include <sys/auxv.h>
#else #else
#define AT_HWCAP 16 #define AT_HWCAP 16
#define AT_HWCAP2 26 #define AT_HWCAP2 26
#endif #endif
#if CPUINFO_MOCK #if CPUINFO_MOCK
static uint32_t mock_hwcap = 0; static uint32_t mock_hwcap = 0;
void cpuinfo_set_hwcap(uint32_t hwcap) { void cpuinfo_set_hwcap(uint32_t hwcap) {
mock_hwcap = hwcap; mock_hwcap = hwcap;
} }
static uint32_t mock_hwcap2 = 0; static uint32_t mock_hwcap2 = 0;
void cpuinfo_set_hwcap2(uint32_t hwcap2) { void cpuinfo_set_hwcap2(uint32_t hwcap2) {
mock_hwcap2 = hwcap2; mock_hwcap2 = hwcap2;
} }
#endif #endif
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
typedef unsigned long (*getauxval_function_t)(unsigned long); typedef unsigned long (*getauxval_function_t)(unsigned long);
bool cpuinfo_arm_linux_hwcap_from_getauxval( bool cpuinfo_arm_linux_hwcap_from_getauxval(uint32_t hwcap[restrict static 1], uint32_t hwcap2[restrict static 1]) {
uint32_t hwcap[restrict static 1], #if CPUINFO_MOCK
uint32_t hwcap2[restrict static 1]) *hwcap = mock_hwcap;
{ *hwcap2 = mock_hwcap2;
#if CPUINFO_MOCK return true;
*hwcap = mock_hwcap; #elif defined(__ANDROID__)
*hwcap2 = mock_hwcap2; /* Android: dynamically check if getauxval is supported */
return true; void* libc = NULL;
#elif defined(__ANDROID__) getauxval_function_t getauxval = NULL;
/* Android: dynamically check if getauxval is supported */
void* libc = NULL;
getauxval_function_t getauxval = NULL;
dlerror(); dlerror();
libc = dlopen("libc.so", RTLD_LAZY); libc = dlopen("libc.so", RTLD_LAZY);
if (libc == NULL) { if (libc == NULL) {
cpuinfo_log_warning("failed to load libc.so: %s", dlerror()); cpuinfo_log_warning("failed to load libc.so: %s", dlerror());
goto cleanup; goto cleanup;
}
getauxval = (getauxval_function_t) dlsym(libc, "getauxval");
if (getauxval == NULL) {
cpuinfo_log_info("failed to locate getauxval in libc.so: %s", dlerror());
goto cleanup;
}
*hwcap = getauxval(AT_HWCAP);
*hwcap2 = getauxval(AT_HWCAP2);
cleanup:
if (libc != NULL) {
dlclose(libc);
libc = NULL;
}
return getauxval != NULL;
#elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) && (__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 16)
/* GNU/Linux: getauxval is supported since glibc-2.16 */
*hwcap = getauxval(AT_HWCAP);
*hwcap2 = getauxval(AT_HWCAP2);
return true;
#else
return false;
#endif
} }
#ifdef __ANDROID__ getauxval = (getauxval_function_t)dlsym(libc, "getauxval");
bool cpuinfo_arm_linux_hwcap_from_procfs( if (getauxval == NULL) {
uint32_t hwcap[restrict static 1], cpuinfo_log_info("failed to locate getauxval in libc.so: %s", dlerror());
uint32_t hwcap2[restrict static 1]) goto cleanup;
{
#if CPUINFO_MOCK
*hwcap = mock_hwcap;
*hwcap2 = mock_hwcap2;
return true;
#else
uint32_t hwcaps[2] = { 0, 0 };
bool result = false;
int file = -1;
file = open("/proc/self/auxv", O_RDONLY);
if (file == -1) {
cpuinfo_log_warning("failed to open /proc/self/auxv: %s", strerror(errno));
goto cleanup;
}
ssize_t bytes_read;
do {
Elf32_auxv_t elf_auxv;
bytes_read = read(file, &elf_auxv, sizeof(Elf32_auxv_t));
if (bytes_read < 0) {
cpuinfo_log_warning("failed to read /proc/self/auxv: %s", strerror(errno));
goto cleanup;
} else if (bytes_read > 0) {
if (bytes_read == sizeof(elf_auxv)) {
switch (elf_auxv.a_type) {
case AT_HWCAP:
hwcaps[0] = (uint32_t) elf_auxv.a_un.a_val;
break;
case AT_HWCAP2:
hwcaps[1] = (uint32_t) elf_auxv.a_un.a_val;
break;
}
} else {
cpuinfo_log_warning(
"failed to read %zu bytes from /proc/self/auxv: %zu bytes available",
sizeof(elf_auxv), (size_t) bytes_read);
goto cleanup;
}
}
} while (bytes_read == sizeof(Elf32_auxv_t));
/* Success, commit results */
*hwcap = hwcaps[0];
*hwcap2 = hwcaps[1];
result = true;
cleanup:
if (file != -1) {
close(file);
file = -1;
}
return result;
#endif
}
#endif /* __ANDROID__ */
#elif CPUINFO_ARCH_ARM64
void cpuinfo_arm_linux_hwcap_from_getauxval(
uint32_t hwcap[restrict static 1],
uint32_t hwcap2[restrict static 1])
{
#if CPUINFO_MOCK
*hwcap = mock_hwcap;
*hwcap2 = mock_hwcap2;
#else
*hwcap = (uint32_t) getauxval(AT_HWCAP);
*hwcap2 = (uint32_t) getauxval(AT_HWCAP2);
return ;
#endif
} }
*hwcap = getauxval(AT_HWCAP);
*hwcap2 = getauxval(AT_HWCAP2);
cleanup:
if (libc != NULL) {
dlclose(libc);
libc = NULL;
}
return getauxval != NULL;
#elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) && (__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 16)
/* GNU/Linux: getauxval is supported since glibc-2.16 */
*hwcap = getauxval(AT_HWCAP);
*hwcap2 = getauxval(AT_HWCAP2);
return true;
#else
return false;
#endif
}
#ifdef __ANDROID__
bool cpuinfo_arm_linux_hwcap_from_procfs(uint32_t hwcap[restrict static 1], uint32_t hwcap2[restrict static 1]) {
#if CPUINFO_MOCK
*hwcap = mock_hwcap;
*hwcap2 = mock_hwcap2;
return true;
#else
uint32_t hwcaps[2] = {0, 0};
bool result = false;
int file = -1;
file = open("/proc/self/auxv", O_RDONLY);
if (file == -1) {
cpuinfo_log_warning("failed to open /proc/self/auxv: %s", strerror(errno));
goto cleanup;
}
ssize_t bytes_read;
do {
Elf32_auxv_t elf_auxv;
bytes_read = read(file, &elf_auxv, sizeof(Elf32_auxv_t));
if (bytes_read < 0) {
cpuinfo_log_warning("failed to read /proc/self/auxv: %s", strerror(errno));
goto cleanup;
} else if (bytes_read > 0) {
if (bytes_read == sizeof(elf_auxv)) {
switch (elf_auxv.a_type) {
case AT_HWCAP:
hwcaps[0] = (uint32_t)elf_auxv.a_un.a_val;
break;
case AT_HWCAP2:
hwcaps[1] = (uint32_t)elf_auxv.a_un.a_val;
break;
}
} else {
cpuinfo_log_warning(
"failed to read %zu bytes from /proc/self/auxv: %zu bytes available",
sizeof(elf_auxv),
(size_t)bytes_read);
goto cleanup;
}
}
} while (bytes_read == sizeof(Elf32_auxv_t));
/* Success, commit results */
*hwcap = hwcaps[0];
*hwcap2 = hwcaps[1];
result = true;
cleanup:
if (file != -1) {
close(file);
file = -1;
}
return result;
#endif
}
#endif /* __ANDROID__ */
#elif CPUINFO_ARCH_ARM64
void cpuinfo_arm_linux_hwcap_from_getauxval(uint32_t hwcap[restrict static 1], uint32_t hwcap2[restrict static 1]) {
#if CPUINFO_MOCK
*hwcap = mock_hwcap;
*hwcap2 = mock_hwcap2;
#else
*hwcap = (uint32_t)getauxval(AT_HWCAP);
*hwcap2 = (uint32_t)getauxval(AT_HWCAP2);
return;
#endif
}
#endif #endif

View File

@ -1,23 +1,22 @@
#include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <cpuinfo.h>
#include <arm/linux/api.h> #include <arm/linux/api.h>
#include <cpuinfo.h>
#if defined(__ANDROID__) #if defined(__ANDROID__)
#include <arm/android/api.h> #include <arm/android/api.h>
#endif #endif
#include <arm/api.h> #include <arm/api.h>
#include <arm/midr.h> #include <arm/midr.h>
#include <linux/api.h>
#include <cpuinfo/internal-api.h> #include <cpuinfo/internal-api.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <linux/api.h>
struct cpuinfo_arm_isa cpuinfo_isa = {0};
struct cpuinfo_arm_isa cpuinfo_isa = { 0 }; static struct cpuinfo_package package = {{0}};
static struct cpuinfo_package package = { { 0 } };
static inline bool bitmask_all(uint32_t bitfield, uint32_t mask) { static inline bool bitmask_all(uint32_t bitfield, uint32_t mask) {
return (bitfield & mask) == mask; return (bitfield & mask) == mask;
@ -32,16 +31,19 @@ static inline int cmp(uint32_t a, uint32_t b) {
} }
static bool cluster_siblings_parser( static bool cluster_siblings_parser(
uint32_t processor, uint32_t siblings_start, uint32_t siblings_end, uint32_t processor,
struct cpuinfo_arm_linux_processor* processors) uint32_t siblings_start,
{ uint32_t siblings_end,
struct cpuinfo_arm_linux_processor* processors) {
processors[processor].flags |= CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER; processors[processor].flags |= CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER;
uint32_t package_leader_id = processors[processor].package_leader_id; uint32_t package_leader_id = processors[processor].package_leader_id;
for (uint32_t sibling = siblings_start; sibling < siblings_end; sibling++) { for (uint32_t sibling = siblings_start; sibling < siblings_end; sibling++) {
if (!bitmask_all(processors[sibling].flags, CPUINFO_LINUX_FLAG_VALID)) { if (!bitmask_all(processors[sibling].flags, CPUINFO_LINUX_FLAG_VALID)) {
cpuinfo_log_info("invalid processor %"PRIu32" reported as a sibling for processor %"PRIu32, cpuinfo_log_info(
sibling, processor); "invalid processor %" PRIu32 " reported as a sibling for processor %" PRIu32,
sibling,
processor);
continue; continue;
} }
@ -60,14 +62,14 @@ static bool cluster_siblings_parser(
} }
static int cmp_arm_linux_processor(const void* ptr_a, const void* ptr_b) { static int cmp_arm_linux_processor(const void* ptr_a, const void* ptr_b) {
const struct cpuinfo_arm_linux_processor* processor_a = (const struct cpuinfo_arm_linux_processor*) ptr_a; const struct cpuinfo_arm_linux_processor* processor_a = (const struct cpuinfo_arm_linux_processor*)ptr_a;
const struct cpuinfo_arm_linux_processor* processor_b = (const struct cpuinfo_arm_linux_processor*) ptr_b; const struct cpuinfo_arm_linux_processor* processor_b = (const struct cpuinfo_arm_linux_processor*)ptr_b;
/* Move usable processors towards the start of the array */ /* Move usable processors towards the start of the array */
const bool usable_a = bitmask_all(processor_a->flags, CPUINFO_LINUX_FLAG_VALID); const bool usable_a = bitmask_all(processor_a->flags, CPUINFO_LINUX_FLAG_VALID);
const bool usable_b = bitmask_all(processor_b->flags, CPUINFO_LINUX_FLAG_VALID); const bool usable_b = bitmask_all(processor_b->flags, CPUINFO_LINUX_FLAG_VALID);
if (usable_a != usable_b) { if (usable_a != usable_b) {
return (int) usable_b - (int) usable_a; return (int)usable_b - (int)usable_a;
} }
/* Compare based on core type (e.g. Cortex-A57 < Cortex-A53) */ /* Compare based on core type (e.g. Cortex-A57 < Cortex-A53) */
@ -95,7 +97,8 @@ static int cmp_arm_linux_processor(const void* ptr_a, const void* ptr_b) {
return cluster_a > cluster_b ? -1 : 1; return cluster_a > cluster_b ? -1 : 1;
} }
/* Compare based on system processor id (i.e. processor 0 < processor 1) */ /* Compare based on system processor id (i.e. processor 0 < processor 1)
*/
const uint32_t id_a = processor_a->system_processor_id; const uint32_t id_a = processor_a->system_processor_id;
const uint32_t id_b = processor_b->system_processor_id; const uint32_t id_b = processor_b->system_processor_id;
return cmp(id_a, id_b); return cmp(id_a, id_b);
@ -116,14 +119,13 @@ void cpuinfo_arm_linux_init(void) {
uint32_t* linux_cpu_to_uarch_index_map = NULL; uint32_t* linux_cpu_to_uarch_index_map = NULL;
const uint32_t max_processors_count = cpuinfo_linux_get_max_processors_count(); const uint32_t max_processors_count = cpuinfo_linux_get_max_processors_count();
cpuinfo_log_debug("system maximum processors count: %"PRIu32, max_processors_count); cpuinfo_log_debug("system maximum processors count: %" PRIu32, max_processors_count);
const uint32_t max_possible_processors_count = 1 + const uint32_t max_possible_processors_count =
cpuinfo_linux_get_max_possible_processor(max_processors_count); 1 + cpuinfo_linux_get_max_possible_processor(max_processors_count);
cpuinfo_log_debug("maximum possible processors count: %"PRIu32, max_possible_processors_count); cpuinfo_log_debug("maximum possible processors count: %" PRIu32, max_possible_processors_count);
const uint32_t max_present_processors_count = 1 + const uint32_t max_present_processors_count = 1 + cpuinfo_linux_get_max_present_processor(max_processors_count);
cpuinfo_linux_get_max_present_processor(max_processors_count); cpuinfo_log_debug("maximum present processors count: %" PRIu32, max_present_processors_count);
cpuinfo_log_debug("maximum present processors count: %"PRIu32, max_present_processors_count);
uint32_t valid_processor_mask = 0; uint32_t valid_processor_mask = 0;
uint32_t arm_linux_processors_count = max_processors_count; uint32_t arm_linux_processors_count = max_processors_count;
@ -143,7 +145,7 @@ void cpuinfo_arm_linux_init(void) {
arm_linux_processors = calloc(arm_linux_processors_count, sizeof(struct cpuinfo_arm_linux_processor)); arm_linux_processors = calloc(arm_linux_processors_count, sizeof(struct cpuinfo_arm_linux_processor));
if (arm_linux_processors == NULL) { if (arm_linux_processors == NULL) {
cpuinfo_log_error( cpuinfo_log_error(
"failed to allocate %zu bytes for descriptions of %"PRIu32" ARM logical processors", "failed to allocate %zu bytes for descriptions of %" PRIu32 " ARM logical processors",
arm_linux_processors_count * sizeof(struct cpuinfo_arm_linux_processor), arm_linux_processors_count * sizeof(struct cpuinfo_arm_linux_processor),
arm_linux_processors_count); arm_linux_processors_count);
return; return;
@ -151,14 +153,16 @@ void cpuinfo_arm_linux_init(void) {
if (max_possible_processors_count) { if (max_possible_processors_count) {
cpuinfo_linux_detect_possible_processors( cpuinfo_linux_detect_possible_processors(
arm_linux_processors_count, &arm_linux_processors->flags, arm_linux_processors_count,
&arm_linux_processors->flags,
sizeof(struct cpuinfo_arm_linux_processor), sizeof(struct cpuinfo_arm_linux_processor),
CPUINFO_LINUX_FLAG_POSSIBLE); CPUINFO_LINUX_FLAG_POSSIBLE);
} }
if (max_present_processors_count) { if (max_present_processors_count) {
cpuinfo_linux_detect_present_processors( cpuinfo_linux_detect_present_processors(
arm_linux_processors_count, &arm_linux_processors->flags, arm_linux_processors_count,
&arm_linux_processors->flags,
sizeof(struct cpuinfo_arm_linux_processor), sizeof(struct cpuinfo_arm_linux_processor),
CPUINFO_LINUX_FLAG_PRESENT); CPUINFO_LINUX_FLAG_PRESENT);
} }
@ -173,13 +177,13 @@ void cpuinfo_arm_linux_init(void) {
if (!cpuinfo_arm_linux_parse_proc_cpuinfo( if (!cpuinfo_arm_linux_parse_proc_cpuinfo(
#if defined(__ANDROID__) #if defined(__ANDROID__)
android_properties.proc_cpuinfo_hardware, android_properties.proc_cpuinfo_hardware,
#else #else
proc_cpuinfo_hardware, proc_cpuinfo_hardware,
#endif #endif
proc_cpuinfo_revision, proc_cpuinfo_revision,
arm_linux_processors_count, arm_linux_processors_count,
arm_linux_processors)) { arm_linux_processors)) {
cpuinfo_log_error("failed to parse processor information from /proc/cpuinfo"); cpuinfo_log_error("failed to parse processor information from /proc/cpuinfo");
return; return;
} }
@ -187,45 +191,49 @@ void cpuinfo_arm_linux_init(void) {
for (uint32_t i = 0; i < arm_linux_processors_count; i++) { for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
if (bitmask_all(arm_linux_processors[i].flags, valid_processor_mask)) { if (bitmask_all(arm_linux_processors[i].flags, valid_processor_mask)) {
arm_linux_processors[i].flags |= CPUINFO_LINUX_FLAG_VALID; arm_linux_processors[i].flags |= CPUINFO_LINUX_FLAG_VALID;
cpuinfo_log_debug("parsed processor %"PRIu32" MIDR 0x%08"PRIx32, cpuinfo_log_debug(
i, arm_linux_processors[i].midr); "parsed processor %" PRIu32 " MIDR 0x%08" PRIx32, i, arm_linux_processors[i].midr);
} }
} }
uint32_t valid_processors = 0, last_midr = 0; uint32_t valid_processors = 0, last_midr = 0;
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
uint32_t last_architecture_version = 0, last_architecture_flags = 0; uint32_t last_architecture_version = 0, last_architecture_flags = 0;
#endif #endif
for (uint32_t i = 0; i < arm_linux_processors_count; i++) { for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
arm_linux_processors[i].system_processor_id = i; arm_linux_processors[i].system_processor_id = i;
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) { if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
if (arm_linux_processors[i].flags & CPUINFO_ARM_LINUX_VALID_PROCESSOR) { if (arm_linux_processors[i].flags & CPUINFO_ARM_LINUX_VALID_PROCESSOR) {
/* /*
* Processor is in possible and present lists, and also reported in /proc/cpuinfo. * Processor is in possible and present lists,
* This processor is availble for compute. * and also reported in /proc/cpuinfo. This
* processor is availble for compute.
*/ */
valid_processors += 1; valid_processors += 1;
} else { } else {
/* /*
* Processor is in possible and present lists, but not reported in /proc/cpuinfo. * Processor is in possible and present lists,
* This is fairly common: high-index processors can be not reported if they are offline. * but not reported in /proc/cpuinfo. This is
* fairly common: high-index processors can be
* not reported if they are offline.
*/ */
cpuinfo_log_info("processor %"PRIu32" is not listed in /proc/cpuinfo", i); cpuinfo_log_info("processor %" PRIu32 " is not listed in /proc/cpuinfo", i);
} }
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_ARM_LINUX_VALID_MIDR)) { if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_ARM_LINUX_VALID_MIDR)) {
last_midr = arm_linux_processors[i].midr; last_midr = arm_linux_processors[i].midr;
} }
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_ARM_LINUX_VALID_ARCHITECTURE)) { if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_ARM_LINUX_VALID_ARCHITECTURE)) {
last_architecture_version = arm_linux_processors[i].architecture_version; last_architecture_version = arm_linux_processors[i].architecture_version;
last_architecture_flags = arm_linux_processors[i].architecture_flags; last_architecture_flags = arm_linux_processors[i].architecture_flags;
} }
#endif #endif
} else { } else {
/* Processor reported in /proc/cpuinfo, but not in possible and/or present lists: log and ignore */ /* Processor reported in /proc/cpuinfo, but not in
* possible and/or present lists: log and ignore */
if (!(arm_linux_processors[i].flags & CPUINFO_ARM_LINUX_VALID_PROCESSOR)) { if (!(arm_linux_processors[i].flags & CPUINFO_ARM_LINUX_VALID_PROCESSOR)) {
cpuinfo_log_warning("invalid processor %"PRIu32" reported in /proc/cpuinfo", i); cpuinfo_log_warning("invalid processor %" PRIu32 " reported in /proc/cpuinfo", i);
} }
} }
} }
@ -238,55 +246,65 @@ void cpuinfo_arm_linux_init(void) {
cpuinfo_arm_linux_decode_chipset(proc_cpuinfo_hardware, proc_cpuinfo_revision, valid_processors, 0); cpuinfo_arm_linux_decode_chipset(proc_cpuinfo_hardware, proc_cpuinfo_revision, valid_processors, 0);
#endif #endif
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
uint32_t isa_features = 0, isa_features2 = 0; uint32_t isa_features = 0, isa_features2 = 0;
#ifdef __ANDROID__ #ifdef __ANDROID__
/*
* On Android before API 20, libc.so does not provide getauxval
* function. Thus, we try to dynamically find it, or use two fallback
* mechanisms:
* 1. dlopen libc.so, and try to find getauxval
* 2. Parse /proc/self/auxv procfs file
* 3. Use features reported in /proc/cpuinfo
*/
if (!cpuinfo_arm_linux_hwcap_from_getauxval(&isa_features, &isa_features2)) {
/* getauxval can't be used, fall back to parsing /proc/self/auxv
*/
if (!cpuinfo_arm_linux_hwcap_from_procfs(&isa_features, &isa_features2)) {
/* /*
* On Android before API 20, libc.so does not provide getauxval function. * Reading /proc/self/auxv failed, probably due to file
* Thus, we try to dynamically find it, or use two fallback mechanisms: * permissions. Use information from /proc/cpuinfo to
* 1. dlopen libc.so, and try to find getauxval * detect ISA.
* 2. Parse /proc/self/auxv procfs file *
* 3. Use features reported in /proc/cpuinfo * If different processors report different ISA
* features, take the intersection.
*/ */
if (!cpuinfo_arm_linux_hwcap_from_getauxval(&isa_features, &isa_features2)) { uint32_t processors_with_features = 0;
/* getauxval can't be used, fall back to parsing /proc/self/auxv */ for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
if (!cpuinfo_arm_linux_hwcap_from_procfs(&isa_features, &isa_features2)) { if (bitmask_all(
/* arm_linux_processors[i].flags,
* Reading /proc/self/auxv failed, probably due to file permissions. CPUINFO_LINUX_FLAG_VALID | CPUINFO_ARM_LINUX_VALID_FEATURES)) {
* Use information from /proc/cpuinfo to detect ISA. if (processors_with_features == 0) {
* isa_features = arm_linux_processors[i].features;
* If different processors report different ISA features, take the intersection. isa_features2 = arm_linux_processors[i].features2;
*/ } else {
uint32_t processors_with_features = 0; isa_features &= arm_linux_processors[i].features;
for (uint32_t i = 0; i < arm_linux_processors_count; i++) { isa_features2 &= arm_linux_processors[i].features2;
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID | CPUINFO_ARM_LINUX_VALID_FEATURES)) {
if (processors_with_features == 0) {
isa_features = arm_linux_processors[i].features;
isa_features2 = arm_linux_processors[i].features2;
} else {
isa_features &= arm_linux_processors[i].features;
isa_features2 &= arm_linux_processors[i].features2;
}
processors_with_features += 1;
}
} }
processors_with_features += 1;
} }
} }
#else }
/* On GNU/Linux getauxval is always available */ }
cpuinfo_arm_linux_hwcap_from_getauxval(&isa_features, &isa_features2); #else
#endif /* On GNU/Linux getauxval is always available */
cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo( cpuinfo_arm_linux_hwcap_from_getauxval(&isa_features, &isa_features2);
isa_features, isa_features2, #endif
last_midr, last_architecture_version, last_architecture_flags, cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
&chipset, &cpuinfo_isa); isa_features,
#elif CPUINFO_ARCH_ARM64 isa_features2,
uint32_t isa_features = 0, isa_features2 = 0; last_midr,
/* getauxval is always available on ARM64 Android */ last_architecture_version,
cpuinfo_arm_linux_hwcap_from_getauxval(&isa_features, &isa_features2); last_architecture_flags,
cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo( &chipset,
isa_features, isa_features2, last_midr, &chipset, &cpuinfo_isa); &cpuinfo_isa);
#endif #elif CPUINFO_ARCH_ARM64
uint32_t isa_features = 0, isa_features2 = 0;
/* getauxval is always available on ARM64 Android */
cpuinfo_arm_linux_hwcap_from_getauxval(&isa_features, &isa_features2);
cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
isa_features, isa_features2, last_midr, &chipset, &cpuinfo_isa);
#endif
/* Detect min/max frequency and package ID */ /* Detect min/max frequency and package ID */
for (uint32_t i = 0; i < arm_linux_processors_count; i++) { for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
@ -322,8 +340,9 @@ void cpuinfo_arm_linux_init(void) {
if (arm_linux_processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_ID) { if (arm_linux_processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_ID) {
cpuinfo_linux_detect_core_siblings( cpuinfo_linux_detect_core_siblings(
arm_linux_processors_count, i, arm_linux_processors_count,
(cpuinfo_siblings_callback) cluster_siblings_parser, i,
(cpuinfo_siblings_callback)cluster_siblings_parser,
arm_linux_processors); arm_linux_processors);
} }
} }
@ -331,79 +350,107 @@ void cpuinfo_arm_linux_init(void) {
/* Propagate all cluster IDs */ /* Propagate all cluster IDs */
uint32_t clustered_processors = 0; uint32_t clustered_processors = 0;
for (uint32_t i = 0; i < arm_linux_processors_count; i++) { for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID | CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER)) { if (bitmask_all(
arm_linux_processors[i].flags,
CPUINFO_LINUX_FLAG_VALID | CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER)) {
clustered_processors += 1; clustered_processors += 1;
const uint32_t package_leader_id = arm_linux_processors[i].package_leader_id; const uint32_t package_leader_id = arm_linux_processors[i].package_leader_id;
if (package_leader_id < i) { if (package_leader_id < i) {
arm_linux_processors[i].package_leader_id = arm_linux_processors[package_leader_id].package_leader_id; arm_linux_processors[i].package_leader_id =
arm_linux_processors[package_leader_id].package_leader_id;
} }
cpuinfo_log_debug("processor %"PRIu32" clustered with processor %"PRIu32" as inferred from system siblings lists", cpuinfo_log_debug(
i, arm_linux_processors[i].package_leader_id); "processor %" PRIu32 " clustered with processor %" PRIu32
" as inferred from system siblings lists",
i,
arm_linux_processors[i].package_leader_id);
} }
} }
if (clustered_processors != valid_processors) { if (clustered_processors != valid_processors) {
/* /*
* Topology information about some or all logical processors may be unavailable, for the following reasons: * Topology information about some or all logical processors may
* - Linux kernel is too old, or configured without support for topology information in sysfs. * be unavailable, for the following reasons:
* - Core is offline, and Linux kernel is configured to not report topology for offline cores. * - Linux kernel is too old, or configured without support for
* topology information in sysfs.
* - Core is offline, and Linux kernel is configured to not
* report topology for offline cores.
* *
* In this case, we assign processors to clusters using two methods: * In this case, we assign processors to clusters using two
* - Try heuristic cluster configurations (e.g. 6-core SoC usually has 4+2 big.LITTLE configuration). * methods:
* - If heuristic failed, assign processors to core clusters in a sequential scan. * - Try heuristic cluster configurations (e.g. 6-core SoC
* usually has 4+2 big.LITTLE configuration).
* - If heuristic failed, assign processors to core clusters in
* a sequential scan.
*/ */
if (!cpuinfo_arm_linux_detect_core_clusters_by_heuristic(valid_processors, arm_linux_processors_count, arm_linux_processors)) { if (!cpuinfo_arm_linux_detect_core_clusters_by_heuristic(
cpuinfo_arm_linux_detect_core_clusters_by_sequential_scan(arm_linux_processors_count, arm_linux_processors); valid_processors, arm_linux_processors_count, arm_linux_processors)) {
cpuinfo_arm_linux_detect_core_clusters_by_sequential_scan(
arm_linux_processors_count, arm_linux_processors);
} }
} }
cpuinfo_arm_linux_count_cluster_processors(arm_linux_processors_count, arm_linux_processors); cpuinfo_arm_linux_count_cluster_processors(arm_linux_processors_count, arm_linux_processors);
const uint32_t cluster_count = cpuinfo_arm_linux_detect_cluster_midr( const uint32_t cluster_count = cpuinfo_arm_linux_detect_cluster_midr(
&chipset, &chipset, arm_linux_processors_count, valid_processors, arm_linux_processors);
arm_linux_processors_count, valid_processors, arm_linux_processors);
/* Initialize core vendor, uarch, MIDR, and frequency for every logical processor */ /* Initialize core vendor, uarch, MIDR, and frequency for every logical
* processor */
for (uint32_t i = 0; i < arm_linux_processors_count; i++) { for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) { if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
const uint32_t cluster_leader = arm_linux_processors[i].package_leader_id; const uint32_t cluster_leader = arm_linux_processors[i].package_leader_id;
if (cluster_leader == i) { if (cluster_leader == i) {
/* Cluster leader: decode core vendor and uarch */ /* Cluster leader: decode core vendor and uarch
*/
cpuinfo_arm_decode_vendor_uarch( cpuinfo_arm_decode_vendor_uarch(
arm_linux_processors[cluster_leader].midr, arm_linux_processors[cluster_leader].midr,
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
!!(arm_linux_processors[cluster_leader].features & CPUINFO_ARM_LINUX_FEATURE_VFPV4), !!(arm_linux_processors[cluster_leader].features &
CPUINFO_ARM_LINUX_FEATURE_VFPV4),
#endif #endif
&arm_linux_processors[cluster_leader].vendor, &arm_linux_processors[cluster_leader].vendor,
&arm_linux_processors[cluster_leader].uarch); &arm_linux_processors[cluster_leader].uarch);
} else { } else {
/* Cluster non-leader: copy vendor, uarch, MIDR, and frequency from cluster leader */ /* Cluster non-leader: copy vendor, uarch, MIDR,
* and frequency from cluster leader */
arm_linux_processors[i].flags |= arm_linux_processors[cluster_leader].flags & arm_linux_processors[i].flags |= arm_linux_processors[cluster_leader].flags &
(CPUINFO_ARM_LINUX_VALID_MIDR | CPUINFO_LINUX_FLAG_MAX_FREQUENCY); (CPUINFO_ARM_LINUX_VALID_MIDR | CPUINFO_LINUX_FLAG_MAX_FREQUENCY);
arm_linux_processors[i].midr = arm_linux_processors[cluster_leader].midr; arm_linux_processors[i].midr = arm_linux_processors[cluster_leader].midr;
arm_linux_processors[i].vendor = arm_linux_processors[cluster_leader].vendor; arm_linux_processors[i].vendor = arm_linux_processors[cluster_leader].vendor;
arm_linux_processors[i].uarch = arm_linux_processors[cluster_leader].uarch; arm_linux_processors[i].uarch = arm_linux_processors[cluster_leader].uarch;
arm_linux_processors[i].max_frequency = arm_linux_processors[cluster_leader].max_frequency; arm_linux_processors[i].max_frequency =
arm_linux_processors[cluster_leader].max_frequency;
} }
} }
} }
for (uint32_t i = 0; i < arm_linux_processors_count; i++) { for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) { if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
cpuinfo_log_debug("post-analysis processor %"PRIu32": MIDR %08"PRIx32" frequency %"PRIu32, cpuinfo_log_debug(
i, arm_linux_processors[i].midr, arm_linux_processors[i].max_frequency); "post-analysis processor %" PRIu32 ": MIDR %08" PRIx32 " frequency %" PRIu32,
i,
arm_linux_processors[i].midr,
arm_linux_processors[i].max_frequency);
} }
} }
qsort(arm_linux_processors, arm_linux_processors_count, qsort(arm_linux_processors,
sizeof(struct cpuinfo_arm_linux_processor), cmp_arm_linux_processor); arm_linux_processors_count,
sizeof(struct cpuinfo_arm_linux_processor),
cmp_arm_linux_processor);
for (uint32_t i = 0; i < arm_linux_processors_count; i++) { for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) { if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
cpuinfo_log_debug("post-sort processor %"PRIu32": system id %"PRIu32" MIDR %08"PRIx32" frequency %"PRIu32, cpuinfo_log_debug(
i, arm_linux_processors[i].system_processor_id, arm_linux_processors[i].midr, arm_linux_processors[i].max_frequency); "post-sort processor %" PRIu32 ": system id %" PRIu32 " MIDR %08" PRIx32
" frequency %" PRIu32,
i,
arm_linux_processors[i].system_processor_id,
arm_linux_processors[i].midr,
arm_linux_processors[i].max_frequency);
} }
} }
@ -422,8 +469,10 @@ void cpuinfo_arm_linux_init(void) {
/* /*
* Assumptions: * Assumptions:
* - No SMP (i.e. each core supports only one hardware thread). * - No SMP (i.e. each core supports only one hardware thread).
* - Level 1 instruction and data caches are private to the core clusters. * - Level 1 instruction and data caches are private to the core
* - Level 2 and level 3 cache is shared between cores in the same cluster. * clusters.
* - Level 2 and level 3 cache is shared between cores in the same
* cluster.
*/ */
cpuinfo_arm_chipset_to_string(&chipset, package.name); cpuinfo_arm_chipset_to_string(&chipset, package.name);
package.processor_count = valid_processors; package.processor_count = valid_processors;
@ -432,66 +481,84 @@ void cpuinfo_arm_linux_init(void) {
processors = calloc(valid_processors, sizeof(struct cpuinfo_processor)); processors = calloc(valid_processors, sizeof(struct cpuinfo_processor));
if (processors == NULL) { if (processors == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" logical processors", cpuinfo_log_error(
valid_processors * sizeof(struct cpuinfo_processor), valid_processors); "failed to allocate %zu bytes for descriptions of %" PRIu32 " logical processors",
valid_processors * sizeof(struct cpuinfo_processor),
valid_processors);
goto cleanup; goto cleanup;
} }
cores = calloc(valid_processors, sizeof(struct cpuinfo_core)); cores = calloc(valid_processors, sizeof(struct cpuinfo_core));
if (cores == NULL) { if (cores == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" cores", cpuinfo_log_error(
valid_processors * sizeof(struct cpuinfo_core), valid_processors); "failed to allocate %zu bytes for descriptions of %" PRIu32 " cores",
valid_processors * sizeof(struct cpuinfo_core),
valid_processors);
goto cleanup; goto cleanup;
} }
clusters = calloc(cluster_count, sizeof(struct cpuinfo_cluster)); clusters = calloc(cluster_count, sizeof(struct cpuinfo_cluster));
if (clusters == NULL) { if (clusters == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" core clusters", cpuinfo_log_error(
cluster_count * sizeof(struct cpuinfo_cluster), cluster_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " core clusters",
cluster_count * sizeof(struct cpuinfo_cluster),
cluster_count);
goto cleanup; goto cleanup;
} }
uarchs = calloc(uarchs_count, sizeof(struct cpuinfo_uarch_info)); uarchs = calloc(uarchs_count, sizeof(struct cpuinfo_uarch_info));
if (uarchs == NULL) { if (uarchs == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" microarchitectures", cpuinfo_log_error(
uarchs_count * sizeof(struct cpuinfo_uarch_info), uarchs_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " microarchitectures",
uarchs_count * sizeof(struct cpuinfo_uarch_info),
uarchs_count);
goto cleanup; goto cleanup;
} }
linux_cpu_to_processor_map = calloc(arm_linux_processors_count, sizeof(struct cpuinfo_processor*)); linux_cpu_to_processor_map = calloc(arm_linux_processors_count, sizeof(struct cpuinfo_processor*));
if (linux_cpu_to_processor_map == NULL) { if (linux_cpu_to_processor_map == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for %"PRIu32" logical processor mapping entries", cpuinfo_log_error(
arm_linux_processors_count * sizeof(struct cpuinfo_processor*), arm_linux_processors_count); "failed to allocate %zu bytes for %" PRIu32 " logical processor mapping entries",
arm_linux_processors_count * sizeof(struct cpuinfo_processor*),
arm_linux_processors_count);
goto cleanup; goto cleanup;
} }
linux_cpu_to_core_map = calloc(arm_linux_processors_count, sizeof(struct cpuinfo_core*)); linux_cpu_to_core_map = calloc(arm_linux_processors_count, sizeof(struct cpuinfo_core*));
if (linux_cpu_to_core_map == NULL) { if (linux_cpu_to_core_map == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for %"PRIu32" core mapping entries", cpuinfo_log_error(
arm_linux_processors_count * sizeof(struct cpuinfo_core*), arm_linux_processors_count); "failed to allocate %zu bytes for %" PRIu32 " core mapping entries",
arm_linux_processors_count * sizeof(struct cpuinfo_core*),
arm_linux_processors_count);
goto cleanup; goto cleanup;
} }
if (uarchs_count > 1) { if (uarchs_count > 1) {
linux_cpu_to_uarch_index_map = calloc(arm_linux_processors_count, sizeof(uint32_t)); linux_cpu_to_uarch_index_map = calloc(arm_linux_processors_count, sizeof(uint32_t));
if (linux_cpu_to_uarch_index_map == NULL) { if (linux_cpu_to_uarch_index_map == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for %"PRIu32" uarch index mapping entries", cpuinfo_log_error(
arm_linux_processors_count * sizeof(uint32_t), arm_linux_processors_count); "failed to allocate %zu bytes for %" PRIu32 " uarch index mapping entries",
arm_linux_processors_count * sizeof(uint32_t),
arm_linux_processors_count);
goto cleanup; goto cleanup;
} }
} }
l1i = calloc(valid_processors, sizeof(struct cpuinfo_cache)); l1i = calloc(valid_processors, sizeof(struct cpuinfo_cache));
if (l1i == NULL) { if (l1i == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L1I caches", cpuinfo_log_error(
valid_processors * sizeof(struct cpuinfo_cache), valid_processors); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L1I caches",
valid_processors * sizeof(struct cpuinfo_cache),
valid_processors);
goto cleanup; goto cleanup;
} }
l1d = calloc(valid_processors, sizeof(struct cpuinfo_cache)); l1d = calloc(valid_processors, sizeof(struct cpuinfo_cache));
if (l1d == NULL) { if (l1d == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L1D caches", cpuinfo_log_error(
valid_processors * sizeof(struct cpuinfo_cache), valid_processors); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L1D caches",
valid_processors * sizeof(struct cpuinfo_cache),
valid_processors);
goto cleanup; goto cleanup;
} }
@ -500,7 +567,7 @@ void cpuinfo_arm_linux_init(void) {
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) { if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
if (uarchs_index == 0 || arm_linux_processors[i].uarch != last_uarch) { if (uarchs_index == 0 || arm_linux_processors[i].uarch != last_uarch) {
last_uarch = arm_linux_processors[i].uarch; last_uarch = arm_linux_processors[i].uarch;
uarchs[uarchs_index] = (struct cpuinfo_uarch_info) { uarchs[uarchs_index] = (struct cpuinfo_uarch_info){
.uarch = arm_linux_processors[i].uarch, .uarch = arm_linux_processors[i].uarch,
.midr = arm_linux_processors[i].midr, .midr = arm_linux_processors[i].midr,
}; };
@ -518,7 +585,7 @@ void cpuinfo_arm_linux_init(void) {
for (uint32_t i = 0; i < valid_processors; i++) { for (uint32_t i = 0; i < valid_processors; i++) {
if (arm_linux_processors[i].package_leader_id == arm_linux_processors[i].system_processor_id) { if (arm_linux_processors[i].package_leader_id == arm_linux_processors[i].system_processor_id) {
cluster_id += 1; cluster_id += 1;
clusters[cluster_id] = (struct cpuinfo_cluster) { clusters[cluster_id] = (struct cpuinfo_cluster){
.processor_start = i, .processor_start = i,
.processor_count = arm_linux_processors[i].package_processor_count, .processor_count = arm_linux_processors[i].package_processor_count,
.core_start = i, .core_start = i,
@ -535,7 +602,7 @@ void cpuinfo_arm_linux_init(void) {
processors[i].core = cores + i; processors[i].core = cores + i;
processors[i].cluster = clusters + cluster_id; processors[i].cluster = clusters + cluster_id;
processors[i].package = &package; processors[i].package = &package;
processors[i].linux_id = (int) arm_linux_processors[i].system_processor_id; processors[i].linux_id = (int)arm_linux_processors[i].system_processor_id;
processors[i].cache.l1i = l1i + i; processors[i].cache.l1i = l1i + i;
processors[i].cache.l1d = l1d + i; processors[i].cache.l1d = l1d + i;
linux_cpu_to_processor_map[arm_linux_processors[i].system_processor_id] = &processors[i]; linux_cpu_to_processor_map[arm_linux_processors[i].system_processor_id] = &processors[i];
@ -555,7 +622,7 @@ void cpuinfo_arm_linux_init(void) {
arm_linux_processors[i].uarch_index; arm_linux_processors[i].uarch_index;
} }
struct cpuinfo_cache temp_l2 = { 0 }, temp_l3 = { 0 }; struct cpuinfo_cache temp_l2 = {0}, temp_l3 = {0};
cpuinfo_arm_decode_cache( cpuinfo_arm_decode_cache(
arm_linux_processors[i].uarch, arm_linux_processors[i].uarch,
arm_linux_processors[i].package_processor_count, arm_linux_processors[i].package_processor_count,
@ -563,38 +630,40 @@ void cpuinfo_arm_linux_init(void) {
&chipset, &chipset,
cluster_id, cluster_id,
arm_linux_processors[i].architecture_version, arm_linux_processors[i].architecture_version,
&l1i[i], &l1d[i], &temp_l2, &temp_l3); &l1i[i],
&l1d[i],
&temp_l2,
&temp_l3);
l1i[i].processor_start = l1d[i].processor_start = i; l1i[i].processor_start = l1d[i].processor_start = i;
l1i[i].processor_count = l1d[i].processor_count = 1; l1i[i].processor_count = l1d[i].processor_count = 1;
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
/* L1I reported in /proc/cpuinfo overrides defaults */ /* L1I reported in /proc/cpuinfo overrides defaults */
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_ARM_LINUX_VALID_ICACHE)) { if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_ARM_LINUX_VALID_ICACHE)) {
l1i[i] = (struct cpuinfo_cache) { l1i[i] = (struct cpuinfo_cache){
.size = arm_linux_processors[i].proc_cpuinfo_cache.i_size, .size = arm_linux_processors[i].proc_cpuinfo_cache.i_size,
.associativity = arm_linux_processors[i].proc_cpuinfo_cache.i_assoc, .associativity = arm_linux_processors[i].proc_cpuinfo_cache.i_assoc,
.sets = arm_linux_processors[i].proc_cpuinfo_cache.i_sets, .sets = arm_linux_processors[i].proc_cpuinfo_cache.i_sets,
.partitions = 1, .partitions = 1,
.line_size = arm_linux_processors[i].proc_cpuinfo_cache.i_line_length .line_size = arm_linux_processors[i].proc_cpuinfo_cache.i_line_length};
}; }
} /* L1D reported in /proc/cpuinfo overrides defaults */
/* L1D reported in /proc/cpuinfo overrides defaults */ if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_ARM_LINUX_VALID_DCACHE)) {
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_ARM_LINUX_VALID_DCACHE)) { l1d[i] = (struct cpuinfo_cache){
l1d[i] = (struct cpuinfo_cache) { .size = arm_linux_processors[i].proc_cpuinfo_cache.d_size,
.size = arm_linux_processors[i].proc_cpuinfo_cache.d_size, .associativity = arm_linux_processors[i].proc_cpuinfo_cache.d_assoc,
.associativity = arm_linux_processors[i].proc_cpuinfo_cache.d_assoc, .sets = arm_linux_processors[i].proc_cpuinfo_cache.d_sets,
.sets = arm_linux_processors[i].proc_cpuinfo_cache.d_sets, .partitions = 1,
.partitions = 1, .line_size = arm_linux_processors[i].proc_cpuinfo_cache.d_line_length};
.line_size = arm_linux_processors[i].proc_cpuinfo_cache.d_line_length }
}; #endif
}
#endif
if (temp_l3.size != 0) { if (temp_l3.size != 0) {
/* /*
* Assumptions: * Assumptions:
* - L2 is private to each core * - L2 is private to each core
* - L3 is shared by cores in the same cluster * - L3 is shared by cores in the same cluster
* - If cores in different clusters report the same L3, it is shared between all cores. * - If cores in different clusters report the same L3,
* it is shared between all cores.
*/ */
l2_count += 1; l2_count += 1;
if (arm_linux_processors[i].package_leader_id == arm_linux_processors[i].system_processor_id) { if (arm_linux_processors[i].package_leader_id == arm_linux_processors[i].system_processor_id) {
@ -602,17 +671,22 @@ void cpuinfo_arm_linux_init(void) {
big_l3_size = temp_l3.size; big_l3_size = temp_l3.size;
l3_count = 1; l3_count = 1;
} else if (temp_l3.size != big_l3_size) { } else if (temp_l3.size != big_l3_size) {
/* If some cores have different L3 size, L3 is not shared between all cores */ /* If some cores have different L3 size,
* L3 is not shared between all cores */
shared_l3 = false; shared_l3 = false;
l3_count += 1; l3_count += 1;
} }
} }
} else { } else {
/* If some cores don't have L3 cache, L3 is not shared between all cores */ /* If some cores don't have L3 cache, L3 is not shared
* between all cores
*/
shared_l3 = false; shared_l3 = false;
if (temp_l2.size != 0) { if (temp_l2.size != 0) {
/* Assume L2 is shared by cores in the same cluster */ /* Assume L2 is shared by cores in the same
if (arm_linux_processors[i].package_leader_id == arm_linux_processors[i].system_processor_id) { * cluster */
if (arm_linux_processors[i].package_leader_id ==
arm_linux_processors[i].system_processor_id) {
l2_count += 1; l2_count += 1;
} }
} }
@ -622,16 +696,20 @@ void cpuinfo_arm_linux_init(void) {
if (l2_count != 0) { if (l2_count != 0) {
l2 = calloc(l2_count, sizeof(struct cpuinfo_cache)); l2 = calloc(l2_count, sizeof(struct cpuinfo_cache));
if (l2 == NULL) { if (l2 == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L2 caches", cpuinfo_log_error(
l2_count * sizeof(struct cpuinfo_cache), l2_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L2 caches",
l2_count * sizeof(struct cpuinfo_cache),
l2_count);
goto cleanup; goto cleanup;
} }
if (l3_count != 0) { if (l3_count != 0) {
l3 = calloc(l3_count, sizeof(struct cpuinfo_cache)); l3 = calloc(l3_count, sizeof(struct cpuinfo_cache));
if (l3 == NULL) { if (l3 == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L3 caches", cpuinfo_log_error(
l3_count * sizeof(struct cpuinfo_cache), l3_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L3 caches",
l3_count * sizeof(struct cpuinfo_cache),
l3_count);
goto cleanup; goto cleanup;
} }
} }
@ -644,7 +722,7 @@ void cpuinfo_arm_linux_init(void) {
cluster_id++; cluster_id++;
} }
struct cpuinfo_cache dummy_l1i, dummy_l1d, temp_l2 = { 0 }, temp_l3 = { 0 }; struct cpuinfo_cache dummy_l1i, dummy_l1d, temp_l2 = {0}, temp_l3 = {0};
cpuinfo_arm_decode_cache( cpuinfo_arm_decode_cache(
arm_linux_processors[i].uarch, arm_linux_processors[i].uarch,
arm_linux_processors[i].package_processor_count, arm_linux_processors[i].package_processor_count,
@ -652,23 +730,27 @@ void cpuinfo_arm_linux_init(void) {
&chipset, &chipset,
cluster_id, cluster_id,
arm_linux_processors[i].architecture_version, arm_linux_processors[i].architecture_version,
&dummy_l1i, &dummy_l1d, &temp_l2, &temp_l3); &dummy_l1i,
&dummy_l1d,
&temp_l2,
&temp_l3);
if (temp_l3.size != 0) { if (temp_l3.size != 0) {
/* /*
* Assumptions: * Assumptions:
* - L2 is private to each core * - L2 is private to each core
* - L3 is shared by cores in the same cluster * - L3 is shared by cores in the same cluster
* - If cores in different clusters report the same L3, it is shared between all cores. * - If cores in different clusters report the same L3,
* it is shared between all cores.
*/ */
l2_index += 1; l2_index += 1;
l2[l2_index] = (struct cpuinfo_cache) { l2[l2_index] = (struct cpuinfo_cache){
.size = temp_l2.size, .size = temp_l2.size,
.associativity = temp_l2.associativity, .associativity = temp_l2.associativity,
.sets = temp_l2.sets, .sets = temp_l2.sets,
.partitions = 1, .partitions = 1,
.line_size = temp_l2.line_size, .line_size = temp_l2.line_size,
.flags = temp_l2.flags, .flags = temp_l2.flags,
.processor_start = i, .processor_start = i,
.processor_count = 1, .processor_count = 1,
}; };
@ -676,16 +758,17 @@ void cpuinfo_arm_linux_init(void) {
if (arm_linux_processors[i].package_leader_id == arm_linux_processors[i].system_processor_id) { if (arm_linux_processors[i].package_leader_id == arm_linux_processors[i].system_processor_id) {
l3_index += 1; l3_index += 1;
if (l3_index < l3_count) { if (l3_index < l3_count) {
l3[l3_index] = (struct cpuinfo_cache) { l3[l3_index] = (struct cpuinfo_cache){
.size = temp_l3.size, .size = temp_l3.size,
.associativity = temp_l3.associativity, .associativity = temp_l3.associativity,
.sets = temp_l3.sets, .sets = temp_l3.sets,
.partitions = 1, .partitions = 1,
.line_size = temp_l3.line_size, .line_size = temp_l3.line_size,
.flags = temp_l3.flags, .flags = temp_l3.flags,
.processor_start = i, .processor_start = i,
.processor_count = .processor_count = shared_l3
shared_l3 ? valid_processors : arm_linux_processors[i].package_processor_count, ? valid_processors
: arm_linux_processors[i].package_processor_count,
}; };
} }
} }
@ -698,13 +781,13 @@ void cpuinfo_arm_linux_init(void) {
/* Assume L2 is shared by cores in the same cluster */ /* Assume L2 is shared by cores in the same cluster */
if (arm_linux_processors[i].package_leader_id == arm_linux_processors[i].system_processor_id) { if (arm_linux_processors[i].package_leader_id == arm_linux_processors[i].system_processor_id) {
l2_index += 1; l2_index += 1;
l2[l2_index] = (struct cpuinfo_cache) { l2[l2_index] = (struct cpuinfo_cache){
.size = temp_l2.size, .size = temp_l2.size,
.associativity = temp_l2.associativity, .associativity = temp_l2.associativity,
.sets = temp_l2.sets, .sets = temp_l2.sets,
.partitions = 1, .partitions = 1,
.line_size = temp_l2.line_size, .line_size = temp_l2.line_size,
.flags = temp_l2.flags, .flags = temp_l2.flags,
.processor_start = i, .processor_start = i,
.processor_count = arm_linux_processors[i].package_processor_count, .processor_count = arm_linux_processors[i].package_processor_count,
}; };
@ -721,8 +804,8 @@ void cpuinfo_arm_linux_init(void) {
cpuinfo_uarchs = uarchs; cpuinfo_uarchs = uarchs;
cpuinfo_cache[cpuinfo_cache_level_1i] = l1i; cpuinfo_cache[cpuinfo_cache_level_1i] = l1i;
cpuinfo_cache[cpuinfo_cache_level_1d] = l1d; cpuinfo_cache[cpuinfo_cache_level_1d] = l1d;
cpuinfo_cache[cpuinfo_cache_level_2] = l2; cpuinfo_cache[cpuinfo_cache_level_2] = l2;
cpuinfo_cache[cpuinfo_cache_level_3] = l3; cpuinfo_cache[cpuinfo_cache_level_3] = l3;
cpuinfo_processors_count = valid_processors; cpuinfo_processors_count = valid_processors;
cpuinfo_cores_count = valid_processors; cpuinfo_cores_count = valid_processors;
@ -731,8 +814,8 @@ void cpuinfo_arm_linux_init(void) {
cpuinfo_uarchs_count = uarchs_count; cpuinfo_uarchs_count = uarchs_count;
cpuinfo_cache_count[cpuinfo_cache_level_1i] = valid_processors; cpuinfo_cache_count[cpuinfo_cache_level_1i] = valid_processors;
cpuinfo_cache_count[cpuinfo_cache_level_1d] = valid_processors; cpuinfo_cache_count[cpuinfo_cache_level_1d] = valid_processors;
cpuinfo_cache_count[cpuinfo_cache_level_2] = l2_count; cpuinfo_cache_count[cpuinfo_cache_level_2] = l2_count;
cpuinfo_cache_count[cpuinfo_cache_level_3] = l3_count; cpuinfo_cache_count[cpuinfo_cache_level_3] = l3_count;
cpuinfo_max_cache_size = cpuinfo_arm_compute_max_cache_size(&processors[0]); cpuinfo_max_cache_size = cpuinfo_arm_compute_max_cache_size(&processors[0]);
cpuinfo_linux_cpu_max = arm_linux_processors_count; cpuinfo_linux_cpu_max = arm_linux_processors_count;

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +1,31 @@
#include <stdio.h> #include <alloca.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <alloca.h>
#include <errno.h> #include <errno.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <mach/machine.h> #include <mach/machine.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <cpuinfo.h> #include <cpuinfo.h>
#include <mach/api.h>
#include <cpuinfo/internal-api.h> #include <cpuinfo/internal-api.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <mach/api.h>
/* Polyfill recent CPUFAMILY_ARM_* values for older SDKs */ /* Polyfill recent CPUFAMILY_ARM_* values for older SDKs */
#ifndef CPUFAMILY_ARM_VORTEX_TEMPEST #ifndef CPUFAMILY_ARM_VORTEX_TEMPEST
#define CPUFAMILY_ARM_VORTEX_TEMPEST 0x07D34B9F #define CPUFAMILY_ARM_VORTEX_TEMPEST 0x07D34B9F
#endif #endif
#ifndef CPUFAMILY_ARM_LIGHTNING_THUNDER #ifndef CPUFAMILY_ARM_LIGHTNING_THUNDER
#define CPUFAMILY_ARM_LIGHTNING_THUNDER 0x462504D2 #define CPUFAMILY_ARM_LIGHTNING_THUNDER 0x462504D2
#endif #endif
#ifndef CPUFAMILY_ARM_FIRESTORM_ICESTORM #ifndef CPUFAMILY_ARM_FIRESTORM_ICESTORM
#define CPUFAMILY_ARM_FIRESTORM_ICESTORM 0x1B588BB3 #define CPUFAMILY_ARM_FIRESTORM_ICESTORM 0x1B588BB3
#endif #endif
#ifndef CPUFAMILY_ARM_AVALANCHE_BLIZZARD #ifndef CPUFAMILY_ARM_AVALANCHE_BLIZZARD
#define CPUFAMILY_ARM_AVALANCHE_BLIZZARD 0xDA33D83D #define CPUFAMILY_ARM_AVALANCHE_BLIZZARD 0xDA33D83D
#endif #endif
struct cpuinfo_arm_isa cpuinfo_isa = { struct cpuinfo_arm_isa cpuinfo_isa = {
@ -39,12 +39,12 @@ struct cpuinfo_arm_isa cpuinfo_isa = {
static uint32_t get_sys_info(int type_specifier, const char* name) { static uint32_t get_sys_info(int type_specifier, const char* name) {
size_t size = 0; size_t size = 0;
uint32_t result = 0; uint32_t result = 0;
int mib[2] = { CTL_HW, type_specifier }; int mib[2] = {CTL_HW, type_specifier};
if (sysctl(mib, 2, NULL, &size, NULL, 0) != 0) { if (sysctl(mib, 2, NULL, &size, NULL, 0) != 0) {
cpuinfo_log_info("sysctl(\"%s\") failed: %s", name, strerror(errno)); cpuinfo_log_info("sysctl(\"%s\") failed: %s", name, strerror(errno));
} else if (size == sizeof(uint32_t)) { } else if (size == sizeof(uint32_t)) {
sysctl(mib, 2, &result, &size, NULL, 0); sysctl(mib, 2, &result, &size, NULL, 0);
cpuinfo_log_debug("%s: %"PRIu32 ", size = %lu", name, result, size); cpuinfo_log_debug("%s: %" PRIu32 ", size = %lu", name, result, size);
} else { } else {
cpuinfo_log_info("sysctl does not support non-integer lookup for (\"%s\")", name); cpuinfo_log_info("sysctl does not support non-integer lookup for (\"%s\")", name);
} }
@ -58,7 +58,7 @@ static uint32_t get_sys_info_by_name(const char* type_specifier) {
cpuinfo_log_info("sysctlbyname(\"%s\") failed: %s", type_specifier, strerror(errno)); cpuinfo_log_info("sysctlbyname(\"%s\") failed: %s", type_specifier, strerror(errno));
} else if (size == sizeof(uint32_t)) { } else if (size == sizeof(uint32_t)) {
sysctlbyname(type_specifier, &result, &size, NULL, 0); sysctlbyname(type_specifier, &result, &size, NULL, 0);
cpuinfo_log_debug("%s: %"PRIu32 ", size = %lu", type_specifier, result, size); cpuinfo_log_debug("%s: %" PRIu32 ", size = %lu", type_specifier, result, size);
} else { } else {
cpuinfo_log_info("sysctl does not support non-integer lookup for (\"%s\")", type_specifier); cpuinfo_log_info("sysctl does not support non-integer lookup for (\"%s\")", type_specifier);
} }
@ -79,13 +79,16 @@ static enum cpuinfo_uarch decode_uarch(uint32_t cpu_family, uint32_t core_index,
/* 2x Monsoon + 4x Mistral cores */ /* 2x Monsoon + 4x Mistral cores */
return core_index < 2 ? cpuinfo_uarch_monsoon : cpuinfo_uarch_mistral; return core_index < 2 ? cpuinfo_uarch_monsoon : cpuinfo_uarch_mistral;
case CPUFAMILY_ARM_VORTEX_TEMPEST: case CPUFAMILY_ARM_VORTEX_TEMPEST:
/* Hexa-core: 2x Vortex + 4x Tempest; Octa-core: 4x Cortex + 4x Tempest */ /* Hexa-core: 2x Vortex + 4x Tempest; Octa-core: 4x
* Cortex + 4x Tempest */
return core_index + 4 < core_count ? cpuinfo_uarch_vortex : cpuinfo_uarch_tempest; return core_index + 4 < core_count ? cpuinfo_uarch_vortex : cpuinfo_uarch_tempest;
case CPUFAMILY_ARM_LIGHTNING_THUNDER: case CPUFAMILY_ARM_LIGHTNING_THUNDER:
/* Hexa-core: 2x Lightning + 4x Thunder; Octa-core (presumed): 4x Lightning + 4x Thunder */ /* Hexa-core: 2x Lightning + 4x Thunder; Octa-core
* (presumed): 4x Lightning + 4x Thunder */
return core_index + 4 < core_count ? cpuinfo_uarch_lightning : cpuinfo_uarch_thunder; return core_index + 4 < core_count ? cpuinfo_uarch_lightning : cpuinfo_uarch_thunder;
case CPUFAMILY_ARM_FIRESTORM_ICESTORM: case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
/* Hexa-core: 2x Firestorm + 4x Icestorm; Octa-core: 4x Firestorm + 4x Icestorm */ /* Hexa-core: 2x Firestorm + 4x Icestorm; Octa-core: 4x
* Firestorm + 4x Icestorm */
return core_index + 4 < core_count ? cpuinfo_uarch_firestorm : cpuinfo_uarch_icestorm; return core_index + 4 < core_count ? cpuinfo_uarch_firestorm : cpuinfo_uarch_icestorm;
case CPUFAMILY_ARM_AVALANCHE_BLIZZARD: case CPUFAMILY_ARM_AVALANCHE_BLIZZARD:
/* Hexa-core: 2x Avalanche + 4x Blizzard */ /* Hexa-core: 2x Avalanche + 4x Blizzard */
@ -105,7 +108,7 @@ static void decode_package_name(char* package_name) {
return; return;
} }
char *machine_name = alloca(size); char* machine_name = alloca(size);
if (sysctlbyname("hw.machine", machine_name, &size, NULL, 0) != 0) { if (sysctlbyname("hw.machine", machine_name, &size, NULL, 0) != 0) {
cpuinfo_log_warning("sysctlbyname(\"hw.machine\") failed: %s", strerror(errno)); cpuinfo_log_warning("sysctlbyname(\"hw.machine\") failed: %s", strerror(errno));
return; return;
@ -114,7 +117,7 @@ static void decode_package_name(char* package_name) {
char name[10]; char name[10];
uint32_t major = 0, minor = 0; uint32_t major = 0, minor = 0;
if (sscanf(machine_name, "%9[^,0123456789]%"SCNu32",%"SCNu32, name, &major, &minor) != 3) { if (sscanf(machine_name, "%9[^,0123456789]%" SCNu32 ",%" SCNu32, name, &major, &minor) != 3) {
cpuinfo_log_warning("parsing \"hw.machine\" failed: %s", strerror(errno)); cpuinfo_log_warning("parsing \"hw.machine\" failed: %s", strerror(errno));
return; return;
} }
@ -149,8 +152,9 @@ static void decode_package_name(char* package_name) {
/* iPad 2 and up are supported */ /* iPad 2 and up are supported */
case 2: case 2:
/* /*
* iPad 2 [A5]: iPad2,1, iPad2,2, iPad2,3, iPad2,4 * iPad 2 [A5]: iPad2,1, iPad2,2, iPad2,3,
* iPad mini [A5]: iPad2,5, iPad2,6, iPad2,7 * iPad2,4 iPad mini [A5]: iPad2,5, iPad2,6,
* iPad2,7
*/ */
chip_model = major + 3; chip_model = major + 3;
break; break;
@ -164,9 +168,10 @@ static void decode_package_name(char* package_name) {
break; break;
case 4: case 4:
/* /*
* iPad Air [A7]: iPad4,1, iPad4,2, iPad4,3 * iPad Air [A7]: iPad4,1, iPad4,2,
* iPad mini Retina [A7]: iPad4,4, iPad4,5, iPad4,6 * iPad4,3 iPad mini Retina [A7]: iPad4,4,
* iPad mini 3 [A7]: iPad4,7, iPad4,8, iPad4,9 * iPad4,5, iPad4,6 iPad mini 3 [A7]:
* iPad4,7, iPad4,8, iPad4,9
*/ */
chip_model = major + 3; chip_model = major + 3;
break; break;
@ -218,7 +223,7 @@ static void decode_package_name(char* package_name) {
cpuinfo_log_info("unknown device: %s", machine_name); cpuinfo_log_info("unknown device: %s", machine_name);
} }
if (chip_model != 0) { if (chip_model != 0) {
snprintf(package_name, CPUINFO_PACKAGE_NAME_MAX, "Apple A%"PRIu32"%c", chip_model, suffix); snprintf(package_name, CPUINFO_PACKAGE_NAME_MAX, "Apple A%" PRIu32 "%c", chip_model, suffix);
} }
} }
@ -236,20 +241,26 @@ void cpuinfo_arm_mach_init(void) {
struct cpuinfo_mach_topology mach_topology = cpuinfo_mach_detect_topology(); struct cpuinfo_mach_topology mach_topology = cpuinfo_mach_detect_topology();
processors = calloc(mach_topology.threads, sizeof(struct cpuinfo_processor)); processors = calloc(mach_topology.threads, sizeof(struct cpuinfo_processor));
if (processors == NULL) { if (processors == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" logical processors", cpuinfo_log_error(
mach_topology.threads * sizeof(struct cpuinfo_processor), mach_topology.threads); "failed to allocate %zu bytes for descriptions of %" PRIu32 " logical processors",
mach_topology.threads * sizeof(struct cpuinfo_processor),
mach_topology.threads);
goto cleanup; goto cleanup;
} }
cores = calloc(mach_topology.cores, sizeof(struct cpuinfo_core)); cores = calloc(mach_topology.cores, sizeof(struct cpuinfo_core));
if (cores == NULL) { if (cores == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" cores", cpuinfo_log_error(
mach_topology.cores * sizeof(struct cpuinfo_core), mach_topology.cores); "failed to allocate %zu bytes for descriptions of %" PRIu32 " cores",
mach_topology.cores * sizeof(struct cpuinfo_core),
mach_topology.cores);
goto cleanup; goto cleanup;
} }
packages = calloc(mach_topology.packages, sizeof(struct cpuinfo_package)); packages = calloc(mach_topology.packages, sizeof(struct cpuinfo_package));
if (packages == NULL) { if (packages == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" packages", cpuinfo_log_error(
mach_topology.packages * sizeof(struct cpuinfo_package), mach_topology.packages); "failed to allocate %zu bytes for descriptions of %" PRIu32 " packages",
mach_topology.packages * sizeof(struct cpuinfo_package),
mach_topology.packages);
goto cleanup; goto cleanup;
} }
@ -258,7 +269,7 @@ void cpuinfo_arm_mach_init(void) {
const uint32_t cores_per_package = mach_topology.cores / mach_topology.packages; const uint32_t cores_per_package = mach_topology.cores / mach_topology.packages;
for (uint32_t i = 0; i < mach_topology.packages; i++) { for (uint32_t i = 0; i < mach_topology.packages; i++) {
packages[i] = (struct cpuinfo_package) { packages[i] = (struct cpuinfo_package){
.processor_start = i * threads_per_package, .processor_start = i * threads_per_package,
.processor_count = threads_per_package, .processor_count = threads_per_package,
.core_start = i * cores_per_package, .core_start = i * cores_per_package,
@ -267,18 +278,19 @@ void cpuinfo_arm_mach_init(void) {
decode_package_name(packages[i].name); decode_package_name(packages[i].name);
} }
const uint32_t cpu_family = get_sys_info_by_name("hw.cpufamily"); const uint32_t cpu_family = get_sys_info_by_name("hw.cpufamily");
/* /*
* iOS 15 and macOS 12 added sysctls for ARM features, use them where possible. * iOS 15 and macOS 12 added sysctls for ARM features, use them where
* Otherwise, fallback to hardcoded set of CPUs with known support. * possible. Otherwise, fallback to hardcoded set of CPUs with known
* support.
*/ */
const uint32_t has_feat_lse = get_sys_info_by_name("hw.optional.arm.FEAT_LSE"); const uint32_t has_feat_lse = get_sys_info_by_name("hw.optional.arm.FEAT_LSE");
if (has_feat_lse != 0) { if (has_feat_lse != 0) {
cpuinfo_isa.atomics = true; cpuinfo_isa.atomics = true;
} else { } else {
// Mandatory in ARMv8.1-A, list only cores released before iOS 15 / macOS 12 // Mandatory in ARMv8.1-A, list only cores released before iOS
// 15 / macOS 12
switch (cpu_family) { switch (cpu_family) {
case CPUFAMILY_ARM_MONSOON_MISTRAL: case CPUFAMILY_ARM_MONSOON_MISTRAL:
case CPUFAMILY_ARM_VORTEX_TEMPEST: case CPUFAMILY_ARM_VORTEX_TEMPEST:
@ -327,8 +339,9 @@ void cpuinfo_arm_mach_init(void) {
if (has_feat_fhm_legacy != 0) { if (has_feat_fhm_legacy != 0) {
cpuinfo_isa.fhm = true; cpuinfo_isa.fhm = true;
} else { } else {
// Mandatory in ARMv8.4-A when FP16 arithmetics is implemented, // Mandatory in ARMv8.4-A when FP16 arithmetics is
// list only cores released before iOS 15 / macOS 12 // implemented, list only cores released before iOS 15 /
// macOS 12
switch (cpu_family) { switch (cpu_family) {
case CPUFAMILY_ARM_LIGHTNING_THUNDER: case CPUFAMILY_ARM_LIGHTNING_THUNDER:
case CPUFAMILY_ARM_FIRESTORM_ICESTORM: case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
@ -346,7 +359,8 @@ void cpuinfo_arm_mach_init(void) {
if (has_feat_fcma != 0) { if (has_feat_fcma != 0) {
cpuinfo_isa.fcma = true; cpuinfo_isa.fcma = true;
} else { } else {
// Mandatory in ARMv8.3-A, list only cores released before iOS 15 / macOS 12 // Mandatory in ARMv8.3-A, list only cores released before iOS
// 15 / macOS 12
switch (cpu_family) { switch (cpu_family) {
case CPUFAMILY_ARM_LIGHTNING_THUNDER: case CPUFAMILY_ARM_LIGHTNING_THUNDER:
case CPUFAMILY_ARM_FIRESTORM_ICESTORM: case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
@ -358,7 +372,8 @@ void cpuinfo_arm_mach_init(void) {
if (has_feat_jscvt != 0) { if (has_feat_jscvt != 0) {
cpuinfo_isa.jscvt = true; cpuinfo_isa.jscvt = true;
} else { } else {
// Mandatory in ARMv8.3-A, list only cores released before iOS 15 / macOS 12 // Mandatory in ARMv8.3-A, list only cores released before iOS
// 15 / macOS 12
switch (cpu_family) { switch (cpu_family) {
case CPUFAMILY_ARM_LIGHTNING_THUNDER: case CPUFAMILY_ARM_LIGHTNING_THUNDER:
case CPUFAMILY_ARM_FIRESTORM_ICESTORM: case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
@ -370,7 +385,8 @@ void cpuinfo_arm_mach_init(void) {
if (has_feat_dotprod != 0) { if (has_feat_dotprod != 0) {
cpuinfo_isa.dot = true; cpuinfo_isa.dot = true;
} else { } else {
// Mandatory in ARMv8.4-A, list only cores released before iOS 15 / macOS 12 // Mandatory in ARMv8.4-A, list only cores released before iOS
// 15 / macOS 12
switch (cpu_family) { switch (cpu_family) {
case CPUFAMILY_ARM_LIGHTNING_THUNDER: case CPUFAMILY_ARM_LIGHTNING_THUNDER:
case CPUFAMILY_ARM_FIRESTORM_ICESTORM: case CPUFAMILY_ARM_FIRESTORM_ICESTORM:
@ -385,7 +401,7 @@ void cpuinfo_arm_mach_init(void) {
uint32_t num_clusters = 1; uint32_t num_clusters = 1;
for (uint32_t i = 0; i < mach_topology.cores; i++) { for (uint32_t i = 0; i < mach_topology.cores; i++) {
cores[i] = (struct cpuinfo_core) { cores[i] = (struct cpuinfo_core){
.processor_start = i * threads_per_core, .processor_start = i * threads_per_core,
.processor_count = threads_per_core, .processor_count = threads_per_core,
.core_id = i % cores_per_package, .core_id = i % cores_per_package,
@ -410,27 +426,29 @@ void cpuinfo_arm_mach_init(void) {
clusters = calloc(num_clusters, sizeof(struct cpuinfo_cluster)); clusters = calloc(num_clusters, sizeof(struct cpuinfo_cluster));
if (clusters == NULL) { if (clusters == NULL) {
cpuinfo_log_error( cpuinfo_log_error(
"failed to allocate %zu bytes for descriptions of %"PRIu32" clusters", "failed to allocate %zu bytes for descriptions of %" PRIu32 " clusters",
num_clusters * sizeof(struct cpuinfo_cluster), num_clusters); num_clusters * sizeof(struct cpuinfo_cluster),
num_clusters);
goto cleanup; goto cleanup;
} }
uarchs = calloc(num_clusters, sizeof(struct cpuinfo_uarch_info)); uarchs = calloc(num_clusters, sizeof(struct cpuinfo_uarch_info));
if (uarchs == NULL) { if (uarchs == NULL) {
cpuinfo_log_error( cpuinfo_log_error(
"failed to allocate %zu bytes for descriptions of %"PRIu32" uarchs", "failed to allocate %zu bytes for descriptions of %" PRIu32 " uarchs",
num_clusters * sizeof(enum cpuinfo_uarch), num_clusters); num_clusters * sizeof(enum cpuinfo_uarch),
num_clusters);
goto cleanup; goto cleanup;
} }
uint32_t cluster_idx = UINT32_MAX; uint32_t cluster_idx = UINT32_MAX;
for (uint32_t i = 0; i < mach_topology.cores; i++) { for (uint32_t i = 0; i < mach_topology.cores; i++) {
if (i == 0 || cores[i].uarch != cores[i - 1].uarch) { if (i == 0 || cores[i].uarch != cores[i - 1].uarch) {
cluster_idx++; cluster_idx++;
uarchs[cluster_idx] = (struct cpuinfo_uarch_info) { uarchs[cluster_idx] = (struct cpuinfo_uarch_info){
.uarch = cores[i].uarch, .uarch = cores[i].uarch,
.processor_count = 1, .processor_count = 1,
.core_count = 1, .core_count = 1,
}; };
clusters[cluster_idx] = (struct cpuinfo_cluster) { clusters[cluster_idx] = (struct cpuinfo_cluster){
.processor_start = i * threads_per_core, .processor_start = i * threads_per_core,
.processor_count = 1, .processor_count = 1,
.core_start = i, .core_start = i,
@ -475,7 +493,7 @@ void cpuinfo_arm_mach_init(void) {
/* Assume L1 caches are private to each core */ /* Assume L1 caches are private to each core */
threads_per_l1 = 1; threads_per_l1 = 1;
l1_count = mach_topology.threads / threads_per_l1; l1_count = mach_topology.threads / threads_per_l1;
cpuinfo_log_debug("detected %"PRIu32" L1 caches", l1_count); cpuinfo_log_debug("detected %" PRIu32 " L1 caches", l1_count);
} }
uint32_t threads_per_l2 = 0, l2_count = 0; uint32_t threads_per_l2 = 0, l2_count = 0;
@ -483,7 +501,7 @@ void cpuinfo_arm_mach_init(void) {
/* Assume L2 cache is shared between all cores */ /* Assume L2 cache is shared between all cores */
threads_per_l2 = mach_topology.cores; threads_per_l2 = mach_topology.cores;
l2_count = 1; l2_count = 1;
cpuinfo_log_debug("detected %"PRIu32" L2 caches", l2_count); cpuinfo_log_debug("detected %" PRIu32 " L2 caches", l2_count);
} }
uint32_t threads_per_l3 = 0, l3_count = 0; uint32_t threads_per_l3 = 0, l3_count = 0;
@ -491,24 +509,26 @@ void cpuinfo_arm_mach_init(void) {
/* Assume L3 cache is shared between all cores */ /* Assume L3 cache is shared between all cores */
threads_per_l3 = mach_topology.cores; threads_per_l3 = mach_topology.cores;
l3_count = 1; l3_count = 1;
cpuinfo_log_debug("detected %"PRIu32" L3 caches", l3_count); cpuinfo_log_debug("detected %" PRIu32 " L3 caches", l3_count);
} }
if (l1i_cache_size != 0) { if (l1i_cache_size != 0) {
l1i = calloc(l1_count, sizeof(struct cpuinfo_cache)); l1i = calloc(l1_count, sizeof(struct cpuinfo_cache));
if (l1i == NULL) { if (l1i == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L1I caches", cpuinfo_log_error(
l1_count * sizeof(struct cpuinfo_cache), l1_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L1I caches",
l1_count * sizeof(struct cpuinfo_cache),
l1_count);
goto cleanup; goto cleanup;
} }
for (uint32_t c = 0; c < l1_count; c++) { for (uint32_t c = 0; c < l1_count; c++) {
l1i[c] = (struct cpuinfo_cache) { l1i[c] = (struct cpuinfo_cache){
.size = l1i_cache_size, .size = l1i_cache_size,
.associativity = l1_cache_associativity, .associativity = l1_cache_associativity,
.sets = l1i_cache_size / (l1_cache_associativity * cacheline_size), .sets = l1i_cache_size / (l1_cache_associativity * cacheline_size),
.partitions = cache_partitions, .partitions = cache_partitions,
.line_size = cacheline_size, .line_size = cacheline_size,
.flags = cache_flags, .flags = cache_flags,
.processor_start = c * threads_per_l1, .processor_start = c * threads_per_l1,
.processor_count = threads_per_l1, .processor_count = threads_per_l1,
}; };
@ -521,18 +541,20 @@ void cpuinfo_arm_mach_init(void) {
if (l1d_cache_size != 0) { if (l1d_cache_size != 0) {
l1d = calloc(l1_count, sizeof(struct cpuinfo_cache)); l1d = calloc(l1_count, sizeof(struct cpuinfo_cache));
if (l1d == NULL) { if (l1d == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L1D caches", cpuinfo_log_error(
l1_count * sizeof(struct cpuinfo_cache), l1_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L1D caches",
l1_count * sizeof(struct cpuinfo_cache),
l1_count);
goto cleanup; goto cleanup;
} }
for (uint32_t c = 0; c < l1_count; c++) { for (uint32_t c = 0; c < l1_count; c++) {
l1d[c] = (struct cpuinfo_cache) { l1d[c] = (struct cpuinfo_cache){
.size = l1d_cache_size, .size = l1d_cache_size,
.associativity = l1_cache_associativity, .associativity = l1_cache_associativity,
.sets = l1d_cache_size / (l1_cache_associativity * cacheline_size), .sets = l1d_cache_size / (l1_cache_associativity * cacheline_size),
.partitions = cache_partitions, .partitions = cache_partitions,
.line_size = cacheline_size, .line_size = cacheline_size,
.flags = cache_flags, .flags = cache_flags,
.processor_start = c * threads_per_l1, .processor_start = c * threads_per_l1,
.processor_count = threads_per_l1, .processor_count = threads_per_l1,
}; };
@ -545,18 +567,20 @@ void cpuinfo_arm_mach_init(void) {
if (l2_count != 0) { if (l2_count != 0) {
l2 = calloc(l2_count, sizeof(struct cpuinfo_cache)); l2 = calloc(l2_count, sizeof(struct cpuinfo_cache));
if (l2 == NULL) { if (l2 == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L2 caches", cpuinfo_log_error(
l2_count * sizeof(struct cpuinfo_cache), l2_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L2 caches",
l2_count * sizeof(struct cpuinfo_cache),
l2_count);
goto cleanup; goto cleanup;
} }
for (uint32_t c = 0; c < l2_count; c++) { for (uint32_t c = 0; c < l2_count; c++) {
l2[c] = (struct cpuinfo_cache) { l2[c] = (struct cpuinfo_cache){
.size = l2_cache_size, .size = l2_cache_size,
.associativity = l2_cache_associativity, .associativity = l2_cache_associativity,
.sets = l2_cache_size / (l2_cache_associativity * cacheline_size), .sets = l2_cache_size / (l2_cache_associativity * cacheline_size),
.partitions = cache_partitions, .partitions = cache_partitions,
.line_size = cacheline_size, .line_size = cacheline_size,
.flags = cache_flags, .flags = cache_flags,
.processor_start = c * threads_per_l2, .processor_start = c * threads_per_l2,
.processor_count = threads_per_l2, .processor_count = threads_per_l2,
}; };
@ -569,18 +593,20 @@ void cpuinfo_arm_mach_init(void) {
if (l3_count != 0) { if (l3_count != 0) {
l3 = calloc(l3_count, sizeof(struct cpuinfo_cache)); l3 = calloc(l3_count, sizeof(struct cpuinfo_cache));
if (l3 == NULL) { if (l3 == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L3 caches", cpuinfo_log_error(
l3_count * sizeof(struct cpuinfo_cache), l3_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L3 caches",
l3_count * sizeof(struct cpuinfo_cache),
l3_count);
goto cleanup; goto cleanup;
} }
for (uint32_t c = 0; c < l3_count; c++) { for (uint32_t c = 0; c < l3_count; c++) {
l3[c] = (struct cpuinfo_cache) { l3[c] = (struct cpuinfo_cache){
.size = l3_cache_size, .size = l3_cache_size,
.associativity = l3_cache_associativity, .associativity = l3_cache_associativity,
.sets = l3_cache_size / (l3_cache_associativity * cacheline_size), .sets = l3_cache_size / (l3_cache_associativity * cacheline_size),
.partitions = cache_partitions, .partitions = cache_partitions,
.line_size = cacheline_size, .line_size = cacheline_size,
.flags = cache_flags, .flags = cache_flags,
.processor_start = c * threads_per_l3, .processor_start = c * threads_per_l3,
.processor_count = threads_per_l3, .processor_count = threads_per_l3,
}; };
@ -598,8 +624,8 @@ void cpuinfo_arm_mach_init(void) {
cpuinfo_uarchs = uarchs; cpuinfo_uarchs = uarchs;
cpuinfo_cache[cpuinfo_cache_level_1i] = l1i; cpuinfo_cache[cpuinfo_cache_level_1i] = l1i;
cpuinfo_cache[cpuinfo_cache_level_1d] = l1d; cpuinfo_cache[cpuinfo_cache_level_1d] = l1d;
cpuinfo_cache[cpuinfo_cache_level_2] = l2; cpuinfo_cache[cpuinfo_cache_level_2] = l2;
cpuinfo_cache[cpuinfo_cache_level_3] = l3; cpuinfo_cache[cpuinfo_cache_level_3] = l3;
cpuinfo_processors_count = mach_topology.threads; cpuinfo_processors_count = mach_topology.threads;
cpuinfo_cores_count = mach_topology.cores; cpuinfo_cores_count = mach_topology.cores;
@ -608,8 +634,8 @@ void cpuinfo_arm_mach_init(void) {
cpuinfo_uarchs_count = num_clusters; cpuinfo_uarchs_count = num_clusters;
cpuinfo_cache_count[cpuinfo_cache_level_1i] = l1_count; cpuinfo_cache_count[cpuinfo_cache_level_1i] = l1_count;
cpuinfo_cache_count[cpuinfo_cache_level_1d] = l1_count; cpuinfo_cache_count[cpuinfo_cache_level_1d] = l1_count;
cpuinfo_cache_count[cpuinfo_cache_level_2] = l2_count; cpuinfo_cache_count[cpuinfo_cache_level_2] = l2_count;
cpuinfo_cache_count[cpuinfo_cache_level_3] = l3_count; cpuinfo_cache_count[cpuinfo_cache_level_3] = l3_count;
cpuinfo_max_cache_size = cpuinfo_compute_max_cache_size(&processors[0]); cpuinfo_max_cache_size = cpuinfo_compute_max_cache_size(&processors[0]);
__sync_synchronize(); __sync_synchronize();

View File

@ -1,40 +1,39 @@
#pragma once #pragma once
#include <stdint.h> #include <stdint.h>
#define CPUINFO_ARM_MIDR_IMPLEMENTER_MASK UINT32_C(0xFF000000)
#define CPUINFO_ARM_MIDR_IMPLEMENTER_MASK UINT32_C(0xFF000000) #define CPUINFO_ARM_MIDR_VARIANT_MASK UINT32_C(0x00F00000)
#define CPUINFO_ARM_MIDR_VARIANT_MASK UINT32_C(0x00F00000)
#define CPUINFO_ARM_MIDR_ARCHITECTURE_MASK UINT32_C(0x000F0000) #define CPUINFO_ARM_MIDR_ARCHITECTURE_MASK UINT32_C(0x000F0000)
#define CPUINFO_ARM_MIDR_PART_MASK UINT32_C(0x0000FFF0) #define CPUINFO_ARM_MIDR_PART_MASK UINT32_C(0x0000FFF0)
#define CPUINFO_ARM_MIDR_REVISION_MASK UINT32_C(0x0000000F) #define CPUINFO_ARM_MIDR_REVISION_MASK UINT32_C(0x0000000F)
#define CPUINFO_ARM_MIDR_IMPLEMENTER_OFFSET 24 #define CPUINFO_ARM_MIDR_IMPLEMENTER_OFFSET 24
#define CPUINFO_ARM_MIDR_VARIANT_OFFSET 20 #define CPUINFO_ARM_MIDR_VARIANT_OFFSET 20
#define CPUINFO_ARM_MIDR_ARCHITECTURE_OFFSET 16 #define CPUINFO_ARM_MIDR_ARCHITECTURE_OFFSET 16
#define CPUINFO_ARM_MIDR_PART_OFFSET 4 #define CPUINFO_ARM_MIDR_PART_OFFSET 4
#define CPUINFO_ARM_MIDR_REVISION_OFFSET 0 #define CPUINFO_ARM_MIDR_REVISION_OFFSET 0
#define CPUINFO_ARM_MIDR_ARM1156 UINT32_C(0x410FB560) #define CPUINFO_ARM_MIDR_ARM1156 UINT32_C(0x410FB560)
#define CPUINFO_ARM_MIDR_CORTEX_A7 UINT32_C(0x410FC070) #define CPUINFO_ARM_MIDR_CORTEX_A7 UINT32_C(0x410FC070)
#define CPUINFO_ARM_MIDR_CORTEX_A9 UINT32_C(0x410FC090) #define CPUINFO_ARM_MIDR_CORTEX_A9 UINT32_C(0x410FC090)
#define CPUINFO_ARM_MIDR_CORTEX_A15 UINT32_C(0x410FC0F0) #define CPUINFO_ARM_MIDR_CORTEX_A15 UINT32_C(0x410FC0F0)
#define CPUINFO_ARM_MIDR_CORTEX_A17 UINT32_C(0x410FC0E0) #define CPUINFO_ARM_MIDR_CORTEX_A17 UINT32_C(0x410FC0E0)
#define CPUINFO_ARM_MIDR_CORTEX_A35 UINT32_C(0x410FD040) #define CPUINFO_ARM_MIDR_CORTEX_A35 UINT32_C(0x410FD040)
#define CPUINFO_ARM_MIDR_CORTEX_A53 UINT32_C(0x410FD030) #define CPUINFO_ARM_MIDR_CORTEX_A53 UINT32_C(0x410FD030)
#define CPUINFO_ARM_MIDR_CORTEX_A55 UINT32_C(0x410FD050) #define CPUINFO_ARM_MIDR_CORTEX_A55 UINT32_C(0x410FD050)
#define CPUINFO_ARM_MIDR_CORTEX_A57 UINT32_C(0x410FD070) #define CPUINFO_ARM_MIDR_CORTEX_A57 UINT32_C(0x410FD070)
#define CPUINFO_ARM_MIDR_CORTEX_A72 UINT32_C(0x410FD080) #define CPUINFO_ARM_MIDR_CORTEX_A72 UINT32_C(0x410FD080)
#define CPUINFO_ARM_MIDR_CORTEX_A73 UINT32_C(0x410FD090) #define CPUINFO_ARM_MIDR_CORTEX_A73 UINT32_C(0x410FD090)
#define CPUINFO_ARM_MIDR_CORTEX_A75 UINT32_C(0x410FD0A0) #define CPUINFO_ARM_MIDR_CORTEX_A75 UINT32_C(0x410FD0A0)
#define CPUINFO_ARM_MIDR_KRYO280_GOLD UINT32_C(0x51AF8001) #define CPUINFO_ARM_MIDR_KRYO280_GOLD UINT32_C(0x51AF8001)
#define CPUINFO_ARM_MIDR_KRYO280_SILVER UINT32_C(0x51AF8014) #define CPUINFO_ARM_MIDR_KRYO280_SILVER UINT32_C(0x51AF8014)
#define CPUINFO_ARM_MIDR_KRYO385_GOLD UINT32_C(0x518F802D) #define CPUINFO_ARM_MIDR_KRYO385_GOLD UINT32_C(0x518F802D)
#define CPUINFO_ARM_MIDR_KRYO385_SILVER UINT32_C(0x518F803C) #define CPUINFO_ARM_MIDR_KRYO385_SILVER UINT32_C(0x518F803C)
#define CPUINFO_ARM_MIDR_KRYO_SILVER_821 UINT32_C(0x510F2010) #define CPUINFO_ARM_MIDR_KRYO_SILVER_821 UINT32_C(0x510F2010)
#define CPUINFO_ARM_MIDR_KRYO_GOLD UINT32_C(0x510F2050) #define CPUINFO_ARM_MIDR_KRYO_GOLD UINT32_C(0x510F2050)
#define CPUINFO_ARM_MIDR_KRYO_SILVER_820 UINT32_C(0x510F2110) #define CPUINFO_ARM_MIDR_KRYO_SILVER_820 UINT32_C(0x510F2110)
#define CPUINFO_ARM_MIDR_EXYNOS_M1_M2 UINT32_C(0x530F0010) #define CPUINFO_ARM_MIDR_EXYNOS_M1_M2 UINT32_C(0x530F0010)
#define CPUINFO_ARM_MIDR_DENVER2 UINT32_C(0x4E0F0030) #define CPUINFO_ARM_MIDR_DENVER2 UINT32_C(0x4E0F0030)
inline static uint32_t midr_set_implementer(uint32_t midr, uint32_t implementer) { inline static uint32_t midr_set_implementer(uint32_t midr, uint32_t implementer) {
return (midr & ~CPUINFO_ARM_MIDR_IMPLEMENTER_MASK) | return (midr & ~CPUINFO_ARM_MIDR_IMPLEMENTER_MASK) |
@ -176,7 +175,9 @@ inline static uint32_t midr_score_core(uint32_t midr) {
case UINT32_C(0x4100D440): /* Cortex-X1 */ case UINT32_C(0x4100D440): /* Cortex-X1 */
case UINT32_C(0x4100D480): /* Cortex-X2 */ case UINT32_C(0x4100D480): /* Cortex-X2 */
case UINT32_C(0x4100D4E0): /* Cortex-X3 */ case UINT32_C(0x4100D4E0): /* Cortex-X3 */
/* These cores are in big role w.r.t Cortex-A75/-A76/-A77/-A78/-A710/-A715 */ /* These cores are in big role w.r.t
* Cortex-A75/-A76/-A77/-A78/-A710/-A715
*/
return 6; return 6;
case UINT32_C(0x4100D080): /* Cortex-A72 */ case UINT32_C(0x4100D080): /* Cortex-A72 */
case UINT32_C(0x4100D090): /* Cortex-A73 */ case UINT32_C(0x4100D090): /* Cortex-A73 */
@ -204,7 +205,8 @@ inline static uint32_t midr_score_core(uint32_t midr) {
/* These cores are always in big role */ /* These cores are always in big role */
return 5; return 5;
case UINT32_C(0x4100D070): /* Cortex-A57 */ case UINT32_C(0x4100D070): /* Cortex-A57 */
/* Cortex-A57 can be in LITTLE role w.r.t. Denver 2, or in big role w.r.t. Cortex-A53 */ /* Cortex-A57 can be in LITTLE role w.r.t. Denver 2, or
* in big role w.r.t. Cortex-A53 */
return 4; return 4;
#if CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM64
case UINT32_C(0x4100D060): /* Cortex-A65 */ case UINT32_C(0x4100D060): /* Cortex-A65 */
@ -212,7 +214,8 @@ inline static uint32_t midr_score_core(uint32_t midr) {
case UINT32_C(0x4100D030): /* Cortex-A53 */ case UINT32_C(0x4100D030): /* Cortex-A53 */
case UINT32_C(0x4100D050): /* Cortex-A55 */ case UINT32_C(0x4100D050): /* Cortex-A55 */
case UINT32_C(0x4100D460): /* Cortex-A510 */ case UINT32_C(0x4100D460): /* Cortex-A510 */
/* Cortex-A53 is usually in LITTLE role, but can be in big role w.r.t. Cortex-A35 */ /* Cortex-A53 is usually in LITTLE role, but can be in
* big role w.r.t. Cortex-A35 */
return 2; return 2;
case UINT32_C(0x4100D040): /* Cortex-A35 */ case UINT32_C(0x4100D040): /* Cortex-A35 */
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
@ -227,10 +230,12 @@ inline static uint32_t midr_score_core(uint32_t midr) {
return 1; return 1;
default: default:
/* /*
* Unknown cores, or cores which do not have big/LITTLE roles. * Unknown cores, or cores which do not have big/LITTLE
* To be future-proof w.r.t. cores not yet recognized in cpuinfo, assume position between * roles. To be future-proof w.r.t. cores not yet
* Cortex-A57/A72/A73/A75 and Cortex-A53/A55. Then at least future cores paired with * recognized in cpuinfo, assume position between
* one of these known cores will be properly scored. * Cortex-A57/A72/A73/A75 and Cortex-A53/A55. Then at
* least future cores paired with one of these known
* cores will be properly scored.
*/ */
return 3; return 3;
} }

View File

@ -5,23 +5,24 @@ switch (uarch) {
/* /*
* Cortex-A5 Technical Reference Manual: * Cortex-A5 Technical Reference Manual:
* 6.3.1. Micro TLB * 6.3.1. Micro TLB
* The first level of caching for the page table information is a micro TLB of * The first level of caching for the page table information
* 10 entries that is implemented on each of the instruction and data sides. * is a micro TLB of 10 entries that is implemented on each of
* 6.3.2. Main TLB * the instruction and data sides. 6.3.2. Main TLB Misses from
* Misses from the instruction and data micro TLBs are handled by a unified main TLB. * the instruction and data micro TLBs are handled by a unified
* The main TLB is 128-entry two-way set-associative. * main TLB. The main TLB is 128-entry two-way set-associative.
*/ */
break; break;
case cpuinfo_uarch_cortex_a7: case cpuinfo_uarch_cortex_a7:
/* /*
* Cortex-A7 MPCore Technical Reference Manual: * Cortex-A7 MPCore Technical Reference Manual:
* 5.3.1. Micro TLB * 5.3.1. Micro TLB
* The first level of caching for the page table information is a micro TLB of * The first level of caching for the page table information
* 10 entries that is implemented on each of the instruction and data sides. * is a micro TLB of 10 entries that is implemented on each of
* 5.3.2. Main TLB * the instruction and data sides. 5.3.2. Main TLB Misses from
* Misses from the micro TLBs are handled by a unified main TLB. This is a 256-entry 2-way * the micro TLBs are handled by a unified main TLB. This is a
* set-associative structure. The main TLB supports all the VMSAv7 page sizes of * 256-entry 2-way set-associative structure. The main TLB
* 4KB, 64KB, 1MB and 16MB in addition to the LPAE page sizes of 2MB and 1G. * supports all the VMSAv7 page sizes of 4KB, 64KB, 1MB and 16MB
* in addition to the LPAE page sizes of 2MB and 1G.
*/ */
break; break;
case cpuinfo_uarch_cortex_a8: case cpuinfo_uarch_cortex_a8:
@ -29,7 +30,8 @@ switch (uarch) {
* Cortex-A8 Technical Reference Manual: * Cortex-A8 Technical Reference Manual:
* 6.1. About the MMU * 6.1. About the MMU
* The MMU features include the following: * The MMU features include the following:
* - separate, fully-associative, 32-entry data and instruction TLBs * - separate, fully-associative, 32-entry data and
* instruction TLBs
* - TLB entries that support 4KB, 64KB, 1MB, and 16MB pages * - TLB entries that support 4KB, 64KB, 1MB, and 16MB pages
*/ */
break; break;
@ -37,51 +39,63 @@ switch (uarch) {
/* /*
* ARM CortexA9 Technical Reference Manual: * ARM CortexA9 Technical Reference Manual:
* 6.2.1 Micro TLB * 6.2.1 Micro TLB
* The first level of caching for the page table information is a micro TLB of 32 entries on the data side, * The first level of caching for the page table information
* and configurable 32 or 64 entries on the instruction side. * is a micro TLB of 32 entries on the data side, and
* 6.2.2 Main TLB * configurable 32 or 64 entries on the instruction side. 6.2.2
* The main TLB is implemented as a combination of: * Main TLB The main TLB is implemented as a combination of:
* - A fully-associative, lockable array of four elements. * - A fully-associative, lockable array of four elements.
* - A 2-way associative structure of 2x32, 2x64, 2x128 or 2x256 entries. * - A 2-way associative structure of 2x32, 2x64, 2x128 or
* 2x256 entries.
*/ */
break; break;
case cpuinfo_uarch_cortex_a15: case cpuinfo_uarch_cortex_a15:
/* /*
* ARM Cortex-A15 MPCore Processor Technical Reference Manual: * ARM Cortex-A15 MPCore Processor Technical Reference Manual:
* 5.2.1. L1 instruction TLB * 5.2.1. L1 instruction TLB
* The L1 instruction TLB is a 32-entry fully-associative structure. This TLB caches entries at the 4KB * The L1 instruction TLB is a 32-entry fully-associative
* granularity of Virtual Address (VA) to Physical Address (PA) mapping only. If the page tables map the * structure. This TLB caches entries at the 4KB granularity of
* memory region to a larger granularity than 4K, it only allocates one mapping for the particular 4K region * Virtual Address (VA) to Physical Address (PA) mapping only.
* to which the current access corresponds. * If the page tables map the memory region to a larger
* 5.2.2. L1 data TLB * granularity than 4K, it only allocates one mapping for the
* There are two separate 32-entry fully-associative TLBs that are used for data loads and stores, * particular 4K region to which the current access
* respectively. Similar to the L1 instruction TLB, both of these cache entries at the 4KB granularity of * corresponds. 5.2.2. L1 data TLB There are two separate
* VA to PA mappings only. At implementation time, the Cortex-A15 MPCore processor can be configured with * 32-entry fully-associative TLBs that are used for data loads
* the -l1tlb_1m option, to have the L1 data TLB cache entries at both the 4KB and 1MB granularity. * and stores, respectively. Similar to the L1 instruction TLB,
* With this configuration, any translation that results in a 1MB or larger page is cached in the L1 data * both of these cache entries at the 4KB granularity of VA to
* TLB as a 1MB entry. Any translation that results in a page smaller than 1MB is cached in the L1 data TLB * PA mappings only. At implementation time, the Cortex-A15
* as a 4KB entry. By default, all translations are cached in the L1 data TLB as a 4KB entry. * MPCore processor can be configured with the -l1tlb_1m option,
* 5.2.3. L2 TLB * to have the L1 data TLB cache entries at both the 4KB and 1MB
* Misses from the L1 instruction and data TLBs are handled by a unified L2 TLB. This is a 512-entry 4-way * granularity. With this configuration, any translation that
* set-associative structure. The L2 TLB supports all the VMSAv7 page sizes of 4K, 64K, 1MB and 16MB in * results in a 1MB or larger page is cached in the L1 data TLB
* addition to the LPAE page sizes of 2MB and 1GB. * as a 1MB entry. Any translation that results in a page
* smaller than 1MB is cached in the L1 data TLB as a 4KB entry.
* By default, all translations are cached in the L1 data TLB as
* a 4KB entry. 5.2.3. L2 TLB Misses from the L1 instruction and
* data TLBs are handled by a unified L2 TLB. This is a
* 512-entry 4-way set-associative structure. The L2 TLB
* supports all the VMSAv7 page sizes of 4K, 64K, 1MB and 16MB
* in addition to the LPAE page sizes of 2MB and 1GB.
*/ */
break; break;
case cpuinfo_uarch_cortex_a17: case cpuinfo_uarch_cortex_a17:
/* /*
* ARM Cortex-A17 MPCore Processor Technical Reference Manual: * ARM Cortex-A17 MPCore Processor Technical Reference Manual:
* 5.2.1. Instruction micro TLB * 5.2.1. Instruction micro TLB
* The instruction micro TLB is implemented as a 32, 48 or 64 entry, fully-associative structure. This TLB * The instruction micro TLB is implemented as a 32, 48 or 64
* caches entries at the 4KB and 1MB granularity of Virtual Address (VA) to Physical Address (PA) mapping * entry, fully-associative structure. This TLB caches entries
* only. If the translation tables map the memory region to a larger granularity than 4KB or 1MB, it only * at the 4KB and 1MB granularity of Virtual Address (VA) to
* allocates one mapping for the particular 4KB region to which the current access corresponds. * Physical Address (PA) mapping only. If the translation tables
* 5.2.2. Data micro TLB * map the memory region to a larger granularity than 4KB or
* The data micro TLB is a 32 entry fully-associative TLB that is used for data loads and stores. The cache * 1MB, it only allocates one mapping for the particular 4KB
* entries have a 4KB and 1MB granularity of VA to PA mappings only. * region to which the current access corresponds. 5.2.2. Data
* 5.2.3. Unified main TLB * micro TLB The data micro TLB is a 32 entry fully-associative
* Misses from the instruction and data micro TLBs are handled by a unified main TLB. This is a 1024 entry * TLB that is used for data loads and stores. The cache entries
* 4-way set-associative structure. The main TLB supports all the VMSAv7 page sizes of 4K, 64K, 1MB and 16MB * have a 4KB and 1MB granularity of VA to PA mappings
* in addition to the LPAE page sizes of 2MB and 1GB. * only. 5.2.3. Unified main TLB Misses from the instruction and
* data micro TLBs are handled by a unified main TLB. This is a
* 1024 entry 4-way set-associative structure. The main TLB
* supports all the VMSAv7 page sizes of 4K, 64K, 1MB and 16MB
* in addition to the LPAE page sizes of 2MB and 1GB.
*/ */
break; break;
case cpuinfo_uarch_cortex_a35: case cpuinfo_uarch_cortex_a35:
@ -89,45 +103,52 @@ switch (uarch) {
* ARM CortexA35 Processor Technical Reference Manual: * ARM CortexA35 Processor Technical Reference Manual:
* A6.2 TLB Organization * A6.2 TLB Organization
* Micro TLB * Micro TLB
* The first level of caching for the translation table information is a micro TLB of ten entries that * The first level of caching for the translation table
* is implemented on each of the instruction and data sides. * information is a micro TLB of ten entries that is implemented
* Main TLB * on each of the instruction and data sides. Main TLB A unified
* A unified main TLB handles misses from the micro TLBs. It has a 512-entry, 2-way, set-associative * main TLB handles misses from the micro TLBs. It has a
* structure and supports all VMSAv8 block sizes, except 1GB. If it fetches a 1GB block, the TLB splits * 512-entry, 2-way, set-associative structure and supports all
* it into 512MB blocks and stores the appropriate block for the lookup. * VMSAv8 block sizes, except 1GB. If it fetches a 1GB block,
* the TLB splits it into 512MB blocks and stores the
* appropriate block for the lookup.
*/ */
break; break;
case cpuinfo_uarch_cortex_a53: case cpuinfo_uarch_cortex_a53:
/* /*
* ARM Cortex-A53 MPCore Processor Technical Reference Manual: * ARM Cortex-A53 MPCore Processor Technical Reference Manual:
* 5.2.1. Micro TLB * 5.2.1. Micro TLB
* The first level of caching for the translation table information is a micro TLB of ten entries that is * The first level of caching for the translation table
* implemented on each of the instruction and data sides. * information is a micro TLB of ten entries that is implemented
* 5.2.2. Main TLB * on each of the instruction and data sides. 5.2.2. Main TLB A
* A unified main TLB handles misses from the micro TLBs. This is a 512-entry, 4-way, set-associative * unified main TLB handles misses from the micro TLBs. This is
* structure. The main TLB supports all VMSAv8 block sizes, except 1GB. If a 1GB block is fetched, it is * a 512-entry, 4-way, set-associative structure. The main TLB
* split into 512MB blocks and the appropriate block for the lookup stored. * supports all VMSAv8 block sizes, except 1GB. If a 1GB block
* is fetched, it is split into 512MB blocks and the appropriate
* block for the lookup stored.
*/ */
break; break;
case cpuinfo_uarch_cortex_a57: case cpuinfo_uarch_cortex_a57:
/* /*
* ARM® Cortex-A57 MPCore Processor Technical Reference Manual: * ARM® Cortex-A57 MPCore Processor Technical Reference Manual:
* 5.2.1 L1 instruction TLB * 5.2.1 L1 instruction TLB
* The L1 instruction TLB is a 48-entry fully-associative structure. This TLB caches entries of three * The L1 instruction TLB is a 48-entry fully-associative
* different page sizes, natively 4KB, 64KB, and 1MB, of VA to PA mappings. If the page tables map the memory * structure. This TLB caches entries of three different page
* region to a larger granularity than 1MB, it only allocates one mapping for the particular 1MB region to * sizes, natively 4KB, 64KB, and 1MB, of VA to PA mappings. If
* which the current access corresponds. * the page tables map the memory region to a larger granularity
* 5.2.2 L1 data TLB * than 1MB, it only allocates one mapping for the particular
* The L1 data TLB is a 32-entry fully-associative TLB that is used for data loads and stores. This TLB * 1MB region to which the current access corresponds. 5.2.2 L1
* caches entries of three different page sizes, natively 4KB, 64KB, and 1MB, of VA to PA mappings. * data TLB The L1 data TLB is a 32-entry fully-associative TLB
* 5.2.3 L2 TLB * that is used for data loads and stores. This TLB caches
* Misses from the L1 instruction and data TLBs are handled by a unified L2 TLB. This is a 1024-entry 4-way * entries of three different page sizes, natively 4KB, 64KB,
* set-associative structure. The L2 TLB supports the page sizes of 4K, 64K, 1MB and 16MB. It also supports * and 1MB, of VA to PA mappings. 5.2.3 L2 TLB Misses from the
* page sizes of 2MB and 1GB for the long descriptor format translation in AArch32 state and in AArch64 state * L1 instruction and data TLBs are handled by a unified L2 TLB.
* when using the 4KB translation granule. In addition, the L2 TLB supports the 512MB page map size defined * This is a 1024-entry 4-way set-associative structure. The L2
* for the AArch64 translations that use a 64KB translation granule. * TLB supports the page sizes of 4K, 64K, 1MB and 16MB. It also
* supports page sizes of 2MB and 1GB for the long descriptor
* format translation in AArch32 state and in AArch64 state when
* using the 4KB translation granule. In addition, the L2 TLB
* supports the 512MB page map size defined for the AArch64
* translations that use a 64KB translation granule.
*/ */
break; break;
} }

View File

@ -4,15 +4,13 @@
#include <arm/midr.h> #include <arm/midr.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
void cpuinfo_arm_decode_vendor_uarch( void cpuinfo_arm_decode_vendor_uarch(
uint32_t midr, uint32_t midr,
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
bool has_vfpv4, bool has_vfpv4,
#endif /* CPUINFO_ARCH_ARM */ #endif /* CPUINFO_ARCH_ARM */
enum cpuinfo_vendor vendor[restrict static 1], enum cpuinfo_vendor vendor[restrict static 1],
enum cpuinfo_uarch uarch[restrict static 1]) enum cpuinfo_uarch uarch[restrict static 1]) {
{
switch (midr_get_implementer(midr)) { switch (midr_get_implementer(midr)) {
case 'A': case 'A':
*vendor = cpuinfo_vendor_arm; *vendor = cpuinfo_vendor_arm;
@ -39,8 +37,9 @@ void cpuinfo_arm_decode_vendor_uarch(
case 0xC0D: case 0xC0D:
/* /*
* Rockchip RK3288 only. * Rockchip RK3288 only.
* Core information is ambiguous: some sources specify Cortex-A12, others - Cortex-A17. * Core information is ambiguous: some
* Assume it is Cortex-A12. * sources specify Cortex-A12, others -
* Cortex-A17. Assume it is Cortex-A12.
*/ */
*uarch = cpuinfo_uarch_cortex_a12; *uarch = cpuinfo_uarch_cortex_a12;
break; break;
@ -58,9 +57,11 @@ void cpuinfo_arm_decode_vendor_uarch(
*uarch = cpuinfo_uarch_cortex_a35; *uarch = cpuinfo_uarch_cortex_a35;
break; break;
case 0xD05: case 0xD05:
// Note: use Variant, not Revision, field // Note: use Variant, not Revision,
*uarch = (midr & CPUINFO_ARM_MIDR_VARIANT_MASK) == 0 ? // field
cpuinfo_uarch_cortex_a55r0 : cpuinfo_uarch_cortex_a55; *uarch = (midr & CPUINFO_ARM_MIDR_VARIANT_MASK) == 0
? cpuinfo_uarch_cortex_a55r0
: cpuinfo_uarch_cortex_a55;
break; break;
case 0xD06: case 0xD06:
*uarch = cpuinfo_uarch_cortex_a65; *uarch = cpuinfo_uarch_cortex_a65;
@ -138,7 +139,9 @@ void cpuinfo_arm_decode_vendor_uarch(
break; break;
#endif /* CPUINFO_ARCH_ARM */ #endif /* CPUINFO_ARCH_ARM */
default: default:
cpuinfo_log_warning("unknown ARM CPU part 0x%03"PRIx32" ignored", midr_get_part(midr)); cpuinfo_log_warning(
"unknown ARM CPU part 0x%03" PRIx32 " ignored",
midr_get_part(midr));
} }
} }
break; break;
@ -153,13 +156,17 @@ void cpuinfo_arm_decode_vendor_uarch(
break; break;
#if CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM64
case 0x516: case 0x516:
/* Broadcom Vulkan was sold to Cavium before it reached the market, so we identify it as Cavium ThunderX2 */ /* Broadcom Vulkan was sold to Cavium
* before it reached the market, so we
* identify it as Cavium ThunderX2 */
*vendor = cpuinfo_vendor_cavium; *vendor = cpuinfo_vendor_cavium;
*uarch = cpuinfo_uarch_thunderx2; *uarch = cpuinfo_uarch_thunderx2;
break; break;
#endif /* CPUINFO_ARCH_ARM64 */ #endif /* CPUINFO_ARCH_ARM64 */
default: default:
cpuinfo_log_warning("unknown Broadcom CPU part 0x%03"PRIx32" ignored", midr_get_part(midr)); cpuinfo_log_warning(
"unknown Broadcom CPU part 0x%03" PRIx32 " ignored",
midr_get_part(midr));
} }
break; break;
#if CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM64
@ -176,7 +183,8 @@ void cpuinfo_arm_decode_vendor_uarch(
*uarch = cpuinfo_uarch_thunderx2; *uarch = cpuinfo_uarch_thunderx2;
break; break;
default: default:
cpuinfo_log_warning("unknown Cavium CPU part 0x%03"PRIx32" ignored", midr_get_part(midr)); cpuinfo_log_warning(
"unknown Cavium CPU part 0x%03" PRIx32 " ignored", midr_get_part(midr));
} }
break; break;
#endif /* CPUINFO_ARCH_ARM64 */ #endif /* CPUINFO_ARCH_ARM64 */
@ -188,12 +196,14 @@ void cpuinfo_arm_decode_vendor_uarch(
*uarch = cpuinfo_uarch_taishan_v110; *uarch = cpuinfo_uarch_taishan_v110;
break; break;
#endif /* CPUINFO_ARCH_ARM64 */ #endif /* CPUINFO_ARCH_ARM64 */
case 0xD40: /* Kirin 980 Big/Medium cores -> Cortex-A76 */ case 0xD40: /* Kirin 980 Big/Medium cores ->
Cortex-A76 */
*vendor = cpuinfo_vendor_arm; *vendor = cpuinfo_vendor_arm;
*uarch = cpuinfo_uarch_cortex_a76; *uarch = cpuinfo_uarch_cortex_a76;
break; break;
default: default:
cpuinfo_log_warning("unknown Huawei CPU part 0x%03"PRIx32" ignored", midr_get_part(midr)); cpuinfo_log_warning(
"unknown Huawei CPU part 0x%03" PRIx32 " ignored", midr_get_part(midr));
} }
break; break;
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
@ -206,7 +216,8 @@ void cpuinfo_arm_decode_vendor_uarch(
*uarch = cpuinfo_uarch_xscale; *uarch = cpuinfo_uarch_xscale;
break; break;
default: default:
cpuinfo_log_warning("unknown Intel CPU part 0x%03"PRIx32" ignored", midr_get_part(midr)); cpuinfo_log_warning(
"unknown Intel CPU part 0x%03" PRIx32 " ignored", midr_get_part(midr));
} }
break; break;
#endif /* CPUINFO_ARCH_ARM */ #endif /* CPUINFO_ARCH_ARM */
@ -223,7 +234,8 @@ void cpuinfo_arm_decode_vendor_uarch(
*uarch = cpuinfo_uarch_carmel; *uarch = cpuinfo_uarch_carmel;
break; break;
default: default:
cpuinfo_log_warning("unknown Nvidia CPU part 0x%03"PRIx32" ignored", midr_get_part(midr)); cpuinfo_log_warning(
"unknown Nvidia CPU part 0x%03" PRIx32 " ignored", midr_get_part(midr));
} }
break; break;
case 'P': case 'P':
@ -233,7 +245,9 @@ void cpuinfo_arm_decode_vendor_uarch(
*uarch = cpuinfo_uarch_xgene; *uarch = cpuinfo_uarch_xgene;
break; break;
default: default:
cpuinfo_log_warning("unknown Applied Micro CPU part 0x%03"PRIx32" ignored", midr_get_part(midr)); cpuinfo_log_warning(
"unknown Applied Micro CPU part 0x%03" PRIx32 " ignored",
midr_get_part(midr));
} }
break; break;
case 'Q': case 'Q':
@ -241,9 +255,12 @@ void cpuinfo_arm_decode_vendor_uarch(
switch (midr_get_part(midr)) { switch (midr_get_part(midr)) {
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
case 0x00F: case 0x00F:
/* Mostly Scorpions, but some Cortex A5 may report this value as well */ /* Mostly Scorpions, but some Cortex A5
* may report this value as well
*/
if (has_vfpv4) { if (has_vfpv4) {
/* Unlike Scorpion, Cortex-A5 comes with VFPv4 */ /* Unlike Scorpion, Cortex-A5
* comes with VFPv4 */
*vendor = cpuinfo_vendor_arm; *vendor = cpuinfo_vendor_arm;
*uarch = cpuinfo_uarch_cortex_a5; *uarch = cpuinfo_uarch_cortex_a5;
} else { } else {
@ -266,39 +283,51 @@ void cpuinfo_arm_decode_vendor_uarch(
* - r0p1 -> Krait 200 * - r0p1 -> Krait 200
* - r0p2 -> Krait 200 * - r0p2 -> Krait 200
* - r1p0 -> Krait 300 * - r1p0 -> Krait 300
* - r2p0 -> Krait 400 (Snapdragon 800 MSMxxxx) * - r2p0 -> Krait 400 (Snapdragon 800
* - r2p1 -> Krait 400 (Snapdragon 801 MSMxxxxPRO) * MSMxxxx)
* - r2p1 -> Krait 400 (Snapdragon 801
* MSMxxxxPRO)
* - r3p1 -> Krait 450 * - r3p1 -> Krait 450
*/ */
*uarch = cpuinfo_uarch_krait; *uarch = cpuinfo_uarch_krait;
break; break;
#endif /* CPUINFO_ARCH_ARM */ #endif /* CPUINFO_ARCH_ARM */
case 0x201: /* Qualcomm Snapdragon 821: Low-power Kryo "Silver" */ case 0x201: /* Qualcomm Snapdragon 821:
case 0x205: /* Qualcomm Snapdragon 820 & 821: High-performance Kryo "Gold" */ Low-power Kryo "Silver" */
case 0x211: /* Qualcomm Snapdragon 820: Low-power Kryo "Silver" */ case 0x205: /* Qualcomm Snapdragon 820 & 821:
High-performance Kryo "Gold" */
case 0x211: /* Qualcomm Snapdragon 820:
Low-power Kryo "Silver" */
*uarch = cpuinfo_uarch_kryo; *uarch = cpuinfo_uarch_kryo;
break; break;
case 0x800: /* High-performance Kryo 260 (r10p2) / Kryo 280 (r10p1) "Gold" -> Cortex-A73 */ case 0x800: /* High-performance Kryo 260 (r10p2)
/ Kryo 280 (r10p1) "Gold" ->
Cortex-A73 */
*vendor = cpuinfo_vendor_arm; *vendor = cpuinfo_vendor_arm;
*uarch = cpuinfo_uarch_cortex_a73; *uarch = cpuinfo_uarch_cortex_a73;
break; break;
case 0x801: /* Low-power Kryo 260 / 280 "Silver" -> Cortex-A53 */ case 0x801: /* Low-power Kryo 260 / 280 "Silver"
-> Cortex-A53 */
*vendor = cpuinfo_vendor_arm; *vendor = cpuinfo_vendor_arm;
*uarch = cpuinfo_uarch_cortex_a53; *uarch = cpuinfo_uarch_cortex_a53;
break; break;
case 0x802: /* High-performance Kryo 385 "Gold" -> Cortex-A75 */ case 0x802: /* High-performance Kryo 385 "Gold"
-> Cortex-A75 */
*vendor = cpuinfo_vendor_arm; *vendor = cpuinfo_vendor_arm;
*uarch = cpuinfo_uarch_cortex_a75; *uarch = cpuinfo_uarch_cortex_a75;
break; break;
case 0x803: /* Low-power Kryo 385 "Silver" -> Cortex-A55r0 */ case 0x803: /* Low-power Kryo 385 "Silver" ->
Cortex-A55r0 */
*vendor = cpuinfo_vendor_arm; *vendor = cpuinfo_vendor_arm;
*uarch = cpuinfo_uarch_cortex_a55r0; *uarch = cpuinfo_uarch_cortex_a55r0;
break; break;
case 0x804: /* High-performance Kryo 485 "Gold" / "Gold Prime" -> Cortex-A76 */ case 0x804: /* High-performance Kryo 485 "Gold"
/ "Gold Prime" -> Cortex-A76 */
*vendor = cpuinfo_vendor_arm; *vendor = cpuinfo_vendor_arm;
*uarch = cpuinfo_uarch_cortex_a76; *uarch = cpuinfo_uarch_cortex_a76;
break; break;
case 0x805: /* Low-performance Kryo 485 "Silver" -> Cortex-A55 */ case 0x805: /* Low-performance Kryo 485 "Silver"
-> Cortex-A55 */
*vendor = cpuinfo_vendor_arm; *vendor = cpuinfo_vendor_arm;
*uarch = cpuinfo_uarch_cortex_a55; *uarch = cpuinfo_uarch_cortex_a55;
break; break;
@ -311,7 +340,9 @@ void cpuinfo_arm_decode_vendor_uarch(
break; break;
#endif /* CPUINFO_ARCH_ARM64 */ #endif /* CPUINFO_ARCH_ARM64 */
default: default:
cpuinfo_log_warning("unknown Qualcomm CPU part 0x%03"PRIx32" ignored", midr_get_part(midr)); cpuinfo_log_warning(
"unknown Qualcomm CPU part 0x%03" PRIx32 " ignored",
midr_get_part(midr));
} }
break; break;
case 'S': case 'S':
@ -319,7 +350,8 @@ void cpuinfo_arm_decode_vendor_uarch(
switch (midr & (CPUINFO_ARM_MIDR_VARIANT_MASK | CPUINFO_ARM_MIDR_PART_MASK)) { switch (midr & (CPUINFO_ARM_MIDR_VARIANT_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
case 0x00100010: case 0x00100010:
/* /*
* Exynos 8890 MIDR = 0x531F0011, assume Exynos M1 has: * Exynos 8890 MIDR = 0x531F0011, assume
* Exynos M1 has:
* - CPU variant 0x1 * - CPU variant 0x1
* - CPU part 0x001 * - CPU part 0x001
*/ */
@ -327,7 +359,8 @@ void cpuinfo_arm_decode_vendor_uarch(
break; break;
case 0x00400010: case 0x00400010:
/* /*
* Exynos 8895 MIDR = 0x534F0010, assume Exynos M2 has: * Exynos 8895 MIDR = 0x534F0010, assume
* Exynos M2 has:
* - CPU variant 0x4 * - CPU variant 0x4
* - CPU part 0x001 * - CPU part 0x001
*/ */
@ -335,7 +368,8 @@ void cpuinfo_arm_decode_vendor_uarch(
break; break;
case 0x00100020: case 0x00100020:
/* /*
* Exynos 9810 MIDR = 0x531F0020, assume Exynos M3 has: * Exynos 9810 MIDR = 0x531F0020, assume
* Exynos M3 has:
* - CPU variant 0x1 * - CPU variant 0x1
* - CPU part 0x002 * - CPU part 0x002
*/ */
@ -343,7 +377,8 @@ void cpuinfo_arm_decode_vendor_uarch(
break; break;
case 0x00100030: case 0x00100030:
/* /*
* Exynos 9820 MIDR = 0x531F0030, assume Exynos M4 has: * Exynos 9820 MIDR = 0x531F0030, assume
* Exynos M4 has:
* - CPU variant 0x1 * - CPU variant 0x1
* - CPU part 0x003 * - CPU part 0x003
*/ */
@ -351,15 +386,19 @@ void cpuinfo_arm_decode_vendor_uarch(
break; break;
case 0x00100040: case 0x00100040:
/* /*
* Exynos 9820 MIDR = 0x531F0040, assume Exynos M5 has: * Exynos 9820 MIDR = 0x531F0040, assume
* Exynos M5 has:
* - CPU variant 0x1 * - CPU variant 0x1
* - CPU part 0x004 * - CPU part 0x004
*/ */
*uarch = cpuinfo_uarch_exynos_m5; *uarch = cpuinfo_uarch_exynos_m5;
break; break;
default: default:
cpuinfo_log_warning("unknown Samsung CPU variant 0x%01"PRIx32" part 0x%03"PRIx32" ignored", cpuinfo_log_warning(
midr_get_variant(midr), midr_get_part(midr)); "unknown Samsung CPU variant 0x%01" PRIx32 " part 0x%03" PRIx32
" ignored",
midr_get_variant(midr),
midr_get_part(midr));
} }
break; break;
#if CPUINFO_ARCH_ARM #if CPUINFO_ARCH_ARM
@ -371,12 +410,17 @@ void cpuinfo_arm_decode_vendor_uarch(
*uarch = cpuinfo_uarch_pj4; *uarch = cpuinfo_uarch_pj4;
break; break;
default: default:
cpuinfo_log_warning("unknown Marvell CPU part 0x%03"PRIx32" ignored", midr_get_part(midr)); cpuinfo_log_warning(
"unknown Marvell CPU part 0x%03" PRIx32 " ignored",
midr_get_part(midr));
} }
break; break;
#endif /* CPUINFO_ARCH_ARM */ #endif /* CPUINFO_ARCH_ARM */
default: default:
cpuinfo_log_warning("unknown CPU implementer '%c' (0x%02"PRIx32") with CPU part 0x%03"PRIx32" ignored", cpuinfo_log_warning(
(char) midr_get_implementer(midr), midr_get_implementer(midr), midr_get_part(midr)); "unknown CPU implementer '%c' (0x%02" PRIx32 ") with CPU part 0x%03" PRIx32 " ignored",
(char)midr_get_implementer(midr),
midr_get_implementer(midr),
midr_get_part(midr));
} }
} }

View File

@ -1,9 +1,9 @@
#include <stdio.h> #include <errno.h>
#include <malloc.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <malloc.h>
#include <errno.h>
#include <sys/types.h> #include <sys/types.h>
#include <cpuinfo.h> #include <cpuinfo.h>
@ -12,7 +12,7 @@
#include "windows-arm-init.h" #include "windows-arm-init.h"
#define MAX_NR_OF_CACHES (cpuinfo_cache_level_max - 1) #define MAX_NR_OF_CACHES (cpuinfo_cache_level_max - 1)
/* Call chain: /* Call chain:
* cpu_info_init_by_logical_sys_info * cpu_info_init_by_logical_sys_info
@ -27,30 +27,28 @@
* store_cache_info_per_processor * store_cache_info_per_processor
*/ */
static uint32_t count_logical_processors( static uint32_t count_logical_processors(const uint32_t max_group_count, uint32_t* global_proc_index_per_group);
const uint32_t max_group_count,
uint32_t* global_proc_index_per_group);
static uint32_t read_packages_for_processors( static uint32_t read_packages_for_processors(
struct cpuinfo_processor* processors, struct cpuinfo_processor* processors,
const uint32_t number_of_processors, const uint32_t number_of_processors,
const uint32_t* global_proc_index_per_group, const uint32_t* global_proc_index_per_group,
const struct woa_chip_info *chip_info); const struct woa_chip_info* chip_info);
static uint32_t read_cores_for_processors( static uint32_t read_cores_for_processors(
struct cpuinfo_processor* processors, struct cpuinfo_processor* processors,
const uint32_t number_of_processors, const uint32_t number_of_processors,
const uint32_t* global_proc_index_per_group, const uint32_t* global_proc_index_per_group,
struct cpuinfo_core* cores, struct cpuinfo_core* cores,
const struct woa_chip_info *chip_info); const struct woa_chip_info* chip_info);
static uint32_t read_caches_for_processors( static uint32_t read_caches_for_processors(
struct cpuinfo_processor *processors, struct cpuinfo_processor* processors,
const uint32_t number_of_processors, const uint32_t number_of_processors,
struct cpuinfo_cache *caches, struct cpuinfo_cache* caches,
uint32_t* numbers_of_caches, uint32_t* numbers_of_caches,
const uint32_t* global_proc_index_per_group, const uint32_t* global_proc_index_per_group,
const struct woa_chip_info *chip_info); const struct woa_chip_info* chip_info);
static uint32_t read_all_logical_processor_info_of_relation( static uint32_t read_all_logical_processor_info_of_relation(
LOGICAL_PROCESSOR_RELATIONSHIP info_type, LOGICAL_PROCESSOR_RELATIONSHIP info_type,
@ -60,7 +58,7 @@ static uint32_t read_all_logical_processor_info_of_relation(
uint32_t* numbers_of_caches, uint32_t* numbers_of_caches,
struct cpuinfo_core* cores, struct cpuinfo_core* cores,
const uint32_t* global_proc_index_per_group, const uint32_t* global_proc_index_per_group,
const struct woa_chip_info *chip_info); const struct woa_chip_info* chip_info);
static bool parse_relation_processor_info( static bool parse_relation_processor_info(
struct cpuinfo_processor* processors, struct cpuinfo_processor* processors,
@ -69,7 +67,7 @@ static bool parse_relation_processor_info(
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info, PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info,
const uint32_t info_id, const uint32_t info_id,
struct cpuinfo_core* cores, struct cpuinfo_core* cores,
const struct woa_chip_info *chip_info); const struct woa_chip_info* chip_info);
static bool parse_relation_cache_info( static bool parse_relation_cache_info(
struct cpuinfo_processor* processors, struct cpuinfo_processor* processors,
@ -91,7 +89,7 @@ static void store_core_info_per_processor(
const uint32_t core_id, const uint32_t core_id,
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX core_info, PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX core_info,
struct cpuinfo_core* cores, struct cpuinfo_core* cores,
const struct woa_chip_info *chip_info); const struct woa_chip_info* chip_info);
static void store_cache_info_per_processor( static void store_cache_info_per_processor(
struct cpuinfo_processor* processors, struct cpuinfo_processor* processors,
@ -112,11 +110,7 @@ static bool connect_packages_cores_clusters_by_processors(
static inline uint32_t low_index_from_kaffinity(KAFFINITY kaffinity); static inline uint32_t low_index_from_kaffinity(KAFFINITY kaffinity);
bool cpu_info_init_by_logical_sys_info(const struct woa_chip_info* chip_info, const enum cpuinfo_vendor vendor) {
bool cpu_info_init_by_logical_sys_info(
const struct woa_chip_info *chip_info,
const enum cpuinfo_vendor vendor)
{
struct cpuinfo_processor* processors = NULL; struct cpuinfo_processor* processors = NULL;
struct cpuinfo_package* packages = NULL; struct cpuinfo_package* packages = NULL;
struct cpuinfo_cluster* clusters = NULL; struct cpuinfo_cluster* clusters = NULL;
@ -135,140 +129,149 @@ bool cpu_info_init_by_logical_sys_info(
HANDLE heap = GetProcessHeap(); HANDLE heap = GetProcessHeap();
/* 1. Count available logical processor groups and processors */ /* 1. Count available logical processor groups and processors */
const uint32_t max_group_count = (uint32_t) GetMaximumProcessorGroupCount(); const uint32_t max_group_count = (uint32_t)GetMaximumProcessorGroupCount();
cpuinfo_log_debug("detected %"PRIu32" processor group(s)", max_group_count); cpuinfo_log_debug("detected %" PRIu32 " processor group(s)", max_group_count);
/* We need to store the absolute processor ID offsets for every groups, because /* We need to store the absolute processor ID offsets for every groups,
* because
* 1. We can't assume every processor groups include the same number of * 1. We can't assume every processor groups include the same number of
* logical processors. * logical processors.
* 2. Every processor groups know its group number and processor IDs within * 2. Every processor groups know its group number and processor IDs
* the group, but not the global processor IDs. * within the group, but not the global processor IDs.
* 3. We need to list every logical processors by global IDs. * 3. We need to list every logical processors by global IDs.
*/ */
uint32_t* global_proc_index_per_group = uint32_t* global_proc_index_per_group = (uint32_t*)HeapAlloc(heap, 0, max_group_count * sizeof(uint32_t));
(uint32_t*) HeapAlloc(heap, 0, max_group_count * sizeof(uint32_t));
if (global_proc_index_per_group == NULL) { if (global_proc_index_per_group == NULL) {
cpuinfo_log_error( cpuinfo_log_error(
"failed to allocate %zu bytes for descriptions of %"PRIu32" processor groups", "failed to allocate %zu bytes for descriptions of %" PRIu32 " processor groups",
max_group_count * sizeof(struct cpuinfo_processor), max_group_count); max_group_count * sizeof(struct cpuinfo_processor),
max_group_count);
goto clean_up; goto clean_up;
} }
uint32_t nr_of_processors = uint32_t nr_of_processors = count_logical_processors(max_group_count, global_proc_index_per_group);
count_logical_processors(max_group_count, global_proc_index_per_group);
processors = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_processors * sizeof(struct cpuinfo_processor)); processors = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_processors * sizeof(struct cpuinfo_processor));
if (processors == NULL) { if (processors == NULL) {
cpuinfo_log_error( cpuinfo_log_error(
"failed to allocate %zu bytes for descriptions of %"PRIu32" logical processors", "failed to allocate %zu bytes for descriptions of %" PRIu32 " logical processors",
nr_of_processors * sizeof(struct cpuinfo_processor), nr_of_processors); nr_of_processors * sizeof(struct cpuinfo_processor),
nr_of_processors);
goto clean_up; goto clean_up;
} }
/* 2. Read topology information via MSDN API: packages, cores and caches*/ /* 2. Read topology information via MSDN API: packages, cores and
nr_of_packages = read_packages_for_processors( * caches*/
processors, nr_of_processors, nr_of_packages =
global_proc_index_per_group, read_packages_for_processors(processors, nr_of_processors, global_proc_index_per_group, chip_info);
chip_info);
if (!nr_of_packages) { if (!nr_of_packages) {
cpuinfo_log_error("error in reading package information"); cpuinfo_log_error("error in reading package information");
goto clean_up; goto clean_up;
} }
cpuinfo_log_debug("detected %"PRIu32" processor package(s)", nr_of_packages); cpuinfo_log_debug("detected %" PRIu32 " processor package(s)", nr_of_packages);
/* We need the EfficiencyClass to parse uarch from the core information, /* We need the EfficiencyClass to parse uarch from the core information,
* but we need to iterate first to count cores and allocate memory then * but we need to iterate first to count cores and allocate memory then
* we will iterate again to read and store data to cpuinfo_core structures. * we will iterate again to read and store data to cpuinfo_core
* structures.
*/ */
nr_of_cores = read_cores_for_processors( nr_of_cores =
processors, nr_of_processors, read_cores_for_processors(processors, nr_of_processors, global_proc_index_per_group, NULL, chip_info);
global_proc_index_per_group, NULL,
chip_info);
if (!nr_of_cores) { if (!nr_of_cores) {
cpuinfo_log_error("error in reading core information"); cpuinfo_log_error("error in reading core information");
goto clean_up; goto clean_up;
} }
cpuinfo_log_debug("detected %"PRIu32" processor core(s)", nr_of_cores); cpuinfo_log_debug("detected %" PRIu32 " processor core(s)", nr_of_cores);
/* There is no API to read number of caches, so we need to iterate twice on caches: /* There is no API to read number of caches, so we need to iterate twice
on caches:
1. Count all type of caches -> allocate memory 1. Count all type of caches -> allocate memory
2. Read out cache data and store to allocated memory 2. Read out cache data and store to allocated memory
*/ */
nr_of_all_caches = read_caches_for_processors( nr_of_all_caches = read_caches_for_processors(
processors, nr_of_processors, processors, nr_of_processors, caches, numbers_of_caches, global_proc_index_per_group, chip_info);
caches, numbers_of_caches,
global_proc_index_per_group, chip_info);
if (!nr_of_all_caches) { if (!nr_of_all_caches) {
cpuinfo_log_error("error in reading cache information"); cpuinfo_log_error("error in reading cache information");
goto clean_up; goto clean_up;
} }
cpuinfo_log_debug("detected %"PRIu32" processor cache(s)", nr_of_all_caches); cpuinfo_log_debug("detected %" PRIu32 " processor cache(s)", nr_of_all_caches);
/* 3. Allocate memory for package, cluster, core and cache structures */ /* 3. Allocate memory for package, cluster, core and cache structures */
packages = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_packages * sizeof(struct cpuinfo_package)); packages = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_packages * sizeof(struct cpuinfo_package));
if (packages == NULL) { if (packages == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" physical packages", cpuinfo_log_error(
nr_of_packages * sizeof(struct cpuinfo_package), nr_of_packages); "failed to allocate %zu bytes for descriptions of %" PRIu32 " physical packages",
nr_of_packages * sizeof(struct cpuinfo_package),
nr_of_packages);
goto clean_up; goto clean_up;
} }
/* We don't have cluster information so we explicitly set clusters to equal to cores. */ /* We don't have cluster information so we explicitly set clusters to
* equal to cores. */
clusters = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_cores * sizeof(struct cpuinfo_cluster)); clusters = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_cores * sizeof(struct cpuinfo_cluster));
if (clusters == NULL) { if (clusters == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" core clusters", cpuinfo_log_error(
nr_of_cores * sizeof(struct cpuinfo_cluster), nr_of_cores); "failed to allocate %zu bytes for descriptions of %" PRIu32 " core clusters",
nr_of_cores * sizeof(struct cpuinfo_cluster),
nr_of_cores);
goto clean_up; goto clean_up;
} }
cores = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_cores * sizeof(struct cpuinfo_core)); cores = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_cores * sizeof(struct cpuinfo_core));
if (cores == NULL) { if (cores == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" cores", cpuinfo_log_error(
nr_of_cores * sizeof(struct cpuinfo_core), nr_of_cores); "failed to allocate %zu bytes for descriptions of %" PRIu32 " cores",
nr_of_cores * sizeof(struct cpuinfo_core),
nr_of_cores);
goto clean_up; goto clean_up;
} }
/* We allocate one contiguous cache array for all caches, then use offsets per cache type. */ /* We allocate one contiguous cache array for all caches, then use
* offsets per cache type. */
caches = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_all_caches * sizeof(struct cpuinfo_cache)); caches = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_all_caches * sizeof(struct cpuinfo_cache));
if (caches == NULL) { if (caches == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" caches", cpuinfo_log_error(
nr_of_all_caches * sizeof(struct cpuinfo_cache), nr_of_all_caches); "failed to allocate %zu bytes for descriptions of %" PRIu32 " caches",
nr_of_all_caches * sizeof(struct cpuinfo_cache),
nr_of_all_caches);
goto clean_up; goto clean_up;
} }
/* 4.Read missing topology information that can't be saved without counted /* 4.Read missing topology information that can't be saved without
* allocate structures in the first round. * counted allocate structures in the first round.
*/ */
nr_of_all_caches = read_caches_for_processors( nr_of_all_caches = read_caches_for_processors(
processors, nr_of_processors, processors, nr_of_processors, caches, numbers_of_caches, global_proc_index_per_group, chip_info);
caches, numbers_of_caches, global_proc_index_per_group, chip_info);
if (!nr_of_all_caches) { if (!nr_of_all_caches) {
cpuinfo_log_error("error in reading cache information"); cpuinfo_log_error("error in reading cache information");
goto clean_up; goto clean_up;
} }
nr_of_cores = read_cores_for_processors( nr_of_cores =
processors, nr_of_processors, read_cores_for_processors(processors, nr_of_processors, global_proc_index_per_group, cores, chip_info);
global_proc_index_per_group, cores,
chip_info);
if (!nr_of_cores) { if (!nr_of_cores) {
cpuinfo_log_error("error in reading core information"); cpuinfo_log_error("error in reading core information");
goto clean_up; goto clean_up;
} }
/* 5. Now that we read out everything from the system we can, fill the package, cluster /* 5. Now that we read out everything from the system we can, fill the
* and core structures respectively. * package, cluster and core structures respectively.
*/ */
result = connect_packages_cores_clusters_by_processors( result = connect_packages_cores_clusters_by_processors(
processors, nr_of_processors, processors,
packages, nr_of_packages, nr_of_processors,
clusters, packages,
cores, nr_of_cores, nr_of_packages,
chip_info, clusters,
vendor); cores,
if(!result) { nr_of_cores,
chip_info,
vendor);
if (!result) {
cpuinfo_log_error("error in connecting information"); cpuinfo_log_error("error in connecting information");
goto clean_up; goto clean_up;
} }
/* 6. Count and store uarchs of cores, assuming same uarchs are neighbors */ /* 6. Count and store uarchs of cores, assuming same uarchs are
* neighbors */
enum cpuinfo_uarch prev_uarch = cpuinfo_uarch_unknown; enum cpuinfo_uarch prev_uarch = cpuinfo_uarch_unknown;
for (uint32_t i = 0; i < nr_of_cores; i++) { for (uint32_t i = 0; i < nr_of_cores; i++) {
if (prev_uarch != cores[i].uarch) { if (prev_uarch != cores[i].uarch) {
@ -278,8 +281,10 @@ bool cpu_info_init_by_logical_sys_info(
} }
uarchs = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_uarchs * sizeof(struct cpuinfo_uarch_info)); uarchs = HeapAlloc(heap, HEAP_ZERO_MEMORY, nr_of_uarchs * sizeof(struct cpuinfo_uarch_info));
if (uarchs == NULL) { if (uarchs == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" uarchs", cpuinfo_log_error(
nr_of_uarchs * sizeof(struct cpuinfo_uarch_info), nr_of_uarchs); "failed to allocate %zu bytes for descriptions of %" PRIu32 " uarchs",
nr_of_uarchs * sizeof(struct cpuinfo_uarch_info),
nr_of_uarchs);
goto clean_up; goto clean_up;
} }
prev_uarch = cpuinfo_uarch_unknown; prev_uarch = cpuinfo_uarch_unknown;
@ -318,10 +323,14 @@ bool cpu_info_init_by_logical_sys_info(
cpuinfo_cache_count[i] = numbers_of_caches[i]; cpuinfo_cache_count[i] = numbers_of_caches[i];
} }
cpuinfo_cache[cpuinfo_cache_level_1i] = caches; cpuinfo_cache[cpuinfo_cache_level_1i] = caches;
cpuinfo_cache[cpuinfo_cache_level_1d] = cpuinfo_cache[cpuinfo_cache_level_1i] + cpuinfo_cache_count[cpuinfo_cache_level_1i]; cpuinfo_cache[cpuinfo_cache_level_1d] =
cpuinfo_cache[cpuinfo_cache_level_2] = cpuinfo_cache[cpuinfo_cache_level_1d] + cpuinfo_cache_count[cpuinfo_cache_level_1d]; cpuinfo_cache[cpuinfo_cache_level_1i] + cpuinfo_cache_count[cpuinfo_cache_level_1i];
cpuinfo_cache[cpuinfo_cache_level_3] = cpuinfo_cache[cpuinfo_cache_level_2] + cpuinfo_cache_count[cpuinfo_cache_level_2]; cpuinfo_cache[cpuinfo_cache_level_2] =
cpuinfo_cache[cpuinfo_cache_level_4] = cpuinfo_cache[cpuinfo_cache_level_3] + cpuinfo_cache_count[cpuinfo_cache_level_3]; cpuinfo_cache[cpuinfo_cache_level_1d] + cpuinfo_cache_count[cpuinfo_cache_level_1d];
cpuinfo_cache[cpuinfo_cache_level_3] =
cpuinfo_cache[cpuinfo_cache_level_2] + cpuinfo_cache_count[cpuinfo_cache_level_2];
cpuinfo_cache[cpuinfo_cache_level_4] =
cpuinfo_cache[cpuinfo_cache_level_3] + cpuinfo_cache_count[cpuinfo_cache_level_3];
cpuinfo_max_cache_size = cpuinfo_compute_max_cache_size(&processors[0]); cpuinfo_max_cache_size = cpuinfo_compute_max_cache_size(&processors[0]);
result = true; result = true;
@ -363,16 +372,13 @@ clean_up:
return result; return result;
} }
static uint32_t count_logical_processors( static uint32_t count_logical_processors(const uint32_t max_group_count, uint32_t* global_proc_index_per_group) {
const uint32_t max_group_count,
uint32_t* global_proc_index_per_group)
{
uint32_t nr_of_processors = 0; uint32_t nr_of_processors = 0;
for (uint32_t i = 0; i < max_group_count; i++) { for (uint32_t i = 0; i < max_group_count; i++) {
uint32_t nr_of_processors_per_group = GetMaximumProcessorCount((WORD) i); uint32_t nr_of_processors_per_group = GetMaximumProcessorCount((WORD)i);
cpuinfo_log_debug("detected %"PRIu32" processor(s) in group %"PRIu32"", cpuinfo_log_debug(
nr_of_processors_per_group, i); "detected %" PRIu32 " processor(s) in group %" PRIu32 "", nr_of_processors_per_group, i);
global_proc_index_per_group[i] = nr_of_processors; global_proc_index_per_group[i] = nr_of_processors;
nr_of_processors += nr_of_processors_per_group; nr_of_processors += nr_of_processors_per_group;
} }
@ -383,8 +389,7 @@ static uint32_t read_packages_for_processors(
struct cpuinfo_processor* processors, struct cpuinfo_processor* processors,
const uint32_t number_of_processors, const uint32_t number_of_processors,
const uint32_t* global_proc_index_per_group, const uint32_t* global_proc_index_per_group,
const struct woa_chip_info *chip_info) const struct woa_chip_info* chip_info) {
{
return read_all_logical_processor_info_of_relation( return read_all_logical_processor_info_of_relation(
RelationProcessorPackage, RelationProcessorPackage,
processors, processors,
@ -401,8 +406,7 @@ uint32_t read_cores_for_processors(
const uint32_t number_of_processors, const uint32_t number_of_processors,
const uint32_t* global_proc_index_per_group, const uint32_t* global_proc_index_per_group,
struct cpuinfo_core* cores, struct cpuinfo_core* cores,
const struct woa_chip_info *chip_info) const struct woa_chip_info* chip_info) {
{
return read_all_logical_processor_info_of_relation( return read_all_logical_processor_info_of_relation(
RelationProcessorCore, RelationProcessorCore,
processors, processors,
@ -420,8 +424,7 @@ static uint32_t read_caches_for_processors(
struct cpuinfo_cache* caches, struct cpuinfo_cache* caches,
uint32_t* numbers_of_caches, uint32_t* numbers_of_caches,
const uint32_t* global_proc_index_per_group, const uint32_t* global_proc_index_per_group,
const struct woa_chip_info *chip_info) const struct woa_chip_info* chip_info) {
{
/* Reset processor start indexes */ /* Reset processor start indexes */
if (caches) { if (caches) {
uint32_t cache_offset = 0; uint32_t cache_offset = 0;
@ -452,8 +455,7 @@ static uint32_t read_all_logical_processor_info_of_relation(
uint32_t* numbers_of_caches, uint32_t* numbers_of_caches,
struct cpuinfo_core* cores, struct cpuinfo_core* cores,
const uint32_t* global_proc_index_per_group, const uint32_t* global_proc_index_per_group,
const struct woa_chip_info* chip_info) const struct woa_chip_info* chip_info) {
{
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX infos = NULL; PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX infos = NULL;
uint32_t nr_of_structs = 0; uint32_t nr_of_structs = 0;
DWORD info_size = 0; DWORD info_size = 0;
@ -465,76 +467,74 @@ static uint32_t read_all_logical_processor_info_of_relation(
const DWORD last_error = GetLastError(); const DWORD last_error = GetLastError();
if (last_error != ERROR_INSUFFICIENT_BUFFER) { if (last_error != ERROR_INSUFFICIENT_BUFFER) {
cpuinfo_log_error( cpuinfo_log_error(
"failed to query size of processor %"PRIu32" information information: error %"PRIu32"", "failed to query size of processor %" PRIu32 " information information: error %" PRIu32
(uint32_t)info_type, (uint32_t) last_error); "",
(uint32_t)info_type,
(uint32_t)last_error);
goto clean_up; goto clean_up;
} }
} }
/* 2. Allocate memory for the information structure */ /* 2. Allocate memory for the information structure */
infos = HeapAlloc(heap, 0, info_size); infos = HeapAlloc(heap, 0, info_size);
if (infos == NULL) { if (infos == NULL) {
cpuinfo_log_error("failed to allocate %"PRIu32" bytes for logical processor information", cpuinfo_log_error(
(uint32_t) info_size); "failed to allocate %" PRIu32 " bytes for logical processor information", (uint32_t)info_size);
goto clean_up; goto clean_up;
} }
/* 3. Read the information structure */ /* 3. Read the information structure */
if (GetLogicalProcessorInformationEx(info_type, infos, &info_size) == FALSE) { if (GetLogicalProcessorInformationEx(info_type, infos, &info_size) == FALSE) {
cpuinfo_log_error("failed to query processor %"PRIu32" information: error %"PRIu32"", cpuinfo_log_error(
(uint32_t)info_type, (uint32_t) GetLastError()); "failed to query processor %" PRIu32 " information: error %" PRIu32 "",
(uint32_t)info_type,
(uint32_t)GetLastError());
goto clean_up; goto clean_up;
} }
/* 4. Parse the structure and store relevant data */ /* 4. Parse the structure and store relevant data */
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info_end = PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info_end =
(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) ((uintptr_t) infos + info_size); (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)((uintptr_t)infos + info_size);
for (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info = infos; for (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info = infos; info < info_end;
info < info_end; info = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)((uintptr_t)info + info->Size)) {
info = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) ((uintptr_t) info + info->Size))
{
if (info->Relationship != info_type) { if (info->Relationship != info_type) {
cpuinfo_log_warning( cpuinfo_log_warning(
"unexpected processor info type (%"PRIu32") for processor information", "unexpected processor info type (%" PRIu32 ") for processor information",
(uint32_t) info->Relationship); (uint32_t)info->Relationship);
continue; continue;
} }
const uint32_t info_id = nr_of_structs++; const uint32_t info_id = nr_of_structs++;
switch(info_type) { switch (info_type) {
case RelationProcessorPackage: case RelationProcessorPackage:
result = parse_relation_processor_info( result = parse_relation_processor_info(
processors, processors,
number_of_processors, number_of_processors,
global_proc_index_per_group, global_proc_index_per_group,
info, info,
info_id, info_id,
cores, cores,
chip_info); chip_info);
break; break;
case RelationProcessorCore: case RelationProcessorCore:
result = parse_relation_processor_info( result = parse_relation_processor_info(
processors, processors,
number_of_processors, number_of_processors,
global_proc_index_per_group, global_proc_index_per_group,
info, info,
info_id, info_id,
cores, cores,
chip_info); chip_info);
break; break;
case RelationCache: case RelationCache:
result = parse_relation_cache_info( result = parse_relation_cache_info(
processors, processors, caches, numbers_of_caches, global_proc_index_per_group, info);
caches, break;
numbers_of_caches,
global_proc_index_per_group,
info);
break;
default: default:
cpuinfo_log_error( cpuinfo_log_error(
"unexpected processor info type (%"PRIu32") for processor information", "unexpected processor info type (%" PRIu32 ") for processor information",
(uint32_t) info->Relationship); (uint32_t)info->Relationship);
result = false; result = false;
break; break;
} }
if (!result) { if (!result) {
nr_of_structs = 0; nr_of_structs = 0;
@ -555,43 +555,45 @@ static bool parse_relation_processor_info(
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info, PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info,
const uint32_t info_id, const uint32_t info_id,
struct cpuinfo_core* cores, struct cpuinfo_core* cores,
const struct woa_chip_info *chip_info) const struct woa_chip_info* chip_info) {
{
for (uint32_t i = 0; i < info->Processor.GroupCount; i++) { for (uint32_t i = 0; i < info->Processor.GroupCount; i++) {
const uint32_t group_id = info->Processor.GroupMask[i].Group; const uint32_t group_id = info->Processor.GroupMask[i].Group;
/* Bitmask representing processors in this group belonging to this package */ /* Bitmask representing processors in this group belonging to
* this package
*/
KAFFINITY group_processors_mask = info->Processor.GroupMask[i].Mask; KAFFINITY group_processors_mask = info->Processor.GroupMask[i].Mask;
while (group_processors_mask != 0) { while (group_processors_mask != 0) {
const uint32_t processor_id_in_group = const uint32_t processor_id_in_group = low_index_from_kaffinity(group_processors_mask);
low_index_from_kaffinity(group_processors_mask);
const uint32_t processor_global_index = const uint32_t processor_global_index =
global_proc_index_per_group[group_id] + processor_id_in_group; global_proc_index_per_group[group_id] + processor_id_in_group;
if(processor_global_index >= nr_of_processors) { if (processor_global_index >= nr_of_processors) {
cpuinfo_log_error("unexpected processor index %"PRIu32"", cpuinfo_log_error("unexpected processor index %" PRIu32 "", processor_global_index);
processor_global_index);
return false; return false;
} }
switch(info->Relationship) { switch (info->Relationship) {
case RelationProcessorPackage: case RelationProcessorPackage:
store_package_info_per_processor( store_package_info_per_processor(
processors, processor_global_index, info_id, processors,
group_id, processor_id_in_group); processor_global_index,
break; info_id,
group_id,
processor_id_in_group);
break;
case RelationProcessorCore: case RelationProcessorCore:
store_core_info_per_processor( store_core_info_per_processor(
processors, processor_global_index, processors, processor_global_index, info_id, info, cores, chip_info);
info_id, info, break;
cores, chip_info);
break;
default: default:
cpuinfo_log_error( cpuinfo_log_error(
"unexpected processor info type (%"PRIu32") for processor information", "unexpected processor info type (%" PRIu32
(uint32_t) info->Relationship); ") for processor information",
break; (uint32_t)info->Relationship);
break;
} }
/* Clear the bits in affinity mask, lower the least set bit. */ /* Clear the bits in affinity mask, lower the least set
* bit. */
group_processors_mask &= (group_processors_mask - 1); group_processors_mask &= (group_processors_mask - 1);
} }
} }
@ -603,8 +605,7 @@ static bool parse_relation_cache_info(
struct cpuinfo_cache* caches, struct cpuinfo_cache* caches,
uint32_t* numbers_of_caches, uint32_t* numbers_of_caches,
const uint32_t* global_proc_index_per_group, const uint32_t* global_proc_index_per_group,
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info) PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info) {
{
static uint32_t l1i_counter = 0; static uint32_t l1i_counter = 0;
static uint32_t l1d_counter = 0; static uint32_t l1d_counter = 0;
static uint32_t l2_counter = 0; static uint32_t l2_counter = 0;
@ -612,45 +613,52 @@ static bool parse_relation_cache_info(
/* Count cache types for allocation at first. */ /* Count cache types for allocation at first. */
if (caches == NULL) { if (caches == NULL) {
switch(info->Cache.Level) { switch (info->Cache.Level) {
case 1: case 1:
switch (info->Cache.Type) { switch (info->Cache.Type) {
case CacheInstruction: case CacheInstruction:
numbers_of_caches[cpuinfo_cache_level_1i]++; numbers_of_caches[cpuinfo_cache_level_1i]++;
break; break;
case CacheData: case CacheData:
numbers_of_caches[cpuinfo_cache_level_1d]++; numbers_of_caches[cpuinfo_cache_level_1d]++;
break; break;
case CacheUnified: case CacheUnified:
break; break;
case CacheTrace: case CacheTrace:
break; break;
default: default:
break; break;
} }
break; break;
case 2: case 2:
numbers_of_caches[cpuinfo_cache_level_2]++; numbers_of_caches[cpuinfo_cache_level_2]++;
break; break;
case 3: case 3:
numbers_of_caches[cpuinfo_cache_level_3]++; numbers_of_caches[cpuinfo_cache_level_3]++;
break; break;
} }
return true; return true;
} }
struct cpuinfo_cache* l1i_base = caches; struct cpuinfo_cache* l1i_base = caches;
struct cpuinfo_cache* l1d_base = l1i_base + numbers_of_caches[cpuinfo_cache_level_1i]; struct cpuinfo_cache* l1d_base = l1i_base + numbers_of_caches[cpuinfo_cache_level_1i];
struct cpuinfo_cache* l2_base = l1d_base + numbers_of_caches[cpuinfo_cache_level_1d]; struct cpuinfo_cache* l2_base = l1d_base + numbers_of_caches[cpuinfo_cache_level_1d];
struct cpuinfo_cache* l3_base = l2_base + numbers_of_caches[cpuinfo_cache_level_2]; struct cpuinfo_cache* l3_base = l2_base + numbers_of_caches[cpuinfo_cache_level_2];
cpuinfo_log_debug( cpuinfo_log_debug(
"info->Cache.GroupCount:%"PRIu32", info->Cache.GroupMask:%"PRIu32"," "info->Cache.GroupCount:%" PRIu32 ", info->Cache.GroupMask:%" PRIu32
"info->Cache.Level:%"PRIu32", info->Cache.Associativity:%"PRIu32"," ","
"info->Cache.LineSize:%"PRIu32"," "info->Cache.Level:%" PRIu32 ", info->Cache.Associativity:%" PRIu32
"info->Cache.CacheSize:%"PRIu32", info->Cache.Type:%"PRIu32"", ","
info->Cache.GroupCount, (unsigned int)info->Cache.GroupMask.Mask, "info->Cache.LineSize:%" PRIu32
info->Cache.Level, info->Cache.Associativity, info->Cache.LineSize, ","
info->Cache.CacheSize, info->Cache.Type); "info->Cache.CacheSize:%" PRIu32 ", info->Cache.Type:%" PRIu32 "",
info->Cache.GroupCount,
(unsigned int)info->Cache.GroupMask.Mask,
info->Cache.Level,
info->Cache.Associativity,
info->Cache.LineSize,
info->Cache.CacheSize,
info->Cache.Type);
struct cpuinfo_cache* current_cache = NULL; struct cpuinfo_cache* current_cache = NULL;
switch (info->Cache.Level) { switch (info->Cache.Level) {
@ -659,27 +667,27 @@ static bool parse_relation_cache_info(
case CacheInstruction: case CacheInstruction:
current_cache = l1i_base + l1i_counter; current_cache = l1i_base + l1i_counter;
l1i_counter++; l1i_counter++;
break; break;
case CacheData: case CacheData:
current_cache = l1d_base + l1d_counter; current_cache = l1d_base + l1d_counter;
l1d_counter++; l1d_counter++;
break; break;
case CacheUnified: case CacheUnified:
break; break;
case CacheTrace: case CacheTrace:
break; break;
default: default:
break; break;
} }
break; break;
case 2: case 2:
current_cache = l2_base + l2_counter; current_cache = l2_base + l2_counter;
l2_counter++; l2_counter++;
break; break;
case 3: case 3:
current_cache = l3_base + l3_counter; current_cache = l3_base + l3_counter;
l3_counter++; l3_counter++;
break; break;
} }
current_cache->size = info->Cache.CacheSize; current_cache->size = info->Cache.CacheSize;
current_cache->line_size = info->Cache.LineSize; current_cache->line_size = info->Cache.LineSize;
@ -688,28 +696,28 @@ static bool parse_relation_cache_info(
* so we set partitions to 1 and calculate the expected sets. * so we set partitions to 1 and calculate the expected sets.
*/ */
current_cache->partitions = 1; current_cache->partitions = 1;
current_cache->sets = current_cache->sets = current_cache->size / current_cache->line_size / current_cache->associativity;
current_cache->size / current_cache->line_size / current_cache->associativity;
if (info->Cache.Type == CacheUnified) { if (info->Cache.Type == CacheUnified) {
current_cache->flags = CPUINFO_CACHE_UNIFIED; current_cache->flags = CPUINFO_CACHE_UNIFIED;
} }
for (uint32_t i = 0; i < info->Cache.GroupCount; i++) { for (uint32_t i = 0; i < info->Cache.GroupCount; i++) {
/* Zero GroupCount is valid, GroupMask still can store bits set. */ /* Zero GroupCount is valid, GroupMask still can store bits set.
*/
const uint32_t group_id = info->Cache.GroupMasks[i].Group; const uint32_t group_id = info->Cache.GroupMasks[i].Group;
/* Bitmask representing processors in this group belonging to this package */ /* Bitmask representing processors in this group belonging to
* this package
*/
KAFFINITY group_processors_mask = info->Cache.GroupMasks[i].Mask; KAFFINITY group_processors_mask = info->Cache.GroupMasks[i].Mask;
while (group_processors_mask != 0) { while (group_processors_mask != 0) {
const uint32_t processor_id_in_group = const uint32_t processor_id_in_group = low_index_from_kaffinity(group_processors_mask);
low_index_from_kaffinity(group_processors_mask);
const uint32_t processor_global_index = const uint32_t processor_global_index =
global_proc_index_per_group[group_id] + processor_id_in_group; global_proc_index_per_group[group_id] + processor_id_in_group;
store_cache_info_per_processor( store_cache_info_per_processor(processors, processor_global_index, info, current_cache);
processors, processor_global_index,
info, current_cache);
/* Clear the bits in affinity mask, lower the least set bit. */ /* Clear the bits in affinity mask, lower the least set
* bit. */
group_processors_mask &= (group_processors_mask - 1); group_processors_mask &= (group_processors_mask - 1);
} }
} }
@ -721,18 +729,15 @@ static void store_package_info_per_processor(
const uint32_t processor_global_index, const uint32_t processor_global_index,
const uint32_t package_id, const uint32_t package_id,
const uint32_t group_id, const uint32_t group_id,
const uint32_t processor_id_in_group) const uint32_t processor_id_in_group) {
{ processors[processor_global_index].windows_group_id = (uint16_t)group_id;
processors[processor_global_index].windows_group_id = processors[processor_global_index].windows_processor_id = (uint16_t)processor_id_in_group;
(uint16_t) group_id;
processors[processor_global_index].windows_processor_id =
(uint16_t) processor_id_in_group;
/* As we're counting the number of packages now, we haven't allocated memory for /* As we're counting the number of packages now, we haven't allocated
* cpuinfo_packages yet, so we only set the package pointer's offset now. * memory for cpuinfo_packages yet, so we only set the package pointer's
* offset now.
*/ */
processors[processor_global_index].package = processors[processor_global_index].package = (const struct cpuinfo_package*)NULL + package_id;
(const struct cpuinfo_package*) NULL + package_id;
} }
void store_core_info_per_processor( void store_core_info_per_processor(
@ -741,22 +746,22 @@ void store_core_info_per_processor(
const uint32_t core_id, const uint32_t core_id,
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX core_info, PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX core_info,
struct cpuinfo_core* cores, struct cpuinfo_core* cores,
const struct woa_chip_info *chip_info) const struct woa_chip_info* chip_info) {
{
if (cores) { if (cores) {
processors[processor_global_index].core = cores + core_id; processors[processor_global_index].core = cores + core_id;
cores[core_id].core_id = core_id; cores[core_id].core_id = core_id;
get_core_uarch_for_efficiency( get_core_uarch_for_efficiency(
chip_info->chip_name, core_info->Processor.EfficiencyClass, chip_info->chip_name,
&(cores[core_id].uarch), &(cores[core_id].frequency)); core_info->Processor.EfficiencyClass,
&(cores[core_id].uarch),
&(cores[core_id].frequency));
/* We don't have cluster information, so we handle it as /* We don't have cluster information, so we handle it as
* fixed 1 to (cluster / cores). * fixed 1 to (cluster / cores).
* Set the cluster offset ID now, as soon as we have the * Set the cluster offset ID now, as soon as we have the
* cluster base address, we'll set the absolute address. * cluster base address, we'll set the absolute address.
*/ */
processors[processor_global_index].cluster = processors[processor_global_index].cluster = (const struct cpuinfo_cluster*)NULL + core_id;
(const struct cpuinfo_cluster*) NULL + core_id;
} }
} }
@ -764,36 +769,35 @@ static void store_cache_info_per_processor(
struct cpuinfo_processor* processors, struct cpuinfo_processor* processors,
const uint32_t processor_global_index, const uint32_t processor_global_index,
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info, PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX info,
struct cpuinfo_cache* current_cache) struct cpuinfo_cache* current_cache) {
{
if (current_cache->processor_start > processor_global_index) { if (current_cache->processor_start > processor_global_index) {
current_cache->processor_start = processor_global_index; current_cache->processor_start = processor_global_index;
} }
current_cache->processor_count++; current_cache->processor_count++;
switch(info->Cache.Level) { switch (info->Cache.Level) {
case 1: case 1:
switch (info->Cache.Type) { switch (info->Cache.Type) {
case CacheInstruction: case CacheInstruction:
processors[processor_global_index].cache.l1i = current_cache; processors[processor_global_index].cache.l1i = current_cache;
break; break;
case CacheData: case CacheData:
processors[processor_global_index].cache.l1d = current_cache; processors[processor_global_index].cache.l1d = current_cache;
break; break;
case CacheUnified: case CacheUnified:
break; break;
case CacheTrace: case CacheTrace:
break; break;
default: default:
break; break;
} }
break; break;
case 2: case 2:
processors[processor_global_index].cache.l2 = current_cache; processors[processor_global_index].cache.l2 = current_cache;
break; break;
case 3: case 3:
processors[processor_global_index].cache.l3 = current_cache; processors[processor_global_index].cache.l3 = current_cache;
break; break;
} }
} }
@ -806,8 +810,7 @@ static bool connect_packages_cores_clusters_by_processors(
struct cpuinfo_core* cores, struct cpuinfo_core* cores,
const uint32_t nr_of_cores, const uint32_t nr_of_cores,
const struct woa_chip_info* chip_info, const struct woa_chip_info* chip_info,
enum cpuinfo_vendor vendor) enum cpuinfo_vendor vendor) {
{
/* Adjust core and package pointers for all logical processors. */ /* Adjust core and package pointers for all logical processors. */
for (uint32_t i = nr_of_processors; i != 0; i--) { for (uint32_t i = nr_of_processors; i != 0; i--) {
const uint32_t processor_id = i - 1; const uint32_t processor_id = i - 1;
@ -815,22 +818,21 @@ static bool connect_packages_cores_clusters_by_processors(
struct cpuinfo_core* core = (struct cpuinfo_core*)processor->core; struct cpuinfo_core* core = (struct cpuinfo_core*)processor->core;
/* We stored the offset of pointers when we haven't allocated memory /* We stored the offset of pointers when we haven't allocated
* for packages and clusters, so now add offsets to base addresses. * memory for packages and clusters, so now add offsets to base
* addresses.
*/ */
struct cpuinfo_package* package = struct cpuinfo_package* package =
(struct cpuinfo_package*) ((uintptr_t) packages + (uintptr_t) processor->package); (struct cpuinfo_package*)((uintptr_t)packages + (uintptr_t)processor->package);
if (package < packages || if (package < packages || package >= (packages + nr_of_packages)) {
package >= (packages + nr_of_packages)) {
cpuinfo_log_error("invalid package indexing"); cpuinfo_log_error("invalid package indexing");
return false; return false;
} }
processor->package = package; processor->package = package;
struct cpuinfo_cluster* cluster = struct cpuinfo_cluster* cluster =
(struct cpuinfo_cluster*) ((uintptr_t) clusters + (uintptr_t) processor->cluster); (struct cpuinfo_cluster*)((uintptr_t)clusters + (uintptr_t)processor->cluster);
if (cluster < clusters || if (cluster < clusters || cluster >= (clusters + nr_of_cores)) {
cluster >= (clusters + nr_of_cores)) {
cpuinfo_log_error("invalid cluster indexing"); cpuinfo_log_error("invalid cluster indexing");
return false; return false;
} }
@ -839,30 +841,34 @@ static bool connect_packages_cores_clusters_by_processors(
if (chip_info) { if (chip_info) {
size_t converted_chars = 0; size_t converted_chars = 0;
if (!WideCharToMultiByte( if (!WideCharToMultiByte(
CP_UTF8, CP_UTF8,
WC_ERR_INVALID_CHARS, WC_ERR_INVALID_CHARS,
chip_info->chip_name_string, chip_info->chip_name_string,
-1, -1,
package->name, package->name,
CPUINFO_PACKAGE_NAME_MAX, CPUINFO_PACKAGE_NAME_MAX,
NULL, NULL,
NULL)) { NULL)) {
cpuinfo_log_error("cpu name character conversion error"); cpuinfo_log_error("cpu name character conversion error");
return false; return false;
}; };
} }
/* Set start indexes and counts per packages / clusters / cores - going backwards */ /* Set start indexes and counts per packages / clusters / cores
* - going backwards */
/* This can be overwritten by lower-index processors on the same package. */ /* This can be overwritten by lower-index processors on the same
* package. */
package->processor_start = processor_id; package->processor_start = processor_id;
package->processor_count++; package->processor_count++;
/* This can be overwritten by lower-index processors on the same cluster. */ /* This can be overwritten by lower-index processors on the same
* cluster. */
cluster->processor_start = processor_id; cluster->processor_start = processor_id;
cluster->processor_count++; cluster->processor_count++;
/* This can be overwritten by lower-index processors on the same core. */ /* This can be overwritten by lower-index processors on the same
* core. */
core->processor_start = processor_id; core->processor_start = processor_id;
core->processor_count++; core->processor_count++;
} }
@ -871,14 +877,16 @@ static bool connect_packages_cores_clusters_by_processors(
const uint32_t global_core_id = i - 1; const uint32_t global_core_id = i - 1;
struct cpuinfo_core* core = cores + global_core_id; struct cpuinfo_core* core = cores + global_core_id;
const struct cpuinfo_processor* processor = processors + core->processor_start; const struct cpuinfo_processor* processor = processors + core->processor_start;
struct cpuinfo_package* package = (struct cpuinfo_package*) processor->package; struct cpuinfo_package* package = (struct cpuinfo_package*)processor->package;
struct cpuinfo_cluster* cluster = (struct cpuinfo_cluster*) processor->cluster; struct cpuinfo_cluster* cluster = (struct cpuinfo_cluster*)processor->cluster;
core->package = package; core->package = package;
core->cluster = cluster; core->cluster = cluster;
core->vendor = vendor; core->vendor = vendor;
/* This can be overwritten by lower-index cores on the same cluster/package. */ /* This can be overwritten by lower-index cores on the same
* cluster/package.
*/
cluster->core_start = global_core_id; cluster->core_start = global_core_id;
cluster->core_count++; cluster->core_count++;
package->core_start = global_core_id; package->core_start = global_core_id;
@ -896,6 +904,6 @@ static bool connect_packages_cores_clusters_by_processors(
static inline uint32_t low_index_from_kaffinity(KAFFINITY kaffinity) { static inline uint32_t low_index_from_kaffinity(KAFFINITY kaffinity) {
unsigned long index; unsigned long index;
_BitScanForward64(&index, (unsigned __int64) kaffinity); _BitScanForward64(&index, (unsigned __int64)kaffinity);
return (uint32_t) index; return (uint32_t)index;
} }

View File

@ -1,7 +1,7 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h> #include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <cpuinfo.h> #include <cpuinfo.h>
#include <cpuinfo/internal-api.h> #include <cpuinfo/internal-api.h>
@ -17,86 +17,62 @@ static struct woa_chip_info* get_system_info_from_registry(void);
static struct woa_chip_info woa_chip_unknown = { static struct woa_chip_info woa_chip_unknown = {
L"Unknown", L"Unknown",
woa_chip_name_unknown, woa_chip_name_unknown,
{ {{cpuinfo_vendor_unknown, cpuinfo_uarch_unknown, 0}}};
{
cpuinfo_vendor_unknown,
cpuinfo_uarch_unknown,
0
}
}
};
/* Please add new SoC/chip info here! */ /* Please add new SoC/chip info here! */
static struct woa_chip_info woa_chips[] = { static struct woa_chip_info woa_chips[woa_chip_name_last] = {
/* Microsoft SQ1 Kryo 495 4 + 4 cores (3 GHz + 1.80 GHz) */ /* Microsoft SQ1 Kryo 495 4 + 4 cores (3 GHz + 1.80 GHz) */
{ [woa_chip_name_microsoft_sq_1] =
L"Microsoft SQ1", {L"Microsoft SQ1",
woa_chip_name_microsoft_sq_1, woa_chip_name_microsoft_sq_1,
{ {{
{ cpuinfo_vendor_arm,
cpuinfo_vendor_arm, cpuinfo_uarch_cortex_a55,
cpuinfo_uarch_cortex_a55, 1800000000,
1800000000, },
}, {
{ cpuinfo_vendor_arm,
cpuinfo_vendor_arm, cpuinfo_uarch_cortex_a76,
cpuinfo_uarch_cortex_a76, 3000000000,
3000000000, }}},
}
}
},
/* Microsoft SQ2 Kryo 495 4 + 4 cores (3.15 GHz + 2.42 GHz) */ /* Microsoft SQ2 Kryo 495 4 + 4 cores (3.15 GHz + 2.42 GHz) */
{ [woa_chip_name_microsoft_sq_2] =
L"Microsoft SQ2", {L"Microsoft SQ2",
woa_chip_name_microsoft_sq_2, woa_chip_name_microsoft_sq_2,
{ {{
{ cpuinfo_vendor_arm,
cpuinfo_vendor_arm, cpuinfo_uarch_cortex_a55,
cpuinfo_uarch_cortex_a55, 2420000000,
2420000000, },
}, {cpuinfo_vendor_arm, cpuinfo_uarch_cortex_a76, 3150000000}}},
{ /* Snapdragon (TM) 8cx Gen 3 @ 3.0 GHz */
cpuinfo_vendor_arm, [woa_chip_name_microsoft_sq_3] =
cpuinfo_uarch_cortex_a76, {L"Snapdragon (TM) 8cx Gen 3",
3150000000 woa_chip_name_microsoft_sq_3,
} {{
} cpuinfo_vendor_arm,
}, cpuinfo_uarch_cortex_a78,
2420000000,
},
{cpuinfo_vendor_arm, cpuinfo_uarch_cortex_x1, 3000000000}}},
/* Microsoft Windows Dev Kit 2023 */ /* Microsoft Windows Dev Kit 2023 */
{ [woa_chip_name_microsoft_sq_3_devkit] =
L"Snapdragon Compute Platform", {L"Snapdragon Compute Platform",
woa_chip_name_microsoft_sq_3, woa_chip_name_microsoft_sq_3_devkit,
{ {{
{ cpuinfo_vendor_arm,
cpuinfo_vendor_arm, cpuinfo_uarch_cortex_a78,
cpuinfo_uarch_cortex_a78, 2420000000,
2420000000, },
}, {cpuinfo_vendor_arm, cpuinfo_uarch_cortex_x1, 3000000000}}},
{
cpuinfo_vendor_arm,
cpuinfo_uarch_cortex_x1,
3000000000
}
}
},
/* Ampere Altra */ /* Ampere Altra */
{ [woa_chip_name_ampere_altra] = {
L"Ampere(R) Altra(R) Processor", L"Ampere(R) Altra(R) Processor",
woa_chip_name_ampere_altra, woa_chip_name_ampere_altra,
{ {{cpuinfo_vendor_arm, cpuinfo_uarch_neoverse_n1, 3000000000}}}};
{
cpuinfo_vendor_arm,
cpuinfo_uarch_neoverse_n1,
3000000000
}
}
}
};
BOOL CALLBACK cpuinfo_arm_windows_init( BOOL CALLBACK cpuinfo_arm_windows_init(PINIT_ONCE init_once, PVOID parameter, PVOID* context) {
PINIT_ONCE init_once, PVOID parameter, PVOID* context) struct woa_chip_info* chip_info = NULL;
{
struct woa_chip_info *chip_info = NULL;
enum cpuinfo_vendor vendor = cpuinfo_vendor_unknown; enum cpuinfo_vendor vendor = cpuinfo_vendor_unknown;
set_cpuinfo_isa_fields(); set_cpuinfo_isa_fields();
@ -112,15 +88,15 @@ BOOL CALLBACK cpuinfo_arm_windows_init(
} }
bool get_core_uarch_for_efficiency( bool get_core_uarch_for_efficiency(
enum woa_chip_name chip, BYTE EfficiencyClass, enum woa_chip_name chip,
enum cpuinfo_uarch* uarch, uint64_t* frequency) BYTE EfficiencyClass,
{ enum cpuinfo_uarch* uarch,
uint64_t* frequency) {
/* For currently supported WoA chips, the Efficiency class selects /* For currently supported WoA chips, the Efficiency class selects
* the pre-defined little and big core. * the pre-defined little and big core.
* Any further supported SoC's logic should be implemented here. * Any further supported SoC's logic should be implemented here.
*/ */
if (uarch && frequency && chip < woa_chip_name_last && if (uarch && frequency && chip < woa_chip_name_last && EfficiencyClass < MAX_WOA_VALID_EFFICIENCY_CLASSES) {
EfficiencyClass < MAX_WOA_VALID_EFFICIENCY_CLASSES) {
*uarch = woa_chips[chip].uarchs[EfficiencyClass].uarch; *uarch = woa_chips[chip].uarchs[EfficiencyClass].uarch;
*frequency = woa_chips[chip].uarchs[EfficiencyClass].frequency; *frequency = woa_chips[chip].uarchs[EfficiencyClass].frequency;
return true; return true;
@ -130,14 +106,11 @@ bool get_core_uarch_for_efficiency(
/* Static helper functions */ /* Static helper functions */
static wchar_t* read_registry( static wchar_t* read_registry(LPCWSTR subkey, LPCWSTR value) {
LPCWSTR subkey,
LPCWSTR value)
{
DWORD key_type = 0; DWORD key_type = 0;
DWORD data_size = 0; DWORD data_size = 0;
const DWORD flags = RRF_RT_REG_SZ; /* Only read strings (REG_SZ) */ const DWORD flags = RRF_RT_REG_SZ; /* Only read strings (REG_SZ) */
wchar_t *text_buffer = NULL; wchar_t* text_buffer = NULL;
LSTATUS result = 0; LSTATUS result = 0;
HANDLE heap = GetProcessHeap(); HANDLE heap = GetProcessHeap();
@ -176,8 +149,7 @@ static wchar_t* read_registry(
return text_buffer; return text_buffer;
} }
static struct woa_chip_info* get_system_info_from_registry(void) static struct woa_chip_info* get_system_info_from_registry(void) {
{
wchar_t* text_buffer = NULL; wchar_t* text_buffer = NULL;
LPCWSTR cpu0_subkey = L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"; LPCWSTR cpu0_subkey = L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
LPCWSTR chip_name_value = L"ProcessorNameString"; LPCWSTR chip_name_value = L"ProcessorNameString";
@ -185,23 +157,27 @@ static struct woa_chip_info* get_system_info_from_registry(void)
HANDLE heap = GetProcessHeap(); HANDLE heap = GetProcessHeap();
/* Read processor model name from registry and find in the hard-coded list. */ /* Read processor model name from registry and find in the hard-coded
* list. */
text_buffer = read_registry(cpu0_subkey, chip_name_value); text_buffer = read_registry(cpu0_subkey, chip_name_value);
if (text_buffer == NULL) { if (text_buffer == NULL) {
cpuinfo_log_error("Registry read error"); cpuinfo_log_error("Registry read error");
return NULL; return NULL;
} }
for (uint32_t i = 0; i < (uint32_t) woa_chip_name_last; i++) { for (uint32_t i = 0; i < (uint32_t)woa_chip_name_last; i++) {
size_t compare_length = wcsnlen(woa_chips[i].chip_name_string, CPUINFO_PACKAGE_NAME_MAX); size_t compare_length = wcsnlen(woa_chips[i].chip_name_string, CPUINFO_PACKAGE_NAME_MAX);
int compare_result = wcsncmp(text_buffer, woa_chips[i].chip_name_string, compare_length); int compare_result = wcsncmp(text_buffer, woa_chips[i].chip_name_string, compare_length);
if (compare_result == 0) { if (compare_result == 0) {
chip_info = woa_chips+i; chip_info = woa_chips + i;
break; break;
} }
} }
if (chip_info == NULL) { if (chip_info == NULL) {
/* No match was found, so print a warning and assign the unknown case. */ /* No match was found, so print a warning and assign the unknown
cpuinfo_log_error("Unknown chip model name '%ls'.\nPlease add new Windows on Arm SoC/chip support to arm/windows/init.c!", text_buffer); * case. */
cpuinfo_log_error(
"Unknown chip model name '%ls'.\nPlease add new Windows on Arm SoC/chip support to arm/windows/init.c!",
text_buffer);
} else { } else {
cpuinfo_log_debug("detected chip model name: %s", chip_info->chip_name_string); cpuinfo_log_debug("detected chip model name: %s", chip_info->chip_name_string);
} }
@ -210,8 +186,7 @@ static struct woa_chip_info* get_system_info_from_registry(void)
return chip_info; return chip_info;
} }
static void set_cpuinfo_isa_fields(void) static void set_cpuinfo_isa_fields(void) {
{
cpuinfo_isa.atomics = IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE) != 0; cpuinfo_isa.atomics = IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE) != 0;
const bool dotprod = IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) != 0; const bool dotprod = IsProcessorFeaturePresent(PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE) != 0;
@ -220,13 +195,14 @@ static void set_cpuinfo_isa_fields(void)
SYSTEM_INFO system_info; SYSTEM_INFO system_info;
GetSystemInfo(&system_info); GetSystemInfo(&system_info);
switch (system_info.wProcessorLevel) { switch (system_info.wProcessorLevel) {
case 0x803: // Kryo 385 Silver (Snapdragon 850) case 0x803: // Kryo 385 Silver (Snapdragon 850)
cpuinfo_isa.fp16arith = dotprod; cpuinfo_isa.fp16arith = dotprod;
cpuinfo_isa.rdm = dotprod; cpuinfo_isa.rdm = dotprod;
break; break;
default: default:
// Assume that Dot Product support implies FP16 arithmetics and RDM support. // Assume that Dot Product support implies FP16
// ARM manuals don't guarantee that, but it holds in practice. // arithmetics and RDM support. ARM manuals don't
// guarantee that, but it holds in practice.
cpuinfo_isa.fp16arith = dotprod; cpuinfo_isa.fp16arith = dotprod;
cpuinfo_isa.rdm = dotprod; cpuinfo_isa.rdm = dotprod;
break; break;

View File

@ -1,15 +1,16 @@
#pragma once #pragma once
/* Efficiency class = 0 means little core, while 1 means big core for now. */ /* Efficiency class = 0 means little core, while 1 means big core for now. */
#define MAX_WOA_VALID_EFFICIENCY_CLASSES 2 #define MAX_WOA_VALID_EFFICIENCY_CLASSES 2
/* List of known and supported Windows on Arm SoCs/chips. */ /* List of known and supported Windows on Arm SoCs/chips. */
enum woa_chip_name { enum woa_chip_name {
woa_chip_name_microsoft_sq_1 = 0, woa_chip_name_microsoft_sq_1 = 0,
woa_chip_name_microsoft_sq_2 = 1, woa_chip_name_microsoft_sq_2 = 1,
woa_chip_name_microsoft_sq_3 = 2, woa_chip_name_microsoft_sq_3 = 2,
woa_chip_name_ampere_altra = 3, woa_chip_name_microsoft_sq_3_devkit = 3,
woa_chip_name_unknown = 4, woa_chip_name_ampere_altra = 4,
woa_chip_name_unknown = 5,
woa_chip_name_last = woa_chip_name_unknown woa_chip_name_last = woa_chip_name_unknown
}; };
@ -30,9 +31,9 @@ struct woa_chip_info {
}; };
bool get_core_uarch_for_efficiency( bool get_core_uarch_for_efficiency(
enum woa_chip_name chip, BYTE EfficiencyClass, enum woa_chip_name chip,
enum cpuinfo_uarch* uarch, uint64_t* frequency); BYTE EfficiencyClass,
enum cpuinfo_uarch* uarch,
uint64_t* frequency);
bool cpu_info_init_by_logical_sys_info( bool cpu_info_init_by_logical_sys_info(const struct woa_chip_info* chip_info, enum cpuinfo_vendor vendor);
const struct woa_chip_info *chip_info,
enum cpuinfo_vendor vendor);

View File

@ -3,16 +3,15 @@
#include <cpuinfo.h> #include <cpuinfo.h>
#include <cpuinfo/internal-api.h> #include <cpuinfo/internal-api.h>
uint32_t cpuinfo_compute_max_cache_size(const struct cpuinfo_processor* processor) { uint32_t cpuinfo_compute_max_cache_size(const struct cpuinfo_processor* processor) {
if (processor->cache.l4 != NULL) { if (processor->cache.l4 != NULL) {
return processor->cache.l4->size; return processor->cache.l4->size;
} else if (processor->cache.l3 != NULL) { } else if (processor->cache.l3 != NULL) {
return processor->cache.l3->size; return processor->cache.l3->size;
} else if (processor->cache.l2 != NULL) { } else if (processor->cache.l2 != NULL) {
return processor->cache.l2->size; return processor->cache.l2->size;
} else if (processor->cache.l1d != NULL) { } else if (processor->cache.l1d != NULL) {
return processor->cache.l1d->size; return processor->cache.l1d->size;
} }
return 0; return 0;
} }

View File

@ -8,33 +8,32 @@
#pragma once #pragma once
#define CPUINFO_COUNT_OF(array) (sizeof(array) / sizeof(0 [array]))
#define CPUINFO_COUNT_OF(array) (sizeof(array) / sizeof(0[array]))
#if defined(__GNUC__) #if defined(__GNUC__)
#define CPUINFO_LIKELY(condition) (__builtin_expect(!!(condition), 1)) #define CPUINFO_LIKELY(condition) (__builtin_expect(!!(condition), 1))
#define CPUINFO_UNLIKELY(condition) (__builtin_expect(!!(condition), 0)) #define CPUINFO_UNLIKELY(condition) (__builtin_expect(!!(condition), 0))
#else #else
#define CPUINFO_LIKELY(condition) (!!(condition)) #define CPUINFO_LIKELY(condition) (!!(condition))
#define CPUINFO_UNLIKELY(condition) (!!(condition)) #define CPUINFO_UNLIKELY(condition) (!!(condition))
#endif #endif
#ifndef CPUINFO_INTERNAL #ifndef CPUINFO_INTERNAL
#if defined(__ELF__) #if defined(__ELF__)
#define CPUINFO_INTERNAL __attribute__((__visibility__("internal"))) #define CPUINFO_INTERNAL __attribute__((__visibility__("internal")))
#elif defined(__MACH__) #elif defined(__MACH__)
#define CPUINFO_INTERNAL __attribute__((__visibility__("hidden"))) #define CPUINFO_INTERNAL __attribute__((__visibility__("hidden")))
#else #else
#define CPUINFO_INTERNAL #define CPUINFO_INTERNAL
#endif #endif
#endif #endif
#ifndef CPUINFO_PRIVATE #ifndef CPUINFO_PRIVATE
#if defined(__ELF__) #if defined(__ELF__)
#define CPUINFO_PRIVATE __attribute__((__visibility__("hidden"))) #define CPUINFO_PRIVATE __attribute__((__visibility__("hidden")))
#elif defined(__MACH__) #elif defined(__MACH__)
#define CPUINFO_PRIVATE __attribute__((__visibility__("hidden"))) #define CPUINFO_PRIVATE __attribute__((__visibility__("hidden")))
#else #else
#define CPUINFO_PRIVATE #define CPUINFO_PRIVATE
#endif #endif
#endif #endif

View File

@ -1,22 +1,21 @@
#pragma once #pragma once
#include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#if defined(_WIN32) || defined(__CYGWIN__) #if defined(_WIN32) || defined(__CYGWIN__)
#include <windows.h> #include <windows.h>
#endif #endif
#include <cpuinfo.h> #include <cpuinfo.h>
#include <cpuinfo/common.h> #include <cpuinfo/common.h>
enum cpuinfo_cache_level { enum cpuinfo_cache_level {
cpuinfo_cache_level_1i = 0, cpuinfo_cache_level_1i = 0,
cpuinfo_cache_level_1d = 1, cpuinfo_cache_level_1d = 1,
cpuinfo_cache_level_2 = 2, cpuinfo_cache_level_2 = 2,
cpuinfo_cache_level_3 = 3, cpuinfo_cache_level_3 = 3,
cpuinfo_cache_level_4 = 4, cpuinfo_cache_level_4 = 4,
cpuinfo_cache_level_max = 5, cpuinfo_cache_level_max = 5,
}; };
@ -36,26 +35,27 @@ extern CPUINFO_INTERNAL uint32_t cpuinfo_cache_count[cpuinfo_cache_level_max];
extern CPUINFO_INTERNAL uint32_t cpuinfo_max_cache_size; extern CPUINFO_INTERNAL uint32_t cpuinfo_max_cache_size;
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
extern CPUINFO_INTERNAL struct cpuinfo_uarch_info* cpuinfo_uarchs; extern CPUINFO_INTERNAL struct cpuinfo_uarch_info* cpuinfo_uarchs;
extern CPUINFO_INTERNAL uint32_t cpuinfo_uarchs_count; extern CPUINFO_INTERNAL uint32_t cpuinfo_uarchs_count;
#else #else
extern CPUINFO_INTERNAL struct cpuinfo_uarch_info cpuinfo_global_uarch; extern CPUINFO_INTERNAL struct cpuinfo_uarch_info cpuinfo_global_uarch;
#endif #endif
#ifdef __linux__ #ifdef __linux__
extern CPUINFO_INTERNAL uint32_t cpuinfo_linux_cpu_max; extern CPUINFO_INTERNAL uint32_t cpuinfo_linux_cpu_max;
extern CPUINFO_INTERNAL const struct cpuinfo_processor** cpuinfo_linux_cpu_to_processor_map; extern CPUINFO_INTERNAL const struct cpuinfo_processor** cpuinfo_linux_cpu_to_processor_map;
extern CPUINFO_INTERNAL const struct cpuinfo_core** cpuinfo_linux_cpu_to_core_map; extern CPUINFO_INTERNAL const struct cpuinfo_core** cpuinfo_linux_cpu_to_core_map;
#endif #endif
CPUINFO_PRIVATE void cpuinfo_x86_mach_init(void); CPUINFO_PRIVATE void cpuinfo_x86_mach_init(void);
CPUINFO_PRIVATE void cpuinfo_x86_linux_init(void); CPUINFO_PRIVATE void cpuinfo_x86_linux_init(void);
CPUINFO_PRIVATE void cpuinfo_x86_freebsd_init(void);
#if defined(_WIN32) || defined(__CYGWIN__) #if defined(_WIN32) || defined(__CYGWIN__)
#if CPUINFO_ARCH_ARM64 #if CPUINFO_ARCH_ARM64
CPUINFO_PRIVATE BOOL CALLBACK cpuinfo_arm_windows_init(PINIT_ONCE init_once, PVOID parameter, PVOID* context); CPUINFO_PRIVATE BOOL CALLBACK cpuinfo_arm_windows_init(PINIT_ONCE init_once, PVOID parameter, PVOID* context);
#else #else
CPUINFO_PRIVATE BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PVOID* context); CPUINFO_PRIVATE BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PVOID* context);
#endif #endif
#endif #endif
CPUINFO_PRIVATE void cpuinfo_arm_mach_init(void); CPUINFO_PRIVATE void cpuinfo_arm_mach_init(void);
CPUINFO_PRIVATE void cpuinfo_arm_linux_init(void); CPUINFO_PRIVATE void cpuinfo_arm_linux_init(void);

View File

@ -5,7 +5,7 @@
#include <stdlib.h> #include <stdlib.h>
#ifndef CPUINFO_LOG_LEVEL #ifndef CPUINFO_LOG_LEVEL
#error "Undefined CPUINFO_LOG_LEVEL" #error "Undefined CPUINFO_LOG_LEVEL"
#endif #endif
#define CPUINFO_LOG_NONE 0 #define CPUINFO_LOG_NONE 0
@ -16,88 +16,87 @@
#define CPUINFO_LOG_DEBUG 5 #define CPUINFO_LOG_DEBUG 5
#ifndef CPUINFO_LOG_DEBUG_PARSERS #ifndef CPUINFO_LOG_DEBUG_PARSERS
#define CPUINFO_LOG_DEBUG_PARSERS 0 #define CPUINFO_LOG_DEBUG_PARSERS 0
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_DEBUG #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_DEBUG
void cpuinfo_vlog_debug(const char* format, va_list args); void cpuinfo_vlog_debug(const char* format, va_list args);
#endif #endif
#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_INFO #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_INFO
void cpuinfo_vlog_info(const char* format, va_list args); void cpuinfo_vlog_info(const char* format, va_list args);
#endif #endif
#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_WARNING #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_WARNING
void cpuinfo_vlog_warning(const char* format, va_list args); void cpuinfo_vlog_warning(const char* format, va_list args);
#endif #endif
#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_ERROR #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_ERROR
void cpuinfo_vlog_error(const char* format, va_list args); void cpuinfo_vlog_error(const char* format, va_list args);
#endif #endif
#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_FATAL #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_FATAL
void cpuinfo_vlog_fatal(const char* format, va_list args); void cpuinfo_vlog_fatal(const char* format, va_list args);
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif
#ifndef CPUINFO_LOG_ARGUMENTS_FORMAT #ifndef CPUINFO_LOG_ARGUMENTS_FORMAT
#ifdef __GNUC__ #ifdef __GNUC__
#define CPUINFO_LOG_ARGUMENTS_FORMAT __attribute__((__format__(__printf__, 1, 2))) #define CPUINFO_LOG_ARGUMENTS_FORMAT __attribute__((__format__(__printf__, 1, 2)))
#else #else
#define CPUINFO_LOG_ARGUMENTS_FORMAT #define CPUINFO_LOG_ARGUMENTS_FORMAT
#endif #endif
#endif #endif
CPUINFO_LOG_ARGUMENTS_FORMAT inline static void cpuinfo_log_debug(const char* format, ...) { CPUINFO_LOG_ARGUMENTS_FORMAT inline static void cpuinfo_log_debug(const char* format, ...) {
#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_DEBUG #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_DEBUG
va_list args; va_list args;
va_start(args, format); va_start(args, format);
cpuinfo_vlog_debug(format, args); cpuinfo_vlog_debug(format, args);
va_end(args); va_end(args);
#endif #endif
} }
CPUINFO_LOG_ARGUMENTS_FORMAT inline static void cpuinfo_log_info(const char* format, ...) { CPUINFO_LOG_ARGUMENTS_FORMAT inline static void cpuinfo_log_info(const char* format, ...) {
#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_INFO #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_INFO
va_list args; va_list args;
va_start(args, format); va_start(args, format);
cpuinfo_vlog_info(format, args); cpuinfo_vlog_info(format, args);
va_end(args); va_end(args);
#endif #endif
} }
CPUINFO_LOG_ARGUMENTS_FORMAT inline static void cpuinfo_log_warning(const char* format, ...) { CPUINFO_LOG_ARGUMENTS_FORMAT inline static void cpuinfo_log_warning(const char* format, ...) {
#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_WARNING #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_WARNING
va_list args; va_list args;
va_start(args, format); va_start(args, format);
cpuinfo_vlog_warning(format, args); cpuinfo_vlog_warning(format, args);
va_end(args); va_end(args);
#endif #endif
} }
CPUINFO_LOG_ARGUMENTS_FORMAT inline static void cpuinfo_log_error(const char* format, ...) { CPUINFO_LOG_ARGUMENTS_FORMAT inline static void cpuinfo_log_error(const char* format, ...) {
#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_ERROR #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_ERROR
va_list args; va_list args;
va_start(args, format); va_start(args, format);
cpuinfo_vlog_error(format, args); cpuinfo_vlog_error(format, args);
va_end(args); va_end(args);
#endif #endif
} }
CPUINFO_LOG_ARGUMENTS_FORMAT inline static void cpuinfo_log_fatal(const char* format, ...) { CPUINFO_LOG_ARGUMENTS_FORMAT inline static void cpuinfo_log_fatal(const char* format, ...) {
#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_FATAL #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_FATAL
va_list args; va_list args;
va_start(args, format); va_start(args, format);
cpuinfo_vlog_fatal(format, args); cpuinfo_vlog_fatal(format, args);
va_end(args); va_end(args);
#endif #endif
abort(); abort();
} }

View File

@ -5,18 +5,17 @@
#endif #endif
#include <stdint.h> #include <stdint.h>
inline static uint32_t bit_length(uint32_t n) { inline static uint32_t bit_length(uint32_t n) {
const uint32_t n_minus_1 = n - 1; const uint32_t n_minus_1 = n - 1;
if (n_minus_1 == 0) { if (n_minus_1 == 0) {
return 0; return 0;
} else { } else {
#ifdef _MSC_VER #ifdef _MSC_VER
unsigned long bsr; unsigned long bsr;
_BitScanReverse(&bsr, n_minus_1); _BitScanReverse(&bsr, n_minus_1);
return bsr + 1; return bsr + 1;
#else #else
return 32 - __builtin_clz(n_minus_1); return 32 - __builtin_clz(n_minus_1);
#endif #endif
} }
} }

View File

@ -1,8 +1,8 @@
#include <math.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <math.h>
#include <emscripten/threading.h> #include <emscripten/threading.h>
@ -10,10 +10,9 @@
#include <cpuinfo/internal-api.h> #include <cpuinfo/internal-api.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
static const volatile float infinity = INFINITY; static const volatile float infinity = INFINITY;
static struct cpuinfo_package static_package = { }; static struct cpuinfo_package static_package = {};
static struct cpuinfo_cache static_x86_l3 = { static struct cpuinfo_cache static_x86_l3 = {
.size = 2 * 1024 * 1024, .size = 2 * 1024 * 1024,
@ -37,7 +36,7 @@ void cpuinfo_emscripten_init(void) {
if (logical_cores_count <= 0) { if (logical_cores_count <= 0) {
logical_cores_count = 1; logical_cores_count = 1;
} }
uint32_t processor_count = (uint32_t) logical_cores_count; uint32_t processor_count = (uint32_t)logical_cores_count;
uint32_t core_count = processor_count; uint32_t core_count = processor_count;
uint32_t cluster_count = 1; uint32_t cluster_count = 1;
uint32_t big_cluster_core_count = core_count; uint32_t big_cluster_core_count = core_count;
@ -60,41 +59,53 @@ void cpuinfo_emscripten_init(void) {
processors = calloc(processor_count, sizeof(struct cpuinfo_processor)); processors = calloc(processor_count, sizeof(struct cpuinfo_processor));
if (processors == NULL) { if (processors == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" logical processors", cpuinfo_log_error(
processor_count * sizeof(struct cpuinfo_processor), processor_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " logical processors",
processor_count * sizeof(struct cpuinfo_processor),
processor_count);
goto cleanup; goto cleanup;
} }
cores = calloc(processor_count, sizeof(struct cpuinfo_core)); cores = calloc(processor_count, sizeof(struct cpuinfo_core));
if (cores == NULL) { if (cores == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" cores", cpuinfo_log_error(
processor_count * sizeof(struct cpuinfo_core), processor_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " cores",
processor_count * sizeof(struct cpuinfo_core),
processor_count);
goto cleanup; goto cleanup;
} }
clusters = calloc(cluster_count, sizeof(struct cpuinfo_cluster)); clusters = calloc(cluster_count, sizeof(struct cpuinfo_cluster));
if (clusters == NULL) { if (clusters == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" clusters", cpuinfo_log_error(
cluster_count * sizeof(struct cpuinfo_cluster), cluster_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " clusters",
cluster_count * sizeof(struct cpuinfo_cluster),
cluster_count);
goto cleanup; goto cleanup;
} }
l1i = calloc(core_count, sizeof(struct cpuinfo_cache)); l1i = calloc(core_count, sizeof(struct cpuinfo_cache));
if (l1i == NULL) { if (l1i == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L1I caches", cpuinfo_log_error(
core_count * sizeof(struct cpuinfo_cache), core_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L1I caches",
core_count * sizeof(struct cpuinfo_cache),
core_count);
goto cleanup; goto cleanup;
} }
l1d = calloc(core_count, sizeof(struct cpuinfo_cache)); l1d = calloc(core_count, sizeof(struct cpuinfo_cache));
if (l1d == NULL) { if (l1d == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L1D caches", cpuinfo_log_error(
core_count * sizeof(struct cpuinfo_cache), core_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L1D caches",
core_count * sizeof(struct cpuinfo_cache),
core_count);
goto cleanup; goto cleanup;
} }
l2 = calloc(l2_count, sizeof(struct cpuinfo_cache)); l2 = calloc(l2_count, sizeof(struct cpuinfo_cache));
if (l2 == NULL) { if (l2 == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L2 caches", cpuinfo_log_error(
l2_count * sizeof(struct cpuinfo_cache), l2_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L2 caches",
l2_count * sizeof(struct cpuinfo_cache),
l2_count);
goto cleanup; goto cleanup;
} }
@ -109,30 +120,30 @@ void cpuinfo_emscripten_init(void) {
for (uint32_t i = 0; i < core_count; i++) { for (uint32_t i = 0; i < core_count; i++) {
for (uint32_t j = 0; j < processors_per_core; j++) { for (uint32_t j = 0; j < processors_per_core; j++) {
processors[i * processors_per_core + j] = (struct cpuinfo_processor) { processors[i * processors_per_core + j] = (struct cpuinfo_processor){
.smt_id = j, .smt_id = j,
.core = cores + i, .core = cores + i,
.cluster = clusters + (uint32_t) (i >= big_cluster_core_count), .cluster = clusters + (uint32_t)(i >= big_cluster_core_count),
.package = &static_package, .package = &static_package,
.cache.l1i = l1i + i, .cache.l1i = l1i + i,
.cache.l1d = l1d + i, .cache.l1d = l1d + i,
.cache.l2 = is_x86 ? l2 + i : l2 + (uint32_t) (i >= big_cluster_core_count), .cache.l2 = is_x86 ? l2 + i : l2 + (uint32_t)(i >= big_cluster_core_count),
.cache.l3 = is_x86 ? &static_x86_l3 : NULL, .cache.l3 = is_x86 ? &static_x86_l3 : NULL,
}; };
} }
cores[i] = (struct cpuinfo_core) { cores[i] = (struct cpuinfo_core){
.processor_start = i * processors_per_core, .processor_start = i * processors_per_core,
.processor_count = processors_per_core, .processor_count = processors_per_core,
.core_id = i, .core_id = i,
.cluster = clusters + (uint32_t) (i >= big_cluster_core_count), .cluster = clusters + (uint32_t)(i >= big_cluster_core_count),
.package = &static_package, .package = &static_package,
.vendor = cpuinfo_vendor_unknown, .vendor = cpuinfo_vendor_unknown,
.uarch = cpuinfo_uarch_unknown, .uarch = cpuinfo_uarch_unknown,
.frequency = 0, .frequency = 0,
}; };
l1i[i] = (struct cpuinfo_cache) { l1i[i] = (struct cpuinfo_cache){
.size = 32 * 1024, .size = 32 * 1024,
.associativity = 4, .associativity = 4,
.sets = 128, .sets = 128,
@ -142,7 +153,7 @@ void cpuinfo_emscripten_init(void) {
.processor_count = processors_per_core, .processor_count = processors_per_core,
}; };
l1d[i] = (struct cpuinfo_cache) { l1d[i] = (struct cpuinfo_cache){
.size = 32 * 1024, .size = 32 * 1024,
.associativity = 4, .associativity = 4,
.sets = 128, .sets = 128,
@ -153,7 +164,7 @@ void cpuinfo_emscripten_init(void) {
}; };
if (is_x86) { if (is_x86) {
l2[i] = (struct cpuinfo_cache) { l2[i] = (struct cpuinfo_cache){
.size = 256 * 1024, .size = 256 * 1024,
.associativity = 8, .associativity = 8,
.sets = 512, .sets = 512,
@ -166,7 +177,7 @@ void cpuinfo_emscripten_init(void) {
} }
if (is_x86) { if (is_x86) {
clusters[0] = (struct cpuinfo_cluster) { clusters[0] = (struct cpuinfo_cluster){
.processor_start = 0, .processor_start = 0,
.processor_count = processor_count, .processor_count = processor_count,
.core_start = 0, .core_start = 0,
@ -180,7 +191,7 @@ void cpuinfo_emscripten_init(void) {
static_x86_l3.processor_count = processor_count; static_x86_l3.processor_count = processor_count;
} else { } else {
clusters[0] = (struct cpuinfo_cluster) { clusters[0] = (struct cpuinfo_cluster){
.processor_start = 0, .processor_start = 0,
.processor_count = big_cluster_core_count, .processor_count = big_cluster_core_count,
.core_start = 0, .core_start = 0,
@ -192,7 +203,7 @@ void cpuinfo_emscripten_init(void) {
.frequency = 0, .frequency = 0,
}; };
l2[0] = (struct cpuinfo_cache) { l2[0] = (struct cpuinfo_cache){
.size = 1024 * 1024, .size = 1024 * 1024,
.associativity = 8, .associativity = 8,
.sets = 2048, .sets = 2048,
@ -203,7 +214,7 @@ void cpuinfo_emscripten_init(void) {
}; };
if (cluster_count > 1) { if (cluster_count > 1) {
l2[1] = (struct cpuinfo_cache) { l2[1] = (struct cpuinfo_cache){
.size = 256 * 1024, .size = 256 * 1024,
.associativity = 8, .associativity = 8,
.sets = 512, .sets = 512,
@ -213,7 +224,7 @@ void cpuinfo_emscripten_init(void) {
.processor_count = processor_count - big_cluster_core_count, .processor_count = processor_count - big_cluster_core_count,
}; };
clusters[1] = (struct cpuinfo_cluster) { clusters[1] = (struct cpuinfo_cluster){
.processor_start = big_cluster_core_count, .processor_start = big_cluster_core_count,
.processor_count = processor_count - big_cluster_core_count, .processor_count = processor_count - big_cluster_core_count,
.core_start = big_cluster_core_count, .core_start = big_cluster_core_count,
@ -230,9 +241,9 @@ void cpuinfo_emscripten_init(void) {
/* Commit changes */ /* Commit changes */
cpuinfo_cache[cpuinfo_cache_level_1i] = l1i; cpuinfo_cache[cpuinfo_cache_level_1i] = l1i;
cpuinfo_cache[cpuinfo_cache_level_1d] = l1d; cpuinfo_cache[cpuinfo_cache_level_1d] = l1d;
cpuinfo_cache[cpuinfo_cache_level_2] = l2; cpuinfo_cache[cpuinfo_cache_level_2] = l2;
if (is_x86) { if (is_x86) {
cpuinfo_cache[cpuinfo_cache_level_3] = &static_x86_l3; cpuinfo_cache[cpuinfo_cache_level_3] = &static_x86_l3;
} }
cpuinfo_processors = processors; cpuinfo_processors = processors;
@ -242,12 +253,12 @@ void cpuinfo_emscripten_init(void) {
cpuinfo_cache_count[cpuinfo_cache_level_1i] = processor_count; cpuinfo_cache_count[cpuinfo_cache_level_1i] = processor_count;
cpuinfo_cache_count[cpuinfo_cache_level_1d] = processor_count; cpuinfo_cache_count[cpuinfo_cache_level_1d] = processor_count;
cpuinfo_cache_count[cpuinfo_cache_level_2] = l2_count; cpuinfo_cache_count[cpuinfo_cache_level_2] = l2_count;
if (is_x86) { if (is_x86) {
cpuinfo_cache_count[cpuinfo_cache_level_3] = 1; cpuinfo_cache_count[cpuinfo_cache_level_3] = 1;
} }
cpuinfo_global_uarch = (struct cpuinfo_uarch_info) { cpuinfo_global_uarch = (struct cpuinfo_uarch_info){
.uarch = cpuinfo_uarch_unknown, .uarch = cpuinfo_uarch_unknown,
.processor_count = processor_count, .processor_count = processor_count,
.core_count = core_count, .core_count = core_count,

12
3rdparty/cpuinfo/src/freebsd/api.h vendored Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include <stdint.h>
struct cpuinfo_freebsd_topology {
uint32_t packages;
uint32_t cores;
uint32_t threads;
uint32_t threads_per_core;
};
struct cpuinfo_freebsd_topology cpuinfo_freebsd_detect_topology(void);

104
3rdparty/cpuinfo/src/freebsd/topology.c vendored Normal file
View File

@ -0,0 +1,104 @@
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <cpuinfo/log.h>
#include <freebsd/api.h>
static int sysctl_int(const char* name) {
int value = 0;
size_t value_size = sizeof(value);
if (sysctlbyname(name, &value, &value_size, NULL, 0) != 0) {
cpuinfo_log_error("sysctlbyname(\"%s\") failed: %s", name, strerror(errno));
} else if (value <= 0) {
cpuinfo_log_error("sysctlbyname(\"%s\") returned invalid value %d %zu", name, value, value_size);
value = 0;
}
return value;
}
static char* sysctl_str(const char* name) {
size_t value_size = 0;
if (sysctlbyname(name, NULL, &value_size, NULL, 0) != 0) {
cpuinfo_log_error("sysctlbyname(\"%s\") failed: %s", name, strerror(errno));
} else if (value_size <= 0) {
cpuinfo_log_error("sysctlbyname(\"%s\") returned invalid value size %zu", name, value_size);
}
value_size += 1;
char* value = calloc(value_size, 1);
if (!value) {
cpuinfo_log_error("calloc %zu bytes failed", value_size);
return NULL;
}
if (sysctlbyname(name, value, &value_size, NULL, 0) != 0) {
cpuinfo_log_error("sysctlbyname(\"%s\") failed: %s", name, strerror(errno));
free(value);
return NULL;
}
return value;
}
struct cpuinfo_freebsd_topology cpuinfo_freebsd_detect_topology(void) {
struct cpuinfo_freebsd_topology topology = {
.packages = 0,
.cores = 0,
.threads_per_core = 0,
.threads = 0,
};
char* topology_spec = sysctl_str("kern.sched.topology_spec");
if (!topology_spec) {
return topology;
}
const char* group_tag = "<group level=\"1\" cache-level=\"0\">";
char* p = strstr(topology_spec, group_tag);
while (p) {
const char* cpu_tag = "cpu count=\"";
char* q = strstr(p, cpu_tag);
if (q) {
p = q + strlen(cpu_tag);
topology.packages += atoi(p);
} else {
break;
}
}
if (topology.packages == 0) {
const char* group_tag = "<group level=\"1\"";
char* p = strstr(topology_spec, group_tag);
while (p) {
topology.packages += 1;
p++;
p = strstr(p, group_tag);
}
}
if (topology.packages == 0) {
cpuinfo_log_error("failed to parse topology_spec:%s", topology_spec);
free(topology_spec);
goto fail;
}
free(topology_spec);
topology.cores = sysctl_int("kern.smp.cores");
if (topology.cores == 0) {
goto fail;
}
if (topology.cores < topology.packages) {
goto fail;
}
topology.threads_per_core = sysctl_int("kern.smp.threads_per_core");
if (topology.threads_per_core == 0) {
goto fail;
}
cpuinfo_log_debug(
"freebsd topology: packages = %d, cores = %d, "
"threads_per_core = %d",
topology.packages,
topology.cores,
topology.threads_per_core);
topology.threads = topology.threads_per_core * topology.cores;
return topology;
fail:
topology.packages = 0;
return topology;
}

View File

@ -1,7 +1,7 @@
#if defined(_WIN32) || defined(__CYGWIN__) #if defined(_WIN32) || defined(__CYGWIN__)
#include <windows.h> #include <windows.h>
#elif !defined(__EMSCRIPTEN__) || defined(__EMSCRIPTEN_PTHREADS__) #elif !defined(__EMSCRIPTEN__) || defined(__EMSCRIPTEN_PTHREADS__)
#include <pthread.h> #include <pthread.h>
#endif #endif
#include <cpuinfo.h> #include <cpuinfo.h>
@ -9,59 +9,59 @@
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#ifdef __APPLE__ #ifdef __APPLE__
#include "TargetConditionals.h" #include "TargetConditionals.h"
#endif #endif
#if defined(_WIN32) || defined(__CYGWIN__) #if defined(_WIN32) || defined(__CYGWIN__)
static INIT_ONCE init_guard = INIT_ONCE_STATIC_INIT; static INIT_ONCE init_guard = INIT_ONCE_STATIC_INIT;
#elif !defined(__EMSCRIPTEN__) || defined(__EMSCRIPTEN_PTHREADS__) #elif !defined(__EMSCRIPTEN__) || defined(__EMSCRIPTEN_PTHREADS__)
static pthread_once_t init_guard = PTHREAD_ONCE_INIT; static pthread_once_t init_guard = PTHREAD_ONCE_INIT;
#else #else
static bool init_guard = false; static bool init_guard = false;
#endif #endif
bool CPUINFO_ABI cpuinfo_initialize(void) { bool CPUINFO_ABI cpuinfo_initialize(void) {
#if CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64 #if CPUINFO_ARCH_X86 || CPUINFO_ARCH_X86_64
#if defined(__MACH__) && defined(__APPLE__) #if defined(__MACH__) && defined(__APPLE__)
pthread_once(&init_guard, &cpuinfo_x86_mach_init); pthread_once(&init_guard, &cpuinfo_x86_mach_init);
#elif defined(__linux__) #elif defined(__FreeBSD__)
pthread_once(&init_guard, &cpuinfo_x86_linux_init); pthread_once(&init_guard, &cpuinfo_x86_freebsd_init);
#elif defined(_WIN32) || defined(__CYGWIN__) #elif defined(__linux__)
InitOnceExecuteOnce(&init_guard, &cpuinfo_x86_windows_init, NULL, NULL); pthread_once(&init_guard, &cpuinfo_x86_linux_init);
#else #elif defined(_WIN32) || defined(__CYGWIN__)
cpuinfo_log_error("operating system is not supported in cpuinfo"); InitOnceExecuteOnce(&init_guard, &cpuinfo_x86_windows_init, NULL, NULL);
#endif #else
cpuinfo_log_error("operating system is not supported in cpuinfo");
#endif
#elif CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 #elif CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
#if defined(__linux__) #if defined(__linux__)
pthread_once(&init_guard, &cpuinfo_arm_linux_init); pthread_once(&init_guard, &cpuinfo_arm_linux_init);
#elif defined(__MACH__) && defined(__APPLE__) #elif defined(__MACH__) && defined(__APPLE__)
pthread_once(&init_guard, &cpuinfo_arm_mach_init); pthread_once(&init_guard, &cpuinfo_arm_mach_init);
#elif defined(_WIN32) #elif defined(_WIN32)
InitOnceExecuteOnce(&init_guard, &cpuinfo_arm_windows_init, NULL, NULL); InitOnceExecuteOnce(&init_guard, &cpuinfo_arm_windows_init, NULL, NULL);
#else #else
cpuinfo_log_error("operating system is not supported in cpuinfo"); cpuinfo_log_error("operating system is not supported in cpuinfo");
#endif #endif
#elif CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64 #elif CPUINFO_ARCH_RISCV32 || CPUINFO_ARCH_RISCV64
#if defined(__linux__) #if defined(__linux__)
pthread_once(&init_guard, &cpuinfo_riscv_linux_init); pthread_once(&init_guard, &cpuinfo_riscv_linux_init);
#else #else
cpuinfo_log_error("operating system is not supported in cpuinfo"); cpuinfo_log_error("operating system is not supported in cpuinfo");
#endif #endif
#elif CPUINFO_ARCH_ASMJS || CPUINFO_ARCH_WASM || CPUINFO_ARCH_WASMSIMD #elif CPUINFO_ARCH_ASMJS || CPUINFO_ARCH_WASM || CPUINFO_ARCH_WASMSIMD
#if defined(__EMSCRIPTEN_PTHREADS__) #if defined(__EMSCRIPTEN_PTHREADS__)
pthread_once(&init_guard, &cpuinfo_emscripten_init); pthread_once(&init_guard, &cpuinfo_emscripten_init);
#else #else
if (!init_guard) { if (!init_guard) {
cpuinfo_emscripten_init(); cpuinfo_emscripten_init();
} }
init_guard = true; init_guard = true;
#endif #endif
#else #else
cpuinfo_log_error("processor architecture is not supported in cpuinfo"); cpuinfo_log_error("processor architecture is not supported in cpuinfo");
#endif #endif
return cpuinfo_is_initialized; return cpuinfo_is_initialized;
} }
void CPUINFO_ABI cpuinfo_deinitialize(void) { void CPUINFO_ABI cpuinfo_deinitialize(void) {}
}

View File

@ -1,35 +1,45 @@
#pragma once #pragma once
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include <cpuinfo.h> #include <cpuinfo.h>
#include <cpuinfo/common.h> #include <cpuinfo/common.h>
#define CPUINFO_LINUX_FLAG_PRESENT UINT32_C(0x00000001)
#define CPUINFO_LINUX_FLAG_PRESENT UINT32_C(0x00000001) #define CPUINFO_LINUX_FLAG_POSSIBLE UINT32_C(0x00000002)
#define CPUINFO_LINUX_FLAG_POSSIBLE UINT32_C(0x00000002) #define CPUINFO_LINUX_FLAG_MAX_FREQUENCY UINT32_C(0x00000004)
#define CPUINFO_LINUX_FLAG_MAX_FREQUENCY UINT32_C(0x00000004) #define CPUINFO_LINUX_FLAG_MIN_FREQUENCY UINT32_C(0x00000008)
#define CPUINFO_LINUX_FLAG_MIN_FREQUENCY UINT32_C(0x00000008) #define CPUINFO_LINUX_FLAG_SMT_ID UINT32_C(0x00000010)
#define CPUINFO_LINUX_FLAG_SMT_ID UINT32_C(0x00000010) #define CPUINFO_LINUX_FLAG_CORE_ID UINT32_C(0x00000020)
#define CPUINFO_LINUX_FLAG_CORE_ID UINT32_C(0x00000020) #define CPUINFO_LINUX_FLAG_PACKAGE_ID UINT32_C(0x00000040)
#define CPUINFO_LINUX_FLAG_PACKAGE_ID UINT32_C(0x00000040) #define CPUINFO_LINUX_FLAG_APIC_ID UINT32_C(0x00000080)
#define CPUINFO_LINUX_FLAG_APIC_ID UINT32_C(0x00000080) #define CPUINFO_LINUX_FLAG_SMT_CLUSTER UINT32_C(0x00000100)
#define CPUINFO_LINUX_FLAG_SMT_CLUSTER UINT32_C(0x00000100) #define CPUINFO_LINUX_FLAG_CORE_CLUSTER UINT32_C(0x00000200)
#define CPUINFO_LINUX_FLAG_CORE_CLUSTER UINT32_C(0x00000200) #define CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER UINT32_C(0x00000400)
#define CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER UINT32_C(0x00000400) #define CPUINFO_LINUX_FLAG_PROC_CPUINFO UINT32_C(0x00000800)
#define CPUINFO_LINUX_FLAG_PROC_CPUINFO UINT32_C(0x00000800) #define CPUINFO_LINUX_FLAG_VALID UINT32_C(0x00001000)
#define CPUINFO_LINUX_FLAG_VALID UINT32_C(0x00001000) #define CPUINFO_LINUX_FLAG_CUR_FREQUENCY UINT32_C(0x00002000)
#define CPUINFO_LINUX_FLAG_CUR_FREQUENCY UINT32_C(0x00002000) #define CPUINFO_LINUX_FLAG_CLUSTER_CLUSTER UINT32_C(0x00004000)
#define CPUINFO_LINUX_FLAG_CLUSTER_CLUSTER UINT32_C(0x00004000)
typedef bool (*cpuinfo_cpulist_callback)(uint32_t, uint32_t, void*); typedef bool (*cpuinfo_cpulist_callback)(uint32_t, uint32_t, void*);
CPUINFO_INTERNAL bool cpuinfo_linux_parse_cpulist(const char* filename, cpuinfo_cpulist_callback callback, void* context); CPUINFO_INTERNAL bool cpuinfo_linux_parse_cpulist(
typedef bool (*cpuinfo_smallfile_callback)(const char*, const char*, void*); const char* filename,
CPUINFO_INTERNAL bool cpuinfo_linux_parse_small_file(const char* filename, size_t buffer_size, cpuinfo_smallfile_callback, void* context); cpuinfo_cpulist_callback callback,
void* context);
typedef bool (*cpuinfo_smallfile_callback)(const char*, const char*, const char*, void*);
CPUINFO_INTERNAL bool cpuinfo_linux_parse_small_file(
const char* filename,
size_t buffer_size,
cpuinfo_smallfile_callback,
void* context);
typedef bool (*cpuinfo_line_callback)(const char*, const char*, void*, uint64_t); typedef bool (*cpuinfo_line_callback)(const char*, const char*, void*, uint64_t);
CPUINFO_INTERNAL bool cpuinfo_linux_parse_multiline_file(const char* filename, size_t buffer_size, cpuinfo_line_callback, void* context); CPUINFO_INTERNAL bool cpuinfo_linux_parse_multiline_file(
const char* filename,
size_t buffer_size,
cpuinfo_line_callback,
void* context);
CPUINFO_INTERNAL uint32_t cpuinfo_linux_get_max_processors_count(void); CPUINFO_INTERNAL uint32_t cpuinfo_linux_get_max_processors_count(void);
CPUINFO_INTERNAL uint32_t cpuinfo_linux_get_max_possible_processor(uint32_t max_processors_count); CPUINFO_INTERNAL uint32_t cpuinfo_linux_get_max_possible_processor(uint32_t max_processors_count);
@ -37,13 +47,21 @@ CPUINFO_INTERNAL uint32_t cpuinfo_linux_get_max_present_processor(uint32_t max_p
CPUINFO_INTERNAL uint32_t cpuinfo_linux_get_processor_cur_frequency(uint32_t processor); CPUINFO_INTERNAL uint32_t cpuinfo_linux_get_processor_cur_frequency(uint32_t processor);
CPUINFO_INTERNAL uint32_t cpuinfo_linux_get_processor_min_frequency(uint32_t processor); CPUINFO_INTERNAL uint32_t cpuinfo_linux_get_processor_min_frequency(uint32_t processor);
CPUINFO_INTERNAL uint32_t cpuinfo_linux_get_processor_max_frequency(uint32_t processor); CPUINFO_INTERNAL uint32_t cpuinfo_linux_get_processor_max_frequency(uint32_t processor);
CPUINFO_INTERNAL bool cpuinfo_linux_get_processor_package_id(uint32_t processor, uint32_t package_id[restrict static 1]); CPUINFO_INTERNAL bool cpuinfo_linux_get_processor_package_id(
uint32_t processor,
uint32_t package_id[restrict static 1]);
CPUINFO_INTERNAL bool cpuinfo_linux_get_processor_core_id(uint32_t processor, uint32_t core_id[restrict static 1]); CPUINFO_INTERNAL bool cpuinfo_linux_get_processor_core_id(uint32_t processor, uint32_t core_id[restrict static 1]);
CPUINFO_INTERNAL bool cpuinfo_linux_detect_possible_processors(uint32_t max_processors_count, CPUINFO_INTERNAL bool cpuinfo_linux_detect_possible_processors(
uint32_t* processor0_flags, uint32_t processor_struct_size, uint32_t possible_flag); uint32_t max_processors_count,
CPUINFO_INTERNAL bool cpuinfo_linux_detect_present_processors(uint32_t max_processors_count, uint32_t* processor0_flags,
uint32_t* processor0_flags, uint32_t processor_struct_size, uint32_t present_flag); uint32_t processor_struct_size,
uint32_t possible_flag);
CPUINFO_INTERNAL bool cpuinfo_linux_detect_present_processors(
uint32_t max_processors_count,
uint32_t* processor0_flags,
uint32_t processor_struct_size,
uint32_t present_flag);
typedef bool (*cpuinfo_siblings_callback)(uint32_t, uint32_t, uint32_t, void*); typedef bool (*cpuinfo_siblings_callback)(uint32_t, uint32_t, uint32_t, void*);
CPUINFO_INTERNAL bool cpuinfo_linux_detect_core_siblings( CPUINFO_INTERNAL bool cpuinfo_linux_detect_core_siblings(

View File

@ -1,21 +1,20 @@
#include <errno.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <sched.h> #include <sched.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#if CPUINFO_MOCK #if CPUINFO_MOCK
#include <cpuinfo-mock.h> #include <cpuinfo-mock.h>
#endif #endif
#include <linux/api.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <linux/api.h>
/* /*
* Size, in chars, of the on-stack buffer used for parsing cpu lists. * Size, in chars, of the on-stack buffer used for parsing cpu lists.
@ -25,7 +24,6 @@
*/ */
#define BUFFER_SIZE 256 #define BUFFER_SIZE 256
/* Locale-independent */ /* Locale-independent */
inline static bool is_whitespace(char c) { inline static bool is_whitespace(char c) {
switch (c) { switch (c) {
@ -42,7 +40,7 @@ inline static bool is_whitespace(char c) {
inline static const char* parse_number(const char* string, const char* end, uint32_t number_ptr[restrict static 1]) { inline static const char* parse_number(const char* string, const char* end, uint32_t number_ptr[restrict static 1]) {
uint32_t number = 0; uint32_t number = 0;
while (string != end) { while (string != end) {
const uint32_t digit = (uint32_t) (*string) - (uint32_t) '0'; const uint32_t digit = (uint32_t)(*string) - (uint32_t)'0';
if (digit >= 10) { if (digit >= 10) {
break; break;
} }
@ -53,7 +51,11 @@ inline static const char* parse_number(const char* string, const char* end, uint
return string; return string;
} }
inline static bool parse_entry(const char* entry_start, const char* entry_end, cpuinfo_cpulist_callback callback, void* context) { inline static bool parse_entry(
const char* entry_start,
const char* entry_end,
cpuinfo_cpulist_callback callback,
void* context) {
/* Skip whitespace at the beginning of an entry */ /* Skip whitespace at the beginning of an entry */
for (; entry_start != entry_end; entry_start++) { for (; entry_start != entry_end; entry_start++) {
if (!is_whitespace(*entry_start)) { if (!is_whitespace(*entry_start)) {
@ -67,36 +69,44 @@ inline static bool parse_entry(const char* entry_start, const char* entry_end, c
} }
} }
const size_t entry_length = (size_t) (entry_end - entry_start); const size_t entry_length = (size_t)(entry_end - entry_start);
if (entry_length == 0) { if (entry_length == 0) {
cpuinfo_log_warning("unexpected zero-length cpu list entry ignored"); cpuinfo_log_warning("unexpected zero-length cpu list entry ignored");
return false; return false;
} }
#if CPUINFO_LOG_DEBUG_PARSERS #if CPUINFO_LOG_DEBUG_PARSERS
cpuinfo_log_debug("parse cpu list entry \"%.*s\" (%zu chars)", (int) entry_length, entry_start, entry_length); cpuinfo_log_debug("parse cpu list entry \"%.*s\" (%zu chars)", (int)entry_length, entry_start, entry_length);
#endif #endif
uint32_t first_cpu, last_cpu; uint32_t first_cpu, last_cpu;
const char* number_end = parse_number(entry_start, entry_end, &first_cpu); const char* number_end = parse_number(entry_start, entry_end, &first_cpu);
if (number_end == entry_start) { if (number_end == entry_start) {
/* Failed to parse the number; ignore the entry */ /* Failed to parse the number; ignore the entry */
cpuinfo_log_warning("invalid character '%c' in the cpu list entry \"%.*s\": entry is ignored", cpuinfo_log_warning(
entry_start[0], (int) entry_length, entry_start); "invalid character '%c' in the cpu list entry \"%.*s\": entry is ignored",
entry_start[0],
(int)entry_length,
entry_start);
return false; return false;
} else if (number_end == entry_end) { } else if (number_end == entry_end) {
/* Completely parsed the entry */ /* Completely parsed the entry */
#if CPUINFO_LOG_DEBUG_PARSERS #if CPUINFO_LOG_DEBUG_PARSERS
cpuinfo_log_debug("cpulist: call callback with list_start = %"PRIu32", list_end = %"PRIu32, cpuinfo_log_debug(
first_cpu, first_cpu + 1); "cpulist: call callback with list_start = %" PRIu32 ", list_end = %" PRIu32,
#endif first_cpu,
first_cpu + 1);
#endif
return callback(first_cpu, first_cpu + 1, context); return callback(first_cpu, first_cpu + 1, context);
} }
/* Parse the second part of the entry */ /* Parse the second part of the entry */
if (*number_end != '-') { if (*number_end != '-') {
cpuinfo_log_warning("invalid character '%c' in the cpu list entry \"%.*s\": entry is ignored", cpuinfo_log_warning(
*number_end, (int) entry_length, entry_start); "invalid character '%c' in the cpu list entry \"%.*s\": entry is ignored",
*number_end,
(int)entry_length,
entry_start);
return false; return false;
} }
@ -104,28 +114,40 @@ inline static bool parse_entry(const char* entry_start, const char* entry_end, c
number_end = parse_number(number_start, entry_end, &last_cpu); number_end = parse_number(number_start, entry_end, &last_cpu);
if (number_end == number_start) { if (number_end == number_start) {
/* Failed to parse the second number; ignore the entry */ /* Failed to parse the second number; ignore the entry */
cpuinfo_log_warning("invalid character '%c' in the cpu list entry \"%.*s\": entry is ignored", cpuinfo_log_warning(
*number_start, (int) entry_length, entry_start); "invalid character '%c' in the cpu list entry \"%.*s\": entry is ignored",
*number_start,
(int)entry_length,
entry_start);
return false; return false;
} }
if (number_end != entry_end) { if (number_end != entry_end) {
/* Partially parsed the entry; ignore unparsed characters and continue with the parsed part */ /* Partially parsed the entry; ignore unparsed characters and
cpuinfo_log_warning("ignored invalid characters \"%.*s\" at the end of cpu list entry \"%.*s\"", * continue with the parsed part */
(int) (entry_end - number_end), number_start, (int) entry_length, entry_start); cpuinfo_log_warning(
"ignored invalid characters \"%.*s\" at the end of cpu list entry \"%.*s\"",
(int)(entry_end - number_end),
number_start,
(int)entry_length,
entry_start);
} }
if (last_cpu < first_cpu) { if (last_cpu < first_cpu) {
cpuinfo_log_warning("ignored cpu list entry \"%.*s\": invalid range %"PRIu32"-%"PRIu32, cpuinfo_log_warning(
(int) entry_length, entry_start, first_cpu, last_cpu); "ignored cpu list entry \"%.*s\": invalid range %" PRIu32 "-%" PRIu32,
(int)entry_length,
entry_start,
first_cpu,
last_cpu);
return false; return false;
} }
/* Parsed both parts of the entry; update CPU set */ /* Parsed both parts of the entry; update CPU set */
#if CPUINFO_LOG_DEBUG_PARSERS #if CPUINFO_LOG_DEBUG_PARSERS
cpuinfo_log_debug("cpulist: call callback with list_start = %"PRIu32", list_end = %"PRIu32, cpuinfo_log_debug(
first_cpu, last_cpu + 1); "cpulist: call callback with list_start = %" PRIu32 ", list_end = %" PRIu32, first_cpu, last_cpu + 1);
#endif #endif
return callback(first_cpu, last_cpu + 1, context); return callback(first_cpu, last_cpu + 1, context);
} }
@ -133,9 +155,9 @@ bool cpuinfo_linux_parse_cpulist(const char* filename, cpuinfo_cpulist_callback
bool status = true; bool status = true;
int file = -1; int file = -1;
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
#if CPUINFO_LOG_DEBUG_PARSERS #if CPUINFO_LOG_DEBUG_PARSERS
cpuinfo_log_debug("parsing cpu list from file %s", filename); cpuinfo_log_debug("parsing cpu list from file %s", filename);
#endif #endif
#if CPUINFO_MOCK #if CPUINFO_MOCK
file = cpuinfo_mock_open(filename, O_RDONLY); file = cpuinfo_mock_open(filename, O_RDONLY);
@ -154,29 +176,32 @@ bool cpuinfo_linux_parse_cpulist(const char* filename, cpuinfo_cpulist_callback
ssize_t bytes_read; ssize_t bytes_read;
do { do {
#if CPUINFO_MOCK #if CPUINFO_MOCK
bytes_read = cpuinfo_mock_read(file, data_start, (size_t) (buffer_end - data_start)); bytes_read = cpuinfo_mock_read(file, data_start, (size_t)(buffer_end - data_start));
#else #else
bytes_read = read(file, data_start, (size_t) (buffer_end - data_start)); bytes_read = read(file, data_start, (size_t)(buffer_end - data_start));
#endif #endif
if (bytes_read < 0) { if (bytes_read < 0) {
cpuinfo_log_info("failed to read file %s at position %zu: %s", filename, position, strerror(errno)); cpuinfo_log_info(
"failed to read file %s at position %zu: %s", filename, position, strerror(errno));
status = false; status = false;
goto cleanup; goto cleanup;
} }
position += (size_t) bytes_read; position += (size_t)bytes_read;
const char* data_end = data_start + (size_t) bytes_read; const char* data_end = data_start + (size_t)bytes_read;
const char* entry_start = buffer; const char* entry_start = buffer;
if (bytes_read == 0) { if (bytes_read == 0) {
/* No more data in the file: process the remaining text in the buffer as a single entry */ /* No more data in the file: process the remaining text
* in the buffer as a single entry */
const char* entry_end = data_end; const char* entry_end = data_end;
const bool entry_status = parse_entry(entry_start, entry_end, callback, context); const bool entry_status = parse_entry(entry_start, entry_end, callback, context);
status &= entry_status; status &= entry_status;
} else { } else {
const char* entry_end; const char* entry_end;
do { do {
/* Find the end of the entry, as indicated by a comma (',') */ /* Find the end of the entry, as indicated by a
* comma (',') */
for (entry_end = entry_start; entry_end != data_end; entry_end++) { for (entry_end = entry_start; entry_end != data_end; entry_end++) {
if (*entry_end == ',') { if (*entry_end == ',') {
break; break;
@ -184,18 +209,21 @@ bool cpuinfo_linux_parse_cpulist(const char* filename, cpuinfo_cpulist_callback
} }
/* /*
* If we located separator at the end of the entry, parse it. * If we located separator at the end of the
* Otherwise, there may be more data at the end; read the file once again. * entry, parse it. Otherwise, there may be more
* data at the end; read the file once again.
*/ */
if (entry_end != data_end) { if (entry_end != data_end) {
const bool entry_status = parse_entry(entry_start, entry_end, callback, context); const bool entry_status =
parse_entry(entry_start, entry_end, callback, context);
status &= entry_status; status &= entry_status;
entry_start = entry_end + 1; entry_start = entry_end + 1;
} }
} while (entry_end != data_end); } while (entry_end != data_end);
/* Move remaining partial entry data at the end to the beginning of the buffer */ /* Move remaining partial entry data at the end to the
const size_t entry_length = (size_t) (entry_end - entry_start); * beginning of the buffer */
const size_t entry_length = (size_t)(entry_end - entry_start);
memmove(buffer, entry_start, entry_length); memmove(buffer, entry_start, entry_length);
data_start = &buffer[entry_length]; data_start = &buffer[entry_length];
} }

View File

@ -1,30 +1,28 @@
#include <errno.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <stddef.h>
#include <string.h> #include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <sched.h> #include <sched.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#if !CPUINFO_MOCK #if !CPUINFO_MOCK
#error This file should be built only in mock mode #error This file should be built only in mock mode
#endif #endif
#include <cpuinfo-mock.h>
#include <arm/linux/api.h> #include <arm/linux/api.h>
#include <arm/midr.h> #include <arm/midr.h>
#include <cpuinfo-mock.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
static struct cpuinfo_mock_file* cpuinfo_mock_files = NULL; static struct cpuinfo_mock_file* cpuinfo_mock_files = NULL;
static uint32_t cpuinfo_mock_file_count = 0; static uint32_t cpuinfo_mock_file_count = 0;
void CPUINFO_ABI cpuinfo_mock_filesystem(struct cpuinfo_mock_file* files) { void CPUINFO_ABI cpuinfo_mock_filesystem(struct cpuinfo_mock_file* files) {
cpuinfo_log_info("filesystem mocking enabled"); cpuinfo_log_info("filesystem mocking enabled");
uint32_t file_count = 0; uint32_t file_count = 0;
@ -54,7 +52,7 @@ int CPUINFO_ABI cpuinfo_mock_open(const char* path, int oflag) {
return -1; return -1;
} }
cpuinfo_mock_files[i].offset = 0; cpuinfo_mock_files[i].offset = 0;
return (int) i; return (int)i;
} }
} }
errno = ENOENT; errno = ENOENT;
@ -67,7 +65,7 @@ int CPUINFO_ABI cpuinfo_mock_close(int fd) {
return close(fd); return close(fd);
} }
if ((unsigned int) fd >= cpuinfo_mock_file_count) { if ((unsigned int)fd >= cpuinfo_mock_file_count) {
errno = EBADF; errno = EBADF;
return -1; return -1;
} }
@ -85,7 +83,7 @@ ssize_t CPUINFO_ABI cpuinfo_mock_read(int fd, void* buffer, size_t capacity) {
return read(fd, buffer, capacity); return read(fd, buffer, capacity);
} }
if ((unsigned int) fd >= cpuinfo_mock_file_count) { if ((unsigned int)fd >= cpuinfo_mock_file_count) {
errno = EBADF; errno = EBADF;
return -1; return -1;
} }
@ -99,7 +97,7 @@ ssize_t CPUINFO_ABI cpuinfo_mock_read(int fd, void* buffer, size_t capacity) {
if (count > capacity) { if (count > capacity) {
count = capacity; count = capacity;
} }
memcpy(buffer, (void*) cpuinfo_mock_files[fd].content + offset, count); memcpy(buffer, (void*)cpuinfo_mock_files[fd].content + offset, count);
cpuinfo_mock_files[fd].offset += count; cpuinfo_mock_files[fd].offset += count;
return (ssize_t) count; return (ssize_t)count;
} }

View File

@ -1,27 +1,29 @@
#include <alloca.h>
#include <errno.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <alloca.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#if CPUINFO_MOCK #if CPUINFO_MOCK
#include <cpuinfo-mock.h> #include <cpuinfo-mock.h>
#endif #endif
#include <linux/api.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <linux/api.h>
bool cpuinfo_linux_parse_multiline_file(
bool cpuinfo_linux_parse_multiline_file(const char* filename, size_t buffer_size, cpuinfo_line_callback callback, void* context) const char* filename,
{ size_t buffer_size,
cpuinfo_line_callback callback,
void* context) {
int file = -1; int file = -1;
bool status = false; bool status = false;
char* buffer = (char*) alloca(buffer_size); char* buffer = (char*)alloca(buffer_size);
#if CPUINFO_MOCK #if CPUINFO_MOCK
file = cpuinfo_mock_open(filename, O_RDONLY); file = cpuinfo_mock_open(filename, O_RDONLY);
@ -41,22 +43,23 @@ bool cpuinfo_linux_parse_multiline_file(const char* filename, size_t buffer_size
ssize_t bytes_read; ssize_t bytes_read;
do { do {
#if CPUINFO_MOCK #if CPUINFO_MOCK
bytes_read = cpuinfo_mock_read(file, data_start, (size_t) (buffer_end - data_start)); bytes_read = cpuinfo_mock_read(file, data_start, (size_t)(buffer_end - data_start));
#else #else
bytes_read = read(file, data_start, (size_t) (buffer_end - data_start)); bytes_read = read(file, data_start, (size_t)(buffer_end - data_start));
#endif #endif
if (bytes_read < 0) { if (bytes_read < 0) {
cpuinfo_log_info("failed to read file %s at position %zu: %s", cpuinfo_log_info(
filename, position, strerror(errno)); "failed to read file %s at position %zu: %s", filename, position, strerror(errno));
goto cleanup; goto cleanup;
} }
position += (size_t) bytes_read; position += (size_t)bytes_read;
const char* data_end = data_start + (size_t) bytes_read; const char* data_end = data_start + (size_t)bytes_read;
const char* line_start = buffer; const char* line_start = buffer;
if (bytes_read == 0) { if (bytes_read == 0) {
/* No more data in the file: process the remaining text in the buffer as a single entry */ /* No more data in the file: process the remaining text
* in the buffer as a single entry */
const char* line_end = data_end; const char* line_end = data_end;
if (!callback(line_start, line_end, context, line_number)) { if (!callback(line_start, line_end, context, line_number)) {
goto cleanup; goto cleanup;
@ -64,7 +67,9 @@ bool cpuinfo_linux_parse_multiline_file(const char* filename, size_t buffer_size
} else { } else {
const char* line_end; const char* line_end;
do { do {
/* Find the end of the entry, as indicated by newline character ('\n') */ /* Find the end of the entry, as indicated by
* newline character ('\n')
*/
for (line_end = line_start; line_end != data_end; line_end++) { for (line_end = line_start; line_end != data_end; line_end++) {
if (*line_end == '\n') { if (*line_end == '\n') {
break; break;
@ -72,8 +77,9 @@ bool cpuinfo_linux_parse_multiline_file(const char* filename, size_t buffer_size
} }
/* /*
* If we located separator at the end of the entry, parse it. * If we located separator at the end of the
* Otherwise, there may be more data at the end; read the file once again. * entry, parse it. Otherwise, there may be more
* data at the end; read the file once again.
*/ */
if (line_end != data_end) { if (line_end != data_end) {
if (!callback(line_start, line_end, context, line_number++)) { if (!callback(line_start, line_end, context, line_number++)) {
@ -83,8 +89,9 @@ bool cpuinfo_linux_parse_multiline_file(const char* filename, size_t buffer_size
} }
} while (line_end != data_end); } while (line_end != data_end);
/* Move remaining partial line data at the end to the beginning of the buffer */ /* Move remaining partial line data at the end to the
const size_t line_length = (size_t) (line_end - line_start); * beginning of the buffer */
const size_t line_length = (size_t)(line_end - line_start);
memmove(buffer, line_start, line_length); memmove(buffer, line_start, line_length);
data_start = &buffer[line_length]; data_start = &buffer[line_length];
} }

View File

@ -1,31 +1,32 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#if !defined(__ANDROID__) #if !defined(__ANDROID__)
/* /*
* sched.h is only used for CPU_SETSIZE constant. * sched.h is only used for CPU_SETSIZE constant.
* Android NDK headers before platform 21 do have this constant in sched.h * Android NDK headers before platform 21 do have this constant in sched.h
*/ */
#include <sched.h> #include <sched.h>
#endif #endif
#include <linux/api.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <linux/api.h>
#define STRINGIFY(token) #token #define STRINGIFY(token) #token
#define KERNEL_MAX_FILENAME "/sys/devices/system/cpu/kernel_max" #define KERNEL_MAX_FILENAME "/sys/devices/system/cpu/kernel_max"
#define KERNEL_MAX_FILESIZE 32 #define KERNEL_MAX_FILESIZE 32
#define FREQUENCY_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/cpufreq/cpuinfo_max_freq")) #define FREQUENCY_FILENAME_SIZE \
(sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/cpufreq/cpuinfo_max_freq"))
#define CUR_FREQUENCY_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/cpufreq/cpuinfo_cur_freq" #define CUR_FREQUENCY_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/cpufreq/cpuinfo_cur_freq"
#define MAX_FREQUENCY_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/cpufreq/cpuinfo_max_freq" #define MAX_FREQUENCY_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/cpufreq/cpuinfo_max_freq"
#define MIN_FREQUENCY_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/cpufreq/cpuinfo_min_freq" #define MIN_FREQUENCY_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/cpufreq/cpuinfo_min_freq"
#define FREQUENCY_FILESIZE 32 #define FREQUENCY_FILESIZE 32
#define PACKAGE_ID_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/physical_package_id")) #define PACKAGE_ID_FILENAME_SIZE \
(sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/physical_package_id"))
#define PACKAGE_ID_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/physical_package_id" #define PACKAGE_ID_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/physical_package_id"
#define PACKAGE_ID_FILESIZE 32 #define PACKAGE_ID_FILESIZE 32
#define CORE_ID_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/core_id")) #define CORE_ID_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/core_id"))
@ -34,24 +35,27 @@
#define CORE_CPUS_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/core_cpus_list")) #define CORE_CPUS_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/core_cpus_list"))
#define CORE_CPUS_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/core_cpus_list" #define CORE_CPUS_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/core_cpus_list"
#define CORE_SIBLINGS_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/core_siblings_list")) #define CORE_SIBLINGS_FILENAME_SIZE \
(sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/core_siblings_list"))
#define CORE_SIBLINGS_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/core_siblings_list" #define CORE_SIBLINGS_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/core_siblings_list"
#define CLUSTER_CPUS_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/cluster_cpus_list")) #define CLUSTER_CPUS_FILENAME_SIZE \
(sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/cluster_cpus_list"))
#define CLUSTER_CPUS_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/cluster_cpus_list" #define CLUSTER_CPUS_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/cluster_cpus_list"
#define PACKAGE_CPUS_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/package_cpus_list")) #define PACKAGE_CPUS_FILENAME_SIZE \
(sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/package_cpus_list"))
#define PACKAGE_CPUS_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/package_cpus_list" #define PACKAGE_CPUS_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/package_cpus_list"
#define THREAD_SIBLINGS_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/thread_siblings_list")) #define THREAD_SIBLINGS_FILENAME_SIZE \
(sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/thread_siblings_list"))
#define THREAD_SIBLINGS_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/thread_siblings_list" #define THREAD_SIBLINGS_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/thread_siblings_list"
#define POSSIBLE_CPULIST_FILENAME "/sys/devices/system/cpu/possible" #define POSSIBLE_CPULIST_FILENAME "/sys/devices/system/cpu/possible"
#define PRESENT_CPULIST_FILENAME "/sys/devices/system/cpu/present" #define PRESENT_CPULIST_FILENAME "/sys/devices/system/cpu/present"
inline static const char* parse_number(const char* start, const char* end, uint32_t number_ptr[restrict static 1]) { inline static const char* parse_number(const char* start, const char* end, uint32_t number_ptr[restrict static 1]) {
uint32_t number = 0; uint32_t number = 0;
const char* parsed = start; const char* parsed = start;
for (; parsed != end; parsed++) { for (; parsed != end; parsed++) {
const uint32_t digit = (uint32_t) (uint8_t) (*parsed) - (uint32_t) '0'; const uint32_t digit = (uint32_t)(uint8_t)(*parsed) - (uint32_t)'0';
if (digit >= 10) { if (digit >= 10) {
break; break;
} }
@ -75,20 +79,20 @@ inline static bool is_whitespace(char c) {
} }
#if defined(__ANDROID__) && !defined(CPU_SETSIZE) #if defined(__ANDROID__) && !defined(CPU_SETSIZE)
/* /*
* Android NDK headers before platform 21 do not define CPU_SETSIZE, * Android NDK headers before platform 21 do not define CPU_SETSIZE,
* so we hard-code its value, as defined in platform 21 headers * so we hard-code its value, as defined in platform 21 headers
*/ */
#if defined(__LP64__) #if defined(__LP64__)
static const uint32_t default_max_processors_count = 1024; static const uint32_t default_max_processors_count = 1024;
#else
static const uint32_t default_max_processors_count = 32;
#endif
#else #else
static const uint32_t default_max_processors_count = CPU_SETSIZE; static const uint32_t default_max_processors_count = 32;
#endif
#else
static const uint32_t default_max_processors_count = CPU_SETSIZE;
#endif #endif
static bool uint32_parser(const char* text_start, const char* text_end, void* context) { static bool uint32_parser(const char* filename, const char* text_start, const char* text_end, void* context) {
if (text_start == text_end) { if (text_start == text_end) {
cpuinfo_log_error("failed to parse file %s: file is empty", KERNEL_MAX_FILENAME); cpuinfo_log_error("failed to parse file %s: file is empty", KERNEL_MAX_FILENAME);
return false; return false;
@ -97,20 +101,26 @@ static bool uint32_parser(const char* text_start, const char* text_end, void* co
uint32_t kernel_max = 0; uint32_t kernel_max = 0;
const char* parsed_end = parse_number(text_start, text_end, &kernel_max); const char* parsed_end = parse_number(text_start, text_end, &kernel_max);
if (parsed_end == text_start) { if (parsed_end == text_start) {
cpuinfo_log_error("failed to parse file %s: \"%.*s\" is not an unsigned number", cpuinfo_log_error(
KERNEL_MAX_FILENAME, (int) (text_end - text_start), text_start); "failed to parse file %s: \"%.*s\" is not an unsigned number",
filename,
(int)(text_end - text_start),
text_start);
return false; return false;
} else { } else {
for (const char* char_ptr = parsed_end; char_ptr != text_end; char_ptr++) { for (const char* char_ptr = parsed_end; char_ptr != text_end; char_ptr++) {
if (!is_whitespace(*char_ptr)) { if (!is_whitespace(*char_ptr)) {
cpuinfo_log_warning("non-whitespace characters \"%.*s\" following number in file %s are ignored", cpuinfo_log_warning(
(int) (text_end - char_ptr), char_ptr, KERNEL_MAX_FILENAME); "non-whitespace characters \"%.*s\" following number in file %s are ignored",
(int)(text_end - char_ptr),
char_ptr,
filename);
break; break;
} }
} }
} }
uint32_t* kernel_max_ptr = (uint32_t*) context; uint32_t* kernel_max_ptr = (uint32_t*)context;
*kernel_max_ptr = kernel_max; *kernel_max_ptr = kernel_max;
return true; return true;
} }
@ -118,133 +128,160 @@ static bool uint32_parser(const char* text_start, const char* text_end, void* co
uint32_t cpuinfo_linux_get_max_processors_count(void) { uint32_t cpuinfo_linux_get_max_processors_count(void) {
uint32_t kernel_max; uint32_t kernel_max;
if (cpuinfo_linux_parse_small_file(KERNEL_MAX_FILENAME, KERNEL_MAX_FILESIZE, uint32_parser, &kernel_max)) { if (cpuinfo_linux_parse_small_file(KERNEL_MAX_FILENAME, KERNEL_MAX_FILESIZE, uint32_parser, &kernel_max)) {
cpuinfo_log_debug("parsed kernel_max value of %"PRIu32" from %s", kernel_max, KERNEL_MAX_FILENAME); cpuinfo_log_debug("parsed kernel_max value of %" PRIu32 " from %s", kernel_max, KERNEL_MAX_FILENAME);
if (kernel_max >= default_max_processors_count) { if (kernel_max >= default_max_processors_count) {
cpuinfo_log_warning("kernel_max value of %"PRIu32" parsed from %s exceeds platform-default limit %"PRIu32, cpuinfo_log_warning(
kernel_max, KERNEL_MAX_FILENAME, default_max_processors_count - 1); "kernel_max value of %" PRIu32
" parsed from %s exceeds platform-default limit %" PRIu32,
kernel_max,
KERNEL_MAX_FILENAME,
default_max_processors_count - 1);
} }
return kernel_max + 1; return kernel_max + 1;
} else { } else {
cpuinfo_log_warning("using platform-default max processors count = %"PRIu32, default_max_processors_count); cpuinfo_log_warning(
"using platform-default max processors count = %" PRIu32, default_max_processors_count);
return default_max_processors_count; return default_max_processors_count;
} }
} }
uint32_t cpuinfo_linux_get_processor_cur_frequency(uint32_t processor) { uint32_t cpuinfo_linux_get_processor_cur_frequency(uint32_t processor) {
char cur_frequency_filename[FREQUENCY_FILENAME_SIZE]; char cur_frequency_filename[FREQUENCY_FILENAME_SIZE];
const int chars_formatted = snprintf( const int chars_formatted =
cur_frequency_filename, FREQUENCY_FILENAME_SIZE, CUR_FREQUENCY_FILENAME_FORMAT, processor); snprintf(cur_frequency_filename, FREQUENCY_FILENAME_SIZE, CUR_FREQUENCY_FILENAME_FORMAT, processor);
if ((unsigned int) chars_formatted >= FREQUENCY_FILENAME_SIZE) { if ((unsigned int)chars_formatted >= FREQUENCY_FILENAME_SIZE) {
cpuinfo_log_warning("failed to format filename for current frequency of processor %"PRIu32, processor); cpuinfo_log_warning("failed to format filename for current frequency of processor %" PRIu32, processor);
return 0; return 0;
} }
uint32_t cur_frequency; uint32_t cur_frequency;
if (cpuinfo_linux_parse_small_file(cur_frequency_filename, FREQUENCY_FILESIZE, uint32_parser, &cur_frequency)) { if (cpuinfo_linux_parse_small_file(cur_frequency_filename, FREQUENCY_FILESIZE, uint32_parser, &cur_frequency)) {
cpuinfo_log_debug("parsed currrent frequency value of %"PRIu32" KHz for logical processor %"PRIu32" from %s", cpuinfo_log_debug(
cur_frequency, processor, cur_frequency_filename); "parsed currrent frequency value of %" PRIu32 " KHz for logical processor %" PRIu32 " from %s",
cur_frequency,
processor,
cur_frequency_filename);
return cur_frequency; return cur_frequency;
} else { } else {
cpuinfo_log_warning("failed to parse current frequency for processor %"PRIu32" from %s", cpuinfo_log_warning(
processor, cur_frequency_filename); "failed to parse current frequency for processor %" PRIu32 " from %s",
processor,
cur_frequency_filename);
return 0; return 0;
} }
} }
uint32_t cpuinfo_linux_get_processor_max_frequency(uint32_t processor) { uint32_t cpuinfo_linux_get_processor_max_frequency(uint32_t processor) {
char max_frequency_filename[FREQUENCY_FILENAME_SIZE]; char max_frequency_filename[FREQUENCY_FILENAME_SIZE];
const int chars_formatted = snprintf( const int chars_formatted =
max_frequency_filename, FREQUENCY_FILENAME_SIZE, MAX_FREQUENCY_FILENAME_FORMAT, processor); snprintf(max_frequency_filename, FREQUENCY_FILENAME_SIZE, MAX_FREQUENCY_FILENAME_FORMAT, processor);
if ((unsigned int) chars_formatted >= FREQUENCY_FILENAME_SIZE) { if ((unsigned int)chars_formatted >= FREQUENCY_FILENAME_SIZE) {
cpuinfo_log_warning("failed to format filename for max frequency of processor %"PRIu32, processor); cpuinfo_log_warning("failed to format filename for max frequency of processor %" PRIu32, processor);
return 0; return 0;
} }
uint32_t max_frequency; uint32_t max_frequency;
if (cpuinfo_linux_parse_small_file(max_frequency_filename, FREQUENCY_FILESIZE, uint32_parser, &max_frequency)) { if (cpuinfo_linux_parse_small_file(max_frequency_filename, FREQUENCY_FILESIZE, uint32_parser, &max_frequency)) {
cpuinfo_log_debug("parsed max frequency value of %"PRIu32" KHz for logical processor %"PRIu32" from %s", cpuinfo_log_debug(
max_frequency, processor, max_frequency_filename); "parsed max frequency value of %" PRIu32 " KHz for logical processor %" PRIu32 " from %s",
max_frequency,
processor,
max_frequency_filename);
return max_frequency; return max_frequency;
} else { } else {
cpuinfo_log_warning("failed to parse max frequency for processor %"PRIu32" from %s", cpuinfo_log_warning(
processor, max_frequency_filename); "failed to parse max frequency for processor %" PRIu32 " from %s",
processor,
max_frequency_filename);
return 0; return 0;
} }
} }
uint32_t cpuinfo_linux_get_processor_min_frequency(uint32_t processor) { uint32_t cpuinfo_linux_get_processor_min_frequency(uint32_t processor) {
char min_frequency_filename[FREQUENCY_FILENAME_SIZE]; char min_frequency_filename[FREQUENCY_FILENAME_SIZE];
const int chars_formatted = snprintf( const int chars_formatted =
min_frequency_filename, FREQUENCY_FILENAME_SIZE, MIN_FREQUENCY_FILENAME_FORMAT, processor); snprintf(min_frequency_filename, FREQUENCY_FILENAME_SIZE, MIN_FREQUENCY_FILENAME_FORMAT, processor);
if ((unsigned int) chars_formatted >= FREQUENCY_FILENAME_SIZE) { if ((unsigned int)chars_formatted >= FREQUENCY_FILENAME_SIZE) {
cpuinfo_log_warning("failed to format filename for min frequency of processor %"PRIu32, processor); cpuinfo_log_warning("failed to format filename for min frequency of processor %" PRIu32, processor);
return 0; return 0;
} }
uint32_t min_frequency; uint32_t min_frequency;
if (cpuinfo_linux_parse_small_file(min_frequency_filename, FREQUENCY_FILESIZE, uint32_parser, &min_frequency)) { if (cpuinfo_linux_parse_small_file(min_frequency_filename, FREQUENCY_FILESIZE, uint32_parser, &min_frequency)) {
cpuinfo_log_debug("parsed min frequency value of %"PRIu32" KHz for logical processor %"PRIu32" from %s", cpuinfo_log_debug(
min_frequency, processor, min_frequency_filename); "parsed min frequency value of %" PRIu32 " KHz for logical processor %" PRIu32 " from %s",
min_frequency,
processor,
min_frequency_filename);
return min_frequency; return min_frequency;
} else { } else {
/* /*
* This error is less severe than parsing max frequency, because min frequency is only useful for clustering, * This error is less severe than parsing max frequency, because
* while max frequency is also needed for peak FLOPS calculation. * min frequency is only useful for clustering, while max
* frequency is also needed for peak FLOPS calculation.
*/ */
cpuinfo_log_info("failed to parse min frequency for processor %"PRIu32" from %s", cpuinfo_log_info(
processor, min_frequency_filename); "failed to parse min frequency for processor %" PRIu32 " from %s",
processor,
min_frequency_filename);
return 0; return 0;
} }
} }
bool cpuinfo_linux_get_processor_core_id(uint32_t processor, uint32_t core_id_ptr[restrict static 1]) { bool cpuinfo_linux_get_processor_core_id(uint32_t processor, uint32_t core_id_ptr[restrict static 1]) {
char core_id_filename[PACKAGE_ID_FILENAME_SIZE]; char core_id_filename[PACKAGE_ID_FILENAME_SIZE];
const int chars_formatted = snprintf( const int chars_formatted =
core_id_filename, CORE_ID_FILENAME_SIZE, CORE_ID_FILENAME_FORMAT, processor); snprintf(core_id_filename, CORE_ID_FILENAME_SIZE, CORE_ID_FILENAME_FORMAT, processor);
if ((unsigned int) chars_formatted >= CORE_ID_FILENAME_SIZE) { if ((unsigned int)chars_formatted >= CORE_ID_FILENAME_SIZE) {
cpuinfo_log_warning("failed to format filename for core id of processor %"PRIu32, processor); cpuinfo_log_warning("failed to format filename for core id of processor %" PRIu32, processor);
return 0; return 0;
} }
uint32_t core_id; uint32_t core_id;
if (cpuinfo_linux_parse_small_file(core_id_filename, CORE_ID_FILESIZE, uint32_parser, &core_id)) { if (cpuinfo_linux_parse_small_file(core_id_filename, CORE_ID_FILESIZE, uint32_parser, &core_id)) {
cpuinfo_log_debug("parsed core id value of %"PRIu32" for logical processor %"PRIu32" from %s", cpuinfo_log_debug(
core_id, processor, core_id_filename); "parsed core id value of %" PRIu32 " for logical processor %" PRIu32 " from %s",
core_id,
processor,
core_id_filename);
*core_id_ptr = core_id; *core_id_ptr = core_id;
return true; return true;
} else { } else {
cpuinfo_log_info("failed to parse core id for processor %"PRIu32" from %s", cpuinfo_log_info(
processor, core_id_filename); "failed to parse core id for processor %" PRIu32 " from %s", processor, core_id_filename);
return false; return false;
} }
} }
bool cpuinfo_linux_get_processor_package_id(uint32_t processor, uint32_t package_id_ptr[restrict static 1]) { bool cpuinfo_linux_get_processor_package_id(uint32_t processor, uint32_t package_id_ptr[restrict static 1]) {
char package_id_filename[PACKAGE_ID_FILENAME_SIZE]; char package_id_filename[PACKAGE_ID_FILENAME_SIZE];
const int chars_formatted = snprintf( const int chars_formatted =
package_id_filename, PACKAGE_ID_FILENAME_SIZE, PACKAGE_ID_FILENAME_FORMAT, processor); snprintf(package_id_filename, PACKAGE_ID_FILENAME_SIZE, PACKAGE_ID_FILENAME_FORMAT, processor);
if ((unsigned int) chars_formatted >= PACKAGE_ID_FILENAME_SIZE) { if ((unsigned int)chars_formatted >= PACKAGE_ID_FILENAME_SIZE) {
cpuinfo_log_warning("failed to format filename for package id of processor %"PRIu32, processor); cpuinfo_log_warning("failed to format filename for package id of processor %" PRIu32, processor);
return 0; return 0;
} }
uint32_t package_id; uint32_t package_id;
if (cpuinfo_linux_parse_small_file(package_id_filename, PACKAGE_ID_FILESIZE, uint32_parser, &package_id)) { if (cpuinfo_linux_parse_small_file(package_id_filename, PACKAGE_ID_FILESIZE, uint32_parser, &package_id)) {
cpuinfo_log_debug("parsed package id value of %"PRIu32" for logical processor %"PRIu32" from %s", cpuinfo_log_debug(
package_id, processor, package_id_filename); "parsed package id value of %" PRIu32 " for logical processor %" PRIu32 " from %s",
package_id,
processor,
package_id_filename);
*package_id_ptr = package_id; *package_id_ptr = package_id;
return true; return true;
} else { } else {
cpuinfo_log_info("failed to parse package id for processor %"PRIu32" from %s", cpuinfo_log_info(
processor, package_id_filename); "failed to parse package id for processor %" PRIu32 " from %s", processor, package_id_filename);
return false; return false;
} }
} }
static bool max_processor_number_parser(uint32_t processor_list_start, uint32_t processor_list_end, void* context) { static bool max_processor_number_parser(uint32_t processor_list_start, uint32_t processor_list_end, void* context) {
uint32_t* processor_number_ptr = (uint32_t*) context; uint32_t* processor_number_ptr = (uint32_t*)context;
const uint32_t processor_list_last = processor_list_end - 1; const uint32_t processor_list_last = processor_list_end - 1;
if (*processor_number_ptr < processor_list_last) { if (*processor_number_ptr < processor_list_last) {
*processor_number_ptr = processor_list_last; *processor_number_ptr = processor_list_last;
@ -254,18 +291,21 @@ static bool max_processor_number_parser(uint32_t processor_list_start, uint32_t
uint32_t cpuinfo_linux_get_max_possible_processor(uint32_t max_processors_count) { uint32_t cpuinfo_linux_get_max_possible_processor(uint32_t max_processors_count) {
uint32_t max_possible_processor = 0; uint32_t max_possible_processor = 0;
if (!cpuinfo_linux_parse_cpulist(POSSIBLE_CPULIST_FILENAME, max_processor_number_parser, &max_possible_processor)) { if (!cpuinfo_linux_parse_cpulist(
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 POSSIBLE_CPULIST_FILENAME, max_processor_number_parser, &max_possible_processor)) {
cpuinfo_log_error("failed to parse the list of possible processors in %s", POSSIBLE_CPULIST_FILENAME); #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
#else cpuinfo_log_error("failed to parse the list of possible processors in %s", POSSIBLE_CPULIST_FILENAME);
cpuinfo_log_warning("failed to parse the list of possible processors in %s", POSSIBLE_CPULIST_FILENAME); #else
#endif cpuinfo_log_warning("failed to parse the list of possible processors in %s", POSSIBLE_CPULIST_FILENAME);
#endif
return UINT32_MAX; return UINT32_MAX;
} }
if (max_possible_processor >= max_processors_count) { if (max_possible_processor >= max_processors_count) {
cpuinfo_log_warning( cpuinfo_log_warning(
"maximum possible processor number %"PRIu32" exceeds system limit %"PRIu32": truncating to the latter", "maximum possible processor number %" PRIu32 " exceeds system limit %" PRIu32
max_possible_processor, max_processors_count - 1); ": truncating to the latter",
max_possible_processor,
max_processors_count - 1);
max_possible_processor = max_processors_count - 1; max_possible_processor = max_processors_count - 1;
} }
return max_possible_processor; return max_possible_processor;
@ -273,18 +313,21 @@ uint32_t cpuinfo_linux_get_max_possible_processor(uint32_t max_processors_count)
uint32_t cpuinfo_linux_get_max_present_processor(uint32_t max_processors_count) { uint32_t cpuinfo_linux_get_max_present_processor(uint32_t max_processors_count) {
uint32_t max_present_processor = 0; uint32_t max_present_processor = 0;
if (!cpuinfo_linux_parse_cpulist(PRESENT_CPULIST_FILENAME, max_processor_number_parser, &max_present_processor)) { if (!cpuinfo_linux_parse_cpulist(
#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 PRESENT_CPULIST_FILENAME, max_processor_number_parser, &max_present_processor)) {
cpuinfo_log_error("failed to parse the list of present processors in %s", PRESENT_CPULIST_FILENAME); #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
#else cpuinfo_log_error("failed to parse the list of present processors in %s", PRESENT_CPULIST_FILENAME);
cpuinfo_log_warning("failed to parse the list of present processors in %s", PRESENT_CPULIST_FILENAME); #else
#endif cpuinfo_log_warning("failed to parse the list of present processors in %s", PRESENT_CPULIST_FILENAME);
#endif
return UINT32_MAX; return UINT32_MAX;
} }
if (max_present_processor >= max_processors_count) { if (max_present_processor >= max_processors_count) {
cpuinfo_log_warning( cpuinfo_log_warning(
"maximum present processor number %"PRIu32" exceeds system limit %"PRIu32": truncating to the latter", "maximum present processor number %" PRIu32 " exceeds system limit %" PRIu32
max_present_processor, max_processors_count - 1); ": truncating to the latter",
max_present_processor,
max_processors_count - 1);
max_present_processor = max_processors_count - 1; max_present_processor = max_processors_count - 1;
} }
return max_present_processor; return max_present_processor;
@ -298,22 +341,25 @@ struct detect_processors_context {
}; };
static bool detect_processor_parser(uint32_t processor_list_start, uint32_t processor_list_end, void* context) { static bool detect_processor_parser(uint32_t processor_list_start, uint32_t processor_list_end, void* context) {
const uint32_t max_processors_count = ((struct detect_processors_context*) context)->max_processors_count; const uint32_t max_processors_count = ((struct detect_processors_context*)context)->max_processors_count;
const uint32_t* processor0_flags = ((struct detect_processors_context*) context)->processor0_flags; const uint32_t* processor0_flags = ((struct detect_processors_context*)context)->processor0_flags;
const uint32_t processor_struct_size = ((struct detect_processors_context*) context)->processor_struct_size; const uint32_t processor_struct_size = ((struct detect_processors_context*)context)->processor_struct_size;
const uint32_t detected_flag = ((struct detect_processors_context*) context)->detected_flag; const uint32_t detected_flag = ((struct detect_processors_context*)context)->detected_flag;
for (uint32_t processor = processor_list_start; processor < processor_list_end; processor++) { for (uint32_t processor = processor_list_start; processor < processor_list_end; processor++) {
if (processor >= max_processors_count) { if (processor >= max_processors_count) {
break; break;
} }
*((uint32_t*) ((uintptr_t) processor0_flags + processor_struct_size * processor)) |= detected_flag; *((uint32_t*)((uintptr_t)processor0_flags + processor_struct_size * processor)) |= detected_flag;
} }
return true; return true;
} }
bool cpuinfo_linux_detect_possible_processors(uint32_t max_processors_count, bool cpuinfo_linux_detect_possible_processors(
uint32_t* processor0_flags, uint32_t processor_struct_size, uint32_t possible_flag) { uint32_t max_processors_count,
uint32_t* processor0_flags,
uint32_t processor_struct_size,
uint32_t possible_flag) {
struct detect_processors_context context = { struct detect_processors_context context = {
.max_processors_count = max_processors_count, .max_processors_count = max_processors_count,
.processor0_flags = processor0_flags, .processor0_flags = processor0_flags,
@ -328,8 +374,11 @@ bool cpuinfo_linux_detect_possible_processors(uint32_t max_processors_count,
} }
} }
bool cpuinfo_linux_detect_present_processors(uint32_t max_processors_count, bool cpuinfo_linux_detect_present_processors(
uint32_t* processor0_flags, uint32_t processor_struct_size, uint32_t present_flag) { uint32_t max_processors_count,
uint32_t* processor0_flags,
uint32_t processor_struct_size,
uint32_t present_flag) {
struct detect_processors_context context = { struct detect_processors_context context = {
.max_processors_count = max_processors_count, .max_processors_count = max_processors_count,
.processor0_flags = processor0_flags, .processor0_flags = processor0_flags,
@ -353,13 +402,17 @@ struct siblings_context {
}; };
static bool siblings_parser(uint32_t sibling_list_start, uint32_t sibling_list_end, struct siblings_context* context) { static bool siblings_parser(uint32_t sibling_list_start, uint32_t sibling_list_end, struct siblings_context* context) {
const char* group_name = context->group_name; const char* group_name = context->group_name;
const uint32_t max_processors_count = context->max_processors_count; const uint32_t max_processors_count = context->max_processors_count;
const uint32_t processor = context->processor; const uint32_t processor = context->processor;
if (sibling_list_end > max_processors_count) { if (sibling_list_end > max_processors_count) {
cpuinfo_log_warning("ignore %s siblings %"PRIu32"-%"PRIu32" of processor %"PRIu32, cpuinfo_log_warning(
group_name, max_processors_count, sibling_list_end - 1, processor); "ignore %s siblings %" PRIu32 "-%" PRIu32 " of processor %" PRIu32,
group_name,
max_processors_count,
sibling_list_end - 1,
processor);
sibling_list_end = max_processors_count; sibling_list_end = max_processors_count;
} }
@ -372,10 +425,10 @@ bool cpuinfo_linux_detect_core_cpus(
cpuinfo_siblings_callback callback, cpuinfo_siblings_callback callback,
void* context) { void* context) {
char core_cpus_filename[CORE_CPUS_FILENAME_SIZE]; char core_cpus_filename[CORE_CPUS_FILENAME_SIZE];
const int chars_formatted = snprintf( const int chars_formatted =
core_cpus_filename, CORE_CPUS_FILENAME_SIZE, CORE_CPUS_FILENAME_FORMAT, processor); snprintf(core_cpus_filename, CORE_CPUS_FILENAME_SIZE, CORE_CPUS_FILENAME_FORMAT, processor);
if ((unsigned int) chars_formatted >= CORE_CPUS_FILENAME_SIZE) { if ((unsigned int)chars_formatted >= CORE_CPUS_FILENAME_SIZE) {
cpuinfo_log_warning("failed to format filename for core cpus of processor %"PRIu32, processor); cpuinfo_log_warning("failed to format filename for core cpus of processor %" PRIu32, processor);
return false; return false;
} }
@ -386,12 +439,14 @@ bool cpuinfo_linux_detect_core_cpus(
.callback = callback, .callback = callback,
.callback_context = context, .callback_context = context,
}; };
if (cpuinfo_linux_parse_cpulist(core_cpus_filename, if (cpuinfo_linux_parse_cpulist(
(cpuinfo_cpulist_callback) siblings_parser, &siblings_context)) { core_cpus_filename, (cpuinfo_cpulist_callback)siblings_parser, &siblings_context)) {
return true; return true;
} else { } else {
cpuinfo_log_info("failed to parse the list of core cpus for processor %"PRIu32" from %s", cpuinfo_log_info(
processor, core_cpus_filename); "failed to parse the list of core cpus for processor %" PRIu32 " from %s",
processor,
core_cpus_filename);
return false; return false;
} }
} }
@ -402,10 +457,10 @@ bool cpuinfo_linux_detect_core_siblings(
cpuinfo_siblings_callback callback, cpuinfo_siblings_callback callback,
void* context) { void* context) {
char core_siblings_filename[CORE_SIBLINGS_FILENAME_SIZE]; char core_siblings_filename[CORE_SIBLINGS_FILENAME_SIZE];
const int chars_formatted = snprintf( const int chars_formatted =
core_siblings_filename, CORE_SIBLINGS_FILENAME_SIZE, CORE_SIBLINGS_FILENAME_FORMAT, processor); snprintf(core_siblings_filename, CORE_SIBLINGS_FILENAME_SIZE, CORE_SIBLINGS_FILENAME_FORMAT, processor);
if ((unsigned int) chars_formatted >= CORE_SIBLINGS_FILENAME_SIZE) { if ((unsigned int)chars_formatted >= CORE_SIBLINGS_FILENAME_SIZE) {
cpuinfo_log_warning("failed to format filename for core siblings of processor %"PRIu32, processor); cpuinfo_log_warning("failed to format filename for core siblings of processor %" PRIu32, processor);
return false; return false;
} }
@ -416,12 +471,14 @@ bool cpuinfo_linux_detect_core_siblings(
.callback = callback, .callback = callback,
.callback_context = context, .callback_context = context,
}; };
if (cpuinfo_linux_parse_cpulist(core_siblings_filename, if (cpuinfo_linux_parse_cpulist(
(cpuinfo_cpulist_callback) siblings_parser, &siblings_context)) { core_siblings_filename, (cpuinfo_cpulist_callback)siblings_parser, &siblings_context)) {
return true; return true;
} else { } else {
cpuinfo_log_info("failed to parse the list of core siblings for processor %"PRIu32" from %s", cpuinfo_log_info(
processor, core_siblings_filename); "failed to parse the list of core siblings for processor %" PRIu32 " from %s",
processor,
core_siblings_filename);
return false; return false;
} }
} }
@ -434,8 +491,8 @@ bool cpuinfo_linux_detect_thread_siblings(
char thread_siblings_filename[THREAD_SIBLINGS_FILENAME_SIZE]; char thread_siblings_filename[THREAD_SIBLINGS_FILENAME_SIZE];
const int chars_formatted = snprintf( const int chars_formatted = snprintf(
thread_siblings_filename, THREAD_SIBLINGS_FILENAME_SIZE, THREAD_SIBLINGS_FILENAME_FORMAT, processor); thread_siblings_filename, THREAD_SIBLINGS_FILENAME_SIZE, THREAD_SIBLINGS_FILENAME_FORMAT, processor);
if ((unsigned int) chars_formatted >= THREAD_SIBLINGS_FILENAME_SIZE) { if ((unsigned int)chars_formatted >= THREAD_SIBLINGS_FILENAME_SIZE) {
cpuinfo_log_warning("failed to format filename for thread siblings of processor %"PRIu32, processor); cpuinfo_log_warning("failed to format filename for thread siblings of processor %" PRIu32, processor);
return false; return false;
} }
@ -446,12 +503,14 @@ bool cpuinfo_linux_detect_thread_siblings(
.callback = callback, .callback = callback,
.callback_context = context, .callback_context = context,
}; };
if (cpuinfo_linux_parse_cpulist(thread_siblings_filename, if (cpuinfo_linux_parse_cpulist(
(cpuinfo_cpulist_callback) siblings_parser, &siblings_context)) { thread_siblings_filename, (cpuinfo_cpulist_callback)siblings_parser, &siblings_context)) {
return true; return true;
} else { } else {
cpuinfo_log_info("failed to parse the list of thread siblings for processor %"PRIu32" from %s", cpuinfo_log_info(
processor, thread_siblings_filename); "failed to parse the list of thread siblings for processor %" PRIu32 " from %s",
processor,
thread_siblings_filename);
return false; return false;
} }
} }
@ -462,10 +521,10 @@ bool cpuinfo_linux_detect_cluster_cpus(
cpuinfo_siblings_callback callback, cpuinfo_siblings_callback callback,
void* context) { void* context) {
char cluster_cpus_filename[CLUSTER_CPUS_FILENAME_SIZE]; char cluster_cpus_filename[CLUSTER_CPUS_FILENAME_SIZE];
const int chars_formatted = snprintf( const int chars_formatted =
cluster_cpus_filename, CLUSTER_CPUS_FILENAME_SIZE, CLUSTER_CPUS_FILENAME_FORMAT, processor); snprintf(cluster_cpus_filename, CLUSTER_CPUS_FILENAME_SIZE, CLUSTER_CPUS_FILENAME_FORMAT, processor);
if ((unsigned int) chars_formatted >= CLUSTER_CPUS_FILENAME_SIZE) { if ((unsigned int)chars_formatted >= CLUSTER_CPUS_FILENAME_SIZE) {
cpuinfo_log_warning("failed to format filename for cluster cpus of processor %"PRIu32, processor); cpuinfo_log_warning("failed to format filename for cluster cpus of processor %" PRIu32, processor);
return false; return false;
} }
@ -476,12 +535,14 @@ bool cpuinfo_linux_detect_cluster_cpus(
.callback = callback, .callback = callback,
.callback_context = context, .callback_context = context,
}; };
if (cpuinfo_linux_parse_cpulist(cluster_cpus_filename, if (cpuinfo_linux_parse_cpulist(
(cpuinfo_cpulist_callback) siblings_parser, &siblings_context)) { cluster_cpus_filename, (cpuinfo_cpulist_callback)siblings_parser, &siblings_context)) {
return true; return true;
} else { } else {
cpuinfo_log_info("failed to parse the list of cluster cpus for processor %"PRIu32" from %s", cpuinfo_log_info(
processor, cluster_cpus_filename); "failed to parse the list of cluster cpus for processor %" PRIu32 " from %s",
processor,
cluster_cpus_filename);
return false; return false;
} }
} }
@ -492,10 +553,10 @@ bool cpuinfo_linux_detect_package_cpus(
cpuinfo_siblings_callback callback, cpuinfo_siblings_callback callback,
void* context) { void* context) {
char package_cpus_filename[PACKAGE_CPUS_FILENAME_SIZE]; char package_cpus_filename[PACKAGE_CPUS_FILENAME_SIZE];
const int chars_formatted = snprintf( const int chars_formatted =
package_cpus_filename, PACKAGE_CPUS_FILENAME_SIZE, PACKAGE_CPUS_FILENAME_FORMAT, processor); snprintf(package_cpus_filename, PACKAGE_CPUS_FILENAME_SIZE, PACKAGE_CPUS_FILENAME_FORMAT, processor);
if ((unsigned int) chars_formatted >= PACKAGE_CPUS_FILENAME_SIZE) { if ((unsigned int)chars_formatted >= PACKAGE_CPUS_FILENAME_SIZE) {
cpuinfo_log_warning("failed to format filename for package cpus of processor %"PRIu32, processor); cpuinfo_log_warning("failed to format filename for package cpus of processor %" PRIu32, processor);
return false; return false;
} }
@ -506,12 +567,14 @@ bool cpuinfo_linux_detect_package_cpus(
.callback = callback, .callback = callback,
.callback_context = context, .callback_context = context,
}; };
if (cpuinfo_linux_parse_cpulist(package_cpus_filename, if (cpuinfo_linux_parse_cpulist(
(cpuinfo_cpulist_callback) siblings_parser, &siblings_context)) { package_cpus_filename, (cpuinfo_cpulist_callback)siblings_parser, &siblings_context)) {
return true; return true;
} else { } else {
cpuinfo_log_info("failed to parse the list of package cpus for processor %"PRIu32" from %s", cpuinfo_log_info(
processor, package_cpus_filename); "failed to parse the list of package cpus for processor %" PRIu32 " from %s",
processor,
package_cpus_filename);
return false; return false;
} }
} }

View File

@ -1,30 +1,33 @@
#include <alloca.h>
#include <errno.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <alloca.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#if CPUINFO_MOCK #if CPUINFO_MOCK
#include <cpuinfo-mock.h> #include <cpuinfo-mock.h>
#endif #endif
#include <linux/api.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <linux/api.h>
bool cpuinfo_linux_parse_small_file(
bool cpuinfo_linux_parse_small_file(const char* filename, size_t buffer_size, cpuinfo_smallfile_callback callback, void* context) { const char* filename,
size_t buffer_size,
cpuinfo_smallfile_callback callback,
void* context) {
int file = -1; int file = -1;
bool status = false; bool status = false;
char* buffer = (char*) alloca(buffer_size); char* buffer = (char*)alloca(buffer_size);
#if CPUINFO_LOG_DEBUG_PARSERS #if CPUINFO_LOG_DEBUG_PARSERS
cpuinfo_log_debug("parsing small file %s", filename); cpuinfo_log_debug("parsing small file %s", filename);
#endif #endif
#if CPUINFO_MOCK #if CPUINFO_MOCK
file = cpuinfo_mock_open(filename, O_RDONLY); file = cpuinfo_mock_open(filename, O_RDONLY);
@ -45,17 +48,22 @@ bool cpuinfo_linux_parse_small_file(const char* filename, size_t buffer_size, cp
bytes_read = read(file, &buffer[buffer_position], buffer_size - buffer_position); bytes_read = read(file, &buffer[buffer_position], buffer_size - buffer_position);
#endif #endif
if (bytes_read < 0) { if (bytes_read < 0) {
cpuinfo_log_info("failed to read file %s at position %zu: %s", filename, buffer_position, strerror(errno)); cpuinfo_log_info(
"failed to read file %s at position %zu: %s",
filename,
buffer_position,
strerror(errno));
goto cleanup; goto cleanup;
} }
buffer_position += (size_t) bytes_read; buffer_position += (size_t)bytes_read;
if (buffer_position >= buffer_size) { if (buffer_position >= buffer_size) {
cpuinfo_log_error("failed to read file %s: insufficient buffer of size %zu", filename, buffer_size); cpuinfo_log_error(
"failed to read file %s: insufficient buffer of size %zu", filename, buffer_size);
goto cleanup; goto cleanup;
} }
} while (bytes_read != 0); } while (bytes_read != 0);
status = callback(buffer, &buffer[buffer_position], context); status = callback(filename, buffer, &buffer[buffer_position], context);
cleanup: cleanup:
if (file != -1) { if (file != -1) {

View File

@ -1,192 +1,203 @@
#include <assert.h> #include <assert.h>
#include <stdarg.h> #include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#else #else
#include <unistd.h> #include <unistd.h>
#endif #endif
#if defined(__ANDROID__) #if defined(__ANDROID__)
#include <android/log.h> #include <android/log.h>
#endif #endif
#if defined(__hexagon__) #if defined(__hexagon__)
#include <qurt_printf.h> #include <qurt_printf.h>
#endif #endif
#ifndef CPUINFO_LOG_TO_STDIO #ifndef CPUINFO_LOG_TO_STDIO
#if defined(__ANDROID__) #if defined(__ANDROID__)
#define CPUINFO_LOG_TO_STDIO 0 #define CPUINFO_LOG_TO_STDIO 0
#else #else
#define CPUINFO_LOG_TO_STDIO 1 #define CPUINFO_LOG_TO_STDIO 1
#endif #endif
#endif #endif
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
/* Messages up to this size are formatted entirely on-stack, and don't allocate
/* Messages up to this size are formatted entirely on-stack, and don't allocate heap memory */ * heap memory */
#define CPUINFO_LOG_STACK_BUFFER_SIZE 1024 #define CPUINFO_LOG_STACK_BUFFER_SIZE 1024
#ifdef _WIN32 #ifdef _WIN32
#define CPUINFO_LOG_NEWLINE_LENGTH 2 #define CPUINFO_LOG_NEWLINE_LENGTH 2
#define CPUINFO_LOG_STDERR STD_ERROR_HANDLE #define CPUINFO_LOG_STDERR STD_ERROR_HANDLE
#define CPUINFO_LOG_STDOUT STD_OUTPUT_HANDLE #define CPUINFO_LOG_STDOUT STD_OUTPUT_HANDLE
#elif defined(__hexagon__) #elif defined(__hexagon__)
#define CPUINFO_LOG_NEWLINE_LENGTH 1 #define CPUINFO_LOG_NEWLINE_LENGTH 1
#define CPUINFO_LOG_STDERR 0 #define CPUINFO_LOG_STDERR 0
#define CPUINFO_LOG_STDOUT 0 #define CPUINFO_LOG_STDOUT 0
#else #else
#define CPUINFO_LOG_NEWLINE_LENGTH 1 #define CPUINFO_LOG_NEWLINE_LENGTH 1
#define CPUINFO_LOG_STDERR STDERR_FILENO #define CPUINFO_LOG_STDERR STDERR_FILENO
#define CPUINFO_LOG_STDOUT STDOUT_FILENO #define CPUINFO_LOG_STDOUT STDOUT_FILENO
#endif #endif
#if CPUINFO_LOG_TO_STDIO #if CPUINFO_LOG_TO_STDIO
static void cpuinfo_vlog(int output_handle, const char* prefix, size_t prefix_length, const char* format, va_list args) { static void cpuinfo_vlog(
char stack_buffer[CPUINFO_LOG_STACK_BUFFER_SIZE]; int output_handle,
char* heap_buffer = NULL; const char* prefix,
char* out_buffer = &stack_buffer[0]; size_t prefix_length,
const char* format,
va_list args) {
char stack_buffer[CPUINFO_LOG_STACK_BUFFER_SIZE];
char* heap_buffer = NULL;
char* out_buffer = &stack_buffer[0];
/* The first call to vsnprintf will clobber args, thus need a copy in case a second vsnprintf call is needed */ /* The first call to vsnprintf will clobber args, thus need a copy in
va_list args_copy; * case a second vsnprintf call is needed */
va_copy(args_copy, args); va_list args_copy;
va_copy(args_copy, args);
memcpy(stack_buffer, prefix, prefix_length * sizeof(char)); memcpy(stack_buffer, prefix, prefix_length * sizeof(char));
assert((prefix_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char) <= CPUINFO_LOG_STACK_BUFFER_SIZE); assert((prefix_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char) <= CPUINFO_LOG_STACK_BUFFER_SIZE);
const int format_chars = vsnprintf( const int format_chars = vsnprintf(
&stack_buffer[prefix_length], &stack_buffer[prefix_length],
CPUINFO_LOG_STACK_BUFFER_SIZE - (prefix_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char), CPUINFO_LOG_STACK_BUFFER_SIZE - (prefix_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char),
format, format,
args); args);
if (format_chars < 0) { if (format_chars < 0) {
/* Format error in the message: silently ignore this particular message. */ /* Format error in the message: silently ignore this particular
goto cleanup; * message. */
} goto cleanup;
const size_t format_length = (size_t) format_chars; }
if ((prefix_length + format_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char) > CPUINFO_LOG_STACK_BUFFER_SIZE) { const size_t format_length = (size_t)format_chars;
/* Allocate a buffer on heap, and vsnprintf to this buffer */ if ((prefix_length + format_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char) >
const size_t heap_buffer_size = (prefix_length + format_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char); CPUINFO_LOG_STACK_BUFFER_SIZE) {
#if _WIN32 /* Allocate a buffer on heap, and vsnprintf to this buffer */
heap_buffer = HeapAlloc(GetProcessHeap(), 0, heap_buffer_size); const size_t heap_buffer_size =
#else (prefix_length + format_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char);
heap_buffer = malloc(heap_buffer_size); #if _WIN32
#endif heap_buffer = HeapAlloc(GetProcessHeap(), 0, heap_buffer_size);
if (heap_buffer == NULL) { #else
goto cleanup; heap_buffer = malloc(heap_buffer_size);
} #endif
if (heap_buffer == NULL) {
goto cleanup;
}
/* Copy pre-formatted prefix into the on-heap buffer */ /* Copy pre-formatted prefix into the on-heap buffer */
memcpy(heap_buffer, prefix, prefix_length * sizeof(char)); memcpy(heap_buffer, prefix, prefix_length * sizeof(char));
vsnprintf(&heap_buffer[prefix_length], (format_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char), format, args_copy); vsnprintf(
out_buffer = heap_buffer; &heap_buffer[prefix_length],
} (format_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char),
#ifdef _WIN32 format,
out_buffer[prefix_length + format_length] = '\r'; args_copy);
out_buffer[prefix_length + format_length + 1] = '\n'; out_buffer = heap_buffer;
}
#ifdef _WIN32
out_buffer[prefix_length + format_length] = '\r';
out_buffer[prefix_length + format_length + 1] = '\n';
DWORD bytes_written; DWORD bytes_written;
WriteFile( WriteFile(
GetStdHandle((DWORD) output_handle), GetStdHandle((DWORD)output_handle),
out_buffer, (prefix_length + format_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char), out_buffer,
&bytes_written, NULL); (prefix_length + format_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char),
#elif defined(__hexagon__) &bytes_written,
qurt_printf("%s", out_buffer); NULL);
#else #elif defined(__hexagon__)
out_buffer[prefix_length + format_length] = '\n'; qurt_printf("%s", out_buffer);
#else
out_buffer[prefix_length + format_length] = '\n';
ssize_t bytes_written = write(output_handle, out_buffer, (prefix_length + format_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char)); ssize_t bytes_written = write(
(void) bytes_written; output_handle, out_buffer, (prefix_length + format_length + CPUINFO_LOG_NEWLINE_LENGTH) * sizeof(char));
#endif (void)bytes_written;
#endif
cleanup: cleanup:
#ifdef _WIN32 #ifdef _WIN32
HeapFree(GetProcessHeap(), 0, heap_buffer); HeapFree(GetProcessHeap(), 0, heap_buffer);
#else #else
free(heap_buffer); free(heap_buffer);
#endif #endif
va_end(args_copy); va_end(args_copy);
} }
#elif defined(__ANDROID__) && CPUINFO_LOG_LEVEL > CPUINFO_LOG_NONE #elif defined(__ANDROID__) && CPUINFO_LOG_LEVEL > CPUINFO_LOG_NONE
static const char cpuinfo_module[] = "XNNPACK"; static const char cpuinfo_module[] = "XNNPACK";
#endif #endif
#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_DEBUG #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_DEBUG
void cpuinfo_vlog_debug(const char* format, va_list args) { void cpuinfo_vlog_debug(const char* format, va_list args) {
#if CPUINFO_LOG_TO_STDIO #if CPUINFO_LOG_TO_STDIO
static const char debug_prefix[17] = { static const char debug_prefix[17] = {
'D', 'e', 'b', 'u', 'g', ' ', '(', 'c', 'p', 'u', 'i', 'n', 'f', 'o', ')', ':', ' ' 'D', 'e', 'b', 'u', 'g', ' ', '(', 'c', 'p', 'u', 'i', 'n', 'f', 'o', ')', ':', ' '};
}; cpuinfo_vlog(CPUINFO_LOG_STDOUT, debug_prefix, 17, format, args);
cpuinfo_vlog(CPUINFO_LOG_STDOUT, debug_prefix, 17, format, args); #elif defined(__ANDROID__)
#elif defined(__ANDROID__) __android_log_vprint(ANDROID_LOG_DEBUG, cpuinfo_module, format, args);
__android_log_vprint(ANDROID_LOG_DEBUG, cpuinfo_module, format, args); #else
#else #error "Platform-specific implementation required"
#error "Platform-specific implementation required" #endif
#endif }
}
#endif #endif
#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_INFO #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_INFO
void cpuinfo_vlog_info(const char* format, va_list args) { void cpuinfo_vlog_info(const char* format, va_list args) {
#if CPUINFO_LOG_TO_STDIO #if CPUINFO_LOG_TO_STDIO
static const char info_prefix[16] = { static const char info_prefix[16] = {
'N', 'o', 't', 'e', ' ', '(', 'c', 'p', 'u', 'i', 'n', 'f', 'o', ')', ':', ' ' 'N', 'o', 't', 'e', ' ', '(', 'c', 'p', 'u', 'i', 'n', 'f', 'o', ')', ':', ' '};
}; cpuinfo_vlog(CPUINFO_LOG_STDOUT, info_prefix, 16, format, args);
cpuinfo_vlog(CPUINFO_LOG_STDOUT, info_prefix, 16, format, args); #elif defined(__ANDROID__)
#elif defined(__ANDROID__) __android_log_vprint(ANDROID_LOG_INFO, cpuinfo_module, format, args);
__android_log_vprint(ANDROID_LOG_INFO, cpuinfo_module, format, args); #else
#else #error "Platform-specific implementation required"
#error "Platform-specific implementation required" #endif
#endif }
}
#endif #endif
#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_WARNING #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_WARNING
void cpuinfo_vlog_warning(const char* format, va_list args) { void cpuinfo_vlog_warning(const char* format, va_list args) {
#if CPUINFO_LOG_TO_STDIO #if CPUINFO_LOG_TO_STDIO
static const char warning_prefix[20] = { static const char warning_prefix[20] = {'W', 'a', 'r', 'n', 'i', 'n', 'g', ' ', 'i', 'n',
'W', 'a', 'r', 'n', 'i', 'n', 'g', ' ', 'i', 'n', ' ', 'c', 'p', 'u', 'i', 'n', 'f', 'o', ':', ' ' ' ', 'c', 'p', 'u', 'i', 'n', 'f', 'o', ':', ' '};
}; cpuinfo_vlog(CPUINFO_LOG_STDERR, warning_prefix, 20, format, args);
cpuinfo_vlog(CPUINFO_LOG_STDERR, warning_prefix, 20, format, args); #elif defined(__ANDROID__)
#elif defined(__ANDROID__) __android_log_vprint(ANDROID_LOG_WARN, cpuinfo_module, format, args);
__android_log_vprint(ANDROID_LOG_WARN, cpuinfo_module, format, args); #else
#else #error "Platform-specific implementation required"
#error "Platform-specific implementation required" #endif
#endif }
}
#endif #endif
#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_ERROR #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_ERROR
void cpuinfo_vlog_error(const char* format, va_list args) { void cpuinfo_vlog_error(const char* format, va_list args) {
#if CPUINFO_LOG_TO_STDIO #if CPUINFO_LOG_TO_STDIO
static const char error_prefix[18] = { static const char error_prefix[18] = {
'E', 'r', 'r', 'o', 'r', ' ', 'i', 'n', ' ', 'c', 'p', 'u', 'i', 'n', 'f', 'o', ':', ' ' 'E', 'r', 'r', 'o', 'r', ' ', 'i', 'n', ' ', 'c', 'p', 'u', 'i', 'n', 'f', 'o', ':', ' '};
}; cpuinfo_vlog(CPUINFO_LOG_STDERR, error_prefix, 18, format, args);
cpuinfo_vlog(CPUINFO_LOG_STDERR, error_prefix, 18, format, args); #elif defined(__ANDROID__)
#elif defined(__ANDROID__) __android_log_vprint(ANDROID_LOG_ERROR, cpuinfo_module, format, args);
__android_log_vprint(ANDROID_LOG_ERROR, cpuinfo_module, format, args); #else
#else #error "Platform-specific implementation required"
#error "Platform-specific implementation required" #endif
#endif }
}
#endif #endif
#if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_FATAL #if CPUINFO_LOG_LEVEL >= CPUINFO_LOG_FATAL
void cpuinfo_vlog_fatal(const char* format, va_list args) { void cpuinfo_vlog_fatal(const char* format, va_list args) {
#if CPUINFO_LOG_TO_STDIO #if CPUINFO_LOG_TO_STDIO
static const char fatal_prefix[24] = { static const char fatal_prefix[24] = {'F', 'a', 't', 'a', 'l', ' ', 'e', 'r', 'r', 'o', 'r', ' ',
'F', 'a', 't', 'a', 'l', ' ', 'e', 'r', 'r', 'o', 'r', ' ', 'i', 'n', ' ', 'c', 'p', 'u', 'i', 'n', 'f', 'o', ':', ' ' 'i', 'n', ' ', 'c', 'p', 'u', 'i', 'n', 'f', 'o', ':', ' '};
}; cpuinfo_vlog(CPUINFO_LOG_STDERR, fatal_prefix, 24, format, args);
cpuinfo_vlog(CPUINFO_LOG_STDERR, fatal_prefix, 24, format, args); #elif defined(__ANDROID__)
#elif defined(__ANDROID__) __android_log_vprint(ANDROID_LOG_FATAL, cpuinfo_module, format, args);
__android_log_vprint(ANDROID_LOG_FATAL, cpuinfo_module, format, args); #else
#else #error "Platform-specific implementation required"
#error "Platform-specific implementation required" #endif
#endif }
}
#endif #endif

View File

@ -4,7 +4,6 @@
#define CPUINFO_MACH_MAX_CACHE_LEVELS 8 #define CPUINFO_MACH_MAX_CACHE_LEVELS 8
struct cpuinfo_mach_topology { struct cpuinfo_mach_topology {
uint32_t packages; uint32_t packages;
uint32_t cores; uint32_t cores;
@ -12,5 +11,4 @@ struct cpuinfo_mach_topology {
uint32_t threads_per_cache[CPUINFO_MACH_MAX_CACHE_LEVELS]; uint32_t threads_per_cache[CPUINFO_MACH_MAX_CACHE_LEVELS];
}; };
struct cpuinfo_mach_topology cpuinfo_mach_detect_topology(void); struct cpuinfo_mach_topology cpuinfo_mach_detect_topology(void);

View File

@ -1,16 +1,15 @@
#include <string.h>
#include <alloca.h> #include <alloca.h>
#include <errno.h> #include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/sysctl.h> #include <sys/sysctl.h>
#include <sys/types.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <mach/api.h> #include <mach/api.h>
#include <TargetConditionals.h> #include <TargetConditionals.h>
struct cpuinfo_mach_topology cpuinfo_mach_detect_topology(void) { struct cpuinfo_mach_topology cpuinfo_mach_detect_topology(void) {
int cores = 1; int cores = 1;
size_t sizeof_cores = sizeof(cores); size_t sizeof_cores = sizeof(cores);
@ -41,12 +40,9 @@ struct cpuinfo_mach_topology cpuinfo_mach_detect_topology(void) {
} }
#endif #endif
cpuinfo_log_debug("mach topology: packages = %d, cores = %d, threads = %d", packages, (int) cores, (int) threads); cpuinfo_log_debug("mach topology: packages = %d, cores = %d, threads = %d", packages, (int)cores, (int)threads);
struct cpuinfo_mach_topology topology = { struct cpuinfo_mach_topology topology = {
.packages = (uint32_t) packages, .packages = (uint32_t)packages, .cores = (uint32_t)cores, .threads = (uint32_t)threads};
.cores = (uint32_t) cores,
.threads = (uint32_t) threads
};
#if !TARGET_OS_IPHONE #if !TARGET_OS_IPHONE
size_t cacheconfig_size = 0; size_t cacheconfig_size = 0;
@ -63,7 +59,7 @@ struct cpuinfo_mach_topology cpuinfo_mach_detect_topology(void) {
cache_configs = CPUINFO_MACH_MAX_CACHE_LEVELS; cache_configs = CPUINFO_MACH_MAX_CACHE_LEVELS;
} }
for (size_t i = 0; i < cache_configs; i++) { for (size_t i = 0; i < cache_configs; i++) {
cpuinfo_log_debug("mach hw.cacheconfig[%zu]: %"PRIu64, i, cacheconfig[i]); cpuinfo_log_debug("mach hw.cacheconfig[%zu]: %" PRIu64, i, cacheconfig[i]);
topology.threads_per_cache[i] = cacheconfig[i]; topology.threads_per_cache[i] = cacheconfig[i];
} }
} }

View File

@ -8,7 +8,7 @@
/* RISC-V Vendor IDs. */ /* RISC-V Vendor IDs. */
enum cpuinfo_riscv_chipset_vendor { enum cpuinfo_riscv_chipset_vendor {
cpuinfo_riscv_chipset_vendor_unknown = 0, cpuinfo_riscv_chipset_vendor_unknown = 0,
cpuinfo_riscv_chipset_sifive = 0x489, cpuinfo_riscv_chipset_vendor_sifive = 0x489,
cpuinfo_riscv_chipset_vendor_max, cpuinfo_riscv_chipset_vendor_max,
}; };
@ -35,8 +35,8 @@ enum cpuinfo_riscv_chipset_impl {
* @param[uarch] - Reference to the cpuinfo_uarch to populate. * @param[uarch] - Reference to the cpuinfo_uarch to populate.
*/ */
CPUINFO_INTERNAL void cpuinfo_riscv_decode_vendor_uarch( CPUINFO_INTERNAL void cpuinfo_riscv_decode_vendor_uarch(
uint32_t vendor_id, uint32_t vendor_id,
uint32_t arch_id, uint32_t arch_id,
uint32_t imp_id, uint32_t imp_id,
enum cpuinfo_vendor vendor[restrict static 1], enum cpuinfo_vendor vendor[restrict static 1],
enum cpuinfo_uarch uarch[restrict static 1]); enum cpuinfo_uarch uarch[restrict static 1]);

View File

@ -22,23 +22,26 @@ struct cpuinfo_riscv_linux_processor {
uint32_t flags; uint32_t flags;
/** /**
* Minimum processor ID on the cluster which includes this logical processor. * Minimum processor ID on the cluster which includes this logical
* This value can serve as an ID for the cluster of logical processors: it is the * processor. This value can serve as an ID for the cluster of logical
* same for all logical processors on the same package. * processors: it is the same for all logical processors on the same
* package.
*/ */
uint32_t cluster_leader_id; uint32_t cluster_leader_id;
/** /**
* Minimum processor ID on the core which includes this logical processor. * Minimum processor ID on the core which includes this logical
* This value can serve as an ID for the core of logical processors: it * processor. This value can serve as an ID for the core of logical
* is the same for all logical processors on the same core. * processors: it is the same for all logical processors on the same
* core.
*/ */
uint32_t core_leader_id; uint32_t core_leader_id;
/** /**
* Minimum processor ID on the package which includes this logical processor. * Minimum processor ID on the package which includes this logical
* This value can serve as an ID for the package of logical processors: it * processor. This value can serve as an ID for the package of logical
* is the same for all logical processors on the same package. * processors: it is the same for all logical processors on the same
* package.
*/ */
uint32_t package_leader_id; uint32_t package_leader_id;
}; };
@ -49,8 +52,7 @@ struct cpuinfo_riscv_linux_processor {
* *
* @param[isa] - Reference to cpuinfo_riscv_isa structure to populate. * @param[isa] - Reference to cpuinfo_riscv_isa structure to populate.
*/ */
CPUINFO_INTERNAL void cpuinfo_riscv_linux_decode_isa_from_hwcap( CPUINFO_INTERNAL void cpuinfo_riscv_linux_decode_isa_from_hwcap(struct cpuinfo_riscv_isa isa[restrict static 1]);
struct cpuinfo_riscv_isa isa[restrict static 1]);
/** /**
* Reads `sys_riscv_hwprobe` and determines the processor vendor and * Reads `sys_riscv_hwprobe` and determines the processor vendor and

View File

@ -10,7 +10,7 @@ struct cpuinfo_riscv_isa cpuinfo_isa;
/* Helper function to bitmask flags and ensure operator precedence. */ /* Helper function to bitmask flags and ensure operator precedence. */
static inline bool bitmask_all(uint32_t flags, uint32_t mask) { static inline bool bitmask_all(uint32_t flags, uint32_t mask) {
return (flags & mask) == mask; return (flags & mask) == mask;
} }
static int compare_riscv_linux_processors(const void* a, const void* b) { static int compare_riscv_linux_processors(const void* a, const void* b) {
@ -18,8 +18,8 @@ static int compare_riscv_linux_processors(const void* a, const void* b) {
* For our purposes, it is only relevant that the list is sorted by * For our purposes, it is only relevant that the list is sorted by
* micro-architecture, so the nature of ordering is irrelevant. * micro-architecture, so the nature of ordering is irrelevant.
*/ */
return ((const struct cpuinfo_riscv_linux_processor*)a)->core.uarch return ((const struct cpuinfo_riscv_linux_processor*)a)->core.uarch -
- ((const struct cpuinfo_riscv_linux_processor*)b)->core.uarch; ((const struct cpuinfo_riscv_linux_processor*)b)->core.uarch;
} }
/** /**
@ -37,10 +37,11 @@ static int compare_riscv_linux_processors(const void* a, const void* b) {
* E.g. processors[0].core_leader_id = 0. * E.g. processors[0].core_leader_id = 0.
*/ */
static bool core_cpus_parser(uint32_t processor, static bool core_cpus_parser(
uint32_t core_cpus_start, uint32_t processor,
uint32_t core_cpus_end, uint32_t core_cpus_start,
struct cpuinfo_riscv_linux_processor* processors) { uint32_t core_cpus_end,
struct cpuinfo_riscv_linux_processor* processors) {
uint32_t processor_start = UINT32_MAX; uint32_t processor_start = UINT32_MAX;
uint32_t processor_count = 0; uint32_t processor_count = 0;
@ -70,8 +71,8 @@ static bool core_cpus_parser(uint32_t processor,
* *
* e.g. core_cpu_list=1,10-12 * e.g. core_cpu_list=1,10-12
*/ */
if (!bitmask_all(processors[processor].flags, CPUINFO_LINUX_FLAG_CORE_CLUSTER) if (!bitmask_all(processors[processor].flags, CPUINFO_LINUX_FLAG_CORE_CLUSTER) ||
|| processors[processor].core.processor_start > processor_start) { processors[processor].core.processor_start > processor_start) {
processors[processor].core.processor_start = processor_start; processors[processor].core.processor_start = processor_start;
processors[processor].core_leader_id = processor_start; processors[processor].core_leader_id = processor_start;
} }
@ -92,10 +93,11 @@ static bool core_cpus_parser(uint32_t processor,
* their 'cluster_leader_id' to their index in the list. * their 'cluster_leader_id' to their index in the list.
* E.g. processors[0].cluster_leader_id = 0. * E.g. processors[0].cluster_leader_id = 0.
*/ */
static bool cluster_cpus_parser(uint32_t processor, static bool cluster_cpus_parser(
uint32_t cluster_cpus_start, uint32_t processor,
uint32_t cluster_cpus_end, uint32_t cluster_cpus_start,
struct cpuinfo_riscv_linux_processor* processors) { uint32_t cluster_cpus_end,
struct cpuinfo_riscv_linux_processor* processors) {
uint32_t processor_start = UINT32_MAX; uint32_t processor_start = UINT32_MAX;
uint32_t processor_count = 0; uint32_t processor_count = 0;
uint32_t core_count = 0; uint32_t core_count = 0;
@ -133,8 +135,8 @@ static bool cluster_cpus_parser(uint32_t processor,
* *
* e.g. cluster_cpus_list=1,10-12 * e.g. cluster_cpus_list=1,10-12
*/ */
if (!bitmask_all(processors[processor].flags, CPUINFO_LINUX_FLAG_CLUSTER_CLUSTER) if (!bitmask_all(processors[processor].flags, CPUINFO_LINUX_FLAG_CLUSTER_CLUSTER) ||
|| processors[processor].cluster.processor_start > processor_start) { processors[processor].cluster.processor_start > processor_start) {
processors[processor].cluster.processor_start = processor_start; processors[processor].cluster.processor_start = processor_start;
processors[processor].cluster.core_start = processor_start; processors[processor].cluster.core_start = processor_start;
processors[processor].cluster.cluster_id = processor_start; processors[processor].cluster.cluster_id = processor_start;
@ -160,10 +162,11 @@ static bool cluster_cpus_parser(uint32_t processor,
* their 'package_leader_id' to their index in the list. * their 'package_leader_id' to their index in the list.
* E.g. processors[0].package_leader_id = 0. * E.g. processors[0].package_leader_id = 0.
*/ */
static bool package_cpus_parser(uint32_t processor, static bool package_cpus_parser(
uint32_t package_cpus_start, uint32_t processor,
uint32_t package_cpus_end, uint32_t package_cpus_start,
struct cpuinfo_riscv_linux_processor* processors) { uint32_t package_cpus_end,
struct cpuinfo_riscv_linux_processor* processors) {
uint32_t processor_start = UINT32_MAX; uint32_t processor_start = UINT32_MAX;
uint32_t processor_count = 0; uint32_t processor_count = 0;
uint32_t cluster_count = 0; uint32_t cluster_count = 0;
@ -205,8 +208,8 @@ static bool package_cpus_parser(uint32_t processor,
* *
* e.g. package_cpus_list=1,10-12 * e.g. package_cpus_list=1,10-12
*/ */
if (!bitmask_all(processors[processor].flags, CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER) if (!bitmask_all(processors[processor].flags, CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER) ||
|| processors[processor].package.processor_start > processor_start) { processors[processor].package.processor_start > processor_start) {
processors[processor].package.processor_start = processor_start; processors[processor].package.processor_start = processor_start;
processors[processor].package.cluster_start = processor_start; processors[processor].package.cluster_start = processor_start;
processors[processor].package.core_start = processor_start; processors[processor].package.core_start = processor_start;
@ -233,8 +236,9 @@ void cpuinfo_riscv_linux_init(void) {
/** /**
* The interesting set of processors are the number of 'present' * The interesting set of processors are the number of 'present'
* processors on the system. There may be more 'possible' processors, but * processors on the system. There may be more 'possible' processors,
* processor information cannot be gathered on non-present processors. * but processor information cannot be gathered on non-present
* processors.
* *
* Note: For SoCs, it is largely the case that all processors are known * Note: For SoCs, it is largely the case that all processors are known
* at boot and no processors are hotplugged at runtime, so the * at boot and no processors are hotplugged at runtime, so the
@ -244,9 +248,8 @@ void cpuinfo_riscv_linux_init(void) {
* processors. It is not a count of the number of processors on the * processors. It is not a count of the number of processors on the
* system. * system.
*/ */
const uint32_t max_processor_id = 1 + const uint32_t max_processor_id =
cpuinfo_linux_get_max_present_processor( 1 + cpuinfo_linux_get_max_present_processor(cpuinfo_linux_get_max_processors_count());
cpuinfo_linux_get_max_processors_count());
if (max_processor_id == 0) { if (max_processor_id == 0) {
cpuinfo_log_error("failed to discover any processors"); cpuinfo_log_error("failed to discover any processors");
return; return;
@ -257,35 +260,36 @@ void cpuinfo_riscv_linux_init(void) {
* sized to the max processor ID as opposed to the number of 'present' * sized to the max processor ID as opposed to the number of 'present'
* processors, to leverage pointer math in the common utility functions. * processors, to leverage pointer math in the common utility functions.
*/ */
riscv_linux_processors = calloc(max_processor_id, riscv_linux_processors = calloc(max_processor_id, sizeof(struct cpuinfo_riscv_linux_processor));
sizeof(struct cpuinfo_riscv_linux_processor));
if (riscv_linux_processors == NULL) { if (riscv_linux_processors == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for %"PRIu32" processors.", cpuinfo_log_error(
"failed to allocate %zu bytes for %" PRIu32 " processors.",
max_processor_id * sizeof(struct cpuinfo_riscv_linux_processor), max_processor_id * sizeof(struct cpuinfo_riscv_linux_processor),
max_processor_id); max_processor_id);
goto cleanup; goto cleanup;
} }
/** /**
* Attempt to detect all processors and apply the corresponding flag to * Attempt to detect all processors and apply the corresponding flag to
* each processor struct that we find. * each processor struct that we find.
*/ */
if (!cpuinfo_linux_detect_present_processors(max_processor_id, if (!cpuinfo_linux_detect_present_processors(
&riscv_linux_processors->flags, max_processor_id,
sizeof(struct cpuinfo_riscv_linux_processor), &riscv_linux_processors->flags,
CPUINFO_LINUX_FLAG_PRESENT | CPUINFO_LINUX_FLAG_VALID)) { sizeof(struct cpuinfo_riscv_linux_processor),
CPUINFO_LINUX_FLAG_PRESENT | CPUINFO_LINUX_FLAG_VALID)) {
cpuinfo_log_error("failed to detect present processors"); cpuinfo_log_error("failed to detect present processors");
goto cleanup; goto cleanup;
} }
/* Populate processor information. */ /* Populate processor information. */
for (size_t processor = 0; processor < max_processor_id; processor++) { for (size_t processor = 0; processor < max_processor_id; processor++) {
if (!bitmask_all(riscv_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { if (!bitmask_all(riscv_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) {
continue; continue;
} }
/* TODO: Determine if an 'smt_id' is available. */ /* TODO: Determine if an 'smt_id' is available. */
riscv_linux_processors[processor].processor.linux_id = processor; riscv_linux_processors[processor].processor.linux_id = processor;
} }
/* Populate core information. */ /* Populate core information. */
for (size_t processor = 0; processor < max_processor_id; processor++) { for (size_t processor = 0; processor < max_processor_id; processor++) {
@ -295,18 +299,16 @@ void cpuinfo_riscv_linux_init(void) {
/* Populate processor start and count information. */ /* Populate processor start and count information. */
if (!cpuinfo_linux_detect_core_cpus( if (!cpuinfo_linux_detect_core_cpus(
max_processor_id, max_processor_id,
processor, processor,
(cpuinfo_siblings_callback) core_cpus_parser, (cpuinfo_siblings_callback)core_cpus_parser,
riscv_linux_processors)) { riscv_linux_processors)) {
cpuinfo_log_error("failed to detect core cpus for processor %zu.", processor); cpuinfo_log_error("failed to detect core cpus for processor %zu.", processor);
goto cleanup; goto cleanup;
} }
/* Populate core ID information. */ /* Populate core ID information. */
if (cpuinfo_linux_get_processor_core_id( if (cpuinfo_linux_get_processor_core_id(processor, &riscv_linux_processors[processor].core.core_id)) {
processor,
&riscv_linux_processors[processor].core.core_id)) {
riscv_linux_processors[processor].flags |= CPUINFO_LINUX_FLAG_CORE_ID; riscv_linux_processors[processor].flags |= CPUINFO_LINUX_FLAG_CORE_ID;
} }
@ -316,9 +318,9 @@ void cpuinfo_riscv_linux_init(void) {
* the values from the core leader will be honored. * the values from the core leader will be honored.
*/ */
cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe( cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe(
processor, processor,
&riscv_linux_processors[processor].core.vendor, &riscv_linux_processors[processor].core.vendor,
&riscv_linux_processors[processor].core.uarch); &riscv_linux_processors[processor].core.uarch);
/* Populate frequency information of this core. */ /* Populate frequency information of this core. */
uint32_t frequency = cpuinfo_linux_get_processor_cur_frequency(processor); uint32_t frequency = cpuinfo_linux_get_processor_cur_frequency(processor);
@ -334,25 +336,23 @@ void cpuinfo_riscv_linux_init(void) {
continue; continue;
} }
if (!cpuinfo_linux_detect_cluster_cpus( if (!cpuinfo_linux_detect_cluster_cpus(
max_processor_id, max_processor_id,
processor, processor,
(cpuinfo_siblings_callback) cluster_cpus_parser, (cpuinfo_siblings_callback)cluster_cpus_parser,
riscv_linux_processors)) { riscv_linux_processors)) {
cpuinfo_log_warning("failed to detect cluster cpus for processor %zu.", processor); cpuinfo_log_warning("failed to detect cluster cpus for processor %zu.", processor);
goto cleanup; goto cleanup;
} }
/** /**
* Populate the vendor, uarch and frequency of this cluster from * Populate the vendor, uarch and frequency of this cluster from
* this logical processor. When the 'clusters' list is constructed, * this logical processor. When the 'clusters' list is
* only the values from the cluster leader will be honored. * constructed, only the values from the cluster leader will be
* honored.
*/ */
riscv_linux_processors[processor].cluster.vendor = riscv_linux_processors[processor].cluster.vendor = riscv_linux_processors[processor].core.vendor;
riscv_linux_processors[processor].core.vendor; riscv_linux_processors[processor].cluster.uarch = riscv_linux_processors[processor].core.uarch;
riscv_linux_processors[processor].cluster.uarch = riscv_linux_processors[processor].cluster.frequency = riscv_linux_processors[processor].core.frequency;
riscv_linux_processors[processor].core.uarch;
riscv_linux_processors[processor].cluster.frequency =
riscv_linux_processors[processor].core.frequency;
} }
/* Populate package information. */ /* Populate package information. */
@ -361,10 +361,10 @@ void cpuinfo_riscv_linux_init(void) {
continue; continue;
} }
if (!cpuinfo_linux_detect_package_cpus( if (!cpuinfo_linux_detect_package_cpus(
max_processor_id, max_processor_id,
processor, processor,
(cpuinfo_siblings_callback) package_cpus_parser, (cpuinfo_siblings_callback)package_cpus_parser,
riscv_linux_processors)) { riscv_linux_processors)) {
cpuinfo_log_warning("failed to detect package cpus for processor %zu.", processor); cpuinfo_log_warning("failed to detect package cpus for processor %zu.", processor);
goto cleanup; goto cleanup;
} }
@ -424,45 +424,44 @@ void cpuinfo_riscv_linux_init(void) {
* As we've sorted by micro-architecture, when the uarch differs * As we've sorted by micro-architecture, when the uarch differs
* between two entries, a unique uarch has been observed. * between two entries, a unique uarch has been observed.
*/ */
if (last_uarch != riscv_linux_processors[processor].core.uarch if (last_uarch != riscv_linux_processors[processor].core.uarch || valid_uarchs_count == 0) {
|| valid_uarchs_count == 0) {
valid_uarchs_count++; valid_uarchs_count++;
last_uarch = riscv_linux_processors[processor].core.uarch; last_uarch = riscv_linux_processors[processor].core.uarch;
} }
} }
/* Allocate and populate final public ABI structures. */ /* Allocate and populate final public ABI structures. */
processors = calloc(valid_processors_count, processors = calloc(valid_processors_count, sizeof(struct cpuinfo_processor));
sizeof(struct cpuinfo_processor));
if (processors == NULL) { if (processors == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for %zu processors.", cpuinfo_log_error(
"failed to allocate %zu bytes for %zu processors.",
valid_processors_count * sizeof(struct cpuinfo_processor), valid_processors_count * sizeof(struct cpuinfo_processor),
valid_processors_count); valid_processors_count);
goto cleanup; goto cleanup;
} }
cores = calloc(valid_cores_count, cores = calloc(valid_cores_count, sizeof(struct cpuinfo_core));
sizeof(struct cpuinfo_core));
if (cores == NULL) { if (cores == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for %zu cores.", cpuinfo_log_error(
"failed to allocate %zu bytes for %zu cores.",
valid_cores_count * sizeof(struct cpuinfo_core), valid_cores_count * sizeof(struct cpuinfo_core),
valid_cores_count); valid_cores_count);
goto cleanup; goto cleanup;
} }
clusters = calloc(valid_clusters_count, clusters = calloc(valid_clusters_count, sizeof(struct cpuinfo_cluster));
sizeof(struct cpuinfo_cluster));
if (clusters == NULL) { if (clusters == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for %zu clusters.", cpuinfo_log_error(
"failed to allocate %zu bytes for %zu clusters.",
valid_clusters_count * sizeof(struct cpuinfo_cluster), valid_clusters_count * sizeof(struct cpuinfo_cluster),
valid_clusters_count); valid_clusters_count);
goto cleanup; goto cleanup;
} }
packages = calloc(valid_packages_count, packages = calloc(valid_packages_count, sizeof(struct cpuinfo_package));
sizeof(struct cpuinfo_package));
if (packages == NULL) { if (packages == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for %zu packages.", cpuinfo_log_error(
"failed to allocate %zu bytes for %zu packages.",
valid_packages_count * sizeof(struct cpuinfo_package), valid_packages_count * sizeof(struct cpuinfo_package),
valid_packages_count); valid_packages_count);
goto cleanup; goto cleanup;
@ -470,36 +469,37 @@ void cpuinfo_riscv_linux_init(void) {
uarchs = calloc(valid_uarchs_count, sizeof(struct cpuinfo_uarch_info)); uarchs = calloc(valid_uarchs_count, sizeof(struct cpuinfo_uarch_info));
if (uarchs == NULL) { if (uarchs == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for %zu packages.", cpuinfo_log_error(
"failed to allocate %zu bytes for %zu packages.",
valid_uarchs_count * sizeof(struct cpuinfo_uarch_info), valid_uarchs_count * sizeof(struct cpuinfo_uarch_info),
valid_uarchs_count); valid_uarchs_count);
goto cleanup; goto cleanup;
} }
linux_cpu_to_processor_map = calloc(max_processor_id, linux_cpu_to_processor_map = calloc(max_processor_id, sizeof(struct cpuinfo_processor*));
sizeof(struct cpuinfo_processor*));
if (linux_cpu_to_processor_map == NULL) { if (linux_cpu_to_processor_map == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for %"PRIu32" processor map.", cpuinfo_log_error(
max_processor_id * sizeof(struct cpuinfo_processor*), "failed to allocate %zu bytes for %" PRIu32 " processor map.",
max_processor_id); max_processor_id * sizeof(struct cpuinfo_processor*),
max_processor_id);
goto cleanup; goto cleanup;
} }
linux_cpu_to_core_map = calloc(max_processor_id, linux_cpu_to_core_map = calloc(max_processor_id, sizeof(struct cpuinfo_core*));
sizeof(struct cpuinfo_core*));
if (linux_cpu_to_core_map == NULL) { if (linux_cpu_to_core_map == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for %"PRIu32" core map.", cpuinfo_log_error(
max_processor_id * sizeof(struct cpuinfo_core*), "failed to allocate %zu bytes for %" PRIu32 " core map.",
max_processor_id); max_processor_id * sizeof(struct cpuinfo_core*),
max_processor_id);
goto cleanup; goto cleanup;
} }
linux_cpu_to_uarch_index_map = calloc(max_processor_id, linux_cpu_to_uarch_index_map = calloc(max_processor_id, sizeof(struct cpuinfo_uarch_info*));
sizeof(struct cpuinfo_uarch_info*));
if (linux_cpu_to_uarch_index_map == NULL) { if (linux_cpu_to_uarch_index_map == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for %"PRIu32" uarch map.", cpuinfo_log_error(
max_processor_id * sizeof(struct cpuinfo_uarch_info*), "failed to allocate %zu bytes for %" PRIu32 " uarch map.",
max_processor_id); max_processor_id * sizeof(struct cpuinfo_uarch_info*),
max_processor_id);
goto cleanup; goto cleanup;
} }
@ -524,17 +524,15 @@ void cpuinfo_riscv_linux_init(void) {
uint32_t linux_id = riscv_linux_processors[processor].processor.linux_id; uint32_t linux_id = riscv_linux_processors[processor].processor.linux_id;
/* Create uarch entry if this uarch has not been seen before. */ /* Create uarch entry if this uarch has not been seen before. */
if (last_uarch != riscv_linux_processors[processor].core.uarch if (last_uarch != riscv_linux_processors[processor].core.uarch || valid_uarchs_index == 0) {
|| valid_uarchs_index == 0) { uarchs[valid_uarchs_index++].uarch = riscv_linux_processors[processor].core.uarch;
uarchs[valid_uarchs_index++].uarch =
riscv_linux_processors[processor].core.uarch;
last_uarch = riscv_linux_processors[processor].core.uarch; last_uarch = riscv_linux_processors[processor].core.uarch;
} }
/* Copy cpuinfo_processor information. */ /* Copy cpuinfo_processor information. */
memcpy(&processors[valid_processors_index++], memcpy(&processors[valid_processors_index++],
&riscv_linux_processors[processor].processor, &riscv_linux_processors[processor].processor,
sizeof(struct cpuinfo_processor)); sizeof(struct cpuinfo_processor));
/* Update uarch processor count. */ /* Update uarch processor count. */
uarchs[valid_uarchs_index - 1].processor_count++; uarchs[valid_uarchs_index - 1].processor_count++;
@ -598,7 +596,8 @@ void cpuinfo_riscv_linux_init(void) {
cpuinfo_is_initialized = true; cpuinfo_is_initialized = true;
/* Mark all public structures NULL to prevent cleanup from erasing them. */ /* Mark all public structures NULL to prevent cleanup from erasing them.
*/
processors = NULL; processors = NULL;
cores = NULL; cores = NULL;
clusters = NULL; clusters = NULL;

View File

@ -1,18 +1,88 @@
/*
* Only enable the C standard library hwprobe interface on Android for now.
* Patches to add a compatible hwprobe API to glibc are available but not
* merged at the time of writing and so cannot easily be tested. The
* #ifdef __ANDROID__ check will be removed in the future.
*/
#ifdef __ANDROID__
#ifdef __has_include
#if __has_include(<sys/hwprobe.h>)
#define CPUINFO_RISCV_LINUX_HAVE_C_HWPROBE
#include <sys/hwprobe.h> #include <sys/hwprobe.h>
#endif
#endif
#endif
#include <sched.h> #include <sched.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <riscv/api.h> #include <riscv/api.h>
#include <riscv/linux/api.h> #include <riscv/linux/api.h>
#ifndef CPUINFO_RISCV_LINUX_HAVE_C_HWPROBE
#include <stdint.h>
#include <sys/syscall.h>
#include <unistd.h>
struct riscv_hwprobe {
int64_t key;
uint64_t value;
};
/*
* The standard C library our binary was compiled with does not support
* hwprobe but the kernel on which we are running might do. The
* constants below are copied from
* /usr/include/riscv64-linux-gnu/asm/hwprobe.h. They allow us to
* invoke the hwprobe syscall directly. We duplicate the constants
* rather than including the kernel hwprobe.h header, as this header
* will only be present if we're building Linux 6.4 or greater.
*/
#define RISCV_HWPROBE_KEY_MVENDORID 0
#define RISCV_HWPROBE_KEY_MARCHID 1
#define RISCV_HWPROBE_KEY_MIMPID 2
#define RISCV_HWPROBE_KEY_BASE_BEHAVIOR 3
#define RISCV_HWPROBE_BASE_BEHAVIOR_IMA (1 << 0)
#define RISCV_HWPROBE_KEY_IMA_EXT_0 4
#define RISCV_HWPROBE_IMA_FD (1 << 0)
#define RISCV_HWPROBE_IMA_C (1 << 1)
#define RISCV_HWPROBE_IMA_V (1 << 2)
#define RISCV_HWPROBE_EXT_ZBA (1 << 3)
#define RISCV_HWPROBE_EXT_ZBB (1 << 4)
#define RISCV_HWPROBE_EXT_ZBS (1 << 5)
#define RISCV_HWPROBE_EXT_ZICBOZ (1 << 6)
#define RISCV_HWPROBE_KEY_CPUPERF_0 5
#define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0)
#define RISCV_HWPROBE_MISALIGNED_EMULATED (1 << 0)
#define RISCV_HWPROBE_MISALIGNED_SLOW (2 << 0)
#define RISCV_HWPROBE_MISALIGNED_FAST (3 << 0)
#define RISCV_HWPROBE_MISALIGNED_UNSUPPORTED (4 << 0)
#define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0)
#ifndef NR_riscv_hwprobe
#ifndef NR_arch_specific_syscall
#define NR_arch_specific_syscall 244
#endif
#define NR_riscv_hwprobe (NR_arch_specific_syscall + 14)
#endif
#endif
void cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe( void cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe(
uint32_t processor, uint32_t processor,
enum cpuinfo_vendor vendor[restrict static 1], enum cpuinfo_vendor vendor[restrict static 1],
enum cpuinfo_uarch uarch[restrict static 1]) { enum cpuinfo_uarch uarch[restrict static 1]) {
struct riscv_hwprobe pairs[] = { struct riscv_hwprobe pairs[] = {
{ .key = RISCV_HWPROBE_KEY_MVENDORID, }, {
{ .key = RISCV_HWPROBE_KEY_MARCHID, }, .key = RISCV_HWPROBE_KEY_MVENDORID,
{ .key = RISCV_HWPROBE_KEY_MIMPID, }, },
{
.key = RISCV_HWPROBE_KEY_MARCHID,
},
{
.key = RISCV_HWPROBE_KEY_MIMPID,
},
}; };
const size_t pairs_count = sizeof(pairs) / sizeof(struct riscv_hwprobe); const size_t pairs_count = sizeof(pairs) / sizeof(struct riscv_hwprobe);
@ -21,17 +91,34 @@ void cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe(
*uarch = cpuinfo_uarch_unknown; *uarch = cpuinfo_uarch_unknown;
/* Create a CPU set with this processor flagged. */ /* Create a CPU set with this processor flagged. */
const size_t cpu_set_size = processor + 1; const size_t cpu_count = processor + 1;
cpu_set_t* cpu_set = CPU_ALLOC(cpu_set_size); cpu_set_t* cpu_set = CPU_ALLOC(cpu_count);
CPU_SET(processor, cpu_set); if (cpu_set == NULL) {
cpuinfo_log_warning("failed to allocate space for cpu_set");
return;
}
const size_t cpu_set_size = CPU_ALLOC_SIZE(cpu_count);
CPU_ZERO_S(cpu_set_size, cpu_set);
CPU_SET_S(processor, cpu_set_size, cpu_set);
/* Request all available information from hwprobe. */ /* Request all available information from hwprobe. */
int ret = __riscv_hwprobe(pairs, pairs_count, #ifndef CPUINFO_RISCV_LINUX_HAVE_C_HWPROBE
cpu_set_size, (unsigned long*)cpu_set, /*
0 /* flags */); * No standard library support for hwprobe. We'll need to invoke the
* syscall directly. See
*
* https://docs.kernel.org/arch/riscv/hwprobe.html
*
* for more details.
*/
int ret = syscall(NR_riscv_hwprobe, pairs, pairs_count, cpu_set_size, (unsigned long*)cpu_set, 0 /* flags */);
#else
int ret = __riscv_hwprobe(pairs, pairs_count, cpu_set_size, (unsigned long*)cpu_set, 0 /* flags */);
#endif
if (ret < 0) { if (ret < 0) {
cpuinfo_log_warning("failed to get hwprobe information, err: %d", ret); cpuinfo_log_warning("failed to get hwprobe information, err: %d", ret);
return; goto cleanup;
} }
/** /**
@ -57,6 +144,8 @@ void cpuinfo_riscv_linux_decode_vendor_uarch_from_hwprobe(
break; break;
} }
} }
cpuinfo_riscv_decode_vendor_uarch(vendor_id, arch_id, imp_id, cpuinfo_riscv_decode_vendor_uarch(vendor_id, arch_id, imp_id, vendor, uarch);
vendor, uarch);
cleanup:
CPU_FREE(cpu_set);
} }

View File

@ -8,16 +8,15 @@
* *
* This must be kept in sync with the upstream kernel header. * This must be kept in sync with the upstream kernel header.
*/ */
#define COMPAT_HWCAP_ISA_I (1 << ('I' - 'A')) #define COMPAT_HWCAP_ISA_I (1 << ('I' - 'A'))
#define COMPAT_HWCAP_ISA_M (1 << ('M' - 'A')) #define COMPAT_HWCAP_ISA_M (1 << ('M' - 'A'))
#define COMPAT_HWCAP_ISA_A (1 << ('A' - 'A')) #define COMPAT_HWCAP_ISA_A (1 << ('A' - 'A'))
#define COMPAT_HWCAP_ISA_F (1 << ('F' - 'A')) #define COMPAT_HWCAP_ISA_F (1 << ('F' - 'A'))
#define COMPAT_HWCAP_ISA_D (1 << ('D' - 'A')) #define COMPAT_HWCAP_ISA_D (1 << ('D' - 'A'))
#define COMPAT_HWCAP_ISA_C (1 << ('C' - 'A')) #define COMPAT_HWCAP_ISA_C (1 << ('C' - 'A'))
#define COMPAT_HWCAP_ISA_V (1 << ('V' - 'A')) #define COMPAT_HWCAP_ISA_V (1 << ('V' - 'A'))
void cpuinfo_riscv_linux_decode_isa_from_hwcap( void cpuinfo_riscv_linux_decode_isa_from_hwcap(struct cpuinfo_riscv_isa isa[restrict static 1]) {
struct cpuinfo_riscv_isa isa[restrict static 1]) {
const unsigned long hwcap = getauxval(AT_HWCAP); const unsigned long hwcap = getauxval(AT_HWCAP);
if (hwcap & COMPAT_HWCAP_ISA_I) { if (hwcap & COMPAT_HWCAP_ISA_I) {

View File

@ -10,13 +10,13 @@ void cpuinfo_riscv_decode_vendor_uarch(
enum cpuinfo_vendor vendor[restrict static 1], enum cpuinfo_vendor vendor[restrict static 1],
enum cpuinfo_uarch uarch[restrict static 1]) { enum cpuinfo_uarch uarch[restrict static 1]) {
/* The vendor ID is sufficient to determine the cpuinfo_vendor. */ /* The vendor ID is sufficient to determine the cpuinfo_vendor. */
switch(vendor_id) { switch (vendor_id) {
case cpuinfo_riscv_chipset_sifive: case cpuinfo_riscv_chipset_vendor_sifive:
*vendor = cpuinfo_vendor_sifive; *vendor = cpuinfo_vendor_sifive;
break; break;
default: default:
*vendor = cpuinfo_vendor_unknown; *vendor = cpuinfo_vendor_unknown;
cpuinfo_log_warning("unknown vendor ID: %"PRIu32, vendor_id); cpuinfo_log_warning("unknown vendor ID: %" PRIu32, vendor_id);
break; break;
} }
/** /**

View File

@ -6,7 +6,6 @@
#include <cpuinfo.h> #include <cpuinfo.h>
#include <cpuinfo/common.h> #include <cpuinfo/common.h>
struct cpuid_regs { struct cpuid_regs {
uint32_t eax; uint32_t eax;
uint32_t ebx; uint32_t ebx;
@ -90,9 +89,12 @@ CPUINFO_INTERNAL enum cpuinfo_uarch cpuinfo_x86_decode_uarch(
const struct cpuinfo_x86_model_info* model_info); const struct cpuinfo_x86_model_info* model_info);
CPUINFO_INTERNAL struct cpuinfo_x86_isa cpuinfo_x86_detect_isa( CPUINFO_INTERNAL struct cpuinfo_x86_isa cpuinfo_x86_detect_isa(
const struct cpuid_regs basic_info, const struct cpuid_regs extended_info, const struct cpuid_regs basic_info,
uint32_t max_base_index, uint32_t max_extended_index, const struct cpuid_regs extended_info,
enum cpuinfo_vendor vendor, enum cpuinfo_uarch uarch); uint32_t max_base_index,
uint32_t max_extended_index,
enum cpuinfo_vendor vendor,
enum cpuinfo_uarch uarch);
CPUINFO_INTERNAL void cpuinfo_x86_detect_topology( CPUINFO_INTERNAL void cpuinfo_x86_detect_topology(
uint32_t max_base_index, uint32_t max_base_index,
@ -101,7 +103,8 @@ CPUINFO_INTERNAL void cpuinfo_x86_detect_topology(
struct cpuinfo_x86_topology* topology); struct cpuinfo_x86_topology* topology);
CPUINFO_INTERNAL void cpuinfo_x86_detect_cache( CPUINFO_INTERNAL void cpuinfo_x86_detect_cache(
uint32_t max_base_index, uint32_t max_extended_index, uint32_t max_base_index,
uint32_t max_extended_index,
bool amd_topology_extensions, bool amd_topology_extensions,
enum cpuinfo_vendor vendor, enum cpuinfo_vendor vendor,
const struct cpuinfo_x86_model_info* model_info, const struct cpuinfo_x86_model_info* model_info,
@ -122,7 +125,8 @@ CPUINFO_INTERNAL void cpuinfo_x86_detect_cache(
uint32_t* log2_package_cores_max); uint32_t* log2_package_cores_max);
CPUINFO_INTERNAL void cpuinfo_x86_decode_cache_descriptor( CPUINFO_INTERNAL void cpuinfo_x86_decode_cache_descriptor(
uint8_t descriptor, enum cpuinfo_vendor vendor, uint8_t descriptor,
enum cpuinfo_vendor vendor,
const struct cpuinfo_x86_model_info* model_info, const struct cpuinfo_x86_model_info* model_info,
struct cpuinfo_x86_caches* cache, struct cpuinfo_x86_caches* cache,
struct cpuinfo_tlb* itlb_4KB, struct cpuinfo_tlb* itlb_4KB,
@ -145,13 +149,9 @@ CPUINFO_INTERNAL bool cpuinfo_x86_decode_deterministic_cache_parameters(
struct cpuinfo_x86_caches* cache, struct cpuinfo_x86_caches* cache,
uint32_t* package_cores_max); uint32_t* package_cores_max);
CPUINFO_INTERNAL bool cpuinfo_x86_decode_cache_properties( CPUINFO_INTERNAL bool cpuinfo_x86_decode_cache_properties(struct cpuid_regs regs, struct cpuinfo_x86_caches* cache);
struct cpuid_regs regs,
struct cpuinfo_x86_caches* cache);
CPUINFO_INTERNAL uint32_t cpuinfo_x86_normalize_brand_string( CPUINFO_INTERNAL uint32_t cpuinfo_x86_normalize_brand_string(const char raw_name[48], char normalized_name[48]);
const char raw_name[48],
char normalized_name[48]);
CPUINFO_INTERNAL uint32_t cpuinfo_x86_format_package_name( CPUINFO_INTERNAL uint32_t cpuinfo_x86_format_package_name(
enum cpuinfo_vendor vendor, enum cpuinfo_vendor vendor,

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,9 @@
#include <stdint.h> #include <stdint.h>
#include <cpuinfo.h> #include <cpuinfo.h>
#include <x86/cpuid.h>
#include <cpuinfo/utils.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <cpuinfo/utils.h>
#include <x86/cpuid.h>
enum cache_type { enum cache_type {
cache_type_none = 0, cache_type_none = 0,
@ -16,8 +15,7 @@ enum cache_type {
bool cpuinfo_x86_decode_deterministic_cache_parameters( bool cpuinfo_x86_decode_deterministic_cache_parameters(
struct cpuid_regs regs, struct cpuid_regs regs,
struct cpuinfo_x86_caches* cache, struct cpuinfo_x86_caches* cache,
uint32_t* package_cores_max) uint32_t* package_cores_max) {
{
const uint32_t type = regs.eax & UINT32_C(0x1F); const uint32_t type = regs.eax & UINT32_C(0x1F);
if (type == cache_type_none) { if (type == cache_type_none) {
return false; return false;
@ -46,112 +44,106 @@ bool cpuinfo_x86_decode_deterministic_cache_parameters(
case 1: case 1:
switch (type) { switch (type) {
case cache_type_unified: case cache_type_unified:
cache->l1d = cache->l1i = (struct cpuinfo_x86_cache) { cache->l1d = cache->l1i = (struct cpuinfo_x86_cache){
.size = associativity * partitions * line_size * sets, .size = associativity * partitions * line_size * sets,
.associativity = associativity, .associativity = associativity,
.sets = sets, .sets = sets,
.partitions = partitions, .partitions = partitions,
.line_size = line_size, .line_size = line_size,
.flags = flags | CPUINFO_CACHE_UNIFIED, .flags = flags | CPUINFO_CACHE_UNIFIED,
.apic_bits = apic_bits .apic_bits = apic_bits};
};
break; break;
case cache_type_data: case cache_type_data:
cache->l1d = (struct cpuinfo_x86_cache) { cache->l1d = (struct cpuinfo_x86_cache){
.size = associativity * partitions * line_size * sets, .size = associativity * partitions * line_size * sets,
.associativity = associativity, .associativity = associativity,
.sets = sets, .sets = sets,
.partitions = partitions, .partitions = partitions,
.line_size = line_size, .line_size = line_size,
.flags = flags, .flags = flags,
.apic_bits = apic_bits .apic_bits = apic_bits};
};
break; break;
case cache_type_instruction: case cache_type_instruction:
cache->l1i = (struct cpuinfo_x86_cache) { cache->l1i = (struct cpuinfo_x86_cache){
.size = associativity * partitions * line_size * sets, .size = associativity * partitions * line_size * sets,
.associativity = associativity, .associativity = associativity,
.sets = sets, .sets = sets,
.partitions = partitions, .partitions = partitions,
.line_size = line_size, .line_size = line_size,
.flags = flags, .flags = flags,
.apic_bits = apic_bits .apic_bits = apic_bits};
};
break; break;
} }
break; break;
case 2: case 2:
switch (type) { switch (type) {
case cache_type_instruction: case cache_type_instruction:
cpuinfo_log_warning("unexpected L2 instruction cache reported in leaf 0x00000004 is ignored"); cpuinfo_log_warning(
"unexpected L2 instruction cache reported in leaf 0x00000004 is ignored");
break; break;
case cache_type_unified: case cache_type_unified:
flags |= CPUINFO_CACHE_UNIFIED; flags |= CPUINFO_CACHE_UNIFIED;
case cache_type_data: case cache_type_data:
cache->l2 = (struct cpuinfo_x86_cache) { cache->l2 = (struct cpuinfo_x86_cache){
.size = associativity * partitions * line_size * sets, .size = associativity * partitions * line_size * sets,
.associativity = associativity, .associativity = associativity,
.sets = sets, .sets = sets,
.partitions = partitions, .partitions = partitions,
.line_size = line_size, .line_size = line_size,
.flags = flags, .flags = flags,
.apic_bits = apic_bits .apic_bits = apic_bits};
};
break; break;
} }
break; break;
case 3: case 3:
switch (type) { switch (type) {
case cache_type_instruction: case cache_type_instruction:
cpuinfo_log_warning("unexpected L3 instruction cache reported in leaf 0x00000004 is ignored"); cpuinfo_log_warning(
"unexpected L3 instruction cache reported in leaf 0x00000004 is ignored");
break; break;
case cache_type_unified: case cache_type_unified:
flags |= CPUINFO_CACHE_UNIFIED; flags |= CPUINFO_CACHE_UNIFIED;
case cache_type_data: case cache_type_data:
cache->l3 = (struct cpuinfo_x86_cache) { cache->l3 = (struct cpuinfo_x86_cache){
.size = associativity * partitions * line_size * sets, .size = associativity * partitions * line_size * sets,
.associativity = associativity, .associativity = associativity,
.sets = sets, .sets = sets,
.partitions = partitions, .partitions = partitions,
.line_size = line_size, .line_size = line_size,
.flags = flags, .flags = flags,
.apic_bits = apic_bits .apic_bits = apic_bits};
};
break; break;
} }
break; break;
case 4: case 4:
switch (type) { switch (type) {
case cache_type_instruction: case cache_type_instruction:
cpuinfo_log_warning("unexpected L4 instruction cache reported in leaf 0x00000004 is ignored"); cpuinfo_log_warning(
"unexpected L4 instruction cache reported in leaf 0x00000004 is ignored");
break; break;
case cache_type_unified: case cache_type_unified:
flags |= CPUINFO_CACHE_UNIFIED; flags |= CPUINFO_CACHE_UNIFIED;
case cache_type_data: case cache_type_data:
cache->l4 = (struct cpuinfo_x86_cache) { cache->l4 = (struct cpuinfo_x86_cache){
.size = associativity * partitions * line_size * sets, .size = associativity * partitions * line_size * sets,
.associativity = associativity, .associativity = associativity,
.sets = sets, .sets = sets,
.partitions = partitions, .partitions = partitions,
.line_size = line_size, .line_size = line_size,
.flags = flags, .flags = flags,
.apic_bits = apic_bits .apic_bits = apic_bits};
};
break; break;
} }
break; break;
default: default:
cpuinfo_log_warning("unexpected L%"PRIu32" cache reported in leaf 0x00000004 is ignored", level); cpuinfo_log_warning(
"unexpected L%" PRIu32 " cache reported in leaf 0x00000004 is ignored", level);
break; break;
} }
return true; return true;
} }
bool cpuinfo_x86_decode_cache_properties(struct cpuid_regs regs, struct cpuinfo_x86_caches* cache) {
bool cpuinfo_x86_decode_cache_properties(
struct cpuid_regs regs,
struct cpuinfo_x86_caches* cache)
{
const uint32_t type = regs.eax & UINT32_C(0x1F); const uint32_t type = regs.eax & UINT32_C(0x1F);
if (type == cache_type_none) { if (type == cache_type_none) {
return false; return false;
@ -175,82 +167,80 @@ bool cpuinfo_x86_decode_cache_properties(
case 1: case 1:
switch (type) { switch (type) {
case cache_type_unified: case cache_type_unified:
cache->l1d = cache->l1i = (struct cpuinfo_x86_cache) { cache->l1d = cache->l1i = (struct cpuinfo_x86_cache){
.size = associativity * partitions * line_size * sets, .size = associativity * partitions * line_size * sets,
.associativity = associativity, .associativity = associativity,
.sets = sets, .sets = sets,
.partitions = partitions, .partitions = partitions,
.line_size = line_size, .line_size = line_size,
.flags = flags | CPUINFO_CACHE_UNIFIED, .flags = flags | CPUINFO_CACHE_UNIFIED,
.apic_bits = apic_bits .apic_bits = apic_bits};
};
break; break;
case cache_type_data: case cache_type_data:
cache->l1d = (struct cpuinfo_x86_cache) { cache->l1d = (struct cpuinfo_x86_cache){
.size = associativity * partitions * line_size * sets, .size = associativity * partitions * line_size * sets,
.associativity = associativity, .associativity = associativity,
.sets = sets, .sets = sets,
.partitions = partitions, .partitions = partitions,
.line_size = line_size, .line_size = line_size,
.flags = flags, .flags = flags,
.apic_bits = apic_bits .apic_bits = apic_bits};
};
break; break;
case cache_type_instruction: case cache_type_instruction:
cache->l1i = (struct cpuinfo_x86_cache) { cache->l1i = (struct cpuinfo_x86_cache){
.size = associativity * partitions * line_size * sets, .size = associativity * partitions * line_size * sets,
.associativity = associativity, .associativity = associativity,
.sets = sets, .sets = sets,
.partitions = partitions, .partitions = partitions,
.line_size = line_size, .line_size = line_size,
.flags = flags, .flags = flags,
.apic_bits = apic_bits .apic_bits = apic_bits};
};
break; break;
} }
break; break;
case 2: case 2:
switch (type) { switch (type) {
case cache_type_instruction: case cache_type_instruction:
cpuinfo_log_warning("unexpected L2 instruction cache reported in leaf 0x8000001D is ignored"); cpuinfo_log_warning(
"unexpected L2 instruction cache reported in leaf 0x8000001D is ignored");
break; break;
case cache_type_unified: case cache_type_unified:
flags |= CPUINFO_CACHE_UNIFIED; flags |= CPUINFO_CACHE_UNIFIED;
case cache_type_data: case cache_type_data:
cache->l2 = (struct cpuinfo_x86_cache) { cache->l2 = (struct cpuinfo_x86_cache){
.size = associativity * partitions * line_size * sets, .size = associativity * partitions * line_size * sets,
.associativity = associativity, .associativity = associativity,
.sets = sets, .sets = sets,
.partitions = partitions, .partitions = partitions,
.line_size = line_size, .line_size = line_size,
.flags = flags, .flags = flags,
.apic_bits = apic_bits .apic_bits = apic_bits};
};
break; break;
} }
break; break;
case 3: case 3:
switch (type) { switch (type) {
case cache_type_instruction: case cache_type_instruction:
cpuinfo_log_warning("unexpected L3 instruction cache reported in leaf 0x8000001D is ignored"); cpuinfo_log_warning(
"unexpected L3 instruction cache reported in leaf 0x8000001D is ignored");
break; break;
case cache_type_unified: case cache_type_unified:
flags |= CPUINFO_CACHE_UNIFIED; flags |= CPUINFO_CACHE_UNIFIED;
case cache_type_data: case cache_type_data:
cache->l3 = (struct cpuinfo_x86_cache) { cache->l3 = (struct cpuinfo_x86_cache){
.size = associativity * partitions * line_size * sets, .size = associativity * partitions * line_size * sets,
.associativity = associativity, .associativity = associativity,
.sets = sets, .sets = sets,
.partitions = partitions, .partitions = partitions,
.line_size = line_size, .line_size = line_size,
.flags = flags, .flags = flags,
.apic_bits = apic_bits .apic_bits = apic_bits};
};
break; break;
} }
break; break;
default: default:
cpuinfo_log_warning("unexpected L%"PRIu32" cache reported in leaf 0x8000001D is ignored", level); cpuinfo_log_warning(
"unexpected L%" PRIu32 " cache reported in leaf 0x8000001D is ignored", level);
break; break;
} }
return true; return true;

View File

@ -1,11 +1,10 @@
#include <stdint.h> #include <stdint.h>
#include <cpuinfo.h> #include <cpuinfo.h>
#include <cpuinfo/utils.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <x86/cpuid.h> #include <cpuinfo/utils.h>
#include <x86/api.h> #include <x86/api.h>
#include <x86/cpuid.h>
union cpuinfo_x86_cache_descriptors { union cpuinfo_x86_cache_descriptors {
struct cpuid_regs regs; struct cpuid_regs regs;
@ -20,7 +19,8 @@ enum cache_type {
}; };
void cpuinfo_x86_detect_cache( void cpuinfo_x86_detect_cache(
uint32_t max_base_index, uint32_t max_extended_index, uint32_t max_base_index,
uint32_t max_extended_index,
bool amd_topology_extensions, bool amd_topology_extensions,
enum cpuinfo_vendor vendor, enum cpuinfo_vendor vendor,
const struct cpuinfo_x86_model_info* model_info, const struct cpuinfo_x86_model_info* model_info,
@ -38,24 +38,34 @@ void cpuinfo_x86_detect_cache(
struct cpuinfo_tlb* stlb2_4KB, struct cpuinfo_tlb* stlb2_4KB,
struct cpuinfo_tlb* stlb2_2MB, struct cpuinfo_tlb* stlb2_2MB,
struct cpuinfo_tlb* stlb2_1GB, struct cpuinfo_tlb* stlb2_1GB,
uint32_t* log2_package_cores_max) uint32_t* log2_package_cores_max) {
{
if (max_base_index >= 2) { if (max_base_index >= 2) {
union cpuinfo_x86_cache_descriptors descriptors; union cpuinfo_x86_cache_descriptors descriptors;
descriptors.regs = cpuid(2); descriptors.regs = cpuid(2);
uint32_t iterations = (uint8_t) descriptors.as_bytes[0]; uint32_t iterations = (uint8_t)descriptors.as_bytes[0];
if (iterations != 0) { if (iterations != 0) {
iterate_descriptors: iterate_descriptors:
for (uint32_t i = 1 /* note: not 0 */; i < 16; i++) { for (uint32_t i = 1 /* note: not 0 */; i < 16; i++) {
const uint8_t descriptor = descriptors.as_bytes[i]; const uint8_t descriptor = descriptors.as_bytes[i];
if (descriptor != 0) { if (descriptor != 0) {
cpuinfo_x86_decode_cache_descriptor( cpuinfo_x86_decode_cache_descriptor(
descriptor, vendor, model_info, descriptor,
vendor,
model_info,
cache, cache,
itlb_4KB, itlb_2MB, itlb_4MB, itlb_4KB,
dtlb0_4KB, dtlb0_2MB, dtlb0_4MB, itlb_2MB,
dtlb_4KB, dtlb_2MB, dtlb_4MB, dtlb_1GB, itlb_4MB,
stlb2_4KB, stlb2_2MB, stlb2_1GB, dtlb0_4KB,
dtlb0_2MB,
dtlb0_4MB,
dtlb_4KB,
dtlb_2MB,
dtlb_4MB,
dtlb_1GB,
stlb2_4KB,
stlb2_2MB,
stlb2_1GB,
&cache->prefetch_size); &cache->prefetch_size);
} }
} }
@ -71,8 +81,7 @@ iterate_descriptors:
uint32_t package_cores_max = 0; uint32_t package_cores_max = 0;
do { do {
leaf4 = cpuidex(4, input_ecx++); leaf4 = cpuidex(4, input_ecx++);
} while (cpuinfo_x86_decode_deterministic_cache_parameters( } while (cpuinfo_x86_decode_deterministic_cache_parameters(leaf4, cache, &package_cores_max));
leaf4, cache, &package_cores_max));
if (package_cores_max != 0) { if (package_cores_max != 0) {
*log2_package_cores_max = bit_length(package_cores_max); *log2_package_cores_max = bit_length(package_cores_max);
} }

View File

@ -2,78 +2,76 @@
#include <stdint.h> #include <stdint.h>
#if defined(__GNUC__) #if defined(__GNUC__)
#include <cpuid.h> #include <cpuid.h>
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
#include <intrin.h> #include <intrin.h>
#endif #endif
#if CPUINFO_MOCK #if CPUINFO_MOCK
#include <cpuinfo-mock.h> #include <cpuinfo-mock.h>
#endif #endif
#include <x86/api.h> #include <x86/api.h>
#if defined(__GNUC__) || defined(_MSC_VER) #if defined(__GNUC__) || defined(_MSC_VER)
static inline struct cpuid_regs cpuid(uint32_t eax) { static inline struct cpuid_regs cpuid(uint32_t eax) {
#if CPUINFO_MOCK #if CPUINFO_MOCK
uint32_t regs_array[4]; uint32_t regs_array[4];
cpuinfo_mock_get_cpuid(eax, regs_array); cpuinfo_mock_get_cpuid(eax, regs_array);
return (struct cpuid_regs) { return (struct cpuid_regs){
.eax = regs_array[0], .eax = regs_array[0],
.ebx = regs_array[1], .ebx = regs_array[1],
.ecx = regs_array[2], .ecx = regs_array[2],
.edx = regs_array[3], .edx = regs_array[3],
}; };
#else #else
struct cpuid_regs regs; struct cpuid_regs regs;
#if defined(__GNUC__) #if defined(__GNUC__)
__cpuid(eax, regs.eax, regs.ebx, regs.ecx, regs.edx); __cpuid(eax, regs.eax, regs.ebx, regs.ecx, regs.edx);
#else #else
int regs_array[4]; int regs_array[4];
__cpuid(regs_array, (int) eax); __cpuid(regs_array, (int)eax);
regs.eax = regs_array[0]; regs.eax = regs_array[0];
regs.ebx = regs_array[1]; regs.ebx = regs_array[1];
regs.ecx = regs_array[2]; regs.ecx = regs_array[2];
regs.edx = regs_array[3]; regs.edx = regs_array[3];
#endif #endif
return regs; return regs;
#endif #endif
} }
static inline struct cpuid_regs cpuidex(uint32_t eax, uint32_t ecx) { static inline struct cpuid_regs cpuidex(uint32_t eax, uint32_t ecx) {
#if CPUINFO_MOCK #if CPUINFO_MOCK
uint32_t regs_array[4]; uint32_t regs_array[4];
cpuinfo_mock_get_cpuidex(eax, ecx, regs_array); cpuinfo_mock_get_cpuidex(eax, ecx, regs_array);
return (struct cpuid_regs) { return (struct cpuid_regs){
.eax = regs_array[0], .eax = regs_array[0],
.ebx = regs_array[1], .ebx = regs_array[1],
.ecx = regs_array[2], .ecx = regs_array[2],
.edx = regs_array[3], .edx = regs_array[3],
}; };
#else #else
struct cpuid_regs regs; struct cpuid_regs regs;
#if defined(__GNUC__) #if defined(__GNUC__)
__cpuid_count(eax, ecx, regs.eax, regs.ebx, regs.ecx, regs.edx); __cpuid_count(eax, ecx, regs.eax, regs.ebx, regs.ecx, regs.edx);
#else #else
int regs_array[4]; int regs_array[4];
__cpuidex(regs_array, (int) eax, (int) ecx); __cpuidex(regs_array, (int)eax, (int)ecx);
regs.eax = regs_array[0]; regs.eax = regs_array[0];
regs.ebx = regs_array[1]; regs.ebx = regs_array[1];
regs.ecx = regs_array[2]; regs.ecx = regs_array[2];
regs.edx = regs_array[3]; regs.edx = regs_array[3];
#endif #endif
return regs; return regs;
#endif #endif
} }
#endif #endif
static inline uint64_t xgetbv(uint32_t ext_ctrl_reg) { static inline uint64_t xgetbv(uint32_t ext_ctrl_reg) {
#ifdef _MSC_VER #ifdef _MSC_VER
return (uint64_t)_xgetbv((unsigned int)ext_ctrl_reg); return (uint64_t)_xgetbv((unsigned int)ext_ctrl_reg);
#else #else
uint32_t lo, hi; uint32_t lo, hi;
__asm__(".byte 0x0F, 0x01, 0xD0" : "=a" (lo), "=d" (hi) : "c" (ext_ctrl_reg)); __asm__(".byte 0x0F, 0x01, 0xD0" : "=a"(lo), "=d"(hi) : "c"(ext_ctrl_reg));
return ((uint64_t) hi << 32) | (uint64_t) lo; return ((uint64_t)hi << 32) | (uint64_t)lo;
#endif #endif
} }

382
3rdparty/cpuinfo/src/x86/freebsd/init.c vendored Normal file
View File

@ -0,0 +1,382 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <cpuinfo.h>
#include <cpuinfo/internal-api.h>
#include <cpuinfo/log.h>
#include <freebsd/api.h>
#include <x86/api.h>
static inline uint32_t max(uint32_t a, uint32_t b) {
return a > b ? a : b;
}
static inline uint32_t bit_mask(uint32_t bits) {
return (UINT32_C(1) << bits) - UINT32_C(1);
}
void cpuinfo_x86_freebsd_init(void) {
struct cpuinfo_processor* processors = NULL;
struct cpuinfo_core* cores = NULL;
struct cpuinfo_cluster* clusters = NULL;
struct cpuinfo_package* packages = NULL;
struct cpuinfo_cache* l1i = NULL;
struct cpuinfo_cache* l1d = NULL;
struct cpuinfo_cache* l2 = NULL;
struct cpuinfo_cache* l3 = NULL;
struct cpuinfo_cache* l4 = NULL;
struct cpuinfo_freebsd_topology freebsd_topology = cpuinfo_freebsd_detect_topology();
if (freebsd_topology.packages == 0) {
cpuinfo_log_error("failed to detect topology");
goto cleanup;
}
processors = calloc(freebsd_topology.threads, sizeof(struct cpuinfo_processor));
if (processors == NULL) {
cpuinfo_log_error(
"failed to allocate %zu bytes for descriptions of %" PRIu32 " logical processors",
freebsd_topology.threads * sizeof(struct cpuinfo_processor),
freebsd_topology.threads);
goto cleanup;
}
cores = calloc(freebsd_topology.cores, sizeof(struct cpuinfo_core));
if (cores == NULL) {
cpuinfo_log_error(
"failed to allocate %zu bytes for descriptions of %" PRIu32 " cores",
freebsd_topology.cores * sizeof(struct cpuinfo_core),
freebsd_topology.cores);
goto cleanup;
}
/* On x86 a cluster of cores is the biggest group of cores that shares a
* cache. */
clusters = calloc(freebsd_topology.packages, sizeof(struct cpuinfo_cluster));
if (clusters == NULL) {
cpuinfo_log_error(
"failed to allocate %zu bytes for descriptions of %" PRIu32 " core clusters",
freebsd_topology.packages * sizeof(struct cpuinfo_cluster),
freebsd_topology.packages);
goto cleanup;
}
packages = calloc(freebsd_topology.packages, sizeof(struct cpuinfo_package));
if (packages == NULL) {
cpuinfo_log_error(
"failed to allocate %zu bytes for descriptions of %" PRIu32 " physical packages",
freebsd_topology.packages * sizeof(struct cpuinfo_package),
freebsd_topology.packages);
goto cleanup;
}
struct cpuinfo_x86_processor x86_processor;
memset(&x86_processor, 0, sizeof(x86_processor));
cpuinfo_x86_init_processor(&x86_processor);
char brand_string[48];
cpuinfo_x86_normalize_brand_string(x86_processor.brand_string, brand_string);
const uint32_t threads_per_core = freebsd_topology.threads_per_core;
const uint32_t threads_per_package = freebsd_topology.threads / freebsd_topology.packages;
const uint32_t cores_per_package = freebsd_topology.cores / freebsd_topology.packages;
for (uint32_t i = 0; i < freebsd_topology.packages; i++) {
clusters[i] = (struct cpuinfo_cluster){
.processor_start = i * threads_per_package,
.processor_count = threads_per_package,
.core_start = i * cores_per_package,
.core_count = cores_per_package,
.cluster_id = 0,
.package = packages + i,
.vendor = x86_processor.vendor,
.uarch = x86_processor.uarch,
.cpuid = x86_processor.cpuid,
};
packages[i].processor_start = i * threads_per_package;
packages[i].processor_count = threads_per_package;
packages[i].core_start = i * cores_per_package;
packages[i].core_count = cores_per_package;
packages[i].cluster_start = i;
packages[i].cluster_count = 1;
cpuinfo_x86_format_package_name(x86_processor.vendor, brand_string, packages[i].name);
}
for (uint32_t i = 0; i < freebsd_topology.cores; i++) {
cores[i] = (struct cpuinfo_core){
.processor_start = i * threads_per_core,
.processor_count = threads_per_core,
.core_id = i % cores_per_package,
.cluster = clusters + i / cores_per_package,
.package = packages + i / cores_per_package,
.vendor = x86_processor.vendor,
.uarch = x86_processor.uarch,
.cpuid = x86_processor.cpuid,
};
}
for (uint32_t i = 0; i < freebsd_topology.threads; i++) {
const uint32_t smt_id = i % threads_per_core;
const uint32_t core_id = i / threads_per_core;
const uint32_t package_id = i / threads_per_package;
/* Reconstruct APIC IDs from topology components */
const uint32_t thread_bits_mask = bit_mask(x86_processor.topology.thread_bits_length);
const uint32_t core_bits_mask = bit_mask(x86_processor.topology.core_bits_length);
const uint32_t package_bits_offset =
max(x86_processor.topology.thread_bits_offset + x86_processor.topology.thread_bits_length,
x86_processor.topology.core_bits_offset + x86_processor.topology.core_bits_length);
const uint32_t apic_id = ((smt_id & thread_bits_mask) << x86_processor.topology.thread_bits_offset) |
((core_id & core_bits_mask) << x86_processor.topology.core_bits_offset) |
(package_id << package_bits_offset);
cpuinfo_log_debug("reconstructed APIC ID 0x%08" PRIx32 " for thread %" PRIu32, apic_id, i);
processors[i].smt_id = smt_id;
processors[i].core = cores + i / threads_per_core;
processors[i].cluster = clusters + i / threads_per_package;
processors[i].package = packages + i / threads_per_package;
processors[i].apic_id = apic_id;
}
uint32_t threads_per_l1 = 0, l1_count = 0;
if (x86_processor.cache.l1i.size != 0 || x86_processor.cache.l1d.size != 0) {
/* Assume that threads on the same core share L1 */
threads_per_l1 = freebsd_topology.threads / freebsd_topology.cores;
cpuinfo_log_warning(
"freebsd kernel did not report number of "
"threads sharing L1 cache; assume %" PRIu32,
threads_per_l1);
l1_count = freebsd_topology.threads / threads_per_l1;
cpuinfo_log_debug("detected %" PRIu32 " L1 caches", l1_count);
}
uint32_t threads_per_l2 = 0, l2_count = 0;
if (x86_processor.cache.l2.size != 0) {
if (x86_processor.cache.l3.size != 0) {
/* This is not a last-level cache; assume that threads
* on the same core share L2 */
threads_per_l2 = freebsd_topology.threads / freebsd_topology.cores;
} else {
/* This is a last-level cache; assume that threads on
* the same package share L2 */
threads_per_l2 = freebsd_topology.threads / freebsd_topology.packages;
}
cpuinfo_log_warning(
"freebsd kernel did not report number of "
"threads sharing L2 cache; assume %" PRIu32,
threads_per_l2);
l2_count = freebsd_topology.threads / threads_per_l2;
cpuinfo_log_debug("detected %" PRIu32 " L2 caches", l2_count);
}
uint32_t threads_per_l3 = 0, l3_count = 0;
if (x86_processor.cache.l3.size != 0) {
/*
* Assume that threads on the same package share L3.
* However, is it not necessarily the last-level cache (there
* may be L4 cache as well)
*/
threads_per_l3 = freebsd_topology.threads / freebsd_topology.packages;
cpuinfo_log_warning(
"freebsd kernel did not report number of "
"threads sharing L3 cache; assume %" PRIu32,
threads_per_l3);
l3_count = freebsd_topology.threads / threads_per_l3;
cpuinfo_log_debug("detected %" PRIu32 " L3 caches", l3_count);
}
uint32_t threads_per_l4 = 0, l4_count = 0;
if (x86_processor.cache.l4.size != 0) {
/*
* Assume that all threads share this L4.
* As of now, L4 cache exists only on notebook x86 CPUs, which
* are single-package, but multi-socket systems could have
* shared L4 (like on IBM POWER8).
*/
threads_per_l4 = freebsd_topology.threads;
cpuinfo_log_warning(
"freebsd kernel did not report number of "
"threads sharing L4 cache; assume %" PRIu32,
threads_per_l4);
l4_count = freebsd_topology.threads / threads_per_l4;
cpuinfo_log_debug("detected %" PRIu32 " L4 caches", l4_count);
}
if (x86_processor.cache.l1i.size != 0) {
l1i = calloc(l1_count, sizeof(struct cpuinfo_cache));
if (l1i == NULL) {
cpuinfo_log_error(
"failed to allocate %zu bytes for descriptions of "
"%" PRIu32 " L1I caches",
l1_count * sizeof(struct cpuinfo_cache),
l1_count);
return;
}
for (uint32_t c = 0; c < l1_count; c++) {
l1i[c] = (struct cpuinfo_cache){
.size = x86_processor.cache.l1i.size,
.associativity = x86_processor.cache.l1i.associativity,
.sets = x86_processor.cache.l1i.sets,
.partitions = x86_processor.cache.l1i.partitions,
.line_size = x86_processor.cache.l1i.line_size,
.flags = x86_processor.cache.l1i.flags,
.processor_start = c * threads_per_l1,
.processor_count = threads_per_l1,
};
}
for (uint32_t t = 0; t < freebsd_topology.threads; t++) {
processors[t].cache.l1i = &l1i[t / threads_per_l1];
}
}
if (x86_processor.cache.l1d.size != 0) {
l1d = calloc(l1_count, sizeof(struct cpuinfo_cache));
if (l1d == NULL) {
cpuinfo_log_error(
"failed to allocate %zu bytes for descriptions of "
"%" PRIu32 " L1D caches",
l1_count * sizeof(struct cpuinfo_cache),
l1_count);
return;
}
for (uint32_t c = 0; c < l1_count; c++) {
l1d[c] = (struct cpuinfo_cache){
.size = x86_processor.cache.l1d.size,
.associativity = x86_processor.cache.l1d.associativity,
.sets = x86_processor.cache.l1d.sets,
.partitions = x86_processor.cache.l1d.partitions,
.line_size = x86_processor.cache.l1d.line_size,
.flags = x86_processor.cache.l1d.flags,
.processor_start = c * threads_per_l1,
.processor_count = threads_per_l1,
};
}
for (uint32_t t = 0; t < freebsd_topology.threads; t++) {
processors[t].cache.l1d = &l1d[t / threads_per_l1];
}
}
if (l2_count != 0) {
l2 = calloc(l2_count, sizeof(struct cpuinfo_cache));
if (l2 == NULL) {
cpuinfo_log_error(
"failed to allocate %zu bytes for descriptions of "
"%" PRIu32 " L2 caches",
l2_count * sizeof(struct cpuinfo_cache),
l2_count);
return;
}
for (uint32_t c = 0; c < l2_count; c++) {
l2[c] = (struct cpuinfo_cache){
.size = x86_processor.cache.l2.size,
.associativity = x86_processor.cache.l2.associativity,
.sets = x86_processor.cache.l2.sets,
.partitions = x86_processor.cache.l2.partitions,
.line_size = x86_processor.cache.l2.line_size,
.flags = x86_processor.cache.l2.flags,
.processor_start = c * threads_per_l2,
.processor_count = threads_per_l2,
};
}
for (uint32_t t = 0; t < freebsd_topology.threads; t++) {
processors[t].cache.l2 = &l2[t / threads_per_l2];
}
}
if (l3_count != 0) {
l3 = calloc(l3_count, sizeof(struct cpuinfo_cache));
if (l3 == NULL) {
cpuinfo_log_error(
"failed to allocate %zu bytes for descriptions of "
"%" PRIu32 " L3 caches",
l3_count * sizeof(struct cpuinfo_cache),
l3_count);
return;
}
for (uint32_t c = 0; c < l3_count; c++) {
l3[c] = (struct cpuinfo_cache){
.size = x86_processor.cache.l3.size,
.associativity = x86_processor.cache.l3.associativity,
.sets = x86_processor.cache.l3.sets,
.partitions = x86_processor.cache.l3.partitions,
.line_size = x86_processor.cache.l3.line_size,
.flags = x86_processor.cache.l3.flags,
.processor_start = c * threads_per_l3,
.processor_count = threads_per_l3,
};
}
for (uint32_t t = 0; t < freebsd_topology.threads; t++) {
processors[t].cache.l3 = &l3[t / threads_per_l3];
}
}
if (l4_count != 0) {
l4 = calloc(l4_count, sizeof(struct cpuinfo_cache));
if (l4 == NULL) {
cpuinfo_log_error(
"failed to allocate %zu bytes for descriptions of "
"%" PRIu32 " L4 caches",
l4_count * sizeof(struct cpuinfo_cache),
l4_count);
return;
}
for (uint32_t c = 0; c < l4_count; c++) {
l4[c] = (struct cpuinfo_cache){
.size = x86_processor.cache.l4.size,
.associativity = x86_processor.cache.l4.associativity,
.sets = x86_processor.cache.l4.sets,
.partitions = x86_processor.cache.l4.partitions,
.line_size = x86_processor.cache.l4.line_size,
.flags = x86_processor.cache.l4.flags,
.processor_start = c * threads_per_l4,
.processor_count = threads_per_l4,
};
}
for (uint32_t t = 0; t < freebsd_topology.threads; t++) {
processors[t].cache.l4 = &l4[t / threads_per_l4];
}
}
/* Commit changes */
cpuinfo_processors = processors;
cpuinfo_cores = cores;
cpuinfo_clusters = clusters;
cpuinfo_packages = packages;
cpuinfo_cache[cpuinfo_cache_level_1i] = l1i;
cpuinfo_cache[cpuinfo_cache_level_1d] = l1d;
cpuinfo_cache[cpuinfo_cache_level_2] = l2;
cpuinfo_cache[cpuinfo_cache_level_3] = l3;
cpuinfo_cache[cpuinfo_cache_level_4] = l4;
cpuinfo_processors_count = freebsd_topology.threads;
cpuinfo_cores_count = freebsd_topology.cores;
cpuinfo_clusters_count = freebsd_topology.packages;
cpuinfo_packages_count = freebsd_topology.packages;
cpuinfo_cache_count[cpuinfo_cache_level_1i] = l1_count;
cpuinfo_cache_count[cpuinfo_cache_level_1d] = l1_count;
cpuinfo_cache_count[cpuinfo_cache_level_2] = l2_count;
cpuinfo_cache_count[cpuinfo_cache_level_3] = l3_count;
cpuinfo_cache_count[cpuinfo_cache_level_4] = l4_count;
cpuinfo_max_cache_size = cpuinfo_compute_max_cache_size(&processors[0]);
cpuinfo_global_uarch = (struct cpuinfo_uarch_info){
.uarch = x86_processor.uarch,
.cpuid = x86_processor.cpuid,
.processor_count = freebsd_topology.threads,
.core_count = freebsd_topology.cores,
};
__sync_synchronize();
cpuinfo_is_initialized = true;
processors = NULL;
cores = NULL;
clusters = NULL;
packages = NULL;
l1i = l1d = l2 = l3 = l4 = NULL;
cleanup:
free(processors);
free(cores);
free(clusters);
free(packages);
free(l1i);
free(l1d);
free(l2);
free(l3);
free(l4);
}

View File

@ -3,17 +3,16 @@
#include <cpuinfo.h> #include <cpuinfo.h>
#include <x86/api.h> #include <x86/api.h>
struct cpuinfo_x86_model_info cpuinfo_x86_decode_model_info(uint32_t eax) { struct cpuinfo_x86_model_info cpuinfo_x86_decode_model_info(uint32_t eax) {
struct cpuinfo_x86_model_info model_info; struct cpuinfo_x86_model_info model_info;
model_info.stepping = eax & 0xF; model_info.stepping = eax & 0xF;
model_info.base_model = (eax >> 4) & 0xF; model_info.base_model = (eax >> 4) & 0xF;
model_info.base_family = (eax >> 8) & 0xF; model_info.base_family = (eax >> 8) & 0xF;
model_info.processor_type = (eax >> 12) & 0x3; model_info.processor_type = (eax >> 12) & 0x3;
model_info.extended_model = (eax >> 16) & 0xF; model_info.extended_model = (eax >> 16) & 0xF;
model_info.extended_family = (eax >> 20) & 0xFF; model_info.extended_family = (eax >> 20) & 0xFF;
model_info.family = model_info.base_family + model_info.extended_family; model_info.family = model_info.base_family + model_info.extended_family;
model_info.model = model_info.base_model + (model_info.extended_model << 4); model_info.model = model_info.base_model + (model_info.extended_model << 4);
return model_info; return model_info;
} }

View File

@ -2,14 +2,13 @@
#include <string.h> #include <string.h>
#include <cpuinfo.h> #include <cpuinfo.h>
#include <x86/cpuid.h>
#include <x86/api.h>
#include <cpuinfo/utils.h>
#include <cpuinfo/log.h>
#include <cpuinfo/common.h> #include <cpuinfo/common.h>
#include <cpuinfo/log.h>
#include <cpuinfo/utils.h>
#include <x86/api.h>
#include <x86/cpuid.h>
struct cpuinfo_x86_isa cpuinfo_isa = {0};
struct cpuinfo_x86_isa cpuinfo_isa = { 0 };
CPUINFO_INTERNAL uint32_t cpuinfo_x86_clflush_size = 0; CPUINFO_INTERNAL uint32_t cpuinfo_x86_clflush_size = 0;
void cpuinfo_x86_init_processor(struct cpuinfo_x86_processor* processor) { void cpuinfo_x86_init_processor(struct cpuinfo_x86_processor* processor) {
@ -19,30 +18,34 @@ void cpuinfo_x86_init_processor(struct cpuinfo_x86_processor* processor) {
cpuinfo_x86_decode_vendor(leaf0.ebx, leaf0.ecx, leaf0.edx); cpuinfo_x86_decode_vendor(leaf0.ebx, leaf0.ecx, leaf0.edx);
const struct cpuid_regs leaf0x80000000 = cpuid(UINT32_C(0x80000000)); const struct cpuid_regs leaf0x80000000 = cpuid(UINT32_C(0x80000000));
const uint32_t max_extended_index = const uint32_t max_extended_index = leaf0x80000000.eax >= UINT32_C(0x80000000) ? leaf0x80000000.eax : 0;
leaf0x80000000.eax >= UINT32_C(0x80000000) ? leaf0x80000000.eax : 0;
const struct cpuid_regs leaf0x80000001 = max_extended_index >= UINT32_C(0x80000001) ? const struct cpuid_regs leaf0x80000001 = max_extended_index >= UINT32_C(0x80000001)
cpuid(UINT32_C(0x80000001)) : (struct cpuid_regs) { 0, 0, 0, 0 }; ? cpuid(UINT32_C(0x80000001))
: (struct cpuid_regs){0, 0, 0, 0};
if (max_base_index >= 1) { if (max_base_index >= 1) {
const struct cpuid_regs leaf1 = cpuid(1); const struct cpuid_regs leaf1 = cpuid(1);
processor->cpuid = leaf1.eax; processor->cpuid = leaf1.eax;
const struct cpuinfo_x86_model_info model_info = cpuinfo_x86_decode_model_info(leaf1.eax); const struct cpuinfo_x86_model_info model_info = cpuinfo_x86_decode_model_info(leaf1.eax);
const enum cpuinfo_uarch uarch = processor->uarch = const enum cpuinfo_uarch uarch = processor->uarch = cpuinfo_x86_decode_uarch(vendor, &model_info);
cpuinfo_x86_decode_uarch(vendor, &model_info);
cpuinfo_x86_clflush_size = ((leaf1.ebx >> 8) & UINT32_C(0x000000FF)) * 8; cpuinfo_x86_clflush_size = ((leaf1.ebx >> 8) & UINT32_C(0x000000FF)) * 8;
/* /*
* Topology extensions support: * Topology extensions support:
* - AMD: ecx[bit 22] in extended info (reserved bit on Intel CPUs). * - AMD: ecx[bit 22] in extended info (reserved bit on Intel
* CPUs).
*/ */
const bool amd_topology_extensions = !!(leaf0x80000001.ecx & UINT32_C(0x00400000)); const bool amd_topology_extensions = !!(leaf0x80000001.ecx & UINT32_C(0x00400000));
cpuinfo_x86_detect_cache( cpuinfo_x86_detect_cache(
max_base_index, max_extended_index, amd_topology_extensions, vendor, &model_info, max_base_index,
max_extended_index,
amd_topology_extensions,
vendor,
&model_info,
&processor->cache, &processor->cache,
&processor->tlb.itlb_4KB, &processor->tlb.itlb_4KB,
&processor->tlb.itlb_2MB, &processor->tlb.itlb_2MB,
@ -61,8 +64,8 @@ void cpuinfo_x86_init_processor(struct cpuinfo_x86_processor* processor) {
cpuinfo_x86_detect_topology(max_base_index, max_extended_index, leaf1, &processor->topology); cpuinfo_x86_detect_topology(max_base_index, max_extended_index, leaf1, &processor->topology);
cpuinfo_isa = cpuinfo_x86_detect_isa(leaf1, leaf0x80000001, cpuinfo_isa = cpuinfo_x86_detect_isa(
max_base_index, max_extended_index, vendor, uarch); leaf1, leaf0x80000001, max_base_index, max_extended_index, vendor, uarch);
} }
if (max_extended_index >= UINT32_C(0x80000004)) { if (max_extended_index >= UINT32_C(0x80000004)) {
struct cpuid_regs brand_string[3]; struct cpuid_regs brand_string[3];

View File

@ -1,77 +1,82 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include <x86/cpuid.h>
#include <cpuinfo.h> #include <cpuinfo.h>
#include <x86/cpuid.h>
#if CPUINFO_ARCH_X86 #if CPUINFO_ARCH_X86
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma pack(push, 2) #pragma pack(push, 2)
#endif #endif
struct fxsave_region { struct fxsave_region {
uint16_t fpu_control_word; uint16_t fpu_control_word;
uint16_t fpu_status_word; uint16_t fpu_status_word;
uint16_t fpu_tag_word; uint16_t fpu_tag_word;
uint16_t fpu_opcode; uint16_t fpu_opcode;
uint32_t fpu_instruction_pointer_offset; uint32_t fpu_instruction_pointer_offset;
uint32_t fpu_instruction_pointer_selector; uint32_t fpu_instruction_pointer_selector;
uint32_t fpu_operand_pointer_offset; uint32_t fpu_operand_pointer_offset;
uint32_t fpu_operand_pointer_selector; uint32_t fpu_operand_pointer_selector;
uint32_t mxcsr_state; uint32_t mxcsr_state;
uint32_t mxcsr_mask; uint32_t mxcsr_mask;
uint64_t fpu_registers[8 * 2]; uint64_t fpu_registers[8 * 2];
uint64_t xmm_registers[8 * 2]; uint64_t xmm_registers[8 * 2];
uint64_t padding[28]; uint64_t padding[28];
} }
#ifndef _MSC_VER #ifndef _MSC_VER
__attribute__((__aligned__(16), __packed__)) __attribute__((__aligned__(16), __packed__))
#endif #endif
; /* end of fxsave_region structure */ ; /* end of fxsave_region structure */
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma pack(pop, 2) #pragma pack(pop, 2)
#endif #endif
#endif #endif
struct cpuinfo_x86_isa cpuinfo_x86_detect_isa( struct cpuinfo_x86_isa cpuinfo_x86_detect_isa(
const struct cpuid_regs basic_info, const struct cpuid_regs extended_info, const struct cpuid_regs basic_info,
uint32_t max_base_index, uint32_t max_extended_index, const struct cpuid_regs extended_info,
enum cpuinfo_vendor vendor, enum cpuinfo_uarch uarch) uint32_t max_base_index,
{ uint32_t max_extended_index,
struct cpuinfo_x86_isa isa = { 0 }; enum cpuinfo_vendor vendor,
enum cpuinfo_uarch uarch) {
struct cpuinfo_x86_isa isa = {0};
const struct cpuid_regs structured_feature_info0 = const struct cpuid_regs structured_feature_info0 =
(max_base_index >= 7) ? cpuidex(7, 0) : (struct cpuid_regs) { 0, 0, 0, 0}; (max_base_index >= 7) ? cpuidex(7, 0) : (struct cpuid_regs){0, 0, 0, 0};
const struct cpuid_regs structured_feature_info1 = const struct cpuid_regs structured_feature_info1 =
(max_base_index >= 7) ? cpuidex(7, 1) : (struct cpuid_regs) { 0, 0, 0, 0}; (max_base_index >= 7) ? cpuidex(7, 1) : (struct cpuid_regs){0, 0, 0, 0};
const uint32_t processor_capacity_info_index = UINT32_C(0x80000008); const uint32_t processor_capacity_info_index = UINT32_C(0x80000008);
const struct cpuid_regs processor_capacity_info = const struct cpuid_regs processor_capacity_info = (max_extended_index >= processor_capacity_info_index)
(max_extended_index >= processor_capacity_info_index) ? ? cpuid(processor_capacity_info_index)
cpuid(processor_capacity_info_index) : (struct cpuid_regs) { 0, 0, 0, 0 }; : (struct cpuid_regs){0, 0, 0, 0};
bool avx_regs = false, avx512_regs = false, mpx_regs = false; bool avx_regs = false, avx512_regs = false, mpx_regs = false;
/* /*
* OSXSAVE: Operating system enabled XSAVE instructions for application use: * OSXSAVE: Operating system enabled XSAVE instructions for application
* - Intel, AMD: ecx[bit 26] in basic info = XSAVE/XRSTOR instructions supported by a chip. * use:
* - Intel, AMD: ecx[bit 27] in basic info = XSAVE/XRSTOR instructions enabled by OS. * - Intel, AMD: ecx[bit 26] in basic info = XSAVE/XRSTOR instructions
* supported by a chip.
* - Intel, AMD: ecx[bit 27] in basic info = XSAVE/XRSTOR instructions
* enabled by OS.
*/ */
const uint32_t osxsave_mask = UINT32_C(0x0C000000); const uint32_t osxsave_mask = UINT32_C(0x0C000000);
if ((basic_info.ecx & osxsave_mask) == osxsave_mask) { if ((basic_info.ecx & osxsave_mask) == osxsave_mask) {
uint64_t xcr0_valid_bits = 0; uint64_t xcr0_valid_bits = 0;
if (max_base_index >= 0xD) { if (max_base_index >= 0xD) {
const struct cpuid_regs regs = cpuidex(0xD, 0); const struct cpuid_regs regs = cpuidex(0xD, 0);
xcr0_valid_bits = ((uint64_t) regs.edx << 32) | regs.eax; xcr0_valid_bits = ((uint64_t)regs.edx << 32) | regs.eax;
} }
const uint64_t xfeature_enabled_mask = xgetbv(0); const uint64_t xfeature_enabled_mask = xgetbv(0);
/* /*
* AVX registers: * AVX registers:
* - Intel, AMD: XFEATURE_ENABLED_MASK[bit 1] for low 128 bits of ymm registers * - Intel, AMD: XFEATURE_ENABLED_MASK[bit 1] for low 128 bits
* - Intel, AMD: XFEATURE_ENABLED_MASK[bit 2] for high 128 bits of ymm registers * of ymm registers
* - Intel, AMD: XFEATURE_ENABLED_MASK[bit 2] for high 128 bits
* of ymm registers
*/ */
const uint64_t avx_regs_mask = UINT64_C(0x0000000000000006); const uint64_t avx_regs_mask = UINT64_C(0x0000000000000006);
if ((xcr0_valid_bits & avx_regs_mask) == avx_regs_mask) { if ((xcr0_valid_bits & avx_regs_mask) == avx_regs_mask) {
@ -80,11 +85,16 @@ struct cpuinfo_x86_isa cpuinfo_x86_detect_isa(
/* /*
* AVX512 registers: * AVX512 registers:
* - Intel, AMD: XFEATURE_ENABLED_MASK[bit 1] for low 128 bits of zmm registers * - Intel, AMD: XFEATURE_ENABLED_MASK[bit 1] for low 128 bits
* - Intel, AMD: XFEATURE_ENABLED_MASK[bit 2] for bits 128-255 of zmm registers * of zmm registers
* - Intel: XFEATURE_ENABLED_MASK[bit 5] for 8 64-bit OpMask registers (k0-k7) * - Intel, AMD: XFEATURE_ENABLED_MASK[bit 2] for bits 128-255
* - Intel: XFEATURE_ENABLED_MASK[bit 6] for the high 256 bits of the zmm registers zmm0-zmm15 * of zmm registers
* - Intel: XFEATURE_ENABLED_MASK[bit 7] for the 512-bit zmm registers zmm16-zmm31 * - Intel: XFEATURE_ENABLED_MASK[bit 5] for 8 64-bit OpMask
* registers (k0-k7)
* - Intel: XFEATURE_ENABLED_MASK[bit 6] for the high 256 bits
* of the zmm registers zmm0-zmm15
* - Intel: XFEATURE_ENABLED_MASK[bit 7] for the 512-bit zmm
* registers zmm16-zmm31
*/ */
const uint64_t avx512_regs_mask = UINT64_C(0x00000000000000E6); const uint64_t avx512_regs_mask = UINT64_C(0x00000000000000E6);
if ((xcr0_valid_bits & avx512_regs_mask) == avx512_regs_mask) { if ((xcr0_valid_bits & avx512_regs_mask) == avx512_regs_mask) {
@ -134,7 +144,8 @@ struct cpuinfo_x86_isa cpuinfo_x86_detect_isa(
/* /*
* CLZERO instruction: * CLZERO instruction:
* - AMD: ebx[bit 0] in processor capacity info (reserved bit on Intel CPUs). * - AMD: ebx[bit 0] in processor capacity info (reserved bit on Intel
* CPUs).
*/ */
isa.clzero = !!(processor_capacity_info.ebx & UINT32_C(0x00000001)); isa.clzero = !!(processor_capacity_info.ebx & UINT32_C(0x00000001));
@ -165,7 +176,8 @@ struct cpuinfo_x86_isa cpuinfo_x86_detect_isa(
/* /*
* FXSAVE/FXRSTOR instructions: * FXSAVE/FXRSTOR instructions:
* - Intel, AMD: edx[bit 24] in basic info. * - Intel, AMD: edx[bit 24] in basic info.
* - AMD: edx[bit 24] in extended info (zero bit on Intel CPUs, EMMX bit on Cyrix CPUs). * - AMD: edx[bit 24] in extended info (zero bit on Intel CPUs, EMMX bit
* on Cyrix CPUs).
*/ */
switch (vendor) { switch (vendor) {
#if CPUINFO_ARCH_X86 #if CPUINFO_ARCH_X86
@ -230,27 +242,35 @@ struct cpuinfo_x86_isa cpuinfo_x86_detect_isa(
/* /*
* PREFETCH instruction: * PREFETCH instruction:
* - AMD: ecx[bit 8] of extended info (one of 3dnow! prefetch instructions). * - AMD: ecx[bit 8] of extended info (one of 3dnow! prefetch
* On Intel this bit indicates PREFETCHW, but not PREFETCH support. * instructions). On Intel this bit indicates PREFETCHW, but not
* - AMD: edx[bit 31] of extended info (implied by 3dnow! support). Reserved bit on Intel CPUs. * PREFETCH support.
* - AMD: edx[bit 30] of extended info (implied by 3dnow!+ support). Reserved bit on Intel CPUs. * - AMD: edx[bit 31] of extended info (implied by 3dnow! support).
* - AMD: edx[bit 29] of extended info (x86-64 support). Does not imply PREFETCH support on non-AMD CPUs!!! * Reserved bit on Intel CPUs.
* - AMD: edx[bit 30] of extended info (implied by 3dnow!+ support).
* Reserved bit on Intel CPUs.
* - AMD: edx[bit 29] of extended info (x86-64 support). Does not imply
* PREFETCH support on non-AMD CPUs!!!
*/ */
switch (vendor) { switch (vendor) {
case cpuinfo_vendor_intel: case cpuinfo_vendor_intel:
/* /*
* Instruction is not documented in the manual, * Instruction is not documented in the manual,
* and the 3dnow! prefetch CPUID bit indicates PREFETCHW instruction. * and the 3dnow! prefetch CPUID bit indicates PREFETCHW
* instruction.
*/ */
break; break;
case cpuinfo_vendor_amd: case cpuinfo_vendor_amd:
case cpuinfo_vendor_hygon: case cpuinfo_vendor_hygon:
isa.prefetch = !!((extended_info.ecx & UINT32_C(0x00000100)) | (extended_info.edx & UINT32_C(0xE0000000))); isa.prefetch =
!!((extended_info.ecx & UINT32_C(0x00000100)) |
(extended_info.edx & UINT32_C(0xE0000000)));
break; break;
default: default:
/* /*
* Conservatively assume, that 3dnow!/3dnow!+ support implies PREFETCH support, but * Conservatively assume, that 3dnow!/3dnow!+ support
* 3dnow! prefetch CPUID bit follows Intel spec (PREFETCHW, but not PREFETCH). * implies PREFETCH support, but 3dnow! prefetch CPUID
* bit follows Intel spec (PREFETCHW, but not PREFETCH).
*/ */
isa.prefetch = !!(extended_info.edx & UINT32_C(0xC0000000)); isa.prefetch = !!(extended_info.edx & UINT32_C(0xC0000000));
break; break;
@ -258,26 +278,36 @@ struct cpuinfo_x86_isa cpuinfo_x86_detect_isa(
/* /*
* PREFETCHW instruction: * PREFETCHW instruction:
* - AMD: ecx[bit 8] of extended info (one of 3dnow! prefetch instructions). * - AMD: ecx[bit 8] of extended info (one of 3dnow! prefetch
* instructions).
* - Intel: ecx[bit 8] of extended info (PREFETCHW instruction only). * - Intel: ecx[bit 8] of extended info (PREFETCHW instruction only).
* - AMD: edx[bit 31] of extended info (implied by 3dnow! support). Reserved bit on Intel CPUs. * - AMD: edx[bit 31] of extended info (implied by 3dnow! support).
* - AMD: edx[bit 30] of extended info (implied by 3dnow!+ support). Reserved bit on Intel CPUs. * Reserved bit on Intel CPUs.
* - AMD: edx[bit 29] of extended info (x86-64 support). Does not imply PREFETCHW support on non-AMD CPUs!!! * - AMD: edx[bit 30] of extended info (implied by 3dnow!+ support).
* Reserved bit on Intel CPUs.
* - AMD: edx[bit 29] of extended info (x86-64 support). Does not imply
* PREFETCHW support on non-AMD CPUs!!!
*/ */
switch (vendor) { switch (vendor) {
case cpuinfo_vendor_amd: case cpuinfo_vendor_amd:
case cpuinfo_vendor_hygon: case cpuinfo_vendor_hygon:
isa.prefetchw = !!((extended_info.ecx & UINT32_C(0x00000100)) | (extended_info.edx & UINT32_C(0xE0000000))); isa.prefetchw =
!!((extended_info.ecx & UINT32_C(0x00000100)) |
(extended_info.edx & UINT32_C(0xE0000000)));
break; break;
default: default:
/* Assume, that 3dnow!/3dnow!+ support implies PREFETCHW support, not implications from x86-64 support */ /* Assume, that 3dnow!/3dnow!+ support implies PREFETCHW
isa.prefetchw = !!((extended_info.ecx & UINT32_C(0x00000100)) | (extended_info.edx & UINT32_C(0xC0000000))); * support, not implications from x86-64 support */
isa.prefetchw =
!!((extended_info.ecx & UINT32_C(0x00000100)) |
(extended_info.edx & UINT32_C(0xC0000000)));
break; break;
} }
/* /*
* PREFETCHWT1 instruction: * PREFETCHWT1 instruction:
* - Intel: ecx[bit 0] of structured feature info (ecx = 0). Reserved bit on AMD. * - Intel: ecx[bit 0] of structured feature info (ecx = 0). Reserved
* bit on AMD.
*/ */
isa.prefetchwt1 = !!(structured_feature_info0.ecx & UINT32_C(0x00000001)); isa.prefetchwt1 = !!(structured_feature_info0.ecx & UINT32_C(0x00000001));
@ -311,12 +341,12 @@ struct cpuinfo_x86_isa cpuinfo_x86_detect_isa(
} else { } else {
/* Detect DAZ support from masked MXCSR bits */ /* Detect DAZ support from masked MXCSR bits */
if (isa.sse && isa.fxsave) { if (isa.sse && isa.fxsave) {
struct fxsave_region region = { 0 }; struct fxsave_region region = {0};
#ifdef _MSC_VER #ifdef _MSC_VER
_fxsave(&region); _fxsave(&region);
#else #else
__asm__ __volatile__ ("fxsave %[region];" : [region] "+m" (region)); __asm__ __volatile__("fxsave %[region];" : [region] "+m"(region));
#endif #endif
/* /*
* Denormals-as-zero (DAZ) flag: * Denormals-as-zero (DAZ) flag:
@ -333,7 +363,6 @@ struct cpuinfo_x86_isa cpuinfo_x86_detect_isa(
*/ */
isa.ssse3 = !!(basic_info.ecx & UINT32_C(0x0000200)); isa.ssse3 = !!(basic_info.ecx & UINT32_C(0x0000200));
/* /*
* SSE4.1 instructions: * SSE4.1 instructions:
* - Intel, AMD: ecx[bit 19] in basic info. * - Intel, AMD: ecx[bit 19] in basic info.
@ -508,6 +537,48 @@ struct cpuinfo_x86_isa cpuinfo_x86_detect_isa(
*/ */
isa.avx512bf16 = avx512_regs && !!(structured_feature_info1.eax & UINT32_C(0x00000020)); isa.avx512bf16 = avx512_regs && !!(structured_feature_info1.eax & UINT32_C(0x00000020));
/*
* AMX_BF16 instructions:
* - Intel: edx[bit 22] in structured feature info (ecx = 0).
*/
isa.amx_bf16 = avx512_regs && !!(structured_feature_info0.edx & UINT32_C(0x00400000));
/*
* AMX_TILE instructions:
* - Intel: edx[bit 24] in structured feature info (ecx = 0).
*/
isa.amx_tile = avx512_regs && !!(structured_feature_info0.edx & UINT32_C(0x01000000));
/*
* AMX_INT8 instructions:
* - Intel: edx[bit 25] in structured feature info (ecx = 0).
*/
isa.amx_int8 = avx512_regs && !!(structured_feature_info0.edx & UINT32_C(0x02000000));
/*
* AMX_FP16 instructions:
* - Intel: eax[bit 21] in structured feature info (ecx = 1).
*/
isa.amx_fp16 = avx512_regs && !!(structured_feature_info1.eax & UINT32_C(0x00200000));
/*
* AVX_VNNI_INT8 instructions:
* - Intel: edx[bit 4] in structured feature info (ecx = 1).
*/
isa.avx_vnni_int8 = avx_regs && !!(structured_feature_info1.edx & UINT32_C(0x00000010));
/*
* AVX_VNNI_INT16 instructions:
* - Intel: edx[bit 10] in structured feature info (ecx = 1).
*/
isa.avx_vnni_int16 = avx_regs && !!(structured_feature_info1.edx & UINT32_C(0x00000400));
/*
* AVX_NE_CONVERT instructions:
* - Intel: edx[bit 5] in structured feature info (ecx = 1).
*/
isa.avx_ne_convert = avx_regs && !!(structured_feature_info1.edx & UINT32_C(0x00000020));
/* /*
* HLE instructions: * HLE instructions:
* - Intel: ebx[bit 4] in structured feature info (ecx = 0). * - Intel: ebx[bit 4] in structured feature info (ecx = 0).
@ -569,7 +640,8 @@ struct cpuinfo_x86_isa cpuinfo_x86_detect_isa(
#if CPUINFO_ARCH_X86_64 #if CPUINFO_ARCH_X86_64
/* /*
* Some early x86-64 CPUs lack LAHF & SAHF instructions. * Some early x86-64 CPUs lack LAHF & SAHF instructions.
* A special CPU feature bit must be checked to ensure their availability: * A special CPU feature bit must be checked to ensure their
* availability:
* - Intel, AMD: ecx[bit 0] in extended info. * - Intel, AMD: ecx[bit 0] in extended info.
*/ */
isa.lahf_sahf = !!(extended_info.ecx & UINT32_C(0x00000001)); isa.lahf_sahf = !!(extended_info.ecx & UINT32_C(0x00000001));
@ -674,40 +746,50 @@ struct cpuinfo_x86_isa cpuinfo_x86_detect_isa(
/* /*
* Padlock RNG extension: * Padlock RNG extension:
* - VIA: edx[bit 2] in padlock info = RNG exists on chip flag. * - VIA: edx[bit 2] in padlock info = RNG exists on
* - VIA: edx[bit 3] in padlock info = RNG enabled by OS. * chip flag.
* - VIA: edx[bit 3] in padlock info = RNG enabled by
* OS.
*/ */
const uint32_t padlock_rng_mask = UINT32_C(0x0000000C); const uint32_t padlock_rng_mask = UINT32_C(0x0000000C);
isa.rng = (padlock_info.edx & padlock_rng_mask) == padlock_rng_mask; isa.rng = (padlock_info.edx & padlock_rng_mask) == padlock_rng_mask;
/* /*
* Padlock ACE extension: * Padlock ACE extension:
* - VIA: edx[bit 6] in padlock info = ACE exists on chip flag. * - VIA: edx[bit 6] in padlock info = ACE exists on
* - VIA: edx[bit 7] in padlock info = ACE enabled by OS. * chip flag.
* - VIA: edx[bit 7] in padlock info = ACE enabled by
* OS.
*/ */
const uint32_t padlock_ace_mask = UINT32_C(0x000000C0); const uint32_t padlock_ace_mask = UINT32_C(0x000000C0);
isa.ace = (padlock_info.edx & padlock_ace_mask) == padlock_ace_mask; isa.ace = (padlock_info.edx & padlock_ace_mask) == padlock_ace_mask;
/* /*
* Padlock ACE 2 extension: * Padlock ACE 2 extension:
* - VIA: edx[bit 8] in padlock info = ACE2 exists on chip flag. * - VIA: edx[bit 8] in padlock info = ACE2 exists on
* - VIA: edx[bit 9] in padlock info = ACE 2 enabled by OS. * chip flag.
* - VIA: edx[bit 9] in padlock info = ACE 2 enabled by
* OS.
*/ */
const uint32_t padlock_ace2_mask = UINT32_C(0x00000300); const uint32_t padlock_ace2_mask = UINT32_C(0x00000300);
isa.ace2 = (padlock_info.edx & padlock_ace2_mask) == padlock_ace2_mask; isa.ace2 = (padlock_info.edx & padlock_ace2_mask) == padlock_ace2_mask;
/* /*
* Padlock PHE extension: * Padlock PHE extension:
* - VIA: edx[bit 10] in padlock info = PHE exists on chip flag. * - VIA: edx[bit 10] in padlock info = PHE exists on
* - VIA: edx[bit 11] in padlock info = PHE enabled by OS. * chip flag.
* - VIA: edx[bit 11] in padlock info = PHE enabled by
* OS.
*/ */
const uint32_t padlock_phe_mask = UINT32_C(0x00000C00); const uint32_t padlock_phe_mask = UINT32_C(0x00000C00);
isa.phe = (padlock_info.edx & padlock_phe_mask) == padlock_phe_mask; isa.phe = (padlock_info.edx & padlock_phe_mask) == padlock_phe_mask;
/* /*
* Padlock PMM extension: * Padlock PMM extension:
* - VIA: edx[bit 12] in padlock info = PMM exists on chip flag. * - VIA: edx[bit 12] in padlock info = PMM exists on
* - VIA: edx[bit 13] in padlock info = PMM enabled by OS. * chip flag.
* - VIA: edx[bit 13] in padlock info = PMM enabled by
* OS.
*/ */
const uint32_t padlock_pmm_mask = UINT32_C(0x00003000); const uint32_t padlock_pmm_mask = UINT32_C(0x00003000);
isa.pmm = (padlock_info.edx & padlock_pmm_mask) == padlock_pmm_mask; isa.pmm = (padlock_info.edx & padlock_pmm_mask) == padlock_pmm_mask;

View File

@ -5,9 +5,8 @@
#include <cpuinfo.h> #include <cpuinfo.h>
#include <cpuinfo/common.h> #include <cpuinfo/common.h>
#include <x86/api.h>
#include <linux/api.h> #include <linux/api.h>
#include <x86/api.h>
struct cpuinfo_x86_linux_processor { struct cpuinfo_x86_linux_processor {
uint32_t apic_id; uint32_t apic_id;

View File

@ -1,25 +1,21 @@
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <stddef.h>
#include <string.h> #include <string.h>
#include <cpuinfo/log.h>
#include <linux/api.h> #include <linux/api.h>
#include <x86/linux/api.h> #include <x86/linux/api.h>
#include <cpuinfo/log.h>
/* /*
* Size, in chars, of the on-stack buffer used for parsing lines of /proc/cpuinfo. * Size, in chars, of the on-stack buffer used for parsing lines of
* This is also the limit on the length of a single line. * /proc/cpuinfo. This is also the limit on the length of a single line.
*/ */
#define BUFFER_SIZE 2048 #define BUFFER_SIZE 2048
static uint32_t parse_processor_number(const char* processor_start, const char* processor_end) {
static uint32_t parse_processor_number( const size_t processor_length = (size_t)(processor_end - processor_start);
const char* processor_start,
const char* processor_end)
{
const size_t processor_length = (size_t) (processor_end - processor_start);
if (processor_length == 0) { if (processor_length == 0) {
cpuinfo_log_warning("Processor number in /proc/cpuinfo is ignored: string is empty"); cpuinfo_log_warning("Processor number in /proc/cpuinfo is ignored: string is empty");
@ -28,10 +24,12 @@ static uint32_t parse_processor_number(
uint32_t processor_number = 0; uint32_t processor_number = 0;
for (const char* digit_ptr = processor_start; digit_ptr != processor_end; digit_ptr++) { for (const char* digit_ptr = processor_start; digit_ptr != processor_end; digit_ptr++) {
const uint32_t digit = (uint32_t) (*digit_ptr - '0'); const uint32_t digit = (uint32_t)(*digit_ptr - '0');
if (digit > 10) { if (digit > 10) {
cpuinfo_log_warning("non-decimal suffix %.*s in /proc/cpuinfo processor number is ignored", cpuinfo_log_warning(
(int) (processor_end - digit_ptr), digit_ptr); "non-decimal suffix %.*s in /proc/cpuinfo processor number is ignored",
(int)(processor_end - digit_ptr),
digit_ptr);
break; break;
} }
@ -50,15 +48,17 @@ static uint32_t parse_processor_number(
static void parse_apic_id( static void parse_apic_id(
const char* apic_start, const char* apic_start,
const char* apic_end, const char* apic_end,
struct cpuinfo_x86_linux_processor processor[restrict static 1]) struct cpuinfo_x86_linux_processor processor[restrict static 1]) {
{
uint32_t apic_id = 0; uint32_t apic_id = 0;
for (const char* digit_ptr = apic_start; digit_ptr != apic_end; digit_ptr++) { for (const char* digit_ptr = apic_start; digit_ptr != apic_end; digit_ptr++) {
const uint32_t digit = *digit_ptr - '0'; const uint32_t digit = *digit_ptr - '0';
if (digit >= 10) { if (digit >= 10) {
cpuinfo_log_warning("APIC ID %.*s in /proc/cpuinfo is ignored due to unexpected non-digit character '%c' at offset %zu", cpuinfo_log_warning(
(int) (apic_end - apic_start), apic_start, "APIC ID %.*s in /proc/cpuinfo is ignored due to unexpected non-digit character '%c' at offset %zu",
*digit_ptr, (size_t) (digit_ptr - apic_start)); (int)(apic_end - apic_start),
apic_start,
*digit_ptr,
(size_t)(digit_ptr - apic_start));
return; return;
} }
@ -84,8 +84,7 @@ static bool parse_line(
const char* line_start, const char* line_start,
const char* line_end, const char* line_end,
struct proc_cpuinfo_parser_state state[restrict static 1], struct proc_cpuinfo_parser_state state[restrict static 1],
uint64_t line_number) uint64_t line_number) {
{
/* Empty line. Skip. */ /* Empty line. Skip. */
if (line_start == line_end) { if (line_start == line_end) {
return true; return true;
@ -100,8 +99,10 @@ static bool parse_line(
} }
/* Skip line if no ':' separator was found. */ /* Skip line if no ':' separator was found. */
if (separator == line_end) { if (separator == line_end) {
cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: key/value separator ':' not found", cpuinfo_log_debug(
(int) (line_end - line_start), line_start); "Line %.*s in /proc/cpuinfo is ignored: key/value separator ':' not found",
(int)(line_end - line_start),
line_start);
return true; return true;
} }
@ -114,8 +115,10 @@ static bool parse_line(
} }
/* Skip line if key contains nothing but spaces. */ /* Skip line if key contains nothing but spaces. */
if (key_end == line_start) { if (key_end == line_start) {
cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: key contains only spaces", cpuinfo_log_debug(
(int) (line_end - line_start), line_start); "Line %.*s in /proc/cpuinfo is ignored: key contains only spaces",
(int)(line_end - line_start),
line_start);
return true; return true;
} }
@ -128,8 +131,10 @@ static bool parse_line(
} }
/* Value part contains nothing but spaces. Skip line. */ /* Value part contains nothing but spaces. Skip line. */
if (value_start == line_end) { if (value_start == line_end) {
cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: value contains only spaces", cpuinfo_log_debug(
(int) (line_end - line_start), line_start); "Line %.*s in /proc/cpuinfo is ignored: value contains only spaces",
(int)(line_end - line_start),
line_start);
return true; return true;
} }
@ -141,10 +146,10 @@ static bool parse_line(
} }
} }
const uint32_t processor_index = state->processor_index; const uint32_t processor_index = state->processor_index;
const uint32_t max_processors_count = state->max_processors_count; const uint32_t max_processors_count = state->max_processors_count;
struct cpuinfo_x86_linux_processor* processors = state->processors; struct cpuinfo_x86_linux_processor* processors = state->processors;
struct cpuinfo_x86_linux_processor* processor = &state->dummy_processor; struct cpuinfo_x86_linux_processor* processor = &state->dummy_processor;
if (processor_index < max_processors_count) { if (processor_index < max_processors_count) {
processor = &processors[processor_index]; processor = &processors[processor_index];
} }
@ -162,20 +167,29 @@ static bool parse_line(
if (memcmp(line_start, "processor", key_length) == 0) { if (memcmp(line_start, "processor", key_length) == 0) {
const uint32_t new_processor_index = parse_processor_number(value_start, value_end); const uint32_t new_processor_index = parse_processor_number(value_start, value_end);
if (new_processor_index < processor_index) { if (new_processor_index < processor_index) {
/* Strange: decreasing processor number */ /* Strange: decreasing processor number
*/
cpuinfo_log_warning( cpuinfo_log_warning(
"unexpectedly low processor number %"PRIu32" following processor %"PRIu32" in /proc/cpuinfo", "unexpectedly low processor number %" PRIu32
new_processor_index, processor_index); " following processor %" PRIu32 " in /proc/cpuinfo",
new_processor_index,
processor_index);
} else if (new_processor_index > processor_index + 1) { } else if (new_processor_index > processor_index + 1) {
/* Strange, but common: skipped processor $(processor_index + 1) */ /* Strange, but common: skipped
cpuinfo_log_info( * processor $(processor_index + 1) */
"unexpectedly high processor number %"PRIu32" following processor %"PRIu32" in /proc/cpuinfo", cpuinfo_log_warning(
new_processor_index, processor_index); "unexpectedly high processor number %" PRIu32
" following processor %" PRIu32 " in /proc/cpuinfo",
new_processor_index,
processor_index);
} }
if (new_processor_index >= max_processors_count) { if (new_processor_index >= max_processors_count) {
/* Log and ignore processor */ /* Log and ignore processor */
cpuinfo_log_warning("processor %"PRIu32" in /proc/cpuinfo is ignored: index exceeds system limit %"PRIu32, cpuinfo_log_warning(
new_processor_index, max_processors_count - 1); "processor %" PRIu32
" in /proc/cpuinfo is ignored: index exceeds system limit %" PRIu32,
new_processor_index,
max_processors_count - 1);
} else { } else {
processors[new_processor_index].flags |= CPUINFO_LINUX_FLAG_PROC_CPUINFO; processors[new_processor_index].flags |= CPUINFO_LINUX_FLAG_PROC_CPUINFO;
} }
@ -187,21 +201,19 @@ static bool parse_line(
break; break;
default: default:
unknown: unknown:
cpuinfo_log_debug("unknown /proc/cpuinfo key: %.*s", (int) key_length, line_start); cpuinfo_log_debug("unknown /proc/cpuinfo key: %.*s", (int)key_length, line_start);
} }
return true; return true;
} }
bool cpuinfo_x86_linux_parse_proc_cpuinfo( bool cpuinfo_x86_linux_parse_proc_cpuinfo(
uint32_t max_processors_count, uint32_t max_processors_count,
struct cpuinfo_x86_linux_processor processors[restrict static max_processors_count]) struct cpuinfo_x86_linux_processor processors[restrict static max_processors_count]) {
{
struct proc_cpuinfo_parser_state state = { struct proc_cpuinfo_parser_state state = {
.processor_index = 0, .processor_index = 0,
.max_processors_count = max_processors_count, .max_processors_count = max_processors_count,
.processors = processors, .processors = processors,
}; };
return cpuinfo_linux_parse_multiline_file("/proc/cpuinfo", BUFFER_SIZE, return cpuinfo_linux_parse_multiline_file(
(cpuinfo_line_callback) parse_line, &state); "/proc/cpuinfo", BUFFER_SIZE, (cpuinfo_line_callback)parse_line, &state);
} }

View File

@ -1,15 +1,14 @@
#include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <cpuinfo.h> #include <cpuinfo.h>
#include <x86/api.h>
#include <x86/linux/api.h>
#include <linux/api.h>
#include <cpuinfo/internal-api.h> #include <cpuinfo/internal-api.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <linux/api.h>
#include <x86/api.h>
#include <x86/linux/api.h>
static inline uint32_t bit_mask(uint32_t bits) { static inline uint32_t bit_mask(uint32_t bits) {
return (UINT32_C(1) << bits) - UINT32_C(1); return (UINT32_C(1) << bits) - UINT32_C(1);
@ -28,14 +27,14 @@ static inline int cmp(uint32_t a, uint32_t b) {
} }
static int cmp_x86_linux_processor(const void* ptr_a, const void* ptr_b) { static int cmp_x86_linux_processor(const void* ptr_a, const void* ptr_b) {
const struct cpuinfo_x86_linux_processor* processor_a = (const struct cpuinfo_x86_linux_processor*) ptr_a; const struct cpuinfo_x86_linux_processor* processor_a = (const struct cpuinfo_x86_linux_processor*)ptr_a;
const struct cpuinfo_x86_linux_processor* processor_b = (const struct cpuinfo_x86_linux_processor*) ptr_b; const struct cpuinfo_x86_linux_processor* processor_b = (const struct cpuinfo_x86_linux_processor*)ptr_b;
/* Move usable processors towards the start of the array */ /* Move usable processors towards the start of the array */
const bool usable_a = bitmask_all(processor_a->flags, CPUINFO_LINUX_FLAG_VALID); const bool usable_a = bitmask_all(processor_a->flags, CPUINFO_LINUX_FLAG_VALID);
const bool usable_b = bitmask_all(processor_b->flags, CPUINFO_LINUX_FLAG_VALID); const bool usable_b = bitmask_all(processor_b->flags, CPUINFO_LINUX_FLAG_VALID);
if (usable_a != usable_b) { if (usable_a != usable_b) {
return (int) usable_b - (int) usable_a; return (int)usable_b - (int)usable_a;
} }
/* Compare based on APIC ID (i.e. processor 0 < processor 1) */ /* Compare based on APIC ID (i.e. processor 0 < processor 1) */
@ -57,12 +56,11 @@ static void cpuinfo_x86_count_objects(
uint32_t l1d_count_ptr[restrict static 1], uint32_t l1d_count_ptr[restrict static 1],
uint32_t l2_count_ptr[restrict static 1], uint32_t l2_count_ptr[restrict static 1],
uint32_t l3_count_ptr[restrict static 1], uint32_t l3_count_ptr[restrict static 1],
uint32_t l4_count_ptr[restrict static 1]) uint32_t l4_count_ptr[restrict static 1]) {
{
const uint32_t core_apic_mask = const uint32_t core_apic_mask =
~(bit_mask(processor->topology.thread_bits_length) << processor->topology.thread_bits_offset); ~(bit_mask(processor->topology.thread_bits_length) << processor->topology.thread_bits_offset);
const uint32_t package_apic_mask = const uint32_t package_apic_mask = core_apic_mask &
core_apic_mask & ~(bit_mask(processor->topology.core_bits_length) << processor->topology.core_bits_offset); ~(bit_mask(processor->topology.core_bits_length) << processor->topology.core_bits_offset);
const uint32_t llc_apic_mask = ~bit_mask(llc_apic_bits); const uint32_t llc_apic_mask = ~bit_mask(llc_apic_bits);
const uint32_t cluster_apic_mask = package_apic_mask | llc_apic_mask; const uint32_t cluster_apic_mask = package_apic_mask | llc_apic_mask;
@ -74,7 +72,10 @@ static void cpuinfo_x86_count_objects(
for (uint32_t i = 0; i < linux_processors_count; i++) { for (uint32_t i = 0; i < linux_processors_count; i++) {
if (bitmask_all(linux_processors[i].flags, valid_processor_mask)) { if (bitmask_all(linux_processors[i].flags, valid_processor_mask)) {
const uint32_t apic_id = linux_processors[i].apic_id; const uint32_t apic_id = linux_processors[i].apic_id;
cpuinfo_log_debug("APID ID %"PRIu32": system processor %"PRIu32, apic_id, linux_processors[i].linux_id); cpuinfo_log_debug(
"APID ID %" PRIu32 ": system processor %" PRIu32,
apic_id,
linux_processors[i].linux_id);
/* All bits of APIC ID except thread ID mask */ /* All bits of APIC ID except thread ID mask */
const uint32_t core_id = apic_id & core_apic_mask; const uint32_t core_id = apic_id & core_apic_mask;
@ -82,13 +83,15 @@ static void cpuinfo_x86_count_objects(
last_core_id = core_id; last_core_id = core_id;
cores_count++; cores_count++;
} }
/* All bits of APIC ID except thread ID and core ID masks */ /* All bits of APIC ID except thread ID and core ID
* masks */
const uint32_t package_id = apic_id & package_apic_mask; const uint32_t package_id = apic_id & package_apic_mask;
if (package_id != last_package_id) { if (package_id != last_package_id) {
last_package_id = package_id; last_package_id = package_id;
packages_count++; packages_count++;
} }
/* Bits of APIC ID which are part of either LLC or package ID mask */ /* Bits of APIC ID which are part of either LLC or
* package ID mask */
const uint32_t cluster_id = apic_id & cluster_apic_mask; const uint32_t cluster_id = apic_id & cluster_apic_mask;
if (cluster_id != last_cluster_id) { if (cluster_id != last_cluster_id) {
last_cluster_id = cluster_id; last_cluster_id = cluster_id;
@ -136,9 +139,9 @@ static void cpuinfo_x86_count_objects(
*packages_count_ptr = packages_count; *packages_count_ptr = packages_count;
*l1i_count_ptr = l1i_count; *l1i_count_ptr = l1i_count;
*l1d_count_ptr = l1d_count; *l1d_count_ptr = l1d_count;
*l2_count_ptr = l2_count; *l2_count_ptr = l2_count;
*l3_count_ptr = l3_count; *l3_count_ptr = l3_count;
*l4_count_ptr = l4_count; *l4_count_ptr = l4_count;
} }
void cpuinfo_x86_linux_init(void) { void cpuinfo_x86_linux_init(void) {
@ -156,14 +159,13 @@ void cpuinfo_x86_linux_init(void) {
struct cpuinfo_cache* l4 = NULL; struct cpuinfo_cache* l4 = NULL;
const uint32_t max_processors_count = cpuinfo_linux_get_max_processors_count(); const uint32_t max_processors_count = cpuinfo_linux_get_max_processors_count();
cpuinfo_log_debug("system maximum processors count: %"PRIu32, max_processors_count); cpuinfo_log_debug("system maximum processors count: %" PRIu32, max_processors_count);
const uint32_t max_possible_processors_count = 1 + const uint32_t max_possible_processors_count =
cpuinfo_linux_get_max_possible_processor(max_processors_count); 1 + cpuinfo_linux_get_max_possible_processor(max_processors_count);
cpuinfo_log_debug("maximum possible processors count: %"PRIu32, max_possible_processors_count); cpuinfo_log_debug("maximum possible processors count: %" PRIu32, max_possible_processors_count);
const uint32_t max_present_processors_count = 1 + const uint32_t max_present_processors_count = 1 + cpuinfo_linux_get_max_present_processor(max_processors_count);
cpuinfo_linux_get_max_present_processor(max_processors_count); cpuinfo_log_debug("maximum present processors count: %" PRIu32, max_present_processors_count);
cpuinfo_log_debug("maximum present processors count: %"PRIu32, max_present_processors_count);
uint32_t valid_processor_mask = 0; uint32_t valid_processor_mask = 0;
uint32_t x86_linux_processors_count = max_processors_count; uint32_t x86_linux_processors_count = max_processors_count;
@ -181,7 +183,7 @@ void cpuinfo_x86_linux_init(void) {
x86_linux_processors = calloc(x86_linux_processors_count, sizeof(struct cpuinfo_x86_linux_processor)); x86_linux_processors = calloc(x86_linux_processors_count, sizeof(struct cpuinfo_x86_linux_processor));
if (x86_linux_processors == NULL) { if (x86_linux_processors == NULL) {
cpuinfo_log_error( cpuinfo_log_error(
"failed to allocate %zu bytes for descriptions of %"PRIu32" x86 logical processors", "failed to allocate %zu bytes for descriptions of %" PRIu32 " x86 logical processors",
x86_linux_processors_count * sizeof(struct cpuinfo_x86_linux_processor), x86_linux_processors_count * sizeof(struct cpuinfo_x86_linux_processor),
x86_linux_processors_count); x86_linux_processors_count);
return; return;
@ -189,14 +191,16 @@ void cpuinfo_x86_linux_init(void) {
if (max_possible_processors_count != 0) { if (max_possible_processors_count != 0) {
cpuinfo_linux_detect_possible_processors( cpuinfo_linux_detect_possible_processors(
x86_linux_processors_count, &x86_linux_processors->flags, x86_linux_processors_count,
&x86_linux_processors->flags,
sizeof(struct cpuinfo_x86_linux_processor), sizeof(struct cpuinfo_x86_linux_processor),
CPUINFO_LINUX_FLAG_POSSIBLE); CPUINFO_LINUX_FLAG_POSSIBLE);
} }
if (max_present_processors_count != 0) { if (max_present_processors_count != 0) {
cpuinfo_linux_detect_present_processors( cpuinfo_linux_detect_present_processors(
x86_linux_processors_count, &x86_linux_processors->flags, x86_linux_processors_count,
&x86_linux_processors->flags,
sizeof(struct cpuinfo_x86_linux_processor), sizeof(struct cpuinfo_x86_linux_processor),
CPUINFO_LINUX_FLAG_PRESENT); CPUINFO_LINUX_FLAG_PRESENT);
} }
@ -226,13 +230,17 @@ void cpuinfo_x86_linux_init(void) {
} }
} }
qsort(x86_linux_processors, x86_linux_processors_count, sizeof(struct cpuinfo_x86_linux_processor), qsort(x86_linux_processors,
cmp_x86_linux_processor); x86_linux_processors_count,
sizeof(struct cpuinfo_x86_linux_processor),
cmp_x86_linux_processor);
processors = calloc(processors_count, sizeof(struct cpuinfo_processor)); processors = calloc(processors_count, sizeof(struct cpuinfo_processor));
if (processors == NULL) { if (processors == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" logical processors", cpuinfo_log_error(
processors_count * sizeof(struct cpuinfo_processor), processors_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " logical processors",
processors_count * sizeof(struct cpuinfo_processor),
processors_count);
goto cleanup; goto cleanup;
} }
@ -249,21 +257,33 @@ void cpuinfo_x86_linux_init(void) {
uint32_t packages_count = 0, clusters_count = 0, cores_count = 0; uint32_t packages_count = 0, clusters_count = 0, cores_count = 0;
uint32_t l1i_count = 0, l1d_count = 0, l2_count = 0, l3_count = 0, l4_count = 0; uint32_t l1i_count = 0, l1d_count = 0, l2_count = 0, l3_count = 0, l4_count = 0;
cpuinfo_x86_count_objects( cpuinfo_x86_count_objects(
x86_linux_processors_count, x86_linux_processors, &x86_processor, valid_processor_mask, llc_apic_bits, x86_linux_processors_count,
&cores_count, &clusters_count, &packages_count, &l1i_count, &l1d_count, &l2_count, &l3_count, &l4_count); x86_linux_processors,
&x86_processor,
valid_processor_mask,
llc_apic_bits,
&cores_count,
&clusters_count,
&packages_count,
&l1i_count,
&l1d_count,
&l2_count,
&l3_count,
&l4_count);
cpuinfo_log_debug("detected %"PRIu32" cores", cores_count); cpuinfo_log_debug("detected %" PRIu32 " cores", cores_count);
cpuinfo_log_debug("detected %"PRIu32" clusters", clusters_count); cpuinfo_log_debug("detected %" PRIu32 " clusters", clusters_count);
cpuinfo_log_debug("detected %"PRIu32" packages", packages_count); cpuinfo_log_debug("detected %" PRIu32 " packages", packages_count);
cpuinfo_log_debug("detected %"PRIu32" L1I caches", l1i_count); cpuinfo_log_debug("detected %" PRIu32 " L1I caches", l1i_count);
cpuinfo_log_debug("detected %"PRIu32" L1D caches", l1d_count); cpuinfo_log_debug("detected %" PRIu32 " L1D caches", l1d_count);
cpuinfo_log_debug("detected %"PRIu32" L2 caches", l2_count); cpuinfo_log_debug("detected %" PRIu32 " L2 caches", l2_count);
cpuinfo_log_debug("detected %"PRIu32" L3 caches", l3_count); cpuinfo_log_debug("detected %" PRIu32 " L3 caches", l3_count);
cpuinfo_log_debug("detected %"PRIu32" L4 caches", l4_count); cpuinfo_log_debug("detected %" PRIu32 " L4 caches", l4_count);
linux_cpu_to_processor_map = calloc(x86_linux_processors_count, sizeof(struct cpuinfo_processor*)); linux_cpu_to_processor_map = calloc(x86_linux_processors_count, sizeof(struct cpuinfo_processor*));
if (linux_cpu_to_processor_map == NULL) { if (linux_cpu_to_processor_map == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for mapping entries of %"PRIu32" logical processors", cpuinfo_log_error(
"failed to allocate %zu bytes for mapping entries of %" PRIu32 " logical processors",
x86_linux_processors_count * sizeof(struct cpuinfo_processor*), x86_linux_processors_count * sizeof(struct cpuinfo_processor*),
x86_linux_processors_count); x86_linux_processors_count);
goto cleanup; goto cleanup;
@ -271,7 +291,8 @@ void cpuinfo_x86_linux_init(void) {
linux_cpu_to_core_map = calloc(x86_linux_processors_count, sizeof(struct cpuinfo_core*)); linux_cpu_to_core_map = calloc(x86_linux_processors_count, sizeof(struct cpuinfo_core*));
if (linux_cpu_to_core_map == NULL) { if (linux_cpu_to_core_map == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for mapping entries of %"PRIu32" cores", cpuinfo_log_error(
"failed to allocate %zu bytes for mapping entries of %" PRIu32 " cores",
x86_linux_processors_count * sizeof(struct cpuinfo_core*), x86_linux_processors_count * sizeof(struct cpuinfo_core*),
x86_linux_processors_count); x86_linux_processors_count);
goto cleanup; goto cleanup;
@ -279,75 +300,93 @@ void cpuinfo_x86_linux_init(void) {
cores = calloc(cores_count, sizeof(struct cpuinfo_core)); cores = calloc(cores_count, sizeof(struct cpuinfo_core));
if (cores == NULL) { if (cores == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" cores", cpuinfo_log_error(
cores_count * sizeof(struct cpuinfo_core), cores_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " cores",
cores_count * sizeof(struct cpuinfo_core),
cores_count);
goto cleanup; goto cleanup;
} }
clusters = calloc(clusters_count, sizeof(struct cpuinfo_cluster)); clusters = calloc(clusters_count, sizeof(struct cpuinfo_cluster));
if (clusters == NULL) { if (clusters == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" core clusters", cpuinfo_log_error(
clusters_count * sizeof(struct cpuinfo_cluster), clusters_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " core clusters",
clusters_count * sizeof(struct cpuinfo_cluster),
clusters_count);
goto cleanup; goto cleanup;
} }
packages = calloc(packages_count, sizeof(struct cpuinfo_package)); packages = calloc(packages_count, sizeof(struct cpuinfo_package));
if (packages == NULL) { if (packages == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" physical packages", cpuinfo_log_error(
packages_count * sizeof(struct cpuinfo_package), packages_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " physical packages",
packages_count * sizeof(struct cpuinfo_package),
packages_count);
goto cleanup; goto cleanup;
} }
if (l1i_count != 0) { if (l1i_count != 0) {
l1i = calloc(l1i_count, sizeof(struct cpuinfo_cache)); l1i = calloc(l1i_count, sizeof(struct cpuinfo_cache));
if (l1i == NULL) { if (l1i == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L1I caches", cpuinfo_log_error(
l1i_count * sizeof(struct cpuinfo_cache), l1i_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L1I caches",
l1i_count * sizeof(struct cpuinfo_cache),
l1i_count);
goto cleanup; goto cleanup;
} }
} }
if (l1d_count != 0) { if (l1d_count != 0) {
l1d = calloc(l1d_count, sizeof(struct cpuinfo_cache)); l1d = calloc(l1d_count, sizeof(struct cpuinfo_cache));
if (l1d == NULL) { if (l1d == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L1D caches", cpuinfo_log_error(
l1d_count * sizeof(struct cpuinfo_cache), l1d_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L1D caches",
l1d_count * sizeof(struct cpuinfo_cache),
l1d_count);
goto cleanup; goto cleanup;
} }
} }
if (l2_count != 0) { if (l2_count != 0) {
l2 = calloc(l2_count, sizeof(struct cpuinfo_cache)); l2 = calloc(l2_count, sizeof(struct cpuinfo_cache));
if (l2 == NULL) { if (l2 == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L2 caches", cpuinfo_log_error(
l2_count * sizeof(struct cpuinfo_cache), l2_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L2 caches",
l2_count * sizeof(struct cpuinfo_cache),
l2_count);
goto cleanup; goto cleanup;
} }
} }
if (l3_count != 0) { if (l3_count != 0) {
l3 = calloc(l3_count, sizeof(struct cpuinfo_cache)); l3 = calloc(l3_count, sizeof(struct cpuinfo_cache));
if (l3 == NULL) { if (l3 == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L3 caches", cpuinfo_log_error(
l3_count * sizeof(struct cpuinfo_cache), l3_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L3 caches",
l3_count * sizeof(struct cpuinfo_cache),
l3_count);
goto cleanup; goto cleanup;
} }
} }
if (l4_count != 0) { if (l4_count != 0) {
l4 = calloc(l4_count, sizeof(struct cpuinfo_cache)); l4 = calloc(l4_count, sizeof(struct cpuinfo_cache));
if (l4 == NULL) { if (l4 == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L4 caches", cpuinfo_log_error(
l4_count * sizeof(struct cpuinfo_cache), l4_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L4 caches",
l4_count * sizeof(struct cpuinfo_cache),
l4_count);
goto cleanup; goto cleanup;
} }
} }
const uint32_t core_apic_mask = const uint32_t core_apic_mask =
~(bit_mask(x86_processor.topology.thread_bits_length) << x86_processor.topology.thread_bits_offset); ~(bit_mask(x86_processor.topology.thread_bits_length) << x86_processor.topology.thread_bits_offset);
const uint32_t package_apic_mask = const uint32_t package_apic_mask = core_apic_mask &
core_apic_mask & ~(bit_mask(x86_processor.topology.core_bits_length) << x86_processor.topology.core_bits_offset); ~(bit_mask(x86_processor.topology.core_bits_length) << x86_processor.topology.core_bits_offset);
const uint32_t llc_apic_mask = ~bit_mask(llc_apic_bits); const uint32_t llc_apic_mask = ~bit_mask(llc_apic_bits);
const uint32_t cluster_apic_mask = package_apic_mask | llc_apic_mask; const uint32_t cluster_apic_mask = package_apic_mask | llc_apic_mask;
uint32_t processor_index = UINT32_MAX, core_index = UINT32_MAX, cluster_index = UINT32_MAX, package_index = UINT32_MAX; uint32_t processor_index = UINT32_MAX, core_index = UINT32_MAX, cluster_index = UINT32_MAX,
uint32_t l1i_index = UINT32_MAX, l1d_index = UINT32_MAX, l2_index = UINT32_MAX, l3_index = UINT32_MAX, l4_index = UINT32_MAX; package_index = UINT32_MAX;
uint32_t l1i_index = UINT32_MAX, l1d_index = UINT32_MAX, l2_index = UINT32_MAX, l3_index = UINT32_MAX,
l4_index = UINT32_MAX;
uint32_t cluster_id = 0, core_id = 0, smt_id = 0; uint32_t cluster_id = 0, core_id = 0, smt_id = 0;
uint32_t last_apic_core_id = UINT32_MAX, last_apic_cluster_id = UINT32_MAX, last_apic_package_id = UINT32_MAX; uint32_t last_apic_core_id = UINT32_MAX, last_apic_cluster_id = UINT32_MAX, last_apic_package_id = UINT32_MAX;
uint32_t last_l1i_id = UINT32_MAX, last_l1d_id = UINT32_MAX; uint32_t last_l1i_id = UINT32_MAX, last_l1d_id = UINT32_MAX;
@ -365,13 +404,15 @@ void cpuinfo_x86_linux_init(void) {
core_id++; core_id++;
smt_id = 0; smt_id = 0;
} }
/* Bits of APIC ID which are part of either LLC or package ID mask */ /* Bits of APIC ID which are part of either LLC or
* package ID mask */
const uint32_t apic_cluster_id = apic_id & cluster_apic_mask; const uint32_t apic_cluster_id = apic_id & cluster_apic_mask;
if (apic_cluster_id != last_apic_cluster_id) { if (apic_cluster_id != last_apic_cluster_id) {
cluster_index++; cluster_index++;
cluster_id++; cluster_id++;
} }
/* All bits of APIC ID except thread ID and core ID masks */ /* All bits of APIC ID except thread ID and core ID
* masks */
const uint32_t apic_package_id = apic_id & package_apic_mask; const uint32_t apic_package_id = apic_id & package_apic_mask;
if (apic_package_id != last_apic_package_id) { if (apic_package_id != last_apic_package_id) {
package_index++; package_index++;
@ -380,16 +421,16 @@ void cpuinfo_x86_linux_init(void) {
} }
/* Initialize logical processor object */ /* Initialize logical processor object */
processors[processor_index].smt_id = smt_id; processors[processor_index].smt_id = smt_id;
processors[processor_index].core = cores + core_index; processors[processor_index].core = cores + core_index;
processors[processor_index].cluster = clusters + cluster_index; processors[processor_index].cluster = clusters + cluster_index;
processors[processor_index].package = packages + package_index; processors[processor_index].package = packages + package_index;
processors[processor_index].linux_id = x86_linux_processors[i].linux_id; processors[processor_index].linux_id = x86_linux_processors[i].linux_id;
processors[processor_index].apic_id = x86_linux_processors[i].apic_id; processors[processor_index].apic_id = x86_linux_processors[i].apic_id;
if (apid_core_id != last_apic_core_id) { if (apid_core_id != last_apic_core_id) {
/* new core */ /* new core */
cores[core_index] = (struct cpuinfo_core) { cores[core_index] = (struct cpuinfo_core){
.processor_start = processor_index, .processor_start = processor_index,
.processor_count = 1, .processor_count = 1,
.core_id = core_id, .core_id = core_id,
@ -420,7 +461,8 @@ void cpuinfo_x86_linux_init(void) {
packages[package_index].cluster_count += 1; packages[package_index].cluster_count += 1;
last_apic_cluster_id = apic_cluster_id; last_apic_cluster_id = apic_cluster_id;
} else { } else {
/* another logical processor on the same cluster */ /* another logical processor on the same cluster
*/
clusters[cluster_index].processor_count++; clusters[cluster_index].processor_count++;
} }
@ -430,10 +472,12 @@ void cpuinfo_x86_linux_init(void) {
packages[package_index].processor_count = 1; packages[package_index].processor_count = 1;
packages[package_index].core_start = core_index; packages[package_index].core_start = core_index;
packages[package_index].cluster_start = cluster_index; packages[package_index].cluster_start = cluster_index;
cpuinfo_x86_format_package_name(x86_processor.vendor, brand_string, packages[package_index].name); cpuinfo_x86_format_package_name(
x86_processor.vendor, brand_string, packages[package_index].name);
last_apic_package_id = apic_package_id; last_apic_package_id = apic_package_id;
} else { } else {
/* another logical processor on the same package */ /* another logical processor on the same package
*/
packages[package_index].processor_count++; packages[package_index].processor_count++;
} }
@ -446,18 +490,19 @@ void cpuinfo_x86_linux_init(void) {
if (l1i_id != last_l1i_id) { if (l1i_id != last_l1i_id) {
/* new cache */ /* new cache */
last_l1i_id = l1i_id; last_l1i_id = l1i_id;
l1i[++l1i_index] = (struct cpuinfo_cache) { l1i[++l1i_index] = (struct cpuinfo_cache){
.size = x86_processor.cache.l1i.size, .size = x86_processor.cache.l1i.size,
.associativity = x86_processor.cache.l1i.associativity, .associativity = x86_processor.cache.l1i.associativity,
.sets = x86_processor.cache.l1i.sets, .sets = x86_processor.cache.l1i.sets,
.partitions = x86_processor.cache.l1i.partitions, .partitions = x86_processor.cache.l1i.partitions,
.line_size = x86_processor.cache.l1i.line_size, .line_size = x86_processor.cache.l1i.line_size,
.flags = x86_processor.cache.l1i.flags, .flags = x86_processor.cache.l1i.flags,
.processor_start = processor_index, .processor_start = processor_index,
.processor_count = 1, .processor_count = 1,
}; };
} else { } else {
/* another processor sharing the same cache */ /* another processor sharing the same
* cache */
l1i[l1i_index].processor_count += 1; l1i[l1i_index].processor_count += 1;
} }
processors[i].cache.l1i = &l1i[l1i_index]; processors[i].cache.l1i = &l1i[l1i_index];
@ -471,18 +516,19 @@ void cpuinfo_x86_linux_init(void) {
if (l1d_id != last_l1d_id) { if (l1d_id != last_l1d_id) {
/* new cache */ /* new cache */
last_l1d_id = l1d_id; last_l1d_id = l1d_id;
l1d[++l1d_index] = (struct cpuinfo_cache) { l1d[++l1d_index] = (struct cpuinfo_cache){
.size = x86_processor.cache.l1d.size, .size = x86_processor.cache.l1d.size,
.associativity = x86_processor.cache.l1d.associativity, .associativity = x86_processor.cache.l1d.associativity,
.sets = x86_processor.cache.l1d.sets, .sets = x86_processor.cache.l1d.sets,
.partitions = x86_processor.cache.l1d.partitions, .partitions = x86_processor.cache.l1d.partitions,
.line_size = x86_processor.cache.l1d.line_size, .line_size = x86_processor.cache.l1d.line_size,
.flags = x86_processor.cache.l1d.flags, .flags = x86_processor.cache.l1d.flags,
.processor_start = processor_index, .processor_start = processor_index,
.processor_count = 1, .processor_count = 1,
}; };
} else { } else {
/* another processor sharing the same cache */ /* another processor sharing the same
* cache */
l1d[l1d_index].processor_count += 1; l1d[l1d_index].processor_count += 1;
} }
processors[i].cache.l1d = &l1d[l1d_index]; processors[i].cache.l1d = &l1d[l1d_index];
@ -496,18 +542,19 @@ void cpuinfo_x86_linux_init(void) {
if (l2_id != last_l2_id) { if (l2_id != last_l2_id) {
/* new cache */ /* new cache */
last_l2_id = l2_id; last_l2_id = l2_id;
l2[++l2_index] = (struct cpuinfo_cache) { l2[++l2_index] = (struct cpuinfo_cache){
.size = x86_processor.cache.l2.size, .size = x86_processor.cache.l2.size,
.associativity = x86_processor.cache.l2.associativity, .associativity = x86_processor.cache.l2.associativity,
.sets = x86_processor.cache.l2.sets, .sets = x86_processor.cache.l2.sets,
.partitions = x86_processor.cache.l2.partitions, .partitions = x86_processor.cache.l2.partitions,
.line_size = x86_processor.cache.l2.line_size, .line_size = x86_processor.cache.l2.line_size,
.flags = x86_processor.cache.l2.flags, .flags = x86_processor.cache.l2.flags,
.processor_start = processor_index, .processor_start = processor_index,
.processor_count = 1, .processor_count = 1,
}; };
} else { } else {
/* another processor sharing the same cache */ /* another processor sharing the same
* cache */
l2[l2_index].processor_count += 1; l2[l2_index].processor_count += 1;
} }
processors[i].cache.l2 = &l2[l2_index]; processors[i].cache.l2 = &l2[l2_index];
@ -521,18 +568,19 @@ void cpuinfo_x86_linux_init(void) {
if (l3_id != last_l3_id) { if (l3_id != last_l3_id) {
/* new cache */ /* new cache */
last_l3_id = l3_id; last_l3_id = l3_id;
l3[++l3_index] = (struct cpuinfo_cache) { l3[++l3_index] = (struct cpuinfo_cache){
.size = x86_processor.cache.l3.size, .size = x86_processor.cache.l3.size,
.associativity = x86_processor.cache.l3.associativity, .associativity = x86_processor.cache.l3.associativity,
.sets = x86_processor.cache.l3.sets, .sets = x86_processor.cache.l3.sets,
.partitions = x86_processor.cache.l3.partitions, .partitions = x86_processor.cache.l3.partitions,
.line_size = x86_processor.cache.l3.line_size, .line_size = x86_processor.cache.l3.line_size,
.flags = x86_processor.cache.l3.flags, .flags = x86_processor.cache.l3.flags,
.processor_start = processor_index, .processor_start = processor_index,
.processor_count = 1, .processor_count = 1,
}; };
} else { } else {
/* another processor sharing the same cache */ /* another processor sharing the same
* cache */
l3[l3_index].processor_count += 1; l3[l3_index].processor_count += 1;
} }
processors[i].cache.l3 = &l3[l3_index]; processors[i].cache.l3 = &l3[l3_index];
@ -546,18 +594,19 @@ void cpuinfo_x86_linux_init(void) {
if (l4_id != last_l4_id) { if (l4_id != last_l4_id) {
/* new cache */ /* new cache */
last_l4_id = l4_id; last_l4_id = l4_id;
l4[++l4_index] = (struct cpuinfo_cache) { l4[++l4_index] = (struct cpuinfo_cache){
.size = x86_processor.cache.l4.size, .size = x86_processor.cache.l4.size,
.associativity = x86_processor.cache.l4.associativity, .associativity = x86_processor.cache.l4.associativity,
.sets = x86_processor.cache.l4.sets, .sets = x86_processor.cache.l4.sets,
.partitions = x86_processor.cache.l4.partitions, .partitions = x86_processor.cache.l4.partitions,
.line_size = x86_processor.cache.l4.line_size, .line_size = x86_processor.cache.l4.line_size,
.flags = x86_processor.cache.l4.flags, .flags = x86_processor.cache.l4.flags,
.processor_start = processor_index, .processor_start = processor_index,
.processor_count = 1, .processor_count = 1,
}; };
} else { } else {
/* another processor sharing the same cache */ /* another processor sharing the same
* cache */
l4[l4_index].processor_count += 1; l4[l4_index].processor_count += 1;
} }
processors[i].cache.l4 = &l4[l4_index]; processors[i].cache.l4 = &l4[l4_index];
@ -575,9 +624,9 @@ void cpuinfo_x86_linux_init(void) {
cpuinfo_packages = packages; cpuinfo_packages = packages;
cpuinfo_cache[cpuinfo_cache_level_1i] = l1i; cpuinfo_cache[cpuinfo_cache_level_1i] = l1i;
cpuinfo_cache[cpuinfo_cache_level_1d] = l1d; cpuinfo_cache[cpuinfo_cache_level_1d] = l1d;
cpuinfo_cache[cpuinfo_cache_level_2] = l2; cpuinfo_cache[cpuinfo_cache_level_2] = l2;
cpuinfo_cache[cpuinfo_cache_level_3] = l3; cpuinfo_cache[cpuinfo_cache_level_3] = l3;
cpuinfo_cache[cpuinfo_cache_level_4] = l4; cpuinfo_cache[cpuinfo_cache_level_4] = l4;
cpuinfo_processors_count = processors_count; cpuinfo_processors_count = processors_count;
cpuinfo_cores_count = cores_count; cpuinfo_cores_count = cores_count;
@ -585,12 +634,12 @@ void cpuinfo_x86_linux_init(void) {
cpuinfo_packages_count = packages_count; cpuinfo_packages_count = packages_count;
cpuinfo_cache_count[cpuinfo_cache_level_1i] = l1i_count; cpuinfo_cache_count[cpuinfo_cache_level_1i] = l1i_count;
cpuinfo_cache_count[cpuinfo_cache_level_1d] = l1d_count; cpuinfo_cache_count[cpuinfo_cache_level_1d] = l1d_count;
cpuinfo_cache_count[cpuinfo_cache_level_2] = l2_count; cpuinfo_cache_count[cpuinfo_cache_level_2] = l2_count;
cpuinfo_cache_count[cpuinfo_cache_level_3] = l3_count; cpuinfo_cache_count[cpuinfo_cache_level_3] = l3_count;
cpuinfo_cache_count[cpuinfo_cache_level_4] = l4_count; cpuinfo_cache_count[cpuinfo_cache_level_4] = l4_count;
cpuinfo_max_cache_size = cpuinfo_compute_max_cache_size(&processors[0]); cpuinfo_max_cache_size = cpuinfo_compute_max_cache_size(&processors[0]);
cpuinfo_global_uarch = (struct cpuinfo_uarch_info) { cpuinfo_global_uarch = (struct cpuinfo_uarch_info){
.uarch = x86_processor.uarch, .uarch = x86_processor.uarch,
.cpuid = x86_processor.cpuid, .cpuid = x86_processor.cpuid,
.processor_count = processors_count, .processor_count = processors_count,

View File

@ -3,11 +3,10 @@
#include <string.h> #include <string.h>
#include <cpuinfo.h> #include <cpuinfo.h>
#include <x86/api.h>
#include <mach/api.h>
#include <cpuinfo/internal-api.h> #include <cpuinfo/internal-api.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <mach/api.h>
#include <x86/api.h>
static inline uint32_t max(uint32_t a, uint32_t b) { static inline uint32_t max(uint32_t a, uint32_t b) {
return a > b ? a : b; return a > b ? a : b;
@ -31,27 +30,35 @@ void cpuinfo_x86_mach_init(void) {
struct cpuinfo_mach_topology mach_topology = cpuinfo_mach_detect_topology(); struct cpuinfo_mach_topology mach_topology = cpuinfo_mach_detect_topology();
processors = calloc(mach_topology.threads, sizeof(struct cpuinfo_processor)); processors = calloc(mach_topology.threads, sizeof(struct cpuinfo_processor));
if (processors == NULL) { if (processors == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" logical processors", cpuinfo_log_error(
mach_topology.threads * sizeof(struct cpuinfo_processor), mach_topology.threads); "failed to allocate %zu bytes for descriptions of %" PRIu32 " logical processors",
mach_topology.threads * sizeof(struct cpuinfo_processor),
mach_topology.threads);
goto cleanup; goto cleanup;
} }
cores = calloc(mach_topology.cores, sizeof(struct cpuinfo_core)); cores = calloc(mach_topology.cores, sizeof(struct cpuinfo_core));
if (cores == NULL) { if (cores == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" cores", cpuinfo_log_error(
mach_topology.cores * sizeof(struct cpuinfo_core), mach_topology.cores); "failed to allocate %zu bytes for descriptions of %" PRIu32 " cores",
mach_topology.cores * sizeof(struct cpuinfo_core),
mach_topology.cores);
goto cleanup; goto cleanup;
} }
/* On x86 cluster of cores is a physical package */ /* On x86 cluster of cores is a physical package */
clusters = calloc(mach_topology.packages, sizeof(struct cpuinfo_cluster)); clusters = calloc(mach_topology.packages, sizeof(struct cpuinfo_cluster));
if (clusters == NULL) { if (clusters == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" core clusters", cpuinfo_log_error(
mach_topology.packages * sizeof(struct cpuinfo_cluster), mach_topology.packages); "failed to allocate %zu bytes for descriptions of %" PRIu32 " core clusters",
mach_topology.packages * sizeof(struct cpuinfo_cluster),
mach_topology.packages);
goto cleanup; goto cleanup;
} }
packages = calloc(mach_topology.packages, sizeof(struct cpuinfo_package)); packages = calloc(mach_topology.packages, sizeof(struct cpuinfo_package));
if (packages == NULL) { if (packages == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" physical packages", cpuinfo_log_error(
mach_topology.packages * sizeof(struct cpuinfo_package), mach_topology.packages); "failed to allocate %zu bytes for descriptions of %" PRIu32 " physical packages",
mach_topology.packages * sizeof(struct cpuinfo_package),
mach_topology.packages);
goto cleanup; goto cleanup;
} }
@ -65,7 +72,7 @@ void cpuinfo_x86_mach_init(void) {
const uint32_t threads_per_package = mach_topology.threads / mach_topology.packages; const uint32_t threads_per_package = mach_topology.threads / mach_topology.packages;
const uint32_t cores_per_package = mach_topology.cores / mach_topology.packages; const uint32_t cores_per_package = mach_topology.cores / mach_topology.packages;
for (uint32_t i = 0; i < mach_topology.packages; i++) { for (uint32_t i = 0; i < mach_topology.packages; i++) {
clusters[i] = (struct cpuinfo_cluster) { clusters[i] = (struct cpuinfo_cluster){
.processor_start = i * threads_per_package, .processor_start = i * threads_per_package,
.processor_count = threads_per_package, .processor_count = threads_per_package,
.core_start = i * cores_per_package, .core_start = i * cores_per_package,
@ -85,7 +92,7 @@ void cpuinfo_x86_mach_init(void) {
cpuinfo_x86_format_package_name(x86_processor.vendor, brand_string, packages[i].name); cpuinfo_x86_format_package_name(x86_processor.vendor, brand_string, packages[i].name);
} }
for (uint32_t i = 0; i < mach_topology.cores; i++) { for (uint32_t i = 0; i < mach_topology.cores; i++) {
cores[i] = (struct cpuinfo_core) { cores[i] = (struct cpuinfo_core){
.processor_start = i * threads_per_core, .processor_start = i * threads_per_core,
.processor_count = threads_per_core, .processor_count = threads_per_core,
.core_id = i % cores_per_package, .core_id = i % cores_per_package,
@ -103,15 +110,14 @@ void cpuinfo_x86_mach_init(void) {
/* Reconstruct APIC IDs from topology components */ /* Reconstruct APIC IDs from topology components */
const uint32_t thread_bits_mask = bit_mask(x86_processor.topology.thread_bits_length); const uint32_t thread_bits_mask = bit_mask(x86_processor.topology.thread_bits_length);
const uint32_t core_bits_mask = bit_mask(x86_processor.topology.core_bits_length); const uint32_t core_bits_mask = bit_mask(x86_processor.topology.core_bits_length);
const uint32_t package_bits_offset = max( const uint32_t package_bits_offset =
x86_processor.topology.thread_bits_offset + x86_processor.topology.thread_bits_length, max(x86_processor.topology.thread_bits_offset + x86_processor.topology.thread_bits_length,
x86_processor.topology.core_bits_offset + x86_processor.topology.core_bits_length); x86_processor.topology.core_bits_offset + x86_processor.topology.core_bits_length);
const uint32_t apic_id = const uint32_t apic_id = ((smt_id & thread_bits_mask) << x86_processor.topology.thread_bits_offset) |
((smt_id & thread_bits_mask) << x86_processor.topology.thread_bits_offset) |
((core_id & core_bits_mask) << x86_processor.topology.core_bits_offset) | ((core_id & core_bits_mask) << x86_processor.topology.core_bits_offset) |
(package_id << package_bits_offset); (package_id << package_bits_offset);
cpuinfo_log_debug("reconstructed APIC ID 0x%08"PRIx32" for thread %"PRIu32, apic_id, i); cpuinfo_log_debug("reconstructed APIC ID 0x%08" PRIx32 " for thread %" PRIu32, apic_id, i);
processors[i].smt_id = smt_id; processors[i].smt_id = smt_id;
processors[i].core = cores + i / threads_per_core; processors[i].core = cores + i / threads_per_core;
@ -126,11 +132,12 @@ void cpuinfo_x86_mach_init(void) {
if (threads_per_l1 == 0) { if (threads_per_l1 == 0) {
/* Assume that threads on the same core share L1 */ /* Assume that threads on the same core share L1 */
threads_per_l1 = mach_topology.threads / mach_topology.cores; threads_per_l1 = mach_topology.threads / mach_topology.cores;
cpuinfo_log_warning("Mach kernel did not report number of threads sharing L1 cache; assume %"PRIu32, cpuinfo_log_warning(
"Mach kernel did not report number of threads sharing L1 cache; assume %" PRIu32,
threads_per_l1); threads_per_l1);
} }
l1_count = mach_topology.threads / threads_per_l1; l1_count = mach_topology.threads / threads_per_l1;
cpuinfo_log_debug("detected %"PRIu32" L1 caches", l1_count); cpuinfo_log_debug("detected %" PRIu32 " L1 caches", l1_count);
} }
uint32_t threads_per_l2 = 0, l2_count = 0; uint32_t threads_per_l2 = 0, l2_count = 0;
@ -138,17 +145,20 @@ void cpuinfo_x86_mach_init(void) {
threads_per_l2 = mach_topology.threads_per_cache[2]; threads_per_l2 = mach_topology.threads_per_cache[2];
if (threads_per_l2 == 0) { if (threads_per_l2 == 0) {
if (x86_processor.cache.l3.size != 0) { if (x86_processor.cache.l3.size != 0) {
/* This is not a last-level cache; assume that threads on the same core share L2 */ /* This is not a last-level cache; assume that
* threads on the same core share L2 */
threads_per_l2 = mach_topology.threads / mach_topology.cores; threads_per_l2 = mach_topology.threads / mach_topology.cores;
} else { } else {
/* This is a last-level cache; assume that threads on the same package share L2 */ /* This is a last-level cache; assume that
* threads on the same package share L2 */
threads_per_l2 = mach_topology.threads / mach_topology.packages; threads_per_l2 = mach_topology.threads / mach_topology.packages;
} }
cpuinfo_log_warning("Mach kernel did not report number of threads sharing L2 cache; assume %"PRIu32, cpuinfo_log_warning(
"Mach kernel did not report number of threads sharing L2 cache; assume %" PRIu32,
threads_per_l2); threads_per_l2);
} }
l2_count = mach_topology.threads / threads_per_l2; l2_count = mach_topology.threads / threads_per_l2;
cpuinfo_log_debug("detected %"PRIu32" L2 caches", l2_count); cpuinfo_log_debug("detected %" PRIu32 " L2 caches", l2_count);
} }
uint32_t threads_per_l3 = 0, l3_count = 0; uint32_t threads_per_l3 = 0, l3_count = 0;
@ -157,14 +167,16 @@ void cpuinfo_x86_mach_init(void) {
if (threads_per_l3 == 0) { if (threads_per_l3 == 0) {
/* /*
* Assume that threads on the same package share L3. * Assume that threads on the same package share L3.
* However, is it not necessarily the last-level cache (there may be L4 cache as well) * However, is it not necessarily the last-level cache
* (there may be L4 cache as well)
*/ */
threads_per_l3 = mach_topology.threads / mach_topology.packages; threads_per_l3 = mach_topology.threads / mach_topology.packages;
cpuinfo_log_warning("Mach kernel did not report number of threads sharing L3 cache; assume %"PRIu32, cpuinfo_log_warning(
"Mach kernel did not report number of threads sharing L3 cache; assume %" PRIu32,
threads_per_l3); threads_per_l3);
} }
l3_count = mach_topology.threads / threads_per_l3; l3_count = mach_topology.threads / threads_per_l3;
cpuinfo_log_debug("detected %"PRIu32" L3 caches", l3_count); cpuinfo_log_debug("detected %" PRIu32 " L3 caches", l3_count);
} }
uint32_t threads_per_l4 = 0, l4_count = 0; uint32_t threads_per_l4 = 0, l4_count = 0;
@ -173,32 +185,36 @@ void cpuinfo_x86_mach_init(void) {
if (threads_per_l4 == 0) { if (threads_per_l4 == 0) {
/* /*
* Assume that all threads share this L4. * Assume that all threads share this L4.
* As of now, L4 cache exists only on notebook x86 CPUs, which are single-package, * As of now, L4 cache exists only on notebook x86 CPUs,
* but multi-socket systems could have shared L4 (like on IBM POWER8). * which are single-package, but multi-socket systems
* could have shared L4 (like on IBM POWER8).
*/ */
threads_per_l4 = mach_topology.threads; threads_per_l4 = mach_topology.threads;
cpuinfo_log_warning("Mach kernel did not report number of threads sharing L4 cache; assume %"PRIu32, cpuinfo_log_warning(
"Mach kernel did not report number of threads sharing L4 cache; assume %" PRIu32,
threads_per_l4); threads_per_l4);
} }
l4_count = mach_topology.threads / threads_per_l4; l4_count = mach_topology.threads / threads_per_l4;
cpuinfo_log_debug("detected %"PRIu32" L4 caches", l4_count); cpuinfo_log_debug("detected %" PRIu32 " L4 caches", l4_count);
} }
if (x86_processor.cache.l1i.size != 0) { if (x86_processor.cache.l1i.size != 0) {
l1i = calloc(l1_count, sizeof(struct cpuinfo_cache)); l1i = calloc(l1_count, sizeof(struct cpuinfo_cache));
if (l1i == NULL) { if (l1i == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L1I caches", cpuinfo_log_error(
l1_count * sizeof(struct cpuinfo_cache), l1_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L1I caches",
l1_count * sizeof(struct cpuinfo_cache),
l1_count);
return; return;
} }
for (uint32_t c = 0; c < l1_count; c++) { for (uint32_t c = 0; c < l1_count; c++) {
l1i[c] = (struct cpuinfo_cache) { l1i[c] = (struct cpuinfo_cache){
.size = x86_processor.cache.l1i.size, .size = x86_processor.cache.l1i.size,
.associativity = x86_processor.cache.l1i.associativity, .associativity = x86_processor.cache.l1i.associativity,
.sets = x86_processor.cache.l1i.sets, .sets = x86_processor.cache.l1i.sets,
.partitions = x86_processor.cache.l1i.partitions, .partitions = x86_processor.cache.l1i.partitions,
.line_size = x86_processor.cache.l1i.line_size, .line_size = x86_processor.cache.l1i.line_size,
.flags = x86_processor.cache.l1i.flags, .flags = x86_processor.cache.l1i.flags,
.processor_start = c * threads_per_l1, .processor_start = c * threads_per_l1,
.processor_count = threads_per_l1, .processor_count = threads_per_l1,
}; };
@ -211,18 +227,20 @@ void cpuinfo_x86_mach_init(void) {
if (x86_processor.cache.l1d.size != 0) { if (x86_processor.cache.l1d.size != 0) {
l1d = calloc(l1_count, sizeof(struct cpuinfo_cache)); l1d = calloc(l1_count, sizeof(struct cpuinfo_cache));
if (l1d == NULL) { if (l1d == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L1D caches", cpuinfo_log_error(
l1_count * sizeof(struct cpuinfo_cache), l1_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L1D caches",
l1_count * sizeof(struct cpuinfo_cache),
l1_count);
return; return;
} }
for (uint32_t c = 0; c < l1_count; c++) { for (uint32_t c = 0; c < l1_count; c++) {
l1d[c] = (struct cpuinfo_cache) { l1d[c] = (struct cpuinfo_cache){
.size = x86_processor.cache.l1d.size, .size = x86_processor.cache.l1d.size,
.associativity = x86_processor.cache.l1d.associativity, .associativity = x86_processor.cache.l1d.associativity,
.sets = x86_processor.cache.l1d.sets, .sets = x86_processor.cache.l1d.sets,
.partitions = x86_processor.cache.l1d.partitions, .partitions = x86_processor.cache.l1d.partitions,
.line_size = x86_processor.cache.l1d.line_size, .line_size = x86_processor.cache.l1d.line_size,
.flags = x86_processor.cache.l1d.flags, .flags = x86_processor.cache.l1d.flags,
.processor_start = c * threads_per_l1, .processor_start = c * threads_per_l1,
.processor_count = threads_per_l1, .processor_count = threads_per_l1,
}; };
@ -235,18 +253,20 @@ void cpuinfo_x86_mach_init(void) {
if (l2_count != 0) { if (l2_count != 0) {
l2 = calloc(l2_count, sizeof(struct cpuinfo_cache)); l2 = calloc(l2_count, sizeof(struct cpuinfo_cache));
if (l2 == NULL) { if (l2 == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L2 caches", cpuinfo_log_error(
l2_count * sizeof(struct cpuinfo_cache), l2_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L2 caches",
l2_count * sizeof(struct cpuinfo_cache),
l2_count);
return; return;
} }
for (uint32_t c = 0; c < l2_count; c++) { for (uint32_t c = 0; c < l2_count; c++) {
l2[c] = (struct cpuinfo_cache) { l2[c] = (struct cpuinfo_cache){
.size = x86_processor.cache.l2.size, .size = x86_processor.cache.l2.size,
.associativity = x86_processor.cache.l2.associativity, .associativity = x86_processor.cache.l2.associativity,
.sets = x86_processor.cache.l2.sets, .sets = x86_processor.cache.l2.sets,
.partitions = x86_processor.cache.l2.partitions, .partitions = x86_processor.cache.l2.partitions,
.line_size = x86_processor.cache.l2.line_size, .line_size = x86_processor.cache.l2.line_size,
.flags = x86_processor.cache.l2.flags, .flags = x86_processor.cache.l2.flags,
.processor_start = c * threads_per_l2, .processor_start = c * threads_per_l2,
.processor_count = threads_per_l2, .processor_count = threads_per_l2,
}; };
@ -259,18 +279,20 @@ void cpuinfo_x86_mach_init(void) {
if (l3_count != 0) { if (l3_count != 0) {
l3 = calloc(l3_count, sizeof(struct cpuinfo_cache)); l3 = calloc(l3_count, sizeof(struct cpuinfo_cache));
if (l3 == NULL) { if (l3 == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L3 caches", cpuinfo_log_error(
l3_count * sizeof(struct cpuinfo_cache), l3_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L3 caches",
l3_count * sizeof(struct cpuinfo_cache),
l3_count);
return; return;
} }
for (uint32_t c = 0; c < l3_count; c++) { for (uint32_t c = 0; c < l3_count; c++) {
l3[c] = (struct cpuinfo_cache) { l3[c] = (struct cpuinfo_cache){
.size = x86_processor.cache.l3.size, .size = x86_processor.cache.l3.size,
.associativity = x86_processor.cache.l3.associativity, .associativity = x86_processor.cache.l3.associativity,
.sets = x86_processor.cache.l3.sets, .sets = x86_processor.cache.l3.sets,
.partitions = x86_processor.cache.l3.partitions, .partitions = x86_processor.cache.l3.partitions,
.line_size = x86_processor.cache.l3.line_size, .line_size = x86_processor.cache.l3.line_size,
.flags = x86_processor.cache.l3.flags, .flags = x86_processor.cache.l3.flags,
.processor_start = c * threads_per_l3, .processor_start = c * threads_per_l3,
.processor_count = threads_per_l3, .processor_count = threads_per_l3,
}; };
@ -283,18 +305,20 @@ void cpuinfo_x86_mach_init(void) {
if (l4_count != 0) { if (l4_count != 0) {
l4 = calloc(l4_count, sizeof(struct cpuinfo_cache)); l4 = calloc(l4_count, sizeof(struct cpuinfo_cache));
if (l4 == NULL) { if (l4 == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L4 caches", cpuinfo_log_error(
l4_count * sizeof(struct cpuinfo_cache), l4_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L4 caches",
l4_count * sizeof(struct cpuinfo_cache),
l4_count);
return; return;
} }
for (uint32_t c = 0; c < l4_count; c++) { for (uint32_t c = 0; c < l4_count; c++) {
l4[c] = (struct cpuinfo_cache) { l4[c] = (struct cpuinfo_cache){
.size = x86_processor.cache.l4.size, .size = x86_processor.cache.l4.size,
.associativity = x86_processor.cache.l4.associativity, .associativity = x86_processor.cache.l4.associativity,
.sets = x86_processor.cache.l4.sets, .sets = x86_processor.cache.l4.sets,
.partitions = x86_processor.cache.l4.partitions, .partitions = x86_processor.cache.l4.partitions,
.line_size = x86_processor.cache.l4.line_size, .line_size = x86_processor.cache.l4.line_size,
.flags = x86_processor.cache.l4.flags, .flags = x86_processor.cache.l4.flags,
.processor_start = c * threads_per_l4, .processor_start = c * threads_per_l4,
.processor_count = threads_per_l4, .processor_count = threads_per_l4,
}; };
@ -311,9 +335,9 @@ void cpuinfo_x86_mach_init(void) {
cpuinfo_packages = packages; cpuinfo_packages = packages;
cpuinfo_cache[cpuinfo_cache_level_1i] = l1i; cpuinfo_cache[cpuinfo_cache_level_1i] = l1i;
cpuinfo_cache[cpuinfo_cache_level_1d] = l1d; cpuinfo_cache[cpuinfo_cache_level_1d] = l1d;
cpuinfo_cache[cpuinfo_cache_level_2] = l2; cpuinfo_cache[cpuinfo_cache_level_2] = l2;
cpuinfo_cache[cpuinfo_cache_level_3] = l3; cpuinfo_cache[cpuinfo_cache_level_3] = l3;
cpuinfo_cache[cpuinfo_cache_level_4] = l4; cpuinfo_cache[cpuinfo_cache_level_4] = l4;
cpuinfo_processors_count = mach_topology.threads; cpuinfo_processors_count = mach_topology.threads;
cpuinfo_cores_count = mach_topology.cores; cpuinfo_cores_count = mach_topology.cores;
@ -321,12 +345,12 @@ void cpuinfo_x86_mach_init(void) {
cpuinfo_packages_count = mach_topology.packages; cpuinfo_packages_count = mach_topology.packages;
cpuinfo_cache_count[cpuinfo_cache_level_1i] = l1_count; cpuinfo_cache_count[cpuinfo_cache_level_1i] = l1_count;
cpuinfo_cache_count[cpuinfo_cache_level_1d] = l1_count; cpuinfo_cache_count[cpuinfo_cache_level_1d] = l1_count;
cpuinfo_cache_count[cpuinfo_cache_level_2] = l2_count; cpuinfo_cache_count[cpuinfo_cache_level_2] = l2_count;
cpuinfo_cache_count[cpuinfo_cache_level_3] = l3_count; cpuinfo_cache_count[cpuinfo_cache_level_3] = l3_count;
cpuinfo_cache_count[cpuinfo_cache_level_4] = l4_count; cpuinfo_cache_count[cpuinfo_cache_level_4] = l4_count;
cpuinfo_max_cache_size = cpuinfo_compute_max_cache_size(&processors[0]); cpuinfo_max_cache_size = cpuinfo_compute_max_cache_size(&processors[0]);
cpuinfo_global_uarch = (struct cpuinfo_uarch_info) { cpuinfo_global_uarch = (struct cpuinfo_uarch_info){
.uarch = x86_processor.uarch, .uarch = x86_processor.uarch,
.cpuid = x86_processor.cpuid, .cpuid = x86_processor.cpuid,
.processor_count = mach_topology.threads, .processor_count = mach_topology.threads,

View File

@ -1,13 +1,12 @@
#include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#if !CPUINFO_MOCK #if !CPUINFO_MOCK
#error This file should be built only in mock mode #error This file should be built only in mock mode
#endif #endif
#include <cpuinfo-mock.h> #include <cpuinfo-mock.h>
static struct cpuinfo_mock_cpuid* cpuinfo_mock_cpuid_data = NULL; static struct cpuinfo_mock_cpuid* cpuinfo_mock_cpuid_data = NULL;
static uint32_t cpuinfo_mock_cpuid_entries = 0; static uint32_t cpuinfo_mock_cpuid_entries = 0;
static uint32_t cpuinfo_mock_cpuid_leaf4_iteration = 0; static uint32_t cpuinfo_mock_cpuid_leaf4_iteration = 0;
@ -56,8 +55,7 @@ void CPUINFO_ABI cpuinfo_mock_get_cpuidex(uint32_t eax, uint32_t ecx, uint32_t r
if (cpuinfo_mock_cpuid_data != NULL && cpuinfo_mock_cpuid_entries != 0) { if (cpuinfo_mock_cpuid_data != NULL && cpuinfo_mock_cpuid_entries != 0) {
for (uint32_t i = 0; i < cpuinfo_mock_cpuid_entries; i++) { for (uint32_t i = 0; i < cpuinfo_mock_cpuid_entries; i++) {
if (eax == cpuinfo_mock_cpuid_data[i].input_eax && if (eax == cpuinfo_mock_cpuid_data[i].input_eax &&
ecx == cpuinfo_mock_cpuid_data[i].input_ecx) ecx == cpuinfo_mock_cpuid_data[i].input_ecx) {
{
regs[0] = cpuinfo_mock_cpuid_data[i].eax; regs[0] = cpuinfo_mock_cpuid_data[i].eax;
regs[1] = cpuinfo_mock_cpuid_data[i].ebx; regs[1] = cpuinfo_mock_cpuid_data[i].ebx;
regs[2] = cpuinfo_mock_cpuid_data[i].ecx; regs[2] = cpuinfo_mock_cpuid_data[i].ecx;

View File

@ -1,6 +1,6 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -8,7 +8,6 @@
#include <cpuinfo/common.h> #include <cpuinfo/common.h>
#include <x86/api.h> #include <x86/api.h>
/* The state of the parser to be preserved between parsing different tokens. */ /* The state of the parser to be preserved between parsing different tokens. */
struct parser_state { struct parser_state {
/* /*
@ -17,8 +16,9 @@ struct parser_state {
*/ */
char* context_model; char* context_model;
/* /*
* Pointer to the start of the previous token if it is a single-uppercase-letter token. * Pointer to the start of the previous token if it is a
* NULL if previous token is anything different. * single-uppercase-letter token. NULL if previous token is anything
* different.
*/ */
char* context_upper_letter; char* context_upper_letter;
/* /*
@ -27,31 +27,36 @@ struct parser_state {
*/ */
char* context_dual; char* context_dual;
/* /*
* Pointer to the start of the previous token if it is "Core", "Dual-Core", "QuadCore", etc. * Pointer to the start of the previous token if it is "Core",
* NULL if previous token is anything different. * "Dual-Core", "QuadCore", etc. NULL if previous token is anything
* different.
*/ */
char* context_core; char* context_core;
/* /*
* Pointer to the start of the previous token if it is "Eng" or "Engineering", etc. * Pointer to the start of the previous token if it is "Eng" or
* NULL if previous token is anything different. * "Engineering", etc. NULL if previous token is anything different.
*/ */
char* context_engineering; char* context_engineering;
/* /*
* Pointer to the '@' symbol in the brand string (separates frequency specification). * Pointer to the '@' symbol in the brand string (separates frequency
* NULL if there is no '@' symbol. * specification). NULL if there is no '@' symbol.
*/ */
char* frequency_separator; char* frequency_separator;
/* Indicates whether the brand string (after transformations) contains frequency. */ /* Indicates whether the brand string (after transformations) contains
* frequency. */
bool frequency_token; bool frequency_token;
/* Indicates whether the processor is of Xeon family (contains "Xeon" substring). */ /* Indicates whether the processor is of Xeon family (contains "Xeon"
* substring). */
bool xeon; bool xeon;
/* Indicates whether the processor model number was already parsed. */ /* Indicates whether the processor model number was already parsed. */
bool parsed_model_number; bool parsed_model_number;
/* Indicates whether the processor is an engineering sample (contains "Engineering Sample" or "Eng Sample" substrings). */ /* Indicates whether the processor is an engineering sample (contains
* "Engineering Sample" or "Eng Sample" substrings). */
bool engineering_sample; bool engineering_sample;
}; };
/** @brief Resets information about the previous token. Keeps all other state information. */ /** @brief Resets information about the previous token. Keeps all other
* state information. */
static void reset_context(struct parser_state* state) { static void reset_context(struct parser_state* state) {
state->context_model = NULL; state->context_model = NULL;
state->context_upper_letter = NULL; state->context_upper_letter = NULL;
@ -60,12 +65,17 @@ static void reset_context(struct parser_state* state) {
} }
/** /**
* @brief Overwrites the supplied string with space characters if it exactly matches the given string. * @brief Overwrites the supplied string with space characters if it
* @param string The string to be compared against other string, and erased in case of matching. * exactly matches the given string.
* @param length The length of the two string to be compared against each other. * @param string The string to be compared against other string, and
* erased in case of matching.
* @param length The length of the two string to be compared against each
* other.
* @param target The string to compare against. * @param target The string to compare against.
* @retval true If the two strings match and the first supplied string was erased (overwritten with space characters). * @retval true If the two strings match and the first supplied string
* @retval false If the two strings are different and the first supplied string remained unchanged. * was erased (overwritten with space characters).
* @retval false If the two strings are different and the first supplied
* string remained unchanged.
*/ */
static inline bool erase_matching(char* string, size_t length, const char* target) { static inline bool erase_matching(char* string, size_t length, const char* target) {
const bool match = memcmp(string, target, length) == 0; const bool match = memcmp(string, target, length) == 0;
@ -76,13 +86,15 @@ static inline bool erase_matching(char* string, size_t length, const char* targe
} }
/** /**
* @brief Checks if the supplied ASCII character is an uppercase latin letter. * @brief Checks if the supplied ASCII character is an uppercase latin
* letter.
* @param character The character to analyse. * @param character The character to analyse.
* @retval true If the supplied character is an uppercase latin letter ('A' to 'Z'). * @retval true If the supplied character is an uppercase latin letter
* ('A' to 'Z').
* @retval false If the supplied character is anything different. * @retval false If the supplied character is anything different.
*/ */
static inline bool is_upper_letter(char character) { static inline bool is_upper_letter(char character) {
return (uint32_t) (character - 'A') <= (uint32_t)('Z' - 'A'); return (uint32_t)(character - 'A') <= (uint32_t)('Z' - 'A');
} }
/** /**
@ -92,7 +104,7 @@ static inline bool is_upper_letter(char character) {
* @retval false If the supplied character is anything different. * @retval false If the supplied character is anything different.
*/ */
static inline bool is_digit(char character) { static inline bool is_digit(char character) {
return (uint32_t) (character - '0') < UINT32_C(10); return (uint32_t)(character - '0') < UINT32_C(10);
} }
static inline bool is_zero_number(const char* token_start, const char* token_end) { static inline bool is_zero_number(const char* token_start, const char* token_end) {
@ -132,7 +144,7 @@ static inline bool is_model_number(const char* token_start, const char* token_en
} }
static inline bool is_frequency(const char* token_start, const char* token_end) { static inline bool is_frequency(const char* token_start, const char* token_end) {
const size_t token_length = (size_t) (token_end - token_start); const size_t token_length = (size_t)(token_end - token_start);
if (token_length > 3 && token_end[-2] == 'H' && token_end[-1] == 'z') { if (token_length > 3 && token_end[-2] == 'H' && token_end[-1] == 'z') {
switch (token_end[-3]) { switch (token_end[-3]) {
case 'K': case 'K':
@ -148,7 +160,7 @@ static inline bool is_frequency(const char* token_start, const char* token_end)
* @warning Input and output tokens can overlap * @warning Input and output tokens can overlap
*/ */
static inline char* move_token(const char* token_start, const char* token_end, char* output_ptr) { static inline char* move_token(const char* token_start, const char* token_end, char* output_ptr) {
const size_t token_length = (size_t) (token_end - token_start); const size_t token_length = (size_t)(token_end - token_start);
memmove(output_ptr, token_start, token_length); memmove(output_ptr, token_start, token_length);
return output_ptr + token_length; return output_ptr + token_length;
} }
@ -157,7 +169,7 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
const struct parser_state previousState = *state; const struct parser_state previousState = *state;
reset_context(state); reset_context(state);
size_t token_length = (size_t) (token_end - token_start); size_t token_length = (size_t)(token_end - token_start);
if (state->frequency_separator != NULL) { if (state->frequency_separator != NULL) {
if (token_start > state->frequency_separator) { if (token_start > state->frequency_separator) {
@ -167,7 +179,6 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
} }
} }
/* Early AMD and Cyrix processors have "tm" suffix for trademark, e.g. /* Early AMD and Cyrix processors have "tm" suffix for trademark, e.g.
* "AMD-K6tm w/ multimedia extensions" * "AMD-K6tm w/ multimedia extensions"
* "Cyrix MediaGXtm MMXtm Enhanced" * "Cyrix MediaGXtm MMXtm Enhanced"
@ -196,11 +207,13 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
switch (token_length) { switch (token_length) {
case 1: case 1:
/* /*
* On some Intel processors there is a space between the first letter of * On some Intel processors there is a space between the
* the name and the number after it, e.g. * first letter of the name and the number after it,
* "Intel(R) Core(TM) i7 CPU X 990 @ 3.47GHz" * e.g. "Intel(R) Core(TM) i7 CPU X 990 @ 3.47GHz"
* "Intel(R) Core(TM) CPU Q 820 @ 1.73GHz" * "Intel(R) Core(TM) CPU Q 820 @ 1.73GHz"
* We want to merge these parts together, in reverse order, i.e. "X 990" -> "990X", "820" -> "820Q" * We want to merge these parts together, in reverse
* order, i.e. "X 990"
* -> "990X", "820" -> "820Q"
*/ */
if (is_upper_letter(token_start[0])) { if (is_upper_letter(token_start[0])) {
state->context_upper_letter = token_start; state->context_upper_letter = token_start;
@ -208,15 +221,17 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
} }
break; break;
case 2: case 2:
/* Erase everything after "w/" in "AMD-K6tm w/ multimedia extensions" */ /* Erase everything after "w/" in "AMD-K6tm w/
* multimedia extensions" */
if (erase_matching(token_start, token_length, "w/")) { if (erase_matching(token_start, token_length, "w/")) {
return false; return false;
} }
/* /*
* Intel Xeon processors since Ivy Bridge use versions, e.g. * Intel Xeon processors since Ivy Bridge use versions,
* "Intel Xeon E3-1230 v2" * e.g. "Intel Xeon E3-1230 v2" Some processor branch
* Some processor branch strings report them as "V<N>", others report as "v<N>". * strings report them as "V<N>", others report as
* Normalize the former (upper-case) to the latter (lower-case) version * "v<N>". Normalize the former (upper-case) to the
* latter (lower-case) version
*/ */
if (token_start[0] == 'V' && is_digit(token_start[1])) { if (token_start[0] == 'V' && is_digit(token_start[1])) {
token_start[0] = 'v'; token_start[0] = 'v';
@ -234,8 +249,9 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
return true; return true;
} }
/* /*
* Erase everything after "SOC" on AMD System-on-Chips, e.g. * Erase everything after "SOC" on AMD System-on-Chips,
* "AMD GX-212JC SOC with Radeon(TM) R2E Graphics \0" * e.g. "AMD GX-212JC SOC with Radeon(TM) R2E Graphics
* \0"
*/ */
if (erase_matching(token_start, token_length, "SOC")) { if (erase_matching(token_start, token_length, "SOC")) {
return false; return false;
@ -258,36 +274,41 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
if (erase_matching(token_start, token_length, "VIA")) { if (erase_matching(token_start, token_length, "VIA")) {
return true; return true;
} }
/* Erase "IDT" in brand string on early Centaur processors, e.g. "IDT WinChip 2-3D" */ /* Erase "IDT" in brand string on early Centaur
* processors, e.g. "IDT WinChip 2-3D" */
if (erase_matching(token_start, token_length, "IDT")) { if (erase_matching(token_start, token_length, "IDT")) {
return true; return true;
} }
/* /*
* Erase everything starting with "MMX" in * Erase everything starting with "MMX" in
* "Cyrix MediaGXtm MMXtm Enhanced" ("tm" suffix is removed by this point) * "Cyrix MediaGXtm MMXtm Enhanced" ("tm" suffix is
* removed by this point)
*/ */
if (erase_matching(token_start, token_length, "MMX")) { if (erase_matching(token_start, token_length, "MMX")) {
return false; return false;
} }
/* /*
* Erase everything starting with "APU" on AMD processors, e.g. * Erase everything starting with "APU" on AMD
* "AMD A10-4600M APU with Radeon(tm) HD Graphics" * processors, e.g. "AMD A10-4600M APU with Radeon(tm)
* "AMD A10-7850K APU with Radeon(TM) R7 Graphics" * HD Graphics" "AMD A10-7850K APU with Radeon(TM) R7
* "AMD A6-6310 APU with AMD Radeon R4 Graphics" * Graphics" "AMD A6-6310 APU with AMD Radeon R4
* Graphics"
*/ */
if (erase_matching(token_start, token_length, "APU")) { if (erase_matching(token_start, token_length, "APU")) {
return false; return false;
} }
/* /*
* Remember to discard string if it contains "Eng Sample", * Remember to discard string if it contains "Eng
* e.g. "Eng Sample, ZD302046W4K43_36/30/20_2/8_A" * Sample", e.g. "Eng Sample,
* ZD302046W4K43_36/30/20_2/8_A"
*/ */
if (memcmp(token_start, "Eng", token_length) == 0) { if (memcmp(token_start, "Eng", token_length) == 0) {
state->context_engineering = token_start; state->context_engineering = token_start;
} }
break; break;
case 4: case 4:
/* Remember to erase "Dual Core" in "AMD Athlon(tm) 64 X2 Dual Core Processor 3800+" */ /* Remember to erase "Dual Core" in "AMD Athlon(tm) 64
* X2 Dual Core Processor 3800+" */
if (memcmp(token_start, "Dual", token_length) == 0) { if (memcmp(token_start, "Dual", token_length) == 0) {
state->context_dual = token_start; state->context_dual = token_start;
} }
@ -295,10 +316,14 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
if (memcmp(token_start, "Xeon", token_length) == 0) { if (memcmp(token_start, "Xeon", token_length) == 0) {
state->xeon = true; state->xeon = true;
} }
/* Erase "Dual Core" in "AMD Athlon(tm) 64 X2 Dual Core Processor 3800+" */ /* Erase "Dual Core" in "AMD Athlon(tm) 64 X2 Dual Core
* Processor 3800+"
*/
if (previousState.context_dual != NULL) { if (previousState.context_dual != NULL) {
if (memcmp(token_start, "Core", token_length) == 0) { if (memcmp(token_start, "Core", token_length) == 0) {
memset(previousState.context_dual, ' ', (size_t) (token_end - previousState.context_dual)); memset(previousState.context_dual,
' ',
(size_t)(token_end - previousState.context_dual));
state->context_core = token_end; state->context_core = token_end;
return true; return true;
} }
@ -306,30 +331,32 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
break; break;
case 5: case 5:
/* /*
* Erase "Intel" in brand string on Intel processors, e.g. * Erase "Intel" in brand string on Intel processors,
* "Intel(R) Xeon(R) CPU X3210 @ 2.13GHz" * e.g. "Intel(R) Xeon(R) CPU X3210 @ 2.13GHz" "Intel(R)
* "Intel(R) Atom(TM) CPU D2700 @ 2.13GHz" * Atom(TM) CPU D2700 @ 2.13GHz" "Genuine Intel(R)
* "Genuine Intel(R) processor 800MHz" * processor 800MHz"
*/ */
if (erase_matching(token_start, token_length, "Intel")) { if (erase_matching(token_start, token_length, "Intel")) {
return true; return true;
} }
/* /*
* Erase "Cyrix" in brand string on Cyrix processors, e.g. * Erase "Cyrix" in brand string on Cyrix processors,
* "Cyrix MediaGXtm MMXtm Enhanced" * e.g. "Cyrix MediaGXtm MMXtm Enhanced"
*/ */
if (erase_matching(token_start, token_length, "Cyrix")) { if (erase_matching(token_start, token_length, "Cyrix")) {
return true; return true;
} }
/* /*
* Erase everything following "Geode" (but not "Geode" token itself) on Geode processors, e.g. * Erase everything following "Geode" (but not "Geode"
* "Geode(TM) Integrated Processor by AMD PCS" * token itself) on Geode processors, e.g. "Geode(TM)
* "Geode(TM) Integrated Processor by National Semi" * Integrated Processor by AMD PCS" "Geode(TM)
* Integrated Processor by National Semi"
*/ */
if (memcmp(token_start, "Geode", token_length) == 0) { if (memcmp(token_start, "Geode", token_length) == 0) {
return false; return false;
} }
/* Remember to erase "model unknown" in "AMD Processor model unknown" */ /* Remember to erase "model unknown" in "AMD Processor
* model unknown" */
if (memcmp(token_start, "model", token_length) == 0) { if (memcmp(token_start, "model", token_length) == 0) {
state->context_model = token_start; state->context_model = token_start;
return true; return true;
@ -337,29 +364,33 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
break; break;
case 6: case 6:
/* /*
* Erase everything starting with "Radeon" or "RADEON" on AMD APUs, e.g. * Erase everything starting with "Radeon" or "RADEON"
* "A8-7670K Radeon R7, 10 Compute Cores 4C+6G" * on AMD APUs, e.g. "A8-7670K Radeon R7, 10 Compute
* "FX-8800P Radeon R7, 12 Compute Cores 4C+8G" * Cores 4C+6G" "FX-8800P Radeon R7, 12 Compute Cores
* "A12-9800 RADEON R7, 12 COMPUTE CORES 4C+8G" * 4C+8G" "A12-9800 RADEON R7, 12 COMPUTE CORES 4C+8G"
* "A9-9410 RADEON R5, 5 COMPUTE CORES 2C+3G" * "A9-9410 RADEON R5, 5 COMPUTE CORES 2C+3G"
*/ */
if (erase_matching(token_start, token_length, "Radeon") || erase_matching(token_start, token_length, "RADEON")) { if (erase_matching(token_start, token_length, "Radeon") ||
erase_matching(token_start, token_length, "RADEON")) {
return false; return false;
} }
/* /*
* Erase "Mobile" when it is not part of the processor name, * Erase "Mobile" when it is not part of the processor
* e.g. in "AMD Turion(tm) X2 Ultra Dual-Core Mobile ZM-82" * name, e.g. in "AMD Turion(tm) X2 Ultra Dual-Core
* Mobile ZM-82"
*/ */
if (previousState.context_core != NULL) { if (previousState.context_core != NULL) {
if (erase_matching(token_start, token_length, "Mobile")) { if (erase_matching(token_start, token_length, "Mobile")) {
return true; return true;
} }
} }
/* Erase "family" in "Intel(R) Pentium(R) III CPU family 1266MHz" */ /* Erase "family" in "Intel(R) Pentium(R) III CPU family
* 1266MHz" */
if (erase_matching(token_start, token_length, "family")) { if (erase_matching(token_start, token_length, "family")) {
return true; return true;
} }
/* Discard the string if it contains "Engineering Sample" */ /* Discard the string if it contains "Engineering
* Sample" */
if (previousState.context_engineering != NULL) { if (previousState.context_engineering != NULL) {
if (memcmp(token_start, "Sample", token_length) == 0) { if (memcmp(token_start, "Sample", token_length) == 0) {
state->engineering_sample = true; state->engineering_sample = true;
@ -369,8 +400,8 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
break; break;
case 7: case 7:
/* /*
* Erase "Geniune" in brand string on Intel engineering samples, e.g. * Erase "Geniune" in brand string on Intel engineering
* "Genuine Intel(R) processor 800MHz" * samples, e.g. "Genuine Intel(R) processor 800MHz"
* "Genuine Intel(R) CPU @ 2.13GHz" * "Genuine Intel(R) CPU @ 2.13GHz"
* "Genuine Intel(R) CPU 0000 @ 1.73GHz" * "Genuine Intel(R) CPU 0000 @ 1.73GHz"
*/ */
@ -378,45 +409,52 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
return true; return true;
} }
/* /*
* Erase "12-core" in brand string on AMD Threadripper, e.g. * Erase "12-core" in brand string on AMD Threadripper,
* "AMD Ryzen Threadripper 1920X 12-Core Processor" * e.g. "AMD Ryzen Threadripper 1920X 12-Core Processor"
*/ */
if (erase_matching(token_start, token_length, "12-Core")) { if (erase_matching(token_start, token_length, "12-Core")) {
return true; return true;
} }
/* /*
* Erase "16-core" in brand string on AMD Threadripper, e.g. * Erase "16-core" in brand string on AMD Threadripper,
* "AMD Ryzen Threadripper 1950X 16-Core Processor" * e.g. "AMD Ryzen Threadripper 1950X 16-Core Processor"
*/ */
if (erase_matching(token_start, token_length, "16-Core")) { if (erase_matching(token_start, token_length, "16-Core")) {
return true; return true;
} }
/* Erase "model unknown" in "AMD Processor model unknown" */ /* Erase "model unknown" in "AMD Processor model
* unknown" */
if (previousState.context_model != NULL) { if (previousState.context_model != NULL) {
if (memcmp(token_start, "unknown", token_length) == 0) { if (memcmp(token_start, "unknown", token_length) == 0) {
memset(previousState.context_model, ' ', token_end - previousState.context_model); memset(previousState.context_model,
' ',
token_end - previousState.context_model);
return true; return true;
} }
} }
/* /*
* Discard the string if it contains "Eng Sample:" or "Eng Sample," e.g. * Discard the string if it contains "Eng Sample:" or
* "AMD Eng Sample, ZD302046W4K43_36/30/20_2/8_A" * "Eng Sample," e.g. "AMD Eng Sample,
* "AMD Eng Sample: 2D3151A2M88E4_35/31_N" * ZD302046W4K43_36/30/20_2/8_A" "AMD Eng Sample:
* 2D3151A2M88E4_35/31_N"
*/ */
if (previousState.context_engineering != NULL) { if (previousState.context_engineering != NULL) {
if (memcmp(token_start, "Sample,", token_length) == 0 || memcmp(token_start, "Sample:", token_length) == 0) { if (memcmp(token_start, "Sample,", token_length) == 0 ||
memcmp(token_start, "Sample:", token_length) == 0) {
state->engineering_sample = true; state->engineering_sample = true;
return false; return false;
} }
} }
break; break;
case 8: case 8:
/* Erase "QuadCore" in "VIA QuadCore L4700 @ 1.2+ GHz" */ /* Erase "QuadCore" in "VIA QuadCore L4700 @ 1.2+ GHz"
*/
if (erase_matching(token_start, token_length, "QuadCore")) { if (erase_matching(token_start, token_length, "QuadCore")) {
state->context_core = token_end; state->context_core = token_end;
return true; return true;
} }
/* Erase "Six-Core" in "AMD FX(tm)-6100 Six-Core Processor" */ /* Erase "Six-Core" in "AMD FX(tm)-6100 Six-Core
* Processor" */
if (erase_matching(token_start, token_length, "Six-Core")) { if (erase_matching(token_start, token_length, "Six-Core")) {
state->context_core = token_end; state->context_core = token_end;
return true; return true;
@ -429,7 +467,8 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
if (erase_matching(token_start, token_length, "processor")) { if (erase_matching(token_start, token_length, "processor")) {
return true; return true;
} }
/* Erase "Dual-Core" in "Pentium(R) Dual-Core CPU T4200 @ 2.00GHz" */ /* Erase "Dual-Core" in "Pentium(R) Dual-Core CPU T4200
* @ 2.00GHz" */
if (erase_matching(token_start, token_length, "Dual-Core")) { if (erase_matching(token_start, token_length, "Dual-Core")) {
state->context_core = token_end; state->context_core = token_end;
return true; return true;
@ -442,9 +481,9 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
state->context_core = token_end; state->context_core = token_end;
return true; return true;
} }
/* Erase "Transmeta" in brand string on Transmeta processors, e.g. /* Erase "Transmeta" in brand string on Transmeta
* "Transmeta(tm) Crusoe(tm) Processor TM5800" * processors, e.g. "Transmeta(tm) Crusoe(tm) Processor
* "Transmeta Efficeon(tm) Processor TM8000" * TM5800" "Transmeta Efficeon(tm) Processor TM8000"
*/ */
if (erase_matching(token_start, token_length, "Transmeta")) { if (erase_matching(token_start, token_length, "Transmeta")) {
return true; return true;
@ -471,8 +510,8 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
return true; return true;
} }
/* /*
* Remember to discard string if it contains "Engineering Sample", * Remember to discard string if it contains
* e.g. "AMD Engineering Sample" * "Engineering Sample", e.g. "AMD Engineering Sample"
*/ */
if (memcmp(token_start, "Engineering", token_length) == 0) { if (memcmp(token_start, "Engineering", token_length) == 0) {
state->context_engineering = token_start; state->context_engineering = token_start;
@ -484,31 +523,38 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
memset(token_start, ' ', token_length); memset(token_start, ' ', token_length);
return true; return true;
} }
/* On some Intel processors the last letter of the name is put before the number, /* On some Intel processors the last letter of the name is put before
* and an additional space it added, e.g. * the number, and an additional space it added, e.g. "Intel(R) Core(TM)
* "Intel(R) Core(TM) i7 CPU X 990 @ 3.47GHz" * i7 CPU X 990 @ 3.47GHz" "Intel(R) Core(TM) CPU Q 820 @ 1.73GHz"
* "Intel(R) Core(TM) CPU Q 820 @ 1.73GHz" * "Intel(R) Core(TM) i5 CPU M 480 @ 2.67GHz" We fix this issue, i.e.
* "Intel(R) Core(TM) i5 CPU M 480 @ 2.67GHz" * "X 990" -> "990X", "Q 820"
* We fix this issue, i.e. "X 990" -> "990X", "Q 820" -> "820Q" * -> "820Q"
*/ */
if (previousState.context_upper_letter != 0) { if (previousState.context_upper_letter != 0) {
/* A single letter token followed by 2-to-5 digit letter is merged together */ /* A single letter token followed by 2-to-5 digit letter is
* merged together
*/
switch (token_length) { switch (token_length) {
case 2: case 2:
case 3: case 3:
case 4: case 4:
case 5: case 5:
if (is_number(token_start, token_end)) { if (is_number(token_start, token_end)) {
/* Load the previous single-letter token */ /* Load the previous single-letter token
*/
const char letter = *previousState.context_upper_letter; const char letter = *previousState.context_upper_letter;
/* Erase the previous single-letter token */ /* Erase the previous single-letter
* token */
*previousState.context_upper_letter = ' '; *previousState.context_upper_letter = ' ';
/* Move the current token one position to the left */ /* Move the current token one position
* to the left */
move_token(token_start, token_end, token_start - 1); move_token(token_start, token_end, token_start - 1);
token_start -= 1; token_start -= 1;
/* /*
* Add the letter on the end * Add the letter on the end
* Note: accessing token_start[-1] is safe because this is not the first token * Note: accessing token_start[-1] is
* safe because this is not the first
* token
*/ */
token_end[-1] = letter; token_end[-1] = letter;
} }
@ -525,23 +571,22 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
return true; return true;
} }
uint32_t cpuinfo_x86_normalize_brand_string( uint32_t cpuinfo_x86_normalize_brand_string(const char raw_name[48], char normalized_name[48]) {
const char raw_name[48],
char normalized_name[48])
{
normalized_name[0] = '\0'; normalized_name[0] = '\0';
char name[48]; char name[48];
memcpy(name, raw_name, sizeof(name)); memcpy(name, raw_name, sizeof(name));
/* /*
* First find the end of the string * First find the end of the string
* Start search from the end because some brand strings contain zeroes in the middle * Start search from the end because some brand strings contain zeroes
* in the middle
*/ */
char* name_end = &name[48]; char* name_end = &name[48];
while (name_end[-1] == '\0') { while (name_end[-1] == '\0') {
/* /*
* Adject name_end by 1 position and check that we didn't reach the start of the brand string. * Adject name_end by 1 position and check that we didn't reach
* This is possible if all characters are zero. * the start of the brand string. This is possible if all
* characters are zero.
*/ */
if (--name_end == name) { if (--name_end == name) {
/* All characters are zeros */ /* All characters are zeros */
@ -549,9 +594,10 @@ uint32_t cpuinfo_x86_normalize_brand_string(
} }
} }
struct parser_state parser_state = { 0 }; struct parser_state parser_state = {0};
/* Now unify all whitespace characters: replace tabs and '\0' with spaces */ /* Now unify all whitespace characters: replace tabs and '\0' with
* spaces */
{ {
bool inside_parentheses = false; bool inside_parentheses = false;
for (char* char_ptr = name; char_ptr != name_end; char_ptr++) { for (char* char_ptr = name; char_ptr != name_end; char_ptr++) {
@ -611,7 +657,8 @@ uint32_t cpuinfo_x86_normalize_brand_string(
/* Check if there is some string before the frequency separator. */ /* Check if there is some string before the frequency separator. */
if (parser_state.frequency_separator != NULL) { if (parser_state.frequency_separator != NULL) {
if (is_space(name, parser_state.frequency_separator)) { if (is_space(name, parser_state.frequency_separator)) {
/* If only frequency is available, return empty string */ /* If only frequency is available, return empty string
*/
return 0; return 0;
} }
} }
@ -634,7 +681,8 @@ uint32_t cpuinfo_x86_normalize_brand_string(
*output_ptr++ = ' '; *output_ptr++ = ' ';
} }
output_ptr = move_token(token_start, char_ptr, output_ptr); output_ptr = move_token(token_start, char_ptr, output_ptr);
/* Note: char_ptr[-1] exists because there is a token before this space */ /* Note: char_ptr[-1] exists because
* there is a token before this space */
previous_token_ends_with_dash = (char_ptr[-1] == '-'); previous_token_ends_with_dash = (char_ptr[-1] == '-');
} }
} else { } else {
@ -662,7 +710,7 @@ uint32_t cpuinfo_x86_normalize_brand_string(
} else { } else {
normalized_name[47] = '\0'; normalized_name[47] = '\0';
} }
return (uint32_t) (output_ptr - normalized_name); return (uint32_t)(output_ptr - normalized_name);
} }
} }
@ -685,24 +733,22 @@ static const char* vendor_string_map[] = {
uint32_t cpuinfo_x86_format_package_name( uint32_t cpuinfo_x86_format_package_name(
enum cpuinfo_vendor vendor, enum cpuinfo_vendor vendor,
const char normalized_brand_string[48], const char normalized_brand_string[48],
char package_name[CPUINFO_PACKAGE_NAME_MAX]) char package_name[CPUINFO_PACKAGE_NAME_MAX]) {
{
if (normalized_brand_string[0] == '\0') { if (normalized_brand_string[0] == '\0') {
package_name[0] = '\0'; package_name[0] = '\0';
return 0; return 0;
} }
const char* vendor_string = NULL; const char* vendor_string = NULL;
if ((uint32_t) vendor < (uint32_t) CPUINFO_COUNT_OF(vendor_string_map)) { if ((uint32_t)vendor < (uint32_t)CPUINFO_COUNT_OF(vendor_string_map)) {
vendor_string = vendor_string_map[(uint32_t) vendor]; vendor_string = vendor_string_map[(uint32_t)vendor];
} }
if (vendor_string == NULL) { if (vendor_string == NULL) {
strncpy(package_name, normalized_brand_string, CPUINFO_PACKAGE_NAME_MAX); strncpy(package_name, normalized_brand_string, CPUINFO_PACKAGE_NAME_MAX);
package_name[CPUINFO_PACKAGE_NAME_MAX - 1] = '\0'; package_name[CPUINFO_PACKAGE_NAME_MAX - 1] = '\0';
return 0; return 0;
} else { } else {
snprintf(package_name, CPUINFO_PACKAGE_NAME_MAX, snprintf(package_name, CPUINFO_PACKAGE_NAME_MAX, "%s %s", vendor_string, normalized_brand_string);
"%s %s", vendor_string, normalized_brand_string); return (uint32_t)strlen(vendor_string) + 1;
return (uint32_t) strlen(vendor_string) + 1;
} }
} }

View File

@ -1,25 +1,23 @@
#include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <cpuinfo.h> #include <cpuinfo.h>
#include <cpuinfo/utils.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <cpuinfo/utils.h>
#include <x86/api.h> #include <x86/api.h>
#include <x86/cpuid.h> #include <x86/cpuid.h>
enum topology_type { enum topology_type {
topology_type_invalid = 0, topology_type_invalid = 0,
topology_type_smt = 1, topology_type_smt = 1,
topology_type_core = 2, topology_type_core = 2,
}; };
void cpuinfo_x86_detect_topology( void cpuinfo_x86_detect_topology(
uint32_t max_base_index, uint32_t max_base_index,
uint32_t max_extended_index, uint32_t max_extended_index,
struct cpuid_regs leaf1, struct cpuid_regs leaf1,
struct cpuinfo_x86_topology* topology) struct cpuinfo_x86_topology* topology) {
{
/* /*
* HTT: indicates multi-core/hyper-threading support on this core. * HTT: indicates multi-core/hyper-threading support on this core.
* - Intel, AMD: edx[bit 28] in basic info. * - Intel, AMD: edx[bit 28] in basic info.
@ -34,7 +32,8 @@ void cpuinfo_x86_detect_topology(
const struct cpuid_regs leaf0x80000001 = cpuid(UINT32_C(0x80000001)); const struct cpuid_regs leaf0x80000001 = cpuid(UINT32_C(0x80000001));
/* /*
* CmpLegacy: core multi-processing legacy mode. * CmpLegacy: core multi-processing legacy mode.
* - AMD: ecx[bit 1] in extended info (reserved bit on Intel CPUs). * - AMD: ecx[bit 1] in extended info (reserved bit on
* Intel CPUs).
*/ */
amd_cmp_legacy = !!(leaf0x80000001.ecx & UINT32_C(0x00000002)); amd_cmp_legacy = !!(leaf0x80000001.ecx & UINT32_C(0x00000002));
} }
@ -42,36 +41,52 @@ void cpuinfo_x86_detect_topology(
if (max_extended_index >= UINT32_C(0x80000008)) { if (max_extended_index >= UINT32_C(0x80000008)) {
const struct cpuid_regs leaf0x80000008 = cpuid(UINT32_C(0x80000008)); const struct cpuid_regs leaf0x80000008 = cpuid(UINT32_C(0x80000008));
/* /*
* NC: number of physical cores - 1. The number of cores in the processor is NC+1. * NC: number of physical cores - 1. The number
* - AMD: ecx[bits 0-7] in leaf 0x80000008 (reserved zero bits on Intel CPUs). * of cores in the processor is NC+1.
* - AMD: ecx[bits 0-7] in leaf 0x80000008
* (reserved zero bits on Intel CPUs).
*/ */
const uint32_t cores_per_processor = 1 + (leaf0x80000008.ecx & UINT32_C(0x000000FF)); const uint32_t cores_per_processor = 1 + (leaf0x80000008.ecx & UINT32_C(0x000000FF));
topology->core_bits_length = bit_length(cores_per_processor); topology->core_bits_length = bit_length(cores_per_processor);
cpuinfo_log_debug("HTT: APIC ID = %08"PRIx32", cores per processor = %"PRIu32, apic_id, cores_per_processor); cpuinfo_log_debug(
"HTT: APIC ID = %08" PRIx32 ", cores per processor = %" PRIu32,
apic_id,
cores_per_processor);
} else { } else {
/* /*
* LogicalProcessorCount: the number of cores per processor. * LogicalProcessorCount: the number of cores
* - AMD: ebx[bits 16-23] in basic info (different interpretation on Intel CPUs). * per processor.
* - AMD: ebx[bits 16-23] in basic info
* (different interpretation on Intel CPUs).
*/ */
const uint32_t cores_per_processor = (leaf1.ebx >> 16) & UINT32_C(0x000000FF); const uint32_t cores_per_processor = (leaf1.ebx >> 16) & UINT32_C(0x000000FF);
if (cores_per_processor != 0) { if (cores_per_processor != 0) {
topology->core_bits_length = bit_length(cores_per_processor); topology->core_bits_length = bit_length(cores_per_processor);
} }
cpuinfo_log_debug("HTT: APIC ID = %08"PRIx32", cores per processor = %"PRIu32, apic_id, cores_per_processor); cpuinfo_log_debug(
"HTT: APIC ID = %08" PRIx32 ", cores per processor = %" PRIu32,
apic_id,
cores_per_processor);
} }
} else { } else {
/* /*
* Maximum number of addressable IDs for logical processors in this physical package. * Maximum number of addressable IDs for logical
* - Intel: ebx[bits 16-23] in basic info (different interpretation on AMD CPUs). * processors in this physical package.
* - Intel: ebx[bits 16-23] in basic info (different
* interpretation on AMD CPUs).
*/ */
const uint32_t logical_processors = (leaf1.ebx >> 16) & UINT32_C(0x000000FF); const uint32_t logical_processors = (leaf1.ebx >> 16) & UINT32_C(0x000000FF);
if (logical_processors != 0) { if (logical_processors != 0) {
const uint32_t log2_max_logical_processors = bit_length(logical_processors); const uint32_t log2_max_logical_processors = bit_length(logical_processors);
const uint32_t log2_max_threads_per_core = log2_max_logical_processors - topology->core_bits_length; const uint32_t log2_max_threads_per_core =
log2_max_logical_processors - topology->core_bits_length;
topology->core_bits_offset = log2_max_threads_per_core; topology->core_bits_offset = log2_max_threads_per_core;
topology->thread_bits_length = log2_max_threads_per_core; topology->thread_bits_length = log2_max_threads_per_core;
} }
cpuinfo_log_debug("HTT: APIC ID = %08"PRIx32", logical processors = %"PRIu32, apic_id, logical_processors); cpuinfo_log_debug(
"HTT: APIC ID = %08" PRIx32 ", logical processors = %" PRIu32,
apic_id,
logical_processors);
} }
} }
@ -84,43 +99,64 @@ void cpuinfo_x86_detect_topology(
uint32_t level = 0; uint32_t level = 0;
uint32_t type; uint32_t type;
uint32_t total_shift = 0; uint32_t total_shift = 0;
topology->thread_bits_offset = topology->thread_bits_length = 0; topology->thread_bits_offset = topology->thread_bits_length = 0;
topology->core_bits_offset = topology->core_bits_length = 0; topology->core_bits_offset = topology->core_bits_length = 0;
do { do {
const struct cpuid_regs leafB = cpuidex(UINT32_C(0xB), level); const struct cpuid_regs leafB = cpuidex(UINT32_C(0xB), level);
type = (leafB.ecx >> 8) & UINT32_C(0x000000FF); type = (leafB.ecx >> 8) & UINT32_C(0x000000FF);
const uint32_t level_shift = leafB.eax & UINT32_C(0x0000001F); const uint32_t level_shift = leafB.eax & UINT32_C(0x0000001F);
const uint32_t x2apic_id = leafB.edx; const uint32_t x2apic_id = leafB.edx;
apic_id = x2apic_id; apic_id = x2apic_id;
switch (type) { switch (type) {
case topology_type_invalid: case topology_type_invalid:
break; break;
case topology_type_smt: case topology_type_smt:
cpuinfo_log_debug("x2 level %"PRIu32": APIC ID = %08"PRIx32", " cpuinfo_log_debug(
"type SMT, shift %"PRIu32", total shift %"PRIu32, "x2 level %" PRIu32 ": APIC ID = %08" PRIx32
level, apic_id, level_shift, total_shift); ", "
"type SMT, shift %" PRIu32 ", total shift %" PRIu32,
level,
apic_id,
level_shift,
total_shift);
topology->thread_bits_offset = total_shift; topology->thread_bits_offset = total_shift;
topology->thread_bits_length = level_shift; topology->thread_bits_length = level_shift;
break; break;
case topology_type_core: case topology_type_core:
cpuinfo_log_debug("x2 level %"PRIu32": APIC ID = %08"PRIx32", " cpuinfo_log_debug(
"type core, shift %"PRIu32", total shift %"PRIu32, "x2 level %" PRIu32 ": APIC ID = %08" PRIx32
level, apic_id, level_shift, total_shift); ", "
"type core, shift %" PRIu32 ", total shift %" PRIu32,
level,
apic_id,
level_shift,
total_shift);
topology->core_bits_offset = total_shift; topology->core_bits_offset = total_shift;
topology->core_bits_length = level_shift; topology->core_bits_length = level_shift;
break; break;
default: default:
cpuinfo_log_warning("unexpected topology type %"PRIu32" (offset %"PRIu32", length %"PRIu32") " cpuinfo_log_warning(
"reported in leaf 0x0000000B is ignored", type, total_shift, level_shift); "unexpected topology type %" PRIu32 " (offset %" PRIu32
", length %" PRIu32
") "
"reported in leaf 0x0000000B is ignored",
type,
total_shift,
level_shift);
break; break;
} }
total_shift += level_shift; total_shift += level_shift;
level += 1; level += 1;
} while (type != 0); } while (type != 0);
cpuinfo_log_debug("x2APIC ID 0x%08"PRIx32", " cpuinfo_log_debug(
"SMT offset %"PRIu32" length %"PRIu32", core offset %"PRIu32" length %"PRIu32, apic_id, "x2APIC ID 0x%08" PRIx32
topology->thread_bits_offset, topology->thread_bits_length, ", "
topology->core_bits_offset, topology->core_bits_length); "SMT offset %" PRIu32 " length %" PRIu32 ", core offset %" PRIu32 " length %" PRIu32,
apic_id,
topology->thread_bits_offset,
topology->thread_bits_length,
topology->core_bits_offset,
topology->core_bits_length);
} }
topology->apic_id = apic_id; topology->apic_id = apic_id;

View File

@ -3,11 +3,9 @@
#include <cpuinfo.h> #include <cpuinfo.h>
#include <x86/api.h> #include <x86/api.h>
enum cpuinfo_uarch cpuinfo_x86_decode_uarch( enum cpuinfo_uarch cpuinfo_x86_decode_uarch(
enum cpuinfo_vendor vendor, enum cpuinfo_vendor vendor,
const struct cpuinfo_x86_model_info* model_info) const struct cpuinfo_x86_model_info* model_info) {
{
switch (vendor) { switch (vendor) {
case cpuinfo_vendor_intel: case cpuinfo_vendor_intel:
switch (model_info->family) { switch (model_info->family) {
@ -15,8 +13,12 @@ enum cpuinfo_uarch cpuinfo_x86_decode_uarch(
case 0x05: case 0x05:
switch (model_info->model) { switch (model_info->model) {
case 0x01: // Pentium (60, 66) case 0x01: // Pentium (60, 66)
case 0x02: // Pentium (75, 90, 100, 120, 133, 150, 166, 200) case 0x02: // Pentium (75, 90,
case 0x03: // Pentium OverDrive for Intel486-based systems // 100, 120, 133,
// 150, 166, 200)
case 0x03: // Pentium OverDrive
// for Intel486-based
// systems
case 0x04: // Pentium MMX case 0x04: // Pentium MMX
return cpuinfo_uarch_p5; return cpuinfo_uarch_p5;
case 0x09: case 0x09:
@ -29,39 +31,109 @@ enum cpuinfo_uarch cpuinfo_x86_decode_uarch(
/* Mainstream cores */ /* Mainstream cores */
#if CPUINFO_ARCH_X86 #if CPUINFO_ARCH_X86
case 0x01: // Pentium Pro case 0x01: // Pentium Pro
case 0x03: // Pentium II (Klamath) and Pentium II Overdrive case 0x03: // Pentium II
case 0x05: // Pentium II (Deschutes, Tonga), Pentium II Celeron (Covington), Pentium II Xeon (Drake) // (Klamath) and
case 0x06: // Pentium II (Dixon), Pentium II Celeron (Mendocino) // Pentium II
case 0x07: // Pentium III (Katmai), Pentium III Xeon (Tanner) // Overdrive
case 0x08: // Pentium III (Coppermine), Pentium II Celeron (Coppermine-128), Pentium III Xeon (Cascades) case 0x05: // Pentium II
case 0x0A: // Pentium III Xeon (Cascades-2MB) // (Deschutes,
case 0x0B: // Pentium III (Tualatin), Pentium III Celeron (Tualatin-256) // Tonga), Pentium II
// Celeron
// (Covington),
// Pentium II Xeon
// (Drake)
case 0x06: // Pentium II
// (Dixon), Pentium
// II Celeron
// (Mendocino)
case 0x07: // Pentium III
// (Katmai), Pentium
// III Xeon (Tanner)
case 0x08: // Pentium III
// (Coppermine),
// Pentium II Celeron
// (Coppermine-128),
// Pentium III Xeon
// (Cascades)
case 0x0A: // Pentium III Xeon
// (Cascades-2MB)
case 0x0B: // Pentium III
// (Tualatin),
// Pentium III
// Celeron
// (Tualatin-256)
return cpuinfo_uarch_p6; return cpuinfo_uarch_p6;
case 0x09: // Pentium M (Banias), Pentium M Celeron (Banias-0, Banias-512) case 0x09: // Pentium M
case 0x0D: // Pentium M (Dothan), Pentium M Celeron (Dothan-512, Dothan-1024) // (Banias), Pentium
case 0x15: // Intel 80579 (Tolapai) // M Celeron
// (Banias-0,
// Banias-512)
case 0x0D: // Pentium M
// (Dothan), Pentium
// M Celeron
// (Dothan-512,
// Dothan-1024)
case 0x15: // Intel 80579
// (Tolapai)
return cpuinfo_uarch_dothan; return cpuinfo_uarch_dothan;
case 0x0E: // Core Solo/Duo (Yonah), Pentium Dual-Core T2xxx (Yonah), Celeron M (Yonah-512, Yonah-1024), Dual-Core Xeon (Sossaman) case 0x0E: // Core Solo/Duo
// (Yonah), Pentium
// Dual-Core T2xxx
// (Yonah), Celeron M
// (Yonah-512,
// Yonah-1024),
// Dual-Core Xeon
// (Sossaman)
return cpuinfo_uarch_yonah; return cpuinfo_uarch_yonah;
#endif /* CPUINFO_ARCH_X86 */ #endif /* CPUINFO_ARCH_X86 */
case 0x0F: // Core 2 Duo (Conroe, Conroe-2M, Merom), Core 2 Quad (Tigerton), Xeon (Woodcrest, Clovertown, Kentsfield) case 0x0F: // Core 2 Duo
case 0x16: // Celeron (Conroe-L, Merom-L), Core 2 Duo (Merom) // (Conroe,
// Conroe-2M, Merom),
// Core 2 Quad
// (Tigerton), Xeon
// (Woodcrest,
// Clovertown,
// Kentsfield)
case 0x16: // Celeron (Conroe-L,
// Merom-L), Core 2
// Duo (Merom)
return cpuinfo_uarch_conroe; return cpuinfo_uarch_conroe;
case 0x17: // Core 2 Duo (Penryn-3M), Core 2 Quad (Yorkfield), Core 2 Extreme (Yorkfield), Xeon (Harpertown), Pentium Dual-Core (Penryn) case 0x17: // Core 2 Duo
// (Penryn-3M), Core
// 2 Quad
// (Yorkfield), Core
// 2 Extreme
// (Yorkfield), Xeon
// (Harpertown),
// Pentium Dual-Core
// (Penryn)
case 0x1D: // Xeon (Dunnington) case 0x1D: // Xeon (Dunnington)
return cpuinfo_uarch_penryn; return cpuinfo_uarch_penryn;
case 0x1A: // Core iX (Bloomfield), Xeon (Gainestown) case 0x1A: // Core iX
case 0x1E: // Core iX (Lynnfield, Clarksfield) // (Bloomfield), Xeon
case 0x1F: // Core iX (Havendale) // (Gainestown)
case 0x1E: // Core iX
// (Lynnfield,
// Clarksfield)
case 0x1F: // Core iX
// (Havendale)
case 0x2E: // Xeon (Beckton) case 0x2E: // Xeon (Beckton)
case 0x25: // Core iX (Clarkdale) case 0x25: // Core iX
case 0x2C: // Core iX (Gulftown), Xeon (Gulftown) // (Clarkdale)
case 0x2C: // Core iX
// (Gulftown), Xeon
// (Gulftown)
case 0x2F: // Xeon (Eagleton) case 0x2F: // Xeon (Eagleton)
return cpuinfo_uarch_nehalem; return cpuinfo_uarch_nehalem;
case 0x2A: // Core iX (Sandy Bridge) case 0x2A: // Core iX (Sandy
case 0x2D: // Core iX (Sandy Bridge-E), Xeon (Sandy Bridge EP/EX) // Bridge)
case 0x2D: // Core iX (Sandy
// Bridge-E), Xeon
// (Sandy Bridge
// EP/EX)
return cpuinfo_uarch_sandy_bridge; return cpuinfo_uarch_sandy_bridge;
case 0x3A: // Core iX (Ivy Bridge) case 0x3A: // Core iX (Ivy
// Bridge)
case 0x3E: // Ivy Bridge-E case 0x3E: // Ivy Bridge-E
return cpuinfo_uarch_ivy_bridge; return cpuinfo_uarch_ivy_bridge;
case 0x3C: case 0x3C:
@ -74,15 +146,21 @@ enum cpuinfo_uarch cpuinfo_x86_decode_uarch(
case 0x4F: // Broadwell-E case 0x4F: // Broadwell-E
case 0x56: // Broadwell-DE case 0x56: // Broadwell-DE
return cpuinfo_uarch_broadwell; return cpuinfo_uarch_broadwell;
case 0x4E: // Sky Lake Client Y/U case 0x4E: // Sky Lake Client
case 0x55: // Sky/Cascade/Cooper Lake Server // Y/U
case 0x5E: // Sky Lake Client DT/H/S case 0x55: // Sky/Cascade/Cooper
case 0x8E: // Kaby/Whiskey/Amber/Comet Lake Y/U // Lake Server
case 0x9E: // Kaby/Coffee Lake DT/H/S case 0x5E: // Sky Lake Client
// DT/H/S
case 0x8E: // Kaby/Whiskey/Amber/Comet
// Lake Y/U
case 0x9E: // Kaby/Coffee Lake
// DT/H/S
case 0xA5: // Comet Lake H/S case 0xA5: // Comet Lake H/S
case 0xA6: // Comet Lake U/Y case 0xA6: // Comet Lake U/Y
return cpuinfo_uarch_sky_lake; return cpuinfo_uarch_sky_lake;
case 0x66: // Cannon Lake (Core i3-8121U) case 0x66: // Cannon Lake (Core
// i3-8121U)
return cpuinfo_uarch_palm_cove; return cpuinfo_uarch_palm_cove;
case 0x6A: // Ice Lake-DE case 0x6A: // Ice Lake-DE
case 0x6C: // Ice Lake-SP case 0x6C: // Ice Lake-SP
@ -91,12 +169,15 @@ enum cpuinfo_uarch cpuinfo_x86_decode_uarch(
return cpuinfo_uarch_sunny_cove; return cpuinfo_uarch_sunny_cove;
/* Low-power cores */ /* Low-power cores */
case 0x1C: // Diamondville, Silverthorne, Pineview case 0x1C: // Diamondville,
// Silverthorne,
// Pineview
case 0x26: // Tunnel Creek case 0x26: // Tunnel Creek
return cpuinfo_uarch_bonnell; return cpuinfo_uarch_bonnell;
case 0x27: // Medfield case 0x27: // Medfield
case 0x35: // Cloverview case 0x35: // Cloverview
case 0x36: // Cedarview, Centerton case 0x36: // Cedarview,
// Centerton
return cpuinfo_uarch_saltwell; return cpuinfo_uarch_saltwell;
case 0x37: // Bay Trail case 0x37: // Bay Trail
case 0x4A: // Merrifield case 0x4A: // Merrifield
@ -104,8 +185,10 @@ enum cpuinfo_uarch cpuinfo_x86_decode_uarch(
case 0x5A: // Moorefield case 0x5A: // Moorefield
case 0x5D: // SoFIA case 0x5D: // SoFIA
return cpuinfo_uarch_silvermont; return cpuinfo_uarch_silvermont;
case 0x4C: // Braswell, Cherry Trail case 0x4C: // Braswell, Cherry
case 0x75: // Spreadtrum SC9853I-IA // Trail
case 0x75: // Spreadtrum
// SC9853I-IA
return cpuinfo_uarch_airmont; return cpuinfo_uarch_airmont;
case 0x5C: // Apollo Lake case 0x5C: // Apollo Lake
case 0x5F: // Denverton case 0x5F: // Denverton
@ -122,14 +205,48 @@ enum cpuinfo_uarch cpuinfo_x86_decode_uarch(
break; break;
case 0x0F: case 0x0F:
switch (model_info->model) { switch (model_info->model) {
case 0x00: // Pentium 4 Xeon (Foster) case 0x00: // Pentium 4 Xeon
case 0x01: // Pentium 4 Celeron (Willamette-128), Pentium 4 Xeon (Foster, Foster MP) // (Foster)
case 0x02: // Pentium 4 (Northwood), Pentium 4 EE (Gallatin), Pentium 4 Celeron (Northwood-128, Northwood-256), Pentium 4 Xeon (Gallatin DP, Prestonia) case 0x01: // Pentium 4 Celeron
// (Willamette-128),
// Pentium 4 Xeon
// (Foster, Foster
// MP)
case 0x02: // Pentium 4
// (Northwood),
// Pentium 4 EE
// (Gallatin),
// Pentium 4 Celeron
// (Northwood-128,
// Northwood-256),
// Pentium 4 Xeon
// (Gallatin DP,
// Prestonia)
return cpuinfo_uarch_willamette; return cpuinfo_uarch_willamette;
break; break;
case 0x03: // Pentium 4 (Prescott), Pentium 4 Xeon (Nocona) case 0x03: // Pentium 4
case 0x04: // Pentium 4 (Prescott-2M), Pentium 4 EE (Prescott-2M), Pentium D (Smithfield), Celeron D (Prescott-256), Pentium 4 Xeon (Cranford, Irwindale, Paxville) // (Prescott),
case 0x06: // Pentium 4 (Cedar Mill), Pentium D EE (Presler), Celeron D (Cedar Mill), Pentium 4 Xeon (Dempsey, Tulsa) // Pentium 4 Xeon
// (Nocona)
case 0x04: // Pentium 4
// (Prescott-2M),
// Pentium 4 EE
// (Prescott-2M),
// Pentium D
// (Smithfield),
// Celeron D
// (Prescott-256),
// Pentium 4 Xeon
// (Cranford,
// Irwindale,
// Paxville)
case 0x06: // Pentium 4 (Cedar
// Mill), Pentium D
// EE (Presler),
// Celeron D (Cedar
// Mill), Pentium 4
// Xeon (Dempsey,
// Tulsa)
return cpuinfo_uarch_prescott; return cpuinfo_uarch_prescott;
} }
break; break;
@ -166,8 +283,10 @@ enum cpuinfo_uarch cpuinfo_x86_decode_uarch(
return cpuinfo_uarch_bobcat; return cpuinfo_uarch_bobcat;
case 0x15: case 0x15:
switch (model_info->model) { switch (model_info->model) {
case 0x00: // Engineering samples case 0x00: // Engineering
case 0x01: // Zambezi, Interlagos // samples
case 0x01: // Zambezi,
// Interlagos
return cpuinfo_uarch_bulldozer; return cpuinfo_uarch_bulldozer;
case 0x02: // Vishera case 0x02: // Vishera
case 0x10: // Trinity case 0x10: // Trinity
@ -184,11 +303,19 @@ enum cpuinfo_uarch cpuinfo_x86_decode_uarch(
switch (model_info->extended_model) { switch (model_info->extended_model) {
case 0x0: case 0x0:
return cpuinfo_uarch_bulldozer; return cpuinfo_uarch_bulldozer;
case 0x1: // No L3 cache case 0x1: // No
case 0x2: // With L3 cache // L3
// cache
case 0x2: // With
// L3
// cache
return cpuinfo_uarch_piledriver; return cpuinfo_uarch_piledriver;
case 0x3: // With L3 cache case 0x3: // With
case 0x4: // No L3 cache // L3
// cache
case 0x4: // No
// L3
// cache
return cpuinfo_uarch_steamroller; return cpuinfo_uarch_steamroller;
} }
break; break;
@ -202,29 +329,61 @@ enum cpuinfo_uarch cpuinfo_x86_decode_uarch(
} }
case 0x17: case 0x17:
switch (model_info->extended_model) { switch (model_info->extended_model) {
case 0x0: // model 01h -> 14 nm Naples/Whitehaven/Summit Ridge/Snowy Owl, model 08h -> 12 nm Colfax/Pinnacle Ridge case 0x0: // model 01h -> 14 nm
case 0x1: // model 11h -> 14 nm Raven Ridge/Great Horned Owl, model 18h -> 14 nm Banded Kestrel / 12 nm Picasso // Naples/Whitehaven/Summit
// Ridge/Snowy Owl,
// model 08h -> 12 nm
// Colfax/Pinnacle
// Ridge
case 0x1: // model 11h -> 14 nm
// Raven Ridge/Great
// Horned Owl, model
// 18h -> 14 nm Banded
// Kestrel / 12 nm
// Picasso
return cpuinfo_uarch_zen; return cpuinfo_uarch_zen;
case 0x3: // model 31h -> Rome/Castle Peak case 0x3: // model 31h ->
case 0x4: // model 47h -> Xbox Series X // Rome/Castle Peak
case 0x6: // model 60h -> Renoir/Grey Hawk, model 68h -> Lucienne case 0x4: // model 47h -> Xbox
case 0x7: // model 71h -> Matisse // Series X
case 0x9: // model 90h -> Van Gogh, model 98h -> Mero case 0x6: // model 60h ->
// Renoir/Grey Hawk,
// model 68h ->
// Lucienne
case 0x7: // model 71h ->
// Matisse
case 0x9: // model 90h -> Van
// Gogh, model 98h ->
// Mero
return cpuinfo_uarch_zen2; return cpuinfo_uarch_zen2;
} }
break; break;
case 0x19: case 0x19:
switch (model_info->extended_model) { switch (model_info->extended_model) {
case 0x0: // model 00h -> Genesis, model 01h -> Milan, model 08h -> Chagall case 0x0: // model 00h ->
case 0x2: // model 21h -> Vermeer // Genesis, model 01h
case 0x3: // model 30h -> Badami, Trento // -> Milan, model 08h
case 0x4: // model 40h -> Rembrandt // -> Chagall
case 0x5: // model 50h -> Cezanne case 0x2: // model 21h ->
// Vermeer
case 0x3: // model 30h ->
// Badami, Trento
case 0x4: // model 40h ->
// Rembrandt
case 0x5: // model 50h ->
// Cezanne
return cpuinfo_uarch_zen3; return cpuinfo_uarch_zen3;
case 0x1: // model 10h..1Fh -> Stones case 0x1: // model 10h..1Fh ->
case 0x6: // model 60h..6Fh -> Raphael // Stones
case 0x7: // model 70h..77h -> Phoenix/Hawkpoint1, model 78h..7Fh -> Phoenix 2/Hawkpoint2 case 0x6: // model 60h..6Fh ->
case 0xA: // model A0h..AFh -> Stones-Dense // Raphael
case 0x7: // model 70h..77h ->
// Phoenix/Hawkpoint1,
// model 78h..7Fh ->
// Phoenix
// 2/Hawkpoint2
case 0xA: // model A0h..AFh ->
// Stones-Dense
return cpuinfo_uarch_zen4; return cpuinfo_uarch_zen4;
} }
break; break;

View File

@ -3,7 +3,6 @@
#include <cpuinfo.h> #include <cpuinfo.h>
#include <x86/api.h> #include <x86/api.h>
/* Intel vendor string: "GenuineIntel" */ /* Intel vendor string: "GenuineIntel" */
#define Genu UINT32_C(0x756E6547) #define Genu UINT32_C(0x756E6547)
#define ineI UINT32_C(0x49656E69) #define ineI UINT32_C(0x49656E69)
@ -15,8 +14,8 @@
#define cAMD UINT32_C(0x444D4163) #define cAMD UINT32_C(0x444D4163)
#define AMDi UINT32_C(0x69444D41) #define AMDi UINT32_C(0x69444D41)
#define sbet UINT32_C(0x74656273) #define sbet UINT32_C(0x74656273)
#define ter UINT32_C(0x21726574) #define ter UINT32_C(0x21726574)
#define AMD UINT32_C(0x20444D41) #define AMD UINT32_C(0x20444D41)
#define ISBE UINT32_C(0x45425349) #define ISBE UINT32_C(0x45425349)
#define TTER UINT32_C(0x52455454) #define TTER UINT32_C(0x52455454)
@ -24,7 +23,7 @@
#define Cent UINT32_C(0x746E6543) #define Cent UINT32_C(0x746E6543)
#define aurH UINT32_C(0x48727561) #define aurH UINT32_C(0x48727561)
#define auls UINT32_C(0x736C7561) #define auls UINT32_C(0x736C7561)
#define VIA UINT32_C(0x20414956) #define VIA UINT32_C(0x20414956)
/* Hygon vendor string: "HygonGenuine" */ /* Hygon vendor string: "HygonGenuine" */
#define Hygo UINT32_C(0x6F677948) #define Hygo UINT32_C(0x6F677948)
@ -49,10 +48,10 @@
/* NSC vendor string: "Geode by NSC" */ /* NSC vendor string: "Geode by NSC" */
#define Geod UINT32_C(0x646F6547) #define Geod UINT32_C(0x646F6547)
#define e_by UINT32_C(0x79622065) #define e_by UINT32_C(0x79622065)
#define NSC UINT32_C(0x43534E20) #define NSC UINT32_C(0x43534E20)
/* SiS vendor string: "SiS SiS SiS " */ /* SiS vendor string: "SiS SiS SiS " */
#define SiS UINT32_C(0x20536953) #define SiS UINT32_C(0x20536953)
/* NexGen vendor string: "NexGenDriven" */ /* NexGen vendor string: "NexGenDriven" */
#define NexG UINT32_C(0x4778654E) #define NexG UINT32_C(0x4778654E)
@ -60,17 +59,16 @@
#define iven UINT32_C(0x6E657669) #define iven UINT32_C(0x6E657669)
/* UMC vendor string: "UMC UMC UMC " */ /* UMC vendor string: "UMC UMC UMC " */
#define UMC UINT32_C(0x20434D55) #define UMC UINT32_C(0x20434D55)
/* RDC vendor string: "Genuine RDC" */ /* RDC vendor string: "Genuine RDC" */
#define ine UINT32_C(0x20656E69) #define ine UINT32_C(0x20656E69)
#define RDC UINT32_C(0x43445220) #define RDC UINT32_C(0x43445220)
/* D&MP vendor string: "Vortex86 SoC" */ /* D&MP vendor string: "Vortex86 SoC" */
#define Vort UINT32_C(0x74726F56) #define Vort UINT32_C(0x74726F56)
#define ex86 UINT32_C(0x36387865) #define ex86 UINT32_C(0x36387865)
#define SoC UINT32_C(0x436F5320) #define SoC UINT32_C(0x436F5320)
enum cpuinfo_vendor cpuinfo_x86_decode_vendor(uint32_t ebx, uint32_t ecx, uint32_t edx) { enum cpuinfo_vendor cpuinfo_x86_decode_vendor(uint32_t ebx, uint32_t ecx, uint32_t edx) {
switch (ebx) { switch (ebx) {

View File

@ -9,15 +9,17 @@
struct cpuinfo_arm_linux_processor { struct cpuinfo_arm_linux_processor {
/** /**
* Minimum processor ID on the package which includes this logical processor. * Minimum processor ID on the package which includes this logical
* This value can serve as an ID for the cluster of logical processors: it is the * processor. This value can serve as an ID for the cluster of logical
* same for all logical processors on the same package. * processors: it is the same for all logical processors on the same
* package.
*/ */
uint32_t package_leader_id; uint32_t package_leader_id;
/** /**
* Minimum processor ID on the core which includes this logical processor. * Minimum processor ID on the core which includes this logical
* This value can serve as an ID for the cluster of logical processors: it is the * processor. This value can serve as an ID for the cluster of logical
* same for all logical processors on the same package. * processors: it is the same for all logical processors on the same
* package.
*/ */
/** /**
* Number of logical processors in the package. * Number of logical processors in the package.
@ -25,14 +27,16 @@ struct cpuinfo_arm_linux_processor {
uint32_t package_processor_count; uint32_t package_processor_count;
/** /**
* Maximum frequency, in kHZ. * Maximum frequency, in kHZ.
* The value is parsed from /sys/devices/system/cpu/cpu<N>/cpufreq/cpuinfo_max_freq * The value is parsed from
* If failed to read or parse the file, the value is 0. * /sys/devices/system/cpu/cpu<N>/cpufreq/cpuinfo_max_freq If failed to
* read or parse the file, the value is 0.
*/ */
uint32_t max_frequency; uint32_t max_frequency;
/** /**
* Minimum frequency, in kHZ. * Minimum frequency, in kHZ.
* The value is parsed from /sys/devices/system/cpu/cpu<N>/cpufreq/cpuinfo_min_freq * The value is parsed from
* If failed to read or parse the file, the value is 0. * /sys/devices/system/cpu/cpu<N>/cpufreq/cpuinfo_min_freq If failed to
* read or parse the file, the value is 0.
*/ */
uint32_t min_frequency; uint32_t min_frequency;
/** Linux processor ID */ /** Linux processor ID */

View File

@ -1,38 +1,37 @@
#include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <cpuinfo.h> #include <cpuinfo.h>
#include <x86/api.h>
#include <cpuinfo/internal-api.h> #include <cpuinfo/internal-api.h>
#include <cpuinfo/log.h> #include <cpuinfo/log.h>
#include <x86/api.h>
#include <windows.h> #include <windows.h>
#ifdef __GNUC__ #ifdef __GNUC__
#define CPUINFO_ALLOCA __builtin_alloca #define CPUINFO_ALLOCA __builtin_alloca
#else #else
#define CPUINFO_ALLOCA _alloca #define CPUINFO_ALLOCA _alloca
#endif #endif
static inline uint32_t bit_mask(uint32_t bits) { static inline uint32_t bit_mask(uint32_t bits) {
return (UINT32_C(1) << bits) - UINT32_C(1); return (UINT32_C(1) << bits) - UINT32_C(1);
} }
static inline uint32_t low_index_from_kaffinity(KAFFINITY kaffinity) { static inline uint32_t low_index_from_kaffinity(KAFFINITY kaffinity) {
#if defined(_M_X64) || defined(_M_AMD64) #if defined(_M_X64) || defined(_M_AMD64)
unsigned long index; unsigned long index;
_BitScanForward64(&index, (unsigned __int64) kaffinity); _BitScanForward64(&index, (unsigned __int64)kaffinity);
return (uint32_t) index; return (uint32_t)index;
#elif defined(_M_IX86) #elif defined(_M_IX86)
unsigned long index; unsigned long index;
_BitScanForward(&index, (unsigned long) kaffinity); _BitScanForward(&index, (unsigned long)kaffinity);
return (uint32_t) index; return (uint32_t)index;
#else #else
#error Platform-specific implementation required #error Platform-specific implementation required
#endif #endif
} }
static void cpuinfo_x86_count_caches( static void cpuinfo_x86_count_caches(
@ -43,14 +42,13 @@ static void cpuinfo_x86_count_caches(
uint32_t* l1d_count_ptr, uint32_t* l1d_count_ptr,
uint32_t* l2_count_ptr, uint32_t* l2_count_ptr,
uint32_t* l3_count_ptr, uint32_t* l3_count_ptr,
uint32_t* l4_count_ptr) uint32_t* l4_count_ptr) {
{
uint32_t l1i_count = 0, l1d_count = 0, l2_count = 0, l3_count = 0, l4_count = 0; uint32_t l1i_count = 0, l1d_count = 0, l2_count = 0, l3_count = 0, l4_count = 0;
uint32_t last_l1i_id = UINT32_MAX, last_l1d_id = UINT32_MAX; uint32_t last_l1i_id = UINT32_MAX, last_l1d_id = UINT32_MAX;
uint32_t last_l2_id = UINT32_MAX, last_l3_id = UINT32_MAX, last_l4_id = UINT32_MAX; uint32_t last_l2_id = UINT32_MAX, last_l3_id = UINT32_MAX, last_l4_id = UINT32_MAX;
for (uint32_t i = 0; i < processors_count; i++) { for (uint32_t i = 0; i < processors_count; i++) {
const uint32_t apic_id = processors[i].apic_id; const uint32_t apic_id = processors[i].apic_id;
cpuinfo_log_debug("APID ID %"PRIu32": logical processor %"PRIu32, apic_id, i); cpuinfo_log_debug("APID ID %" PRIu32 ": logical processor %" PRIu32, apic_id, i);
if (x86_processor->cache.l1i.size != 0) { if (x86_processor->cache.l1i.size != 0) {
const uint32_t l1i_id = apic_id & ~bit_mask(x86_processor->cache.l1i.apic_bits); const uint32_t l1i_id = apic_id & ~bit_mask(x86_processor->cache.l1i.apic_bits);
@ -90,9 +88,9 @@ static void cpuinfo_x86_count_caches(
} }
*l1i_count_ptr = l1i_count; *l1i_count_ptr = l1i_count;
*l1d_count_ptr = l1d_count; *l1d_count_ptr = l1d_count;
*l2_count_ptr = l2_count; *l2_count_ptr = l2_count;
*l3_count_ptr = l3_count; *l3_count_ptr = l3_count;
*l4_count_ptr = l4_count; *l4_count_ptr = l4_count;
} }
static bool cpuinfo_x86_windows_is_wine(void) { static bool cpuinfo_x86_windows_is_wine(void) {
@ -126,36 +124,38 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
cpuinfo_x86_normalize_brand_string(x86_processor.brand_string, brand_string); cpuinfo_x86_normalize_brand_string(x86_processor.brand_string, brand_string);
const uint32_t thread_bits_mask = bit_mask(x86_processor.topology.thread_bits_length); const uint32_t thread_bits_mask = bit_mask(x86_processor.topology.thread_bits_length);
const uint32_t core_bits_mask = bit_mask(x86_processor.topology.core_bits_length); const uint32_t core_bits_mask = bit_mask(x86_processor.topology.core_bits_length);
const uint32_t package_bits_offset = max( const uint32_t package_bits_offset =
x86_processor.topology.thread_bits_offset + x86_processor.topology.thread_bits_length, max(x86_processor.topology.thread_bits_offset + x86_processor.topology.thread_bits_length,
x86_processor.topology.core_bits_offset + x86_processor.topology.core_bits_length); x86_processor.topology.core_bits_offset + x86_processor.topology.core_bits_length);
/* WINE doesn't implement GetMaximumProcessorGroupCount and aborts when calling it */ /* WINE doesn't implement GetMaximumProcessorGroupCount and aborts when
const uint32_t max_group_count = is_wine ? 1 : (uint32_t) GetMaximumProcessorGroupCount(); * calling it */
cpuinfo_log_debug("detected %"PRIu32" processor groups", max_group_count); const uint32_t max_group_count = is_wine ? 1 : (uint32_t)GetMaximumProcessorGroupCount();
cpuinfo_log_debug("detected %" PRIu32 " processor groups", max_group_count);
uint32_t processors_count = 0; uint32_t processors_count = 0;
uint32_t* processors_per_group = (uint32_t*) CPUINFO_ALLOCA(max_group_count * sizeof(uint32_t)); uint32_t* processors_per_group = (uint32_t*)CPUINFO_ALLOCA(max_group_count * sizeof(uint32_t));
for (uint32_t i = 0; i < max_group_count; i++) { for (uint32_t i = 0; i < max_group_count; i++) {
processors_per_group[i] = GetMaximumProcessorCount((WORD) i); processors_per_group[i] = GetMaximumProcessorCount((WORD)i);
cpuinfo_log_debug("detected %"PRIu32" processors in group %"PRIu32, cpuinfo_log_debug("detected %" PRIu32 " processors in group %" PRIu32, processors_per_group[i], i);
processors_per_group[i], i);
processors_count += processors_per_group[i]; processors_count += processors_per_group[i];
} }
uint32_t* processors_before_group = (uint32_t*) CPUINFO_ALLOCA(max_group_count * sizeof(uint32_t)); uint32_t* processors_before_group = (uint32_t*)CPUINFO_ALLOCA(max_group_count * sizeof(uint32_t));
for (uint32_t i = 0, count = 0; i < max_group_count; i++) { for (uint32_t i = 0, count = 0; i < max_group_count; i++) {
processors_before_group[i] = count; processors_before_group[i] = count;
cpuinfo_log_debug("detected %"PRIu32" processors before group %"PRIu32, cpuinfo_log_debug(
processors_before_group[i], i); "detected %" PRIu32 " processors before group %" PRIu32, processors_before_group[i], i);
count += processors_per_group[i]; count += processors_per_group[i];
} }
processors = HeapAlloc(heap, HEAP_ZERO_MEMORY, processors_count * sizeof(struct cpuinfo_processor)); processors = HeapAlloc(heap, HEAP_ZERO_MEMORY, processors_count * sizeof(struct cpuinfo_processor));
if (processors == NULL) { if (processors == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" logical processors", cpuinfo_log_error(
processors_count * sizeof(struct cpuinfo_processor), processors_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " logical processors",
processors_count * sizeof(struct cpuinfo_processor),
processors_count);
goto cleanup; goto cleanup;
} }
@ -163,8 +163,9 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
if (GetLogicalProcessorInformationEx(RelationProcessorCore, NULL, &cores_info_size) == FALSE) { if (GetLogicalProcessorInformationEx(RelationProcessorCore, NULL, &cores_info_size) == FALSE) {
const DWORD last_error = GetLastError(); const DWORD last_error = GetLastError();
if (last_error != ERROR_INSUFFICIENT_BUFFER) { if (last_error != ERROR_INSUFFICIENT_BUFFER) {
cpuinfo_log_error("failed to query size of processor cores information: error %"PRIu32, cpuinfo_log_error(
(uint32_t) last_error); "failed to query size of processor cores information: error %" PRIu32,
(uint32_t)last_error);
goto cleanup; goto cleanup;
} }
} }
@ -173,8 +174,9 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
if (GetLogicalProcessorInformationEx(RelationProcessorPackage, NULL, &packages_info_size) == FALSE) { if (GetLogicalProcessorInformationEx(RelationProcessorPackage, NULL, &packages_info_size) == FALSE) {
const DWORD last_error = GetLastError(); const DWORD last_error = GetLastError();
if (last_error != ERROR_INSUFFICIENT_BUFFER) { if (last_error != ERROR_INSUFFICIENT_BUFFER) {
cpuinfo_log_error("failed to query size of processor packages information: error %"PRIu32, cpuinfo_log_error(
(uint32_t) last_error); "failed to query size of processor packages information: error %" PRIu32,
(uint32_t)last_error);
goto cleanup; goto cleanup;
} }
} }
@ -183,27 +185,27 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
processor_infos = HeapAlloc(heap, 0, max_info_size); processor_infos = HeapAlloc(heap, 0, max_info_size);
if (processor_infos == NULL) { if (processor_infos == NULL) {
cpuinfo_log_error("failed to allocate %"PRIu32" bytes for logical processor information", cpuinfo_log_error(
(uint32_t) max_info_size); "failed to allocate %" PRIu32 " bytes for logical processor information",
(uint32_t)max_info_size);
goto cleanup; goto cleanup;
} }
if (GetLogicalProcessorInformationEx(RelationProcessorPackage, processor_infos, &max_info_size) == FALSE) { if (GetLogicalProcessorInformationEx(RelationProcessorPackage, processor_infos, &max_info_size) == FALSE) {
cpuinfo_log_error("failed to query processor packages information: error %"PRIu32, cpuinfo_log_error(
(uint32_t) GetLastError()); "failed to query processor packages information: error %" PRIu32, (uint32_t)GetLastError());
goto cleanup; goto cleanup;
} }
uint32_t packages_count = 0; uint32_t packages_count = 0;
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX packages_info_end = PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX packages_info_end =
(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) ((uintptr_t) processor_infos + packages_info_size); (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)((uintptr_t)processor_infos + packages_info_size);
for (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX package_info = processor_infos; for (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX package_info = processor_infos; package_info < packages_info_end;
package_info < packages_info_end; package_info = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)((uintptr_t)package_info + package_info->Size)) {
package_info = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) ((uintptr_t) package_info + package_info->Size))
{
if (package_info->Relationship != RelationProcessorPackage) { if (package_info->Relationship != RelationProcessorPackage) {
cpuinfo_log_warning("unexpected processor info type (%"PRIu32") for processor package information", cpuinfo_log_warning(
(uint32_t) package_info->Relationship); "unexpected processor info type (%" PRIu32 ") for processor package information",
(uint32_t)package_info->Relationship);
continue; continue;
} }
@ -211,19 +213,23 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
const uint32_t package_id = packages_count++; const uint32_t package_id = packages_count++;
/* Reconstruct package part of APIC ID */ /* Reconstruct package part of APIC ID */
const uint32_t package_apic_id = package_id << package_bits_offset; const uint32_t package_apic_id = package_id << package_bits_offset;
/* Iterate processor groups and set the package part of APIC ID */ /* Iterate processor groups and set the package part of APIC ID
*/
for (uint32_t i = 0; i < package_info->Processor.GroupCount; i++) { for (uint32_t i = 0; i < package_info->Processor.GroupCount; i++) {
const uint32_t group_id = package_info->Processor.GroupMask[i].Group; const uint32_t group_id = package_info->Processor.GroupMask[i].Group;
/* Global index of the first logical processor belonging to this group */ /* Global index of the first logical processor belonging
* to this group */
const uint32_t group_processors_start = processors_before_group[group_id]; const uint32_t group_processors_start = processors_before_group[group_id];
/* Bitmask representing processors in this group belonging to this package */ /* Bitmask representing processors in this group
* belonging to this package
*/
KAFFINITY group_processors_mask = package_info->Processor.GroupMask[i].Mask; KAFFINITY group_processors_mask = package_info->Processor.GroupMask[i].Mask;
while (group_processors_mask != 0) { while (group_processors_mask != 0) {
const uint32_t group_processor_id = low_index_from_kaffinity(group_processors_mask); const uint32_t group_processor_id = low_index_from_kaffinity(group_processors_mask);
const uint32_t processor_id = group_processors_start + group_processor_id; const uint32_t processor_id = group_processors_start + group_processor_id;
processors[processor_id].package = (const struct cpuinfo_package*) NULL + package_id; processors[processor_id].package = (const struct cpuinfo_package*)NULL + package_id;
processors[processor_id].windows_group_id = (uint16_t) group_id; processors[processor_id].windows_group_id = (uint16_t)group_id;
processors[processor_id].windows_processor_id = (uint16_t) group_processor_id; processors[processor_id].windows_processor_id = (uint16_t)group_processor_id;
processors[processor_id].apic_id = package_apic_id; processors[processor_id].apic_id = package_apic_id;
/* Reset the lowest bit in affinity mask */ /* Reset the lowest bit in affinity mask */
@ -234,44 +240,50 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
max_info_size = max(cores_info_size, packages_info_size); max_info_size = max(cores_info_size, packages_info_size);
if (GetLogicalProcessorInformationEx(RelationProcessorCore, processor_infos, &max_info_size) == FALSE) { if (GetLogicalProcessorInformationEx(RelationProcessorCore, processor_infos, &max_info_size) == FALSE) {
cpuinfo_log_error("failed to query processor cores information: error %"PRIu32, cpuinfo_log_error(
(uint32_t) GetLastError()); "failed to query processor cores information: error %" PRIu32, (uint32_t)GetLastError());
goto cleanup; goto cleanup;
} }
uint32_t cores_count = 0; uint32_t cores_count = 0;
/* Index (among all cores) of the the first core on the current package */ /* Index (among all cores) of the the first core on the current package
*/
uint32_t package_core_start = 0; uint32_t package_core_start = 0;
uint32_t current_package_apic_id = 0; uint32_t current_package_apic_id = 0;
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX cores_info_end = PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX cores_info_end =
(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) ((uintptr_t) processor_infos + cores_info_size); (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)((uintptr_t)processor_infos + cores_info_size);
for (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX core_info = processor_infos; for (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX core_info = processor_infos; core_info < cores_info_end;
core_info < cores_info_end; core_info = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)((uintptr_t)core_info + core_info->Size)) {
core_info = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) ((uintptr_t) core_info + core_info->Size))
{
if (core_info->Relationship != RelationProcessorCore) { if (core_info->Relationship != RelationProcessorCore) {
cpuinfo_log_warning("unexpected processor info type (%"PRIu32") for processor core information", cpuinfo_log_warning(
(uint32_t) core_info->Relationship); "unexpected processor info type (%" PRIu32 ") for processor core information",
(uint32_t)core_info->Relationship);
continue; continue;
} }
/* We assume that cores and logical processors are reported in APIC order */ /* We assume that cores and logical processors are reported in
* APIC order */
const uint32_t core_id = cores_count++; const uint32_t core_id = cores_count++;
uint32_t smt_id = 0; uint32_t smt_id = 0;
/* Reconstruct core part of APIC ID */ /* Reconstruct core part of APIC ID */
const uint32_t core_apic_id = (core_id & core_bits_mask) << x86_processor.topology.core_bits_offset; const uint32_t core_apic_id = (core_id & core_bits_mask) << x86_processor.topology.core_bits_offset;
/* Iterate processor groups and set the core & SMT parts of APIC ID */ /* Iterate processor groups and set the core & SMT parts of APIC
* ID */
for (uint32_t i = 0; i < core_info->Processor.GroupCount; i++) { for (uint32_t i = 0; i < core_info->Processor.GroupCount; i++) {
const uint32_t group_id = core_info->Processor.GroupMask[i].Group; const uint32_t group_id = core_info->Processor.GroupMask[i].Group;
/* Global index of the first logical processor belonging to this group */ /* Global index of the first logical processor belonging
* to this group */
const uint32_t group_processors_start = processors_before_group[group_id]; const uint32_t group_processors_start = processors_before_group[group_id];
/* Bitmask representing processors in this group belonging to this package */ /* Bitmask representing processors in this group
* belonging to this package
*/
KAFFINITY group_processors_mask = core_info->Processor.GroupMask[i].Mask; KAFFINITY group_processors_mask = core_info->Processor.GroupMask[i].Mask;
while (group_processors_mask != 0) { while (group_processors_mask != 0) {
const uint32_t group_processor_id = low_index_from_kaffinity(group_processors_mask); const uint32_t group_processor_id = low_index_from_kaffinity(group_processors_mask);
const uint32_t processor_id = group_processors_start + group_processor_id; const uint32_t processor_id = group_processors_start + group_processor_id;
/* Check if this is the first core on a new package */ /* Check if this is the first core on a new
* package */
if (processors[processor_id].apic_id != current_package_apic_id) { if (processors[processor_id].apic_id != current_package_apic_id) {
package_core_start = core_id; package_core_start = core_id;
current_package_apic_id = processors[processor_id].apic_id; current_package_apic_id = processors[processor_id].apic_id;
@ -283,12 +295,17 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
processors[processor_id].apic_id |= processors[processor_id].apic_id |=
((smt_id & thread_bits_mask) << x86_processor.topology.thread_bits_offset) | ((smt_id & thread_bits_mask) << x86_processor.topology.thread_bits_offset) |
((package_core_id & core_bits_mask) << x86_processor.topology.core_bits_offset); ((package_core_id & core_bits_mask) << x86_processor.topology.core_bits_offset);
cpuinfo_log_debug("reconstructed APIC ID 0x%08"PRIx32" for processor %"PRIu32" in group %"PRIu32, cpuinfo_log_debug(
processors[processor_id].apic_id, group_processor_id, group_id); "reconstructed APIC ID 0x%08" PRIx32 " for processor %" PRIu32
" in group %" PRIu32,
processors[processor_id].apic_id,
group_processor_id,
group_id);
/* Set SMT ID (assume logical processors within the core are reported in APIC order) */ /* Set SMT ID (assume logical processors within
* the core are reported in APIC order) */
processors[processor_id].smt_id = smt_id++; processors[processor_id].smt_id = smt_id++;
processors[processor_id].core = (const struct cpuinfo_core*) NULL + core_id; processors[processor_id].core = (const struct cpuinfo_core*)NULL + core_id;
/* Reset the lowest bit in affinity mask */ /* Reset the lowest bit in affinity mask */
group_processors_mask &= (group_processors_mask - 1); group_processors_mask &= (group_processors_mask - 1);
@ -298,22 +315,28 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
cores = HeapAlloc(heap, HEAP_ZERO_MEMORY, cores_count * sizeof(struct cpuinfo_core)); cores = HeapAlloc(heap, HEAP_ZERO_MEMORY, cores_count * sizeof(struct cpuinfo_core));
if (cores == NULL) { if (cores == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" cores", cpuinfo_log_error(
cores_count * sizeof(struct cpuinfo_core), cores_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " cores",
cores_count * sizeof(struct cpuinfo_core),
cores_count);
goto cleanup; goto cleanup;
} }
clusters = HeapAlloc(heap, HEAP_ZERO_MEMORY, packages_count * sizeof(struct cpuinfo_cluster)); clusters = HeapAlloc(heap, HEAP_ZERO_MEMORY, packages_count * sizeof(struct cpuinfo_cluster));
if (clusters == NULL) { if (clusters == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" core clusters", cpuinfo_log_error(
packages_count * sizeof(struct cpuinfo_cluster), packages_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " core clusters",
packages_count * sizeof(struct cpuinfo_cluster),
packages_count);
goto cleanup; goto cleanup;
} }
packages = HeapAlloc(heap, HEAP_ZERO_MEMORY, packages_count * sizeof(struct cpuinfo_package)); packages = HeapAlloc(heap, HEAP_ZERO_MEMORY, packages_count * sizeof(struct cpuinfo_package));
if (packages == NULL) { if (packages == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" physical packages", cpuinfo_log_error(
packages_count * sizeof(struct cpuinfo_package), packages_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " physical packages",
packages_count * sizeof(struct cpuinfo_package),
packages_count);
goto cleanup; goto cleanup;
} }
@ -321,26 +344,29 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
const uint32_t processor_id = i - 1; const uint32_t processor_id = i - 1;
struct cpuinfo_processor* processor = processors + processor_id; struct cpuinfo_processor* processor = processors + processor_id;
/* Adjust core and package pointers for all logical processors */ /* Adjust core and package pointers for all logical processors
struct cpuinfo_core* core = */
(struct cpuinfo_core*) ((uintptr_t) cores + (uintptr_t) processor->core); struct cpuinfo_core* core = (struct cpuinfo_core*)((uintptr_t)cores + (uintptr_t)processor->core);
processor->core = core; processor->core = core;
struct cpuinfo_cluster* cluster = struct cpuinfo_cluster* cluster =
(struct cpuinfo_cluster*) ((uintptr_t) clusters + (uintptr_t) processor->cluster); (struct cpuinfo_cluster*)((uintptr_t)clusters + (uintptr_t)processor->cluster);
processor->cluster = cluster; processor->cluster = cluster;
struct cpuinfo_package* package = struct cpuinfo_package* package =
(struct cpuinfo_package*) ((uintptr_t) packages + (uintptr_t) processor->package); (struct cpuinfo_package*)((uintptr_t)packages + (uintptr_t)processor->package);
processor->package = package; processor->package = package;
/* This can be overwritten by lower-index processors on the same package */ /* This can be overwritten by lower-index processors on the same
* package */
package->processor_start = processor_id; package->processor_start = processor_id;
package->processor_count += 1; package->processor_count += 1;
/* This can be overwritten by lower-index processors on the same cluster */ /* This can be overwritten by lower-index processors on the same
* cluster */
cluster->processor_start = processor_id; cluster->processor_start = processor_id;
cluster->processor_count += 1; cluster->processor_count += 1;
/* This can be overwritten by lower-index processors on the same core*/ /* This can be overwritten by lower-index processors on the same
* core*/
core->processor_start = processor_id; core->processor_start = processor_id;
core->processor_count += 1; core->processor_count += 1;
} }
@ -350,18 +376,19 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
const uint32_t global_core_id = i - 1; const uint32_t global_core_id = i - 1;
struct cpuinfo_core* core = cores + global_core_id; struct cpuinfo_core* core = cores + global_core_id;
const struct cpuinfo_processor* processor = processors + core->processor_start; const struct cpuinfo_processor* processor = processors + core->processor_start;
struct cpuinfo_package* package = (struct cpuinfo_package*) processor->package; struct cpuinfo_package* package = (struct cpuinfo_package*)processor->package;
struct cpuinfo_cluster* cluster = (struct cpuinfo_cluster*) processor->cluster; struct cpuinfo_cluster* cluster = (struct cpuinfo_cluster*)processor->cluster;
core->cluster = cluster; core->cluster = cluster;
core->package = package; core->package = package;
core->core_id = core_bits_mask & core->core_id = core_bits_mask & (processor->apic_id >> x86_processor.topology.core_bits_offset);
(processor->apic_id >> x86_processor.topology.core_bits_offset);
core->vendor = x86_processor.vendor; core->vendor = x86_processor.vendor;
core->uarch = x86_processor.uarch; core->uarch = x86_processor.uarch;
core->cpuid = x86_processor.cpuid; core->cpuid = x86_processor.cpuid;
/* This can be overwritten by lower-index cores on the same cluster/package */ /* This can be overwritten by lower-index cores on the same
* cluster/package
*/
cluster->core_start = global_core_id; cluster->core_start = global_core_id;
cluster->core_count += 1; cluster->core_count += 1;
package->core_start = global_core_id; package->core_start = global_core_id;
@ -383,53 +410,64 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
/* Count caches */ /* Count caches */
uint32_t l1i_count, l1d_count, l2_count, l3_count, l4_count; uint32_t l1i_count, l1d_count, l2_count, l3_count, l4_count;
cpuinfo_x86_count_caches(processors_count, processors, &x86_processor, cpuinfo_x86_count_caches(
&l1i_count, &l1d_count, &l2_count, &l3_count, &l4_count); processors_count, processors, &x86_processor, &l1i_count, &l1d_count, &l2_count, &l3_count, &l4_count);
/* Allocate cache descriptions */ /* Allocate cache descriptions */
if (l1i_count != 0) { if (l1i_count != 0) {
l1i = HeapAlloc(heap, HEAP_ZERO_MEMORY, l1i_count * sizeof(struct cpuinfo_cache)); l1i = HeapAlloc(heap, HEAP_ZERO_MEMORY, l1i_count * sizeof(struct cpuinfo_cache));
if (l1i == NULL) { if (l1i == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L1I caches", cpuinfo_log_error(
l1i_count * sizeof(struct cpuinfo_cache), l1i_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L1I caches",
l1i_count * sizeof(struct cpuinfo_cache),
l1i_count);
goto cleanup; goto cleanup;
} }
} }
if (l1d_count != 0) { if (l1d_count != 0) {
l1d = HeapAlloc(heap, HEAP_ZERO_MEMORY, l1d_count * sizeof(struct cpuinfo_cache)); l1d = HeapAlloc(heap, HEAP_ZERO_MEMORY, l1d_count * sizeof(struct cpuinfo_cache));
if (l1d == NULL) { if (l1d == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L1D caches", cpuinfo_log_error(
l1d_count * sizeof(struct cpuinfo_cache), l1d_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L1D caches",
l1d_count * sizeof(struct cpuinfo_cache),
l1d_count);
goto cleanup; goto cleanup;
} }
} }
if (l2_count != 0) { if (l2_count != 0) {
l2 = HeapAlloc(heap, HEAP_ZERO_MEMORY, l2_count * sizeof(struct cpuinfo_cache)); l2 = HeapAlloc(heap, HEAP_ZERO_MEMORY, l2_count * sizeof(struct cpuinfo_cache));
if (l2 == NULL) { if (l2 == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L2 caches", cpuinfo_log_error(
l2_count * sizeof(struct cpuinfo_cache), l2_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L2 caches",
l2_count * sizeof(struct cpuinfo_cache),
l2_count);
goto cleanup; goto cleanup;
} }
} }
if (l3_count != 0) { if (l3_count != 0) {
l3 = HeapAlloc(heap, HEAP_ZERO_MEMORY, l3_count * sizeof(struct cpuinfo_cache)); l3 = HeapAlloc(heap, HEAP_ZERO_MEMORY, l3_count * sizeof(struct cpuinfo_cache));
if (l3 == NULL) { if (l3 == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L3 caches", cpuinfo_log_error(
l3_count * sizeof(struct cpuinfo_cache), l3_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L3 caches",
l3_count * sizeof(struct cpuinfo_cache),
l3_count);
goto cleanup; goto cleanup;
} }
} }
if (l4_count != 0) { if (l4_count != 0) {
l4 = HeapAlloc(heap, HEAP_ZERO_MEMORY, l4_count * sizeof(struct cpuinfo_cache)); l4 = HeapAlloc(heap, HEAP_ZERO_MEMORY, l4_count * sizeof(struct cpuinfo_cache));
if (l4 == NULL) { if (l4 == NULL) {
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L4 caches", cpuinfo_log_error(
l4_count * sizeof(struct cpuinfo_cache), l4_count); "failed to allocate %zu bytes for descriptions of %" PRIu32 " L4 caches",
l4_count * sizeof(struct cpuinfo_cache),
l4_count);
goto cleanup; goto cleanup;
} }
} }
/* Set cache information */ /* Set cache information */
uint32_t l1i_index = UINT32_MAX, l1d_index = UINT32_MAX, l2_index = UINT32_MAX, l3_index = UINT32_MAX, l4_index = UINT32_MAX; uint32_t l1i_index = UINT32_MAX, l1d_index = UINT32_MAX, l2_index = UINT32_MAX, l3_index = UINT32_MAX,
l4_index = UINT32_MAX;
uint32_t last_l1i_id = UINT32_MAX, last_l1d_id = UINT32_MAX; uint32_t last_l1i_id = UINT32_MAX, last_l1d_id = UINT32_MAX;
uint32_t last_l2_id = UINT32_MAX, last_l3_id = UINT32_MAX, last_l4_id = UINT32_MAX; uint32_t last_l2_id = UINT32_MAX, last_l3_id = UINT32_MAX, last_l4_id = UINT32_MAX;
for (uint32_t i = 0; i < processors_count; i++) { for (uint32_t i = 0; i < processors_count; i++) {
@ -441,13 +479,13 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
if (l1i_id != last_l1i_id) { if (l1i_id != last_l1i_id) {
/* new cache */ /* new cache */
last_l1i_id = l1i_id; last_l1i_id = l1i_id;
l1i[++l1i_index] = (struct cpuinfo_cache) { l1i[++l1i_index] = (struct cpuinfo_cache){
.size = x86_processor.cache.l1i.size, .size = x86_processor.cache.l1i.size,
.associativity = x86_processor.cache.l1i.associativity, .associativity = x86_processor.cache.l1i.associativity,
.sets = x86_processor.cache.l1i.sets, .sets = x86_processor.cache.l1i.sets,
.partitions = x86_processor.cache.l1i.partitions, .partitions = x86_processor.cache.l1i.partitions,
.line_size = x86_processor.cache.l1i.line_size, .line_size = x86_processor.cache.l1i.line_size,
.flags = x86_processor.cache.l1i.flags, .flags = x86_processor.cache.l1i.flags,
.processor_start = i, .processor_start = i,
.processor_count = 1, .processor_count = 1,
}; };
@ -466,13 +504,13 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
if (l1d_id != last_l1d_id) { if (l1d_id != last_l1d_id) {
/* new cache */ /* new cache */
last_l1d_id = l1d_id; last_l1d_id = l1d_id;
l1d[++l1d_index] = (struct cpuinfo_cache) { l1d[++l1d_index] = (struct cpuinfo_cache){
.size = x86_processor.cache.l1d.size, .size = x86_processor.cache.l1d.size,
.associativity = x86_processor.cache.l1d.associativity, .associativity = x86_processor.cache.l1d.associativity,
.sets = x86_processor.cache.l1d.sets, .sets = x86_processor.cache.l1d.sets,
.partitions = x86_processor.cache.l1d.partitions, .partitions = x86_processor.cache.l1d.partitions,
.line_size = x86_processor.cache.l1d.line_size, .line_size = x86_processor.cache.l1d.line_size,
.flags = x86_processor.cache.l1d.flags, .flags = x86_processor.cache.l1d.flags,
.processor_start = i, .processor_start = i,
.processor_count = 1, .processor_count = 1,
}; };
@ -491,13 +529,13 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
if (l2_id != last_l2_id) { if (l2_id != last_l2_id) {
/* new cache */ /* new cache */
last_l2_id = l2_id; last_l2_id = l2_id;
l2[++l2_index] = (struct cpuinfo_cache) { l2[++l2_index] = (struct cpuinfo_cache){
.size = x86_processor.cache.l2.size, .size = x86_processor.cache.l2.size,
.associativity = x86_processor.cache.l2.associativity, .associativity = x86_processor.cache.l2.associativity,
.sets = x86_processor.cache.l2.sets, .sets = x86_processor.cache.l2.sets,
.partitions = x86_processor.cache.l2.partitions, .partitions = x86_processor.cache.l2.partitions,
.line_size = x86_processor.cache.l2.line_size, .line_size = x86_processor.cache.l2.line_size,
.flags = x86_processor.cache.l2.flags, .flags = x86_processor.cache.l2.flags,
.processor_start = i, .processor_start = i,
.processor_count = 1, .processor_count = 1,
}; };
@ -516,13 +554,13 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
if (l3_id != last_l3_id) { if (l3_id != last_l3_id) {
/* new cache */ /* new cache */
last_l3_id = l3_id; last_l3_id = l3_id;
l3[++l3_index] = (struct cpuinfo_cache) { l3[++l3_index] = (struct cpuinfo_cache){
.size = x86_processor.cache.l3.size, .size = x86_processor.cache.l3.size,
.associativity = x86_processor.cache.l3.associativity, .associativity = x86_processor.cache.l3.associativity,
.sets = x86_processor.cache.l3.sets, .sets = x86_processor.cache.l3.sets,
.partitions = x86_processor.cache.l3.partitions, .partitions = x86_processor.cache.l3.partitions,
.line_size = x86_processor.cache.l3.line_size, .line_size = x86_processor.cache.l3.line_size,
.flags = x86_processor.cache.l3.flags, .flags = x86_processor.cache.l3.flags,
.processor_start = i, .processor_start = i,
.processor_count = 1, .processor_count = 1,
}; };
@ -541,13 +579,13 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
if (l4_id != last_l4_id) { if (l4_id != last_l4_id) {
/* new cache */ /* new cache */
last_l4_id = l4_id; last_l4_id = l4_id;
l4[++l4_index] = (struct cpuinfo_cache) { l4[++l4_index] = (struct cpuinfo_cache){
.size = x86_processor.cache.l4.size, .size = x86_processor.cache.l4.size,
.associativity = x86_processor.cache.l4.associativity, .associativity = x86_processor.cache.l4.associativity,
.sets = x86_processor.cache.l4.sets, .sets = x86_processor.cache.l4.sets,
.partitions = x86_processor.cache.l4.partitions, .partitions = x86_processor.cache.l4.partitions,
.line_size = x86_processor.cache.l4.line_size, .line_size = x86_processor.cache.l4.line_size,
.flags = x86_processor.cache.l4.flags, .flags = x86_processor.cache.l4.flags,
.processor_start = i, .processor_start = i,
.processor_count = 1, .processor_count = 1,
}; };
@ -562,7 +600,6 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
} }
} }
/* Commit changes */ /* Commit changes */
cpuinfo_processors = processors; cpuinfo_processors = processors;
cpuinfo_cores = cores; cpuinfo_cores = cores;
@ -570,9 +607,9 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
cpuinfo_packages = packages; cpuinfo_packages = packages;
cpuinfo_cache[cpuinfo_cache_level_1i] = l1i; cpuinfo_cache[cpuinfo_cache_level_1i] = l1i;
cpuinfo_cache[cpuinfo_cache_level_1d] = l1d; cpuinfo_cache[cpuinfo_cache_level_1d] = l1d;
cpuinfo_cache[cpuinfo_cache_level_2] = l2; cpuinfo_cache[cpuinfo_cache_level_2] = l2;
cpuinfo_cache[cpuinfo_cache_level_3] = l3; cpuinfo_cache[cpuinfo_cache_level_3] = l3;
cpuinfo_cache[cpuinfo_cache_level_4] = l4; cpuinfo_cache[cpuinfo_cache_level_4] = l4;
cpuinfo_processors_count = processors_count; cpuinfo_processors_count = processors_count;
cpuinfo_cores_count = cores_count; cpuinfo_cores_count = cores_count;
@ -580,12 +617,12 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
cpuinfo_packages_count = packages_count; cpuinfo_packages_count = packages_count;
cpuinfo_cache_count[cpuinfo_cache_level_1i] = l1i_count; cpuinfo_cache_count[cpuinfo_cache_level_1i] = l1i_count;
cpuinfo_cache_count[cpuinfo_cache_level_1d] = l1d_count; cpuinfo_cache_count[cpuinfo_cache_level_1d] = l1d_count;
cpuinfo_cache_count[cpuinfo_cache_level_2] = l2_count; cpuinfo_cache_count[cpuinfo_cache_level_2] = l2_count;
cpuinfo_cache_count[cpuinfo_cache_level_3] = l3_count; cpuinfo_cache_count[cpuinfo_cache_level_3] = l3_count;
cpuinfo_cache_count[cpuinfo_cache_level_4] = l4_count; cpuinfo_cache_count[cpuinfo_cache_level_4] = l4_count;
cpuinfo_max_cache_size = cpuinfo_compute_max_cache_size(&processors[0]); cpuinfo_max_cache_size = cpuinfo_compute_max_cache_size(&processors[0]);
cpuinfo_global_uarch = (struct cpuinfo_uarch_info) { cpuinfo_global_uarch = (struct cpuinfo_uarch_info){
.uarch = x86_processor.uarch, .uarch = x86_processor.uarch,
.cpuid = x86_processor.cpuid, .cpuid = x86_processor.cpuid,
.processor_count = processors_count, .processor_count = processors_count,