| #include <stdint.h> |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <string.h> |
|
|
| #include <cpuinfo.h> |
| #include <arm/linux/api.h> |
| #if defined(__ANDROID__) |
| #include <arm/android/api.h> |
| #endif |
| #include <arm/api.h> |
| #include <arm/midr.h> |
| #include <linux/api.h> |
| #include <cpuinfo/internal-api.h> |
| #include <cpuinfo/log.h> |
|
|
| static inline bool bitmask_all(uint32_t bitfield, uint32_t mask) { |
| return (bitfield & mask) == mask; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| bool cpuinfo_arm_linux_detect_core_clusters_by_heuristic( |
| uint32_t usable_processors, |
| uint32_t max_processors, |
| struct cpuinfo_arm_linux_processor processors[restrict static max_processors]) |
| { |
| uint32_t cluster_processors[3]; |
| switch (usable_processors) { |
| case 10: |
| cluster_processors[0] = 4; |
| cluster_processors[1] = 4; |
| cluster_processors[2] = 2; |
| break; |
| case 8: |
| cluster_processors[0] = 4; |
| cluster_processors[1] = 4; |
| break; |
| case 6: |
| cluster_processors[0] = 4; |
| cluster_processors[1] = 2; |
| break; |
| #if defined(__ANDROID__) && CPUINFO_ARCH_ARM |
| case 5: |
| |
| |
| |
| |
| cluster_processors[0] = 4; |
| cluster_processors[1] = 1; |
| break; |
| #endif |
| default: |
| return false; |
| } |
|
|
| |
| |
| |
| |
| |
|
|
| uint32_t cluster = 0; |
| uint32_t expected_cluster_processors = 0; |
| uint32_t cluster_start, cluster_flags, cluster_midr, cluster_max_frequency, cluster_min_frequency; |
| bool expected_cluster_exists; |
| for (uint32_t i = 0; i < max_processors; i++) { |
| if (bitmask_all(processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) { |
| if (expected_cluster_processors == 0) { |
| |
|
|
| expected_cluster_exists = !!(processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER); |
| if (expected_cluster_exists) { |
| if (processors[i].package_leader_id != i) { |
| cpuinfo_log_debug( |
| "heuristic detection of core clusters failed: " |
| "processor %"PRIu32" is expected to start a new cluster #%"PRIu32" with %"PRIu32" cores, " |
| "but system siblings lists reported it as a sibling of processor %"PRIu32, |
| i, cluster, cluster_processors[cluster], processors[i].package_leader_id); |
| return false; |
| } |
| } else { |
| cluster_flags = 0; |
| } |
|
|
| cluster_start = i; |
| expected_cluster_processors = cluster_processors[cluster++]; |
| } else { |
| |
|
|
| if (expected_cluster_exists) { |
| |
| |
| |
| |
| |
| |
| |
|
|
| if ((processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER) == 0) { |
| cpuinfo_log_debug( |
| "heuristic detection of core clusters failed: " |
| "processor %"PRIu32" is expected to belong to the cluster of processor %"PRIu32", " |
| "but system siblings lists did not report it as a sibling of processor %"PRIu32, |
| i, cluster_start, cluster_start); |
| return false; |
| } |
| if (processors[i].package_leader_id != cluster_start) { |
| cpuinfo_log_debug( |
| "heuristic detection of core clusters failed: " |
| "processor %"PRIu32" is expected to belong to the cluster of processor %"PRIu32", " |
| "but system siblings lists reported it to belong to the cluster of processor %"PRIu32, |
| i, cluster_start, cluster_start); |
| return false; |
| } |
| } else { |
| |
| |
| |
| |
| |
| |
| |
|
|
| if (processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER) { |
| cpuinfo_log_debug( |
| "heuristic detection of core clusters failed: " |
| "processor %"PRIu32" is expected to be unassigned to any cluster, " |
| "but system siblings lists reported it to belong to the cluster of processor %"PRIu32, |
| i, processors[i].package_leader_id); |
| return false; |
| } |
|
|
| if (processors[i].flags & CPUINFO_LINUX_FLAG_MIN_FREQUENCY) { |
| if (cluster_flags & CPUINFO_LINUX_FLAG_MIN_FREQUENCY) { |
| if (cluster_min_frequency != processors[i].min_frequency) { |
| cpuinfo_log_debug( |
| "heuristic detection of core clusters failed: " |
| "minimum frequency of processor %"PRIu32" (%"PRIu32" KHz) is different than of its expected cluster (%"PRIu32" KHz)", |
| i, processors[i].min_frequency, cluster_min_frequency); |
| return false; |
| } |
| } else { |
| cluster_min_frequency = processors[i].min_frequency; |
| cluster_flags |= CPUINFO_LINUX_FLAG_MIN_FREQUENCY; |
| } |
| } |
|
|
| if (processors[i].flags & CPUINFO_LINUX_FLAG_MAX_FREQUENCY) { |
| if (cluster_flags & CPUINFO_LINUX_FLAG_MAX_FREQUENCY) { |
| if (cluster_max_frequency != processors[i].max_frequency) { |
| cpuinfo_log_debug( |
| "heuristic detection of core clusters failed: " |
| "maximum frequency of processor %"PRIu32" (%"PRIu32" KHz) is different than of its expected cluster (%"PRIu32" KHz)", |
| i, processors[i].max_frequency, cluster_max_frequency); |
| return false; |
| } |
| } else { |
| cluster_max_frequency = processors[i].max_frequency; |
| cluster_flags |= CPUINFO_LINUX_FLAG_MAX_FREQUENCY; |
| } |
| } |
|
|
| if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_IMPLEMENTER) { |
| if (cluster_flags & CPUINFO_ARM_LINUX_VALID_IMPLEMENTER) { |
| if ((cluster_midr & CPUINFO_ARM_MIDR_IMPLEMENTER_MASK) != (processors[i].midr & CPUINFO_ARM_MIDR_IMPLEMENTER_MASK)) { |
| cpuinfo_log_debug( |
| "heuristic detection of core clusters failed: " |
| "CPU Implementer of processor %"PRIu32" (0x%02"PRIx32") is different than of its expected cluster (0x%02"PRIx32")", |
| i, midr_get_implementer(processors[i].midr), midr_get_implementer(cluster_midr)); |
| return false; |
| } |
| } else { |
| cluster_midr = midr_copy_implementer(cluster_midr, processors[i].midr); |
| cluster_flags |= CPUINFO_ARM_LINUX_VALID_IMPLEMENTER; |
| } |
| } |
|
|
| if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_VARIANT) { |
| if (cluster_flags & CPUINFO_ARM_LINUX_VALID_VARIANT) { |
| if ((cluster_midr & CPUINFO_ARM_MIDR_VARIANT_MASK) != (processors[i].midr & CPUINFO_ARM_MIDR_VARIANT_MASK)) { |
| cpuinfo_log_debug( |
| "heuristic detection of core clusters failed: " |
| "CPU Variant of processor %"PRIu32" (0x%"PRIx32") is different than of its expected cluster (0x%"PRIx32")", |
| i, midr_get_variant(processors[i].midr), midr_get_variant(cluster_midr)); |
| return false; |
| } |
| } else { |
| cluster_midr = midr_copy_variant(cluster_midr, processors[i].midr); |
| cluster_flags |= CPUINFO_ARM_LINUX_VALID_VARIANT; |
| } |
| } |
|
|
| if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_PART) { |
| if (cluster_flags & CPUINFO_ARM_LINUX_VALID_PART) { |
| if ((cluster_midr & CPUINFO_ARM_MIDR_PART_MASK) != (processors[i].midr & CPUINFO_ARM_MIDR_PART_MASK)) { |
| cpuinfo_log_debug( |
| "heuristic detection of core clusters failed: " |
| "CPU Part of processor %"PRIu32" (0x%03"PRIx32") is different than of its expected cluster (0x%03"PRIx32")", |
| i, midr_get_part(processors[i].midr), midr_get_part(cluster_midr)); |
| return false; |
| } |
| } else { |
| cluster_midr = midr_copy_part(cluster_midr, processors[i].midr); |
| cluster_flags |= CPUINFO_ARM_LINUX_VALID_PART; |
| } |
| } |
|
|
| if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_REVISION) { |
| if (cluster_flags & CPUINFO_ARM_LINUX_VALID_REVISION) { |
| if ((cluster_midr & CPUINFO_ARM_MIDR_REVISION_MASK) != (processors[i].midr & CPUINFO_ARM_MIDR_REVISION_MASK)) { |
| cpuinfo_log_debug( |
| "heuristic detection of core clusters failed: " |
| "CPU Revision of processor %"PRIu32" (0x%"PRIx32") is different than of its expected cluster (0x%"PRIx32")", |
| i, midr_get_revision(cluster_midr), midr_get_revision(processors[i].midr)); |
| return false; |
| } |
| } else { |
| cluster_midr = midr_copy_revision(cluster_midr, processors[i].midr); |
| cluster_flags |= CPUINFO_ARM_LINUX_VALID_REVISION; |
| } |
| } |
| } |
| } |
| expected_cluster_processors--; |
| } |
| } |
|
|
| |
| cluster = 0; |
| expected_cluster_processors = 0; |
| for (uint32_t i = 0; i < max_processors; i++) { |
| if (bitmask_all(processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) { |
| if (expected_cluster_processors == 0) { |
| |
|
|
| cluster_start = i; |
| expected_cluster_processors = cluster_processors[cluster++]; |
| } else { |
| |
|
|
| if (!(processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER)) { |
| cpuinfo_log_debug("assigned processor %"PRIu32" to cluster of processor %"PRIu32" based on heuristic", |
| i, cluster_start); |
| } |
|
|
| processors[i].package_leader_id = cluster_start; |
| processors[i].flags |= CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER; |
| } |
| expected_cluster_processors--; |
| } |
| } |
| return true; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void cpuinfo_arm_linux_detect_core_clusters_by_sequential_scan( |
| uint32_t max_processors, |
| struct cpuinfo_arm_linux_processor processors[restrict static max_processors]) |
| { |
| uint32_t cluster_flags = 0; |
| uint32_t cluster_processors = 0; |
| uint32_t cluster_start, cluster_midr, cluster_max_frequency, cluster_min_frequency; |
| for (uint32_t i = 0; i < max_processors; i++) { |
| if ((processors[i].flags & (CPUINFO_LINUX_FLAG_VALID | CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER)) == CPUINFO_LINUX_FLAG_VALID) { |
| if (cluster_processors == 0) { |
| goto new_cluster; |
| } |
|
|
| if (processors[i].flags & CPUINFO_LINUX_FLAG_MIN_FREQUENCY) { |
| if (cluster_flags & CPUINFO_LINUX_FLAG_MIN_FREQUENCY) { |
| if (cluster_min_frequency != processors[i].min_frequency) { |
| cpuinfo_log_info( |
| "minimum frequency of processor %"PRIu32" (%"PRIu32" KHz) is different than of preceding cluster (%"PRIu32" KHz); " |
| "processor %"PRIu32" starts to a new cluster", |
| i, processors[i].min_frequency, cluster_min_frequency, i); |
| goto new_cluster; |
| } |
| } else { |
| cluster_min_frequency = processors[i].min_frequency; |
| cluster_flags |= CPUINFO_LINUX_FLAG_MIN_FREQUENCY; |
| } |
| } |
|
|
| if (processors[i].flags & CPUINFO_LINUX_FLAG_MAX_FREQUENCY) { |
| if (cluster_flags & CPUINFO_LINUX_FLAG_MAX_FREQUENCY) { |
| if (cluster_max_frequency != processors[i].max_frequency) { |
| cpuinfo_log_debug( |
| "maximum frequency of processor %"PRIu32" (%"PRIu32" KHz) is different than of preceding cluster (%"PRIu32" KHz); " |
| "processor %"PRIu32" starts a new cluster", |
| i, processors[i].max_frequency, cluster_max_frequency, i); |
| goto new_cluster; |
| } |
| } else { |
| cluster_max_frequency = processors[i].max_frequency; |
| cluster_flags |= CPUINFO_LINUX_FLAG_MAX_FREQUENCY; |
| } |
| } |
|
|
| if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_IMPLEMENTER) { |
| if (cluster_flags & CPUINFO_ARM_LINUX_VALID_IMPLEMENTER) { |
| if ((cluster_midr & CPUINFO_ARM_MIDR_IMPLEMENTER_MASK) != (processors[i].midr & CPUINFO_ARM_MIDR_IMPLEMENTER_MASK)) { |
| cpuinfo_log_debug( |
| "CPU Implementer of processor %"PRIu32" (0x%02"PRIx32") is different than of preceding cluster (0x%02"PRIx32"); " |
| "processor %"PRIu32" starts to a new cluster", |
| i, midr_get_implementer(processors[i].midr), midr_get_implementer(cluster_midr), i); |
| goto new_cluster; |
| } |
| } else { |
| cluster_midr = midr_copy_implementer(cluster_midr, processors[i].midr); |
| cluster_flags |= CPUINFO_ARM_LINUX_VALID_IMPLEMENTER; |
| } |
| } |
|
|
| if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_VARIANT) { |
| if (cluster_flags & CPUINFO_ARM_LINUX_VALID_VARIANT) { |
| if ((cluster_midr & CPUINFO_ARM_MIDR_VARIANT_MASK) != (processors[i].midr & CPUINFO_ARM_MIDR_VARIANT_MASK)) { |
| cpuinfo_log_debug( |
| "CPU Variant of processor %"PRIu32" (0x%"PRIx32") is different than of its expected cluster (0x%"PRIx32")" |
| "processor %"PRIu32" starts to a new cluster", |
| i, midr_get_variant(processors[i].midr), midr_get_variant(cluster_midr), i); |
| goto new_cluster; |
| } |
| } else { |
| cluster_midr = midr_copy_variant(cluster_midr, processors[i].midr); |
| cluster_flags |= CPUINFO_ARM_LINUX_VALID_VARIANT; |
| } |
| } |
|
|
| if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_PART) { |
| if (cluster_flags & CPUINFO_ARM_LINUX_VALID_PART) { |
| if ((cluster_midr & CPUINFO_ARM_MIDR_PART_MASK) != (processors[i].midr & CPUINFO_ARM_MIDR_PART_MASK)) { |
| cpuinfo_log_debug( |
| "CPU Part of processor %"PRIu32" (0x%03"PRIx32") is different than of its expected cluster (0x%03"PRIx32")" |
| "processor %"PRIu32" starts to a new cluster", |
| i, midr_get_part(processors[i].midr), midr_get_part(cluster_midr), i); |
| goto new_cluster; |
| } |
| } else { |
| cluster_midr = midr_copy_part(cluster_midr, processors[i].midr); |
| cluster_flags |= CPUINFO_ARM_LINUX_VALID_PART; |
| } |
| } |
|
|
| if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_REVISION) { |
| if (cluster_flags & CPUINFO_ARM_LINUX_VALID_REVISION) { |
| if ((cluster_midr & CPUINFO_ARM_MIDR_REVISION_MASK) != (processors[i].midr & CPUINFO_ARM_MIDR_REVISION_MASK)) { |
| cpuinfo_log_debug( |
| "CPU Revision of processor %"PRIu32" (0x%"PRIx32") is different than of its expected cluster (0x%"PRIx32")" |
| "processor %"PRIu32" starts to a new cluster", |
| i, midr_get_revision(cluster_midr), midr_get_revision(processors[i].midr), i); |
| goto new_cluster; |
| } |
| } else { |
| cluster_midr = midr_copy_revision(cluster_midr, processors[i].midr); |
| cluster_flags |= CPUINFO_ARM_LINUX_VALID_REVISION; |
| } |
| } |
|
|
| |
| cluster_processors++; |
| processors[i].package_leader_id = cluster_start; |
| processors[i].flags |= CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER; |
| cpuinfo_log_debug("assigned processor %"PRIu32" to preceding cluster of processor %"PRIu32, i, cluster_start); |
| continue; |
|
|
| new_cluster: |
| |
| cluster_start = i; |
| processors[i].package_leader_id = i; |
| processors[i].flags |= CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER; |
| cluster_processors = 1; |
|
|
| |
| cluster_flags = 0; |
| if (processors[i].flags & CPUINFO_LINUX_FLAG_MIN_FREQUENCY) { |
| cluster_min_frequency = processors[i].min_frequency; |
| cluster_flags |= CPUINFO_LINUX_FLAG_MIN_FREQUENCY; |
| } |
| if (processors[i].flags & CPUINFO_LINUX_FLAG_MAX_FREQUENCY) { |
| cluster_max_frequency = processors[i].max_frequency; |
| cluster_flags |= CPUINFO_LINUX_FLAG_MAX_FREQUENCY; |
| } |
| if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_IMPLEMENTER) { |
| cluster_midr = midr_copy_implementer(cluster_midr, processors[i].midr); |
| cluster_flags |= CPUINFO_ARM_LINUX_VALID_IMPLEMENTER; |
| } |
| if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_VARIANT) { |
| cluster_midr = midr_copy_variant(cluster_midr, processors[i].midr); |
| cluster_flags |= CPUINFO_ARM_LINUX_VALID_VARIANT; |
| } |
| if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_PART) { |
| cluster_midr = midr_copy_part(cluster_midr, processors[i].midr); |
| cluster_flags |= CPUINFO_ARM_LINUX_VALID_PART; |
| } |
| if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_REVISION) { |
| cluster_midr = midr_copy_revision(cluster_midr, processors[i].midr); |
| cluster_flags |= CPUINFO_ARM_LINUX_VALID_REVISION; |
| } |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| void cpuinfo_arm_linux_count_cluster_processors( |
| uint32_t max_processors, |
| struct cpuinfo_arm_linux_processor processors[restrict static max_processors]) |
| { |
| |
| for (uint32_t i = 0; i < max_processors; i++) { |
| if (bitmask_all(processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) { |
| const uint32_t package_leader_id = processors[i].package_leader_id; |
| processors[package_leader_id].package_processor_count += 1; |
| } |
| } |
| |
| for (uint32_t i = 0; i < max_processors; i++) { |
| if (bitmask_all(processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) { |
| const uint32_t package_leader_id = processors[i].package_leader_id; |
| processors[i].package_processor_count = processors[package_leader_id].package_processor_count; |
| } |
| } |
| } |
|
|