blob: 10db278849c35d243460e1973aecabc2088b2070 [file] [log] [blame]
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <arm/linux/api.h>
#include <cpuinfo.h>
#if defined(__ANDROID__)
#include <arm/android/api.h>
#endif
#include <arm/api.h>
#include <arm/midr.h>
#include <cpuinfo/common.h>
#include <cpuinfo/internal-api.h>
#include <cpuinfo/log.h>
#include <linux/api.h>
#define CLUSTERS_MAX 3
static inline bool bitmask_all(uint32_t bitfield, uint32_t mask) {
return (bitfield & mask) == mask;
}
/* Description of core clusters configuration in a chipset (identified by series
* and model number) */
struct cluster_config {
/* Number of cores (logical processors) */
uint8_t cores;
/* ARM chipset series (see cpuinfo_arm_chipset_series enum) */
uint8_t series;
/* Chipset model number (see cpuinfo_arm_chipset struct) */
uint16_t model;
/* Number of heterogenous clusters in the CPU package */
uint8_t clusters;
/*
* Number of cores in each cluster:
# - Symmetric configurations: [0] = # cores
* - big.LITTLE configurations: [0] = # LITTLE cores, [1] = # big cores
* - Max.Med.Min configurations: [0] = # Min cores, [1] = # Med cores,
[2] = # Max cores
*/
uint8_t cluster_cores[CLUSTERS_MAX];
/*
* MIDR of cores in each cluster:
* - Symmetric configurations: [0] = core MIDR
* - big.LITTLE configurations: [0] = LITTLE core MIDR, [1] = big core
* MIDR
* - Max.Med.Min configurations: [0] = Min core MIDR, [1] = Med core
* MIDR, [2] = Max core MIDR
*/
uint32_t cluster_midr[CLUSTERS_MAX];
};
/*
* The list of chipsets where MIDR may not be unambigiously decoded at least on
* some devices. The typical reasons for impossibility to decoded MIDRs are
* buggy kernels, which either do not report all MIDR information (e.g. on
* ATM7029 kernel doesn't report CPU Part), or chipsets have more than one type
* of cores (i.e. 4x Cortex-A53 + 4x Cortex-A53 is out) and buggy kernels report
* MIDR information only about some cores in /proc/cpuinfo (either only online
* cores, or only the core that reads /proc/cpuinfo). On these kernels/chipsets,
* it is not possible to detect all core types by just parsing /proc/cpuinfo, so
* we use chipset name and this table to find their MIDR (and thus
* microarchitecture, cache, etc).
*
* Note: not all chipsets with heterogeneous multiprocessing need an entry in
* this table. The following HMP chipsets always list information about all
* cores in /proc/cpuinfo:
*
* - Snapdragon 660
* - Snapdragon 820 (MSM8996)
* - Snapdragon 821 (MSM8996PRO)
* - Snapdragon 835 (MSM8998)
* - Exynos 8895
* - Kirin 960
*
* As these are all new processors, there is hope that this table won't
* uncontrollably grow over time.
*/
static const struct cluster_config
cluster_configs[] =
{
#if CPUINFO_ARCH_ARM
{
/*
* MSM8916 (Snapdragon 410): 4x Cortex-A53
* Some AArch32 phones use non-standard /proc/cpuinfo format.
*/
.cores = 4,
.series = cpuinfo_arm_chipset_series_qualcomm_msm,
.model = UINT16_C(8916),
.clusters = 1,
.cluster_cores =
{
[0] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FD030),
},
},
{
/*
* MSM8939 (Snapdragon 615): 4x Cortex-A53 + 4x Cortex-A53
* Some AArch32 phones use non-standard /proc/cpuinfo format.
*/
.cores = 8,
.series = cpuinfo_arm_chipset_series_qualcomm_msm,
.model = UINT16_C(8939),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FD034),
[1] = UINT32_C(0x410FD034),
},
},
#endif
{
/* MSM8956 (Snapdragon 650): 2x Cortex-A72 + 4x Cortex-A53 */
.cores = 6,
.series = cpuinfo_arm_chipset_series_qualcomm_msm,
.model = UINT16_C(8956),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 2,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FD034),
[1] = UINT32_C(0x410FD080),
},
},
{
/* MSM8976/MSM8976PRO (Snapdragon 652/653): 4x Cortex-A72 + 4x
Cortex-A53 */
.cores = 8,
.series = cpuinfo_arm_chipset_series_qualcomm_msm,
.model = UINT16_C(8976),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FD034),
[1] = UINT32_C(0x410FD080),
},
},
{
/* MSM8992 (Snapdragon 808): 2x Cortex-A57 + 4x Cortex-A53 */
.cores = 6,
.series = cpuinfo_arm_chipset_series_qualcomm_msm,
.model = UINT16_C(8992),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 2,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FD033),
[1] = UINT32_C(0x411FD072),
},
},
{
/* MSM8994/MSM8994V (Snapdragon 810): 4x Cortex-A57 + 4x
Cortex-A53 */
.cores = 8,
.series = cpuinfo_arm_chipset_series_qualcomm_msm,
.model = UINT16_C(8994),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FD032),
[1] = UINT32_C(0x411FD071),
},
},
#if CPUINFO_ARCH_ARM
{
/* Exynos 5422: 4x Cortex-A15 + 4x Cortex-A7 */
.cores = 8,
.series = cpuinfo_arm_chipset_series_samsung_exynos,
.model = UINT16_C(5422),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FC073),
[1] = UINT32_C(0x412FC0F3),
},
},
{
/* Exynos 5430: 4x Cortex-A15 + 4x Cortex-A7 */
.cores = 8,
.series = cpuinfo_arm_chipset_series_samsung_exynos,
.model = UINT16_C(5430),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FC074),
[1] = UINT32_C(0x413FC0F3),
},
},
#endif /* CPUINFO_ARCH_ARM */
{
/* Exynos 5433: 4x Cortex-A57 + 4x Cortex-A53 */
.cores = 8,
.series = cpuinfo_arm_chipset_series_samsung_exynos,
.model = UINT16_C(5433),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FD031),
[1] = UINT32_C(0x411FD070),
},
},
{
/* Exynos 7420: 4x Cortex-A57 + 4x Cortex-A53 */
.cores = 8,
.series = cpuinfo_arm_chipset_series_samsung_exynos,
.model = UINT16_C(7420),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FD032),
[1] = UINT32_C(0x411FD070),
},
},
{
/* Exynos 8890: 4x Exynos M1 + 4x Cortex-A53 */
.cores = 8,
.series = cpuinfo_arm_chipset_series_samsung_exynos,
.model = UINT16_C(8890),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FD034),
[1] = UINT32_C(0x531F0011),
},
},
#if CPUINFO_ARCH_ARM
{
/* Kirin 920: 4x Cortex-A15 + 4x Cortex-A7 */
.cores = 8,
.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
.model = UINT16_C(920),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FC075),
[1] = UINT32_C(0x413FC0F3),
},
},
{
/* Kirin 925: 4x Cortex-A15 + 4x Cortex-A7 */
.cores = 8,
.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
.model = UINT16_C(925),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FC075),
[1] = UINT32_C(0x413FC0F3),
},
},
{
/* Kirin 928: 4x Cortex-A15 + 4x Cortex-A7 */
.cores = 8,
.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
.model = UINT16_C(928),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FC075),
[1] = UINT32_C(0x413FC0F3),
},
},
#endif /* CPUINFO_ARCH_ARM */
{
/* Kirin 950: 4x Cortex-A72 + 4x Cortex-A53 */
.cores = 8,
.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
.model = UINT16_C(950),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FD034),
[1] = UINT32_C(0x410FD080),
},
},
{
/* Kirin 955: 4x Cortex-A72 + 4x Cortex-A53 */
.cores = 8,
.series = cpuinfo_arm_chipset_series_hisilicon_kirin,
.model = UINT16_C(955),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FD034),
[1] = UINT32_C(0x410FD080),
},
},
#if CPUINFO_ARCH_ARM
{
/* MediaTek MT8135: 2x Cortex-A7 + 2x Cortex-A15 */
.cores = 4,
.series = cpuinfo_arm_chipset_series_mediatek_mt,
.model = UINT16_C(8135),
.clusters = 2,
.cluster_cores =
{
[0] = 2,
[1] = 2,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FC073),
[1] = UINT32_C(0x413FC0F2),
},
},
#endif
{
/* MediaTek MT8173: 2x Cortex-A72 + 2x Cortex-A53 */
.cores = 4,
.series = cpuinfo_arm_chipset_series_mediatek_mt,
.model = UINT16_C(8173),
.clusters = 2,
.cluster_cores =
{
[0] = 2,
[1] = 2,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FD032),
[1] = UINT32_C(0x410FD080),
},
},
{
/* MediaTek MT8176: 2x Cortex-A72 + 4x Cortex-A53 */
.cores = 6,
.series = cpuinfo_arm_chipset_series_mediatek_mt,
.model = UINT16_C(8176),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 2,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FD032),
[1] = UINT32_C(0x410FD080),
},
},
#if CPUINFO_ARCH_ARM64
{
/*
* MediaTek MT8735: 4x Cortex-A53
* Some AArch64 phones use non-standard /proc/cpuinfo format.
*/
.cores = 4,
.series = cpuinfo_arm_chipset_series_mediatek_mt,
.model = UINT16_C(8735),
.clusters = 1,
.cluster_cores =
{
[0] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FD034),
},
},
#endif
#if CPUINFO_ARCH_ARM
{
/*
* MediaTek MT6592: 4x Cortex-A7 + 4x Cortex-A7
* Some phones use non-standard /proc/cpuinfo format.
*/
.cores = 4,
.series = cpuinfo_arm_chipset_series_mediatek_mt,
.model = UINT16_C(6592),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FC074),
[1] = UINT32_C(0x410FC074),
},
},
{
/* MediaTek MT6595: 4x Cortex-A17 + 4x Cortex-A7 */
.cores = 8,
.series = cpuinfo_arm_chipset_series_mediatek_mt,
.model = UINT16_C(6595),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FC075),
[1] = UINT32_C(0x410FC0E0),
},
},
#endif
{
/* MediaTek MT6797: 2x Cortex-A72 + 4x Cortex-A53 + 4x
Cortex-A53 */
.cores = 10,
.series = cpuinfo_arm_chipset_series_mediatek_mt,
.model = UINT16_C(6797),
.clusters = 3,
.cluster_cores =
{
[0] = 4,
[1] = 4,
[2] = 2,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FD034),
[1] = UINT32_C(0x410FD034),
[2] = UINT32_C(0x410FD081),
},
},
{
/* MediaTek MT6799: 2x Cortex-A73 + 4x Cortex-A53 + 4x
Cortex-A35 */
.cores = 10,
.series = cpuinfo_arm_chipset_series_mediatek_mt,
.model = UINT16_C(6799),
.clusters = 3,
.cluster_cores =
{
[0] = 4,
[1] = 4,
[2] = 2,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FD041),
[1] = UINT32_C(0x410FD034),
[2] = UINT32_C(0x410FD092),
},
},
{
/* Rockchip RK3399: 2x Cortex-A72 + 4x Cortex-A53 */
.cores = 6,
.series = cpuinfo_arm_chipset_series_rockchip_rk,
.model = UINT16_C(3399),
.clusters = 2,
.cluster_cores =
{
[0] = 4,
[1] = 2,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FD034),
[1] = UINT32_C(0x410FD082),
},
},
#if CPUINFO_ARCH_ARM
{
/* Actions ATM8029: 4x Cortex-A5
* Most devices use non-standard /proc/cpuinfo format.
*/
.cores = 4,
.series = cpuinfo_arm_chipset_series_actions_atm,
.model = UINT16_C(7029),
.clusters = 1,
.cluster_cores =
{
[0] = 4,
},
.cluster_midr =
{
[0] = UINT32_C(0x410FC051),
},
},
#endif
};
/*
* Searches chipset name in mapping of chipset name to cores' MIDR values. If
* match is successful, initializes MIDR for all clusters' leaders with
* tabulated values.
*
* @param[in] chipset - chipset (SoC) name information.
* @param clusters_count - number of CPU core clusters detected in the SoC.
* @param cluster_leaders - indices of core clusters' leaders in the @p
* processors array.
* @param processors_count - number of usable logical processors in the system.
* @param[in,out] processors - array of logical processor descriptions with
* pre-parsed MIDR, maximum frequency, and decoded core cluster
* (package_leader_id) information. Upon successful return, processors[i].midr
* for all clusters' leaders contains the tabulated MIDR values.
* @param verify_midr - indicated whether the function should check that the
* MIDR values to be assigned to leaders of core clusters are consistent with
* known parts of their parsed values. Set if to false if the only MIDR value
* parsed from /proc/cpuinfo is for the last processor reported in /proc/cpuinfo
* and thus can't be unambiguously attributed to that processor.
*
* @retval true if the chipset was found in the mapping and core clusters'
* leaders initialized with MIDR values.
* @retval false if the chipset was not found in the mapping, or any consistency
* check failed.
*/
static bool cpuinfo_arm_linux_detect_cluster_midr_by_chipset(
const struct cpuinfo_arm_chipset chipset[restrict static 1],
uint32_t clusters_count,
const uint32_t cluster_leaders[restrict static CLUSTERS_MAX],
uint32_t processors_count,
struct cpuinfo_arm_linux_processor processors[restrict static processors_count],
bool verify_midr) {
if (clusters_count <= CLUSTERS_MAX) {
for (uint32_t c = 0; c < CPUINFO_COUNT_OF(cluster_configs); c++) {
if (cluster_configs[c].model == chipset->model &&
cluster_configs[c].series == chipset->series) {
/* Verify that the total number of cores and
* clusters of cores matches expectation */
if (cluster_configs[c].cores != processors_count ||
cluster_configs[c].clusters != clusters_count) {
return false;
}
/* Verify that core cluster configuration
* matches expectation */
for (uint32_t cluster = 0; cluster < clusters_count; cluster++) {
const uint32_t cluster_leader = cluster_leaders[cluster];
if (cluster_configs[c].cluster_cores[cluster] !=
processors[cluster_leader].package_processor_count) {
return false;
}
}
if (verify_midr) {
/* Verify known parts of MIDR */
for (uint32_t cluster = 0; cluster < clusters_count; cluster++) {
const uint32_t cluster_leader = cluster_leaders[cluster];
/* Create a mask of known midr
* bits */
uint32_t midr_mask = 0;
if (processors[cluster_leader].flags &
CPUINFO_ARM_LINUX_VALID_IMPLEMENTER) {
midr_mask |= CPUINFO_ARM_MIDR_IMPLEMENTER_MASK;
}
if (processors[cluster_leader].flags &
CPUINFO_ARM_LINUX_VALID_VARIANT) {
midr_mask |= CPUINFO_ARM_MIDR_VARIANT_MASK;
}
if (processors[cluster_leader].flags & CPUINFO_ARM_LINUX_VALID_PART) {
midr_mask |= CPUINFO_ARM_MIDR_PART_MASK;
}
if (processors[cluster_leader].flags &
CPUINFO_ARM_LINUX_VALID_REVISION) {
midr_mask |= CPUINFO_ARM_MIDR_REVISION_MASK;
}
/* Verify the bits under the
* mask */
if ((processors[cluster_leader].midr ^
cluster_configs[c].cluster_midr[cluster]) &
midr_mask) {
cpuinfo_log_debug(
"parsed MIDR of cluster %08" PRIu32
" does not match tabulated value %08" PRIu32,
processors[cluster_leader].midr,
cluster_configs[c].cluster_midr[cluster]);
return false;
}
}
}
/* Assign MIDRs according to tabulated
* configurations */
for (uint32_t cluster = 0; cluster < clusters_count; cluster++) {
const uint32_t cluster_leader = cluster_leaders[cluster];
processors[cluster_leader].midr = cluster_configs[c].cluster_midr[cluster];
processors[cluster_leader].flags |= CPUINFO_ARM_LINUX_VALID_MIDR;
cpuinfo_log_debug(
"cluster %" PRIu32 " MIDR = 0x%08" PRIx32,
cluster,
cluster_configs[c].cluster_midr[cluster]);
}
return true;
}
}
}
return false;
}
/*
* Initializes MIDR for leaders of core clusters using a heuristic for
* big.LITTLE systems:
* - If the only known MIDR is for the big core cluster, guess the matching MIDR
* for the LITTLE cluster.
* - Estimate which of the clusters is big using maximum frequency, if known,
* otherwise using system processor ID.
* - Initialize the MIDR for big and LITTLE core clusters using the guesstimates
* values.
*
* @param clusters_count - number of CPU core clusters detected in the SoC.
* @param cluster_with_midr_count - number of CPU core clusters in the SoC with
* known MIDR values.
* @param last_processor_with_midr - index of the last logical processor with
* known MIDR in the @p processors array.
* @param cluster_leaders - indices of core clusters' leaders in the @p
* processors array.
* @param[in,out] processors - array of logical processor descriptions with
* pre-parsed MIDR, maximum frequency, and decoded core cluster
* (package_leader_id) information. Upon successful return, processors[i].midr
* for all core clusters' leaders contains the heuristically detected MIDR
* value.
* @param verify_midr - indicated whether the function should check that the
* MIDR values to be assigned to leaders of core clusters are consistent with
* known parts of their parsed values. Set if to false if the only MIDR value
* parsed from /proc/cpuinfo is for the last processor reported in /proc/cpuinfo
* and thus can't be unambiguously attributed to that processor.
*
* @retval true if this is a big.LITTLE system with only one known MIDR and the
* CPU core clusters' leaders were initialized with MIDR values.
* @retval false if this is not a big.LITTLE system.
*/
static bool cpuinfo_arm_linux_detect_cluster_midr_by_big_little_heuristic(
uint32_t clusters_count,
uint32_t cluster_with_midr_count,
uint32_t last_processor_with_midr,
const uint32_t cluster_leaders[restrict static CLUSTERS_MAX],
struct cpuinfo_arm_linux_processor processors[restrict static last_processor_with_midr],
bool verify_midr) {
if (clusters_count != 2 || cluster_with_midr_count != 1) {
/* Not a big.LITTLE system, or MIDR is known for both/neither
* clusters */
return false;
}
const uint32_t midr_flags =
(processors[processors[last_processor_with_midr].package_leader_id].flags &
CPUINFO_ARM_LINUX_VALID_MIDR);
const uint32_t big_midr = processors[processors[last_processor_with_midr].package_leader_id].midr;
const uint32_t little_midr = midr_little_core_for_big(big_midr);
/* Default assumption: the first reported cluster is LITTLE cluster
* (this holds on most Linux kernels) */
uint32_t little_cluster_leader = cluster_leaders[0];
const uint32_t other_cluster_leader = cluster_leaders[1];
/* If maximum frequency is known for both clusters, assume LITTLE
* cluster is the one with lower frequency */
if (processors[little_cluster_leader].flags & processors[other_cluster_leader].flags &
CPUINFO_LINUX_FLAG_MAX_FREQUENCY) {
if (processors[little_cluster_leader].max_frequency > processors[other_cluster_leader].max_frequency) {
little_cluster_leader = other_cluster_leader;
}
}
if (verify_midr) {
/* Verify known parts of MIDR */
for (uint32_t cluster = 0; cluster < clusters_count; cluster++) {
const uint32_t cluster_leader = cluster_leaders[cluster];
/* Create a mask of known midr bits */
uint32_t midr_mask = 0;
if (processors[cluster_leader].flags & CPUINFO_ARM_LINUX_VALID_IMPLEMENTER) {
midr_mask |= CPUINFO_ARM_MIDR_IMPLEMENTER_MASK;
}
if (processors[cluster_leader].flags & CPUINFO_ARM_LINUX_VALID_VARIANT) {
midr_mask |= CPUINFO_ARM_MIDR_VARIANT_MASK;
}
if (processors[cluster_leader].flags & CPUINFO_ARM_LINUX_VALID_PART) {
midr_mask |= CPUINFO_ARM_MIDR_PART_MASK;
}
if (processors[cluster_leader].flags & CPUINFO_ARM_LINUX_VALID_REVISION) {
midr_mask |= CPUINFO_ARM_MIDR_REVISION_MASK;
}
/* Verify the bits under the mask */
const uint32_t midr = (cluster_leader == little_cluster_leader) ? little_midr : big_midr;
if ((processors[cluster_leader].midr ^ midr) & midr_mask) {
cpuinfo_log_debug(
"parsed MIDR %08" PRIu32 " of cluster leader %" PRIu32
" is inconsistent with expected value %08" PRIu32,
processors[cluster_leader].midr,
cluster_leader,
midr);
return false;
}
}
}
for (uint32_t c = 0; c < clusters_count; c++) {
/* Skip cluster with already assigned MIDR */
const uint32_t cluster_leader = cluster_leaders[c];
if (bitmask_all(processors[cluster_leader].flags, CPUINFO_ARM_LINUX_VALID_MIDR)) {
continue;
}
const uint32_t midr = (cluster_leader == little_cluster_leader) ? little_midr : big_midr;
cpuinfo_log_info("assume processor %" PRIu32 " to have MIDR %08" PRIx32, cluster_leader, midr);
/* To be consistent, we copy the MIDR entirely, rather than by
* parts */
processors[cluster_leader].midr = midr;
processors[cluster_leader].flags |= midr_flags;
}
return true;
}
/*
* Initializes MIDR for leaders of core clusters in a single sequential scan:
* - Clusters preceding the first reported MIDR value are assumed to have
* default MIDR value.
* - Clusters following any reported MIDR value to have that MIDR value.
*
* @param default_midr - MIDR value that will be assigned to cluster leaders
* preceding any reported MIDR value.
* @param processors_count - number of logical processor descriptions in the @p
* processors array.
* @param[in,out] processors - array of logical processor descriptions with
* pre-parsed MIDR, maximum frequency, and decoded core cluster
* (package_leader_id) information. Upon successful return, processors[i].midr
* for all core clusters' leaders contains the assigned MIDR value.
*/
static void cpuinfo_arm_linux_detect_cluster_midr_by_sequential_scan(
uint32_t default_midr,
uint32_t processors_count,
struct cpuinfo_arm_linux_processor processors[restrict static processors_count]) {
uint32_t midr = default_midr;
for (uint32_t i = 0; i < processors_count; i++) {
if (bitmask_all(processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
if (processors[i].package_leader_id == i) {
if (bitmask_all(processors[i].flags, CPUINFO_ARM_LINUX_VALID_MIDR)) {
midr = processors[i].midr;
} else {
cpuinfo_log_info(
"assume processor %" PRIu32 " to have MIDR %08" PRIx32, i, midr);
/* To be consistent, we copy the MIDR
* entirely, rather than by parts
*/
processors[i].midr = midr;
processors[i].flags |= CPUINFO_ARM_LINUX_VALID_MIDR;
}
}
}
}
}
/*
* Detects MIDR of each CPU core clusters' leader.
*
* @param[in] chipset - chipset (SoC) name information.
* @param max_processors - number of processor descriptions in the @p processors
* array.
* @param usable_processors - number of processor descriptions in the @p
* processors array with both POSSIBLE and PRESENT flags.
* @param[in,out] processors - array of logical processor descriptions with
* pre-parsed MIDR, maximum frequency, and decoded core cluster
* (package_leader_id) information. Upon return, processors[i].midr for all
* clusters' leaders contains the MIDR value.
*
* @returns The number of core clusters
*/
uint32_t cpuinfo_arm_linux_detect_cluster_midr(
const struct cpuinfo_arm_chipset chipset[restrict static 1],
uint32_t max_processors,
uint32_t usable_processors,
struct cpuinfo_arm_linux_processor processors[restrict static max_processors]) {
uint32_t clusters_count = 0;
uint32_t cluster_leaders[CLUSTERS_MAX];
uint32_t last_processor_in_cpuinfo = max_processors;
uint32_t last_processor_with_midr = max_processors;
uint32_t processors_with_midr_count = 0;
for (uint32_t i = 0; i < max_processors; i++) {
if (bitmask_all(processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
if (processors[i].flags & CPUINFO_ARM_LINUX_VALID_PROCESSOR) {
last_processor_in_cpuinfo = i;
}
if (bitmask_all(
processors[i].flags,
CPUINFO_ARM_LINUX_VALID_IMPLEMENTER | CPUINFO_ARM_LINUX_VALID_PART)) {
last_processor_with_midr = i;
processors_with_midr_count += 1;
}
const uint32_t group_leader = processors[i].package_leader_id;
if (group_leader == i) {
if (clusters_count < CLUSTERS_MAX) {
cluster_leaders[clusters_count] = i;
}
clusters_count += 1;
} else {
/* Copy known bits of information to cluster
* leader */
if ((processors[i].flags & ~processors[group_leader].flags) &
CPUINFO_LINUX_FLAG_MAX_FREQUENCY) {
processors[group_leader].max_frequency = processors[i].max_frequency;
processors[group_leader].flags |= CPUINFO_LINUX_FLAG_MAX_FREQUENCY;
}
if (!bitmask_all(processors[group_leader].flags, CPUINFO_ARM_LINUX_VALID_MIDR) &&
bitmask_all(processors[i].flags, CPUINFO_ARM_LINUX_VALID_MIDR)) {
processors[group_leader].midr = processors[i].midr;
processors[group_leader].flags |= CPUINFO_ARM_LINUX_VALID_MIDR;
}
}
}
}
cpuinfo_log_debug("detected %" PRIu32 " core clusters", clusters_count);
/*
* Two relations between reported /proc/cpuinfo information, and cores
* is possible:
* - /proc/cpuinfo reports information for all or some of the cores
* below the corresponding "processor : <number>" lines. Information on
* offline cores may be missing.
* - /proc/cpuinfo reports information only once, after all "processor :
* <number>" lines. The reported information may relate to processor #0
* or to the processor which executed the system calls to read
* /proc/cpuinfo. It is also indistinguishable from /proc/cpuinfo
* reporting information only for the last core (e.g. if all other cores
* are offline).
*
* We detect the second case by checking if /proc/cpuinfo contains valid
* MIDR only for one, last reported, processor. Note, that the last
* reported core may be not the last present & possible processor, as
* /proc/cpuinfo may non-report high-index offline cores.
*/
if (processors_with_midr_count == 1 && last_processor_in_cpuinfo == last_processor_with_midr &&
clusters_count > 1) {
/*
* There are multiple core clusters, but /proc/cpuinfo reported
* MIDR only for one processor, and we don't even know which
* logical processor this information refers to.
*
* We make three attempts to detect MIDR for all clusters:
* 1. Search tabulated MIDR values for chipsets which have
* heterogeneous clusters and ship with Linux kernels which do
* not always report all cores in /proc/cpuinfo. If found, use
* the tabulated values.
* 2. For systems with 2 clusters and MIDR known for one
* cluster, assume big.LITTLE configuration, and estimate MIDR
* for the other cluster under assumption that MIDR for the big
* cluster is known.
* 3. Initialize MIDRs for all core clusters to the only parsed
* MIDR value.
*/
cpuinfo_log_debug("the only reported MIDR can not be attributed to a particular processor");
if (cpuinfo_arm_linux_detect_cluster_midr_by_chipset(
chipset, clusters_count, cluster_leaders, usable_processors, processors, false)) {
return clusters_count;
}
/* Try big.LITTLE heuristic */
if (cpuinfo_arm_linux_detect_cluster_midr_by_big_little_heuristic(
clusters_count, 1, last_processor_with_midr, cluster_leaders, processors, false)) {
return clusters_count;
}
/* Fall back to sequential initialization of MIDR values for
* core clusters
*/
cpuinfo_arm_linux_detect_cluster_midr_by_sequential_scan(
processors[processors[last_processor_with_midr].package_leader_id].midr,
max_processors,
processors);
} else if (processors_with_midr_count < usable_processors) {
/*
* /proc/cpuinfo reported MIDR only for some processors, and
* probably some core clusters do not have MIDR for any of the
* cores. Check if this is the case.
*/
uint32_t clusters_with_midr_count = 0;
for (uint32_t i = 0; i < max_processors; i++) {
if (bitmask_all(processors[i].flags, CPUINFO_LINUX_FLAG_VALID | CPUINFO_ARM_LINUX_VALID_MIDR)) {
if (processors[i].package_leader_id == i) {
clusters_with_midr_count += 1;
}
}
}
if (clusters_with_midr_count < clusters_count) {
/*
* /proc/cpuinfo reported MIDR only for some clusters,
* need to reconstruct others. We make three attempts to
* detect MIDR for clusters without it:
* 1. Search tabulated MIDR values for chipsets which
* have heterogeneous clusters and ship with Linux
* kernels which do not always report all cores in
* /proc/cpuinfo. If found, use the tabulated values.
* 2. For systems with 2 clusters and MIDR known for one
* cluster, assume big.LITTLE configuration, and
* estimate MIDR for the other cluster under assumption
* that MIDR for the big cluster is known.
* 3. Initialize MIDRs for core clusters in a single
* sequential scan:
* - Clusters preceding the first reported MIDR value
* are assumed to have the last reported MIDR value.
* - Clusters following any reported MIDR value to
* have that MIDR value.
*/
if (cpuinfo_arm_linux_detect_cluster_midr_by_chipset(
chipset, clusters_count, cluster_leaders, usable_processors, processors, true)) {
return clusters_count;
}
if (last_processor_with_midr != max_processors) {
/* Try big.LITTLE heuristic */
if (cpuinfo_arm_linux_detect_cluster_midr_by_big_little_heuristic(
clusters_count,
processors_with_midr_count,
last_processor_with_midr,
cluster_leaders,
processors,
true)) {
return clusters_count;
}
/* Fall back to sequential initialization of
* MIDR values for core clusters */
cpuinfo_arm_linux_detect_cluster_midr_by_sequential_scan(
processors[processors[last_processor_with_midr].package_leader_id].midr,
max_processors,
processors);
}
}
}
return clusters_count;
}