mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-18 13:05:46 -04:00
dep: Add cpuinfo
This commit is contained in:
267
dep/cpuinfo/src/arm/linux/aarch32-isa.c
Normal file
267
dep/cpuinfo/src/arm/linux/aarch32-isa.c
Normal file
@ -0,0 +1,267 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#if CPUINFO_MOCK
|
||||
#include <cpuinfo-mock.h>
|
||||
#endif
|
||||
#include <arm/linux/api.h>
|
||||
#include <arm/linux/cp.h>
|
||||
#include <arm/midr.h>
|
||||
#include <cpuinfo/log.h>
|
||||
|
||||
|
||||
#if CPUINFO_MOCK
|
||||
uint32_t cpuinfo_arm_fpsid = 0;
|
||||
uint32_t cpuinfo_arm_mvfr0 = 0;
|
||||
uint32_t cpuinfo_arm_wcid = 0;
|
||||
|
||||
void cpuinfo_set_fpsid(uint32_t fpsid) {
|
||||
cpuinfo_arm_fpsid = fpsid;
|
||||
}
|
||||
|
||||
void cpuinfo_set_wcid(uint32_t wcid) {
|
||||
cpuinfo_arm_wcid = wcid;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
|
||||
uint32_t features,
|
||||
uint32_t features2,
|
||||
uint32_t midr,
|
||||
uint32_t architecture_version,
|
||||
uint32_t architecture_flags,
|
||||
const struct cpuinfo_arm_chipset chipset[restrict static 1],
|
||||
struct cpuinfo_arm_isa isa[restrict static 1])
|
||||
{
|
||||
if (architecture_version >= 8) {
|
||||
/*
|
||||
* ARMv7 code running on ARMv8: IDIV, VFP, NEON are always supported,
|
||||
* but may be not reported in /proc/cpuinfo features.
|
||||
*/
|
||||
isa->armv5e = true;
|
||||
isa->armv6 = true;
|
||||
isa->armv6k = true;
|
||||
isa->armv7 = true;
|
||||
isa->armv7mp = true;
|
||||
isa->armv8 = true;
|
||||
isa->thumb = true;
|
||||
isa->thumb2 = true;
|
||||
isa->idiv = true;
|
||||
isa->vfpv3 = true;
|
||||
isa->d32 = true;
|
||||
isa->fp16 = true;
|
||||
isa->fma = true;
|
||||
isa->neon = true;
|
||||
|
||||
/*
|
||||
* NEON FP16 compute extension and VQRDMLAH/VQRDMLSH instructions are not indicated in /proc/cpuinfo.
|
||||
* Use a MIDR-based heuristic to whitelist processors known to support it:
|
||||
* - Processors with Cortex-A55 cores
|
||||
* - Processors with Cortex-A65 cores
|
||||
* - Processors with Cortex-A75 cores
|
||||
* - Processors with Cortex-A76 cores
|
||||
* - Processors with Cortex-A77 cores
|
||||
* - Processors with Exynos M4 cores
|
||||
* - Processors with Exynos M5 cores
|
||||
* - Neoverse N1 cores
|
||||
*/
|
||||
if (chipset->series == cpuinfo_arm_chipset_series_samsung_exynos && chipset->model == 9810) {
|
||||
/* Only little cores of Exynos 9810 support FP16 & RDM */
|
||||
cpuinfo_log_warning("FP16 arithmetics and RDM disabled: only little cores in Exynos 9810 support these extensions");
|
||||
} else {
|
||||
switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
|
||||
case UINT32_C(0x4100D050): /* Cortex-A55 */
|
||||
case UINT32_C(0x4100D060): /* Cortex-A65 */
|
||||
case UINT32_C(0x4100D0B0): /* Cortex-A76 */
|
||||
case UINT32_C(0x4100D0C0): /* Neoverse N1 */
|
||||
case UINT32_C(0x4100D0D0): /* Cortex-A77 */
|
||||
case UINT32_C(0x4100D0E0): /* Cortex-A76AE */
|
||||
case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */
|
||||
case UINT32_C(0x51008020): /* Kryo 385 Gold (Cortex-A75) */
|
||||
case UINT32_C(0x51008030): /* Kryo 385 Silver (Cortex-A55) */
|
||||
case UINT32_C(0x51008040): /* Kryo 485 Gold (Cortex-A76) */
|
||||
case UINT32_C(0x51008050): /* Kryo 485 Silver (Cortex-A55) */
|
||||
case UINT32_C(0x53000030): /* Exynos M4 */
|
||||
case UINT32_C(0x53000040): /* Exynos M5 */
|
||||
isa->fp16arith = true;
|
||||
isa->rdm = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* NEON VDOT instructions are not indicated in /proc/cpuinfo.
|
||||
* Use a MIDR-based heuristic to whitelist processors known to support it.
|
||||
*/
|
||||
switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
|
||||
case UINT32_C(0x4100D0B0): /* Cortex-A76 */
|
||||
case UINT32_C(0x4100D0D0): /* Cortex-A77 */
|
||||
case UINT32_C(0x4100D0E0): /* Cortex-A76AE */
|
||||
case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */
|
||||
case UINT32_C(0x51008040): /* Kryo 485 Gold (Cortex-A76) */
|
||||
case UINT32_C(0x51008050): /* Kryo 485 Silver (Cortex-A55) */
|
||||
case UINT32_C(0x53000030): /* Exynos-M4 */
|
||||
case UINT32_C(0x53000040): /* Exynos-M5 */
|
||||
isa->dot = true;
|
||||
break;
|
||||
case UINT32_C(0x4100D050): /* Cortex A55: revision 1 or later only */
|
||||
isa->dot = !!(midr_get_variant(midr) >= 1);
|
||||
break;
|
||||
case UINT32_C(0x4100D0A0): /* Cortex A75: revision 2 or later only */
|
||||
isa->dot = !!(midr_get_variant(midr) >= 2);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* ARMv7 or lower: use feature flags to detect optional features */
|
||||
|
||||
/*
|
||||
* ARM11 (ARM 1136/1156/1176/11 MPCore) processors can report v7 architecture
|
||||
* even though they support only ARMv6 instruction set.
|
||||
*/
|
||||
if (architecture_version == 7 && midr_is_arm11(midr)) {
|
||||
cpuinfo_log_warning("kernel-reported architecture ARMv7 ignored due to mismatch with processor microarchitecture (ARM11)");
|
||||
architecture_version = 6;
|
||||
}
|
||||
|
||||
if (architecture_version < 7) {
|
||||
const uint32_t armv7_features_mask = CPUINFO_ARM_LINUX_FEATURE_VFPV3 | CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | CPUINFO_ARM_LINUX_FEATURE_VFPD32 |
|
||||
CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON | CPUINFO_ARM_LINUX_FEATURE_IDIVT | CPUINFO_ARM_LINUX_FEATURE_IDIVA;
|
||||
if (features & armv7_features_mask) {
|
||||
architecture_version = 7;
|
||||
}
|
||||
}
|
||||
if ((architecture_version >= 6) || (features & CPUINFO_ARM_LINUX_FEATURE_EDSP) || (architecture_flags & CPUINFO_ARM_LINUX_ARCH_E)) {
|
||||
isa->armv5e = true;
|
||||
}
|
||||
if (architecture_version >= 6) {
|
||||
isa->armv6 = true;
|
||||
}
|
||||
if (architecture_version >= 7) {
|
||||
isa->armv6k = true;
|
||||
isa->armv7 = true;
|
||||
|
||||
/*
|
||||
* ARMv7 MP extension (PLDW instruction) is not indicated in /proc/cpuinfo.
|
||||
* Use heuristic list of supporting processors:
|
||||
* - Processors supporting UDIV/SDIV instructions ("idiva" + "idivt" features in /proc/cpuinfo)
|
||||
* - Cortex-A5
|
||||
* - Cortex-A9
|
||||
* - Dual-Core Scorpion
|
||||
* - Krait (supports UDIV/SDIV, but kernels may not report it in /proc/cpuinfo)
|
||||
*
|
||||
* TODO: check single-core Qualcomm Scorpion.
|
||||
*/
|
||||
switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
|
||||
case UINT32_C(0x4100C050): /* Cortex-A5 */
|
||||
case UINT32_C(0x4100C090): /* Cortex-A9 */
|
||||
case UINT32_C(0x510002D0): /* Scorpion (dual-core) */
|
||||
case UINT32_C(0x510004D0): /* Krait (dual-core) */
|
||||
case UINT32_C(0x510006F0): /* Krait (quad-core) */
|
||||
isa->armv7mp = true;
|
||||
break;
|
||||
default:
|
||||
/* In practice IDIV instruction implies ARMv7+MP ISA */
|
||||
isa->armv7mp = (features & CPUINFO_ARM_LINUX_FEATURE_IDIV) == CPUINFO_ARM_LINUX_FEATURE_IDIV;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (features & CPUINFO_ARM_LINUX_FEATURE_IWMMXT) {
|
||||
const uint32_t wcid = read_wcid();
|
||||
cpuinfo_log_debug("WCID = 0x%08"PRIx32, wcid);
|
||||
const uint32_t coprocessor_type = (wcid >> 8) & UINT32_C(0xFF);
|
||||
if (coprocessor_type >= 0x10) {
|
||||
isa->wmmx = true;
|
||||
if (coprocessor_type >= 0x20) {
|
||||
isa->wmmx2 = true;
|
||||
}
|
||||
} else {
|
||||
cpuinfo_log_warning("WMMX ISA disabled: OS reported iwmmxt feature, "
|
||||
"but WCID coprocessor type 0x%"PRIx32" indicates no WMMX support",
|
||||
coprocessor_type);
|
||||
}
|
||||
}
|
||||
|
||||
if ((features & CPUINFO_ARM_LINUX_FEATURE_THUMB) || (architecture_flags & CPUINFO_ARM_LINUX_ARCH_T)) {
|
||||
isa->thumb = true;
|
||||
|
||||
/*
|
||||
* There is no separate feature flag for Thumb 2.
|
||||
* All ARMv7 processors and ARM 1156 support Thumb 2.
|
||||
*/
|
||||
if (architecture_version >= 7 || midr_is_arm1156(midr)) {
|
||||
isa->thumb2 = true;
|
||||
}
|
||||
}
|
||||
if (features & CPUINFO_ARM_LINUX_FEATURE_THUMBEE) {
|
||||
isa->thumbee = true;
|
||||
}
|
||||
if ((features & CPUINFO_ARM_LINUX_FEATURE_JAVA) || (architecture_flags & CPUINFO_ARM_LINUX_ARCH_J)) {
|
||||
isa->jazelle = true;
|
||||
}
|
||||
|
||||
/* Qualcomm Krait may have buggy kernel configuration that doesn't report IDIV */
|
||||
if ((features & CPUINFO_ARM_LINUX_FEATURE_IDIV) == CPUINFO_ARM_LINUX_FEATURE_IDIV || midr_is_krait(midr)) {
|
||||
isa->idiv = true;
|
||||
}
|
||||
|
||||
const uint32_t vfp_mask = \
|
||||
CPUINFO_ARM_LINUX_FEATURE_VFP | CPUINFO_ARM_LINUX_FEATURE_VFPV3 | CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | \
|
||||
CPUINFO_ARM_LINUX_FEATURE_VFPD32 | CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON;
|
||||
if (features & vfp_mask) {
|
||||
const uint32_t vfpv3_mask = CPUINFO_ARM_LINUX_FEATURE_VFPV3 | CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | \
|
||||
CPUINFO_ARM_LINUX_FEATURE_VFPD32 | CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON;
|
||||
if ((architecture_version >= 7) || (features & vfpv3_mask)) {
|
||||
isa->vfpv3 = true;
|
||||
|
||||
const uint32_t d32_mask = CPUINFO_ARM_LINUX_FEATURE_VFPD32 | CPUINFO_ARM_LINUX_FEATURE_NEON;
|
||||
if (features & d32_mask) {
|
||||
isa->d32 = true;
|
||||
}
|
||||
} else {
|
||||
#if defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_8A__) || defined(__ARM_ARCH) && (__ARM_ARCH >= 7)
|
||||
isa->vfpv3 = true;
|
||||
#else
|
||||
const uint32_t fpsid = read_fpsid();
|
||||
cpuinfo_log_debug("FPSID = 0x%08"PRIx32, fpsid);
|
||||
const uint32_t subarchitecture = (fpsid >> 16) & UINT32_C(0x7F);
|
||||
if (subarchitecture >= 0x01) {
|
||||
isa->vfpv2 = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (features & CPUINFO_ARM_LINUX_FEATURE_NEON) {
|
||||
isa->neon = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* There is no separate feature flag for FP16 support.
|
||||
* VFPv4 implies VFPv3-FP16 support (and in practice, NEON-HP as well).
|
||||
* Additionally, ARM Cortex-A9 and Qualcomm Scorpion support FP16.
|
||||
*/
|
||||
if ((features & CPUINFO_ARM_LINUX_FEATURE_VFPV4) || midr_is_cortex_a9(midr) || midr_is_scorpion(midr)) {
|
||||
isa->fp16 = true;
|
||||
}
|
||||
|
||||
if (features & CPUINFO_ARM_LINUX_FEATURE_VFPV4) {
|
||||
isa->fma = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_AES) {
|
||||
isa->aes = true;
|
||||
}
|
||||
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_PMULL) {
|
||||
isa->pmull = true;
|
||||
}
|
||||
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SHA1) {
|
||||
isa->sha1 = true;
|
||||
}
|
||||
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SHA2) {
|
||||
isa->sha2 = true;
|
||||
}
|
||||
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_CRC32) {
|
||||
isa->crc32 = true;
|
||||
}
|
||||
}
|
127
dep/cpuinfo/src/arm/linux/aarch64-isa.c
Normal file
127
dep/cpuinfo/src/arm/linux/aarch64-isa.c
Normal file
@ -0,0 +1,127 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <arm/linux/api.h>
|
||||
#include <cpuinfo/log.h>
|
||||
|
||||
|
||||
void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
|
||||
uint32_t features,
|
||||
uint32_t features2,
|
||||
uint32_t midr,
|
||||
const struct cpuinfo_arm_chipset chipset[restrict static 1],
|
||||
struct cpuinfo_arm_isa isa[restrict static 1])
|
||||
{
|
||||
if (features & CPUINFO_ARM_LINUX_FEATURE_AES) {
|
||||
isa->aes = true;
|
||||
}
|
||||
if (features & CPUINFO_ARM_LINUX_FEATURE_PMULL) {
|
||||
isa->pmull = true;
|
||||
}
|
||||
if (features & CPUINFO_ARM_LINUX_FEATURE_SHA1) {
|
||||
isa->sha1 = true;
|
||||
}
|
||||
if (features & CPUINFO_ARM_LINUX_FEATURE_SHA2) {
|
||||
isa->sha2 = true;
|
||||
}
|
||||
if (features & CPUINFO_ARM_LINUX_FEATURE_CRC32) {
|
||||
isa->crc32 = true;
|
||||
}
|
||||
if (features & CPUINFO_ARM_LINUX_FEATURE_ATOMICS) {
|
||||
isa->atomics = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some phones ship with an old kernel configuration that doesn't report NEON FP16 compute extension and SQRDMLAH/SQRDMLSH/UQRDMLAH/UQRDMLSH instructions.
|
||||
* Use a MIDR-based heuristic to whitelist processors known to support it:
|
||||
* - Processors with Cortex-A55 cores
|
||||
* - Processors with Cortex-A65 cores
|
||||
* - Processors with Cortex-A75 cores
|
||||
* - Processors with Cortex-A76 cores
|
||||
* - Processors with Cortex-A77 cores
|
||||
* - Processors with Exynos M4 cores
|
||||
* - Processors with Exynos M5 cores
|
||||
* - Neoverse N1 cores
|
||||
*/
|
||||
if (chipset->series == cpuinfo_arm_chipset_series_samsung_exynos && chipset->model == 9810) {
|
||||
/* Exynos 9810 reports that it supports FP16 compute, but in fact only little cores do */
|
||||
cpuinfo_log_warning("FP16 arithmetics and RDM disabled: only little cores in Exynos 9810 support these extensions");
|
||||
} else {
|
||||
const uint32_t fp16arith_mask = CPUINFO_ARM_LINUX_FEATURE_FPHP | CPUINFO_ARM_LINUX_FEATURE_ASIMDHP;
|
||||
switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
|
||||
case UINT32_C(0x4100D050): /* Cortex-A55 */
|
||||
case UINT32_C(0x4100D060): /* Cortex-A65 */
|
||||
case UINT32_C(0x4100D0B0): /* Cortex-A76 */
|
||||
case UINT32_C(0x4100D0C0): /* Neoverse N1 */
|
||||
case UINT32_C(0x4100D0D0): /* Cortex-A77 */
|
||||
case UINT32_C(0x4100D0E0): /* Cortex-A76AE */
|
||||
case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */
|
||||
case UINT32_C(0x51008020): /* Kryo 385 Gold (Cortex-A75) */
|
||||
case UINT32_C(0x51008030): /* Kryo 385 Silver (Cortex-A55) */
|
||||
case UINT32_C(0x51008040): /* Kryo 485 Gold (Cortex-A76) */
|
||||
case UINT32_C(0x51008050): /* Kryo 485 Silver (Cortex-A55) */
|
||||
case UINT32_C(0x53000030): /* Exynos M4 */
|
||||
case UINT32_C(0x53000040): /* Exynos M5 */
|
||||
isa->fp16arith = true;
|
||||
isa->rdm = true;
|
||||
break;
|
||||
default:
|
||||
if ((features & fp16arith_mask) == fp16arith_mask) {
|
||||
isa->fp16arith = true;
|
||||
} else if (features & CPUINFO_ARM_LINUX_FEATURE_FPHP) {
|
||||
cpuinfo_log_warning("FP16 arithmetics disabled: detected support only for scalar operations");
|
||||
} else if (features & CPUINFO_ARM_LINUX_FEATURE_ASIMDHP) {
|
||||
cpuinfo_log_warning("FP16 arithmetics disabled: detected support only for SIMD operations");
|
||||
}
|
||||
if (features & CPUINFO_ARM_LINUX_FEATURE_ASIMDRDM) {
|
||||
isa->rdm = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Many phones ship with an old kernel configuration that doesn't report UDOT/SDOT instructions.
|
||||
* Use a MIDR-based heuristic to whitelist processors known to support it.
|
||||
*/
|
||||
switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
|
||||
case UINT32_C(0x4100D060): /* Cortex-A65 */
|
||||
case UINT32_C(0x4100D0B0): /* Cortex-A76 */
|
||||
case UINT32_C(0x4100D0C0): /* Neoverse N1 */
|
||||
case UINT32_C(0x4100D0D0): /* Cortex-A77 */
|
||||
case UINT32_C(0x4100D0E0): /* Cortex-A76AE */
|
||||
case UINT32_C(0x4100D4A0): /* Neoverse E1 */
|
||||
case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */
|
||||
case UINT32_C(0x51008040): /* Kryo 485 Gold (Cortex-A76) */
|
||||
case UINT32_C(0x51008050): /* Kryo 485 Silver (Cortex-A55) */
|
||||
case UINT32_C(0x53000030): /* Exynos-M4 */
|
||||
case UINT32_C(0x53000040): /* Exynos-M5 */
|
||||
isa->dot = true;
|
||||
break;
|
||||
case UINT32_C(0x4100D050): /* Cortex A55: revision 1 or later only */
|
||||
isa->dot = !!(midr_get_variant(midr) >= 1);
|
||||
break;
|
||||
case UINT32_C(0x4100D0A0): /* Cortex A75: revision 2 or later only */
|
||||
isa->dot = !!(midr_get_variant(midr) >= 2);
|
||||
break;
|
||||
default:
|
||||
if (features & CPUINFO_ARM_LINUX_FEATURE_ASIMDDP) {
|
||||
isa->dot = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (features & CPUINFO_ARM_LINUX_FEATURE_JSCVT) {
|
||||
isa->jscvt = true;
|
||||
}
|
||||
if (features & CPUINFO_ARM_LINUX_FEATURE_JSCVT) {
|
||||
isa->jscvt = true;
|
||||
}
|
||||
if (features & CPUINFO_ARM_LINUX_FEATURE_FCMA) {
|
||||
isa->fcma = true;
|
||||
}
|
||||
if (features & CPUINFO_ARM_LINUX_FEATURE_SVE) {
|
||||
isa->sve = true;
|
||||
}
|
||||
if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SVE2) {
|
||||
isa->sve2 = true;
|
||||
}
|
||||
}
|
384
dep/cpuinfo/src/arm/linux/api.h
Normal file
384
dep/cpuinfo/src/arm/linux/api.h
Normal file
@ -0,0 +1,384 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cpuinfo.h>
|
||||
#include <cpuinfo/common.h>
|
||||
#include <arm/midr.h>
|
||||
#include <arm/api.h>
|
||||
#include <linux/api.h>
|
||||
|
||||
/* No hard limit in the kernel, maximum length observed on non-rogue kernels is 64 */
|
||||
#define CPUINFO_HARDWARE_VALUE_MAX 64
|
||||
/* No hard limit in the kernel, maximum length on Raspberry Pi is 8. Add 1 symbol to detect overly large revision strings */
|
||||
#define CPUINFO_REVISION_VALUE_MAX 9
|
||||
|
||||
#ifdef __ANDROID__
|
||||
/* As per include/sys/system_properties.h in Android NDK */
|
||||
#define CPUINFO_BUILD_PROP_NAME_MAX 32
|
||||
#define CPUINFO_BUILD_PROP_VALUE_MAX 92
|
||||
|
||||
struct cpuinfo_android_properties {
|
||||
char proc_cpuinfo_hardware[CPUINFO_HARDWARE_VALUE_MAX];
|
||||
char ro_product_board[CPUINFO_BUILD_PROP_VALUE_MAX];
|
||||
char ro_board_platform[CPUINFO_BUILD_PROP_VALUE_MAX];
|
||||
char ro_mediatek_platform[CPUINFO_BUILD_PROP_VALUE_MAX];
|
||||
char ro_arch[CPUINFO_BUILD_PROP_VALUE_MAX];
|
||||
char ro_chipname[CPUINFO_BUILD_PROP_VALUE_MAX];
|
||||
char ro_hardware_chipname[CPUINFO_BUILD_PROP_VALUE_MAX];
|
||||
};
|
||||
#endif
|
||||
|
||||
#define CPUINFO_ARM_LINUX_ARCH_T UINT32_C(0x00000001)
|
||||
#define CPUINFO_ARM_LINUX_ARCH_E UINT32_C(0x00000002)
|
||||
#define CPUINFO_ARM_LINUX_ARCH_J UINT32_C(0x00000004)
|
||||
|
||||
#define CPUINFO_ARM_LINUX_ARCH_TE UINT32_C(0x00000003)
|
||||
#define CPUINFO_ARM_LINUX_ARCH_TEJ UINT32_C(0x00000007)
|
||||
|
||||
struct cpuinfo_arm_linux_proc_cpuinfo_cache {
|
||||
uint32_t i_size;
|
||||
uint32_t i_assoc;
|
||||
uint32_t i_line_length;
|
||||
uint32_t i_sets;
|
||||
uint32_t d_size;
|
||||
uint32_t d_assoc;
|
||||
uint32_t d_line_length;
|
||||
uint32_t d_sets;
|
||||
};
|
||||
|
||||
#if CPUINFO_ARCH_ARM
|
||||
/* arch/arm/include/uapi/asm/hwcap.h */
|
||||
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_SWP UINT32_C(0x00000001)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_HALF UINT32_C(0x00000002)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_THUMB UINT32_C(0x00000004)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_26BIT UINT32_C(0x00000008)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_FASTMULT UINT32_C(0x00000010)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_FPA UINT32_C(0x00000020)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_VFP UINT32_C(0x00000040)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_EDSP UINT32_C(0x00000080)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_JAVA UINT32_C(0x00000100)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_IWMMXT UINT32_C(0x00000200)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_CRUNCH UINT32_C(0x00000400)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_THUMBEE UINT32_C(0x00000800)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_NEON UINT32_C(0x00001000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_VFPV3 UINT32_C(0x00002000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 UINT32_C(0x00004000) /* Also set for VFPv4 with 16 double-precision registers */
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_TLS UINT32_C(0x00008000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_VFPV4 UINT32_C(0x00010000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_IDIVA UINT32_C(0x00020000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_IDIVT UINT32_C(0x00040000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_IDIV UINT32_C(0x00060000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_VFPD32 UINT32_C(0x00080000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_LPAE UINT32_C(0x00100000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_EVTSTRM UINT32_C(0x00200000)
|
||||
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_AES UINT32_C(0x00000001)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_PMULL UINT32_C(0x00000002)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_SHA1 UINT32_C(0x00000004)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_SHA2 UINT32_C(0x00000008)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_CRC32 UINT32_C(0x00000010)
|
||||
#elif CPUINFO_ARCH_ARM64
|
||||
/* arch/arm64/include/uapi/asm/hwcap.h */
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_FP UINT32_C(0x00000001)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_ASIMD UINT32_C(0x00000002)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_EVTSTRM UINT32_C(0x00000004)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_AES UINT32_C(0x00000008)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_PMULL UINT32_C(0x00000010)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_SHA1 UINT32_C(0x00000020)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_SHA2 UINT32_C(0x00000040)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_CRC32 UINT32_C(0x00000080)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_ATOMICS UINT32_C(0x00000100)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_FPHP UINT32_C(0x00000200)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_ASIMDHP UINT32_C(0x00000400)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_CPUID UINT32_C(0x00000800)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_ASIMDRDM UINT32_C(0x00001000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_JSCVT UINT32_C(0x00002000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_FCMA UINT32_C(0x00004000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_LRCPC UINT32_C(0x00008000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_DCPOP UINT32_C(0x00010000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_SHA3 UINT32_C(0x00020000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_SM3 UINT32_C(0x00040000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_SM4 UINT32_C(0x00080000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_ASIMDDP UINT32_C(0x00100000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_SHA512 UINT32_C(0x00200000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_SVE UINT32_C(0x00400000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_ASIMDFHM UINT32_C(0x00800000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_DIT UINT32_C(0x01000000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_USCAT UINT32_C(0x02000000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_ILRCPC UINT32_C(0x04000000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_FLAGM UINT32_C(0x08000000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_SSBS UINT32_C(0x10000000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_SB UINT32_C(0x20000000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_PACA UINT32_C(0x40000000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE_PACG UINT32_C(0x80000000)
|
||||
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_DCPODP UINT32_C(0x00000001)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_SVE2 UINT32_C(0x00000002)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_SVEAES UINT32_C(0x00000004)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_SVEPMULL UINT32_C(0x00000008)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_SVEBITPERM UINT32_C(0x00000010)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_SVESHA3 UINT32_C(0x00000020)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_SVESM4 UINT32_C(0x00000040)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_FLAGM2 UINT32_C(0x00000080)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_FRINT UINT32_C(0x00000100)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_SVEI8MM UINT32_C(0x00000200)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_SVEF32MM UINT32_C(0x00000400)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_SVEF64MM UINT32_C(0x00000800)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_SVEBF16 UINT32_C(0x00001000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_I8MM UINT32_C(0x00002000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_BF16 UINT32_C(0x00004000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_DGH UINT32_C(0x00008000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_RNG UINT32_C(0x00010000)
|
||||
#define CPUINFO_ARM_LINUX_FEATURE2_BTI UINT32_C(0x00020000)
|
||||
#endif
|
||||
|
||||
#define CPUINFO_ARM_LINUX_VALID_ARCHITECTURE UINT32_C(0x00010000)
|
||||
#define CPUINFO_ARM_LINUX_VALID_IMPLEMENTER UINT32_C(0x00020000)
|
||||
#define CPUINFO_ARM_LINUX_VALID_VARIANT UINT32_C(0x00040000)
|
||||
#define CPUINFO_ARM_LINUX_VALID_PART UINT32_C(0x00080000)
|
||||
#define CPUINFO_ARM_LINUX_VALID_REVISION UINT32_C(0x00100000)
|
||||
#define CPUINFO_ARM_LINUX_VALID_PROCESSOR UINT32_C(0x00200000)
|
||||
#define CPUINFO_ARM_LINUX_VALID_FEATURES UINT32_C(0x00400000)
|
||||
#if CPUINFO_ARCH_ARM
|
||||
#define CPUINFO_ARM_LINUX_VALID_ICACHE_SIZE UINT32_C(0x01000000)
|
||||
#define CPUINFO_ARM_LINUX_VALID_ICACHE_SETS UINT32_C(0x02000000)
|
||||
#define CPUINFO_ARM_LINUX_VALID_ICACHE_WAYS UINT32_C(0x04000000)
|
||||
#define CPUINFO_ARM_LINUX_VALID_ICACHE_LINE UINT32_C(0x08000000)
|
||||
#define CPUINFO_ARM_LINUX_VALID_DCACHE_SIZE UINT32_C(0x10000000)
|
||||
#define CPUINFO_ARM_LINUX_VALID_DCACHE_SETS UINT32_C(0x20000000)
|
||||
#define CPUINFO_ARM_LINUX_VALID_DCACHE_WAYS UINT32_C(0x40000000)
|
||||
#define CPUINFO_ARM_LINUX_VALID_DCACHE_LINE UINT32_C(0x80000000)
|
||||
#endif
|
||||
|
||||
#define CPUINFO_ARM_LINUX_VALID_INFO UINT32_C(0x007F0000)
|
||||
#define CPUINFO_ARM_LINUX_VALID_MIDR UINT32_C(0x003F0000)
|
||||
#if CPUINFO_ARCH_ARM
|
||||
#define CPUINFO_ARM_LINUX_VALID_ICACHE UINT32_C(0x0F000000)
|
||||
#define CPUINFO_ARM_LINUX_VALID_DCACHE UINT32_C(0xF0000000)
|
||||
#define CPUINFO_ARM_LINUX_VALID_CACHE_LINE UINT32_C(0x88000000)
|
||||
#endif
|
||||
|
||||
struct cpuinfo_arm_linux_processor {
|
||||
uint32_t architecture_version;
|
||||
#if CPUINFO_ARCH_ARM
|
||||
uint32_t architecture_flags;
|
||||
struct cpuinfo_arm_linux_proc_cpuinfo_cache proc_cpuinfo_cache;
|
||||
#endif
|
||||
uint32_t features;
|
||||
uint32_t features2;
|
||||
/**
|
||||
* Main ID Register value.
|
||||
*/
|
||||
uint32_t midr;
|
||||
enum cpuinfo_vendor vendor;
|
||||
enum cpuinfo_uarch uarch;
|
||||
uint32_t uarch_index;
|
||||
/**
|
||||
* ID of the physical package which includes this logical processor.
|
||||
* The value is parsed from /sys/devices/system/cpu/cpu<N>/topology/physical_package_id
|
||||
*/
|
||||
uint32_t package_id;
|
||||
/**
|
||||
* Minimum processor ID on the package 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 package_leader_id;
|
||||
/**
|
||||
* Number of logical processors in the package.
|
||||
*/
|
||||
uint32_t package_processor_count;
|
||||
/**
|
||||
* Maximum frequency, in kHZ.
|
||||
* The value is parsed from /sys/devices/system/cpu/cpu<N>/cpufreq/cpuinfo_max_freq
|
||||
* If failed to read or parse the file, the value is 0.
|
||||
*/
|
||||
uint32_t max_frequency;
|
||||
/**
|
||||
* Minimum frequency, in kHZ.
|
||||
* The value is parsed from /sys/devices/system/cpu/cpu<N>/cpufreq/cpuinfo_min_freq
|
||||
* If failed to read or parse the file, the value is 0.
|
||||
*/
|
||||
uint32_t min_frequency;
|
||||
/** Linux processor ID */
|
||||
uint32_t system_processor_id;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
struct cpuinfo_arm_linux_cluster {
|
||||
uint32_t processor_id_min;
|
||||
uint32_t processor_id_max;
|
||||
};
|
||||
|
||||
/* Returns true if the two processors do belong to the same cluster */
|
||||
static inline bool cpuinfo_arm_linux_processor_equals(
|
||||
struct cpuinfo_arm_linux_processor processor_i[restrict static 1],
|
||||
struct cpuinfo_arm_linux_processor processor_j[restrict static 1])
|
||||
{
|
||||
const uint32_t joint_flags = processor_i->flags & processor_j->flags;
|
||||
|
||||
bool same_max_frequency = false;
|
||||
if (joint_flags & CPUINFO_LINUX_FLAG_MAX_FREQUENCY) {
|
||||
if (processor_i->max_frequency != processor_j->max_frequency) {
|
||||
return false;
|
||||
} else {
|
||||
same_max_frequency = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool same_min_frequency = false;
|
||||
if (joint_flags & CPUINFO_LINUX_FLAG_MIN_FREQUENCY) {
|
||||
if (processor_i->min_frequency != processor_j->min_frequency) {
|
||||
return false;
|
||||
} else {
|
||||
same_min_frequency = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((joint_flags & CPUINFO_ARM_LINUX_VALID_MIDR) == CPUINFO_ARM_LINUX_VALID_MIDR) {
|
||||
if (processor_i->midr == processor_j->midr) {
|
||||
if (midr_is_cortex_a53(processor_i->midr)) {
|
||||
return same_min_frequency & same_max_frequency;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return same_max_frequency && same_min_frequency;
|
||||
}
|
||||
|
||||
/* Returns true if the two processors certainly don't belong to the same cluster */
|
||||
static inline bool cpuinfo_arm_linux_processor_not_equals(
|
||||
struct cpuinfo_arm_linux_processor processor_i[restrict static 1],
|
||||
struct cpuinfo_arm_linux_processor processor_j[restrict static 1])
|
||||
{
|
||||
const uint32_t joint_flags = processor_i->flags & processor_j->flags;
|
||||
|
||||
if (joint_flags & CPUINFO_LINUX_FLAG_MAX_FREQUENCY) {
|
||||
if (processor_i->max_frequency != processor_j->max_frequency) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (joint_flags & CPUINFO_LINUX_FLAG_MIN_FREQUENCY) {
|
||||
if (processor_i->min_frequency != processor_j->min_frequency) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((joint_flags & CPUINFO_ARM_LINUX_VALID_MIDR) == CPUINFO_ARM_LINUX_VALID_MIDR) {
|
||||
if (processor_i->midr != processor_j->midr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CPUINFO_INTERNAL bool cpuinfo_arm_linux_parse_proc_cpuinfo(
|
||||
char hardware[restrict static CPUINFO_HARDWARE_VALUE_MAX],
|
||||
char revision[restrict static CPUINFO_REVISION_VALUE_MAX],
|
||||
uint32_t max_processors_count,
|
||||
struct cpuinfo_arm_linux_processor processors[restrict static max_processors_count]);
|
||||
|
||||
#if CPUINFO_ARCH_ARM
|
||||
CPUINFO_INTERNAL bool cpuinfo_arm_linux_hwcap_from_getauxval(
|
||||
uint32_t hwcap[restrict static 1],
|
||||
uint32_t hwcap2[restrict static 1]);
|
||||
CPUINFO_INTERNAL bool cpuinfo_arm_linux_hwcap_from_procfs(
|
||||
uint32_t hwcap[restrict static 1],
|
||||
uint32_t hwcap2[restrict static 1]);
|
||||
|
||||
CPUINFO_INTERNAL void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
|
||||
uint32_t features,
|
||||
uint32_t features2,
|
||||
uint32_t midr,
|
||||
uint32_t architecture_version,
|
||||
uint32_t architecture_flags,
|
||||
const struct cpuinfo_arm_chipset chipset[restrict static 1],
|
||||
struct cpuinfo_arm_isa isa[restrict static 1]);
|
||||
#elif CPUINFO_ARCH_ARM64
|
||||
CPUINFO_INTERNAL void cpuinfo_arm_linux_hwcap_from_getauxval(
|
||||
uint32_t hwcap[restrict static 1],
|
||||
uint32_t hwcap2[restrict static 1]);
|
||||
|
||||
CPUINFO_INTERNAL void cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
|
||||
uint32_t features,
|
||||
uint32_t features2,
|
||||
uint32_t midr,
|
||||
const struct cpuinfo_arm_chipset chipset[restrict static 1],
|
||||
struct cpuinfo_arm_isa isa[restrict static 1]);
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
CPUINFO_INTERNAL struct cpuinfo_arm_chipset
|
||||
cpuinfo_arm_android_decode_chipset(
|
||||
const struct cpuinfo_android_properties properties[restrict static 1],
|
||||
uint32_t cores,
|
||||
uint32_t max_cpu_freq_max);
|
||||
#else
|
||||
CPUINFO_INTERNAL struct cpuinfo_arm_chipset
|
||||
cpuinfo_arm_linux_decode_chipset(
|
||||
const char hardware[restrict static CPUINFO_HARDWARE_VALUE_MAX],
|
||||
const char revision[restrict static CPUINFO_REVISION_VALUE_MAX],
|
||||
uint32_t cores,
|
||||
uint32_t max_cpu_freq_max);
|
||||
#endif
|
||||
|
||||
CPUINFO_INTERNAL struct cpuinfo_arm_chipset
|
||||
cpuinfo_arm_linux_decode_chipset_from_proc_cpuinfo_hardware(
|
||||
const char proc_cpuinfo_hardware[restrict static CPUINFO_HARDWARE_VALUE_MAX],
|
||||
uint32_t cores, uint32_t max_cpu_freq_max, bool is_tegra);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
CPUINFO_INTERNAL struct cpuinfo_arm_chipset
|
||||
cpuinfo_arm_android_decode_chipset_from_ro_product_board(
|
||||
const char ro_product_board[restrict static CPUINFO_BUILD_PROP_VALUE_MAX],
|
||||
uint32_t cores, uint32_t max_cpu_freq_max);
|
||||
CPUINFO_INTERNAL struct cpuinfo_arm_chipset
|
||||
cpuinfo_arm_android_decode_chipset_from_ro_board_platform(
|
||||
const char ro_board_platform[restrict static CPUINFO_BUILD_PROP_VALUE_MAX],
|
||||
uint32_t cores, uint32_t max_cpu_freq_max);
|
||||
CPUINFO_INTERNAL struct cpuinfo_arm_chipset
|
||||
cpuinfo_arm_android_decode_chipset_from_ro_mediatek_platform(
|
||||
const char ro_mediatek_platform[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]);
|
||||
CPUINFO_INTERNAL struct cpuinfo_arm_chipset
|
||||
cpuinfo_arm_android_decode_chipset_from_ro_arch(
|
||||
const char ro_arch[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]);
|
||||
CPUINFO_INTERNAL struct cpuinfo_arm_chipset
|
||||
cpuinfo_arm_android_decode_chipset_from_ro_chipname(
|
||||
const char ro_chipname[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]);
|
||||
CPUINFO_INTERNAL struct cpuinfo_arm_chipset
|
||||
cpuinfo_arm_android_decode_chipset_from_ro_hardware_chipname(
|
||||
const char ro_hardware_chipname[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]);
|
||||
#else
|
||||
CPUINFO_INTERNAL struct cpuinfo_arm_chipset
|
||||
cpuinfo_arm_linux_decode_chipset_from_proc_cpuinfo_revision(
|
||||
const char proc_cpuinfo_revision[restrict static CPUINFO_REVISION_VALUE_MAX]);
|
||||
#endif
|
||||
|
||||
CPUINFO_INTERNAL 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]);
|
||||
|
||||
CPUINFO_INTERNAL void cpuinfo_arm_linux_detect_core_clusters_by_sequential_scan(
|
||||
uint32_t max_processors,
|
||||
struct cpuinfo_arm_linux_processor processors[restrict static max_processors]);
|
||||
|
||||
CPUINFO_INTERNAL void cpuinfo_arm_linux_count_cluster_processors(
|
||||
uint32_t max_processors,
|
||||
struct cpuinfo_arm_linux_processor processors[restrict static max_processors]);
|
||||
|
||||
CPUINFO_INTERNAL 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]);
|
||||
|
||||
extern CPUINFO_INTERNAL const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map;
|
||||
extern CPUINFO_INTERNAL uint32_t cpuinfo_linux_cpu_to_uarch_index_map_entries;
|
3860
dep/cpuinfo/src/arm/linux/chipset.c
Normal file
3860
dep/cpuinfo/src/arm/linux/chipset.c
Normal file
File diff suppressed because it is too large
Load Diff
493
dep/cpuinfo/src/arm/linux/clusters.c
Normal file
493
dep/cpuinfo/src/arm/linux/clusters.c
Normal file
@ -0,0 +1,493 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assigns logical processors to clusters of cores using heuristic based on the typical configuration of clusters for
|
||||
* 5, 6, 8, and 10 cores:
|
||||
* - 5 cores (ARM32 Android only): 2 clusters of 4+1 cores
|
||||
* - 6 cores: 2 clusters of 4+2 cores
|
||||
* - 8 cores: 2 clusters of 4+4 cores
|
||||
* - 10 cores: 3 clusters of 4+4+2 cores
|
||||
*
|
||||
* The function must be called after parsing OS-provided information on core clusters.
|
||||
* Its purpose is to detect clusters of cores when OS-provided information is lacking or incomplete, i.e.
|
||||
* - Linux kernel is not configured to report information in sysfs topology leaf.
|
||||
* - Linux kernel reports topology information only for online cores, and only cores on one cluster are online, e.g.:
|
||||
* - Exynos 8890 has 8 cores in 4+4 clusters, but only the first cluster of 4 cores is reported, and cluster
|
||||
* configuration of logical processors 4-7 is not reported (all remaining processors 4-7 form cluster 1)
|
||||
* - MT6797 has 10 cores in 4+4+2, but only the first cluster of 4 cores is reported, and cluster configuration
|
||||
* of logical processors 4-9 is not reported (processors 4-7 form cluster 1, and processors 8-9 form cluster 2).
|
||||
*
|
||||
* Heuristic assignment of processors to the above pre-defined clusters fails if such assignment would contradict
|
||||
* information provided by the operating system:
|
||||
* - Any of the OS-reported processor clusters is different than the corresponding heuristic cluster.
|
||||
* - Processors in a heuristic cluster have no OS-provided cluster siblings information, but have known and different
|
||||
* minimum/maximum frequency.
|
||||
* - Processors in a heuristic cluster have no OS-provided cluster siblings information, but have known and different
|
||||
* MIDR components.
|
||||
*
|
||||
* If the heuristic assignment of processors to clusters of cores fails, all processors' clusters are unchanged.
|
||||
*
|
||||
* @param usable_processors - number of processors in the @p processors array with CPUINFO_LINUX_FLAG_VALID flags.
|
||||
* @param max_processors - number of elements in the @p processors array.
|
||||
* @param[in,out] processors - processor descriptors with pre-parsed POSSIBLE and PRESENT flags, minimum/maximum
|
||||
* frequency, MIDR infromation, and core cluster (package siblings list) information.
|
||||
*
|
||||
* @retval true if the heuristic successfully assigned all processors into clusters of cores.
|
||||
* @retval false if known details about processors contradict the heuristic configuration of core clusters.
|
||||
*/
|
||||
bool cpuinfo_arm_linux_detect_core_clusters_by_heuristic(
|
||||
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:
|
||||
/*
|
||||
* The only processor with 5 cores is Leadcore L1860C (ARMv7, mobile),
|
||||
* but this configuration is not too unreasonable for a virtualized ARM server.
|
||||
*/
|
||||
cluster_processors[0] = 4;
|
||||
cluster_processors[1] = 1;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assignment of processors to core clusters is done in two passes:
|
||||
* 1. Verify that the clusters proposed by heuristic are compatible with known details about processors.
|
||||
* 2. If verification passed, update core clusters for the processors.
|
||||
*/
|
||||
|
||||
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) {
|
||||
/* Expect this processor to start a new cluster */
|
||||
|
||||
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 {
|
||||
/* Expect this processor to belong to the same cluster as processor */
|
||||
|
||||
if (expected_cluster_exists) {
|
||||
/*
|
||||
* The cluster suggested by the heuristic was already parsed from system siblings lists.
|
||||
* For all processors we expect in the cluster, check that:
|
||||
* - They have pre-assigned cluster from siblings lists (CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER flag).
|
||||
* - They were assigned to the same cluster based on siblings lists
|
||||
* (package_leader_id points to the first processor in the cluster).
|
||||
*/
|
||||
|
||||
if ((processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER) == 0) {
|
||||
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 {
|
||||
/*
|
||||
* The cluster suggest by the heuristic was not parsed from system siblings lists.
|
||||
* For all processors we expect in the cluster, check that:
|
||||
* - They have no pre-assigned cluster from siblings lists.
|
||||
* - If their min/max CPU frequency is known, it is the same.
|
||||
* - If any part of their MIDR (Implementer, Variant, Part, Revision) is known, it is the same.
|
||||
*/
|
||||
|
||||
if (processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER) {
|
||||
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--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Verification passed, assign all processors to new clusters */
|
||||
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) {
|
||||
/* Expect this processor to start a new cluster */
|
||||
|
||||
cluster_start = i;
|
||||
expected_cluster_processors = cluster_processors[cluster++];
|
||||
} else {
|
||||
/* Expect this processor to belong to the same cluster as processor */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assigns logical processors to clusters of cores in sequential manner:
|
||||
* - Clusters detected from OS-provided information are unchanged:
|
||||
* - Processors assigned to these clusters stay assigned to the same clusters
|
||||
* - No new processors are added to these clusters
|
||||
* - Processors without pre-assigned cluster are clustered in one sequential scan:
|
||||
* - If known details (min/max frequency, MIDR components) of a processor are compatible with a preceeding
|
||||
* processor, without pre-assigned cluster, the processor is assigned to the cluster of the preceeding processor.
|
||||
* - If known details (min/max frequency, MIDR components) of a processor are not compatible with a preceeding
|
||||
* processor, the processor is assigned to a newly created cluster.
|
||||
*
|
||||
* The function must be called after parsing OS-provided information on core clusters, and usually is called only
|
||||
* if heuristic assignment of processors to clusters (cpuinfo_arm_linux_cluster_processors_by_heuristic) failed.
|
||||
*
|
||||
* Its purpose is to detect clusters of cores when OS-provided information is lacking or incomplete, i.e.
|
||||
* - Linux kernel is not configured to report information in sysfs topology leaf.
|
||||
* - Linux kernel reports topology information only for online cores, and all cores on some of the clusters are offline.
|
||||
*
|
||||
* Sequential assignment of processors to clusters always succeeds, and upon exit, all usable processors in the
|
||||
* @p processors array have cluster information.
|
||||
*
|
||||
* @param max_processors - number of elements in the @p processors array.
|
||||
* @param[in,out] processors - processor descriptors with pre-parsed POSSIBLE and PRESENT flags, minimum/maximum
|
||||
* frequency, MIDR infromation, and core cluster (package siblings list) information.
|
||||
*
|
||||
* @retval true if the heuristic successfully assigned all processors into clusters of cores.
|
||||
* @retval false if known details about processors contradict the heuristic configuration of core clusters.
|
||||
*/
|
||||
void cpuinfo_arm_linux_detect_core_clusters_by_sequential_scan(
|
||||
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 preceeding 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 preceeding 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 preceeding 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* All checks passed, attach processor to the preceeding cluster */
|
||||
cluster_processors++;
|
||||
processors[i].package_leader_id = cluster_start;
|
||||
processors[i].flags |= CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER;
|
||||
cpuinfo_log_debug("assigned processor %"PRIu32" to preceeding cluster of processor %"PRIu32, i, cluster_start);
|
||||
continue;
|
||||
|
||||
new_cluster:
|
||||
/* Create a new cluster starting with processor i */
|
||||
cluster_start = i;
|
||||
processors[i].package_leader_id = i;
|
||||
processors[i].flags |= CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER;
|
||||
cluster_processors = 1;
|
||||
|
||||
/* Copy known information from processor to cluster, and set the flags accordingly */
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Counts the number of logical processors in each core cluster.
|
||||
* This function should be called after all processors are assigned to core clusters.
|
||||
*
|
||||
* @param max_processors - number of elements in the @p processors array.
|
||||
* @param[in,out] processors - processor descriptors with pre-parsed POSSIBLE and PRESENT flags,
|
||||
* and decoded core cluster (package_leader_id) information.
|
||||
* The function expects the value of processors[i].package_processor_count to be zero.
|
||||
* Upon return, processors[i].package_processor_count will contain the number of logical
|
||||
* processors in the respective core cluster.
|
||||
*/
|
||||
void cpuinfo_arm_linux_count_cluster_processors(
|
||||
uint32_t max_processors,
|
||||
struct cpuinfo_arm_linux_processor processors[restrict static max_processors])
|
||||
{
|
||||
/* First pass: accumulate the number of processors at the group leader's package_processor_count */
|
||||
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;
|
||||
}
|
||||
}
|
||||
/* Second pass: copy the package_processor_count from the group leader processor */
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
44
dep/cpuinfo/src/arm/linux/cp.h
Normal file
44
dep/cpuinfo/src/arm/linux/cp.h
Normal file
@ -0,0 +1,44 @@
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#if CPUINFO_MOCK
|
||||
extern uint32_t cpuinfo_arm_fpsid;
|
||||
extern uint32_t cpuinfo_arm_mvfr0;
|
||||
extern uint32_t cpuinfo_arm_wcid;
|
||||
|
||||
static inline uint32_t read_fpsid(void) {
|
||||
return cpuinfo_arm_fpsid;
|
||||
}
|
||||
|
||||
static inline uint32_t read_mvfr0(void) {
|
||||
return cpuinfo_arm_mvfr0;
|
||||
}
|
||||
|
||||
static inline uint32_t read_wcid(void) {
|
||||
return cpuinfo_arm_wcid;
|
||||
}
|
||||
#else
|
||||
#if !defined(__ARM_ARCH_7A__) && !defined(__ARM_ARCH_8A__) && !(defined(__ARM_ARCH) && (__ARM_ARCH >= 7))
|
||||
/*
|
||||
* CoProcessor 10 is inaccessible from user mode since ARMv7,
|
||||
* and clang refuses to compile inline assembly when targeting ARMv7+
|
||||
*/
|
||||
static inline uint32_t read_fpsid(void) {
|
||||
uint32_t fpsid;
|
||||
__asm__ __volatile__("MRC p10, 0x7, %[fpsid], cr0, cr0, 0" : [fpsid] "=r" (fpsid));
|
||||
return fpsid;
|
||||
}
|
||||
|
||||
static inline uint32_t read_mvfr0(void) {
|
||||
uint32_t mvfr0;
|
||||
__asm__ __volatile__("MRC p10, 0x7, %[mvfr0], cr7, cr0, 0" : [mvfr0] "=r" (mvfr0));
|
||||
return mvfr0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline uint32_t read_wcid(void) {
|
||||
uint32_t wcid;
|
||||
__asm__ __volatile__("MRC p1, 0, %[wcid], c0, c0" : [wcid] "=r" (wcid));
|
||||
return wcid;
|
||||
}
|
||||
#endif
|
908
dep/cpuinfo/src/arm/linux/cpuinfo.c
Normal file
908
dep/cpuinfo/src/arm/linux/cpuinfo.c
Normal file
@ -0,0 +1,908 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <linux/api.h>
|
||||
#include <arm/linux/api.h>
|
||||
#include <arm/midr.h>
|
||||
#include <cpuinfo/log.h>
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Full list of ARM features reported in /proc/cpuinfo:
|
||||
*
|
||||
* * swp - support for SWP instruction (deprecated in ARMv7, can be removed in future)
|
||||
* * half - support for half-word loads and stores. These instruction are part of ARMv4,
|
||||
* so no need to check it on supported CPUs.
|
||||
* * thumb - support for 16-bit Thumb instruction set. Note that BX instruction is detected
|
||||
* by ARMv4T architecture, not by this flag.
|
||||
* * 26bit - old CPUs merged 26-bit PC and program status register (flags) into 32-bit PC
|
||||
* and had special instructions for working with packed PC. Now it is all deprecated.
|
||||
* * fastmult - most old ARM CPUs could only compute 2 bits of multiplication result per clock
|
||||
* cycle, but CPUs with M suffix (e.g. ARM7TDMI) could compute 4 bits per cycle.
|
||||
* Of course, now it makes no sense.
|
||||
* * fpa - floating point accelerator available. On original ARM ABI all floating-point operations
|
||||
* generated FPA instructions. If FPA was not available, these instructions generated
|
||||
* "illegal operation" interrupts, and the OS processed them by emulating the FPA instructions.
|
||||
* Debian used this ABI before it switched to EABI. Now FPA is deprecated.
|
||||
* * vfp - vector floating point instructions. Available on most modern CPUs (as part of VFPv3).
|
||||
* Required by Android ARMv7A ABI and by Ubuntu on ARM.
|
||||
* Note: there is no flag for VFPv2.
|
||||
* * edsp - V5E instructions: saturating add/sub and 16-bit x 16-bit -> 32/64-bit multiplications.
|
||||
* Required on Android, supported by all CPUs in production.
|
||||
* * java - Jazelle extension. Supported on most CPUs.
|
||||
* * iwmmxt - Intel/Marvell Wireless MMX instructions. 64-bit integer SIMD.
|
||||
* Supported on XScale (Since PXA270) and Sheeva (PJ1, PJ4) architectures.
|
||||
* Note that there is no flag for WMMX2 instructions.
|
||||
* * crunch - Maverick Crunch instructions. Junk.
|
||||
* * thumbee - ThumbEE instructions. Almost no documentation is available.
|
||||
* * neon - NEON instructions (aka Advanced SIMD). MVFR1 register gives more
|
||||
* fine-grained information on particular supported features, but
|
||||
* the Linux kernel exports only a single flag for all of them.
|
||||
* According to ARMv7A docs it also implies the availability of VFPv3
|
||||
* (with 32 double-precision registers d0-d31).
|
||||
* * vfpv3 - VFPv3 instructions. Available on most modern CPUs. Augment VFPv2 by
|
||||
* conversion to/from integers and load constant instructions.
|
||||
* Required by Android ARMv7A ABI and by Ubuntu on ARM.
|
||||
* * vfpv3d16 - VFPv3 instructions with only 16 double-precision registers (d0-d15).
|
||||
* * tls - software thread ID registers.
|
||||
* Used by kernel (and likely libc) for efficient implementation of TLS.
|
||||
* * vfpv4 - fused multiply-add instructions.
|
||||
* * idiva - DIV instructions available in ARM mode.
|
||||
* * idivt - DIV instructions available in Thumb mode.
|
||||
* * vfpd32 - VFP (of any version) with 32 double-precision registers d0-d31.
|
||||
* * lpae - Large Physical Address Extension (physical address up to 40 bits).
|
||||
* * evtstrm - generation of Event Stream by timer.
|
||||
* * aes - AES instructions.
|
||||
* * pmull - Polinomial Multiplication instructions.
|
||||
* * sha1 - SHA1 instructions.
|
||||
* * sha2 - SHA2 instructions.
|
||||
* * crc32 - CRC32 instructions.
|
||||
*
|
||||
* /proc/cpuinfo on ARM is populated in file arch/arm/kernel/setup.c in Linux kernel
|
||||
* Note that some devices may use patched Linux kernels with different feature names.
|
||||
* However, the names above were checked on a large number of /proc/cpuinfo listings.
|
||||
*/
|
||||
static void parse_features(
|
||||
const char* features_start,
|
||||
const char* features_end,
|
||||
struct cpuinfo_arm_linux_processor processor[restrict static 1])
|
||||
{
|
||||
const char* feature_start = features_start;
|
||||
const char* feature_end;
|
||||
|
||||
/* Mark the features as valid */
|
||||
processor->flags |= CPUINFO_ARM_LINUX_VALID_FEATURES | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
|
||||
|
||||
do {
|
||||
feature_end = feature_start + 1;
|
||||
for (; feature_end != features_end; feature_end++) {
|
||||
if (*feature_end == ' ') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
const size_t feature_length = (size_t) (feature_end - feature_start);
|
||||
|
||||
switch (feature_length) {
|
||||
case 2:
|
||||
if (memcmp(feature_start, "fp", feature_length) == 0) {
|
||||
#if CPUINFO_ARCH_ARM64
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_FP;
|
||||
#endif
|
||||
#if CPUINFO_ARCH_ARM
|
||||
} else if (memcmp(feature_start, "wp", feature_length) == 0) {
|
||||
/*
|
||||
* Some AArch64 kernels, including the one on Nexus 5X,
|
||||
* erroneously report "swp" as "wp" to AArch32 programs
|
||||
*/
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_SWP;
|
||||
#endif
|
||||
} else {
|
||||
goto unexpected;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (memcmp(feature_start, "aes", feature_length) == 0) {
|
||||
#if CPUINFO_ARCH_ARM
|
||||
processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_AES;
|
||||
#elif CPUINFO_ARCH_ARM64
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_AES;
|
||||
#endif
|
||||
#if CPUINFO_ARCH_ARM
|
||||
} else if (memcmp(feature_start, "swp", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_SWP;
|
||||
} else if (memcmp(feature_start, "fpa", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_FPA;
|
||||
} else if (memcmp(feature_start, "vfp", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_VFP;
|
||||
} else if (memcmp(feature_start, "tls", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_TLS;
|
||||
#endif /* CPUINFO_ARCH_ARM */
|
||||
} else {
|
||||
goto unexpected;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (memcmp(feature_start, "sha1", feature_length) == 0) {
|
||||
#if CPUINFO_ARCH_ARM
|
||||
processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_SHA1;
|
||||
#elif CPUINFO_ARCH_ARM64
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_SHA1;
|
||||
#endif
|
||||
} else if (memcmp(feature_start, "sha2", feature_length) == 0) {
|
||||
#if CPUINFO_ARCH_ARM
|
||||
processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_SHA2;
|
||||
#elif CPUINFO_ARCH_ARM64
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_SHA2;
|
||||
#endif
|
||||
} else if (memcmp(feature_start, "fphp", feature_length) == 0) {
|
||||
#if CPUINFO_ARCH_ARM64
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_FPHP;
|
||||
#endif
|
||||
} else if (memcmp(feature_start, "fcma", feature_length) == 0) {
|
||||
#if CPUINFO_ARCH_ARM64
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_FCMA;
|
||||
#endif
|
||||
#if CPUINFO_ARCH_ARM
|
||||
} else if (memcmp(feature_start, "half", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_HALF;
|
||||
} else if (memcmp(feature_start, "edsp", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_EDSP;
|
||||
} else if (memcmp(feature_start, "java", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_JAVA;
|
||||
} else if (memcmp(feature_start, "neon", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_NEON;
|
||||
} else if (memcmp(feature_start, "lpae", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_LPAE;
|
||||
} else if (memcmp(feature_start, "tlsi", feature_length) == 0) {
|
||||
/*
|
||||
* Some AArch64 kernels, including the one on Nexus 5X,
|
||||
* erroneously report "tls" as "tlsi" to AArch32 programs
|
||||
*/
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_TLS;
|
||||
#endif /* CPUINFO_ARCH_ARM */
|
||||
} else {
|
||||
goto unexpected;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if (memcmp(feature_start, "pmull", feature_length) == 0) {
|
||||
#if CPUINFO_ARCH_ARM
|
||||
processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_PMULL;
|
||||
#elif CPUINFO_ARCH_ARM64
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_PMULL;
|
||||
#endif
|
||||
} else if (memcmp(feature_start, "crc32", feature_length) == 0) {
|
||||
#if CPUINFO_ARCH_ARM
|
||||
processor->features2 |= CPUINFO_ARM_LINUX_FEATURE2_CRC32;
|
||||
#elif CPUINFO_ARCH_ARM64
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_CRC32;
|
||||
#endif
|
||||
} else if (memcmp(feature_start, "asimd", feature_length) == 0) {
|
||||
#if CPUINFO_ARCH_ARM64
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_ASIMD;
|
||||
#endif
|
||||
} else if (memcmp(feature_start, "cpuid", feature_length) == 0) {
|
||||
#if CPUINFO_ARCH_ARM64
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_CPUID;
|
||||
#endif
|
||||
} else if (memcmp(feature_start, "jscvt", feature_length) == 0) {
|
||||
#if CPUINFO_ARCH_ARM64
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_JSCVT;
|
||||
#endif
|
||||
} else if (memcmp(feature_start, "lrcpc", feature_length) == 0) {
|
||||
#if CPUINFO_ARCH_ARM64
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_LRCPC;
|
||||
#endif
|
||||
#if CPUINFO_ARCH_ARM
|
||||
} else if (memcmp(feature_start, "thumb", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_THUMB;
|
||||
} else if (memcmp(feature_start, "26bit", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_26BIT;
|
||||
} else if (memcmp(feature_start, "vfpv3", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_VFPV3;
|
||||
} else if (memcmp(feature_start, "vfpv4", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_VFPV4;
|
||||
} else if (memcmp(feature_start, "idiva", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_IDIVA;
|
||||
} else if (memcmp(feature_start, "idivt", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_IDIVT;
|
||||
#endif /* CPUINFO_ARCH_ARM */
|
||||
} else {
|
||||
goto unexpected;
|
||||
}
|
||||
break;
|
||||
#if CPUINFO_ARCH_ARM
|
||||
case 6:
|
||||
if (memcmp(feature_start, "iwmmxt", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_IWMMXT;
|
||||
} else if (memcmp(feature_start, "crunch", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_CRUNCH;
|
||||
} else if (memcmp(feature_start, "vfpd32", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_VFPD32;
|
||||
} else {
|
||||
goto unexpected;
|
||||
}
|
||||
break;
|
||||
#endif /* CPUINFO_ARCH_ARM */
|
||||
case 7:
|
||||
if (memcmp(feature_start, "evtstrm", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_EVTSTRM;
|
||||
} else if (memcmp(feature_start, "atomics", feature_length) == 0) {
|
||||
#if CPUINFO_ARCH_ARM64
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_ATOMICS;
|
||||
#endif
|
||||
} else if (memcmp(feature_start, "asimdhp", feature_length) == 0) {
|
||||
#if CPUINFO_ARCH_ARM64
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_ASIMDHP;
|
||||
#endif
|
||||
#if CPUINFO_ARCH_ARM
|
||||
} else if (memcmp(feature_start, "thumbee", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_THUMBEE;
|
||||
#endif /* CPUINFO_ARCH_ARM */
|
||||
} else {
|
||||
goto unexpected;
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
if (memcmp(feature_start, "asimdrdm", feature_length) == 0) {
|
||||
#if CPUINFO_ARCH_ARM64
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_ASIMDRDM;
|
||||
#endif
|
||||
#if CPUINFO_ARCH_ARM
|
||||
} else if (memcmp(feature_start, "fastmult", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_FASTMULT;
|
||||
} else if (memcmp(feature_start, "vfpv3d16", feature_length) == 0) {
|
||||
processor->features |= CPUINFO_ARM_LINUX_FEATURE_VFPV3D16;
|
||||
#endif /* CPUINFO_ARCH_ARM */
|
||||
} else {
|
||||
goto unexpected;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
unexpected:
|
||||
cpuinfo_log_warning("unexpected /proc/cpuinfo feature \"%.*s\" is ignored",
|
||||
(int) feature_length, feature_start);
|
||||
break;
|
||||
}
|
||||
feature_start = feature_end;
|
||||
for (; feature_start != features_end; feature_start++) {
|
||||
if (*feature_start != ' ') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (feature_start != feature_end);
|
||||
}
|
||||
|
||||
static void parse_cpu_architecture(
|
||||
const char* cpu_architecture_start,
|
||||
const char* cpu_architecture_end,
|
||||
struct cpuinfo_arm_linux_processor processor[restrict static 1])
|
||||
{
|
||||
const size_t cpu_architecture_length = (size_t) (cpu_architecture_end - cpu_architecture_start);
|
||||
/* Early AArch64 kernels report "CPU architecture: AArch64" instead of a numeric value 8 */
|
||||
if (cpu_architecture_length == 7) {
|
||||
if (memcmp(cpu_architecture_start, "AArch64", cpu_architecture_length) == 0) {
|
||||
processor->midr = midr_set_architecture(processor->midr, UINT32_C(0xF));
|
||||
processor->architecture_version = 8;
|
||||
processor->flags |= CPUINFO_ARM_LINUX_VALID_ARCHITECTURE | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t architecture = 0;
|
||||
const char* cpu_architecture_ptr = cpu_architecture_start;
|
||||
for (; cpu_architecture_ptr != cpu_architecture_end; cpu_architecture_ptr++) {
|
||||
const uint32_t digit = (*cpu_architecture_ptr) - '0';
|
||||
|
||||
/* Verify that CPU architecture is a decimal number */
|
||||
if (digit >= 10) {
|
||||
break;
|
||||
}
|
||||
|
||||
architecture = architecture * 10 + digit;
|
||||
}
|
||||
|
||||
if (cpu_architecture_ptr == cpu_architecture_start) {
|
||||
cpuinfo_log_warning("CPU architecture %.*s in /proc/cpuinfo is ignored due to non-digit at the beginning of the string",
|
||||
(int) cpu_architecture_length, cpu_architecture_start);
|
||||
} else {
|
||||
if (architecture != 0) {
|
||||
processor->architecture_version = architecture;
|
||||
processor->flags |= CPUINFO_ARM_LINUX_VALID_ARCHITECTURE | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
|
||||
|
||||
for (; cpu_architecture_ptr != cpu_architecture_end; cpu_architecture_ptr++) {
|
||||
const char feature = *cpu_architecture_ptr;
|
||||
switch (feature) {
|
||||
#if CPUINFO_ARCH_ARM
|
||||
case 'T':
|
||||
processor->architecture_flags |= CPUINFO_ARM_LINUX_ARCH_T;
|
||||
break;
|
||||
case 'E':
|
||||
processor->architecture_flags |= CPUINFO_ARM_LINUX_ARCH_E;
|
||||
break;
|
||||
case 'J':
|
||||
processor->architecture_flags |= CPUINFO_ARM_LINUX_ARCH_J;
|
||||
break;
|
||||
#endif /* CPUINFO_ARCH_ARM */
|
||||
case ' ':
|
||||
case '\t':
|
||||
/* Ignore whitespace at the end */
|
||||
break;
|
||||
default:
|
||||
cpuinfo_log_warning("skipped unknown architectural feature '%c' for ARMv%"PRIu32,
|
||||
feature, architecture);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cpuinfo_log_warning("CPU architecture %.*s in /proc/cpuinfo is ignored due to invalid value (0)",
|
||||
(int) cpu_architecture_length, cpu_architecture_start);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t midr_architecture = UINT32_C(0xF);
|
||||
#if CPUINFO_ARCH_ARM
|
||||
switch (processor->architecture_version) {
|
||||
case 6:
|
||||
midr_architecture = UINT32_C(0x7); /* ARMv6 */
|
||||
break;
|
||||
case 5:
|
||||
if ((processor->architecture_flags & CPUINFO_ARM_LINUX_ARCH_TEJ) == CPUINFO_ARM_LINUX_ARCH_TEJ) {
|
||||
midr_architecture = UINT32_C(0x6); /* ARMv5TEJ */
|
||||
} else if ((processor->architecture_flags & CPUINFO_ARM_LINUX_ARCH_TE) == CPUINFO_ARM_LINUX_ARCH_TE) {
|
||||
midr_architecture = UINT32_C(0x5); /* ARMv5TE */
|
||||
} else {
|
||||
midr_architecture = UINT32_C(0x4); /* ARMv5T */
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
processor->midr = midr_set_architecture(processor->midr, midr_architecture);
|
||||
}
|
||||
|
||||
static void parse_cpu_part(
|
||||
const char* cpu_part_start,
|
||||
const char* cpu_part_end,
|
||||
struct cpuinfo_arm_linux_processor processor[restrict static 1])
|
||||
{
|
||||
const size_t cpu_part_length = (size_t) (cpu_part_end - cpu_part_start);
|
||||
|
||||
/*
|
||||
* CPU part should contain hex prefix (0x) and one to three hex digits.
|
||||
* I have never seen less than three digits as a value of this field,
|
||||
* but I don't think it is impossible to see such values in future.
|
||||
* Value can not contain more than three hex digits since
|
||||
* Main ID Register (MIDR) assigns only a 12-bit value for CPU part.
|
||||
*/
|
||||
if (cpu_part_length < 3 || cpu_part_length > 5) {
|
||||
cpuinfo_log_warning("CPU part %.*s in /proc/cpuinfo is ignored due to unexpected length (%zu)",
|
||||
(int) cpu_part_length, cpu_part_start, cpu_part_length);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Verify the presence of hex prefix */
|
||||
if (cpu_part_start[0] != '0' || cpu_part_start[1] != 'x') {
|
||||
cpuinfo_log_warning("CPU part %.*s in /proc/cpuinfo is ignored due to lack of 0x prefix",
|
||||
(int) cpu_part_length, cpu_part_start);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Verify that characters after hex prefix are hexadecimal digits and decode them */
|
||||
uint32_t cpu_part = 0;
|
||||
for (const char* digit_ptr = cpu_part_start + 2; digit_ptr != cpu_part_end; digit_ptr++) {
|
||||
const char digit_char = *digit_ptr;
|
||||
uint32_t digit;
|
||||
if (digit_char >= '0' && digit_char <= '9') {
|
||||
digit = digit_char - '0';
|
||||
} else if ((uint32_t) (digit_char - 'A') < 6) {
|
||||
digit = 10 + (digit_char - 'A');
|
||||
} else if ((uint32_t) (digit_char - 'a') < 6) {
|
||||
digit = 10 + (digit_char - 'a');
|
||||
} else {
|
||||
cpuinfo_log_warning("CPU part %.*s in /proc/cpuinfo is ignored due to unexpected non-hex character %c at offset %zu",
|
||||
(int) cpu_part_length, cpu_part_start, digit_char, (size_t) (digit_ptr - cpu_part_start));
|
||||
return;
|
||||
}
|
||||
cpu_part = cpu_part * 16 + digit;
|
||||
}
|
||||
|
||||
processor->midr = midr_set_part(processor->midr, cpu_part);
|
||||
processor->flags |= CPUINFO_ARM_LINUX_VALID_PART | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
|
||||
}
|
||||
|
||||
static void parse_cpu_implementer(
|
||||
const char* cpu_implementer_start,
|
||||
const char* cpu_implementer_end,
|
||||
struct cpuinfo_arm_linux_processor processor[restrict static 1])
|
||||
{
|
||||
const size_t cpu_implementer_length = cpu_implementer_end - cpu_implementer_start;
|
||||
|
||||
/*
|
||||
* Value should contain hex prefix (0x) and one or two hex digits.
|
||||
* I have never seen single hex digit as a value of this field,
|
||||
* but I don't think it is impossible in future.
|
||||
* Value can not contain more than two hex digits since
|
||||
* Main ID Register (MIDR) assigns only an 8-bit value for CPU implementer.
|
||||
*/
|
||||
switch (cpu_implementer_length) {
|
||||
case 3:
|
||||
case 4:
|
||||
break;
|
||||
default:
|
||||
cpuinfo_log_warning("CPU implementer %.*s in /proc/cpuinfo is ignored due to unexpected length (%zu)",
|
||||
(int) cpu_implementer_length, cpu_implementer_start, cpu_implementer_length);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Verify the presence of hex prefix */
|
||||
if (cpu_implementer_start[0] != '0' || cpu_implementer_start[1] != 'x') {
|
||||
cpuinfo_log_warning("CPU implementer %.*s in /proc/cpuinfo is ignored due to lack of 0x prefix",
|
||||
(int) cpu_implementer_length, cpu_implementer_start);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Verify that characters after hex prefix are hexadecimal digits and decode them */
|
||||
uint32_t cpu_implementer = 0;
|
||||
for (const char* digit_ptr = cpu_implementer_start + 2; digit_ptr != cpu_implementer_end; digit_ptr++) {
|
||||
const char digit_char = *digit_ptr;
|
||||
uint32_t digit;
|
||||
if (digit_char >= '0' && digit_char <= '9') {
|
||||
digit = digit_char - '0';
|
||||
} else if ((uint32_t) (digit_char - 'A') < 6) {
|
||||
digit = 10 + (digit_char - 'A');
|
||||
} else if ((uint32_t) (digit_char - 'a') < 6) {
|
||||
digit = 10 + (digit_char - 'a');
|
||||
} else {
|
||||
cpuinfo_log_warning("CPU implementer %.*s in /proc/cpuinfo is ignored due to unexpected non-hex character '%c' at offset %zu",
|
||||
(int) cpu_implementer_length, cpu_implementer_start, digit_char, (size_t) (digit_ptr - cpu_implementer_start));
|
||||
return;
|
||||
}
|
||||
cpu_implementer = cpu_implementer * 16 + digit;
|
||||
}
|
||||
|
||||
processor->midr = midr_set_implementer(processor->midr, cpu_implementer);
|
||||
processor->flags |= CPUINFO_ARM_LINUX_VALID_IMPLEMENTER | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
|
||||
}
|
||||
|
||||
static void parse_cpu_variant(
|
||||
const char* cpu_variant_start,
|
||||
const char* cpu_variant_end,
|
||||
struct cpuinfo_arm_linux_processor processor[restrict static 1])
|
||||
{
|
||||
const size_t cpu_variant_length = cpu_variant_end - cpu_variant_start;
|
||||
|
||||
/*
|
||||
* Value should contain hex prefix (0x) and one hex digit.
|
||||
* Value can not contain more than one hex digits since
|
||||
* Main ID Register (MIDR) assigns only a 4-bit value for CPU variant.
|
||||
*/
|
||||
if (cpu_variant_length != 3) {
|
||||
cpuinfo_log_warning("CPU variant %.*s in /proc/cpuinfo is ignored due to unexpected length (%zu)",
|
||||
(int) cpu_variant_length, cpu_variant_start, cpu_variant_length);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Skip if there is no hex prefix (0x) */
|
||||
if (cpu_variant_start[0] != '0' || cpu_variant_start[1] != 'x') {
|
||||
cpuinfo_log_warning("CPU variant %.*s in /proc/cpuinfo is ignored due to lack of 0x prefix",
|
||||
(int) cpu_variant_length, cpu_variant_start);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if the value after hex prefix is indeed a hex digit and decode it. */
|
||||
const char digit_char = cpu_variant_start[2];
|
||||
uint32_t cpu_variant;
|
||||
if ((uint32_t) (digit_char - '0') < 10) {
|
||||
cpu_variant = (uint32_t) (digit_char - '0');
|
||||
} else if ((uint32_t) (digit_char - 'A') < 6) {
|
||||
cpu_variant = 10 + (uint32_t) (digit_char - 'A');
|
||||
} else if ((uint32_t) (digit_char - 'a') < 6) {
|
||||
cpu_variant = 10 + (uint32_t) (digit_char - 'a');
|
||||
} else {
|
||||
cpuinfo_log_warning("CPU variant %.*s in /proc/cpuinfo is ignored due to unexpected non-hex character '%c'",
|
||||
(int) cpu_variant_length, cpu_variant_start, digit_char);
|
||||
return;
|
||||
}
|
||||
|
||||
processor->midr = midr_set_variant(processor->midr, cpu_variant);
|
||||
processor->flags |= CPUINFO_ARM_LINUX_VALID_VARIANT | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
|
||||
}
|
||||
|
||||
static void parse_cpu_revision(
|
||||
const char* cpu_revision_start,
|
||||
const char* cpu_revision_end,
|
||||
struct cpuinfo_arm_linux_processor processor[restrict static 1])
|
||||
{
|
||||
uint32_t cpu_revision = 0;
|
||||
for (const char* digit_ptr = cpu_revision_start; digit_ptr != cpu_revision_end; digit_ptr++) {
|
||||
const uint32_t digit = (uint32_t) (*digit_ptr - '0');
|
||||
|
||||
/* Verify that the character in CPU revision is a decimal digit */
|
||||
if (digit >= 10) {
|
||||
cpuinfo_log_warning("CPU revision %.*s in /proc/cpuinfo is ignored due to unexpected non-digit character '%c' at offset %zu",
|
||||
(int) (cpu_revision_end - cpu_revision_start), cpu_revision_start,
|
||||
*digit_ptr, (size_t) (digit_ptr - cpu_revision_start));
|
||||
return;
|
||||
}
|
||||
|
||||
cpu_revision = cpu_revision * 10 + digit;
|
||||
}
|
||||
|
||||
processor->midr = midr_set_revision(processor->midr, cpu_revision);
|
||||
processor->flags |= CPUINFO_ARM_LINUX_VALID_REVISION | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
|
||||
}
|
||||
|
||||
#if CPUINFO_ARCH_ARM
|
||||
/*
|
||||
* Decode one of the cache-related numbers reported by Linux kernel
|
||||
* for pre-ARMv7 architecture.
|
||||
* An example cache-related information in /proc/cpuinfo:
|
||||
*
|
||||
* I size : 32768
|
||||
* I assoc : 4
|
||||
* I line length : 32
|
||||
* I sets : 256
|
||||
* D size : 16384
|
||||
* D assoc : 4
|
||||
* D line length : 32
|
||||
* D sets : 128
|
||||
*
|
||||
*/
|
||||
static void parse_cache_number(
|
||||
const char* number_start,
|
||||
const char* number_end,
|
||||
const char* number_name,
|
||||
uint32_t number_ptr[restrict static 1],
|
||||
uint32_t flags[restrict static 1],
|
||||
uint32_t number_mask)
|
||||
{
|
||||
uint32_t number = 0;
|
||||
for (const char* digit_ptr = number_start; digit_ptr != number_end; digit_ptr++) {
|
||||
const uint32_t digit = *digit_ptr - '0';
|
||||
if (digit >= 10) {
|
||||
cpuinfo_log_warning("%s %.*s in /proc/cpuinfo is ignored due to unexpected non-digit character '%c' at offset %zu",
|
||||
number_name, (int) (number_end - number_start), number_start,
|
||||
*digit_ptr, (size_t) (digit_ptr - number_start));
|
||||
return;
|
||||
}
|
||||
|
||||
number = number * 10 + digit;
|
||||
}
|
||||
|
||||
if (number == 0) {
|
||||
cpuinfo_log_warning("%s %.*s in /proc/cpuinfo is ignored due to invalid value of zero reported by the kernel",
|
||||
number_name, (int) (number_end - number_start), number_start);
|
||||
}
|
||||
|
||||
/* If the number specifies a cache line size, verify that is a reasonable power of 2 */
|
||||
if (number_mask & CPUINFO_ARM_LINUX_VALID_CACHE_LINE) {
|
||||
switch (number) {
|
||||
case 16:
|
||||
case 32:
|
||||
case 64:
|
||||
case 128:
|
||||
break;
|
||||
default:
|
||||
cpuinfo_log_warning("invalid %s %.*s is ignored: a value of 16, 32, 64, or 128 expected",
|
||||
number_name, (int) (number_end - number_start), number_start);
|
||||
}
|
||||
}
|
||||
|
||||
*number_ptr = number;
|
||||
*flags |= number_mask | CPUINFO_ARM_LINUX_VALID_PROCESSOR;
|
||||
}
|
||||
#endif /* CPUINFO_ARCH_ARM */
|
||||
|
||||
struct proc_cpuinfo_parser_state {
|
||||
char* hardware;
|
||||
char* revision;
|
||||
uint32_t processor_index;
|
||||
uint32_t max_processors_count;
|
||||
struct cpuinfo_arm_linux_processor* processors;
|
||||
struct cpuinfo_arm_linux_processor dummy_processor;
|
||||
};
|
||||
|
||||
/*
|
||||
* Decode a single line of /proc/cpuinfo information.
|
||||
* Lines have format <words-with-spaces>[ ]*:[ ]<space-separated words>
|
||||
* An example of /proc/cpuinfo (from Pandaboard-ES):
|
||||
*
|
||||
* Processor : ARMv7 Processor rev 10 (v7l)
|
||||
* processor : 0
|
||||
* BogoMIPS : 1392.74
|
||||
*
|
||||
* processor : 1
|
||||
* BogoMIPS : 1363.33
|
||||
*
|
||||
* Features : swp half thumb fastmult vfp edsp thumbee neon vfpv3
|
||||
* CPU implementer : 0x41
|
||||
* CPU architecture: 7
|
||||
* CPU variant : 0x2
|
||||
* CPU part : 0xc09
|
||||
* CPU revision : 10
|
||||
*
|
||||
* Hardware : OMAP4 Panda board
|
||||
* Revision : 0020
|
||||
* Serial : 0000000000000000
|
||||
*/
|
||||
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_info("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_info("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_info("Line %.*s in /proc/cpuinfo is ignored: value contains only spaces",
|
||||
(int) (line_end - line_start), line_start);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t processor_index = state->processor_index;
|
||||
const uint32_t max_processors_count = state->max_processors_count;
|
||||
struct cpuinfo_arm_linux_processor* processors = state->processors;
|
||||
struct cpuinfo_arm_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 6:
|
||||
if (memcmp(line_start, "Serial", key_length) == 0) {
|
||||
/* Usually contains just zeros, useless */
|
||||
#if CPUINFO_ARCH_ARM
|
||||
} else if (memcmp(line_start, "I size", key_length) == 0) {
|
||||
parse_cache_number(value_start, value_end,
|
||||
"instruction cache size", &processor->proc_cpuinfo_cache.i_size,
|
||||
&processor->flags, CPUINFO_ARM_LINUX_VALID_ICACHE_SIZE);
|
||||
} else if (memcmp(line_start, "I sets", key_length) == 0) {
|
||||
parse_cache_number(value_start, value_end,
|
||||
"instruction cache sets", &processor->proc_cpuinfo_cache.i_sets,
|
||||
&processor->flags, CPUINFO_ARM_LINUX_VALID_ICACHE_SETS);
|
||||
} else if (memcmp(line_start, "D size", key_length) == 0) {
|
||||
parse_cache_number(value_start, value_end,
|
||||
"data cache size", &processor->proc_cpuinfo_cache.d_size,
|
||||
&processor->flags, CPUINFO_ARM_LINUX_VALID_DCACHE_SIZE);
|
||||
} else if (memcmp(line_start, "D sets", key_length) == 0) {
|
||||
parse_cache_number(value_start, value_end,
|
||||
"data cache sets", &processor->proc_cpuinfo_cache.d_sets,
|
||||
&processor->flags, CPUINFO_ARM_LINUX_VALID_DCACHE_SETS);
|
||||
#endif /* CPUINFO_ARCH_ARM */
|
||||
} else {
|
||||
goto unknown;
|
||||
}
|
||||
break;
|
||||
#if CPUINFO_ARCH_ARM
|
||||
case 7:
|
||||
if (memcmp(line_start, "I assoc", key_length) == 0) {
|
||||
parse_cache_number(value_start, value_end,
|
||||
"instruction cache associativity", &processor->proc_cpuinfo_cache.i_assoc,
|
||||
&processor->flags, CPUINFO_ARM_LINUX_VALID_ICACHE_WAYS);
|
||||
} else if (memcmp(line_start, "D assoc", key_length) == 0) {
|
||||
parse_cache_number(value_start, value_end,
|
||||
"data cache associativity", &processor->proc_cpuinfo_cache.d_assoc,
|
||||
&processor->flags, CPUINFO_ARM_LINUX_VALID_DCACHE_WAYS);
|
||||
} else {
|
||||
goto unknown;
|
||||
}
|
||||
break;
|
||||
#endif /* CPUINFO_ARCH_ARM */
|
||||
case 8:
|
||||
if (memcmp(line_start, "CPU part", key_length) == 0) {
|
||||
parse_cpu_part(value_start, value_end, processor);
|
||||
} else if (memcmp(line_start, "Features", key_length) == 0) {
|
||||
parse_features(value_start, value_end, processor);
|
||||
} else if (memcmp(line_start, "BogoMIPS", key_length) == 0) {
|
||||
/* BogoMIPS is useless, don't parse */
|
||||
} else if (memcmp(line_start, "Hardware", key_length) == 0) {
|
||||
size_t value_length = value_end - value_start;
|
||||
if (value_length > CPUINFO_HARDWARE_VALUE_MAX) {
|
||||
cpuinfo_log_info(
|
||||
"length of Hardware value \"%.*s\" in /proc/cpuinfo exceeds limit (%d): truncating to the limit",
|
||||
(int) value_length, value_start, CPUINFO_HARDWARE_VALUE_MAX);
|
||||
value_length = CPUINFO_HARDWARE_VALUE_MAX;
|
||||
} else {
|
||||
state->hardware[value_length] = '\0';
|
||||
}
|
||||
memcpy(state->hardware, value_start, value_length);
|
||||
cpuinfo_log_debug("parsed /proc/cpuinfo Hardware = \"%.*s\"", (int) value_length, value_start);
|
||||
} else if (memcmp(line_start, "Revision", key_length) == 0) {
|
||||
size_t value_length = value_end - value_start;
|
||||
if (value_length > CPUINFO_REVISION_VALUE_MAX) {
|
||||
cpuinfo_log_info(
|
||||
"length of Revision value \"%.*s\" in /proc/cpuinfo exceeds limit (%d): truncating to the limit",
|
||||
(int) value_length, value_start, CPUINFO_REVISION_VALUE_MAX);
|
||||
value_length = CPUINFO_REVISION_VALUE_MAX;
|
||||
} else {
|
||||
state->revision[value_length] = '\0';
|
||||
}
|
||||
memcpy(state->revision, value_start, value_length);
|
||||
cpuinfo_log_debug("parsed /proc/cpuinfo Revision = \"%.*s\"", (int) value_length, value_start);
|
||||
} else {
|
||||
goto unknown;
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
if (memcmp(line_start, "processor", key_length) == 0) {
|
||||
const uint32_t new_processor_index = parse_processor_number(value_start, value_end);
|
||||
if (new_processor_index < processor_index) {
|
||||
/* Strange: decreasing processor number */
|
||||
cpuinfo_log_warning(
|
||||
"unexpectedly low processor number %"PRIu32" following processor %"PRIu32" in /proc/cpuinfo",
|
||||
new_processor_index, processor_index);
|
||||
} else if (new_processor_index > processor_index + 1) {
|
||||
/* Strange, but common: skipped processor $(processor_index + 1) */
|
||||
cpuinfo_log_info(
|
||||
"unexpectedly high processor number %"PRIu32" following processor %"PRIu32" in /proc/cpuinfo",
|
||||
new_processor_index, processor_index);
|
||||
}
|
||||
if (new_processor_index < max_processors_count) {
|
||||
/* Record that the processor was mentioned in /proc/cpuinfo */
|
||||
processors[new_processor_index].flags |= CPUINFO_ARM_LINUX_VALID_PROCESSOR;
|
||||
} else {
|
||||
/* Log and ignore processor */
|
||||
cpuinfo_log_warning("processor %"PRIu32" in /proc/cpuinfo is ignored: index exceeds system limit %"PRIu32,
|
||||
new_processor_index, max_processors_count - 1);
|
||||
}
|
||||
state->processor_index = new_processor_index;
|
||||
return true;
|
||||
} else if (memcmp(line_start, "Processor", key_length) == 0) {
|
||||
/* TODO: parse to fix misreported architecture, similar to Android's cpufeatures */
|
||||
} else {
|
||||
goto unknown;
|
||||
}
|
||||
break;
|
||||
case 11:
|
||||
if (memcmp(line_start, "CPU variant", key_length) == 0) {
|
||||
parse_cpu_variant(value_start, value_end, processor);
|
||||
} else {
|
||||
goto unknown;
|
||||
}
|
||||
break;
|
||||
case 12:
|
||||
if (memcmp(line_start, "CPU revision", key_length) == 0) {
|
||||
parse_cpu_revision(value_start, value_end, processor);
|
||||
} else {
|
||||
goto unknown;
|
||||
}
|
||||
break;
|
||||
#if CPUINFO_ARCH_ARM
|
||||
case 13:
|
||||
if (memcmp(line_start, "I line length", key_length) == 0) {
|
||||
parse_cache_number(value_start, value_end,
|
||||
"instruction cache line size", &processor->proc_cpuinfo_cache.i_line_length,
|
||||
&processor->flags, CPUINFO_ARM_LINUX_VALID_ICACHE_LINE);
|
||||
} else if (memcmp(line_start, "D line length", key_length) == 0) {
|
||||
parse_cache_number(value_start, value_end,
|
||||
"data cache line size", &processor->proc_cpuinfo_cache.d_line_length,
|
||||
&processor->flags, CPUINFO_ARM_LINUX_VALID_DCACHE_LINE);
|
||||
} else {
|
||||
goto unknown;
|
||||
}
|
||||
break;
|
||||
#endif /* CPUINFO_ARCH_ARM */
|
||||
case 15:
|
||||
if (memcmp(line_start, "CPU implementer", key_length) == 0) {
|
||||
parse_cpu_implementer(value_start, value_end, processor);
|
||||
} else if (memcmp(line_start, "CPU implementor", key_length) == 0) {
|
||||
parse_cpu_implementer(value_start, value_end, processor);
|
||||
} else {
|
||||
goto unknown;
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
if (memcmp(line_start, "CPU architecture", key_length) == 0) {
|
||||
parse_cpu_architecture(value_start, value_end, processor);
|
||||
} else {
|
||||
goto unknown;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
unknown:
|
||||
cpuinfo_log_debug("unknown /proc/cpuinfo key: %.*s", (int) key_length, line_start);
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cpuinfo_arm_linux_parse_proc_cpuinfo(
|
||||
char hardware[restrict static CPUINFO_HARDWARE_VALUE_MAX],
|
||||
char revision[restrict static CPUINFO_REVISION_VALUE_MAX],
|
||||
uint32_t max_processors_count,
|
||||
struct cpuinfo_arm_linux_processor processors[restrict static max_processors_count])
|
||||
{
|
||||
struct proc_cpuinfo_parser_state state = {
|
||||
.hardware = hardware,
|
||||
.revision = revision,
|
||||
.processor_index = 0,
|
||||
.max_processors_count = max_processors_count,
|
||||
.processors = processors,
|
||||
};
|
||||
return cpuinfo_linux_parse_multiline_file("/proc/cpuinfo", BUFFER_SIZE,
|
||||
(cpuinfo_line_callback) parse_line, &state);
|
||||
}
|
159
dep/cpuinfo/src/arm/linux/hwcap.c
Normal file
159
dep/cpuinfo/src/arm/linux/hwcap.c
Normal file
@ -0,0 +1,159 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <dlfcn.h>
|
||||
#include <elf.h>
|
||||
|
||||
#if CPUINFO_MOCK
|
||||
#include <cpuinfo-mock.h>
|
||||
#endif
|
||||
#include <cpuinfo.h>
|
||||
#include <arm/linux/api.h>
|
||||
#include <cpuinfo/log.h>
|
||||
|
||||
#if CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_ARM && !defined(__ANDROID__)
|
||||
#include <sys/auxv.h>
|
||||
#else
|
||||
#define AT_HWCAP 16
|
||||
#define AT_HWCAP2 26
|
||||
#endif
|
||||
|
||||
|
||||
#if CPUINFO_MOCK
|
||||
static uint32_t mock_hwcap = 0;
|
||||
void cpuinfo_set_hwcap(uint32_t hwcap) {
|
||||
mock_hwcap = hwcap;
|
||||
}
|
||||
|
||||
static uint32_t mock_hwcap2 = 0;
|
||||
void cpuinfo_set_hwcap2(uint32_t hwcap2) {
|
||||
mock_hwcap2 = hwcap2;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if CPUINFO_ARCH_ARM
|
||||
typedef unsigned long (*getauxval_function_t)(unsigned long);
|
||||
|
||||
bool cpuinfo_arm_linux_hwcap_from_getauxval(
|
||||
uint32_t hwcap[restrict static 1],
|
||||
uint32_t hwcap2[restrict static 1])
|
||||
{
|
||||
#if CPUINFO_MOCK
|
||||
*hwcap = mock_hwcap;
|
||||
*hwcap2 = mock_hwcap2;
|
||||
return true;
|
||||
#elif defined(__ANDROID__)
|
||||
/* Android: dynamically check if getauxval is supported */
|
||||
void* libc = NULL;
|
||||
getauxval_function_t getauxval = NULL;
|
||||
|
||||
dlerror();
|
||||
libc = dlopen("libc.so", RTLD_LAZY);
|
||||
if (libc == NULL) {
|
||||
cpuinfo_log_warning("failed to load libc.so: %s", dlerror());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
getauxval = (getauxval_function_t) dlsym(libc, "getauxval");
|
||||
if (getauxval == NULL) {
|
||||
cpuinfo_log_info("failed to locate getauxval in libc.so: %s", dlerror());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*hwcap = getauxval(AT_HWCAP);
|
||||
*hwcap2 = getauxval(AT_HWCAP2);
|
||||
|
||||
cleanup:
|
||||
if (libc != NULL) {
|
||||
dlclose(libc);
|
||||
libc = NULL;
|
||||
}
|
||||
return getauxval != NULL;
|
||||
#else
|
||||
/* GNU/Linux: getauxval is always supported */
|
||||
*hwcap = getauxval(AT_HWCAP);
|
||||
*hwcap2 = getauxval(AT_HWCAP2);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
bool cpuinfo_arm_linux_hwcap_from_procfs(
|
||||
uint32_t hwcap[restrict static 1],
|
||||
uint32_t hwcap2[restrict static 1])
|
||||
{
|
||||
#if CPUINFO_MOCK
|
||||
*hwcap = mock_hwcap;
|
||||
*hwcap2 = mock_hwcap2;
|
||||
return true;
|
||||
#else
|
||||
uint32_t hwcaps[2] = { 0, 0 };
|
||||
bool result = false;
|
||||
int file = -1;
|
||||
|
||||
file = open("/proc/self/auxv", O_RDONLY);
|
||||
if (file == -1) {
|
||||
cpuinfo_log_warning("failed to open /proc/self/auxv: %s", strerror(errno));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ssize_t bytes_read;
|
||||
do {
|
||||
Elf32_auxv_t elf_auxv;
|
||||
bytes_read = read(file, &elf_auxv, sizeof(Elf32_auxv_t));
|
||||
if (bytes_read < 0) {
|
||||
cpuinfo_log_warning("failed to read /proc/self/auxv: %s", strerror(errno));
|
||||
goto cleanup;
|
||||
} else if (bytes_read > 0) {
|
||||
if (bytes_read == sizeof(elf_auxv)) {
|
||||
switch (elf_auxv.a_type) {
|
||||
case AT_HWCAP:
|
||||
hwcaps[0] = (uint32_t) elf_auxv.a_un.a_val;
|
||||
break;
|
||||
case AT_HWCAP2:
|
||||
hwcaps[1] = (uint32_t) elf_auxv.a_un.a_val;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
cpuinfo_log_warning(
|
||||
"failed to read %zu bytes from /proc/self/auxv: %zu bytes available",
|
||||
sizeof(elf_auxv), (size_t) bytes_read);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
} while (bytes_read == sizeof(Elf32_auxv_t));
|
||||
|
||||
/* Success, commit results */
|
||||
*hwcap = hwcaps[0];
|
||||
*hwcap2 = hwcaps[1];
|
||||
result = true;
|
||||
|
||||
cleanup:
|
||||
if (file != -1) {
|
||||
close(file);
|
||||
file = -1;
|
||||
}
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
#endif /* __ANDROID__ */
|
||||
#elif CPUINFO_ARCH_ARM64
|
||||
void cpuinfo_arm_linux_hwcap_from_getauxval(
|
||||
uint32_t hwcap[restrict static 1],
|
||||
uint32_t hwcap2[restrict static 1])
|
||||
{
|
||||
#if CPUINFO_MOCK
|
||||
*hwcap = mock_hwcap;
|
||||
*hwcap2 = mock_hwcap2;
|
||||
#else
|
||||
*hwcap = (uint32_t) getauxval(AT_HWCAP);
|
||||
*hwcap2 = (uint32_t) getauxval(AT_HWCAP2);
|
||||
return ;
|
||||
#endif
|
||||
}
|
||||
#endif
|
765
dep/cpuinfo/src/arm/linux/init.c
Normal file
765
dep/cpuinfo/src/arm/linux/init.c
Normal file
@ -0,0 +1,765 @@
|
||||
#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>
|
||||
|
||||
|
||||
struct cpuinfo_arm_isa cpuinfo_isa = { 0 };
|
||||
|
||||
static struct cpuinfo_package package = { { 0 } };
|
||||
|
||||
static inline bool bitmask_all(uint32_t bitfield, uint32_t mask) {
|
||||
return (bitfield & mask) == mask;
|
||||
}
|
||||
|
||||
static inline uint32_t min(uint32_t a, uint32_t b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
static inline int cmp(uint32_t a, uint32_t b) {
|
||||
return (a > b) - (a < b);
|
||||
}
|
||||
|
||||
static bool cluster_siblings_parser(
|
||||
uint32_t processor, uint32_t siblings_start, uint32_t siblings_end,
|
||||
struct cpuinfo_arm_linux_processor* processors)
|
||||
{
|
||||
processors[processor].flags |= CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER;
|
||||
uint32_t package_leader_id = processors[processor].package_leader_id;
|
||||
|
||||
for (uint32_t sibling = siblings_start; sibling < siblings_end; sibling++) {
|
||||
if (!bitmask_all(processors[sibling].flags, CPUINFO_LINUX_FLAG_VALID)) {
|
||||
cpuinfo_log_info("invalid processor %"PRIu32" reported as a sibling for processor %"PRIu32,
|
||||
sibling, processor);
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t sibling_package_leader_id = processors[sibling].package_leader_id;
|
||||
if (sibling_package_leader_id < package_leader_id) {
|
||||
package_leader_id = sibling_package_leader_id;
|
||||
}
|
||||
|
||||
processors[sibling].package_leader_id = package_leader_id;
|
||||
processors[sibling].flags |= CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER;
|
||||
}
|
||||
|
||||
processors[processor].package_leader_id = package_leader_id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cmp_arm_linux_processor(const void* ptr_a, const void* ptr_b) {
|
||||
const struct cpuinfo_arm_linux_processor* processor_a = (const struct cpuinfo_arm_linux_processor*) ptr_a;
|
||||
const struct cpuinfo_arm_linux_processor* processor_b = (const struct cpuinfo_arm_linux_processor*) ptr_b;
|
||||
|
||||
/* Move usable processors towards the start of the array */
|
||||
const bool usable_a = bitmask_all(processor_a->flags, CPUINFO_LINUX_FLAG_VALID);
|
||||
const bool usable_b = bitmask_all(processor_b->flags, CPUINFO_LINUX_FLAG_VALID);
|
||||
if (usable_a != usable_b) {
|
||||
return (int) usable_b - (int) usable_a;
|
||||
}
|
||||
|
||||
/* Compare based on core type (e.g. Cortex-A57 < Cortex-A53) */
|
||||
const uint32_t midr_a = processor_a->midr;
|
||||
const uint32_t midr_b = processor_b->midr;
|
||||
if (midr_a != midr_b) {
|
||||
const uint32_t score_a = midr_score_core(midr_a);
|
||||
const uint32_t score_b = midr_score_core(midr_b);
|
||||
if (score_a != score_b) {
|
||||
return score_a > score_b ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compare based on core frequency (e.g. 2.0 GHz < 1.2 GHz) */
|
||||
const uint32_t frequency_a = processor_a->max_frequency;
|
||||
const uint32_t frequency_b = processor_b->max_frequency;
|
||||
if (frequency_a != frequency_b) {
|
||||
return frequency_a > frequency_b ? -1 : 1;
|
||||
}
|
||||
|
||||
/* Compare based on cluster leader id (i.e. cluster 1 < cluster 0) */
|
||||
const uint32_t cluster_a = processor_a->package_leader_id;
|
||||
const uint32_t cluster_b = processor_b->package_leader_id;
|
||||
if (cluster_a != cluster_b) {
|
||||
return cluster_a > cluster_b ? -1 : 1;
|
||||
}
|
||||
|
||||
/* Compare based on system processor id (i.e. processor 0 < processor 1) */
|
||||
const uint32_t id_a = processor_a->system_processor_id;
|
||||
const uint32_t id_b = processor_b->system_processor_id;
|
||||
return cmp(id_a, id_b);
|
||||
}
|
||||
|
||||
void cpuinfo_arm_linux_init(void) {
|
||||
struct cpuinfo_arm_linux_processor* arm_linux_processors = NULL;
|
||||
struct cpuinfo_processor* processors = NULL;
|
||||
struct cpuinfo_core* cores = NULL;
|
||||
struct cpuinfo_cluster* clusters = NULL;
|
||||
struct cpuinfo_uarch_info* uarchs = NULL;
|
||||
struct cpuinfo_cache* l1i = NULL;
|
||||
struct cpuinfo_cache* l1d = NULL;
|
||||
struct cpuinfo_cache* l2 = NULL;
|
||||
struct cpuinfo_cache* l3 = 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;
|
||||
|
||||
const uint32_t max_processors_count = cpuinfo_linux_get_max_processors_count();
|
||||
cpuinfo_log_debug("system maximum processors count: %"PRIu32, max_processors_count);
|
||||
|
||||
const uint32_t max_possible_processors_count = 1 +
|
||||
cpuinfo_linux_get_max_possible_processor(max_processors_count);
|
||||
cpuinfo_log_debug("maximum possible processors count: %"PRIu32, max_possible_processors_count);
|
||||
const uint32_t max_present_processors_count = 1 +
|
||||
cpuinfo_linux_get_max_present_processor(max_processors_count);
|
||||
cpuinfo_log_debug("maximum present processors count: %"PRIu32, max_present_processors_count);
|
||||
|
||||
uint32_t valid_processor_mask = 0;
|
||||
uint32_t arm_linux_processors_count = max_processors_count;
|
||||
if (max_present_processors_count != 0) {
|
||||
arm_linux_processors_count = min(arm_linux_processors_count, max_present_processors_count);
|
||||
valid_processor_mask = CPUINFO_LINUX_FLAG_PRESENT;
|
||||
}
|
||||
if (max_possible_processors_count != 0) {
|
||||
arm_linux_processors_count = min(arm_linux_processors_count, max_possible_processors_count);
|
||||
valid_processor_mask |= CPUINFO_LINUX_FLAG_POSSIBLE;
|
||||
}
|
||||
if ((max_present_processors_count | max_possible_processors_count) == 0) {
|
||||
cpuinfo_log_error("failed to parse both lists of possible and present processors");
|
||||
return;
|
||||
}
|
||||
|
||||
arm_linux_processors = calloc(arm_linux_processors_count, sizeof(struct cpuinfo_arm_linux_processor));
|
||||
if (arm_linux_processors == NULL) {
|
||||
cpuinfo_log_error(
|
||||
"failed to allocate %zu bytes for descriptions of %"PRIu32" ARM logical processors",
|
||||
arm_linux_processors_count * sizeof(struct cpuinfo_arm_linux_processor),
|
||||
arm_linux_processors_count);
|
||||
return;
|
||||
}
|
||||
|
||||
if (max_possible_processors_count) {
|
||||
cpuinfo_linux_detect_possible_processors(
|
||||
arm_linux_processors_count, &arm_linux_processors->flags,
|
||||
sizeof(struct cpuinfo_arm_linux_processor),
|
||||
CPUINFO_LINUX_FLAG_POSSIBLE);
|
||||
}
|
||||
|
||||
if (max_present_processors_count) {
|
||||
cpuinfo_linux_detect_present_processors(
|
||||
arm_linux_processors_count, &arm_linux_processors->flags,
|
||||
sizeof(struct cpuinfo_arm_linux_processor),
|
||||
CPUINFO_LINUX_FLAG_PRESENT);
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
struct cpuinfo_android_properties android_properties;
|
||||
cpuinfo_arm_android_parse_properties(&android_properties);
|
||||
#else
|
||||
char proc_cpuinfo_hardware[CPUINFO_HARDWARE_VALUE_MAX];
|
||||
#endif
|
||||
char proc_cpuinfo_revision[CPUINFO_REVISION_VALUE_MAX];
|
||||
|
||||
if (!cpuinfo_arm_linux_parse_proc_cpuinfo(
|
||||
#if defined(__ANDROID__)
|
||||
android_properties.proc_cpuinfo_hardware,
|
||||
#else
|
||||
proc_cpuinfo_hardware,
|
||||
#endif
|
||||
proc_cpuinfo_revision,
|
||||
arm_linux_processors_count,
|
||||
arm_linux_processors)) {
|
||||
cpuinfo_log_error("failed to parse processor information from /proc/cpuinfo");
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
|
||||
if (bitmask_all(arm_linux_processors[i].flags, valid_processor_mask)) {
|
||||
arm_linux_processors[i].flags |= CPUINFO_LINUX_FLAG_VALID;
|
||||
cpuinfo_log_debug("parsed processor %"PRIu32" MIDR 0x%08"PRIx32,
|
||||
i, arm_linux_processors[i].midr);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t valid_processors = 0, last_midr = 0;
|
||||
#if CPUINFO_ARCH_ARM
|
||||
uint32_t last_architecture_version = 0, last_architecture_flags = 0;
|
||||
#endif
|
||||
for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
|
||||
arm_linux_processors[i].system_processor_id = i;
|
||||
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
|
||||
valid_processors += 1;
|
||||
|
||||
if (!(arm_linux_processors[i].flags & CPUINFO_ARM_LINUX_VALID_PROCESSOR)) {
|
||||
/*
|
||||
* Processor is in possible and present lists, but not reported in /proc/cpuinfo.
|
||||
* This is fairly common: high-index processors can be not reported if they are offline.
|
||||
*/
|
||||
cpuinfo_log_info("processor %"PRIu32" is not listed in /proc/cpuinfo", i);
|
||||
}
|
||||
|
||||
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_ARM_LINUX_VALID_MIDR)) {
|
||||
last_midr = arm_linux_processors[i].midr;
|
||||
}
|
||||
#if CPUINFO_ARCH_ARM
|
||||
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_ARM_LINUX_VALID_ARCHITECTURE)) {
|
||||
last_architecture_version = arm_linux_processors[i].architecture_version;
|
||||
last_architecture_flags = arm_linux_processors[i].architecture_flags;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
/* Processor reported in /proc/cpuinfo, but not in possible and/or present lists: log and ignore */
|
||||
if (!(arm_linux_processors[i].flags & CPUINFO_ARM_LINUX_VALID_PROCESSOR)) {
|
||||
cpuinfo_log_warning("invalid processor %"PRIu32" reported in /proc/cpuinfo", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
const struct cpuinfo_arm_chipset chipset =
|
||||
cpuinfo_arm_android_decode_chipset(&android_properties, valid_processors, 0);
|
||||
#else
|
||||
const struct cpuinfo_arm_chipset chipset =
|
||||
cpuinfo_arm_linux_decode_chipset(proc_cpuinfo_hardware, proc_cpuinfo_revision, valid_processors, 0);
|
||||
#endif
|
||||
|
||||
#if CPUINFO_ARCH_ARM
|
||||
uint32_t isa_features = 0, isa_features2 = 0;
|
||||
#ifdef __ANDROID__
|
||||
/*
|
||||
* On Android before API 20, libc.so does not provide getauxval function.
|
||||
* Thus, we try to dynamically find it, or use two fallback mechanisms:
|
||||
* 1. dlopen libc.so, and try to find getauxval
|
||||
* 2. Parse /proc/self/auxv procfs file
|
||||
* 3. Use features reported in /proc/cpuinfo
|
||||
*/
|
||||
if (!cpuinfo_arm_linux_hwcap_from_getauxval(&isa_features, &isa_features2)) {
|
||||
/* getauxval can't be used, fall back to parsing /proc/self/auxv */
|
||||
if (!cpuinfo_arm_linux_hwcap_from_procfs(&isa_features, &isa_features2)) {
|
||||
/*
|
||||
* Reading /proc/self/auxv failed, probably due to file permissions.
|
||||
* Use information from /proc/cpuinfo to detect ISA.
|
||||
*
|
||||
* If different processors report different ISA features, take the intersection.
|
||||
*/
|
||||
uint32_t processors_with_features = 0;
|
||||
for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
|
||||
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID | CPUINFO_ARM_LINUX_VALID_FEATURES)) {
|
||||
if (processors_with_features == 0) {
|
||||
isa_features = arm_linux_processors[i].features;
|
||||
isa_features2 = arm_linux_processors[i].features2;
|
||||
} else {
|
||||
isa_features &= arm_linux_processors[i].features;
|
||||
isa_features2 &= arm_linux_processors[i].features2;
|
||||
}
|
||||
processors_with_features += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* On GNU/Linux getauxval is always available */
|
||||
cpuinfo_arm_linux_hwcap_from_getauxval(&isa_features, &isa_features2);
|
||||
#endif
|
||||
cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
|
||||
isa_features, isa_features2,
|
||||
last_midr, last_architecture_version, last_architecture_flags,
|
||||
&chipset, &cpuinfo_isa);
|
||||
#elif CPUINFO_ARCH_ARM64
|
||||
uint32_t isa_features = 0, isa_features2 = 0;
|
||||
/* getauxval is always available on ARM64 Android */
|
||||
cpuinfo_arm_linux_hwcap_from_getauxval(&isa_features, &isa_features2);
|
||||
cpuinfo_arm64_linux_decode_isa_from_proc_cpuinfo(
|
||||
isa_features, isa_features2, last_midr, &chipset, &cpuinfo_isa);
|
||||
#endif
|
||||
|
||||
/* Detect min/max frequency and package ID */
|
||||
for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
|
||||
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
|
||||
const uint32_t max_frequency = cpuinfo_linux_get_processor_max_frequency(i);
|
||||
if (max_frequency != 0) {
|
||||
arm_linux_processors[i].max_frequency = max_frequency;
|
||||
arm_linux_processors[i].flags |= CPUINFO_LINUX_FLAG_MAX_FREQUENCY;
|
||||
}
|
||||
|
||||
const uint32_t min_frequency = cpuinfo_linux_get_processor_min_frequency(i);
|
||||
if (min_frequency != 0) {
|
||||
arm_linux_processors[i].min_frequency = min_frequency;
|
||||
arm_linux_processors[i].flags |= CPUINFO_LINUX_FLAG_MIN_FREQUENCY;
|
||||
}
|
||||
|
||||
if (cpuinfo_linux_get_processor_package_id(i, &arm_linux_processors[i].package_id)) {
|
||||
arm_linux_processors[i].flags |= CPUINFO_LINUX_FLAG_PACKAGE_ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize topology group IDs */
|
||||
for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
|
||||
arm_linux_processors[i].package_leader_id = i;
|
||||
}
|
||||
|
||||
/* Propagate topology group IDs among siblings */
|
||||
for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
|
||||
if (!bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arm_linux_processors[i].flags & CPUINFO_LINUX_FLAG_PACKAGE_ID) {
|
||||
cpuinfo_linux_detect_core_siblings(
|
||||
arm_linux_processors_count, i,
|
||||
(cpuinfo_siblings_callback) cluster_siblings_parser,
|
||||
arm_linux_processors);
|
||||
}
|
||||
}
|
||||
|
||||
/* Propagate all cluster IDs */
|
||||
uint32_t clustered_processors = 0;
|
||||
for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
|
||||
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID | CPUINFO_LINUX_FLAG_PACKAGE_CLUSTER)) {
|
||||
clustered_processors += 1;
|
||||
|
||||
const uint32_t package_leader_id = arm_linux_processors[i].package_leader_id;
|
||||
if (package_leader_id < i) {
|
||||
arm_linux_processors[i].package_leader_id = arm_linux_processors[package_leader_id].package_leader_id;
|
||||
}
|
||||
|
||||
cpuinfo_log_debug("processor %"PRIu32" clustered with processor %"PRIu32" as inferred from system siblings lists",
|
||||
i, arm_linux_processors[i].package_leader_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (clustered_processors != valid_processors) {
|
||||
/*
|
||||
* Topology information about some or all logical processors may be unavailable, for the following reasons:
|
||||
* - Linux kernel is too old, or configured without support for topology information in sysfs.
|
||||
* - Core is offline, and Linux kernel is configured to not report topology for offline cores.
|
||||
*
|
||||
* In this case, we assign processors to clusters using two methods:
|
||||
* - Try heuristic cluster configurations (e.g. 6-core SoC usually has 4+2 big.LITTLE configuration).
|
||||
* - If heuristic failed, assign processors to core clusters in a sequential scan.
|
||||
*/
|
||||
if (!cpuinfo_arm_linux_detect_core_clusters_by_heuristic(valid_processors, arm_linux_processors_count, arm_linux_processors)) {
|
||||
cpuinfo_arm_linux_detect_core_clusters_by_sequential_scan(arm_linux_processors_count, arm_linux_processors);
|
||||
}
|
||||
}
|
||||
|
||||
cpuinfo_arm_linux_count_cluster_processors(arm_linux_processors_count, arm_linux_processors);
|
||||
|
||||
const uint32_t cluster_count = cpuinfo_arm_linux_detect_cluster_midr(
|
||||
&chipset,
|
||||
arm_linux_processors_count, valid_processors, arm_linux_processors);
|
||||
|
||||
/* Initialize core vendor, uarch, MIDR, and frequency for every logical processor */
|
||||
for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
|
||||
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
|
||||
const uint32_t cluster_leader = arm_linux_processors[i].package_leader_id;
|
||||
if (cluster_leader == i) {
|
||||
/* Cluster leader: decode core vendor and uarch */
|
||||
cpuinfo_arm_decode_vendor_uarch(
|
||||
arm_linux_processors[cluster_leader].midr,
|
||||
#if CPUINFO_ARCH_ARM
|
||||
!!(arm_linux_processors[cluster_leader].features & CPUINFO_ARM_LINUX_FEATURE_VFPV4),
|
||||
#endif
|
||||
&arm_linux_processors[cluster_leader].vendor,
|
||||
&arm_linux_processors[cluster_leader].uarch);
|
||||
} else {
|
||||
/* Cluster non-leader: copy vendor, uarch, MIDR, and frequency from cluster leader */
|
||||
arm_linux_processors[i].flags |= arm_linux_processors[cluster_leader].flags &
|
||||
(CPUINFO_ARM_LINUX_VALID_MIDR | CPUINFO_LINUX_FLAG_MAX_FREQUENCY);
|
||||
arm_linux_processors[i].midr = arm_linux_processors[cluster_leader].midr;
|
||||
arm_linux_processors[i].vendor = arm_linux_processors[cluster_leader].vendor;
|
||||
arm_linux_processors[i].uarch = arm_linux_processors[cluster_leader].uarch;
|
||||
arm_linux_processors[i].max_frequency = arm_linux_processors[cluster_leader].max_frequency;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
|
||||
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
|
||||
cpuinfo_log_debug("post-analysis processor %"PRIu32": MIDR %08"PRIx32" frequency %"PRIu32,
|
||||
i, arm_linux_processors[i].midr, arm_linux_processors[i].max_frequency);
|
||||
}
|
||||
}
|
||||
|
||||
qsort(arm_linux_processors, arm_linux_processors_count,
|
||||
sizeof(struct cpuinfo_arm_linux_processor), cmp_arm_linux_processor);
|
||||
|
||||
for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
|
||||
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
|
||||
cpuinfo_log_debug("post-sort processor %"PRIu32": system id %"PRIu32" MIDR %08"PRIx32" frequency %"PRIu32,
|
||||
i, arm_linux_processors[i].system_processor_id, arm_linux_processors[i].midr, arm_linux_processors[i].max_frequency);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t uarchs_count = 0;
|
||||
enum cpuinfo_uarch last_uarch;
|
||||
for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
|
||||
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
|
||||
if (uarchs_count == 0 || arm_linux_processors[i].uarch != last_uarch) {
|
||||
last_uarch = arm_linux_processors[i].uarch;
|
||||
uarchs_count += 1;
|
||||
}
|
||||
arm_linux_processors[i].uarch_index = uarchs_count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Assumptions:
|
||||
* - No SMP (i.e. each core supports only one hardware thread).
|
||||
* - Level 1 instruction and data caches are private to the core clusters.
|
||||
* - Level 2 and level 3 cache is shared between cores in the same cluster.
|
||||
*/
|
||||
cpuinfo_arm_chipset_to_string(&chipset, package.name);
|
||||
package.processor_count = valid_processors;
|
||||
package.core_count = valid_processors;
|
||||
package.cluster_count = cluster_count;
|
||||
|
||||
processors = calloc(valid_processors, sizeof(struct cpuinfo_processor));
|
||||
if (processors == NULL) {
|
||||
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" logical processors",
|
||||
valid_processors * sizeof(struct cpuinfo_processor), valid_processors);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cores = calloc(valid_processors, sizeof(struct cpuinfo_core));
|
||||
if (cores == NULL) {
|
||||
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" cores",
|
||||
valid_processors * sizeof(struct cpuinfo_core), valid_processors);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
clusters = calloc(cluster_count, sizeof(struct cpuinfo_cluster));
|
||||
if (clusters == NULL) {
|
||||
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" core clusters",
|
||||
cluster_count * sizeof(struct cpuinfo_cluster), cluster_count);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
uarchs = calloc(uarchs_count, sizeof(struct cpuinfo_uarch_info));
|
||||
if (uarchs == NULL) {
|
||||
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" microarchitectures",
|
||||
uarchs_count * sizeof(struct cpuinfo_uarch_info), uarchs_count);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
linux_cpu_to_processor_map = calloc(arm_linux_processors_count, sizeof(struct cpuinfo_processor*));
|
||||
if (linux_cpu_to_processor_map == NULL) {
|
||||
cpuinfo_log_error("failed to allocate %zu bytes for %"PRIu32" logical processor mapping entries",
|
||||
arm_linux_processors_count * sizeof(struct cpuinfo_processor*), arm_linux_processors_count);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
linux_cpu_to_core_map = calloc(arm_linux_processors_count, sizeof(struct cpuinfo_core*));
|
||||
if (linux_cpu_to_core_map == NULL) {
|
||||
cpuinfo_log_error("failed to allocate %zu bytes for %"PRIu32" core mapping entries",
|
||||
arm_linux_processors_count * sizeof(struct cpuinfo_core*), arm_linux_processors_count);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (uarchs_count > 1) {
|
||||
linux_cpu_to_uarch_index_map = calloc(arm_linux_processors_count, sizeof(uint32_t));
|
||||
if (linux_cpu_to_uarch_index_map == NULL) {
|
||||
cpuinfo_log_error("failed to allocate %zu bytes for %"PRIu32" uarch index mapping entries",
|
||||
arm_linux_processors_count * sizeof(uint32_t), arm_linux_processors_count);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
l1i = calloc(valid_processors, sizeof(struct cpuinfo_cache));
|
||||
if (l1i == NULL) {
|
||||
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L1I caches",
|
||||
valid_processors * sizeof(struct cpuinfo_cache), valid_processors);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
l1d = calloc(valid_processors, sizeof(struct cpuinfo_cache));
|
||||
if (l1d == NULL) {
|
||||
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L1D caches",
|
||||
valid_processors * sizeof(struct cpuinfo_cache), valid_processors);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
uint32_t uarchs_index = 0;
|
||||
for (uint32_t i = 0; i < arm_linux_processors_count; i++) {
|
||||
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_LINUX_FLAG_VALID)) {
|
||||
if (uarchs_index == 0 || arm_linux_processors[i].uarch != last_uarch) {
|
||||
last_uarch = arm_linux_processors[i].uarch;
|
||||
uarchs[uarchs_index] = (struct cpuinfo_uarch_info) {
|
||||
.uarch = arm_linux_processors[i].uarch,
|
||||
.midr = arm_linux_processors[i].midr,
|
||||
};
|
||||
uarchs_index += 1;
|
||||
}
|
||||
uarchs[uarchs_index - 1].processor_count += 1;
|
||||
uarchs[uarchs_index - 1].core_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t l2_count = 0, l3_count = 0, big_l3_size = 0, cluster_id = UINT32_MAX;
|
||||
/* Indication whether L3 (if it exists) is shared between all cores */
|
||||
bool shared_l3 = true;
|
||||
/* Populate cache infromation structures in l1i, l1d */
|
||||
for (uint32_t i = 0; i < valid_processors; i++) {
|
||||
if (arm_linux_processors[i].package_leader_id == arm_linux_processors[i].system_processor_id) {
|
||||
cluster_id += 1;
|
||||
clusters[cluster_id] = (struct cpuinfo_cluster) {
|
||||
.processor_start = i,
|
||||
.processor_count = arm_linux_processors[i].package_processor_count,
|
||||
.core_start = i,
|
||||
.core_count = arm_linux_processors[i].package_processor_count,
|
||||
.cluster_id = cluster_id,
|
||||
.package = &package,
|
||||
.vendor = arm_linux_processors[i].vendor,
|
||||
.uarch = arm_linux_processors[i].uarch,
|
||||
.midr = arm_linux_processors[i].midr,
|
||||
};
|
||||
}
|
||||
|
||||
processors[i].smt_id = 0;
|
||||
processors[i].core = cores + i;
|
||||
processors[i].cluster = clusters + cluster_id;
|
||||
processors[i].package = &package;
|
||||
processors[i].linux_id = (int) arm_linux_processors[i].system_processor_id;
|
||||
processors[i].cache.l1i = l1i + i;
|
||||
processors[i].cache.l1d = l1d + i;
|
||||
linux_cpu_to_processor_map[arm_linux_processors[i].system_processor_id] = &processors[i];
|
||||
|
||||
cores[i].processor_start = i;
|
||||
cores[i].processor_count = 1;
|
||||
cores[i].core_id = i;
|
||||
cores[i].cluster = clusters + cluster_id;
|
||||
cores[i].package = &package;
|
||||
cores[i].vendor = arm_linux_processors[i].vendor;
|
||||
cores[i].uarch = arm_linux_processors[i].uarch;
|
||||
cores[i].midr = arm_linux_processors[i].midr;
|
||||
linux_cpu_to_core_map[arm_linux_processors[i].system_processor_id] = &cores[i];
|
||||
|
||||
if (linux_cpu_to_uarch_index_map != NULL) {
|
||||
linux_cpu_to_uarch_index_map[arm_linux_processors[i].system_processor_id] =
|
||||
arm_linux_processors[i].uarch_index;
|
||||
}
|
||||
|
||||
struct cpuinfo_cache temp_l2 = { 0 }, temp_l3 = { 0 };
|
||||
cpuinfo_arm_decode_cache(
|
||||
arm_linux_processors[i].uarch,
|
||||
arm_linux_processors[i].package_processor_count,
|
||||
arm_linux_processors[i].midr,
|
||||
&chipset,
|
||||
cluster_id,
|
||||
arm_linux_processors[i].architecture_version,
|
||||
&l1i[i], &l1d[i], &temp_l2, &temp_l3);
|
||||
l1i[i].processor_start = l1d[i].processor_start = i;
|
||||
l1i[i].processor_count = l1d[i].processor_count = 1;
|
||||
#if CPUINFO_ARCH_ARM
|
||||
/* L1I reported in /proc/cpuinfo overrides defaults */
|
||||
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_ARM_LINUX_VALID_ICACHE)) {
|
||||
l1i[i] = (struct cpuinfo_cache) {
|
||||
.size = arm_linux_processors[i].proc_cpuinfo_cache.i_size,
|
||||
.associativity = arm_linux_processors[i].proc_cpuinfo_cache.i_assoc,
|
||||
.sets = arm_linux_processors[i].proc_cpuinfo_cache.i_sets,
|
||||
.partitions = 1,
|
||||
.line_size = arm_linux_processors[i].proc_cpuinfo_cache.i_line_length
|
||||
};
|
||||
}
|
||||
/* L1D reported in /proc/cpuinfo overrides defaults */
|
||||
if (bitmask_all(arm_linux_processors[i].flags, CPUINFO_ARM_LINUX_VALID_DCACHE)) {
|
||||
l1d[i] = (struct cpuinfo_cache) {
|
||||
.size = arm_linux_processors[i].proc_cpuinfo_cache.d_size,
|
||||
.associativity = arm_linux_processors[i].proc_cpuinfo_cache.d_assoc,
|
||||
.sets = arm_linux_processors[i].proc_cpuinfo_cache.d_sets,
|
||||
.partitions = 1,
|
||||
.line_size = arm_linux_processors[i].proc_cpuinfo_cache.d_line_length
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
if (temp_l3.size != 0) {
|
||||
/*
|
||||
* Assumptions:
|
||||
* - L2 is private to each core
|
||||
* - L3 is shared by cores in the same cluster
|
||||
* - If cores in different clusters report the same L3, it is shared between all cores.
|
||||
*/
|
||||
l2_count += 1;
|
||||
if (arm_linux_processors[i].package_leader_id == arm_linux_processors[i].system_processor_id) {
|
||||
if (cluster_id == 0) {
|
||||
big_l3_size = temp_l3.size;
|
||||
l3_count = 1;
|
||||
} else if (temp_l3.size != big_l3_size) {
|
||||
/* If some cores have different L3 size, L3 is not shared between all cores */
|
||||
shared_l3 = false;
|
||||
l3_count += 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* If some cores don't have L3 cache, L3 is not shared between all cores */
|
||||
shared_l3 = false;
|
||||
if (temp_l2.size != 0) {
|
||||
/* Assume L2 is shared by cores in the same cluster */
|
||||
if (arm_linux_processors[i].package_leader_id == arm_linux_processors[i].system_processor_id) {
|
||||
l2_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (l2_count != 0) {
|
||||
l2 = calloc(l2_count, sizeof(struct cpuinfo_cache));
|
||||
if (l2 == NULL) {
|
||||
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L2 caches",
|
||||
l2_count * sizeof(struct cpuinfo_cache), l2_count);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (l3_count != 0) {
|
||||
l3 = calloc(l3_count, sizeof(struct cpuinfo_cache));
|
||||
if (l3 == NULL) {
|
||||
cpuinfo_log_error("failed to allocate %zu bytes for descriptions of %"PRIu32" L3 caches",
|
||||
l3_count * sizeof(struct cpuinfo_cache), l3_count);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cluster_id = UINT32_MAX;
|
||||
uint32_t l2_index = UINT32_MAX, l3_index = UINT32_MAX;
|
||||
for (uint32_t i = 0; i < valid_processors; i++) {
|
||||
if (arm_linux_processors[i].package_leader_id == arm_linux_processors[i].system_processor_id) {
|
||||
cluster_id++;
|
||||
}
|
||||
|
||||
struct cpuinfo_cache dummy_l1i, dummy_l1d, temp_l2 = { 0 }, temp_l3 = { 0 };
|
||||
cpuinfo_arm_decode_cache(
|
||||
arm_linux_processors[i].uarch,
|
||||
arm_linux_processors[i].package_processor_count,
|
||||
arm_linux_processors[i].midr,
|
||||
&chipset,
|
||||
cluster_id,
|
||||
arm_linux_processors[i].architecture_version,
|
||||
&dummy_l1i, &dummy_l1d, &temp_l2, &temp_l3);
|
||||
|
||||
if (temp_l3.size != 0) {
|
||||
/*
|
||||
* Assumptions:
|
||||
* - L2 is private to each core
|
||||
* - L3 is shared by cores in the same cluster
|
||||
* - If cores in different clusters report the same L3, it is shared between all cores.
|
||||
*/
|
||||
l2_index += 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,
|
||||
};
|
||||
processors[i].cache.l2 = l2 + l2_index;
|
||||
if (arm_linux_processors[i].package_leader_id == arm_linux_processors[i].system_processor_id) {
|
||||
l3_index += 1;
|
||||
if (l3_index < l3_count) {
|
||||
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 =
|
||||
shared_l3 ? valid_processors : arm_linux_processors[i].package_processor_count,
|
||||
};
|
||||
}
|
||||
}
|
||||
if (shared_l3) {
|
||||
processors[i].cache.l3 = l3;
|
||||
} else if (l3_index < l3_count) {
|
||||
processors[i].cache.l3 = l3 + l3_index;
|
||||
}
|
||||
} else if (temp_l2.size != 0) {
|
||||
/* Assume L2 is shared by cores in the same cluster */
|
||||
if (arm_linux_processors[i].package_leader_id == arm_linux_processors[i].system_processor_id) {
|
||||
l2_index += 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 = arm_linux_processors[i].package_processor_count,
|
||||
};
|
||||
}
|
||||
processors[i].cache.l2 = l2 + l2_index;
|
||||
}
|
||||
}
|
||||
|
||||
/* Commit */
|
||||
cpuinfo_processors = processors;
|
||||
cpuinfo_cores = cores;
|
||||
cpuinfo_clusters = clusters;
|
||||
cpuinfo_packages = &package;
|
||||
cpuinfo_uarchs = uarchs;
|
||||
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_processors_count = valid_processors;
|
||||
cpuinfo_cores_count = valid_processors;
|
||||
cpuinfo_clusters_count = cluster_count;
|
||||
cpuinfo_packages_count = 1;
|
||||
cpuinfo_uarchs_count = uarchs_count;
|
||||
cpuinfo_cache_count[cpuinfo_cache_level_1i] = valid_processors;
|
||||
cpuinfo_cache_count[cpuinfo_cache_level_1d] = valid_processors;
|
||||
cpuinfo_cache_count[cpuinfo_cache_level_2] = l2_count;
|
||||
cpuinfo_cache_count[cpuinfo_cache_level_3] = l3_count;
|
||||
cpuinfo_max_cache_size = cpuinfo_arm_compute_max_cache_size(&processors[0]);
|
||||
|
||||
cpuinfo_linux_cpu_max = arm_linux_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;
|
||||
|
||||
__sync_synchronize();
|
||||
|
||||
cpuinfo_is_initialized = true;
|
||||
|
||||
processors = NULL;
|
||||
cores = NULL;
|
||||
clusters = NULL;
|
||||
uarchs = NULL;
|
||||
l1i = l1d = l2 = l3 = NULL;
|
||||
linux_cpu_to_processor_map = NULL;
|
||||
linux_cpu_to_core_map = NULL;
|
||||
linux_cpu_to_uarch_index_map = NULL;
|
||||
|
||||
cleanup:
|
||||
free(arm_linux_processors);
|
||||
free(processors);
|
||||
free(cores);
|
||||
free(clusters);
|
||||
free(uarchs);
|
||||
free(l1i);
|
||||
free(l1d);
|
||||
free(l2);
|
||||
free(l3);
|
||||
free(linux_cpu_to_processor_map);
|
||||
free(linux_cpu_to_core_map);
|
||||
free(linux_cpu_to_uarch_index_map);
|
||||
}
|
863
dep/cpuinfo/src/arm/linux/midr.c
Normal file
863
dep/cpuinfo/src/arm/linux/midr.c
Normal file
@ -0,0 +1,863 @@
|
||||
#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>
|
||||
#include <cpuinfo/common.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 preceeding 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 preceeding 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 preceeding 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;
|
||||
}
|
Reference in New Issue
Block a user