From 68fecdb7ce3800a67d0ff2641aa55dc630033e58 Mon Sep 17 00:00:00 2001 From: Mahesh Bodapati Date: Wed, 30 Oct 2024 02:20:05 -0400 Subject: [PATCH 1/2] Add initial support for powerpc64le initialization. * Adds header definitions for PPC64le * Adds support to construct the processor, core, cluster, package and cache(L1i,L1d,L2 and L3) information reported by the system. Test: Build and ran cpu_info on PPC64le linux machine. confirmed that it properly reports the logical processors, cores, clusters, packages and cache information. --- CMakeLists.txt | 18 +- configure.py | 12 + include/cpuinfo.h | 60 +++ src/api.c | 14 +- src/cpuinfo/internal-api.h | 3 +- src/init.c | 6 + src/linux/api.h | 1 + src/linux/processors.c | 28 ++ src/powerpc/api.h | 21 + src/powerpc/cache.c | 131 +++++ src/powerpc/linux/api.h | 108 ++++ src/powerpc/linux/cpuinfo.c | 325 ++++++++++++ src/powerpc/linux/init.c | 895 ++++++++++++++++++++++++++++++++++ src/powerpc/linux/ppc64-hw.c | 36 ++ src/powerpc/linux/ppc64-isa.c | 13 + src/powerpc/uarch.c | 25 + test/init.cc | 34 ++ test/name/power-features.cc | 41 ++ tools/cpu-info.c | 16 + 19 files changed, 1778 insertions(+), 9 deletions(-) create mode 100644 src/powerpc/api.h create mode 100644 src/powerpc/cache.c create mode 100644 src/powerpc/linux/api.h create mode 100644 src/powerpc/linux/cpuinfo.c create mode 100644 src/powerpc/linux/init.c create mode 100644 src/powerpc/linux/ppc64-hw.c create mode 100644 src/powerpc/linux/ppc64-isa.c create mode 100644 src/powerpc/uarch.c create mode 100644 test/name/power-features.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index bd9f77f8..d0af5442 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,7 +95,7 @@ IF(NOT CMAKE_SYSTEM_PROCESSOR) "cpuinfo will compile, but cpuinfo_initialize() will always fail.") SET(CPUINFO_SUPPORTED_PLATFORM FALSE) ENDIF() -ELSEIF(NOT CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86(_64)?|armv[5-8].*|aarch64|arm64.*|ARM64.*|riscv(32|64))$") +ELSEIF(NOT CPUINFO_TARGET_PROCESSOR MATCHES "^(i[3-6]86|AMD64|x86(_64)?|armv[5-8].*|aarch64|arm64.*|ARM64.*|riscv(32|64)|ppc64le)$") MESSAGE(WARNING "Target processor architecture \"${CPUINFO_TARGET_PROCESSOR}\" is not supported in cpuinfo. " "cpuinfo will compile, but cpuinfo_initialize() will always fail.") @@ -184,6 +184,14 @@ IF(CPUINFO_SUPPORTED_PLATFORM) ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") LIST(APPEND CPUINFO_SRCS src/x86/freebsd/init.c) ENDIF() + ELSEIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^(ppc64le)$" ) + LIST(APPEND CPUINFO_SRCS + src/powerpc/uarch.c + src/powerpc/cache.c + src/powerpc/linux/cpuinfo.c + src/powerpc/linux/ppc64-hw.c + src/powerpc/linux/init.c + src/powerpc/linux/ppc64-isa.c) ELSEIF(CMAKE_SYSTEM_NAME MATCHES "^Windows" AND CPUINFO_TARGET_PROCESSOR MATCHES "^(ARM64|arm64)$") LIST(APPEND CPUINFO_SRCS src/arm/windows/init-by-logical-sys-info.c @@ -856,6 +864,14 @@ IF(CPUINFO_SUPPORTED_PLATFORM AND CPUINFO_BUILD_UNIT_TESTS) TARGET_LINK_LIBRARIES(cache-test PRIVATE cpuinfo_internals gtest gtest_main) ADD_TEST(NAME cache-test COMMAND cache-test) ENDIF() + + IF(CMAKE_SYSTEM_NAME STREQUAL "POWER" OR CMAKE_SYSTEM_PROCESSOR MATCHES "^(ppc64le)$") + ADD_EXECUTABLE(power-features-test test/name/power-features.cc) + CPUINFO_TARGET_ENABLE_CXX11(power-features-test) + CPUINFO_TARGET_RUNTIME_LIBRARY(power-features-test) + TARGET_LINK_LIBRARIES(power-features-test PRIVATE cpuinfo_internals gtest gtest_main) + ADD_TEST(NAME power-features-test COMMAND power-features-test) + ENDIF() ENDIF() # ---[ Helper and debug tools diff --git a/configure.py b/configure.py index 00bba24b..e8c04409 100755 --- a/configure.py +++ b/configure.py @@ -75,6 +75,16 @@ def main(args): if options.mock: sources += ["linux/mockfile.c"] build.static_library("cpuinfo", map(build.cc, sources)) + if build.target.is_ppc64: + sources += [ "powerpc/uarch.c", "powerpc/cache.c"] + if build.target.is_linux: + sources += [ + "powerpc/linux/cpuinfo.c" + "powerpc/linux/ppc64-hw.c", + "powerpc/linux/init.c", + "powerpc/linux/ppc64-isa.c", + ] + with build.options(source_dir="tools", deps=[build, build.deps.clog]): build.executable("cpu-info", build.cc("cpu-info.c")) @@ -91,6 +101,8 @@ def main(args): build.smoketest("get-current-test", build.cxx("get-current.cc")) if build.target.is_x86_64: build.smoketest("brand-string-test", build.cxx("name/brand-string.cc")) + if build.target.is_ppc64: + build.smoketest("power-features-test", build.cxx("name/power-features.cc")) if options.mock: with build.options(source_dir="test", include_dirs="test", macros="CPUINFO_MOCK", deps=[build, build.deps.googletest]): if build.target.is_arm64 and build.target.is_linux: diff --git a/include/cpuinfo.h b/include/cpuinfo.h index 9ed5d924..15625e02 100644 --- a/include/cpuinfo.h +++ b/include/cpuinfo.h @@ -601,6 +601,21 @@ enum cpuinfo_uarch { /** HiSilicon TaiShan v110 (Huawei Kunpeng 920 series processors). */ cpuinfo_uarch_taishan_v110 = 0x00C00100, + + /** IBM POWER 7. */ + cpuinfo_uarch_power7 = 0x00D00100, + /** IBM POWER 7p. */ + cpuinfo_uarch_power7p = 0x00D00101, + /** IBM POWER 8. */ + cpuinfo_uarch_power8 = 0x00D00200, + /** IBM POWER8E. */ + cpuinfo_uarch_power8e = 0x00D00201, + /** IBM POWER8NVL */ + cpuinfo_uarch_power8nvl = 0x00D00202, + /** IBM POWER 9. */ + cpuinfo_uarch_power9 = 0x00D00303, + /** IBM POWER 10. */ + cpuinfo_uarch_power10 = 0x00D00400, }; struct cpuinfo_processor { @@ -671,9 +686,14 @@ struct cpuinfo_core { #elif CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 /** Value of Main ID Register (MIDR) for this core */ uint32_t midr; +#elif CPUINFO_ARCH_PPC64 + /** Value of Processor Version Register for this core */ + uint32_t pvr; #endif /** Clock rate (non-Turbo) of the core, in Hz */ uint64_t frequency; + + bool disabled; }; struct cpuinfo_cluster { @@ -699,6 +719,9 @@ struct cpuinfo_cluster { #elif CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 /** Value of Main ID Register (MIDR) of the cores in the cluster */ uint32_t midr; +#elif CPUINFO_ARCH_PPC64 + /** Value of Processor Version Register in this cluster */ + uint32_t pvr; #endif /** Clock rate (non-Turbo) of the cores in the cluster, in Hz */ uint64_t frequency; @@ -732,6 +755,9 @@ struct cpuinfo_uarch_info { #elif CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 /** Value of Main ID Register (MIDR) for the microarchitecture */ uint32_t midr; +#elif CPUINFO_ARCH_PPC64 + /** Value of Processor Version Register for this core */ + uint32_t pvr; #endif /** Number of logical processors with the microarchitecture */ uint32_t processor_count; @@ -2218,6 +2244,40 @@ static inline bool cpuinfo_has_riscv_v(void) { #endif } +#if CPUINFO_ARCH_PPC64 + struct cpuinfo_powerpc_isa { + bool vsx; + bool htm; + bool mma; + }; + + extern struct cpuinfo_powerpc_isa cpuinfo_isa; +#endif + +static inline bool cpuinfo_has_powerpc_vsx(void) { +#if CPUINFO_ARCH_PPC64 + return cpuinfo_isa.vsx; +#else + return false; +#endif +} + +static inline bool cpuinfo_has_powerpc_htm(void) { +#if CPUINFO_ARCH_PPC64 + return cpuinfo_isa.htm; +#else + return false; +#endif +} + +static inline bool cpuinfo_has_powerpc_mma(void) { +#if CPUINFO_ARCH_PPC64 + return cpuinfo_isa.mma; +#else + return false; +#endif +} + const struct cpuinfo_processor* CPUINFO_ABI cpuinfo_get_processors(void); const struct cpuinfo_core* CPUINFO_ABI cpuinfo_get_cores(void); const struct cpuinfo_cluster* CPUINFO_ABI cpuinfo_get_clusters(void); diff --git a/src/api.c b/src/api.c index b8c999f3..e36dc342 100644 --- a/src/api.c +++ b/src/api.c @@ -30,7 +30,7 @@ uint32_t cpuinfo_packages_count = 0; uint32_t cpuinfo_cache_count[cpuinfo_cache_level_max] = {0}; uint32_t cpuinfo_max_cache_size = 0; -#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 || CPUINFO_ARCH_PPC64 struct cpuinfo_uarch_info* cpuinfo_uarchs = NULL; uint32_t cpuinfo_uarchs_count = 0; #else @@ -41,7 +41,7 @@ struct cpuinfo_uarch_info cpuinfo_global_uarch = {cpuinfo_uarch_unknown}; uint32_t cpuinfo_linux_cpu_max = 0; const struct cpuinfo_processor** cpuinfo_linux_cpu_to_processor_map = NULL; const struct cpuinfo_core** cpuinfo_linux_cpu_to_core_map = NULL; -#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 || CPUINFO_ARCH_PPC64 const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map = NULL; #endif #endif @@ -78,7 +78,7 @@ const struct cpuinfo_uarch_info* cpuinfo_get_uarchs() { if (!cpuinfo_is_initialized) { cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarchs"); } -#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 || CPUINFO_ARCH_PPC64 return cpuinfo_uarchs; #else return &cpuinfo_global_uarch; @@ -129,7 +129,7 @@ const struct cpuinfo_uarch_info* cpuinfo_get_uarch(uint32_t index) { if (!cpuinfo_is_initialized) { cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarch"); } -#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 || CPUINFO_ARCH_PPC64 if CPUINFO_UNLIKELY (index >= cpuinfo_uarchs_count) { return NULL; } @@ -174,7 +174,7 @@ uint32_t cpuinfo_get_uarchs_count(void) { if (!cpuinfo_is_initialized) { cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarchs_count"); } -#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 || CPUINFO_ARCH_PPC64 return cpuinfo_uarchs_count; #else return 1; @@ -350,7 +350,7 @@ uint32_t CPUINFO_ABI cpuinfo_get_current_uarch_index(void) { if CPUINFO_UNLIKELY (!cpuinfo_is_initialized) { cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_uarch_index"); } -#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 || CPUINFO_ARCH_PPC64 #ifdef __linux__ if (cpuinfo_linux_cpu_to_uarch_index_map == NULL) { /* Special case: avoid syscall on systems with only a single @@ -385,7 +385,7 @@ uint32_t CPUINFO_ABI cpuinfo_get_current_uarch_index_with_default(uint32_t defau cpuinfo_log_fatal( "cpuinfo_get_%s called before cpuinfo is initialized", "current_uarch_index_with_default"); } -#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 || CPUINFO_ARCH_PPC64 #ifdef __linux__ if (cpuinfo_linux_cpu_to_uarch_index_map == NULL) { /* Special case: avoid syscall on systems with only a single diff --git a/src/cpuinfo/internal-api.h b/src/cpuinfo/internal-api.h index d84b26a8..9edd1120 100644 --- a/src/cpuinfo/internal-api.h +++ b/src/cpuinfo/internal-api.h @@ -34,7 +34,7 @@ extern CPUINFO_INTERNAL uint32_t cpuinfo_packages_count; extern CPUINFO_INTERNAL uint32_t cpuinfo_cache_count[cpuinfo_cache_level_max]; 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 || CPUINFO_ARCH_PPC64 extern CPUINFO_INTERNAL struct cpuinfo_uarch_info* cpuinfo_uarchs; extern CPUINFO_INTERNAL uint32_t cpuinfo_uarchs_count; #else @@ -61,6 +61,7 @@ CPUINFO_PRIVATE void cpuinfo_arm_mach_init(void); CPUINFO_PRIVATE void cpuinfo_arm_linux_init(void); CPUINFO_PRIVATE void cpuinfo_riscv_linux_init(void); CPUINFO_PRIVATE void cpuinfo_emscripten_init(void); +CPUINFO_PRIVATE void cpuinfo_powerpc_linux_init(void); CPUINFO_PRIVATE uint32_t cpuinfo_compute_max_cache_size(const struct cpuinfo_processor* processor); diff --git a/src/init.c b/src/init.c index 81d5721c..1fdeefcf 100644 --- a/src/init.c +++ b/src/init.c @@ -33,6 +33,12 @@ bool CPUINFO_ABI cpuinfo_initialize(void) { #else cpuinfo_log_error("operating system is not supported in cpuinfo"); #endif +#elif CPUINFO_ARCH_PPC64 +#if defined(__linux__) + pthread_once(&init_guard, &cpuinfo_powerpc_linux_init); +#else + cpuinfo_log_error("operating system is not supported in cpuinfo"); +#endif #elif CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 #if defined(__linux__) pthread_once(&init_guard, &cpuinfo_arm_linux_init); diff --git a/src/linux/api.h b/src/linux/api.h index 0966bd57..e1ff6a6f 100644 --- a/src/linux/api.h +++ b/src/linux/api.h @@ -51,6 +51,7 @@ 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_online_status(uint32_t processor, uint32_t* online_status); CPUINFO_INTERNAL bool cpuinfo_linux_detect_possible_processors( uint32_t max_processors_count, diff --git a/src/linux/processors.c b/src/linux/processors.c index b68cd1cc..6c9622da 100644 --- a/src/linux/processors.c +++ b/src/linux/processors.c @@ -33,6 +33,9 @@ #define CORE_ID_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/core_id" #define CORE_ID_FILESIZE 32 +#define PROCESSOR_ONLINE_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/online")) +#define PROCESSOR_ONLINE_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/online" +#define PROCESSOR_ONLINE_FILESIZE 32 #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_SIBLINGS_FILENAME_SIZE \ @@ -280,6 +283,31 @@ bool cpuinfo_linux_get_processor_package_id(uint32_t processor, uint32_t package } } +bool cpuinfo_linux_get_processor_online_status(uint32_t processor, uint32_t* online_status_ptr) { + char processor_online_filename[PROCESSOR_ONLINE_FILENAME_SIZE]; + const int chars_formatted = + snprintf(processor_online_filename, PROCESSOR_ONLINE_FILENAME_SIZE, PROCESSOR_ONLINE_FILENAME_FORMAT, processor); + if ((unsigned int)chars_formatted >= PROCESSOR_ONLINE_FILENAME_SIZE) { + cpuinfo_log_warning("failed to format filename for online status of processor %" PRIu32, processor); + return 0; + } + uint32_t online_status; + if (cpuinfo_linux_parse_small_file(processor_online_filename, PROCESSOR_ONLINE_FILESIZE, uint32_parser, &online_status)) { + cpuinfo_log_debug( + "parsed online status value of %" PRIu32 " for logical processor %" PRIu32 " from %s", + online_status, + processor, + processor_online_filename); + *online_status_ptr = online_status; + return true; + } else { + cpuinfo_log_info( + "failed to parse online status for processor %" PRIu32 " from %s", processor, processor_online_filename); + return false; + } +} + + 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; const uint32_t processor_list_last = processor_list_end - 1; diff --git a/src/powerpc/api.h b/src/powerpc/api.h new file mode 100644 index 00000000..79464f68 --- /dev/null +++ b/src/powerpc/api.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include + +enum cpuinfo_powerpc_chipset_vendor { + cpuinfo_powerpc_chipset_vendor_unknown = 0, + cpuinfo_powerpc_chipset_vendor_ibm +}; +void cpuinfo_powerpc_decode_vendor_uarch( + uint32_t vendor_id, + enum cpuinfo_vendor vendor[restrict static 1], + enum cpuinfo_uarch uarch[restrict static 1]); + +void cpuinfo_powerpc_decode_cache( + struct cpuinfo_cache l1i[restrict static 1], + struct cpuinfo_cache l1d[restrict static 1], + struct cpuinfo_cache l2[restrict static 1], + struct cpuinfo_cache l3[restrict static 1]); diff --git a/src/powerpc/cache.c b/src/powerpc/cache.c new file mode 100644 index 00000000..0ddd5331 --- /dev/null +++ b/src/powerpc/cache.c @@ -0,0 +1,131 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define NUM_CACHE 4 +#define BUF_SIZE 128 + +int path_exist(const char *path) +{ + return (access(path, F_OK) == 0); +} + +void read_str(char *result, size_t len, const char *path) +{ + FILE *f; + + f = fopen(path, "r"); + + fgets(result, len, f); + fclose(f); + + len = strlen(result); + if (result[len - 1] == '\n') + result[len - 1] = '\0'; +} + +void decode_cache_features(uint32_t cache_features[], unsigned long geometry) +{ + cache_features[1] = (geometry >> 16) & 0xffff; + cache_features[2] = geometry & 0xffff; + /* If associativity = 65535 it means upper limit of 16 bit of AT_L%d_CACHEGEOMETRY has reached so we calculate it from, size, sets and line_size*/ + if(cache_features[1] == 65535) + cache_features[1] = cache_features[0]/(cache_features[2]*cache_features[3]); +} + +void cpuinfo_powerpc_decode_cache( + struct cpuinfo_cache l1i[restrict static 1], + struct cpuinfo_cache l1d[restrict static 1], + struct cpuinfo_cache l2[restrict static 1], + struct cpuinfo_cache l3[restrict static 1]) +{ + char buf[BUF_SIZE]; + char result[BUF_SIZE]; + uint32_t cache_features[NUM_CACHE]; + uint32_t i; + uint32_t size = 0, sets = 0; + size_t len; + + for(i = 0; i < NUM_CACHE; i++) { + + sprintf (buf, "/sys/devices/system/cpu/cpu%d/cache/index%d/number_of_sets", i); + if(path_exist(buf)) + read_str(result, sizeof(result), buf); + + sets = atoi(result); + cache_features[3] = sets; + + unsigned long geometry; + + switch (i) { + case 0: + geometry = getauxval(AT_L1D_CACHEGEOMETRY); + size = getauxval(AT_L1D_CACHESIZE); + cache_features[0] = size; + decode_cache_features(cache_features, geometry); + sets = size/(cache_features[1] * cache_features[2]); + *l1d = (struct cpuinfo_cache) { + .size = size, + .associativity = cache_features[1], + .line_size = cache_features[2], + .sets = sets, + .partitions = 1 + + }; + break; + case 1: + geometry = getauxval(AT_L1I_CACHEGEOMETRY); + size = getauxval(AT_L1I_CACHESIZE); + cache_features[0] = size; + decode_cache_features(cache_features, geometry); + sets = size/(cache_features[1] * cache_features[2]); + *l1i = (struct cpuinfo_cache) { + .size = size, + .associativity = cache_features[1], + .line_size = cache_features[2], + .sets = sets, + .partitions = 1 + }; + break; + case 2: + geometry = getauxval(AT_L2_CACHEGEOMETRY); + size = getauxval(AT_L2_CACHESIZE); + cache_features[0] = size; + decode_cache_features(cache_features, geometry); + sets = size/(cache_features[1] * cache_features[2]); + *l2 = (struct cpuinfo_cache) { + .size = size, + .associativity = cache_features[1], + .line_size = cache_features[2], + .sets = sets, + .partitions = 1 + }; + break; + case 3: + geometry = getauxval(AT_L3_CACHEGEOMETRY); + size = getauxval(AT_L3_CACHESIZE); + cache_features[0] = size; + decode_cache_features(cache_features, geometry); + sets = size/(cache_features[1] * cache_features[2]); + *l3 = (struct cpuinfo_cache) { + .size = size, + .associativity = cache_features[1], + .line_size = cache_features[2], + .sets = sets, + .partitions = 1 + }; + break; + + } + } +} diff --git a/src/powerpc/linux/api.h b/src/powerpc/linux/api.h new file mode 100644 index 00000000..739a519d --- /dev/null +++ b/src/powerpc/linux/api.h @@ -0,0 +1,108 @@ + + +#pragma once + +#include +#include + +#include +#include +#include +#include + +/* No hard limit in the kernel, maximum length observed on non-rogue kernels is 64 */ +#define CPUINFO_HARDWARE_VALUE_MAX 64 + +/* from /arch/powerpc/kernel/cputable.c */ +#define CPUINFO_POWERPC_LINUX_FEATURE_64 UINT32_C(0x40000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_ALTIVEC UINT32_C(0x10000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_FPU UINT32_C(0x08000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_MMU UINT32_C(0x04000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_NO_TB UINT32_C(0x00100000) +#define CPUINFO_POWERPC_LINUX_FEATURE_SMT UINT32_C(0x00004000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_DFP UINT32_C(0x00000400) +#define CPUINFO_POWERPC_LINUX_FEATURE_POWER6_EXT UINT32_C(0x00000200) +#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_VSX UINT32_C(0x00000080) +#define CPUINFO_POWERPC_LINUX_FEATURE_TRUE_LE UINT32_C(0x00000002) +#define CPUINFO_POWERPC_LINUX_FEATURE_PPC_LE UINT32_C(0x00000001 +#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_2_07 UINT32_C(0x80000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HTM UINT32_C(0x40000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_DSCR UINT32_C(0x20000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_EBB UINT32_C(0x10000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_ISEL UINT32_C(0x08000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_TAR UINT32_C(0x04000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_VEC_CRYPTO UINT32_C(0x02000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HTM_NOSC UINT32_C(0x01000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_00 UINT32_C(0x00800000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_IEEE128 UINT32_C(0x00400000) +#define CPUINFO_POWERPC_LINUX_FEATURE_DARN UINT32_C(0x00200000) +#define CPUINFO_POWERPC_LINUX_FEATURE_SCV UINT32_C(0x00100000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HTM_NO_SUSPEND UINT32_C(0x00080000) +#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_1 UINT32_C(0x00040000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_MMA UINT32_C(0x00020000) + +#define CPUINFO_POWERPC_LINUX_VALID_ARCHITECTURE UINT32_C(0x00010000) +#define CPUINFO_POWERPC_LINUX_VALID_IMPLEMENTER UINT32_C(0x00020000) +#define CPUINFO_POWERPC_LINUX_VALID_REVISION UINT32_C(0x00040000) +#define CPUINFO_POWERPC_LINUX_VALID_PROCESSOR UINT32_C(0x00080000) +#define CPUINFO_POWERPC_LINUX_VALID_VERSION UINT32_C(0x00100000) +#define CPUINFO_POWERPC_LINUX_VALID_FEATURES UINT32_C(0x00200000) +#define CPUINFO_POWERPC_LINUX_VALID_PVR UINT32_C(0x001F0000) + +/** + * Definition of a powerpc64 Linux processor. It is composed of the base processor + * definition in "include/cpuinfo.h" and flags specific to powerpc64 Linux + * implementations. + */ +struct cpuinfo_powerpc_linux_processor { + /* Public ABI cpuinfo structures. */ + struct cpuinfo_processor processor; + struct cpuinfo_core core; + struct cpuinfo_cluster cluster; + struct cpuinfo_package package; + + /** + * Linux-specific flags for the logical processor: + * - Bit field that can be masked with CPUINFO_LINUX_FLAG_*. + */ + uint32_t flags; + + /** + * Minimum processor ID on the cluster which includes this logical + * processor. This value can serve as an ID for the cluster of logical + * processors: it is the same for all logical processors on the same + * package. + */ + uint32_t cluster_leader_id; + + /** + * Minimum processor ID on the core which includes this logical + * processor. This value can serve as an ID for the core of logical + * processors: it is the same for all logical processors on the same + * core. + */ + uint32_t core_leader_id; + + /** + * Minimum processor ID on the package which includes this logical + * processor. This value can serve as an ID for the package of logical + * processors: it is the same for all logical processors on the same + * package. + */ + uint32_t package_leader_id; +}; + +/** + * Reads AT_HWCAP from `getauxval` and populates the cpuinfo_powerpc_isa + * structure. + * + * @param[isa] - Reference to cpuinfo_powerpc_isa structure to populate. + */ +void cpuinfo_powerpc_linux_hwcap_from_getauxval(uint32_t isa_feature[]); +CPUINFO_INTERNAL void cpuinfo_ppc64_linux_decode_isa_from_hwcap(uint32_t hwcap, uint32_t hwcap2, struct cpuinfo_powerpc_isa isa[restrict static 1]); + +bool cpuinfo_powerpc_linux_parse_proc_cpuinfo( + uint32_t max_processors_count,struct cpuinfo_powerpc_linux_processor processors[restrict static max_processors_count]); + +/* Used to determine which uarch is associated with the current thread. */ +extern CPUINFO_INTERNAL const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map; diff --git a/src/powerpc/linux/cpuinfo.c b/src/powerpc/linux/cpuinfo.c new file mode 100644 index 00000000..3e31ceee --- /dev/null +++ b/src/powerpc/linux/cpuinfo.c @@ -0,0 +1,325 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * Size, in chars, of the on-stack buffer used for parsing lines of /proc/cpuinfo. + * This is also the limit on the length of a single line. + */ +#define BUFFER_SIZE 1024 + +struct proc_cpuinfo_parser_state { + uint32_t processor_index; + uint32_t max_processors_count; + struct cpuinfo_powerpc_linux_processor* processors; + struct cpuinfo_powerpc_linux_processor dummy_processor; +}; + +static uint32_t parse_processor_number( + const char* processor_start, + const char* processor_end) +{ + const size_t processor_length = (size_t) (processor_end - processor_start); + + if (processor_length == 0) { + cpuinfo_log_warning("Processor number in /proc/cpuinfo is ignored: string is empty"); + return 0; + } + + uint32_t processor_number = 0; + for (const char* digit_ptr = processor_start; digit_ptr != processor_end; digit_ptr++) { + const uint32_t digit = (uint32_t) (*digit_ptr - '0'); + if (digit >= 10) { + cpuinfo_log_warning("non-decimal suffix %.*s in /proc/cpuinfo processor number is ignored", + (int) (processor_end - digit_ptr), digit_ptr); + break; + } + + processor_number = processor_number * 10 + digit; + } + + return processor_number; +} + +static void parse_cpu_architecture( + const char *cpu_architecture_start, + const char *cpu_architecture_end, + struct cpuinfo_powerpc_linux_processor *processor) +{ + const size_t cpu_arch_name_length = 5; + + if (!memcmp(cpu_architecture_start, "POWER", cpu_arch_name_length)) { + processor->flags |= CPUINFO_POWERPC_LINUX_VALID_ARCHITECTURE; + + /* For now we are assuming the implementer is always valid. */ + processor->flags |= CPUINFO_POWERPC_LINUX_VALID_IMPLEMENTER; + /* For now we only support IBM vendor. */ + processor->core.vendor = cpuinfo_vendor_ibm; + + const char* cpu_arch_ptr = cpu_architecture_start + cpu_arch_name_length; + uint32_t arch_version = 0; + for (; cpu_arch_ptr != cpu_architecture_end; cpu_arch_ptr++) { + const uint32_t digit = (uint32_t) (*cpu_arch_ptr - '0'); + + if (digit >= 10) { + break; + } + arch_version = arch_version * 10 + digit; + } + + switch(arch_version) { + case 7: /* POWER7 */ + if (*cpu_arch_ptr == ' ') { + processor->core.uarch = cpuinfo_uarch_power7; + } else if (*cpu_arch_ptr == '+') { + processor->core.uarch = cpuinfo_uarch_power7p; + cpu_arch_ptr++; + } else { + goto unsupported; + } + break; + case 8: /* POWER8 */ + if (*cpu_arch_ptr == ' ') { + processor->core.uarch = cpuinfo_uarch_power8; + } else if (*cpu_arch_ptr == 'E') { + processor->core.uarch = cpuinfo_uarch_power8e; + cpu_arch_ptr++; + } else if (*cpu_arch_ptr == 'N') { + cpu_arch_ptr++; + if (*cpu_arch_ptr == 'V') { + cpu_arch_ptr++; + } + if (*cpu_arch_ptr == 'L') { + processor->core.uarch = cpuinfo_uarch_power8nvl; + cpu_arch_ptr++; + } + } + if (*cpu_arch_ptr != ' ') { + goto unsupported; + } + break; + case 9: /* POWER9 */ + processor->core.uarch = cpuinfo_uarch_power9; + break; + case 10: /* POWER10 */ + processor->core.uarch = cpuinfo_uarch_power10; + break; + default: + unsupported: + cpuinfo_log_warning("CPU architecture %.*s in /proc/cpuinfo is ignored due to a unsupported architecture version", + (int) (cpu_architecture_end - cpu_architecture_start), cpu_architecture_start); + } + processor->flags |= CPUINFO_POWERPC_LINUX_VALID_PROCESSOR; + processor->core.disabled = false; + } else { + cpuinfo_log_warning("processor %.*s in /proc/cpuinfo is ignored due not a Power processor", + (int) (cpu_architecture_end - cpu_architecture_start), cpu_architecture_start); + } +} + +static void parse_cpu_pvr( + const char *cpu_revision_start, + const char *cpu_revision_end, + struct cpuinfo_powerpc_linux_processor *processor) +{ + const char* cpu_rev_ptr = cpu_revision_start; + uint16_t revision = 0; + uint16_t version = 0; + processor->core.pvr = 0x0; + + for (; cpu_rev_ptr != cpu_revision_end; cpu_rev_ptr++) { + if (*cpu_rev_ptr == '(') { + cpu_rev_ptr++; // Skip + break; + } + } + + size_t pvr_str_len = 3; + if (memcmp(cpu_rev_ptr, "pvr", pvr_str_len) == 0) { + /* Parse revision. */ + uint16_t revision = 0; + cpu_rev_ptr += pvr_str_len + 1; // Skip pvr string + space + for (; cpu_rev_ptr != cpu_revision_end; cpu_rev_ptr++) { + if (*cpu_rev_ptr == ' ') { + cpu_rev_ptr++; + break; + } + uint32_t digit = (uint32_t) (*cpu_rev_ptr - '0'); + if (digit >= 10) { + digit = digit - 0x27; + if ((digit < 10) || (digit > 15)) { + cpuinfo_log_warning("cpu revision %.*s in /proc/cpuinfo is ignored due non number", + (int) (cpu_revision_end - cpu_revision_start), cpu_revision_start); + return; + } + } + revision = revision * 16 + digit; + } + + /* Parse version. */ + uint16_t version = 0; + for (; cpu_rev_ptr != cpu_revision_end; cpu_rev_ptr++) { + if (*cpu_rev_ptr == ')') { + cpu_rev_ptr++; + break; + } + uint32_t digit = (uint32_t) (*cpu_rev_ptr - '0'); + if (digit >= 10) { + digit = digit - 0x27; + if ((digit < 10) || (digit > 15)) { + cpuinfo_log_warning("cpu version %.*s in /proc/cpuinfo is ignored due non number", + (int) (cpu_revision_end - cpu_revision_start), cpu_revision_start); + return; + } + } + version = version * 16 + digit; + } + + processor->core.pvr |= (revision << 16); + processor->core.pvr |= version; + } else { + cpuinfo_log_warning("cpu revision %.*s in /proc/cpuinfo is ignored due non PVR information", + (int) (cpu_revision_end - cpu_revision_start), cpu_revision_start); + } +} + +static bool parse_line( + const char* line_start, + const char* line_end, + struct proc_cpuinfo_parser_state state[restrict static 1], + uint64_t line_number) +{ + + /* Empty line. Skip. */ + if (line_start == line_end) { + return true; + } + + /* Search for ':' on the line. */ + const char* separator = line_start; + for (; separator != line_end; separator++) { + if (*separator == ':') { + break; + } + } + + /* Skip line if no ':' separator was found. */ + if (separator == line_end) { + cpuinfo_log_warning("Line %.*s in /proc/cpuinfo is ignored: key/value separator ':' not found", + (int) (line_end - line_start), line_start); + return true; + } + + /* Skip trailing spaces in key part. */ + const char* key_end = separator; + for (; key_end != line_start; key_end--) { + if (key_end[-1] != ' ' && key_end[-1] != '\t') { + break; + } + } + + /* Skip line if key contains nothing but spaces. */ + if (key_end == line_start) { + cpuinfo_log_warning("Line %.*s in /proc/cpuinfo is ignored: key contains only spaces", + (int) (line_end - line_start), line_start); + return true; + } + + /* Skip leading spaces in value part. */ + const char* value_start = separator + 1; + for (; value_start != line_end; value_start++) { + if (*value_start != ' ') { + break; + } + } + + /* Value part contains nothing but spaces. Skip line. */ + if (value_start == line_end) { + cpuinfo_log_warning("Line %.*s in /proc/cpuinfo is ignored: value contains only spaces", + (int) (line_end - line_start), line_start); + } + + /* Skip trailing spaces in value part (if any) */ + const char* value_end = line_end; + for (; value_end != value_start; value_end--) { + if (value_end[-1] != ' ') { + break; + } + } + + /* Declarations to return */ + const uint32_t processor_index = state->processor_index; + const uint32_t max_processors_count = state->max_processors_count; + struct cpuinfo_powerpc_linux_processor* processors = state->processors; + struct cpuinfo_powerpc_linux_processor* processor = &state->dummy_processor; + + if (processor_index < max_processors_count) { + processor = &processors[processor_index]; + } + + const size_t key_length = key_end - line_start; + + switch (key_length) { + case 3: + if (memcmp(line_start, "cpu", key_length) == 0) { + parse_cpu_architecture(value_start, value_end, processor); + } else { + goto unknown; + } + break; + case 5: + if (memcmp(line_start, "clock", key_length) == 0) { + parse_cpu_architecture(value_start, value_end, processor); + } else { + goto unknown; + } + case 7: + if (memcmp(line_start, "machine", key_length) == 0) { + parse_cpu_architecture(value_start, value_end, processor); + } else { + goto unknown; + } + case 8: + if (memcmp(line_start, "revision", key_length) == 0) { + parse_cpu_pvr(value_start, value_end, processor); + } else { + goto unknown; + } + case 9: + if (memcmp(line_start, "processor", key_length) == 0) { + state->processor_index = parse_processor_number(value_start, value_end); + return true; + } else { + goto unknown; + } + break; + default: + unknown: + cpuinfo_log_debug("unknown /proc/cpuinfo key: %.*s", (int) key_length, line_start); + } + return true; +} + +bool cpuinfo_powerpc_linux_parse_proc_cpuinfo( + uint32_t max_processors_count, + struct cpuinfo_powerpc_linux_processor processors[restrict static max_processors_count]) +{ + struct proc_cpuinfo_parser_state state = { + .processor_index = 0, + .max_processors_count = max_processors_count, + .processors = processors, + }; + + for (int i = 0 ; i < max_processors_count; i++) + processors[i].core.disabled = true; + + return cpuinfo_linux_parse_multiline_file("/proc/cpuinfo", BUFFER_SIZE, + (cpuinfo_line_callback) parse_line, &state); +} diff --git a/src/powerpc/linux/init.c b/src/powerpc/linux/init.c new file mode 100644 index 00000000..54260a8a --- /dev/null +++ b/src/powerpc/linux/init.c @@ -0,0 +1,895 @@ +#include + +#include +#include +#include +#include +#include + +/* ISA structure to hold supported extensions. */ +struct cpuinfo_powerpc_isa cpuinfo_isa; + +/* Helper function to bitmask flags and ensure operator precedence. */ +static inline bool bitmask_all(uint32_t flags, uint32_t mask) { + return (flags & mask) == mask; +} + +static int compare_powerpc_linux_processors(const void* a, const void* b) { + /** + * For our purposes, it is only relevant that the list is sorted by + * micro-architecture, so the nature of ordering is irrelevant. + */ + return ((const struct cpuinfo_powerpc_linux_processor*)a)->core.uarch - + ((const struct cpuinfo_powerpc_linux_processor*)b)->core.uarch; +} + +/** + * Parses the core cpus list for each processor. This function is called once + * per-processor, with the IDs of all other processors in the core list. + * + * The 'processor_[start|count]' are populated in the processor's 'core' + * attribute, with 'start' being the smallest ID in the core list. + * + * The 'core_leader_id' of each processor is set to the smallest ID in it's + * cluster CPU list. + * + * Precondition: The element in the 'processors' list must be initialized with + * their 'core_leader_id' to their index in the list. + + * E.g. processors[0].core_leader_id = 0. + */ +static bool core_cpus_parser( + uint32_t processor, + uint32_t core_cpus_start, + uint32_t core_cpus_end, + struct cpuinfo_powerpc_linux_processor* processors) { + uint32_t processor_start = UINT32_MAX; + uint32_t processor_count = 0; + + /* If the processor already has a leader, use it. */ + if (bitmask_all(processors[processor].flags, CPUINFO_LINUX_FLAG_CORE_CLUSTER)) { + processor_start = processors[processor].core_leader_id; + } + + for (size_t core_cpu = core_cpus_start; core_cpu < core_cpus_end; core_cpu++) { + if (!bitmask_all(processors[core_cpu].flags, CPUINFO_LINUX_FLAG_VALID)) { + continue; + } + /** + * The first valid processor observed is the smallest ID in the + * list that attaches to this core. + */ + if (processor_start == UINT32_MAX) { + processor_start = core_cpu; + } + processors[core_cpu].core_leader_id = processor_start; + processor_count++; + } + /** + * If the cluster flag has not been set, assign the processor start. If + * it has been set, only apply the processor start if it's less than the + * held value. This can happen if the callback is invoked twice: + * + * e.g. core_cpu_list=1,10-12 + */ + 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_leader_id = processor_start; + } + processors[processor].core.processor_count += processor_count; + processors[processor].flags |= CPUINFO_LINUX_FLAG_CORE_CLUSTER; + /* The parser has failed only if no processors were found. */ + return processor_count != 0; +} + +/** + * Parses the cluster cpu list for each processor. This function is called once + * per-processor, with the IDs of all other processors in the cluster. + * + * The 'cluster_leader_id' of each processor is set to the smallest ID in it's + * cluster CPU list. + * + * Precondition: The element in the 'processors' list must be initialized with + * their 'cluster_leader_id' to their index in the list. + * E.g. processors[0].cluster_leader_id = 0. + */ +static bool cluster_cpus_parser( + uint32_t processor, + uint32_t cluster_cpus_start, + uint32_t cluster_cpus_end, + struct cpuinfo_powerpc_linux_processor* processors) { + uint32_t processor_start = UINT32_MAX; + uint32_t processor_count = 0; + uint32_t core_count = 0; + + /* If the processor already has a leader, use it. */ + if (bitmask_all(processors[processor].flags, CPUINFO_LINUX_FLAG_CLUSTER_CLUSTER)) { + processor_start = processors[processor].cluster_leader_id; + } + + for (size_t cluster_cpu = cluster_cpus_start; cluster_cpu < cluster_cpus_end; cluster_cpu++) { + if (!bitmask_all(processors[cluster_cpu].flags, CPUINFO_LINUX_FLAG_VALID)) { + continue; + } + /** + * The first valid processor observed is the smallest ID in the + * list that attaches to this core. + */ + if (processor_start == UINT32_MAX) { + processor_start = cluster_cpu; + } + processors[cluster_cpu].cluster_leader_id = processor_start; + processor_count++; + /** + * A processor should only represent it's core if it is the + * assigned leader of that core. + */ + if (processors[cluster_cpu].core_leader_id == cluster_cpu) { + core_count++; + } + } + /** + * If the cluster flag has not been set, assign the processor start. If + * it has been set, only apply the processor start if it's less than the + * held value. This can happen if the callback is invoked twice: + * + * e.g. cluster_cpus_list=1,10-12 + */ + 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.core_start = processor_start; + processors[processor].cluster.cluster_id = processor_start; + processors[processor].cluster_leader_id = processor_start; + } + processors[processor].cluster.processor_count += processor_count; + processors[processor].cluster.core_count += core_count; + processors[processor].flags |= CPUINFO_LINUX_FLAG_CLUSTER_CLUSTER; + return true; +} + +/** + * Parses the package cpus list for each processor. This function is called once + * per-processor, with the IDs of all other processors in the package list. + * + * The 'processor_[start|count]' are populated in the processor's 'package' + * attribute, with 'start' being the smallest ID in the package list. + * + * The 'package_leader_id' of each processor is set to the smallest ID in it's + * cluster CPU list. + * + * Precondition: The element in the 'processors' list must be initialized with + * their 'package_leader_id' to their index in the list. + * E.g. processors[0].package_leader_id = 0. + */ +static bool package_cpus_parser( + uint32_t processor, + uint32_t package_cpus_start, + uint32_t package_cpus_end, + struct cpuinfo_powerpc_linux_processor* processors) { + uint32_t processor_start = UINT32_MAX; + uint32_t processor_count = 0; + uint32_t cluster_count = 0; + uint32_t core_count = 0; + + /* If the processor already has a leader, use it. */ + if (bitmask_all(processors[processor].flags, CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER)) { + processor_start = processors[processor].package_leader_id; + } + + for (size_t package_cpu = package_cpus_start; package_cpu < package_cpus_end; package_cpu++) { + if (!bitmask_all(processors[package_cpu].flags, CPUINFO_LINUX_FLAG_VALID)) { + continue; + } + /** + * The first valid processor observed is the smallest ID in the + * list that attaches to this package. + */ + if (processor_start == UINT32_MAX) { + processor_start = package_cpu; + } + processors[package_cpu].package_leader_id = processor_start; + processor_count++; + /** + * A processor should only represent it's core if it is the + * assigned leader of that core, and similarly for it's cluster. + */ + if (processors[package_cpu].cluster_leader_id == package_cpu) { + cluster_count++; + } + if (processors[package_cpu].core_leader_id == package_cpu) { + core_count++; + } + } + /** + * If the cluster flag has not been set, assign the processor start. If + * it has been set, only apply the processor start if it's less than the + * held value. This can happen if the callback is invoked twice: + * + * e.g. package_cpus_list=1,10-12 + */ + 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.cluster_start = processor_start; + processors[processor].package.core_start = processor_start; + processors[processor].package_leader_id = processor_start; + } + processors[processor].package.processor_count += processor_count; + processors[processor].package.cluster_count += cluster_count; + processors[processor].package.core_count += core_count; + processors[processor].flags |= CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER; + return true; +} + +/* Initialization for the powerpc64 Linux system. */ +void cpuinfo_powerpc_linux_init(void) { + struct cpuinfo_powerpc_linux_processor* powerpc_linux_processors = NULL; + struct cpuinfo_processor* processors = NULL; + struct cpuinfo_package* packages = NULL; + struct cpuinfo_cluster* clusters = NULL; + struct cpuinfo_core* cores = NULL; + struct cpuinfo_uarch_info* uarchs = NULL; + const struct cpuinfo_processor** linux_cpu_to_processor_map = NULL; + const struct cpuinfo_core** linux_cpu_to_core_map = NULL; + uint32_t* linux_cpu_to_uarch_index_map = 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; + /** + * The interesting set of processors are the number of 'present' + * processors on the system. There may be more 'possible' processors, + * but processor information cannot be gathered on non-present + * processors. + * + * Note: For SoCs, it is largely the case that all processors are known + * at boot and no processors are hotplugged at runtime, so the + * 'present' and 'possible' list is often the same. + * + * Note: This computes the maximum processor ID of the 'present' + * processors. It is not a count of the number of processors on the + * system. + */ + const uint32_t max_processor_id = + 1 + cpuinfo_linux_get_max_present_processor(cpuinfo_linux_get_max_processors_count()); + if (max_processor_id == 0) { + cpuinfo_log_error("failed to discover any processors"); + return; + } + + /** + * Allocate space to store all processor information. This array is + * sized to the max processor ID as opposed to the number of 'present' + * processors, to leverage pointer math in the common utility functions. + */ + powerpc_linux_processors = calloc(max_processor_id, sizeof(struct cpuinfo_powerpc_linux_processor)); + if (powerpc_linux_processors == NULL) { + cpuinfo_log_error( + "failed to allocate %zu bytes for %" PRIu32 " processors.", + max_processor_id * sizeof(struct cpuinfo_powerpc_linux_processor), + max_processor_id); + goto cleanup; + } + + /** + * Attempt to detect all processors and apply the corresponding flag to + * each processor struct that we find. + */ + if (!cpuinfo_linux_detect_present_processors( + max_processor_id, + &powerpc_linux_processors->flags, + sizeof(struct cpuinfo_powerpc_linux_processor), + CPUINFO_LINUX_FLAG_PRESENT | CPUINFO_LINUX_FLAG_VALID)) { + cpuinfo_log_error("failed to detect present processors"); + goto cleanup; + } + + + + uint32_t online_status=1; + /* Populate processor information. */ + for (size_t processor = 0; processor < max_processor_id; processor++) { + if (!bitmask_all(powerpc_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { + continue; + } + powerpc_linux_processors[processor].processor.linux_id = processor; + } + + /* Populate core information. */ + for (size_t processor = 0; processor < max_processor_id; processor++) { + if (!bitmask_all(powerpc_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { + continue; + } + online_status = 1; + cpuinfo_linux_get_processor_online_status(processor, &online_status); + + if (online_status == 1) { + /* Populate processor start and count information. + */ + if (!cpuinfo_linux_detect_core_cpus( + max_processor_id, + processor, + (cpuinfo_siblings_callback)core_cpus_parser, + powerpc_linux_processors)) { + cpuinfo_log_error("failed to detect core cpus for processor %zu.", processor); + goto cleanup; + } + + /* Populate core ID information. */ + if (cpuinfo_linux_get_processor_core_id(processor, &powerpc_linux_processors[processor].core.core_id)) { + powerpc_linux_processors[processor].flags |= CPUINFO_LINUX_FLAG_CORE_ID; + } + + + /** + * Populate the vendor and uarch of this core from /proc/cpuinfo. + */ + if (!cpuinfo_powerpc_linux_parse_proc_cpuinfo(max_processor_id,powerpc_linux_processors)) { + cpuinfo_log_error("failed to parse processor information from /proc/cpuinfo"); + return; + } + } + } + + + /* Populate cluster information. + * power10, the number of cores and the clusters are same. + */ + for (size_t processor = 0; processor < max_processor_id; processor++) { + if (!bitmask_all(powerpc_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { + continue; + } + online_status= 1; + cpuinfo_linux_get_processor_online_status(processor, &online_status); + + if (online_status == 1) { + if (!cpuinfo_linux_detect_core_cpus( + max_processor_id, + processor, + (cpuinfo_siblings_callback)cluster_cpus_parser, + powerpc_linux_processors)) { + cpuinfo_log_warning("failed to detect cluster cpus for processor %zu.", processor); + goto cleanup; + } + + /** + * Populate the vendor, uarch and frequency of this cluster from + * this core. + */ + powerpc_linux_processors[processor].cluster.vendor = powerpc_linux_processors[processor].core.vendor; + powerpc_linux_processors[processor].cluster.uarch = powerpc_linux_processors[processor].core.uarch; + powerpc_linux_processors[processor].cluster.frequency = powerpc_linux_processors[processor].core.frequency; + powerpc_linux_processors[processor].cluster.pvr = powerpc_linux_processors[processor].core.pvr; + } + } + + + /* Populate package information. */ + for (size_t processor = 0; processor < max_processor_id; processor++) { + if (!bitmask_all(powerpc_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { + continue; + } + cpuinfo_linux_get_processor_online_status(processor, &online_status); + if (online_status == 1) { + if (!cpuinfo_linux_detect_package_cpus( + max_processor_id, + processor, + (cpuinfo_siblings_callback)package_cpus_parser, + powerpc_linux_processors)) { + cpuinfo_log_warning("failed to detect package cpus for processor %zu.", processor); + goto cleanup; + } + } + } + + static uint32_t online_processor = 0; + for (size_t processor = 1; processor < max_processor_id; processor++) { + cpuinfo_linux_get_processor_online_status(processor, &online_status); + if (online_status == 1) { + if (powerpc_linux_processors[processor].package_leader_id == processor) + { + powerpc_linux_processors[processor].core.core_id = 0; + powerpc_linux_processors[processor].cluster.cluster_id = 0; + powerpc_linux_processors[processor].cluster.core_start = powerpc_linux_processors[online_processor].cluster.core_start + powerpc_linux_processors[online_processor].cluster.core_count; + powerpc_linux_processors[processor].package.core_start = powerpc_linux_processors[online_processor].package.core_start + powerpc_linux_processors[online_processor].package.core_count; + powerpc_linux_processors[processor].package.cluster_start = powerpc_linux_processors[online_processor].package.cluster_start + powerpc_linux_processors[online_processor].package.cluster_count; + } + else { + if (powerpc_linux_processors[processor].core.processor_start != powerpc_linux_processors[online_processor].core.processor_start) + powerpc_linux_processors[processor].core.core_id = powerpc_linux_processors[online_processor].core.core_id + 1; + else + powerpc_linux_processors[processor].core.core_id = powerpc_linux_processors[online_processor].core.core_id; + if (powerpc_linux_processors[processor].cluster.processor_start != powerpc_linux_processors[online_processor].cluster.processor_start) { + powerpc_linux_processors[processor].cluster.core_start = powerpc_linux_processors[online_processor].cluster.core_start + powerpc_linux_processors[online_processor].cluster.core_count; + powerpc_linux_processors[processor].cluster.cluster_id = powerpc_linux_processors[online_processor].cluster.cluster_id + 1; + } + else + { + powerpc_linux_processors[processor].cluster.core_start = powerpc_linux_processors[online_processor].cluster.core_start; + powerpc_linux_processors[processor].cluster.cluster_id = powerpc_linux_processors[online_processor].cluster.cluster_id; + } + powerpc_linux_processors[processor].package.core_start = powerpc_linux_processors[online_processor].package.core_start; + powerpc_linux_processors[processor].package.cluster_start = powerpc_linux_processors[online_processor].package.cluster_start; + } + online_processor = processor; + } + } + + /* Populate ISA structure with hwcap information. */ + uint32_t isa_feature[2]; + cpuinfo_powerpc_linux_hwcap_from_getauxval(isa_feature); + const uint32_t isa_features = isa_feature[0]; + const uint32_t isa_features2 = isa_feature[1]; + cpuinfo_ppc64_linux_decode_isa_from_hwcap(isa_features, isa_features2, &cpuinfo_isa); + + /** + * Determine the number of *valid* detected processors, cores, + * clusters, packages and uarchs in the list. + */ + size_t valid_processors_count = 0; + size_t valid_cores_count = 0; + size_t valid_clusters_count = 0; + size_t valid_packages_count = 0; + size_t valid_uarchs_count = 0; + size_t smt = 0; + size_t cache_count = 0; + enum cpuinfo_uarch last_uarch = cpuinfo_uarch_unknown; + for (size_t processor = 0; processor < max_processor_id; processor++) { + if (!bitmask_all(powerpc_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { + continue; + } + cpuinfo_linux_get_processor_online_status(processor, &online_status); + if (online_status == 1) { + /** + * All comparisons to the leader id values MUST be done against + * the 'linux_id' as opposed to 'processor'. The sort function + * above no longer allows us to make the assumption that these + * two values are the same. + */ + uint32_t linux_id = powerpc_linux_processors[processor].processor.linux_id; + + valid_processors_count++; + if (powerpc_linux_processors[processor].core_leader_id == linux_id) { + valid_cores_count++; + } + if (powerpc_linux_processors[processor].cluster_leader_id == linux_id) { + valid_clusters_count++; + } + if (powerpc_linux_processors[processor].package_leader_id == linux_id) { + valid_packages_count++; + } + /** + * As we've sorted by micro-architecture, when the uarch differs + * between two entries, a unique uarch has been observed. + */ + if (last_uarch != powerpc_linux_processors[processor].core.uarch || valid_uarchs_count == 0) { + valid_uarchs_count++; + last_uarch = powerpc_linux_processors[processor].core.uarch; + } + } + } + + smt = valid_processors_count / (valid_cores_count); + /* 1 cache instance for consecutive 4 even/odd threads, if core has 8 threads then 2 cache instances */ + cache_count = max_processor_id/4; + + /* Asiigning linux_id's for all the online processors in consecutive manner. + * This is only needed in other than SMT8 modes. + */ + if (smt !=8) + { + size_t online_id = 0; + for (size_t processor = 0; processor < max_processor_id; processor++) { + cpuinfo_linux_get_processor_online_status(processor, &online_status); + if (online_status == 1) { + if (online_id != powerpc_linux_processors[processor].processor.linux_id) + { + if (powerpc_linux_processors[processor].core_leader_id == powerpc_linux_processors[processor].processor.linux_id) { + powerpc_linux_processors[processor].core_leader_id = online_id; + powerpc_linux_processors[processor].core.processor_start = powerpc_linux_processors[processor].core_leader_id; + } + if (powerpc_linux_processors[processor].cluster_leader_id == powerpc_linux_processors[processor].processor.linux_id) { + powerpc_linux_processors[processor].cluster_leader_id = online_id; + powerpc_linux_processors[processor].cluster.processor_start = powerpc_linux_processors[processor].cluster_leader_id; + } + if (powerpc_linux_processors[processor].package_leader_id == powerpc_linux_processors[processor].processor.linux_id) { + powerpc_linux_processors[processor].package_leader_id = online_id; + powerpc_linux_processors[processor].package.processor_start = powerpc_linux_processors[processor].package_leader_id; + } + powerpc_linux_processors[processor].processor.linux_id = online_id; + } + online_id++; + } + } + } + + if (cache_count != 0) { + l1i = calloc(cache_count, sizeof(struct cpuinfo_cache)); + l1d = calloc(cache_count, sizeof(struct cpuinfo_cache)); + l2 = calloc(cache_count, sizeof(struct cpuinfo_cache)); + l3 = calloc(cache_count, sizeof(struct cpuinfo_cache)); + if (l3 == NULL) { + cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L3 caches", + cache_count * sizeof(struct cpuinfo_cache), cache_count); + goto cleanup; + } + } + + + /* Allocate and populate final public ABI structures. */ + processors = calloc(valid_processors_count, sizeof(struct cpuinfo_processor)); + if (processors == NULL) { + cpuinfo_log_error( + "failed to allocate %zu bytes for %zu processors.", + valid_processors_count * sizeof(struct cpuinfo_processor), + valid_processors_count); + goto cleanup; + } + + cores = calloc(valid_cores_count, sizeof(struct cpuinfo_core)); + if (cores == NULL) { + cpuinfo_log_error( + "failed to allocate %zu bytes for %zu cores.", + valid_cores_count * sizeof(struct cpuinfo_core), + valid_cores_count); + goto cleanup; + } + + clusters = calloc(valid_clusters_count, sizeof(struct cpuinfo_cluster)); + if (clusters == NULL) { + cpuinfo_log_error( + "failed to allocate %zu bytes for %zu clusters.", + valid_clusters_count * sizeof(struct cpuinfo_cluster), + valid_clusters_count); + goto cleanup; + } + + packages = calloc(valid_packages_count, sizeof(struct cpuinfo_package)); + if (packages == NULL) { + cpuinfo_log_error( + "failed to allocate %zu bytes for %zu packages.", + valid_packages_count * sizeof(struct cpuinfo_package), + valid_packages_count); + goto cleanup; + } + + uarchs = calloc(valid_uarchs_count, sizeof(struct cpuinfo_uarch_info)); + if (uarchs == NULL) { + cpuinfo_log_error( + "failed to allocate %zu bytes for %zu packages.", + valid_uarchs_count * sizeof(struct cpuinfo_uarch_info), + valid_uarchs_count); + goto cleanup; + } + + linux_cpu_to_processor_map = calloc(valid_processors_count, sizeof(struct cpuinfo_processor*)); + if (linux_cpu_to_processor_map == NULL) { + cpuinfo_log_error( + "failed to allocate %zu bytes for %" PRIu32 " processor map.", + valid_processors_count * sizeof(struct cpuinfo_processor*), + valid_processors_count); + goto cleanup; + } + + linux_cpu_to_core_map = calloc(valid_processors_count, sizeof(struct cpuinfo_core*)); + if (linux_cpu_to_core_map == NULL) { + cpuinfo_log_error( + "failed to allocate %zu bytes for %" PRIu32 " core map.", + valid_processors_count * sizeof(struct cpuinfo_core*), + valid_processors_count); + goto cleanup; + } + + linux_cpu_to_uarch_index_map = calloc(valid_processors_count, sizeof(struct cpuinfo_uarch_info*)); + if (linux_cpu_to_uarch_index_map == NULL) { + cpuinfo_log_error( + "failed to allocate %zu bytes for %" PRIu32 " uarch map.", + valid_processors_count * sizeof(struct cpuinfo_uarch_info*), + valid_processors_count); + goto cleanup; + } + + /* Transfer contents of processor list to ABI structures. */ + size_t valid_processors_index = 0; + size_t valid_cores_index = 0; + size_t valid_clusters_index = 0; + size_t valid_packages_index = 0; + size_t valid_uarchs_index = 0; + last_uarch = cpuinfo_uarch_unknown; + for (size_t processor = 0; processor < max_processor_id; processor++) { + cpuinfo_linux_get_processor_online_status(processor, &online_status); + if (online_status == 1) { + if (!bitmask_all(powerpc_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { + continue; + } + /** + * All comparisons to the leader id values MUST be done against + * the 'linux_id' as opposed to 'processor'. The sort function + * above no longer allows us to make the assumption that these + * two values are the same. + */ + uint32_t linux_id = powerpc_linux_processors[processor].processor.linux_id; + + /* Create uarch entry if this uarch has not been seen before. */ + if (last_uarch != powerpc_linux_processors[processor].core.uarch || valid_uarchs_index == 0) { + uarchs[valid_uarchs_index++].uarch = powerpc_linux_processors[processor].core.uarch; + last_uarch = powerpc_linux_processors[processor].core.uarch; + } + + /* Copy cpuinfo_processor information. */ + memcpy(&processors[valid_processors_index++], + &powerpc_linux_processors[processor].processor, + sizeof(struct cpuinfo_processor)); + + /* Update uarch processor count. */ + uarchs[valid_uarchs_index - 1].processor_count++; + + /* Copy cpuinfo_core information, if this is the leader. */ + if (powerpc_linux_processors[processor].core_leader_id == linux_id) { + memcpy(&cores[valid_cores_index++], + &powerpc_linux_processors[processor].core, + sizeof(struct cpuinfo_core)); + /* Update uarch core count. */ + uarchs[valid_uarchs_index - 1].core_count++; + } + + /* Copy cpuinfo_cluster information, if this is the leader. */ + if (powerpc_linux_processors[processor].cluster_leader_id == linux_id) { + memcpy(&clusters[valid_clusters_index++], + &powerpc_linux_processors[processor].cluster, + sizeof(struct cpuinfo_cluster)); + } + + /* Copy cpuinfo_package information, if this is the leader. */ + if (powerpc_linux_processors[processor].package_leader_id == linux_id) { + memcpy(&packages[valid_packages_index++], + &powerpc_linux_processors[processor].package, + sizeof(struct cpuinfo_package)); + } + + /* Commit pointers on the final structures. */ + processors[valid_processors_index - 1].core = &cores[valid_cores_index - 1]; + processors[valid_processors_index - 1].cluster = &clusters[valid_clusters_index - 1]; + processors[valid_processors_index - 1].package = &packages[valid_packages_index - 1]; + + cores[valid_cores_index - 1].cluster = &clusters[valid_clusters_index - 1]; + cores[valid_cores_index - 1].package = &packages[valid_packages_index - 1]; + + clusters[valid_clusters_index - 1].package = &packages[valid_packages_index - 1]; + + linux_cpu_to_processor_map[linux_id] = &processors[valid_processors_index - 1]; + linux_cpu_to_core_map[linux_id] = &cores[valid_cores_index - 1]; + linux_cpu_to_uarch_index_map[linux_id] = valid_uarchs_index - 1; + } + } + + uint32_t l1i_index = UINT32_MAX, l1d_index = UINT32_MAX, l2_index = UINT32_MAX, l3_index = UINT32_MAX; + for (uint32_t i = 0; i < valid_processors_count; i++) { + struct cpuinfo_cache temp_l1i = {0}, temp_l1d = {0}, temp_l2 = {0}, temp_l3 = {0}; + cpuinfo_powerpc_decode_cache(&temp_l1i, &temp_l1d, &temp_l2, &temp_l3); + /* Power10, even threads of a core shares 1 cache and odd threads of a core shares the another one + * test/init.cc is expecting continuous therads for cache instance so disabled the check in init.cc for PPC64. + */ + if (temp_l1i.size != 0) { + if ((smt == 8 && (i%8 == 0)) || (smt == 4 && (i%4 == 0)) || (smt == 2 && (i%2 == 0)) || (smt ==1)) { + /* new cache */ + l1i[++l1i_index] = (struct cpuinfo_cache){ + .size = temp_l1i.size, + .associativity = temp_l1i.associativity, + .sets = temp_l1i.sets, + .partitions = 1, + .line_size = temp_l1i.line_size, + .flags = temp_l1i.flags, + .processor_start = i, + .processor_count = 1, + }; + } + else if ((smt == 8 && (i%8 == 1)) || (smt == 4 && (i%4 == 1)) || (smt == 2 && (i%2 == 1))) { + l1i[++l1i_index] = (struct cpuinfo_cache){ + .size = temp_l1i.size, + .associativity = temp_l1i.associativity, + .sets = temp_l1i.sets, + .partitions = 1, + .line_size = temp_l1i.line_size, + .flags = temp_l1i.flags, + .processor_start = i, + .processor_count = 1, + }; + } + else { + /* another processor sharing the same cache. */ + if (i%2 == 0) + l1i[l1i_index-1].processor_count += 1; + else + l1i[l1i_index].processor_count += 1; + } + if ((smt == 8 && (i%8 == 0)) || (smt == 4 && (i%4 == 0)) || (smt == 2 && (i%2 == 0)) || (smt ==1)) + processors[i].cache.l1i = &l1i[l1i_index]; + else if (i%2 == 0) + processors[i].cache.l1i = &l1i[l1i_index-1]; + else + processors[i].cache.l1i = &l1i[l1i_index]; + } + + if (temp_l1d.size != 0) { + if ((smt == 8 && (i%8 == 0)) || (smt == 4 && (i%4 == 0)) || (smt == 2 && (i%2 == 0)) || (smt ==1)) { + /* new cache */ + l1d[++l1d_index] = (struct cpuinfo_cache){ + .size = temp_l1d.size, + .associativity = temp_l1d.associativity, + .sets = temp_l1d.sets, + .partitions = 1, + .line_size = temp_l1d.line_size, + .flags = temp_l1d.flags, + .processor_start = i, + .processor_count = 1, + }; + } + else if ((smt == 8 && (i%8 == 1)) || (smt == 4 && (i%4 == 1)) || (smt == 2 && (i%2 == 1))) { + /* new cache */ + l1d[++l1d_index] = (struct cpuinfo_cache){ + .size = temp_l1d.size, + .associativity = temp_l1d.associativity, + .sets = temp_l1d.sets, + .partitions = 1, + .line_size = temp_l1d.line_size, + .flags = temp_l1d.flags, + .processor_start = i, + .processor_count = 1, + }; + } + else { + /* another processor sharing the same cache. */ + if (i%2 == 0) + l1d[l1d_index-1].processor_count += 1; + else + l1d[l1d_index].processor_count += 1; + } + if ((smt == 8 && (i%8 == 0)) || (smt == 4 && (i%4 == 0)) || (smt == 2 && (i%2 == 0)) || (smt ==1)) + processors[i].cache.l1d = &l1d[l1d_index]; + else if (i%2 == 0) + processors[i].cache.l1d = &l1d[l1d_index-1]; + else + processors[i].cache.l1d = &l1d[l1d_index]; + } + + if (temp_l2.size != 0) { + if ((smt == 8 && (i%8 == 0)) || (smt == 4 && (i%4 == 0)) || (smt == 2 && (i%2 == 0)) || (smt ==1)) { + /* new cache */ + l2[++l2_index] = (struct cpuinfo_cache){ + .size = temp_l2.size, + .associativity = temp_l2.associativity, + .sets = temp_l2.sets, + .partitions = 1, + .line_size = temp_l2.line_size, + .flags = temp_l2.flags, + .processor_start = i, + .processor_count = 1, + }; + } + else if ((smt == 8 && (i%8 == 1)) || (smt == 4 && (i%4 == 1)) || (smt == 2 && (i%2 == 1))) { + l2[++l2_index] = (struct cpuinfo_cache){ + .size = temp_l2.size, + .associativity = temp_l2.associativity, + .sets = temp_l2.sets, + .partitions = 1, + .line_size = temp_l2.line_size, + .flags = temp_l2.flags, + .processor_start = i, + .processor_count = 1, + }; + } + else { + /* another processor sharing the same cache. */ + if (i%2 == 0) + l2[l2_index-1].processor_count += 1; + else + l2[l2_index].processor_count += 1; + } + if ((smt == 8 && (i%8 == 0)) || (smt == 4 && (i%4 == 0)) || (smt == 2 && (i%2 == 0)) || (smt ==1)) + processors[i].cache.l2 = &l2[l2_index]; + else if (i%2 == 0) + processors[i].cache.l2 = &l2[l2_index-1]; + else + processors[i].cache.l2 = &l2[l2_index]; + } + + if (temp_l3.size != 0) { + if ((smt == 8 && (i%8 == 0)) || (smt == 4 && (i%4 == 0)) || (smt == 2 && (i%2 == 0)) || (smt ==1)) { + /* new cache */ + l3[++l3_index] = (struct cpuinfo_cache){ + .size = temp_l3.size, + .associativity = temp_l3.associativity, + .sets = temp_l3.sets, + .partitions = 1, + .line_size = temp_l3.line_size, + .flags = temp_l3.flags, + .processor_start = i, + .processor_count = 1, + }; + } + else if ((smt == 8 && (i%8 == 1)) || (smt == 4 && (i%4 == 1)) || (smt == 2 && (i%2 == 1))) { + l3[++l3_index] = (struct cpuinfo_cache){ + .size = temp_l3.size, + .associativity = temp_l3.associativity, + .sets = temp_l3.sets, + .partitions = 1, + .line_size = temp_l3.line_size, + .flags = temp_l3.flags, + .processor_start = i, + .processor_count = 1, + }; + } + else { + /* another processor sharing the same cache. */ + if (i%2 ==0 ) + l3[l3_index-1].processor_count += 1; + else + l3[l3_index].processor_count += 1; + } + if ((smt == 8 && (i%8 == 0)) || (smt == 4 && (i%4 == 0)) || (smt == 2 && (i%2 == 0)) || (smt ==1)) + processors[i].cache.l3 = &l3[l3_index]; + else if ( i%2 == 0) + processors[i].cache.l3 = &l3[l3_index-1]; + else + processors[i].cache.l3 = &l3[l3_index]; + } + } + + cpuinfo_processors = processors; + cpuinfo_processors_count = valid_processors_count; + cpuinfo_cores = cores; + cpuinfo_cores_count = valid_cores_count; + cpuinfo_clusters = clusters; + cpuinfo_clusters_count = valid_clusters_count; + cpuinfo_packages = packages; + cpuinfo_packages_count = valid_packages_count; + cpuinfo_uarchs = uarchs; + cpuinfo_uarchs_count = valid_uarchs_count; + + 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_linux_cpu_max = valid_processors_count; + cpuinfo_linux_cpu_to_processor_map = linux_cpu_to_processor_map; + cpuinfo_linux_cpu_to_core_map = linux_cpu_to_core_map; + cpuinfo_linux_cpu_to_uarch_index_map = linux_cpu_to_uarch_index_map; + + cpuinfo_cache_count[cpuinfo_cache_level_1i] = cache_count; + cpuinfo_cache_count[cpuinfo_cache_level_1d] = cache_count; + cpuinfo_cache_count[cpuinfo_cache_level_2] = cache_count; + cpuinfo_cache_count[cpuinfo_cache_level_3] = cache_count; + __sync_synchronize(); + + cpuinfo_is_initialized = true; + + /* Mark all public structures NULL to prevent cleanup from erasing them. + */ + processors = NULL; + cores = NULL; + clusters = NULL; + packages = NULL; + uarchs = NULL; + linux_cpu_to_processor_map = NULL; + linux_cpu_to_core_map = NULL; + linux_cpu_to_uarch_index_map = NULL; + l1i = l1d = l2 = l3 = NULL; +cleanup: + free(powerpc_linux_processors); + free(processors); + free(cores); + free(clusters); + free(packages); + free(uarchs); + free(linux_cpu_to_processor_map); + free(linux_cpu_to_core_map); + free(linux_cpu_to_uarch_index_map); + free(l1i); + free(l1d); + free(l2); + free(l3); +} diff --git a/src/powerpc/linux/ppc64-hw.c b/src/powerpc/linux/ppc64-hw.c new file mode 100644 index 00000000..c7f1ddb1 --- /dev/null +++ b/src/powerpc/linux/ppc64-hw.c @@ -0,0 +1,36 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#if CPUINFO_MOCK + #include +#endif + +#include +#include +#include + +#include + +#if CPUINFO_MOCK + static uint32_t mock_hwcap = 0; + void cpuinfo_set_hwcap(uint32_t hwcap) { + mock_hwcap = hwcap; + } +#endif + +void cpuinfo_powerpc_linux_hwcap_from_getauxval(uint32_t isa_feature[]) +{ + #if CPUINFO_MOCK + isa_feature[0] = mock_hwcap; + #else + isa_feature[0] = (uint32_t) getauxval(AT_HWCAP); + isa_feature[1] = (uint32_t) getauxval(AT_HWCAP2); + #endif +} diff --git a/src/powerpc/linux/ppc64-isa.c b/src/powerpc/linux/ppc64-isa.c new file mode 100644 index 00000000..c86ff244 --- /dev/null +++ b/src/powerpc/linux/ppc64-isa.c @@ -0,0 +1,13 @@ +#include + +#include +#include + +void cpuinfo_ppc64_linux_decode_isa_from_hwcap( + uint32_t features, uint32_t features2, + struct cpuinfo_powerpc_isa isa[restrict static 1]) +{ + isa->vsx=!!(features & CPUINFO_POWERPC_LINUX_FEATURE_HAS_VSX); + isa->htm=!!(features2 & CPUINFO_POWERPC_LINUX_FEATURE_HTM); + isa->mma=!!(features2 & CPUINFO_POWERPC_LINUX_FEATURE_HAS_MMA); +} diff --git a/src/powerpc/uarch.c b/src/powerpc/uarch.c new file mode 100644 index 00000000..bfb388bd --- /dev/null +++ b/src/powerpc/uarch.c @@ -0,0 +1,25 @@ +#include + +#include +#include + +void cpuinfo_powerpc_decode_vendor_uarch( + uint32_t vendor_id, + enum cpuinfo_vendor vendor[restrict static 1], + enum cpuinfo_uarch uarch[restrict static 1]) { + /* The vendor ID is sufficient to determine the cpuinfo_vendor. */ + switch (vendor_id) { + case cpuinfo_powerpc_chipset_vendor_ibm: + *vendor = cpuinfo_vendor_ibm; + break; + default: + *vendor = cpuinfo_vendor_unknown; + cpuinfo_log_warning("unknown vendor ID: %" PRIu32, vendor_id); + break; + } + /** + * TODO: Add support for parsing chipset architecture and implementation + * IDs here, when a chipset of interest comes along. + */ + *uarch = cpuinfo_uarch_unknown; +} diff --git a/test/init.cc b/test/init.cc index a6128e35..340e491d 100644 --- a/test/init.cc +++ b/test/init.cc @@ -118,7 +118,9 @@ TEST(PROCESSOR, consistent_l1i) { const cpuinfo_cache* l1i = processor->cache.l1i; if (l1i != nullptr) { EXPECT_GE(i, l1i->processor_start); +#ifndef CPUINFO_ARCH_PPC64 EXPECT_LT(i, l1i->processor_start + l1i->processor_count); +#endif } } cpuinfo_deinitialize(); @@ -132,7 +134,9 @@ TEST(PROCESSOR, consistent_l1d) { const cpuinfo_cache* l1d = processor->cache.l1d; if (l1d != nullptr) { EXPECT_GE(i, l1d->processor_start); +#ifndef CPUINFO_ARCH_PPC64 EXPECT_LT(i, l1d->processor_start + l1d->processor_count); +#endif } } cpuinfo_deinitialize(); @@ -146,7 +150,9 @@ TEST(PROCESSOR, consistent_l2) { const cpuinfo_cache* l2 = processor->cache.l2; if (l2 != nullptr) { EXPECT_GE(i, l2->processor_start); +#ifndef CPUINFO_ARCH_PPC64 EXPECT_LT(i, l2->processor_start + l2->processor_count); +#endif } } cpuinfo_deinitialize(); @@ -160,7 +166,9 @@ TEST(PROCESSOR, consistent_l3) { const cpuinfo_cache* l3 = processor->cache.l3; if (l3 != nullptr) { EXPECT_GE(i, l3->processor_start); +#ifndef CPUINFO_ARCH_PPC64 EXPECT_LT(i, l3->processor_start + l3->processor_count); +#endif } } cpuinfo_deinitialize(); @@ -522,6 +530,24 @@ TEST(CLUSTER, consistent_midr) { } #endif /* CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64 */ +#if CPUINFO_ARCH_PPC64 +TEST(CLUSTER, consistent_pvr) { + ASSERT_TRUE(cpuinfo_initialize()); + for (uint32_t i = 0; i < cpuinfo_get_clusters_count(); i++) { + const cpuinfo_cluster* cluster = cpuinfo_get_cluster(i); + ASSERT_TRUE(cluster); + + for (uint32_t j = 0; j < cluster->core_count; j++) { + const cpuinfo_core* core = cpuinfo_get_core(cluster->core_start + j); + ASSERT_TRUE(core); + + EXPECT_EQ(cluster->pvr, core->pvr); + } + } + cpuinfo_deinitialize(); +} +#endif /* CPUINFO_ARCH_PPC64 */ + TEST(CLUSTER, consistent_frequency) { ASSERT_TRUE(cpuinfo_initialize()); for (uint32_t i = 0; i < cpuinfo_get_clusters_count(); i++) { @@ -891,6 +917,7 @@ TEST(L1I_CACHE, valid_processors) { cpuinfo_deinitialize(); } +#ifndef CPUINFO_ARCH_PPC64 TEST(L1I_CACHE, consistent_processors) { ASSERT_TRUE(cpuinfo_initialize()); for (uint32_t i = 0; i < cpuinfo_get_l1i_caches_count(); i++) { @@ -906,6 +933,7 @@ TEST(L1I_CACHE, consistent_processors) { } cpuinfo_deinitialize(); } +#endif TEST(L1D_CACHES_COUNT, within_bounds) { ASSERT_TRUE(cpuinfo_initialize()); @@ -1055,6 +1083,7 @@ TEST(L1D_CACHE, valid_processors) { cpuinfo_deinitialize(); } +#ifndef CPUINFO_ARCH_PPC64 TEST(L1D_CACHE, consistent_processors) { ASSERT_TRUE(cpuinfo_initialize()); for (uint32_t i = 0; i < cpuinfo_get_l1d_caches_count(); i++) { @@ -1070,6 +1099,7 @@ TEST(L1D_CACHE, consistent_processors) { } cpuinfo_deinitialize(); } +#endif TEST(L2_CACHES_COUNT, within_bounds) { ASSERT_TRUE(cpuinfo_initialize()); @@ -1210,6 +1240,7 @@ TEST(L2_CACHE, valid_processors) { cpuinfo_deinitialize(); } +#ifndef CPUINFO_ARCH_PPC64 TEST(L2_CACHE, consistent_processors) { ASSERT_TRUE(cpuinfo_initialize()); for (uint32_t i = 0; i < cpuinfo_get_l2_caches_count(); i++) { @@ -1225,6 +1256,7 @@ TEST(L2_CACHE, consistent_processors) { } cpuinfo_deinitialize(); } +#endif TEST(L3_CACHES_COUNT, within_bounds) { ASSERT_TRUE(cpuinfo_initialize()); @@ -1357,6 +1389,7 @@ TEST(L3_CACHE, valid_processors) { cpuinfo_deinitialize(); } +#ifndef CPUINFO_ARCH_PPC64 TEST(L3_CACHE, consistent_processors) { ASSERT_TRUE(cpuinfo_initialize()); for (uint32_t i = 0; i < cpuinfo_get_l3_caches_count(); i++) { @@ -1372,6 +1405,7 @@ TEST(L3_CACHE, consistent_processors) { } cpuinfo_deinitialize(); } +#endif TEST(L4_CACHES_COUNT, within_bounds) { ASSERT_TRUE(cpuinfo_initialize()); diff --git a/test/name/power-features.cc b/test/name/power-features.cc new file mode 100644 index 00000000..8f039de0 --- /dev/null +++ b/test/name/power-features.cc @@ -0,0 +1,41 @@ +#include +#include +#include + +extern "C" bool CPUINFO_ABI cpuinfo_initialize(void); +extern "C" inline bool cpuinfo_has_powerpc_vsx(void); +extern "C" inline bool cpuinfo_has_powerpc_htm(void); +extern "C" inline bool cpuinfo_has_powerpc_mma(void); + +#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_1 UINT32_C(0x00040000) //To check if the architecture is Power10 +#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_00 UINT32_C(0x00800000) //To check if the architecture is Power9 +#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_2_07 UINT32_C(0x80000000) //To check if the architecture is Power8 + +TEST(PowerFeatures, Power){ + if (!cpuinfo_initialize()) { + fprintf(stderr, "failed to initialize CPU information\n"); + exit(1); + } + #if CPUINFO_MOCK + int a = mock_hwcap; + #else + int a = (uint32_t) getauxval(AT_HWCAP); + int b = (uint32_t) getauxval(AT_HWCAP2); + #endif //CPUINFO_MOCK // + + #if (b & CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_1) + EXPECT_EQ(0, cpuinfo_has_powerpc_htm()); + EXPECT_EQ(1, cpuinfo_has_powerpc_mma()); + EXPECT_EQ(1, cpuinfo_has_powerpc_vsx()); + #elif (b & CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_00) + EXPECT_EQ(0, cpuinfo_has_powerpc_htm()); + EXPECT_EQ(0, cpuinfo_has_powerpc_mma()); + EXPECT_EQ(1, cpuinfo_has_powerpc_vsx()); + #elif (b & CPUINFO_POWERPC_LINUX_FEATURE_ARCH_2_07) + EXPECT_EQ(1, cpuinfo_has_powerpc_htm()); + EXPECT_EQ(0, cpuinfo_has_powerpc_mma()); + EXPECT_EQ(1, cpuinfo_has_powerpc_vsx()); + #endif + + cpuinfo_deinitialize(); +} diff --git a/tools/cpu-info.c b/tools/cpu-info.c index b896b270..b8adfb9e 100644 --- a/tools/cpu-info.c +++ b/tools/cpu-info.c @@ -274,6 +274,20 @@ static const char* uarch_to_string(enum cpuinfo_uarch uarch) { return "ThunderX2"; case cpuinfo_uarch_pj4: return "PJ4"; + case cpuinfo_uarch_power7: + return "POWER7"; + case cpuinfo_uarch_power7p: + return "POWER7+"; + case cpuinfo_uarch_power8: + return "POWER8"; + case cpuinfo_uarch_power8e: + return "POWER8E"; + case cpuinfo_uarch_power8nvl: + return "POWER8NVL"; + case cpuinfo_uarch_power9: + return "POWER9"; + case cpuinfo_uarch_power10: + return "POWER10"; case cpuinfo_uarch_brahma_b15: return "Brahma B15"; case cpuinfo_uarch_brahma_b53: @@ -317,6 +331,8 @@ int main(int argc, char** argv) { printf("Cores:\n"); for (uint32_t i = 0; i < cpuinfo_get_cores_count(); i++) { const struct cpuinfo_core* core = cpuinfo_get_core(i); + if (core->disabled) + continue; if (core->processor_count == 1) { printf("\t%" PRIu32 ": 1 processor (%" PRIu32 ")", i, core->processor_start); } else { From 71c527b49dc94ac1de707848be0fce1d14d5d797 Mon Sep 17 00:00:00 2001 From: "bmahi496@linux.ibm.com" Date: Thu, 5 Dec 2024 00:22:50 -0500 Subject: [PATCH 2/2] Fixing Indentation --- include/cpuinfo.h | 24 +- src/linux/processors.c | 22 +- src/powerpc/api.h | 13 +- src/powerpc/cache.c | 64 ++- src/powerpc/linux/api.h | 72 +-- src/powerpc/linux/cpuinfo.c | 126 +++--- src/powerpc/linux/init.c | 806 ++++++++++++++++++---------------- src/powerpc/linux/ppc64-hw.c | 37 +- src/powerpc/linux/ppc64-isa.c | 14 +- test/name/power-features.cc | 46 +- 10 files changed, 627 insertions(+), 597 deletions(-) diff --git a/include/cpuinfo.h b/include/cpuinfo.h index 15625e02..8e5decc1 100644 --- a/include/cpuinfo.h +++ b/include/cpuinfo.h @@ -603,19 +603,19 @@ enum cpuinfo_uarch { cpuinfo_uarch_taishan_v110 = 0x00C00100, /** IBM POWER 7. */ - cpuinfo_uarch_power7 = 0x00D00100, + cpuinfo_uarch_power7 = 0x00D00100, /** IBM POWER 7p. */ - cpuinfo_uarch_power7p = 0x00D00101, + cpuinfo_uarch_power7p = 0x00D00101, /** IBM POWER 8. */ - cpuinfo_uarch_power8 = 0x00D00200, + cpuinfo_uarch_power8 = 0x00D00200, /** IBM POWER8E. */ - cpuinfo_uarch_power8e = 0x00D00201, + cpuinfo_uarch_power8e = 0x00D00201, /** IBM POWER8NVL */ cpuinfo_uarch_power8nvl = 0x00D00202, /** IBM POWER 9. */ - cpuinfo_uarch_power9 = 0x00D00303, + cpuinfo_uarch_power9 = 0x00D00303, /** IBM POWER 10. */ - cpuinfo_uarch_power10 = 0x00D00400, + cpuinfo_uarch_power10 = 0x00D00400, }; struct cpuinfo_processor { @@ -2245,13 +2245,13 @@ static inline bool cpuinfo_has_riscv_v(void) { } #if CPUINFO_ARCH_PPC64 - struct cpuinfo_powerpc_isa { - bool vsx; - bool htm; - bool mma; - }; +struct cpuinfo_powerpc_isa { + bool vsx; + bool htm; + bool mma; +}; - extern struct cpuinfo_powerpc_isa cpuinfo_isa; +extern struct cpuinfo_powerpc_isa cpuinfo_isa; #endif static inline bool cpuinfo_has_powerpc_vsx(void) { diff --git a/src/linux/processors.c b/src/linux/processors.c index 6c9622da..114ada79 100644 --- a/src/linux/processors.c +++ b/src/linux/processors.c @@ -285,29 +285,31 @@ bool cpuinfo_linux_get_processor_package_id(uint32_t processor, uint32_t package bool cpuinfo_linux_get_processor_online_status(uint32_t processor, uint32_t* online_status_ptr) { char processor_online_filename[PROCESSOR_ONLINE_FILENAME_SIZE]; - const int chars_formatted = - snprintf(processor_online_filename, PROCESSOR_ONLINE_FILENAME_SIZE, PROCESSOR_ONLINE_FILENAME_FORMAT, processor); - if ((unsigned int)chars_formatted >= PROCESSOR_ONLINE_FILENAME_SIZE) { + const int chars_formatted = snprintf( + processor_online_filename, PROCESSOR_ONLINE_FILENAME_SIZE, PROCESSOR_ONLINE_FILENAME_FORMAT, processor); + if ((unsigned int)chars_formatted >= PROCESSOR_ONLINE_FILENAME_SIZE) { cpuinfo_log_warning("failed to format filename for online status of processor %" PRIu32, processor); return 0; } uint32_t online_status; - if (cpuinfo_linux_parse_small_file(processor_online_filename, PROCESSOR_ONLINE_FILESIZE, uint32_parser, &online_status)) { + if (cpuinfo_linux_parse_small_file( + processor_online_filename, PROCESSOR_ONLINE_FILESIZE, uint32_parser, &online_status)) { cpuinfo_log_debug( "parsed online status value of %" PRIu32 " for logical processor %" PRIu32 " from %s", online_status, processor, processor_online_filename); - *online_status_ptr = online_status; - return true; + *online_status_ptr = online_status; + return true; } else { - cpuinfo_log_info( - "failed to parse online status for processor %" PRIu32 " from %s", processor, processor_online_filename); - return false; + cpuinfo_log_info( + "failed to parse online status for processor %" PRIu32 " from %s", + processor, + processor_online_filename); + return false; } } - 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; const uint32_t processor_list_last = processor_list_end - 1; diff --git a/src/powerpc/api.h b/src/powerpc/api.h index 79464f68..34ab330b 100644 --- a/src/powerpc/api.h +++ b/src/powerpc/api.h @@ -5,17 +5,14 @@ #include -enum cpuinfo_powerpc_chipset_vendor { - cpuinfo_powerpc_chipset_vendor_unknown = 0, - cpuinfo_powerpc_chipset_vendor_ibm -}; +enum cpuinfo_powerpc_chipset_vendor { cpuinfo_powerpc_chipset_vendor_unknown = 0, cpuinfo_powerpc_chipset_vendor_ibm }; void cpuinfo_powerpc_decode_vendor_uarch( uint32_t vendor_id, enum cpuinfo_vendor vendor[restrict static 1], enum cpuinfo_uarch uarch[restrict static 1]); void cpuinfo_powerpc_decode_cache( - struct cpuinfo_cache l1i[restrict static 1], - struct cpuinfo_cache l1d[restrict static 1], - struct cpuinfo_cache l2[restrict static 1], - struct cpuinfo_cache l3[restrict static 1]); + struct cpuinfo_cache l1i[restrict static 1], + struct cpuinfo_cache l1d[restrict static 1], + struct cpuinfo_cache l2[restrict static 1], + struct cpuinfo_cache l3[restrict static 1]); diff --git a/src/powerpc/cache.c b/src/powerpc/cache.c index 0ddd5331..ace751a4 100644 --- a/src/powerpc/cache.c +++ b/src/powerpc/cache.c @@ -1,10 +1,10 @@ -#include #include +#include #include #include -#include #include +#include #include #include @@ -12,17 +12,15 @@ #include #include -#define NUM_CACHE 4 -#define BUF_SIZE 128 +#define NUM_CACHE 4 +#define BUF_SIZE 128 -int path_exist(const char *path) -{ +int path_exist(const char* path) { return (access(path, F_OK) == 0); } -void read_str(char *result, size_t len, const char *path) -{ - FILE *f; +void read_str(char* result, size_t len, const char* path) { + FILE* f; f = fopen(path, "r"); @@ -34,21 +32,20 @@ void read_str(char *result, size_t len, const char *path) result[len - 1] = '\0'; } -void decode_cache_features(uint32_t cache_features[], unsigned long geometry) -{ +void decode_cache_features(uint32_t cache_features[], unsigned long geometry) { cache_features[1] = (geometry >> 16) & 0xffff; cache_features[2] = geometry & 0xffff; - /* If associativity = 65535 it means upper limit of 16 bit of AT_L%d_CACHEGEOMETRY has reached so we calculate it from, size, sets and line_size*/ - if(cache_features[1] == 65535) - cache_features[1] = cache_features[0]/(cache_features[2]*cache_features[3]); + /* If associativity = 65535 it means upper limit of 16 bit of AT_L%d_CACHEGEOMETRY has reached so we calculate + * it from, size, sets and line_size*/ + if (cache_features[1] == 65535) + cache_features[1] = cache_features[0] / (cache_features[2] * cache_features[3]); } void cpuinfo_powerpc_decode_cache( struct cpuinfo_cache l1i[restrict static 1], struct cpuinfo_cache l1d[restrict static 1], struct cpuinfo_cache l2[restrict static 1], - struct cpuinfo_cache l3[restrict static 1]) -{ + struct cpuinfo_cache l3[restrict static 1]) { char buf[BUF_SIZE]; char result[BUF_SIZE]; uint32_t cache_features[NUM_CACHE]; @@ -56,11 +53,10 @@ void cpuinfo_powerpc_decode_cache( uint32_t size = 0, sets = 0; size_t len; - for(i = 0; i < NUM_CACHE; i++) { - - sprintf (buf, "/sys/devices/system/cpu/cpu%d/cache/index%d/number_of_sets", i); - if(path_exist(buf)) - read_str(result, sizeof(result), buf); + for (i = 0; i < NUM_CACHE; i++) { + sprintf(buf, "/sys/devices/system/cpu/cpu%d/cache/index%d/number_of_sets", i); + if (path_exist(buf)) + read_str(result, sizeof(result), buf); sets = atoi(result); cache_features[3] = sets; @@ -73,8 +69,8 @@ void cpuinfo_powerpc_decode_cache( size = getauxval(AT_L1D_CACHESIZE); cache_features[0] = size; decode_cache_features(cache_features, geometry); - sets = size/(cache_features[1] * cache_features[2]); - *l1d = (struct cpuinfo_cache) { + sets = size / (cache_features[1] * cache_features[2]); + *l1d = (struct cpuinfo_cache){ .size = size, .associativity = cache_features[1], .line_size = cache_features[2], @@ -88,44 +84,40 @@ void cpuinfo_powerpc_decode_cache( size = getauxval(AT_L1I_CACHESIZE); cache_features[0] = size; decode_cache_features(cache_features, geometry); - sets = size/(cache_features[1] * cache_features[2]); - *l1i = (struct cpuinfo_cache) { + sets = size / (cache_features[1] * cache_features[2]); + *l1i = (struct cpuinfo_cache){ .size = size, .associativity = cache_features[1], .line_size = cache_features[2], .sets = sets, - .partitions = 1 - }; + .partitions = 1}; break; case 2: geometry = getauxval(AT_L2_CACHEGEOMETRY); size = getauxval(AT_L2_CACHESIZE); cache_features[0] = size; decode_cache_features(cache_features, geometry); - sets = size/(cache_features[1] * cache_features[2]); - *l2 = (struct cpuinfo_cache) { + sets = size / (cache_features[1] * cache_features[2]); + *l2 = (struct cpuinfo_cache){ .size = size, .associativity = cache_features[1], .line_size = cache_features[2], .sets = sets, - .partitions = 1 - }; + .partitions = 1}; break; case 3: geometry = getauxval(AT_L3_CACHEGEOMETRY); size = getauxval(AT_L3_CACHESIZE); cache_features[0] = size; decode_cache_features(cache_features, geometry); - sets = size/(cache_features[1] * cache_features[2]); - *l3 = (struct cpuinfo_cache) { + sets = size / (cache_features[1] * cache_features[2]); + *l3 = (struct cpuinfo_cache){ .size = size, .associativity = cache_features[1], .line_size = cache_features[2], .sets = sets, - .partitions = 1 - }; + .partitions = 1}; break; - } } } diff --git a/src/powerpc/linux/api.h b/src/powerpc/linux/api.h index 739a519d..a03aaa73 100644 --- a/src/powerpc/linux/api.h +++ b/src/powerpc/linux/api.h @@ -7,47 +7,47 @@ #include #include -#include #include +#include /* No hard limit in the kernel, maximum length observed on non-rogue kernels is 64 */ #define CPUINFO_HARDWARE_VALUE_MAX 64 /* from /arch/powerpc/kernel/cputable.c */ -#define CPUINFO_POWERPC_LINUX_FEATURE_64 UINT32_C(0x40000000) -#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_ALTIVEC UINT32_C(0x10000000) -#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_FPU UINT32_C(0x08000000) -#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_MMU UINT32_C(0x04000000) -#define CPUINFO_POWERPC_LINUX_FEATURE_NO_TB UINT32_C(0x00100000) -#define CPUINFO_POWERPC_LINUX_FEATURE_SMT UINT32_C(0x00004000) -#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_DFP UINT32_C(0x00000400) -#define CPUINFO_POWERPC_LINUX_FEATURE_POWER6_EXT UINT32_C(0x00000200) -#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_VSX UINT32_C(0x00000080) -#define CPUINFO_POWERPC_LINUX_FEATURE_TRUE_LE UINT32_C(0x00000002) +#define CPUINFO_POWERPC_LINUX_FEATURE_64 UINT32_C(0x40000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_ALTIVEC UINT32_C(0x10000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_FPU UINT32_C(0x08000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_MMU UINT32_C(0x04000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_NO_TB UINT32_C(0x00100000) +#define CPUINFO_POWERPC_LINUX_FEATURE_SMT UINT32_C(0x00004000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_DFP UINT32_C(0x00000400) +#define CPUINFO_POWERPC_LINUX_FEATURE_POWER6_EXT UINT32_C(0x00000200) +#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_VSX UINT32_C(0x00000080) +#define CPUINFO_POWERPC_LINUX_FEATURE_TRUE_LE UINT32_C(0x00000002) #define CPUINFO_POWERPC_LINUX_FEATURE_PPC_LE UINT32_C(0x00000001 -#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_2_07 UINT32_C(0x80000000) -#define CPUINFO_POWERPC_LINUX_FEATURE_HTM UINT32_C(0x40000000) -#define CPUINFO_POWERPC_LINUX_FEATURE_DSCR UINT32_C(0x20000000) -#define CPUINFO_POWERPC_LINUX_FEATURE_EBB UINT32_C(0x10000000) -#define CPUINFO_POWERPC_LINUX_FEATURE_ISEL UINT32_C(0x08000000) -#define CPUINFO_POWERPC_LINUX_FEATURE_TAR UINT32_C(0x04000000) -#define CPUINFO_POWERPC_LINUX_FEATURE_VEC_CRYPTO UINT32_C(0x02000000) -#define CPUINFO_POWERPC_LINUX_FEATURE_HTM_NOSC UINT32_C(0x01000000) -#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_00 UINT32_C(0x00800000) -#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_IEEE128 UINT32_C(0x00400000) -#define CPUINFO_POWERPC_LINUX_FEATURE_DARN UINT32_C(0x00200000) -#define CPUINFO_POWERPC_LINUX_FEATURE_SCV UINT32_C(0x00100000) -#define CPUINFO_POWERPC_LINUX_FEATURE_HTM_NO_SUSPEND UINT32_C(0x00080000) -#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_1 UINT32_C(0x00040000) -#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_MMA UINT32_C(0x00020000) +#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_2_07 UINT32_C(0x80000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HTM UINT32_C(0x40000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_DSCR UINT32_C(0x20000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_EBB UINT32_C(0x10000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_ISEL UINT32_C(0x08000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_TAR UINT32_C(0x04000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_VEC_CRYPTO UINT32_C(0x02000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HTM_NOSC UINT32_C(0x01000000) +#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_00 UINT32_C(0x00800000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_IEEE128 UINT32_C(0x00400000) +#define CPUINFO_POWERPC_LINUX_FEATURE_DARN UINT32_C(0x00200000) +#define CPUINFO_POWERPC_LINUX_FEATURE_SCV UINT32_C(0x00100000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HTM_NO_SUSPEND UINT32_C(0x00080000) +#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_1 UINT32_C(0x00040000) +#define CPUINFO_POWERPC_LINUX_FEATURE_HAS_MMA UINT32_C(0x00020000) #define CPUINFO_POWERPC_LINUX_VALID_ARCHITECTURE UINT32_C(0x00010000) -#define CPUINFO_POWERPC_LINUX_VALID_IMPLEMENTER UINT32_C(0x00020000) -#define CPUINFO_POWERPC_LINUX_VALID_REVISION UINT32_C(0x00040000) -#define CPUINFO_POWERPC_LINUX_VALID_PROCESSOR UINT32_C(0x00080000) -#define CPUINFO_POWERPC_LINUX_VALID_VERSION UINT32_C(0x00100000) -#define CPUINFO_POWERPC_LINUX_VALID_FEATURES UINT32_C(0x00200000) -#define CPUINFO_POWERPC_LINUX_VALID_PVR UINT32_C(0x001F0000) +#define CPUINFO_POWERPC_LINUX_VALID_IMPLEMENTER UINT32_C(0x00020000) +#define CPUINFO_POWERPC_LINUX_VALID_REVISION UINT32_C(0x00040000) +#define CPUINFO_POWERPC_LINUX_VALID_PROCESSOR UINT32_C(0x00080000) +#define CPUINFO_POWERPC_LINUX_VALID_VERSION UINT32_C(0x00100000) +#define CPUINFO_POWERPC_LINUX_VALID_FEATURES UINT32_C(0x00200000) +#define CPUINFO_POWERPC_LINUX_VALID_PVR UINT32_C(0x001F0000) /** * Definition of a powerpc64 Linux processor. It is composed of the base processor @@ -99,10 +99,14 @@ struct cpuinfo_powerpc_linux_processor { * @param[isa] - Reference to cpuinfo_powerpc_isa structure to populate. */ void cpuinfo_powerpc_linux_hwcap_from_getauxval(uint32_t isa_feature[]); -CPUINFO_INTERNAL void cpuinfo_ppc64_linux_decode_isa_from_hwcap(uint32_t hwcap, uint32_t hwcap2, struct cpuinfo_powerpc_isa isa[restrict static 1]); +CPUINFO_INTERNAL void cpuinfo_ppc64_linux_decode_isa_from_hwcap( + uint32_t hwcap, + uint32_t hwcap2, + struct cpuinfo_powerpc_isa isa[restrict static 1]); bool cpuinfo_powerpc_linux_parse_proc_cpuinfo( - uint32_t max_processors_count,struct cpuinfo_powerpc_linux_processor processors[restrict static max_processors_count]); + uint32_t max_processors_count, + struct cpuinfo_powerpc_linux_processor processors[restrict static max_processors_count]); /* Used to determine which uarch is associated with the current thread. */ extern CPUINFO_INTERNAL const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map; diff --git a/src/powerpc/linux/cpuinfo.c b/src/powerpc/linux/cpuinfo.c index 3e31ceee..1567f9f8 100644 --- a/src/powerpc/linux/cpuinfo.c +++ b/src/powerpc/linux/cpuinfo.c @@ -1,13 +1,13 @@ #include +#include #include #include #include -#include #include +#include #include #include -#include /* * Size, in chars, of the on-stack buffer used for parsing lines of /proc/cpuinfo. @@ -22,11 +22,8 @@ struct proc_cpuinfo_parser_state { struct cpuinfo_powerpc_linux_processor dummy_processor; }; -static uint32_t parse_processor_number( - const char* processor_start, - const char* processor_end) -{ - const size_t processor_length = (size_t) (processor_end - processor_start); +static uint32_t parse_processor_number(const char* processor_start, const char* processor_end) { + const size_t processor_length = (size_t)(processor_end - processor_start); if (processor_length == 0) { cpuinfo_log_warning("Processor number in /proc/cpuinfo is ignored: string is empty"); @@ -35,10 +32,12 @@ static uint32_t parse_processor_number( uint32_t processor_number = 0; 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) { - cpuinfo_log_warning("non-decimal suffix %.*s in /proc/cpuinfo processor number is ignored", - (int) (processor_end - digit_ptr), digit_ptr); + cpuinfo_log_warning( + "non-decimal suffix %.*s in /proc/cpuinfo processor number is ignored", + (int)(processor_end - digit_ptr), + digit_ptr); break; } @@ -49,10 +48,9 @@ static uint32_t parse_processor_number( } static void parse_cpu_architecture( - const char *cpu_architecture_start, - const char *cpu_architecture_end, - struct cpuinfo_powerpc_linux_processor *processor) -{ + const char* cpu_architecture_start, + const char* cpu_architecture_end, + struct cpuinfo_powerpc_linux_processor* processor) { const size_t cpu_arch_name_length = 5; if (!memcmp(cpu_architecture_start, "POWER", cpu_arch_name_length)) { @@ -66,7 +64,7 @@ static void parse_cpu_architecture( const char* cpu_arch_ptr = cpu_architecture_start + cpu_arch_name_length; uint32_t arch_version = 0; for (; cpu_arch_ptr != cpu_architecture_end; cpu_arch_ptr++) { - const uint32_t digit = (uint32_t) (*cpu_arch_ptr - '0'); + const uint32_t digit = (uint32_t)(*cpu_arch_ptr - '0'); if (digit >= 10) { break; @@ -74,7 +72,7 @@ static void parse_cpu_architecture( arch_version = arch_version * 10 + digit; } - switch(arch_version) { + switch (arch_version) { case 7: /* POWER7 */ if (*cpu_arch_ptr == ' ') { processor->core.uarch = cpuinfo_uarch_power7; @@ -113,25 +111,28 @@ static void parse_cpu_architecture( break; default: unsupported: - cpuinfo_log_warning("CPU architecture %.*s in /proc/cpuinfo is ignored due to a unsupported architecture version", - (int) (cpu_architecture_end - cpu_architecture_start), cpu_architecture_start); + cpuinfo_log_warning( + "CPU architecture %.*s in /proc/cpuinfo is ignored due to a unsupported architecture version", + (int)(cpu_architecture_end - cpu_architecture_start), + cpu_architecture_start); } processor->flags |= CPUINFO_POWERPC_LINUX_VALID_PROCESSOR; processor->core.disabled = false; } else { - cpuinfo_log_warning("processor %.*s in /proc/cpuinfo is ignored due not a Power processor", - (int) (cpu_architecture_end - cpu_architecture_start), cpu_architecture_start); + cpuinfo_log_warning( + "processor %.*s in /proc/cpuinfo is ignored due not a Power processor", + (int)(cpu_architecture_end - cpu_architecture_start), + cpu_architecture_start); } } static void parse_cpu_pvr( - const char *cpu_revision_start, - const char *cpu_revision_end, - struct cpuinfo_powerpc_linux_processor *processor) -{ + const char* cpu_revision_start, + const char* cpu_revision_end, + struct cpuinfo_powerpc_linux_processor* processor) { const char* cpu_rev_ptr = cpu_revision_start; uint16_t revision = 0; - uint16_t version = 0; + uint16_t version = 0; processor->core.pvr = 0x0; for (; cpu_rev_ptr != cpu_revision_end; cpu_rev_ptr++) { @@ -151,12 +152,14 @@ static void parse_cpu_pvr( cpu_rev_ptr++; break; } - uint32_t digit = (uint32_t) (*cpu_rev_ptr - '0'); + uint32_t digit = (uint32_t)(*cpu_rev_ptr - '0'); if (digit >= 10) { digit = digit - 0x27; if ((digit < 10) || (digit > 15)) { - cpuinfo_log_warning("cpu revision %.*s in /proc/cpuinfo is ignored due non number", - (int) (cpu_revision_end - cpu_revision_start), cpu_revision_start); + cpuinfo_log_warning( + "cpu revision %.*s in /proc/cpuinfo is ignored due non number", + (int)(cpu_revision_end - cpu_revision_start), + cpu_revision_start); return; } } @@ -164,18 +167,20 @@ static void parse_cpu_pvr( } /* Parse version. */ - uint16_t version = 0; + uint16_t version = 0; for (; cpu_rev_ptr != cpu_revision_end; cpu_rev_ptr++) { if (*cpu_rev_ptr == ')') { cpu_rev_ptr++; break; } - uint32_t digit = (uint32_t) (*cpu_rev_ptr - '0'); + uint32_t digit = (uint32_t)(*cpu_rev_ptr - '0'); if (digit >= 10) { digit = digit - 0x27; if ((digit < 10) || (digit > 15)) { - cpuinfo_log_warning("cpu version %.*s in /proc/cpuinfo is ignored due non number", - (int) (cpu_revision_end - cpu_revision_start), cpu_revision_start); + cpuinfo_log_warning( + "cpu version %.*s in /proc/cpuinfo is ignored due non number", + (int)(cpu_revision_end - cpu_revision_start), + cpu_revision_start); return; } } @@ -185,8 +190,10 @@ static void parse_cpu_pvr( processor->core.pvr |= (revision << 16); processor->core.pvr |= version; } else { - cpuinfo_log_warning("cpu revision %.*s in /proc/cpuinfo is ignored due non PVR information", - (int) (cpu_revision_end - cpu_revision_start), cpu_revision_start); + cpuinfo_log_warning( + "cpu revision %.*s in /proc/cpuinfo is ignored due non PVR information", + (int)(cpu_revision_end - cpu_revision_start), + cpu_revision_start); } } @@ -194,9 +201,7 @@ static bool parse_line( const char* line_start, const char* line_end, struct proc_cpuinfo_parser_state state[restrict static 1], - uint64_t line_number) -{ - + uint64_t line_number) { /* Empty line. Skip. */ if (line_start == line_end) { return true; @@ -212,10 +217,12 @@ static bool parse_line( /* Skip line if no ':' separator was found. */ if (separator == line_end) { - cpuinfo_log_warning("Line %.*s in /proc/cpuinfo is ignored: key/value separator ':' not found", - (int) (line_end - line_start), line_start); + cpuinfo_log_warning( + "Line %.*s in /proc/cpuinfo is ignored: key/value separator ':' not found", + (int)(line_end - line_start), + line_start); return true; - } + } /* Skip trailing spaces in key part. */ const char* key_end = separator; @@ -227,8 +234,10 @@ static bool parse_line( /* Skip line if key contains nothing but spaces. */ if (key_end == line_start) { - cpuinfo_log_warning("Line %.*s in /proc/cpuinfo is ignored: key contains only spaces", - (int) (line_end - line_start), line_start); + cpuinfo_log_warning( + "Line %.*s in /proc/cpuinfo is ignored: key contains only spaces", + (int)(line_end - line_start), + line_start); return true; } @@ -242,8 +251,10 @@ static bool parse_line( /* Value part contains nothing but spaces. Skip line. */ if (value_start == line_end) { - cpuinfo_log_warning("Line %.*s in /proc/cpuinfo is ignored: value contains only spaces", - (int) (line_end - line_start), line_start); + cpuinfo_log_warning( + "Line %.*s in /proc/cpuinfo is ignored: value contains only spaces", + (int)(line_end - line_start), + line_start); } /* Skip trailing spaces in value part (if any) */ @@ -255,10 +266,10 @@ static bool parse_line( } /* Declarations to return */ - 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; struct cpuinfo_powerpc_linux_processor* processors = state->processors; - struct cpuinfo_powerpc_linux_processor* processor = &state->dummy_processor; + struct cpuinfo_powerpc_linux_processor* processor = &state->dummy_processor; if (processor_index < max_processors_count) { processor = &processors[processor_index]; @@ -277,15 +288,15 @@ static bool parse_line( case 5: if (memcmp(line_start, "clock", key_length) == 0) { parse_cpu_architecture(value_start, value_end, processor); - } else { - goto unknown; - } + } else { + goto unknown; + } case 7: if (memcmp(line_start, "machine", key_length) == 0) { parse_cpu_architecture(value_start, value_end, processor); - } else { - goto unknown; - } + } else { + goto unknown; + } case 8: if (memcmp(line_start, "revision", key_length) == 0) { parse_cpu_pvr(value_start, value_end, processor); @@ -302,24 +313,23 @@ static bool parse_line( break; default: 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; } bool cpuinfo_powerpc_linux_parse_proc_cpuinfo( uint32_t max_processors_count, - struct cpuinfo_powerpc_linux_processor processors[restrict static max_processors_count]) -{ + struct cpuinfo_powerpc_linux_processor processors[restrict static max_processors_count]) { struct proc_cpuinfo_parser_state state = { .processor_index = 0, .max_processors_count = max_processors_count, .processors = processors, }; - for (int i = 0 ; i < max_processors_count; i++) + for (int i = 0; i < max_processors_count; i++) processors[i].core.disabled = true; - return cpuinfo_linux_parse_multiline_file("/proc/cpuinfo", BUFFER_SIZE, - (cpuinfo_line_callback) parse_line, &state); + return cpuinfo_linux_parse_multiline_file( + "/proc/cpuinfo", BUFFER_SIZE, (cpuinfo_line_callback)parse_line, &state); } diff --git a/src/powerpc/linux/init.c b/src/powerpc/linux/init.c index 54260a8a..0553f066 100644 --- a/src/powerpc/linux/init.c +++ b/src/powerpc/linux/init.c @@ -234,7 +234,7 @@ void cpuinfo_powerpc_linux_init(void) { const struct cpuinfo_processor** linux_cpu_to_processor_map = NULL; const struct cpuinfo_core** linux_cpu_to_core_map = NULL; uint32_t* linux_cpu_to_uarch_index_map = NULL; - struct cpuinfo_cache* l1i = NULL; + struct cpuinfo_cache* l1i = NULL; struct cpuinfo_cache* l1d = NULL; struct cpuinfo_cache* l2 = NULL; struct cpuinfo_cache* l3 = NULL; @@ -287,9 +287,7 @@ void cpuinfo_powerpc_linux_init(void) { goto cleanup; } - - - uint32_t online_status=1; + uint32_t online_status = 1; /* Populate processor information. */ for (size_t processor = 0; processor < max_processor_id; processor++) { if (!bitmask_all(powerpc_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { @@ -300,41 +298,40 @@ void cpuinfo_powerpc_linux_init(void) { /* Populate core information. */ for (size_t processor = 0; processor < max_processor_id; processor++) { - if (!bitmask_all(powerpc_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { - continue; - } - online_status = 1; - cpuinfo_linux_get_processor_online_status(processor, &online_status); - - if (online_status == 1) { - /* Populate processor start and count information. - */ - if (!cpuinfo_linux_detect_core_cpus( - max_processor_id, - processor, - (cpuinfo_siblings_callback)core_cpus_parser, - powerpc_linux_processors)) { - cpuinfo_log_error("failed to detect core cpus for processor %zu.", processor); - goto cleanup; - } - - /* Populate core ID information. */ - if (cpuinfo_linux_get_processor_core_id(processor, &powerpc_linux_processors[processor].core.core_id)) { - powerpc_linux_processors[processor].flags |= CPUINFO_LINUX_FLAG_CORE_ID; - } - + if (!bitmask_all(powerpc_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { + continue; + } + online_status = 1; + cpuinfo_linux_get_processor_online_status(processor, &online_status); - /** - * Populate the vendor and uarch of this core from /proc/cpuinfo. - */ - if (!cpuinfo_powerpc_linux_parse_proc_cpuinfo(max_processor_id,powerpc_linux_processors)) { - cpuinfo_log_error("failed to parse processor information from /proc/cpuinfo"); - return; - } - } + if (online_status == 1) { + /* Populate processor start and count information. + */ + if (!cpuinfo_linux_detect_core_cpus( + max_processor_id, + processor, + (cpuinfo_siblings_callback)core_cpus_parser, + powerpc_linux_processors)) { + cpuinfo_log_error("failed to detect core cpus for processor %zu.", processor); + goto cleanup; + } + + /* Populate core ID information. */ + if (cpuinfo_linux_get_processor_core_id( + processor, &powerpc_linux_processors[processor].core.core_id)) { + powerpc_linux_processors[processor].flags |= CPUINFO_LINUX_FLAG_CORE_ID; + } + + /** + * Populate the vendor and uarch of this core from /proc/cpuinfo. + */ + if (!cpuinfo_powerpc_linux_parse_proc_cpuinfo(max_processor_id, powerpc_linux_processors)) { + cpuinfo_log_error("failed to parse processor information from /proc/cpuinfo"); + return; + } + } } - /* Populate cluster information. * power10, the number of cores and the clusters are same. */ @@ -342,30 +339,32 @@ void cpuinfo_powerpc_linux_init(void) { if (!bitmask_all(powerpc_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { continue; } - online_status= 1; + online_status = 1; cpuinfo_linux_get_processor_online_status(processor, &online_status); if (online_status == 1) { - if (!cpuinfo_linux_detect_core_cpus( - max_processor_id, - processor, - (cpuinfo_siblings_callback)cluster_cpus_parser, - powerpc_linux_processors)) { - cpuinfo_log_warning("failed to detect cluster cpus for processor %zu.", processor); - goto cleanup; + if (!cpuinfo_linux_detect_core_cpus( + max_processor_id, + processor, + (cpuinfo_siblings_callback)cluster_cpus_parser, + powerpc_linux_processors)) { + cpuinfo_log_warning("failed to detect cluster cpus for processor %zu.", processor); + goto cleanup; + } + + /** + * Populate the vendor, uarch and frequency of this cluster from + * this core. + */ + powerpc_linux_processors[processor].cluster.vendor = + powerpc_linux_processors[processor].core.vendor; + powerpc_linux_processors[processor].cluster.uarch = + powerpc_linux_processors[processor].core.uarch; + powerpc_linux_processors[processor].cluster.frequency = + powerpc_linux_processors[processor].core.frequency; + powerpc_linux_processors[processor].cluster.pvr = powerpc_linux_processors[processor].core.pvr; } - - /** - * Populate the vendor, uarch and frequency of this cluster from - * this core. - */ - powerpc_linux_processors[processor].cluster.vendor = powerpc_linux_processors[processor].core.vendor; - powerpc_linux_processors[processor].cluster.uarch = powerpc_linux_processors[processor].core.uarch; - powerpc_linux_processors[processor].cluster.frequency = powerpc_linux_processors[processor].core.frequency; - powerpc_linux_processors[processor].cluster.pvr = powerpc_linux_processors[processor].core.pvr; } - } - /* Populate package information. */ for (size_t processor = 0; processor < max_processor_id; processor++) { @@ -374,14 +373,14 @@ void cpuinfo_powerpc_linux_init(void) { } cpuinfo_linux_get_processor_online_status(processor, &online_status); if (online_status == 1) { - if (!cpuinfo_linux_detect_package_cpus( - max_processor_id, - processor, - (cpuinfo_siblings_callback)package_cpus_parser, - powerpc_linux_processors)) { - cpuinfo_log_warning("failed to detect package cpus for processor %zu.", processor); - goto cleanup; - } + if (!cpuinfo_linux_detect_package_cpus( + max_processor_id, + processor, + (cpuinfo_siblings_callback)package_cpus_parser, + powerpc_linux_processors)) { + cpuinfo_log_warning("failed to detect package cpus for processor %zu.", processor); + goto cleanup; + } } } @@ -389,32 +388,45 @@ void cpuinfo_powerpc_linux_init(void) { for (size_t processor = 1; processor < max_processor_id; processor++) { cpuinfo_linux_get_processor_online_status(processor, &online_status); if (online_status == 1) { - if (powerpc_linux_processors[processor].package_leader_id == processor) - { - powerpc_linux_processors[processor].core.core_id = 0; - powerpc_linux_processors[processor].cluster.cluster_id = 0; - powerpc_linux_processors[processor].cluster.core_start = powerpc_linux_processors[online_processor].cluster.core_start + powerpc_linux_processors[online_processor].cluster.core_count; - powerpc_linux_processors[processor].package.core_start = powerpc_linux_processors[online_processor].package.core_start + powerpc_linux_processors[online_processor].package.core_count; - powerpc_linux_processors[processor].package.cluster_start = powerpc_linux_processors[online_processor].package.cluster_start + powerpc_linux_processors[online_processor].package.cluster_count; - } - else { - if (powerpc_linux_processors[processor].core.processor_start != powerpc_linux_processors[online_processor].core.processor_start) - powerpc_linux_processors[processor].core.core_id = powerpc_linux_processors[online_processor].core.core_id + 1; - else - powerpc_linux_processors[processor].core.core_id = powerpc_linux_processors[online_processor].core.core_id; - if (powerpc_linux_processors[processor].cluster.processor_start != powerpc_linux_processors[online_processor].cluster.processor_start) { - powerpc_linux_processors[processor].cluster.core_start = powerpc_linux_processors[online_processor].cluster.core_start + powerpc_linux_processors[online_processor].cluster.core_count; - powerpc_linux_processors[processor].cluster.cluster_id = powerpc_linux_processors[online_processor].cluster.cluster_id + 1; - } - else - { - powerpc_linux_processors[processor].cluster.core_start = powerpc_linux_processors[online_processor].cluster.core_start; - powerpc_linux_processors[processor].cluster.cluster_id = powerpc_linux_processors[online_processor].cluster.cluster_id; - } - powerpc_linux_processors[processor].package.core_start = powerpc_linux_processors[online_processor].package.core_start; - powerpc_linux_processors[processor].package.cluster_start = powerpc_linux_processors[online_processor].package.cluster_start; - } - online_processor = processor; + if (powerpc_linux_processors[processor].package_leader_id == processor) { + powerpc_linux_processors[processor].core.core_id = 0; + powerpc_linux_processors[processor].cluster.cluster_id = 0; + powerpc_linux_processors[processor].cluster.core_start = + powerpc_linux_processors[online_processor].cluster.core_start + + powerpc_linux_processors[online_processor].cluster.core_count; + powerpc_linux_processors[processor].package.core_start = + powerpc_linux_processors[online_processor].package.core_start + + powerpc_linux_processors[online_processor].package.core_count; + powerpc_linux_processors[processor].package.cluster_start = + powerpc_linux_processors[online_processor].package.cluster_start + + powerpc_linux_processors[online_processor].package.cluster_count; + } else { + if (powerpc_linux_processors[processor].core.processor_start != + powerpc_linux_processors[online_processor].core.processor_start) + powerpc_linux_processors[processor].core.core_id = + powerpc_linux_processors[online_processor].core.core_id + 1; + else + powerpc_linux_processors[processor].core.core_id = + powerpc_linux_processors[online_processor].core.core_id; + if (powerpc_linux_processors[processor].cluster.processor_start != + powerpc_linux_processors[online_processor].cluster.processor_start) { + powerpc_linux_processors[processor].cluster.core_start = + powerpc_linux_processors[online_processor].cluster.core_start + + powerpc_linux_processors[online_processor].cluster.core_count; + powerpc_linux_processors[processor].cluster.cluster_id = + powerpc_linux_processors[online_processor].cluster.cluster_id + 1; + } else { + powerpc_linux_processors[processor].cluster.core_start = + powerpc_linux_processors[online_processor].cluster.core_start; + powerpc_linux_processors[processor].cluster.cluster_id = + powerpc_linux_processors[online_processor].cluster.cluster_id; + } + powerpc_linux_processors[processor].package.core_start = + powerpc_linux_processors[online_processor].package.core_start; + powerpc_linux_processors[processor].package.cluster_start = + powerpc_linux_processors[online_processor].package.cluster_start; + } + online_processor = processor; } } @@ -443,82 +455,87 @@ void cpuinfo_powerpc_linux_init(void) { } cpuinfo_linux_get_processor_online_status(processor, &online_status); if (online_status == 1) { - /** - * All comparisons to the leader id values MUST be done against - * the 'linux_id' as opposed to 'processor'. The sort function - * above no longer allows us to make the assumption that these - * two values are the same. - */ - uint32_t linux_id = powerpc_linux_processors[processor].processor.linux_id; - - valid_processors_count++; - if (powerpc_linux_processors[processor].core_leader_id == linux_id) { - valid_cores_count++; - } - if (powerpc_linux_processors[processor].cluster_leader_id == linux_id) { - valid_clusters_count++; - } - if (powerpc_linux_processors[processor].package_leader_id == linux_id) { - valid_packages_count++; - } - /** - * As we've sorted by micro-architecture, when the uarch differs - * between two entries, a unique uarch has been observed. - */ - if (last_uarch != powerpc_linux_processors[processor].core.uarch || valid_uarchs_count == 0) { - valid_uarchs_count++; - last_uarch = powerpc_linux_processors[processor].core.uarch; - } + /** + * All comparisons to the leader id values MUST be done against + * the 'linux_id' as opposed to 'processor'. The sort function + * above no longer allows us to make the assumption that these + * two values are the same. + */ + uint32_t linux_id = powerpc_linux_processors[processor].processor.linux_id; + + valid_processors_count++; + if (powerpc_linux_processors[processor].core_leader_id == linux_id) { + valid_cores_count++; + } + if (powerpc_linux_processors[processor].cluster_leader_id == linux_id) { + valid_clusters_count++; + } + if (powerpc_linux_processors[processor].package_leader_id == linux_id) { + valid_packages_count++; + } + /** + * As we've sorted by micro-architecture, when the uarch differs + * between two entries, a unique uarch has been observed. + */ + if (last_uarch != powerpc_linux_processors[processor].core.uarch || valid_uarchs_count == 0) { + valid_uarchs_count++; + last_uarch = powerpc_linux_processors[processor].core.uarch; + } } } smt = valid_processors_count / (valid_cores_count); /* 1 cache instance for consecutive 4 even/odd threads, if core has 8 threads then 2 cache instances */ - cache_count = max_processor_id/4; + cache_count = max_processor_id / 4; /* Asiigning linux_id's for all the online processors in consecutive manner. * This is only needed in other than SMT8 modes. */ - if (smt !=8) - { - size_t online_id = 0; - for (size_t processor = 0; processor < max_processor_id; processor++) { - cpuinfo_linux_get_processor_online_status(processor, &online_status); - if (online_status == 1) { - if (online_id != powerpc_linux_processors[processor].processor.linux_id) - { - if (powerpc_linux_processors[processor].core_leader_id == powerpc_linux_processors[processor].processor.linux_id) { - powerpc_linux_processors[processor].core_leader_id = online_id; - powerpc_linux_processors[processor].core.processor_start = powerpc_linux_processors[processor].core_leader_id; + if (smt != 8) { + size_t online_id = 0; + for (size_t processor = 0; processor < max_processor_id; processor++) { + cpuinfo_linux_get_processor_online_status(processor, &online_status); + if (online_status == 1) { + if (online_id != powerpc_linux_processors[processor].processor.linux_id) { + if (powerpc_linux_processors[processor].core_leader_id == + powerpc_linux_processors[processor].processor.linux_id) { + powerpc_linux_processors[processor].core_leader_id = online_id; + powerpc_linux_processors[processor].core.processor_start = + powerpc_linux_processors[processor].core_leader_id; + } + if (powerpc_linux_processors[processor].cluster_leader_id == + powerpc_linux_processors[processor].processor.linux_id) { + powerpc_linux_processors[processor].cluster_leader_id = online_id; + powerpc_linux_processors[processor].cluster.processor_start = + powerpc_linux_processors[processor].cluster_leader_id; + } + if (powerpc_linux_processors[processor].package_leader_id == + powerpc_linux_processors[processor].processor.linux_id) { + powerpc_linux_processors[processor].package_leader_id = online_id; + powerpc_linux_processors[processor].package.processor_start = + powerpc_linux_processors[processor].package_leader_id; + } + powerpc_linux_processors[processor].processor.linux_id = online_id; + } + online_id++; + } } - if (powerpc_linux_processors[processor].cluster_leader_id == powerpc_linux_processors[processor].processor.linux_id) { - powerpc_linux_processors[processor].cluster_leader_id = online_id; - powerpc_linux_processors[processor].cluster.processor_start = powerpc_linux_processors[processor].cluster_leader_id; - } - if (powerpc_linux_processors[processor].package_leader_id == powerpc_linux_processors[processor].processor.linux_id) { - powerpc_linux_processors[processor].package_leader_id = online_id; - powerpc_linux_processors[processor].package.processor_start = powerpc_linux_processors[processor].package_leader_id; - } - powerpc_linux_processors[processor].processor.linux_id = online_id; - } - online_id++; - } - } } if (cache_count != 0) { - l1i = calloc(cache_count, sizeof(struct cpuinfo_cache)); - l1d = calloc(cache_count, sizeof(struct cpuinfo_cache)); - l2 = calloc(cache_count, sizeof(struct cpuinfo_cache)); - l3 = calloc(cache_count, sizeof(struct cpuinfo_cache)); - if (l3 == NULL) { - cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L3 caches", - cache_count * sizeof(struct cpuinfo_cache), cache_count); - goto cleanup; - } + l1i = calloc(cache_count, sizeof(struct cpuinfo_cache)); + l1d = calloc(cache_count, sizeof(struct cpuinfo_cache)); + l2 = calloc(cache_count, sizeof(struct cpuinfo_cache)); + l3 = calloc(cache_count, sizeof(struct cpuinfo_cache)); + if (l3 == NULL) { + cpuinfo_log_error( + "failed to allocate %zu bytes for descriptions of %" PRIu32 " L3 caches", + cache_count * sizeof(struct cpuinfo_cache), + cache_count); + goto cleanup; + } } - /* Allocate and populate final public ABI structures. */ processors = calloc(valid_processors_count, sizeof(struct cpuinfo_processor)); if (processors == NULL) { @@ -600,245 +617,254 @@ void cpuinfo_powerpc_linux_init(void) { size_t valid_uarchs_index = 0; last_uarch = cpuinfo_uarch_unknown; for (size_t processor = 0; processor < max_processor_id; processor++) { - cpuinfo_linux_get_processor_online_status(processor, &online_status); - if (online_status == 1) { - if (!bitmask_all(powerpc_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { - continue; - } - /** - * All comparisons to the leader id values MUST be done against - * the 'linux_id' as opposed to 'processor'. The sort function - * above no longer allows us to make the assumption that these - * two values are the same. + cpuinfo_linux_get_processor_online_status(processor, &online_status); + if (online_status == 1) { + if (!bitmask_all(powerpc_linux_processors[processor].flags, CPUINFO_LINUX_FLAG_VALID)) { + continue; + } + /** + * All comparisons to the leader id values MUST be done against + * the 'linux_id' as opposed to 'processor'. The sort function + * above no longer allows us to make the assumption that these + * two values are the same. + */ + uint32_t linux_id = powerpc_linux_processors[processor].processor.linux_id; + + /* Create uarch entry if this uarch has not been seen before. */ + if (last_uarch != powerpc_linux_processors[processor].core.uarch || valid_uarchs_index == 0) { + uarchs[valid_uarchs_index++].uarch = powerpc_linux_processors[processor].core.uarch; + last_uarch = powerpc_linux_processors[processor].core.uarch; + } + + /* Copy cpuinfo_processor information. */ + memcpy(&processors[valid_processors_index++], + &powerpc_linux_processors[processor].processor, + sizeof(struct cpuinfo_processor)); + + /* Update uarch processor count. */ + uarchs[valid_uarchs_index - 1].processor_count++; + + /* Copy cpuinfo_core information, if this is the leader. */ + if (powerpc_linux_processors[processor].core_leader_id == linux_id) { + memcpy(&cores[valid_cores_index++], + &powerpc_linux_processors[processor].core, + sizeof(struct cpuinfo_core)); + /* Update uarch core count. */ + uarchs[valid_uarchs_index - 1].core_count++; + } + + /* Copy cpuinfo_cluster information, if this is the leader. */ + if (powerpc_linux_processors[processor].cluster_leader_id == linux_id) { + memcpy(&clusters[valid_clusters_index++], + &powerpc_linux_processors[processor].cluster, + sizeof(struct cpuinfo_cluster)); + } + + /* Copy cpuinfo_package information, if this is the leader. */ + if (powerpc_linux_processors[processor].package_leader_id == linux_id) { + memcpy(&packages[valid_packages_index++], + &powerpc_linux_processors[processor].package, + sizeof(struct cpuinfo_package)); + } + + /* Commit pointers on the final structures. */ + processors[valid_processors_index - 1].core = &cores[valid_cores_index - 1]; + processors[valid_processors_index - 1].cluster = &clusters[valid_clusters_index - 1]; + processors[valid_processors_index - 1].package = &packages[valid_packages_index - 1]; + + cores[valid_cores_index - 1].cluster = &clusters[valid_clusters_index - 1]; + cores[valid_cores_index - 1].package = &packages[valid_packages_index - 1]; + + clusters[valid_clusters_index - 1].package = &packages[valid_packages_index - 1]; + + linux_cpu_to_processor_map[linux_id] = &processors[valid_processors_index - 1]; + linux_cpu_to_core_map[linux_id] = &cores[valid_cores_index - 1]; + linux_cpu_to_uarch_index_map[linux_id] = valid_uarchs_index - 1; + } + } + + uint32_t l1i_index = UINT32_MAX, l1d_index = UINT32_MAX, l2_index = UINT32_MAX, l3_index = UINT32_MAX; + for (uint32_t i = 0; i < valid_processors_count; i++) { + struct cpuinfo_cache temp_l1i = {0}, temp_l1d = {0}, temp_l2 = {0}, temp_l3 = {0}; + cpuinfo_powerpc_decode_cache(&temp_l1i, &temp_l1d, &temp_l2, &temp_l3); + /* Power10, even threads of a core shares 1 cache and odd threads of a core shares the another one + * test/init.cc is expecting continuous therads for cache instance so disabled the check in init.cc for + * PPC64. */ - uint32_t linux_id = powerpc_linux_processors[processor].processor.linux_id; - - /* Create uarch entry if this uarch has not been seen before. */ - if (last_uarch != powerpc_linux_processors[processor].core.uarch || valid_uarchs_index == 0) { - uarchs[valid_uarchs_index++].uarch = powerpc_linux_processors[processor].core.uarch; - last_uarch = powerpc_linux_processors[processor].core.uarch; - } - - /* Copy cpuinfo_processor information. */ - memcpy(&processors[valid_processors_index++], - &powerpc_linux_processors[processor].processor, - sizeof(struct cpuinfo_processor)); - - /* Update uarch processor count. */ - uarchs[valid_uarchs_index - 1].processor_count++; - - /* Copy cpuinfo_core information, if this is the leader. */ - if (powerpc_linux_processors[processor].core_leader_id == linux_id) { - memcpy(&cores[valid_cores_index++], - &powerpc_linux_processors[processor].core, - sizeof(struct cpuinfo_core)); - /* Update uarch core count. */ - uarchs[valid_uarchs_index - 1].core_count++; - } - - /* Copy cpuinfo_cluster information, if this is the leader. */ - if (powerpc_linux_processors[processor].cluster_leader_id == linux_id) { - memcpy(&clusters[valid_clusters_index++], - &powerpc_linux_processors[processor].cluster, - sizeof(struct cpuinfo_cluster)); - } - - /* Copy cpuinfo_package information, if this is the leader. */ - if (powerpc_linux_processors[processor].package_leader_id == linux_id) { - memcpy(&packages[valid_packages_index++], - &powerpc_linux_processors[processor].package, - sizeof(struct cpuinfo_package)); + if (temp_l1i.size != 0) { + if ((smt == 8 && (i % 8 == 0)) || (smt == 4 && (i % 4 == 0)) || (smt == 2 && (i % 2 == 0)) || + (smt == 1)) { + /* new cache */ + l1i[++l1i_index] = (struct cpuinfo_cache){ + .size = temp_l1i.size, + .associativity = temp_l1i.associativity, + .sets = temp_l1i.sets, + .partitions = 1, + .line_size = temp_l1i.line_size, + .flags = temp_l1i.flags, + .processor_start = i, + .processor_count = 1, + }; + } else if ( + (smt == 8 && (i % 8 == 1)) || (smt == 4 && (i % 4 == 1)) || + (smt == 2 && (i % 2 == 1))) { + l1i[++l1i_index] = (struct cpuinfo_cache){ + .size = temp_l1i.size, + .associativity = temp_l1i.associativity, + .sets = temp_l1i.sets, + .partitions = 1, + .line_size = temp_l1i.line_size, + .flags = temp_l1i.flags, + .processor_start = i, + .processor_count = 1, + }; + } else { + /* another processor sharing the same cache. */ + if (i % 2 == 0) + l1i[l1i_index - 1].processor_count += 1; + else + l1i[l1i_index].processor_count += 1; + } + if ((smt == 8 && (i % 8 == 0)) || (smt == 4 && (i % 4 == 0)) || (smt == 2 && (i % 2 == 0)) || + (smt == 1)) + processors[i].cache.l1i = &l1i[l1i_index]; + else if (i % 2 == 0) + processors[i].cache.l1i = &l1i[l1i_index - 1]; + else + processors[i].cache.l1i = &l1i[l1i_index]; + } + + if (temp_l1d.size != 0) { + if ((smt == 8 && (i % 8 == 0)) || (smt == 4 && (i % 4 == 0)) || (smt == 2 && (i % 2 == 0)) || + (smt == 1)) { + /* new cache */ + l1d[++l1d_index] = (struct cpuinfo_cache){ + .size = temp_l1d.size, + .associativity = temp_l1d.associativity, + .sets = temp_l1d.sets, + .partitions = 1, + .line_size = temp_l1d.line_size, + .flags = temp_l1d.flags, + .processor_start = i, + .processor_count = 1, + }; + } else if ( + (smt == 8 && (i % 8 == 1)) || (smt == 4 && (i % 4 == 1)) || + (smt == 2 && (i % 2 == 1))) { + /* new cache */ + l1d[++l1d_index] = (struct cpuinfo_cache){ + .size = temp_l1d.size, + .associativity = temp_l1d.associativity, + .sets = temp_l1d.sets, + .partitions = 1, + .line_size = temp_l1d.line_size, + .flags = temp_l1d.flags, + .processor_start = i, + .processor_count = 1, + }; + } else { + /* another processor sharing the same cache. */ + if (i % 2 == 0) + l1d[l1d_index - 1].processor_count += 1; + else + l1d[l1d_index].processor_count += 1; + } + if ((smt == 8 && (i % 8 == 0)) || (smt == 4 && (i % 4 == 0)) || (smt == 2 && (i % 2 == 0)) || + (smt == 1)) + processors[i].cache.l1d = &l1d[l1d_index]; + else if (i % 2 == 0) + processors[i].cache.l1d = &l1d[l1d_index - 1]; + else + processors[i].cache.l1d = &l1d[l1d_index]; + } + + if (temp_l2.size != 0) { + if ((smt == 8 && (i % 8 == 0)) || (smt == 4 && (i % 4 == 0)) || (smt == 2 && (i % 2 == 0)) || + (smt == 1)) { + /* new cache */ + l2[++l2_index] = (struct cpuinfo_cache){ + .size = temp_l2.size, + .associativity = temp_l2.associativity, + .sets = temp_l2.sets, + .partitions = 1, + .line_size = temp_l2.line_size, + .flags = temp_l2.flags, + .processor_start = i, + .processor_count = 1, + }; + } else if ( + (smt == 8 && (i % 8 == 1)) || (smt == 4 && (i % 4 == 1)) || + (smt == 2 && (i % 2 == 1))) { + l2[++l2_index] = (struct cpuinfo_cache){ + .size = temp_l2.size, + .associativity = temp_l2.associativity, + .sets = temp_l2.sets, + .partitions = 1, + .line_size = temp_l2.line_size, + .flags = temp_l2.flags, + .processor_start = i, + .processor_count = 1, + }; + } else { + /* another processor sharing the same cache. */ + if (i % 2 == 0) + l2[l2_index - 1].processor_count += 1; + else + l2[l2_index].processor_count += 1; + } + if ((smt == 8 && (i % 8 == 0)) || (smt == 4 && (i % 4 == 0)) || (smt == 2 && (i % 2 == 0)) || + (smt == 1)) + processors[i].cache.l2 = &l2[l2_index]; + else if (i % 2 == 0) + processors[i].cache.l2 = &l2[l2_index - 1]; + else + processors[i].cache.l2 = &l2[l2_index]; + } + + if (temp_l3.size != 0) { + if ((smt == 8 && (i % 8 == 0)) || (smt == 4 && (i % 4 == 0)) || (smt == 2 && (i % 2 == 0)) || + (smt == 1)) { + /* new cache */ + l3[++l3_index] = (struct cpuinfo_cache){ + .size = temp_l3.size, + .associativity = temp_l3.associativity, + .sets = temp_l3.sets, + .partitions = 1, + .line_size = temp_l3.line_size, + .flags = temp_l3.flags, + .processor_start = i, + .processor_count = 1, + }; + } else if ( + (smt == 8 && (i % 8 == 1)) || (smt == 4 && (i % 4 == 1)) || + (smt == 2 && (i % 2 == 1))) { + l3[++l3_index] = (struct cpuinfo_cache){ + .size = temp_l3.size, + .associativity = temp_l3.associativity, + .sets = temp_l3.sets, + .partitions = 1, + .line_size = temp_l3.line_size, + .flags = temp_l3.flags, + .processor_start = i, + .processor_count = 1, + }; + } else { + /* another processor sharing the same cache. */ + if (i % 2 == 0) + l3[l3_index - 1].processor_count += 1; + else + l3[l3_index].processor_count += 1; + } + if ((smt == 8 && (i % 8 == 0)) || (smt == 4 && (i % 4 == 0)) || (smt == 2 && (i % 2 == 0)) || + (smt == 1)) + processors[i].cache.l3 = &l3[l3_index]; + else if (i % 2 == 0) + processors[i].cache.l3 = &l3[l3_index - 1]; + else + processors[i].cache.l3 = &l3[l3_index]; } - - /* Commit pointers on the final structures. */ - processors[valid_processors_index - 1].core = &cores[valid_cores_index - 1]; - processors[valid_processors_index - 1].cluster = &clusters[valid_clusters_index - 1]; - processors[valid_processors_index - 1].package = &packages[valid_packages_index - 1]; - - cores[valid_cores_index - 1].cluster = &clusters[valid_clusters_index - 1]; - cores[valid_cores_index - 1].package = &packages[valid_packages_index - 1]; - - clusters[valid_clusters_index - 1].package = &packages[valid_packages_index - 1]; - - linux_cpu_to_processor_map[linux_id] = &processors[valid_processors_index - 1]; - linux_cpu_to_core_map[linux_id] = &cores[valid_cores_index - 1]; - linux_cpu_to_uarch_index_map[linux_id] = valid_uarchs_index - 1; - } } - uint32_t l1i_index = UINT32_MAX, l1d_index = UINT32_MAX, l2_index = UINT32_MAX, l3_index = UINT32_MAX; - for (uint32_t i = 0; i < valid_processors_count; i++) { - struct cpuinfo_cache temp_l1i = {0}, temp_l1d = {0}, temp_l2 = {0}, temp_l3 = {0}; - cpuinfo_powerpc_decode_cache(&temp_l1i, &temp_l1d, &temp_l2, &temp_l3); - /* Power10, even threads of a core shares 1 cache and odd threads of a core shares the another one - * test/init.cc is expecting continuous therads for cache instance so disabled the check in init.cc for PPC64. - */ - if (temp_l1i.size != 0) { - if ((smt == 8 && (i%8 == 0)) || (smt == 4 && (i%4 == 0)) || (smt == 2 && (i%2 == 0)) || (smt ==1)) { - /* new cache */ - l1i[++l1i_index] = (struct cpuinfo_cache){ - .size = temp_l1i.size, - .associativity = temp_l1i.associativity, - .sets = temp_l1i.sets, - .partitions = 1, - .line_size = temp_l1i.line_size, - .flags = temp_l1i.flags, - .processor_start = i, - .processor_count = 1, - }; - } - else if ((smt == 8 && (i%8 == 1)) || (smt == 4 && (i%4 == 1)) || (smt == 2 && (i%2 == 1))) { - l1i[++l1i_index] = (struct cpuinfo_cache){ - .size = temp_l1i.size, - .associativity = temp_l1i.associativity, - .sets = temp_l1i.sets, - .partitions = 1, - .line_size = temp_l1i.line_size, - .flags = temp_l1i.flags, - .processor_start = i, - .processor_count = 1, - }; - } - else { - /* another processor sharing the same cache. */ - if (i%2 == 0) - l1i[l1i_index-1].processor_count += 1; - else - l1i[l1i_index].processor_count += 1; - } - if ((smt == 8 && (i%8 == 0)) || (smt == 4 && (i%4 == 0)) || (smt == 2 && (i%2 == 0)) || (smt ==1)) - processors[i].cache.l1i = &l1i[l1i_index]; - else if (i%2 == 0) - processors[i].cache.l1i = &l1i[l1i_index-1]; - else - processors[i].cache.l1i = &l1i[l1i_index]; - } - - if (temp_l1d.size != 0) { - if ((smt == 8 && (i%8 == 0)) || (smt == 4 && (i%4 == 0)) || (smt == 2 && (i%2 == 0)) || (smt ==1)) { - /* new cache */ - l1d[++l1d_index] = (struct cpuinfo_cache){ - .size = temp_l1d.size, - .associativity = temp_l1d.associativity, - .sets = temp_l1d.sets, - .partitions = 1, - .line_size = temp_l1d.line_size, - .flags = temp_l1d.flags, - .processor_start = i, - .processor_count = 1, - }; - } - else if ((smt == 8 && (i%8 == 1)) || (smt == 4 && (i%4 == 1)) || (smt == 2 && (i%2 == 1))) { - /* new cache */ - l1d[++l1d_index] = (struct cpuinfo_cache){ - .size = temp_l1d.size, - .associativity = temp_l1d.associativity, - .sets = temp_l1d.sets, - .partitions = 1, - .line_size = temp_l1d.line_size, - .flags = temp_l1d.flags, - .processor_start = i, - .processor_count = 1, - }; - } - else { - /* another processor sharing the same cache. */ - if (i%2 == 0) - l1d[l1d_index-1].processor_count += 1; - else - l1d[l1d_index].processor_count += 1; - } - if ((smt == 8 && (i%8 == 0)) || (smt == 4 && (i%4 == 0)) || (smt == 2 && (i%2 == 0)) || (smt ==1)) - processors[i].cache.l1d = &l1d[l1d_index]; - else if (i%2 == 0) - processors[i].cache.l1d = &l1d[l1d_index-1]; - else - processors[i].cache.l1d = &l1d[l1d_index]; - } - - if (temp_l2.size != 0) { - if ((smt == 8 && (i%8 == 0)) || (smt == 4 && (i%4 == 0)) || (smt == 2 && (i%2 == 0)) || (smt ==1)) { - /* new cache */ - l2[++l2_index] = (struct cpuinfo_cache){ - .size = temp_l2.size, - .associativity = temp_l2.associativity, - .sets = temp_l2.sets, - .partitions = 1, - .line_size = temp_l2.line_size, - .flags = temp_l2.flags, - .processor_start = i, - .processor_count = 1, - }; - } - else if ((smt == 8 && (i%8 == 1)) || (smt == 4 && (i%4 == 1)) || (smt == 2 && (i%2 == 1))) { - l2[++l2_index] = (struct cpuinfo_cache){ - .size = temp_l2.size, - .associativity = temp_l2.associativity, - .sets = temp_l2.sets, - .partitions = 1, - .line_size = temp_l2.line_size, - .flags = temp_l2.flags, - .processor_start = i, - .processor_count = 1, - }; - } - else { - /* another processor sharing the same cache. */ - if (i%2 == 0) - l2[l2_index-1].processor_count += 1; - else - l2[l2_index].processor_count += 1; - } - if ((smt == 8 && (i%8 == 0)) || (smt == 4 && (i%4 == 0)) || (smt == 2 && (i%2 == 0)) || (smt ==1)) - processors[i].cache.l2 = &l2[l2_index]; - else if (i%2 == 0) - processors[i].cache.l2 = &l2[l2_index-1]; - else - processors[i].cache.l2 = &l2[l2_index]; - } - - if (temp_l3.size != 0) { - if ((smt == 8 && (i%8 == 0)) || (smt == 4 && (i%4 == 0)) || (smt == 2 && (i%2 == 0)) || (smt ==1)) { - /* new cache */ - l3[++l3_index] = (struct cpuinfo_cache){ - .size = temp_l3.size, - .associativity = temp_l3.associativity, - .sets = temp_l3.sets, - .partitions = 1, - .line_size = temp_l3.line_size, - .flags = temp_l3.flags, - .processor_start = i, - .processor_count = 1, - }; - } - else if ((smt == 8 && (i%8 == 1)) || (smt == 4 && (i%4 == 1)) || (smt == 2 && (i%2 == 1))) { - l3[++l3_index] = (struct cpuinfo_cache){ - .size = temp_l3.size, - .associativity = temp_l3.associativity, - .sets = temp_l3.sets, - .partitions = 1, - .line_size = temp_l3.line_size, - .flags = temp_l3.flags, - .processor_start = i, - .processor_count = 1, - }; - } - else { - /* another processor sharing the same cache. */ - if (i%2 ==0 ) - l3[l3_index-1].processor_count += 1; - else - l3[l3_index].processor_count += 1; - } - if ((smt == 8 && (i%8 == 0)) || (smt == 4 && (i%4 == 0)) || (smt == 2 && (i%2 == 0)) || (smt ==1)) - processors[i].cache.l3 = &l3[l3_index]; - else if ( i%2 == 0) - processors[i].cache.l3 = &l3[l3_index-1]; - else - processors[i].cache.l3 = &l3[l3_index]; - } - } - cpuinfo_processors = processors; cpuinfo_processors_count = valid_processors_count; cpuinfo_cores = cores; @@ -852,8 +878,8 @@ void cpuinfo_powerpc_linux_init(void) { 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_2] = l2; + cpuinfo_cache[cpuinfo_cache_level_3] = l3; cpuinfo_linux_cpu_max = valid_processors_count; cpuinfo_linux_cpu_to_processor_map = linux_cpu_to_processor_map; cpuinfo_linux_cpu_to_core_map = linux_cpu_to_core_map; @@ -861,8 +887,8 @@ void cpuinfo_powerpc_linux_init(void) { cpuinfo_cache_count[cpuinfo_cache_level_1i] = cache_count; cpuinfo_cache_count[cpuinfo_cache_level_1d] = cache_count; - cpuinfo_cache_count[cpuinfo_cache_level_2] = cache_count; - cpuinfo_cache_count[cpuinfo_cache_level_3] = cache_count; + cpuinfo_cache_count[cpuinfo_cache_level_2] = cache_count; + cpuinfo_cache_count[cpuinfo_cache_level_3] = cache_count; __sync_synchronize(); cpuinfo_is_initialized = true; @@ -877,7 +903,7 @@ void cpuinfo_powerpc_linux_init(void) { linux_cpu_to_processor_map = NULL; linux_cpu_to_core_map = NULL; linux_cpu_to_uarch_index_map = NULL; - l1i = l1d = l2 = l3 = NULL; + l1i = l1d = l2 = l3 = NULL; cleanup: free(powerpc_linux_processors); free(processors); diff --git a/src/powerpc/linux/ppc64-hw.c b/src/powerpc/linux/ppc64-hw.c index c7f1ddb1..6d7140f0 100644 --- a/src/powerpc/linux/ppc64-hw.c +++ b/src/powerpc/linux/ppc64-hw.c @@ -1,36 +1,35 @@ #include -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include #if CPUINFO_MOCK - #include +#include #endif #include -#include #include +#include #include #if CPUINFO_MOCK - static uint32_t mock_hwcap = 0; - void cpuinfo_set_hwcap(uint32_t hwcap) { - mock_hwcap = hwcap; - } +static uint32_t mock_hwcap = 0; +void cpuinfo_set_hwcap(uint32_t hwcap) { + mock_hwcap = hwcap; +} #endif -void cpuinfo_powerpc_linux_hwcap_from_getauxval(uint32_t isa_feature[]) -{ - #if CPUINFO_MOCK - isa_feature[0] = mock_hwcap; - #else - isa_feature[0] = (uint32_t) getauxval(AT_HWCAP); - isa_feature[1] = (uint32_t) getauxval(AT_HWCAP2); - #endif +void cpuinfo_powerpc_linux_hwcap_from_getauxval(uint32_t isa_feature[]) { +#if CPUINFO_MOCK + isa_feature[0] = mock_hwcap; +#else + isa_feature[0] = (uint32_t)getauxval(AT_HWCAP); + isa_feature[1] = (uint32_t)getauxval(AT_HWCAP2); +#endif } diff --git a/src/powerpc/linux/ppc64-isa.c b/src/powerpc/linux/ppc64-isa.c index c86ff244..ecc30723 100644 --- a/src/powerpc/linux/ppc64-isa.c +++ b/src/powerpc/linux/ppc64-isa.c @@ -1,13 +1,13 @@ #include -#include #include +#include void cpuinfo_ppc64_linux_decode_isa_from_hwcap( - uint32_t features, uint32_t features2, - struct cpuinfo_powerpc_isa isa[restrict static 1]) -{ - isa->vsx=!!(features & CPUINFO_POWERPC_LINUX_FEATURE_HAS_VSX); - isa->htm=!!(features2 & CPUINFO_POWERPC_LINUX_FEATURE_HTM); - isa->mma=!!(features2 & CPUINFO_POWERPC_LINUX_FEATURE_HAS_MMA); + uint32_t features, + uint32_t features2, + struct cpuinfo_powerpc_isa isa[restrict static 1]) { + isa->vsx = !!(features & CPUINFO_POWERPC_LINUX_FEATURE_HAS_VSX); + isa->htm = !!(features2 & CPUINFO_POWERPC_LINUX_FEATURE_HTM); + isa->mma = !!(features2 & CPUINFO_POWERPC_LINUX_FEATURE_HAS_MMA); } diff --git a/test/name/power-features.cc b/test/name/power-features.cc index 8f039de0..4cb23a41 100644 --- a/test/name/power-features.cc +++ b/test/name/power-features.cc @@ -7,35 +7,35 @@ extern "C" inline bool cpuinfo_has_powerpc_vsx(void); extern "C" inline bool cpuinfo_has_powerpc_htm(void); extern "C" inline bool cpuinfo_has_powerpc_mma(void); -#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_1 UINT32_C(0x00040000) //To check if the architecture is Power10 -#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_00 UINT32_C(0x00800000) //To check if the architecture is Power9 -#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_2_07 UINT32_C(0x80000000) //To check if the architecture is Power8 +#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_1 UINT32_C(0x00040000) // To check if the architecture is Power10 +#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_00 UINT32_C(0x00800000) // To check if the architecture is Power9 +#define CPUINFO_POWERPC_LINUX_FEATURE_ARCH_2_07 UINT32_C(0x80000000) // To check if the architecture is Power8 -TEST(PowerFeatures, Power){ +TEST(PowerFeatures, Power) { if (!cpuinfo_initialize()) { fprintf(stderr, "failed to initialize CPU information\n"); exit(1); } - #if CPUINFO_MOCK - int a = mock_hwcap; - #else - int a = (uint32_t) getauxval(AT_HWCAP); - int b = (uint32_t) getauxval(AT_HWCAP2); - #endif //CPUINFO_MOCK // +#if CPUINFO_MOCK + int a = mock_hwcap; +#else + int a = (uint32_t)getauxval(AT_HWCAP); + int b = (uint32_t)getauxval(AT_HWCAP2); +#endif // CPUINFO_MOCK // - #if (b & CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_1) - EXPECT_EQ(0, cpuinfo_has_powerpc_htm()); - EXPECT_EQ(1, cpuinfo_has_powerpc_mma()); - EXPECT_EQ(1, cpuinfo_has_powerpc_vsx()); - #elif (b & CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_00) - EXPECT_EQ(0, cpuinfo_has_powerpc_htm()); - EXPECT_EQ(0, cpuinfo_has_powerpc_mma()); - EXPECT_EQ(1, cpuinfo_has_powerpc_vsx()); - #elif (b & CPUINFO_POWERPC_LINUX_FEATURE_ARCH_2_07) - EXPECT_EQ(1, cpuinfo_has_powerpc_htm()); - EXPECT_EQ(0, cpuinfo_has_powerpc_mma()); - EXPECT_EQ(1, cpuinfo_has_powerpc_vsx()); - #endif +#if (b & CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_1) + EXPECT_EQ(0, cpuinfo_has_powerpc_htm()); + EXPECT_EQ(1, cpuinfo_has_powerpc_mma()); + EXPECT_EQ(1, cpuinfo_has_powerpc_vsx()); +#elif (b & CPUINFO_POWERPC_LINUX_FEATURE_ARCH_3_00) + EXPECT_EQ(0, cpuinfo_has_powerpc_htm()); + EXPECT_EQ(0, cpuinfo_has_powerpc_mma()); + EXPECT_EQ(1, cpuinfo_has_powerpc_vsx()); +#elif (b & CPUINFO_POWERPC_LINUX_FEATURE_ARCH_2_07) + EXPECT_EQ(1, cpuinfo_has_powerpc_htm()); + EXPECT_EQ(0, cpuinfo_has_powerpc_mma()); + EXPECT_EQ(1, cpuinfo_has_powerpc_vsx()); +#endif cpuinfo_deinitialize(); }