|
#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; |
|
} |
|
} |
|
} |
|
|