From ffa3dd1766f250cc305d3917fc1db8e6f20f7aae Mon Sep 17 00:00:00 2001 From: A1ive <10670106+a1ive@users.noreply.github.com> Date: Thu, 4 Jul 2024 10:49:20 +0900 Subject: [PATCH] update libcpuid --- libcpuid/asm-bits.c | 9 +- libcpuid/asm-bits.h | 33 +- libcpuid/cpuid_main.c | 613 +++++++++++++++---- libcpuid/libcpuid.h | 486 ++++++++++++++- libcpuid/libcpuid.vcxproj | 2 + libcpuid/libcpuid.vcxproj.filters | 6 + libcpuid/libcpuid_constants.h | 9 +- libcpuid/libcpuid_internal.h | 2 +- libcpuid/libcpuid_types.h | 10 +- libcpuid/libcpuid_util.c | 136 ++++- libcpuid/libcpuid_util.h | 8 + libcpuid/rdmsr.c | 250 +++++++- libcpuid/rdtsc.c | 113 +++- libcpuid/recog_amd.c | 56 +- libcpuid/recog_arm.c | 959 ++++++++++++++++++++++++++++++ libcpuid/recog_arm.h | 32 + libcpuid/recog_centaur.c | 10 + libcpuid/recog_intel.c | 51 +- libnw/cpuid.c | 10 +- 19 files changed, 2574 insertions(+), 221 deletions(-) create mode 100644 libcpuid/recog_arm.c create mode 100644 libcpuid/recog_arm.h diff --git a/libcpuid/asm-bits.c b/libcpuid/asm-bits.c index db1f2fbc..fb9be74f 100644 --- a/libcpuid/asm-bits.c +++ b/libcpuid/asm-bits.c @@ -25,6 +25,7 @@ */ #include "libcpuid.h" +#include "libcpuid_util.h" #include "asm-bits.h" int cpuid_exists_by_eflags(void) @@ -135,6 +136,8 @@ void exec_cpuid(uint32_t *regs) :"m"(regs) :"memory", "eax", "edi" ); +# else + UNUSED(regs); # endif /* COMPILER_GCC */ #else # ifdef COMPILER_MICROSOFT @@ -167,7 +170,7 @@ void exec_cpuid(uint32_t *regs) # endif /* COMPILER_MICROSOFT */ #endif } -#endif /* INLINE_ASSEMBLY_SUPPORTED */ +#endif /* INLINE_ASM_SUPPORTED */ #ifdef INLINE_ASM_SUPPORTED void cpu_rdtsc(uint64_t* result) @@ -516,6 +519,8 @@ void busy_sse_loop(int cycles) " jnz 1b\n" ::"a"(cycles) ); +#else + UNUSED(cycles); #endif #else # ifdef COMPILER_MICROSOFT @@ -829,4 +834,4 @@ void busy_sse_loop(int cycles) # endif /* COMPILER_MICROSOFT */ #endif /* COMPILER_GCC */ } -#endif /* INLINE_ASSEMBLY_SUPPORTED */ \ No newline at end of file +#endif /* INLINE_ASM_SUPPORTED */ diff --git a/libcpuid/asm-bits.h b/libcpuid/asm-bits.h index f9ab24e7..04b659d1 100644 --- a/libcpuid/asm-bits.h +++ b/libcpuid/asm-bits.h @@ -51,13 +51,32 @@ #if !defined(PLATFORM_X86) # define PLATFORM_X86 #endif -#elif defined(__ARMEL__) +#elif defined(__arm__) #if !defined(PLATFORM_ARM) # define PLATFORM_ARM #endif #elif defined(__aarch64__) -#if !defined(PLATFORM_ARM) +#if !defined(PLATFORM_AARCH64) # define PLATFORM_AARCH64 +/* + * Older assemblers don't recognize newer system register names, + * but we can still access them by the Sn_n_Cn_Cn_n syntax. + */ +# define SYS_ID_AA64PFR0_EL1 "S3_0_C0_C4_0" +# define SYS_ID_AA64PFR1_EL1 "S3_0_C0_C4_1" +# define SYS_ID_AA64PFR2_EL1 "S3_0_C0_C4_2" +# define SYS_ID_AA64ZFR0_EL1 "S3_0_C0_C4_4" +# define SYS_ID_AA64SMFR0_EL1 "S3_0_C0_C4_5" +# define SYS_ID_AA64DFR0_EL1 "S3_0_C0_C5_0" +# define SYS_ID_AA64DFR1_EL1 "S3_0_C0_C5_1" +# define SYS_ID_AA64ISAR0_EL1 "S3_0_C0_C6_0" +# define SYS_ID_AA64ISAR1_EL1 "S3_0_C0_C6_1" +# define SYS_ID_AA64ISAR2_EL1 "S3_0_C0_C6_2" +# define SYS_ID_AA64MMFR0_EL1 "S3_0_C0_C7_0" +# define SYS_ID_AA64MMFR1_EL1 "S3_0_C0_C7_1" +# define SYS_ID_AA64MMFR2_EL1 "S3_0_C0_C7_2" +# define SYS_ID_AA64MMFR3_EL1 "S3_0_C0_C7_3" +# define SYS_ID_AA64MMFR4_EL1 "S3_0_C0_C7_4" #endif #endif @@ -67,6 +86,16 @@ # define INLINE_ASM_SUPPORTED #endif +#ifdef INLINE_ASM_SUPPORTED +# if defined(COMPILER_GCC) || defined(COMPILER_CLANG) +# define cpu_exec_mrs(reg_name, reg_value) __asm __volatile("mrs %0, " reg_name : "=r" (reg_value)) +# elif defined(COMPILER_MICROSOFT) +# define cpu_exec_mrs(reg_name, reg_value) __asm { mrs reg_value, reg_name } +# else +# error "Unsupported compiler" +# endif /* COMPILER */ +#endif /* INLINE_ASM_SUPPORTED */ + int cpuid_exists_by_eflags(void); void exec_cpuid(uint32_t *regs); void busy_sse_loop(int cycles); diff --git a/libcpuid/cpuid_main.c b/libcpuid/cpuid_main.c index bd8b5466..1eaea034 100644 --- a/libcpuid/cpuid_main.c +++ b/libcpuid/cpuid_main.c @@ -26,6 +26,7 @@ #include "libcpuid.h" #include "libcpuid_internal.h" #include "recog_amd.h" +#include "recog_arm.h" #include "recog_centaur.h" #include "recog_intel.h" #include "asm-bits.h" @@ -36,6 +37,7 @@ #include #include #include +#include /* Implementation: */ @@ -63,11 +65,14 @@ static void raw_data_t_constructor(struct cpu_raw_data_t* raw) static void cpu_id_t_constructor(struct cpu_id_t* id) { memset(id, 0, sizeof(struct cpu_id_t)); + id->architecture = ARCHITECTURE_UNKNOWN; + id->feature_level = FEATURE_LEVEL_UNKNOWN; + id->vendor = VENDOR_UNKNOWN; id->l1_data_cache = id->l1_instruction_cache = id->l2_cache = id->l3_cache = id->l4_cache = -1; - id->l1_assoc = id->l1_data_assoc = id->l1_instruction_assoc = id->l2_assoc = id->l3_assoc = id->l4_assoc = -1; - id->l1_cacheline = id->l1_data_cacheline = id->l1_instruction_cacheline = id->l2_cacheline = id->l3_cacheline = id->l4_cacheline = -1; + id->l1_data_assoc = id->l1_instruction_assoc = id->l2_assoc = id->l3_assoc = id->l4_assoc = -1; + id->l1_data_cacheline = id->l1_instruction_cacheline = id->l2_cacheline = id->l3_cacheline = id->l4_cacheline = -1; id->l1_data_instances = id->l1_instruction_instances = id->l2_instances = id->l3_instances = id->l4_instances = -1; - id->sse_size = -1; + id->x86.sse_size = -1; init_affinity_mask(&id->affinity_mask); id->purpose = PURPOSE_GENERAL; } @@ -90,14 +95,14 @@ static void system_id_t_constructor(struct system_id_t* system) system->l4_total_instances = -1; } -static void apic_info_t_constructor(struct internal_apic_info_t* apic_info, logical_cpu_t logical_cpu) +static void topology_t_constructor(struct internal_topology_t* topology, logical_cpu_t logical_cpu) { - memset(apic_info, 0, sizeof(struct internal_apic_info_t)); - apic_info->apic_id = -1; - apic_info->package_id = -1; - apic_info->core_id = -1; - apic_info->smt_id = -1; - apic_info->logical_cpu = logical_cpu; + memset(topology, 0, sizeof(struct internal_topology_t)); + topology->apic_id = -1; + topology->package_id = -1; + topology->core_id = -1; + topology->smt_id = -1; + topology->logical_cpu = logical_cpu; } static void core_instances_t_constructor(struct internal_core_instances_t* data) @@ -119,11 +124,11 @@ static void type_info_array_t_constructor(struct internal_type_info_array_t* dat } static int16_t cpuid_find_index_system_id(struct system_id_t* system, cpu_purpose_t purpose, - struct internal_type_info_array_t* type_info, int32_t package_id, bool is_apic_supported) + struct internal_type_info_array_t* type_info, int32_t package_id, bool is_topology_supported) { int16_t i = 0; - if (is_apic_supported) { + if (is_topology_supported) { for (i = 0; i < system->num_cpu_types; i++) if ((system->cpu_types[i].purpose == purpose) && (type_info->data[i].package_id == package_id)) return i; @@ -143,6 +148,7 @@ static void cpuid_grow_raw_data_array(struct cpu_raw_data_array_t* raw_array, lo struct cpu_raw_data_t *tmp = NULL; if ((n <= 0) || (n < raw_array->num_raw)) return; + debugf(3, "Growing cpu_raw_data_array_t from %u to %u items\n", raw_array->num_raw, n); tmp = realloc(raw_array->raw, sizeof(struct cpu_raw_data_t) * n); if (tmp == NULL) { /* Memory allocation failure */ cpuid_set_error(ERR_NO_MEM); @@ -161,6 +167,7 @@ static void cpuid_grow_system_id(struct system_id_t* system, uint8_t n) struct cpu_id_t *tmp = NULL; if ((n <= 0) || (n < system->num_cpu_types)) return; + debugf(3, "Growing system_id_t from %u to %u items\n", system->num_cpu_types, n); tmp = realloc(system->cpu_types, sizeof(struct cpu_id_t) * n); if (tmp == NULL) { /* Memory allocation failure */ cpuid_set_error(ERR_NO_MEM); @@ -182,6 +189,7 @@ static void cpuid_grow_type_info(struct internal_type_info_array_t* type_info, u struct internal_type_info_t *tmp = NULL; if ((n <= 0) || (n < type_info->num)) return; + debugf(3, "Growing internal_type_info_t from %u to %u items\n", type_info->num, n); tmp = realloc(type_info->data, sizeof(struct internal_type_info_t) * n); if (tmp == NULL) { /* Memory allocation failure */ cpuid_set_error(ERR_NO_MEM); @@ -203,6 +211,16 @@ static void cpuid_free_type_info(struct internal_type_info_array_t* type_info) type_info->num = 0; } +static cpu_architecture_t cpuid_architecture_identify(struct cpu_raw_data_t* raw) +{ + if (raw->basic_cpuid[0][EAX] != 0x0 || raw->basic_cpuid[0][EBX] != 0x0 || raw->basic_cpuid[0][ECX] != 0x0 || raw->basic_cpuid[0][EDX] != 0x0) + return ARCHITECTURE_X86; + else if (raw->arm_midr != 0x0) + return ARCHITECTURE_ARM; + + return ARCHITECTURE_UNKNOWN; +} + /* get_total_cpus() system specific code: uses OS routines to determine total number of CPUs */ static int get_total_cpus(void) { @@ -341,10 +359,10 @@ static void load_features_common(struct cpu_raw_data_t* raw, struct cpu_id_t* da /* apply guesswork to check if the SSE unit width is 128 bit */ switch (data->vendor) { case VENDOR_AMD: - data->sse_size = (data->ext_family >= 16 && data->ext_family != 17) ? 128 : 64; + data->x86.sse_size = (data->x86.ext_family >= 16 && data->x86.ext_family != 17) ? 128 : 64; break; case VENDOR_INTEL: - data->sse_size = (data->family == 6 && data->ext_model >= 15) ? 128 : 64; + data->x86.sse_size = (data->x86.family == 6 && data->x86.ext_model >= 15) ? 128 : 64; break; default: break; @@ -414,25 +432,23 @@ static hypervisor_vendor_t cpuid_get_hypervisor(struct cpu_id_t* data) { HYPERVISOR_XEN , "XenVMMXenVMM" }, }; - memset(data->hypervisor_str, 0, VENDOR_STR_MAX); - /* Intel and AMD CPUs have reserved bit 31 of ECX of CPUID leaf 0x1 as the hypervisor present bit - Source: https://kb.vmware.com/s/article/1009458 */ + *Source: https://kb.vmware.com/s/article/1009458 */ switch (data->vendor) { - case VENDOR_AMD: - case VENDOR_INTEL: - break; - default: - return HYPERVISOR_UNKNOWN; + case VENDOR_AMD: + case VENDOR_INTEL: + break; + default: + return HYPERVISOR_UNKNOWN; } if (!data->flags[CPU_FEATURE_HYPERVISOR]) return HYPERVISOR_NONE; /* Intel and AMD have also reserved CPUID leaves 0x40000000 - 0x400000FF for software use. - Hypervisors can use these leaves to provide an interface to pass information - from the hypervisor to the guest operating system running inside a virtual machine. - The hypervisor bit indicates the presence of a hypervisor - and that it is safe to test these additional software leaves. */ + * Hypervisors can use these leaves to provide an interface to pass information + * from the hypervisor to the guest operating system running inside a virtual machine. + * The hypervisor bit indicates the presence of a hypervisor + * and that it is safe to test these additional software leaves. */ memset(hypervisor_fn40000000h, 0, sizeof(hypervisor_fn40000000h)); hypervisor_fn40000000h[EAX] = 0x40000000; cpu_exec_cpuid_ext(hypervisor_fn40000000h); @@ -458,19 +474,19 @@ static int cpuid_basic_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* dat if (data->vendor == VENDOR_UNKNOWN) return cpuid_set_error(ERR_CPU_UNKN); - data->architecture = ARCHITECTURE_X86; + basic = raw->basic_cpuid[0][EAX]; if (basic >= 1) { - data->family = (raw->basic_cpuid[1][EAX] >> 8) & 0xf; - data->model = (raw->basic_cpuid[1][EAX] >> 4) & 0xf; - data->stepping = raw->basic_cpuid[1][EAX] & 0xf; + data->x86.family = (raw->basic_cpuid[1][EAX] >> 8) & 0xf; + data->x86.model = (raw->basic_cpuid[1][EAX] >> 4) & 0xf; + data->x86.stepping = raw->basic_cpuid[1][EAX] & 0xf; xmodel = (raw->basic_cpuid[1][EAX] >> 16) & 0xf; xfamily = (raw->basic_cpuid[1][EAX] >> 20) & 0xff; - if (data->vendor == VENDOR_AMD && data->family < 0xf) - data->ext_family = data->family; + if (data->vendor == VENDOR_AMD && data->x86.family < 0xf) + data->x86.ext_family = data->x86.family; else - data->ext_family = data->family + xfamily; - data->ext_model = data->model + (xmodel << 4); + data->x86.ext_family = data->x86.family + xfamily; + data->x86.ext_model = data->x86.model + (xmodel << 4); } ext = raw->ext_cpuid[0][EAX] - 0x80000000; @@ -488,24 +504,20 @@ static int cpuid_basic_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* dat } load_features_common(raw, data); data->total_logical_cpus = get_total_cpus(); - data->hypervisor_vendor = cpuid_get_hypervisor(data); return cpuid_set_error(ERR_OK); } -static bool cpu_ident_apic_id(logical_cpu_t logical_cpu, struct cpu_raw_data_t* raw, struct internal_apic_info_t* apic_info) +static bool cpu_ident_id_x86(struct cpu_raw_data_t* raw, struct internal_topology_t* topology) { bool is_apic_id_supported = false; uint8_t subleaf; uint8_t level_type = 0; uint8_t mask_core_shift = 0; uint32_t mask_smt_shift, core_plus_mask_width, package_mask, core_mask, smt_mask = 0; - cpu_vendor_t vendor = VENDOR_UNKNOWN; char vendor_str[VENDOR_STR_MAX]; - apic_info_t_constructor(apic_info, logical_cpu); - /* Only AMD and Intel x86 CPUs support Extended Processor Topology Eumeration */ - vendor = cpuid_vendor_identify(raw->basic_cpuid[0], vendor_str); + const cpu_vendor_t vendor = cpuid_vendor_identify(raw->basic_cpuid[0], vendor_str); switch (vendor) { case VENDOR_INTEL: case VENDOR_AMD: @@ -527,7 +539,7 @@ static bool cpu_ident_apic_id(logical_cpu_t logical_cpu, struct cpu_raw_data_t* /* Check if leaf 0Bh is supported and if number of logical processors at this level type is greater than 0 */ if (!is_apic_id_supported || (raw->basic_cpuid[0][EAX] < 11) || (EXTRACTS_BITS(raw->basic_cpuid[11][EBX], 15, 0) == 0)) { - // Warning: APIC ID are not supported, core count can be wrong if SMT is disabled and cache instances count will not be available. + warnf("Warning: APIC ID are not supported, core count can be wrong if SMT is disabled and cache instances count will not be available.\n"); return false; } @@ -537,18 +549,18 @@ static bool cpu_ident_apic_id(logical_cpu_t logical_cpu, struct cpu_raw_data_t* /* Find mask and ID for SMT and cores */ for (subleaf = 0; (raw->intel_fn11[subleaf][EAX] != 0x0) && (raw->intel_fn11[subleaf][EBX] != 0x0) && (subleaf < MAX_INTELFN11_LEVEL); subleaf++) { - level_type = EXTRACTS_BITS(raw->intel_fn11[subleaf][ECX], 15, 8); - apic_info->apic_id = raw->intel_fn11[subleaf][EDX]; + level_type = EXTRACTS_BITS(raw->intel_fn11[subleaf][ECX], 15, 8); + topology->apic_id = raw->intel_fn11[subleaf][EDX]; switch (level_type) { case 0x01: mask_smt_shift = EXTRACTS_BITS(raw->intel_fn11[subleaf][EAX], 4, 0); smt_mask = ~(((uint32_t) -1) << mask_smt_shift); - apic_info->smt_id = apic_info->apic_id & smt_mask; + topology->smt_id = topology->apic_id & smt_mask; break; case 0x02: core_plus_mask_width = ~(((uint32_t) -1) << mask_core_shift); core_mask = core_plus_mask_width ^ smt_mask; - apic_info->core_id = apic_info->apic_id & core_mask; + topology->core_id = topology->apic_id & core_mask; break; default: break; @@ -557,11 +569,51 @@ static bool cpu_ident_apic_id(logical_cpu_t logical_cpu, struct cpu_raw_data_t* /* Find mask and ID for packages */ package_mask = ((uint32_t) -1) << mask_core_shift; - apic_info->package_id = apic_info->apic_id & package_mask; + topology->package_id = topology->apic_id & package_mask; return (level_type > 0); } +static bool cpu_ident_id_arm(struct cpu_raw_data_t* raw, struct internal_topology_t* topology) +{ + /* Documentation: Multiprocessor Affinity Register + https://developer.arm.com/documentation/ddi0601/2020-12/AArch64-Registers/MPIDR-EL1--Multiprocessor-Affinity-Register + */ + const bool aff0_is_threads = EXTRACTS_BIT(raw->arm_mpidr, 24); + if (aff0_is_threads) { + /* Aff0: the level identifies individual threads within a multithreaded core + On single-threaded CPUs this field has the value 0x00 */ + topology->smt_id = EXTRACTS_BITS(raw->arm_mpidr, 7, 0); // Aff0 + topology->core_id = EXTRACTS_BITS(raw->arm_mpidr, 15, 8); // Aff1 + topology->package_id = EXTRACTS_BITS(raw->arm_mpidr, 23, 16); // Aff2 + } + else { + topology->core_id = EXTRACTS_BITS(raw->arm_mpidr, 7, 0); // Aff0 + topology->package_id = EXTRACTS_BITS(raw->arm_mpidr, 15, 8); // Aff1 + } + + /* Always implemented since ARMv7 + https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/System-Control-Registers-in-a-PMSA-implementation/PMSA-System-control-registers-descriptions--in-register-order/MPIDR--Multiprocessor-Affinity-Register--PMSA?lang=en + */ + return true; +} + +static bool cpu_ident_id(logical_cpu_t logical_cpu, struct cpu_raw_data_t* raw, struct internal_topology_t* topology) +{ + topology_t_constructor(topology, logical_cpu); + + const cpu_architecture_t architecture = cpuid_architecture_identify(raw); + switch (architecture) { + case ARCHITECTURE_X86: + return cpu_ident_id_x86(raw, topology); + case ARCHITECTURE_ARM: + return cpu_ident_id_arm(raw, topology); + default: + break; + } + + return false; +} /* Interface: */ @@ -572,7 +624,14 @@ int cpuid_get_total_cpus(void) int cpuid_present(void) { +#if defined(PLATFORM_X86) || defined(PLATFORM_X64) return cpuid_exists_by_eflags(); +#elif defined(PLATFORM_AARCH64) + /* On AArch64, return 0 by default */ + return 0; +#else + return 0; +#endif } void cpu_exec_cpuid(uint32_t eax, uint32_t* regs) @@ -589,9 +648,10 @@ void cpu_exec_cpuid_ext(uint32_t* regs) int cpuid_get_raw_data(struct cpu_raw_data_t* data) { - unsigned i; if (!cpuid_present()) return cpuid_set_error(ERR_NO_CPUID); +#if defined(PLATFORM_X86) || defined(PLATFORM_X64) + unsigned i; for (i = 0; i < 32; i++) cpu_exec_cpuid(i, data->basic_cpuid[i]); for (i = 0; i < 32; i++) @@ -626,6 +686,43 @@ int cpuid_get_raw_data(struct cpu_raw_data_t* data) data->amd_fn8000001dh[i][ECX] = i; cpu_exec_cpuid_ext(data->amd_fn8000001dh[i]); } + for (i = 0; i < MAX_AMDFN80000026H_LEVEL; i++) { + memset(data->amd_fn80000026h[i], 0, sizeof(data->amd_fn80000026h[i])); + data->amd_fn80000026h[i][EAX] = 0x80000026; + data->amd_fn80000026h[i][ECX] = i; + cpu_exec_cpuid_ext(data->amd_fn80000026h[i]); + } +#elif defined(PLATFORM_ARM) + /* We cannot support ARM CPUs running in 32-bit mode, because the Main ID Register is accessible only in privileged modes + Some related links: + - https://github.com/anrieff/libcpuid/issues/96 + - https://developer.arm.com/documentation/ddi0406/b/System-Level-Architecture/Protected-Memory-System-Architecture--PMSA-/CP15-registers-for-a-PMSA-implementation/c0--Main-ID-Register--MIDR- + */ +# warning The 32-bit ARM platform is not supported (Main ID Register is accessible only in privileged modes) + UNUSED(data); +#elif defined(PLATFORM_AARCH64) + cpu_exec_mrs("MIDR_EL1", data->arm_midr); + cpu_exec_mrs("MPIDR_EL1", data->arm_mpidr); + cpu_exec_mrs("REVIDR_EL1", data->arm_revidr); + cpu_exec_mrs(SYS_ID_AA64DFR0_EL1, data->arm_id_aa64dfr[0]); + cpu_exec_mrs(SYS_ID_AA64DFR1_EL1, data->arm_id_aa64dfr[1]); + cpu_exec_mrs(SYS_ID_AA64ISAR0_EL1, data->arm_id_aa64isar[0]); + cpu_exec_mrs(SYS_ID_AA64ISAR1_EL1, data->arm_id_aa64isar[1]); + cpu_exec_mrs(SYS_ID_AA64ISAR2_EL1, data->arm_id_aa64isar[2]); + cpu_exec_mrs(SYS_ID_AA64MMFR0_EL1, data->arm_id_aa64mmfr[0]); + cpu_exec_mrs(SYS_ID_AA64MMFR1_EL1, data->arm_id_aa64mmfr[1]); + cpu_exec_mrs(SYS_ID_AA64MMFR2_EL1, data->arm_id_aa64mmfr[2]); + cpu_exec_mrs(SYS_ID_AA64MMFR3_EL1, data->arm_id_aa64mmfr[3]); + cpu_exec_mrs(SYS_ID_AA64MMFR4_EL1, data->arm_id_aa64mmfr[4]); + cpu_exec_mrs(SYS_ID_AA64PFR0_EL1, data->arm_id_aa64pfr[0]); + cpu_exec_mrs(SYS_ID_AA64PFR1_EL1, data->arm_id_aa64pfr[1]); + cpu_exec_mrs(SYS_ID_AA64PFR2_EL1, data->arm_id_aa64pfr[2]); + cpu_exec_mrs(SYS_ID_AA64SMFR0_EL1, data->arm_id_aa64smfr[0]); + cpu_exec_mrs(SYS_ID_AA64ZFR0_EL1, data->arm_id_aa64zfr[0]); +#else +# warning This CPU architecture is not supported by libcpuid + UNUSED(data); +#endif return cpuid_set_error(ERR_OK); } @@ -643,6 +740,7 @@ int cpuid_get_all_raw_data(struct cpu_raw_data_array_t* data) cpu_raw_data_array_t_constructor(data, true); while (set_cpu_affinity(logical_cpu) || logical_cpu == 0) { + debugf(2, "Getting raw dump for logical CPU %i\n", logical_cpu); cpuid_grow_raw_data_array(data, logical_cpu + 1); raw_ptr = &data->raw[logical_cpu]; cur_error = cpuid_get_raw_data(raw_ptr); @@ -668,26 +766,55 @@ int cpu_ident_internal(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct } cpu_id_t_constructor(data); memset(internal->cache_mask, 0, sizeof(internal->cache_mask)); - if ((r = cpuid_basic_identify(raw, data)) < 0) - return cpuid_set_error(r); - switch (data->vendor) { - case VENDOR_INTEL: - r = cpuid_identify_intel(raw, data, internal); - break; - case VENDOR_AMD: - case VENDOR_HYGON: - r = cpuid_identify_amd(raw, data, internal); + data->architecture = cpuid_architecture_identify(raw); + + switch (data->architecture) { + case ARCHITECTURE_X86: + if ((r = cpuid_basic_identify(raw, data)) < 0) + return cpuid_set_error(r); + switch (data->vendor) { + case VENDOR_INTEL: + r = cpuid_identify_intel(raw, data, internal); + break; + case VENDOR_AMD: + case VENDOR_HYGON: + r = cpuid_identify_amd(raw, data, internal); + break; + case VENDOR_CENTAUR: + r = cpuid_identify_centaur(raw, data, internal); + break; + default: + break; + } break; - case VENDOR_CENTAUR: - r = cpuid_identify_centaur(raw, data, internal); + case ARCHITECTURE_ARM: + r = cpuid_identify_arm(raw, data); break; default: + r = ERR_CPU_UNKN; break; } + +#ifndef LIBCPUID_DISABLE_DEPRECATED +# if defined(__GNUC__) || defined(GNUC) +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# elif defined(__clang__) +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +# endif /* Backward compatibility */ /* - Deprecated since v0.5.0 */ data->l1_assoc = data->l1_data_assoc; data->l1_cacheline = data->l1_data_cacheline; + /* - Deprecated since v0.7.0 */ + data->family = data->x86.family; + data->model = data->x86.model; + data->stepping = data->x86.stepping; + data->ext_family = data->x86.ext_family; + data->ext_model = data->x86.ext_model; + data->sse_size = data->x86.sse_size; + data->sgx = data->x86.sgx; +#endif /* LIBCPUID_DISABLE_DEPRECATED */ + return cpuid_set_error(r); } @@ -697,24 +824,30 @@ static cpu_purpose_t cpu_ident_purpose(struct cpu_raw_data_t* raw) cpu_purpose_t purpose = PURPOSE_GENERAL; char vendor_str[VENDOR_STR_MAX]; - vendor = cpuid_vendor_identify(raw->basic_cpuid[0], vendor_str); - if (vendor == VENDOR_UNKNOWN) { - cpuid_set_error(ERR_CPU_UNKN); - return purpose; - } - - switch (vendor) { - case VENDOR_AMD: - purpose = cpuid_identify_purpose_amd(raw); + const cpu_architecture_t architecture = cpuid_architecture_identify(raw); + switch (architecture) { + case ARCHITECTURE_X86: + vendor = cpuid_vendor_identify(raw->basic_cpuid[0], vendor_str); + switch (vendor) { + case VENDOR_AMD: + purpose = cpuid_identify_purpose_amd(raw); + break; + case VENDOR_INTEL: + purpose = cpuid_identify_purpose_intel(raw); + break; + default: + break; + } break; - case VENDOR_INTEL: - purpose = cpuid_identify_purpose_intel(raw); + case ARCHITECTURE_ARM: + purpose = cpuid_identify_purpose_arm(raw); break; default: - purpose = PURPOSE_GENERAL; break; } + debugf(3, "Identified a '%s' CPU core type\n", cpu_purpose_str(purpose)); + return purpose; } @@ -727,46 +860,54 @@ int cpu_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* data) } static void update_core_instances(struct internal_core_instances_t* cores, - struct internal_apic_info_t* apic_info) + struct internal_topology_t* topology) { uint32_t core_id_index = 0; - core_id_index = apic_info->core_id % CORES_HTABLE_SIZE; - if ((cores->htable[core_id_index].core_id == 0) || (cores->htable[core_id_index].core_id == apic_info->core_id)) { + core_id_index = topology->core_id % CORES_HTABLE_SIZE; + if ((cores->htable[core_id_index].core_id == 0) || (cores->htable[core_id_index].core_id == topology->core_id)) { if (cores->htable[core_id_index].num_logical_cpu == 0) cores->instances++; - cores->htable[core_id_index].core_id = apic_info->core_id; + cores->htable[core_id_index].core_id = topology->core_id; cores->htable[core_id_index].num_logical_cpu++; } else { - // Warning: update_core_instances: collision at core_id_index + warnf("update_core_instances: collision at index %u (core ID is %i, not %i)\n", + core_id_index, cores->htable[core_id_index].core_id, topology->core_id); } } static void update_cache_instances(struct internal_cache_instances_t* caches, - struct internal_apic_info_t* apic_info, - struct internal_id_info_t* id_info) + struct internal_topology_t* topology, + struct internal_id_info_t* id_info, + bool debugf_is_needed) { uint32_t cache_id_index = 0; cache_type_t level; for (level = 0; level < NUM_CACHE_TYPES; level++) { if (id_info->cache_mask[level] == 0x00000000) { - apic_info->cache_id[level] = -1; + topology->cache_id[level] = -1; continue; } - apic_info->cache_id[level] = apic_info->apic_id & id_info->cache_mask[level]; - cache_id_index = apic_info->cache_id[level] % CACHES_HTABLE_SIZE; - if ((caches->htable[level][cache_id_index].cache_id == 0) || (caches->htable[level][cache_id_index].cache_id == apic_info->cache_id[level])) { + topology->cache_id[level] = topology->apic_id & id_info->cache_mask[level]; + cache_id_index = topology->cache_id[level] % CACHES_HTABLE_SIZE; + if ((caches->htable[level][cache_id_index].cache_id == 0) || (caches->htable[level][cache_id_index].cache_id == topology->cache_id[level])) { if (caches->htable[level][cache_id_index].num_logical_cpu == 0) caches->instances[level]++; - caches->htable[level][cache_id_index].cache_id = apic_info->cache_id[level]; + caches->htable[level][cache_id_index].cache_id = topology->cache_id[level]; caches->htable[level][cache_id_index].num_logical_cpu++; } else { - // Warning: collision at index cache_id_index + warnf("update_cache_instances: collision at index %u (cache ID is %i, not %i)\n", + cache_id_index, caches->htable[level][cache_id_index].cache_id, topology->cache_id[level]); } } + + if (debugf_is_needed) + debugf(3, "Logical CPU %4u: APIC ID %4i, package ID %4i, core ID %4i, thread %i, L1I$ ID %4i, L1D$ ID %4i, L2$ ID %4i, L3$ ID %4i, L4$ ID %4i\n", + topology->logical_cpu, topology->apic_id, topology->package_id, topology->core_id, topology->smt_id, + topology->cache_id[L1I], topology->cache_id[L1D], topology->cache_id[L2], topology->cache_id[L3], topology->cache_id[L4]); } int cpu_identify_all(struct cpu_raw_data_array_t* raw_array, struct system_id_t* system) @@ -775,13 +916,13 @@ int cpu_identify_all(struct cpu_raw_data_array_t* raw_array, struct system_id_t* int ret_error = cpuid_set_error(ERR_OK); double smt_divisor; bool is_smt_supported; - bool is_apic_supported = true; + bool is_topology_supported = true; int16_t cpu_type_index = -1; int32_t cur_package_id = 0; logical_cpu_t logical_cpu = 0; cpu_purpose_t purpose; cpu_affinity_mask_t affinity_mask; - struct internal_apic_info_t apic_info; + struct internal_topology_t topology; struct internal_type_info_array_t type_info; struct internal_cache_instances_t* caches_all = malloc(sizeof(*caches_all)); @@ -792,7 +933,6 @@ int cpu_identify_all(struct cpu_raw_data_array_t* raw_array, struct system_id_t* free(caches_all); return cpuid_set_error(ERR_HANDLE); } - system_id_t_constructor(system); type_info_array_t_constructor(&type_info); cache_instances_t_constructor(caches_all); @@ -801,29 +941,30 @@ int cpu_identify_all(struct cpu_raw_data_array_t* raw_array, struct system_id_t* /* Iterate over all raw */ for (logical_cpu = 0; logical_cpu < raw_array->num_raw; logical_cpu++) { + debugf(2, "Identifying logical core %u\n", logical_cpu); /* Get CPU purpose and APIC ID For hybrid CPUs, the purpose may be different than the previous iteration (e.g. from P-cores to E-cores) APIC ID are unique for each logical CPU cores. */ purpose = cpu_ident_purpose(&raw_array->raw[logical_cpu]); - if (raw_array->with_affinity && is_apic_supported) { - is_apic_supported = cpu_ident_apic_id(logical_cpu, &raw_array->raw[logical_cpu], &apic_info); - if (is_apic_supported) - cur_package_id = apic_info.package_id; + if (raw_array->with_affinity && is_topology_supported) { + is_topology_supported = cpu_ident_id(logical_cpu, &raw_array->raw[logical_cpu], &topology); + if (is_topology_supported) + cur_package_id = topology.package_id; } /* Put data to system->cpu_types on the first iteration or when purpose is new. For motherboards with multiple CPUs, we grow the array when package ID is different. */ - cpu_type_index = cpuid_find_index_system_id(system, purpose, &type_info, cur_package_id, is_apic_supported); + cpu_type_index = cpuid_find_index_system_id(system, purpose, &type_info, cur_package_id, is_topology_supported); if (cpu_type_index < 0) { - cpu_type_index = system->num_cpu_types; + cpu_type_index = system->num_cpu_types; cpuid_grow_system_id(system, system->num_cpu_types + 1); cpuid_grow_type_info(&type_info, type_info.num + 1); cur_error = cpu_ident_internal(&raw_array->raw[logical_cpu], &system->cpu_types[cpu_type_index], &type_info.data[cpu_type_index].id_info); type_info.data[cpu_type_index].purpose = purpose; if (ret_error == ERR_OK) ret_error = cur_error; - if (is_apic_supported) + if (is_topology_supported) type_info.data[cpu_type_index].package_id = cur_package_id; if (raw_array->with_affinity) system->cpu_types[cpu_type_index].num_logical_cpus = 0; @@ -833,10 +974,10 @@ int cpu_identify_all(struct cpu_raw_data_array_t* raw_array, struct system_id_t* if (raw_array->with_affinity) { set_affinity_mask_bit(logical_cpu, &system->cpu_types[cpu_type_index].affinity_mask); system->cpu_types[cpu_type_index].num_logical_cpus++; - if (is_apic_supported) { - update_core_instances(&type_info.data[cpu_type_index].core_instances, &apic_info); - update_cache_instances(&type_info.data[cpu_type_index].cache_instances, &apic_info, &type_info.data[cpu_type_index].id_info); - update_cache_instances(caches_all, &apic_info, &type_info.data[cpu_type_index].id_info); + if (is_topology_supported) { + update_core_instances(&type_info.data[cpu_type_index].core_instances, &topology); + update_cache_instances(&type_info.data[cpu_type_index].cache_instances, &topology, &type_info.data[cpu_type_index].id_info, true); + update_cache_instances(caches_all, &topology, &type_info.data[cpu_type_index].id_info, false); } } } @@ -845,7 +986,7 @@ int cpu_identify_all(struct cpu_raw_data_array_t* raw_array, struct system_id_t* for (cpu_type_index = 0; cpu_type_index < system->num_cpu_types; cpu_type_index++) { /* Overwrite core and cache counters when information is available per core */ if (raw_array->with_affinity) { - if (is_apic_supported) { + if (is_topology_supported) { system->cpu_types[cpu_type_index].num_cores = type_info.data[cpu_type_index].core_instances.instances; system->cpu_types[cpu_type_index].l1_instruction_instances = type_info.data[cpu_type_index].cache_instances.instances[L1I]; system->cpu_types[cpu_type_index].l1_data_instances = type_info.data[cpu_type_index].cache_instances.instances[L1D]; @@ -867,7 +1008,7 @@ int cpu_identify_all(struct cpu_raw_data_array_t* raw_array, struct system_id_t* cpuid_free_type_info(&type_info); /* Update the grand total of cache instances */ - if (is_apic_supported) { + if (is_topology_supported) { system->l1_instruction_total_instances = caches_all->instances[L1I]; system->l1_data_total_instances = caches_all->instances[L1D]; system->l2_total_instances = caches_all->instances[L2]; @@ -875,7 +1016,6 @@ int cpu_identify_all(struct cpu_raw_data_array_t* raw_array, struct system_id_t* system->l4_total_instances = caches_all->instances[L4]; } - free(caches_all); return ret_error; } @@ -912,8 +1052,7 @@ const char* cpu_architecture_str(cpu_architecture_t architecture) }; unsigned i, n = COUNT_OF(matchtable); if (n != NUM_CPU_ARCHITECTURES + 1) { - // Warning: incomplete library - // architecture matchtable size differs from the actual number of architectures. + warnf("Warning: incomplete library, architecture matchtable size differs from the actual number of architectures.\n"); } for (i = 0; i < n; i++) if (matchtable[i].architecture == architecture) @@ -921,6 +1060,65 @@ const char* cpu_architecture_str(cpu_architecture_t architecture) return ""; } +const char* cpu_feature_level_str(cpu_feature_level_t level) +{ + const struct { cpu_feature_level_t level; const char* name; } + matchtable[] = { + { FEATURE_LEVEL_UNKNOWN, "unknown" }, + /* x86 */ + { CPU_FEATURE_LEVEL_I386, "i386" }, + { CPU_FEATURE_LEVEL_I486, "i486" }, + { CPU_FEATURE_LEVEL_I586, "i586" }, + { CPU_FEATURE_LEVEL_I686, "i686" }, + { CPU_FEATURE_LEVEL_X86_64_V1, "x86-64-v1" }, + { CPU_FEATURE_LEVEL_X86_64_V2, "x86-64-v2" }, + { CPU_FEATURE_LEVEL_X86_64_V3, "x86-64-v3" }, + { CPU_FEATURE_LEVEL_X86_64_V4, "x86-64-v4" }, + /* ARM */ + { FEATURE_LEVEL_ARM_V1, "ARMv1" }, + { FEATURE_LEVEL_ARM_V2, "ARMv2" }, + { FEATURE_LEVEL_ARM_V3, "ARMv3" }, + { FEATURE_LEVEL_ARM_V4, "ARMv4" }, + { FEATURE_LEVEL_ARM_V4T, "ARMv4T" }, + { FEATURE_LEVEL_ARM_V5, "ARMv5" }, + { FEATURE_LEVEL_ARM_V5T, "ARMv5T" }, + { FEATURE_LEVEL_ARM_V5TE, "ARMv5TE" }, + { FEATURE_LEVEL_ARM_V5TEJ, "ARMv5TEJ" }, + { FEATURE_LEVEL_ARM_V6, "ARMv6" }, + { FEATURE_LEVEL_ARM_V6_M, "ARMv6-M" }, + { FEATURE_LEVEL_ARM_V7_A, "ARMv7-A" }, + { FEATURE_LEVEL_ARM_V7_M, "ARMv7-M" }, + { FEATURE_LEVEL_ARM_V7_R, "ARMv7-R" }, + { FEATURE_LEVEL_ARM_V7E_M, "ARMv7E-M" }, + { FEATURE_LEVEL_ARM_V8_0_A, "ARMv8.0-A" }, + { FEATURE_LEVEL_ARM_V8_0_M, "ARMv8.0-M" }, + { FEATURE_LEVEL_ARM_V8_0_R, "ARMv8.0-R" }, + { FEATURE_LEVEL_ARM_V8_1_A, "ARMv8.1-A" }, + { FEATURE_LEVEL_ARM_V8_1_M, "ARMv8.1-M" }, + { FEATURE_LEVEL_ARM_V8_2_A, "ARMv8.2-A" }, + { FEATURE_LEVEL_ARM_V8_3_A, "ARMv8.3-A" }, + { FEATURE_LEVEL_ARM_V8_4_A, "ARMv8.4-A" }, + { FEATURE_LEVEL_ARM_V8_5_A, "ARMv8.5-A" }, + { FEATURE_LEVEL_ARM_V8_6_A, "ARMv8.6-A" }, + { FEATURE_LEVEL_ARM_V8_7_A, "ARMv8.7-A" }, + { FEATURE_LEVEL_ARM_V8_8_A, "ARMv8.8-A" }, + { FEATURE_LEVEL_ARM_V8_9_A, "ARMv8.9-A" }, + { FEATURE_LEVEL_ARM_V9_0_A, "ARMv9.0-A" }, + { FEATURE_LEVEL_ARM_V9_1_A, "ARMv9.1-A" }, + { FEATURE_LEVEL_ARM_V9_2_A, "ARMv9.2-A" }, + { FEATURE_LEVEL_ARM_V9_3_A, "ARMv9.3-A" }, + { FEATURE_LEVEL_ARM_V9_4_A, "ARMv9.4-A" }, + }; + unsigned i, n = COUNT_OF(matchtable); + if (n != (NUM_CPU_FEATURE_LEVELS - FEATURE_LEVEL_ARM_V1) + (CPU_FEATURE_LEVEL_X86_64_V4 - CPU_FEATURE_LEVEL_I386) + 2) { + warnf("Warning: incomplete library, feature level matchtable size differs from the actual number of levels.\n"); + } + for (i = 0; i < n; i++) + if (matchtable[i].level == level) + return matchtable[i].name; + return ""; +} + const char* cpu_purpose_str(cpu_purpose_t purpose) { const struct { cpu_purpose_t purpose; const char* name; } @@ -929,11 +1127,11 @@ const char* cpu_purpose_str(cpu_purpose_t purpose) { PURPOSE_PERFORMANCE, "performance" }, { PURPOSE_EFFICIENCY, "efficiency" }, { PURPOSE_LP_EFFICIENCY, "low-power efficiency" }, + { PURPOSE_U_PERFORMANCE, "ultimate performance" }, }; unsigned i, n = COUNT_OF(matchtable); if (n != NUM_CPU_PURPOSES) { - // Warning: incomplete library - // purpose matchtable size differs from the actual number of purposes. + warnf("Warning: incomplete library, purpose matchtable size differs from the actual number of purposes.\n"); } for (i = 0; i < n; i++) if (matchtable[i].purpose == purpose) @@ -1089,11 +1287,214 @@ const char* cpu_feature_str(cpu_feature_t feature) { CPU_FEATURE_HYPERVISOR, "hypervisor" }, { CPU_FEATURE_INTEL_DTS, "intel_dts" }, { CPU_FEATURE_INTEL_PTM, "intel_ptm" }, + { CPU_FEATURE_ASID16, "asid16" }, + { CPU_FEATURE_ADVSIMD, "advsimd" }, + { CPU_FEATURE_CRC32, "crc32" }, + { CPU_FEATURE_CSV2_1P1, "csv2_1p1" }, + { CPU_FEATURE_CSV2_1P2, "csv2_1p2" }, + { CPU_FEATURE_CSV2_2, "csv2_2" }, + { CPU_FEATURE_CSV2_3, "csv2_3" }, + { CPU_FEATURE_DOUBLELOCK, "doublelock" }, + { CPU_FEATURE_ETS2, "ets2" }, + { CPU_FEATURE_FP, "fp" }, + { CPU_FEATURE_MIXEDEND, "mixedend" }, + { CPU_FEATURE_MIXEDENDEL0, "mixedendel0" }, + { CPU_FEATURE_PMULL, "pmull" }, + { CPU_FEATURE_PMUV3, "pmuv3" }, + { CPU_FEATURE_SHA1, "sha1" }, + { CPU_FEATURE_SHA256, "sha256" }, + { CPU_FEATURE_HAFDBS, "hafdbs" }, + { CPU_FEATURE_HPDS, "hpds" }, + { CPU_FEATURE_LOR, "lor" }, + { CPU_FEATURE_LSE, "lse" }, + { CPU_FEATURE_PAN, "pan" }, + { CPU_FEATURE_PMUV3P1, "pmuv3p1" }, + { CPU_FEATURE_RDM, "rdm" }, + { CPU_FEATURE_VHE, "vhe" }, + { CPU_FEATURE_VMID16, "vmid16" }, + //{ CPU_FEATURE_AA32HPD, "aa32hpd" }, + //{ CPU_FEATURE_AA32I8MM, "aa32i8mm" }, + { CPU_FEATURE_DPB, "dpb" }, + { CPU_FEATURE_DEBUGV8P2, "debugv8p2" }, + { CPU_FEATURE_F32MM, "f32mm" }, + { CPU_FEATURE_F64MM, "f64mm" }, + { CPU_FEATURE_FP16, "fp16" }, + { CPU_FEATURE_HPDS2, "hpds2" }, + { CPU_FEATURE_I8MM, "i8mm" }, + { CPU_FEATURE_IESB, "iesb" }, + { CPU_FEATURE_LPA, "lpa" }, + { CPU_FEATURE_LSMAOC, "lsmaoc" }, + { CPU_FEATURE_LVA, "lva" }, + { CPU_FEATURE_PAN2, "pan2" }, + { CPU_FEATURE_RAS, "ras" }, + { CPU_FEATURE_SHA3, "sha3" }, + { CPU_FEATURE_SHA512, "sha512" }, + { CPU_FEATURE_SM3, "sm3" }, + { CPU_FEATURE_SM4, "sm4" }, + { CPU_FEATURE_SPE, "spe" }, + { CPU_FEATURE_SVE, "sve" }, + { CPU_FEATURE_TTCNP, "ttcnp" }, + { CPU_FEATURE_UAO, "uao" }, + { CPU_FEATURE_XNX, "xnx" }, + { CPU_FEATURE_CCIDX, "ccidx" }, + { CPU_FEATURE_CONSTPACFIELD, "constpacfield" }, + { CPU_FEATURE_EPAC, "epac" }, + { CPU_FEATURE_FCMA, "fcma" }, + { CPU_FEATURE_FPAC, "fpac" }, + { CPU_FEATURE_FPACCOMBINE, "fpaccombine" }, + { CPU_FEATURE_JSCVT, "jscvt" }, + { CPU_FEATURE_LRCPC, "lrcpc" }, + { CPU_FEATURE_PACIMP, "pacimp" }, + { CPU_FEATURE_PACQARMA3, "pacqarma3" }, + { CPU_FEATURE_PACQARMA5, "pacqarma5" }, + { CPU_FEATURE_PAUTH, "pauth" }, + { CPU_FEATURE_SPEV1P1, "spev1p1" }, + { CPU_FEATURE_AMUV1, "amuv1" }, + { CPU_FEATURE_BBM, "bbm" }, + { CPU_FEATURE_DIT, "dit" }, + { CPU_FEATURE_DEBUGV8P4, "debugv8p4" }, + { CPU_FEATURE_DOTPROD, "dotprod" }, + { CPU_FEATURE_DOUBLEFAULT, "doublefault" }, + { CPU_FEATURE_FHM, "fhm" }, + { CPU_FEATURE_FLAGM, "flagm" }, + { CPU_FEATURE_IDST, "idst" }, + { CPU_FEATURE_LRCPC2, "lrcpc2" }, + { CPU_FEATURE_LSE2, "lse2" }, + { CPU_FEATURE_MPAM, "mpam" }, + { CPU_FEATURE_PMUV3P4, "pmuv3p4" }, + { CPU_FEATURE_RASV1P1, "rasv1p1" }, + { CPU_FEATURE_S2FWB, "s2fwb" }, + { CPU_FEATURE_SEL2, "sel2" }, + { CPU_FEATURE_TLBIOS, "tlbios" }, + { CPU_FEATURE_TLBIRANGE, "tlbirange" }, + { CPU_FEATURE_TRF, "trf" }, + { CPU_FEATURE_TTL, "ttl" }, + { CPU_FEATURE_TTST, "ttst" }, + { CPU_FEATURE_BTI, "bti" }, + { CPU_FEATURE_CSV2, "csv2" }, + { CPU_FEATURE_CSV3, "csv3" }, + { CPU_FEATURE_DPB2, "dpb2" }, + { CPU_FEATURE_E0PD, "e0pd" }, + { CPU_FEATURE_EVT, "evt" }, + { CPU_FEATURE_EXS, "exs" }, + { CPU_FEATURE_FRINTTS, "frintts" }, + { CPU_FEATURE_FLAGM2, "flagm2" }, + { CPU_FEATURE_MTE, "mte" }, + { CPU_FEATURE_MTE2, "mte2" }, + { CPU_FEATURE_PMUV3P5, "pmuv3p5" }, + { CPU_FEATURE_RNG, "rng" }, + { CPU_FEATURE_RNG_TRAP, "rng_trap" }, + { CPU_FEATURE_SB, "sb" }, + { CPU_FEATURE_SPECRES, "specres" }, + { CPU_FEATURE_SSBS, "ssbs" }, + { CPU_FEATURE_SSBS2, "ssbs2" }, + { CPU_FEATURE_AMUV1P1, "amuv1p1" }, + { CPU_FEATURE_BF16, "bf16" }, + { CPU_FEATURE_DGH, "dgh" }, + { CPU_FEATURE_ECV, "ecv" }, + { CPU_FEATURE_FGT, "fgt" }, + { CPU_FEATURE_MPAMV0P1, "mpamv0p1" }, + { CPU_FEATURE_MPAMV1P1, "mpamv1p1" }, + { CPU_FEATURE_MTPMU, "mtpmu" }, + { CPU_FEATURE_PAUTH2, "pauth2" }, + { CPU_FEATURE_TWED, "twed" }, + { CPU_FEATURE_AFP, "afp" }, + { CPU_FEATURE_EBF16, "ebf16" }, + { CPU_FEATURE_HCX, "hcx" }, + { CPU_FEATURE_LPA2, "lpa2" }, + { CPU_FEATURE_LS64, "ls64" }, + { CPU_FEATURE_LS64_ACCDATA, "ls64_accdata" }, + { CPU_FEATURE_LS64_V, "ls64_v" }, + { CPU_FEATURE_MTE3, "mte3" }, + { CPU_FEATURE_MTE_ASYM_FAULT, "mte_asym_fault" }, + { CPU_FEATURE_PAN3, "pan3" }, + { CPU_FEATURE_PMUV3P7, "pmuv3p7" }, + { CPU_FEATURE_RPRES, "rpres" }, + { CPU_FEATURE_SPEV1P2, "spev1p2" }, + { CPU_FEATURE_WFXT, "wfxt" }, + { CPU_FEATURE_XS, "xs" }, + { CPU_FEATURE_CMOW, "cmow" }, + { CPU_FEATURE_DEBUGV8P8, "debugv8p8" }, + { CPU_FEATURE_HBC, "hbc" }, + { CPU_FEATURE_MOPS, "mops" }, + { CPU_FEATURE_NMI, "nmi" }, + { CPU_FEATURE_PMUV3P8, "pmuv3p8" }, + { CPU_FEATURE_SCTLR2, "sctlr2" }, + { CPU_FEATURE_SPEV1P3, "spev1p3" }, + { CPU_FEATURE_TCR2, "tcr2" }, + { CPU_FEATURE_TIDCP1, "tidcp1" }, + { CPU_FEATURE_ADERR, "aderr" }, + { CPU_FEATURE_AIE, "aie" }, + { CPU_FEATURE_ANERR, "anerr" }, + { CPU_FEATURE_ATS1A, "ats1a" }, + { CPU_FEATURE_CLRBHB, "clrbhb" }, + { CPU_FEATURE_CSSC, "cssc" }, + { CPU_FEATURE_DEBUGV8P9, "debugv8p9" }, + { CPU_FEATURE_DOUBLEFAULT2, "doublefault2" }, + { CPU_FEATURE_ECBHB, "ecbhb" }, + { CPU_FEATURE_FGT2, "fgt2" }, + { CPU_FEATURE_HAFT, "haft" }, + { CPU_FEATURE_LRCPC3, "lrcpc3" }, + { CPU_FEATURE_MTE4, "mte4" }, + { CPU_FEATURE_MTE_ASYNC, "mte_async" }, + { CPU_FEATURE_MTE_CANONICAL_TAGS, "mte_canonical_tags" }, + { CPU_FEATURE_MTE_NO_ADDRESS_TAGS, "mte_no_address_tags" }, + { CPU_FEATURE_MTE_PERM, "mte_perm" }, + { CPU_FEATURE_MTE_STORE_ONLY, "mte_store_only" }, + { CPU_FEATURE_MTE_TAGGED_FAR, "mte_tagged_far" }, + { CPU_FEATURE_PFAR, "pfar" }, + { CPU_FEATURE_PMUV3_ICNTR, "pmuv3_icntr" }, + { CPU_FEATURE_PMUV3_SS, "pmuv3_ss" }, + { CPU_FEATURE_PMUV3P9, "pmuv3p9" }, + { CPU_FEATURE_PRFMSLC, "prfmslc" }, + { CPU_FEATURE_RASV2, "rasv2" }, + { CPU_FEATURE_RPRFM, "rprfm" }, + { CPU_FEATURE_S1PIE, "s1pie" }, + { CPU_FEATURE_S1POE, "s1poe" }, + { CPU_FEATURE_S2PIE, "s2pie" }, + { CPU_FEATURE_S2POE, "s2poe" }, + { CPU_FEATURE_SPECRES2, "specres2" }, + { CPU_FEATURE_SPE_DPFZS, "spe_dpfzs" }, + { CPU_FEATURE_SPEV1P4, "spev1p4" }, + { CPU_FEATURE_SPMU, "spmu" }, + { CPU_FEATURE_THE, "the" }, + { CPU_FEATURE_SVE2, "sve2" }, + { CPU_FEATURE_SVE_AES, "sve_aes" }, + { CPU_FEATURE_SVE_BITPERM, "sve_bitperm" }, + { CPU_FEATURE_SVE_PMULL128, "sve_pmull128" }, + { CPU_FEATURE_SVE_SHA3, "sve_sha3" }, + { CPU_FEATURE_SVE_SM4, "sve_sm4" }, + { CPU_FEATURE_TME, "tme" }, + { CPU_FEATURE_TRBE, "trbe" }, + { CPU_FEATURE_BRBE, "brbe" }, + { CPU_FEATURE_RME, "rme" }, + { CPU_FEATURE_SME, "sme" }, + { CPU_FEATURE_SME_F64F64, "sme_f64f64" }, + { CPU_FEATURE_SME_FA64, "sme_fa64" }, + { CPU_FEATURE_SME_I16I64, "sme_i16i64" }, + { CPU_FEATURE_BRBEV1P1, "brbev1p1" }, + { CPU_FEATURE_MEC, "mec" }, + { CPU_FEATURE_SME2, "sme2" }, + { CPU_FEATURE_ABLE, "able" }, + { CPU_FEATURE_BWE, "bwe" }, + { CPU_FEATURE_D128, "d128" }, + { CPU_FEATURE_EBEP, "ebep" }, + { CPU_FEATURE_GCS, "gcs" }, + { CPU_FEATURE_ITE, "ite" }, + { CPU_FEATURE_LSE128, "lse128" }, + { CPU_FEATURE_LVA3, "lva3" }, + { CPU_FEATURE_SEBEP, "sebep" }, + { CPU_FEATURE_SME2P1, "sme2p1" }, + { CPU_FEATURE_SME_F16F16, "sme_f16f16" }, + { CPU_FEATURE_SVE2P1, "sve2p1" }, + { CPU_FEATURE_SVE_B16B16, "sve_b16b16" }, + { CPU_FEATURE_SYSINSTR128, "sysinstr128" }, + { CPU_FEATURE_SYSREG128, "sysreg128" }, + { CPU_FEATURE_TRBE_EXT, "trbe_ext" }, }; unsigned i, n = COUNT_OF(matchtable); if (n != NUM_CPU_FEATURES) { - // Warning: incomplete library - // feature matchtable size differs from the actual number of features. + warnf("Warning: incomplete library, feature matchtable size differs from the actual number of features.\n"); } for (i = 0; i < n; i++) if (matchtable[i].feature == feature) diff --git a/libcpuid/libcpuid.h b/libcpuid/libcpuid.h index db768d56..d11acf5f 100644 --- a/libcpuid/libcpuid.h +++ b/libcpuid/libcpuid.h @@ -86,14 +86,15 @@ * LibCPUID provides CPU identification and access to the CPUID and RDTSC * instructions on the x86. *

- * To execute CPUID, use \ref cpu_exec_cpuid
- * To execute RDTSC, use \ref cpu_rdtsc
+ * To execute CPUID, use \ref cpu_exec_cpuid.
+ * To execute RDTSC, use \ref cpu_rdtsc.
* To fetch the CPUID info needed for CPU identification, use - * \ref cpuid_get_raw_data
- * To make sense of that data (decode, extract features), use \ref cpu_identify
+ * \ref cpuid_get_raw_data or \ref cpuid_get_all_raw_data.
+ * To make sense of that data (decode, extract features), use + * \ref cpu_identify or \ref cpu_identify_all.
* To detect the CPU speed, use either \ref cpu_clock, \ref cpu_clock_by_os, * \ref cpu_tsc_mark + \ref cpu_tsc_unmark + \ref cpu_clock_by_mark, - * \ref cpu_clock_measure or \ref cpu_clock_by_ic. + * \ref cpu_clock_measure, \ref cpu_clock_by_ic or \ref cpu_clock_by_tsc. * Read carefully for pros/cons of each method.
* * To read MSRs, use \ref cpu_msr_driver_open to get a handle, and then @@ -111,6 +112,7 @@ #include #include +#define LIBCPUID_DISABLE_DEPRECATED /* Include some integer type specifications: */ #include "libcpuid_types.h" @@ -118,6 +120,21 @@ /* Some limits and other constants */ #include "libcpuid_constants.h" +#ifndef LIBCPUID_DEPRECATED +# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ +# define LIBCPUID_DEPRECATED(message) [[deprecated(message)]] +# elif (defined(GNUC) && (GNUC > 4 || (GNUC == 4 && GNUC_MINOR >= 5))) || defined(__clang__) +# define LIBCPUID_DEPRECATED(message) __attribute__((deprecated(message))) +# elif defined(__GNUC__) && (__GNUC__ >= 3) +# define LIBCPUID_DEPRECATED(message) __attribute__((deprecated)) +# elif defined(_MSC_VER) +# define LIBCPUID_DEPRECATED(message) __declspec(deprecated(message)) +# else +# pragma message("WARNING: You need to implement LIBCPUID_DEPRECATED for this compiler") +# define LIBCPUID_DEPRECATED(message) +# endif +#endif /* LIBCPUID_DEPRECATED */ + #ifdef __cplusplus extern "C" { #endif @@ -140,6 +157,24 @@ typedef enum { VENDOR_VORTEX86, /*!< DM&P Vortex86 CPU */ VENDOR_VIA, /*!< x86 CPU by VIA */ VENDOR_ZHAOXIN, /*!< x86 CPU by Zhaoxin */ + VENDOR_ARM, /*!< ARM CPU */ + VENDOR_BROADCOM, /*!< Broadcom Corporation CPU */ + VENDOR_CAVIUM, /*!< Cavium Inc. CPU */ + VENDOR_DEC, /*!< Digital Equipment Corporation CPU */ + VENDOR_FUJITSU, /*!< Fujitsu Ltd. CPU */ + VENDOR_HISILICON, /*!< HiSilicon Technology Co., Ltd. CPU */ + VENDOR_INFINEON, /*!< Infineon Technologies AG CPU */ + VENDOR_FREESCALE, /*!< Motorola or Freescale Semiconductor Inc. CPU */ + VENDOR_NVIDIA, /*!< NVIDIA Corporation CPU */ + VENDOR_APM, /*!< Applied Micro Circuits Corporation CPU */ + VENDOR_QUALCOMM, /*!< Qualcomm Inc. CPU */ + VENDOR_SAMSUNG, /*!< Samsung Group CPU */ + VENDOR_MARVELL, /*!< Marvell International Ltd. CPU */ + VENDOR_APPLE, /*!< Apple Inc. CPU */ + VENDOR_FARADAY, /*!< Faraday Technology CPU */ + VENDOR_MICROSOFT, /*!< Microsoft Corporation CPU */ + VENDOR_PHYTIUM, /*!< Phytium Technology Co., Ltd CPU */ + VENDOR_AMPERE, /*!< Ampere Computing CPU */ NUM_CPU_VENDORS, /*!< Valid CPU vendor ids: 0..NUM_CPU_VENDORS - 1 */ VENDOR_UNKNOWN = -1, @@ -158,6 +193,60 @@ typedef enum { } cpu_architecture_t; #define NUM_CPU_ARCHITECTURES NUM_CPU_ARCHITECTURES +/** + * @brief CPU architecture + */ +typedef enum { + /* x86: https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels */ + CPU_FEATURE_LEVEL_I386, /*!< i386 */ + CPU_FEATURE_LEVEL_I486, /*!< i486 */ + CPU_FEATURE_LEVEL_I586, /*!< i586 */ + CPU_FEATURE_LEVEL_I686, /*!< i686 */ + CPU_FEATURE_LEVEL_X86_64_V1, /*!< x86-64-v1 */ + CPU_FEATURE_LEVEL_X86_64_V2, /*!< x86-64-v2 */ + CPU_FEATURE_LEVEL_X86_64_V3, /*!< x86-64-v3 */ + CPU_FEATURE_LEVEL_X86_64_V4, /*!< x86-64-v4 */ + + /* ARM: https://en.wikipedia.org/wiki/ARM_architecture_family#Cores */ + FEATURE_LEVEL_ARM_V1 = 100, /*!< ARMv1 */ + FEATURE_LEVEL_ARM_V2, /*!< ARMv2 */ + FEATURE_LEVEL_ARM_V3, /*!< ARMv3 */ + FEATURE_LEVEL_ARM_V4, /*!< ARMv4 */ + FEATURE_LEVEL_ARM_V4T, /*!< ARMv4T */ + FEATURE_LEVEL_ARM_V5, /*!< ARMv5 (obsolete) */ + FEATURE_LEVEL_ARM_V5T, /*!< ARMv5T */ + FEATURE_LEVEL_ARM_V5TE, /*!< ARMv5TE */ + FEATURE_LEVEL_ARM_V5TEJ, /*!< ARMv5TEJ */ + FEATURE_LEVEL_ARM_V6, /*!< ARMv6 */ + FEATURE_LEVEL_ARM_V6_M, /*!< ARMv6-M */ + FEATURE_LEVEL_ARM_V7_A, /*!< ARMv7-A */ + FEATURE_LEVEL_ARM_V7_M, /*!< ARMv7-M */ + FEATURE_LEVEL_ARM_V7_R, /*!< ARMv7-R */ + FEATURE_LEVEL_ARM_V7E_M, /*!< ARMv7E-M */ + FEATURE_LEVEL_ARM_V8_0_A, /*!< ARMv8.0-A */ + FEATURE_LEVEL_ARM_V8_0_M, /*!< ARMv8.0-M */ + FEATURE_LEVEL_ARM_V8_0_R, /*!< ARMv8.0-R */ + FEATURE_LEVEL_ARM_V8_1_A, /*!< ARMv8.1-A */ + FEATURE_LEVEL_ARM_V8_1_M, /*!< ARMv8.1-M */ + FEATURE_LEVEL_ARM_V8_2_A, /*!< ARMv8.2-A */ + FEATURE_LEVEL_ARM_V8_3_A, /*!< ARMv8.3-A */ + FEATURE_LEVEL_ARM_V8_4_A, /*!< ARMv8.4-A */ + FEATURE_LEVEL_ARM_V8_5_A, /*!< ARMv8.5-A */ + FEATURE_LEVEL_ARM_V8_6_A, /*!< ARMv8.6-A */ + FEATURE_LEVEL_ARM_V8_7_A, /*!< ARMv8.7-A */ + FEATURE_LEVEL_ARM_V8_8_A, /*!< ARMv8.8-A */ + FEATURE_LEVEL_ARM_V8_9_A, /*!< ARMv8.9-A */ + FEATURE_LEVEL_ARM_V9_0_A, /*!< ARMv9.0-A */ + FEATURE_LEVEL_ARM_V9_1_A, /*!< ARMv9.1-A */ + FEATURE_LEVEL_ARM_V9_2_A, /*!< ARMv9.2-A */ + FEATURE_LEVEL_ARM_V9_3_A, /*!< ARMv9.3-A */ + FEATURE_LEVEL_ARM_V9_4_A, /*!< ARMv9.4-A */ + + NUM_CPU_FEATURE_LEVELS, /*!< Valid feature level ids: 0..NUM_CPU_FEATURE_LEVELS - 1 */ + FEATURE_LEVEL_UNKNOWN = -1, +} cpu_feature_level_t; +#define NUM_CPU_FEATURE_LEVELS NUM_CPU_FEATURE_LEVELS + /** * @brief CPU purpose */ @@ -166,6 +255,7 @@ typedef enum { PURPOSE_PERFORMANCE, /*!< performance CPU */ PURPOSE_EFFICIENCY, /*!< efficiency CPU */ PURPOSE_LP_EFFICIENCY, /*!< low-power efficiency CPU */ + PURPOSE_U_PERFORMANCE, /*!< ultimate performance CPU */ NUM_CPU_PURPOSES, /*!< Valid CPU purpose ids: 0..NUM_CPU_PURPOSES - 1 */ } cpu_purpose_t; @@ -231,6 +321,48 @@ struct cpu_raw_data_t { * this stores the result of CPUID with eax = 8000001Dh and * ecx = 0, 1, 2... */ uint32_t amd_fn8000001dh[MAX_AMDFN8000001DH_LEVEL][NUM_REGS]; + + /** when the CPU is AMD and supports leaf 80000026h + * (Extended CPU Topology leaf) + * this stores the result of CPUID with eax = 80000026h and + * ecx = 0, 1, 2... */ + uint32_t amd_fn80000026h[MAX_AMDFN80000026H_LEVEL][NUM_REGS]; + + /** when then CPU is ARM-based and supports MIDR + * (Main ID Register) */ + uint64_t arm_midr; + + /** when then CPU is ARM-based and supports MPIDR + * (Multiprocessor Affinity Register) */ + uint64_t arm_mpidr; + + /** when then CPU is ARM-based and supports REVIDR + * (Revision ID Register) */ + uint64_t arm_revidr; + + /** when then CPU is ARM-based and supports ID_AA64DFR* + * (AArch64 Debug Feature Register) */ + uint64_t arm_id_aa64dfr[MAX_ARM_ID_AA64DFR_REGS]; + + /** when then CPU is ARM-based and supports D_AA64ISAR* + * (AArch64 Instruction Set Attribute Register) */ + uint64_t arm_id_aa64isar[MAX_ARM_ID_AA64ISAR_REGS]; + + /** when then CPU is ARM-based and supports ID_AA64MMFR* + * (AArch64 Memory Model Feature Register) */ + uint64_t arm_id_aa64mmfr[MAX_ARM_ID_AA64MMFR_REGS]; + + /** when then CPU is ARM-based and supports ID_AA64PFR* + * (AArch64 Processor Feature Register) */ + uint64_t arm_id_aa64pfr[MAX_ARM_ID_AA64PFR_REGS]; + + /** when then CPU is ARM-based and supports ID_AA64SMFR* + * (AArch64 SME Feature ID Register ) */ + uint64_t arm_id_aa64smfr[MAX_ARM_ID_AA64SMFR_REGS]; + + /** when then CPU is ARM-based and supports ID_AA64ZFR* + * (SVE Feature ID register) */ + uint64_t arm_id_aa64zfr[MAX_ARM_ID_AA64ZFR_REGS]; }; /** @@ -320,6 +452,56 @@ struct cpu_sgx_t { uint64_t secs_xfrm; }; +/** + * @brief Contains x86 specific info. + * + * @note This is part of \ref cpu_id_t. + */ +struct x86_id_t { + /** CPU family (BaseFamily[3:0]) */ + int32_t family; + + /** CPU model (BaseModel[3:0]) */ + int32_t model; + + /** CPU stepping */ + int32_t stepping; + + /** CPU display ("true") family (computed as BaseFamily[3:0]+ExtendedFamily[7:0]) */ + int32_t ext_family; + + /** + * CPU display ("true") model (computed as (ExtendedModel[3:0]<<4) + BaseModel[3:0]) + * For detailed discussion about what BaseModel / ExtendedModel / Model are, see Github issue #150. + */ + int32_t ext_model; + + /** SSE execution unit size (64 or 128; -1 if N/A) */ + int32_t sse_size; + + /** contains information about SGX features if the processor, if present */ + struct cpu_sgx_t sgx; +}; + +/** + * @brief Contains ARM specific info. + * + * @note This is part of \ref cpu_id_t. + */ +struct arm_id_t { + /** CPU implementer code */ + uint8_t implementer; + + /** CPU variant number */ + uint8_t variant; + + /** CPU primary part number */ + uint16_t part_num; + + /** CPU revision number */ + uint8_t revision; +}; + /** * @brief This contains the recognized CPU features/info */ @@ -329,6 +511,13 @@ struct cpu_id_t { /** contains the CPU architecture ID (e.g. ARCHITECTURE_X86) */ cpu_architecture_t architecture; + /** + * contains the CPU feature level, + * also know as microarchitecture levels (x86) + * and architecture version (ARM) + */ + cpu_feature_level_t feature_level; + /** contains the CPU vendor string, e.g. "GenuineIntel" */ char vendor_str[VENDOR_STR_MAX]; @@ -345,23 +534,52 @@ struct cpu_id_t { */ uint8_t flags[CPU_FLAGS_MAX]; - /** CPU family (BaseFamily[3:0]) */ +#ifndef LIBCPUID_DISABLE_DEPRECATED + /** + * CPU family (BaseFamily[3:0]) + * @deprecated replaced by \ref x86_id_t::family (prefix member with `x86.`, e.g. `id.x86.family`) + */ + LIBCPUID_DEPRECATED("replace with '.x86.family' in your code to fix the warning") int32_t family; - /** CPU model (BaseModel[3:0]) */ + /** + * CPU model (BaseModel[3:0]) + * @deprecated replaced by \ref x86_id_t::model (prefix member with `x86.`, e.g. `id.x86.model`) + */ + LIBCPUID_DEPRECATED("replace with '.x86.model' in your code to fix the warning") int32_t model; - /** CPU stepping */ + /** + * CPU stepping + * @deprecated replaced by \ref x86_id_t::stepping (prefix member with `x86.`, e.g. `id.x86.stepping`) + */ + LIBCPUID_DEPRECATED("replace with '.x86.stepping' in your code to fix the warning") int32_t stepping; - /** CPU display ("true") family (computed as BaseFamily[3:0]+ExtendedFamily[7:0]) */ + /** + * CPU display ("true") family (computed as BaseFamily[3:0]+ExtendedFamily[7:0]) + * @deprecated replaced by \ref x86_id_t::ext_family (prefix member with `x86.`, e.g. `id.x86.ext_family`) + */ + LIBCPUID_DEPRECATED("replace with '.x86.ext_family' in your code to fix the warning") int32_t ext_family; /** * CPU display ("true") model (computed as (ExtendedModel[3:0]<<4) + BaseModel[3:0]) * For detailed discussion about what BaseModel / ExtendedModel / Model are, see Github issue #150. + * @deprecated replaced by \ref x86_id_t::ext_model (prefix member with `x86.`, e.g. `id.x86.ext_model`) */ + LIBCPUID_DEPRECATED("replace with '.x86.ext_model' in your code to fix the warning") int32_t ext_model; +#endif /* LIBCPUID_DISABLE_DEPRECATED */ + + /** + * contains architecture specific info. + * Use \ref cpu_id_t::architecture to know which member is valid. + */ + union { + struct x86_id_t x86; + struct arm_id_t arm; + }; /** Number of CPU cores on the current processor */ int32_t num_cores; @@ -415,10 +633,13 @@ struct cpu_id_t { /** L4 cache size in KB. Zero on most systems */ int32_t l4_cache; +#ifndef LIBCPUID_DISABLE_DEPRECATED /** Cache associativity for the L1 data cache. -1 if undetermined * @deprecated replaced by \ref cpu_id_t::l1_data_assoc */ + LIBCPUID_DEPRECATED("replace with 'l1_data_assoc' in your code to fix the warning") int32_t l1_assoc; +#endif /* LIBCPUID_DISABLE_DEPRECATED */ /** Cache associativity for the L1 data cache. -1 if undetermined */ int32_t l1_data_assoc; @@ -435,10 +656,13 @@ struct cpu_id_t { /** Cache associativity for the L4 cache. -1 if undetermined */ int32_t l4_assoc; +#ifndef LIBCPUID_DISABLE_DEPRECATED /** Cache-line size for L1 data cache. -1 if undetermined * @deprecated replaced by \ref cpu_id_t::l1_data_cacheline */ + LIBCPUID_DEPRECATED("replace with 'l1_data_cacheline' in your code to fix the warning") int32_t l1_cacheline; +#endif /* LIBCPUID_DISABLE_DEPRECATED */ /** Cache-line size for L1 data cache. -1 if undetermined */ int32_t l1_data_cacheline; @@ -486,7 +710,11 @@ struct cpu_id_t { */ char cpu_codename[CODENAME_STR_MAX]; - /** SSE execution unit size (64 or 128; -1 if N/A) */ + /** + * SSE execution unit size (64 or 128; -1 if N/A) + * @deprecated replaced by \ref x86_id_t::sse_size (prefix member with `x86.`, e.g. `id.x86.sse_size`) + */ + LIBCPUID_DEPRECATED("replace with '.x86.sse_size' in your code to fix the warning") int32_t sse_size; /** @@ -496,7 +724,11 @@ struct cpu_id_t { */ uint8_t detection_hints[CPU_HINTS_MAX]; - /** contains information about SGX features if the processor, if present */ + /** + * contains information about SGX features if the processor, if present + * @deprecated replaced by \ref x86_id_t::sgx (prefix member with `x86.`, e.g. `id.x86.sgx`) + */ + LIBCPUID_DEPRECATED("replace with '.x86.sgx' in your code to fix the warning") struct cpu_sgx_t sgx; /** bitmask of the affinity ids this processor type is occupying */ @@ -675,6 +907,210 @@ typedef enum { CPU_FEATURE_HYPERVISOR, /*!< Hypervisor present (always zero on physical CPUs) */ CPU_FEATURE_INTEL_DTS, /*!< Intel Digital Thermal Sensor */ CPU_FEATURE_INTEL_PTM, /*!< Intel Package Thermal Management */ + CPU_FEATURE_ASID16, /*!< ARM: 16 bit ASID */ + CPU_FEATURE_ADVSIMD, /*!< ARM: Advanced SIMD Extension */ + CPU_FEATURE_CRC32, /*!< ARM: CRC32 instructions */ + CPU_FEATURE_CSV2_1P1, /*!< ARM: Cache Speculation Variant 2 */ + CPU_FEATURE_CSV2_1P2, /*!< ARM: Cache Speculation Variant 2 version 1.2 */ + CPU_FEATURE_CSV2_2, /*!< ARM: Cache Speculation Variant 2 version 2 */ + CPU_FEATURE_CSV2_3, /*!< ARM: Cache Speculation Variant 2 version 3 */ + CPU_FEATURE_DOUBLELOCK, /*!< ARM: Double Lock */ + CPU_FEATURE_ETS2, /*!< ARM: Enhanced Translation Synchronization */ + CPU_FEATURE_FP, /*!< ARM: Floating Point extensions */ + CPU_FEATURE_MIXEDEND, /*!< ARM: Mixed-endian support */ + CPU_FEATURE_MIXEDENDEL0, /*!< ARM: Mixed-endian support at EL0 */ + CPU_FEATURE_PMULL, /*!< ARM: Advanced SIMD PMULL instructions */ + CPU_FEATURE_PMUV3, /*!< ARM: PMU extension version 3 */ + CPU_FEATURE_SHA1, /*!< ARM: Advanced SIMD SHA1 instructions */ + CPU_FEATURE_SHA256, /*!< ARM: Advanced SIMD SHA256 instructions */ + CPU_FEATURE_HAFDBS, /*!< ARM: Hardware management of the Access flag and dirty state */ + CPU_FEATURE_HPDS, /*!< ARM: Hierarchical permission disables in translations tables */ + CPU_FEATURE_LOR, /*!< ARM: Limited ordering regions */ + CPU_FEATURE_LSE, /*!< ARM: Large System Extensions */ + CPU_FEATURE_PAN, /*!< ARM: Privileged access never */ + CPU_FEATURE_PMUV3P1, /*!< ARM: Armv8.1 PMU extensions */ + CPU_FEATURE_RDM, /*!< ARM: Advanced SIMD rounding double multiply accumulate instructions */ + CPU_FEATURE_VHE, /*!< ARM: Virtualization Host Extensions */ + CPU_FEATURE_VMID16, /*!< ARM: 16-bit VMID */ + //CPU_FEATURE_AA32HPD, /*!< ARM: AArch32 Hierarchical permission disables */ + //CPU_FEATURE_AA32I8MM, /*!< ARM: AArch32 Int8 matrix multiplication instructions */ + CPU_FEATURE_DPB, /*!< ARM: DC CVAP instruction */ + CPU_FEATURE_DEBUGV8P2, /*!< ARM: Debug v8.2 */ + CPU_FEATURE_F32MM, /*!< ARM: Single-precision Matrix Multiplication */ + CPU_FEATURE_F64MM, /*!< ARM: Double-precision Matrix Multiplication */ + CPU_FEATURE_FP16, /*!< ARM: Half-precision floating-point data processing */ + CPU_FEATURE_HPDS2, /*!< ARM: Hierarchical permission disables */ + CPU_FEATURE_I8MM, /*!< ARM: AArch64 Int8 matrix multiplication instructions */ + CPU_FEATURE_IESB, /*!< ARM: Implicit Error Synchronization event */ + CPU_FEATURE_LPA, /*!< ARM: Large PA and IPA support */ + CPU_FEATURE_LSMAOC, /*!< ARM: AArch32 Load/Store Multiple instruction atomicity and ordering controls */ + CPU_FEATURE_LVA, /*!< ARM: Large VA support */ + CPU_FEATURE_PAN2, /*!< ARM: AT S1E1R and AT S1E1W instruction variants affected by PSTATE.PAN */ + CPU_FEATURE_RAS, /*!< ARM: Reliability, Availability and Serviceability (RAS) Extension */ + CPU_FEATURE_SHA3, /*!< ARM: Advanced SIMD SHA3 instructions (ARMv8.2 architecture extension) */ + CPU_FEATURE_SHA512, /*!< ARM: Advanced SIMD SHA512 instructions (ARMv8.1 architecture extension) */ + CPU_FEATURE_SM3, /*!< ARM: Advanced SIMD SM3 instructions */ + CPU_FEATURE_SM4, /*!< ARM: Advanced SIMD SM4 instructions */ + CPU_FEATURE_SPE, /*!< ARM: Statistical Profiling Extension */ + CPU_FEATURE_SVE, /*!< ARM: Scalable Vector Extension */ + CPU_FEATURE_TTCNP, /*!< ARM: Translation table Common not private translations */ + CPU_FEATURE_UAO, /*!< ARM: Unprivileged Access Override control */ + CPU_FEATURE_XNX, /*!< ARM: Translation table stage 2 Unprivileged Execute-never */ + CPU_FEATURE_CCIDX, /*!< ARM: Extended cache index */ + CPU_FEATURE_CONSTPACFIELD, /*!< ARM: PAC algorithm enhancement */ + CPU_FEATURE_EPAC, /*!< ARM: Enhanced pointer authentication */ + CPU_FEATURE_FCMA, /*!< ARM: Floating-point complex number instructions */ + CPU_FEATURE_FPAC, /*!< ARM: Faulting on AUT* instructions */ + CPU_FEATURE_FPACCOMBINE, /*!< ARM: Faulting on combined pointer authentication instructions */ + CPU_FEATURE_JSCVT, /*!< ARM: JavaScript conversion instructions */ + CPU_FEATURE_LRCPC, /*!< ARM: Load-Acquire RCpc instructions */ + CPU_FEATURE_PACIMP, /*!< ARM: Pointer authentication - IMPLEMENTATION DEFINED algorithm */ + CPU_FEATURE_PACQARMA3, /*!< ARM: Pointer authentication - QARMA3 algorithm */ + CPU_FEATURE_PACQARMA5, /*!< ARM: Pointer authentication - QARMA5 algorithm */ + CPU_FEATURE_PAUTH, /*!< ARM: Pointer authentication */ + CPU_FEATURE_SPEV1P1, /*!< ARM: Statistical Profiling Extension version 1 */ + CPU_FEATURE_AMUV1, /*!< ARM: Activity Monitors Extension version 1 */ + CPU_FEATURE_BBM, /*!< ARM: Translation table break-before-make levels */ + CPU_FEATURE_DIT, /*!< ARM: Data Independent Timing instructions */ + CPU_FEATURE_DEBUGV8P4, /*!< ARM: Debug v8.4 */ + CPU_FEATURE_DOTPROD, /*!< ARM: Advanced SIMD dot product instructions */ + CPU_FEATURE_DOUBLEFAULT, /*!< ARM: Double Fault Extension */ + CPU_FEATURE_FHM, /*!< ARM: Floating-point half-precision to single-precision multiply-add instructions */ + CPU_FEATURE_FLAGM, /*!< ARM: Condition flag manipulation instructions */ + CPU_FEATURE_IDST, /*!< ARM: ID space trap handling */ + CPU_FEATURE_LRCPC2, /*!< ARM: Load-Acquire RCpc instructions version 2 */ + CPU_FEATURE_LSE2, /*!< ARM: Large System Extensions version 2 */ + CPU_FEATURE_MPAM, /*!< ARM: Memory Partitioning and Monitoring Extension */ + CPU_FEATURE_PMUV3P4, /*!< ARM: Arm8.4 PMU extensions */ + CPU_FEATURE_RASV1P1, /*!< ARM: RAS extension v1.1 */ + CPU_FEATURE_S2FWB, /*!< ARM: Stage 2 forced Write-Back */ + CPU_FEATURE_SEL2, /*!< ARM: Secure EL2 */ + CPU_FEATURE_TLBIOS, /*!< ARM: TLB invalidate instructions in Outer Shareable domain */ + CPU_FEATURE_TLBIRANGE, /*!< ARM: TLB invalidate range instructions */ + CPU_FEATURE_TRF, /*!< ARM: Self-hosted Trace extensions */ + CPU_FEATURE_TTL, /*!< ARM: Translation Table Level */ + CPU_FEATURE_TTST, /*!< ARM: Small translation tables */ + CPU_FEATURE_BTI, /*!< ARM: Branch Target Identification */ + CPU_FEATURE_CSV2, /*!< ARM: Cache Speculation Variant 2 */ + CPU_FEATURE_CSV3, /*!< ARM: Cache Speculation Variant 3 */ + CPU_FEATURE_DPB2, /*!< ARM: DC CVADP instruction */ + CPU_FEATURE_E0PD, /*!< ARM: Preventing EL0 access to halves of address maps */ + CPU_FEATURE_EVT, /*!< ARM: Enhanced Virtualization Traps */ + CPU_FEATURE_EXS, /*!< ARM: Context synchronization and exception handling */ + CPU_FEATURE_FRINTTS, /*!< ARM: Floating-point to integer instructions */ + CPU_FEATURE_FLAGM2, /*!< ARM: Enhancements to flag manipulation instructions */ + CPU_FEATURE_MTE, /*!< ARM: Memory Tagging Extension */ + CPU_FEATURE_MTE2, /*!< ARM: Memory Tagging Extension */ + CPU_FEATURE_PMUV3P5, /*!< ARM: Arm8.5 PMU extensions */ + CPU_FEATURE_RNG, /*!< ARM: Random number generator */ + CPU_FEATURE_RNG_TRAP, /*!< ARM: Trapping support for RNDR/RNDRRS */ + CPU_FEATURE_SB, /*!< ARM: Speculation Barrier */ + CPU_FEATURE_SPECRES, /*!< ARM: Speculation restriction instructions */ + CPU_FEATURE_SSBS, /*!< ARM: Speculative Store Bypass Safe */ + CPU_FEATURE_SSBS2, /*!< ARM: MRS and MSR instructions for SSBS version 2 */ + CPU_FEATURE_AMUV1P1, /*!< ARM: Activity Monitors Extension version 1.1 */ + CPU_FEATURE_BF16, /*!< ARM: AArch64 BFloat16 instructions */ + CPU_FEATURE_DGH, /*!< ARM: Data Gathering Hint */ + CPU_FEATURE_ECV, /*!< ARM: Enhanced Counter Virtualization */ + CPU_FEATURE_FGT, /*!< ARM: Fine Grain Traps */ + CPU_FEATURE_MPAMV0P1, /*!< ARM: Memory Partitioning and Monitoring version 0.1 */ + CPU_FEATURE_MPAMV1P1, /*!< ARM: Memory Partitioning and Monitoring version 1.1 */ + CPU_FEATURE_MTPMU, /*!< ARM: Multi-threaded PMU extensions */ + CPU_FEATURE_PAUTH2, /*!< ARM: Enhancements to pointer authentication */ + CPU_FEATURE_TWED, /*!< ARM: Delayed Trapping of WFE */ + CPU_FEATURE_AFP, /*!< ARM: Alternate floating-point behavior */ + CPU_FEATURE_EBF16, /*!< ARM: AArch64 Extended BFloat16 instructions */ + CPU_FEATURE_HCX, /*!< ARM: Support for the HCRX_EL2 register */ + CPU_FEATURE_LPA2, /*!< ARM: Larger physical address for 4KB and 16KB translation granules */ + CPU_FEATURE_LS64, /*!< ARM: Support for 64-byte loads and stores without status */ + CPU_FEATURE_LS64_ACCDATA, /*!< ARM: Support for 64-byte EL0 stores with status */ + CPU_FEATURE_LS64_V, /*!< ARM: Support for 64-byte stores with status */ + CPU_FEATURE_MTE3, /*!< ARM: MTE Asymmetric Fault Handling */ + CPU_FEATURE_MTE_ASYM_FAULT, /*!< ARM: Memory tagging asymmetric faults */ + CPU_FEATURE_PAN3, /*!< ARM: Support for SCTLR_ELx.EPAN */ + CPU_FEATURE_PMUV3P7, /*!< ARM: Armv8.7 PMU extensions */ + CPU_FEATURE_RPRES, /*!< ARM: Increased precision of FRECPE and FRSQRTE */ + CPU_FEATURE_SPEV1P2, /*!< ARM: Statistical Profiling Extensions version 1.2 */ + CPU_FEATURE_WFXT, /*!< ARM: WFE and WFI instructions with timeout */ + CPU_FEATURE_XS, /*!< ARM: XS attribute */ + CPU_FEATURE_CMOW, /*!< ARM: Control for cache maintenance permission */ + CPU_FEATURE_DEBUGV8P8, /*!< ARM: Debug v8.8 */ + CPU_FEATURE_HBC, /*!< ARM: Hinted conditional branches */ + CPU_FEATURE_MOPS, /*!< ARM: Standardization of memory operations */ + CPU_FEATURE_NMI, /*!< ARM: Non-maskable Interrupts */ + CPU_FEATURE_PMUV3P8, /*!< ARM: Armv8.8 PMU extensions */ + CPU_FEATURE_SCTLR2, /*!< ARM: Extension to SCTLR_ELx */ + CPU_FEATURE_SPEV1P3, /*!< ARM: Statistical Profiling Extensions version 1.3 */ + CPU_FEATURE_TCR2, /*!< ARM: Support for TCR2_ELx */ + CPU_FEATURE_TIDCP1, /*!< ARM: EL0 use of IMPLEMENTATION DEFINED functionality */ + CPU_FEATURE_ADERR, /*!< ARM: Asynchronous Device Error Exceptions */ + CPU_FEATURE_AIE, /*!< ARM: Memory Attribute Index Enhancement */ + CPU_FEATURE_ANERR, /*!< ARM: Asynchronous Normal Error Exceptions */ + CPU_FEATURE_ATS1A, /*!< ARM: Address Translation operations that ignore stage 1 permissions */ + CPU_FEATURE_CLRBHB, /*!< ARM: Support for Clear Branch History instruction */ + CPU_FEATURE_CSSC, /*!< ARM: Common Short Sequence Compression instructions */ + CPU_FEATURE_DEBUGV8P9, /*!< ARM: Debug v8.9 */ + CPU_FEATURE_DOUBLEFAULT2, /*!< ARM: Double Fault Extension v2 */ + CPU_FEATURE_ECBHB, /*!< ARM: Exploitative control using branch history information */ + CPU_FEATURE_FGT2, /*!< ARM: Fine-grained traps 2 */ + CPU_FEATURE_HAFT, /*!< ARM: Hardware managed Access Flag for Table descriptors */ + CPU_FEATURE_LRCPC3, /*!< ARM: Load-Acquire RCpc instructions version 3 */ + CPU_FEATURE_MTE4, /*!< ARM: Enhanced Memory Tagging Extension */ + CPU_FEATURE_MTE_ASYNC, /*!< ARM: Asynchronous reporting of Tag Check Fault */ + CPU_FEATURE_MTE_CANONICAL_TAGS, /*!< ARM: Canonical Tag checking for Untagged memory */ + CPU_FEATURE_MTE_NO_ADDRESS_TAGS, /*!< ARM: Memory tagging with Address tagging disabled */ + CPU_FEATURE_MTE_PERM, /*!< ARM: Allocation tag access permission */ + CPU_FEATURE_MTE_STORE_ONLY, /*!< ARM: Store-only Tag Checking */ + CPU_FEATURE_MTE_TAGGED_FAR, /*!< ARM: FAR_ELx on a Tag Check Fault */ + CPU_FEATURE_PFAR, /*!< ARM: Physical Fault Address Register Extension */ + CPU_FEATURE_PMUV3_ICNTR, /*!< ARM: Fixed-function instruction counter */ + CPU_FEATURE_PMUV3_SS, /*!< ARM: PMU Snapshot extension */ + CPU_FEATURE_PMUV3P9, /*!< ARM: Armv8.9 PMU extensions */ + CPU_FEATURE_PRFMSLC, /*!< ARM: SLC target support for PRFM instructions */ + CPU_FEATURE_RASV2, /*!< ARM: RAS Extension v2 */ + CPU_FEATURE_RPRFM, /*!< ARM: Support for Range Prefetch Memory instruction */ + CPU_FEATURE_S1PIE, /*!< ARM: Stage 1 permission indirections */ + CPU_FEATURE_S1POE, /*!< ARM: Stage 1 permission overlays */ + CPU_FEATURE_S2PIE, /*!< ARM: Stage 2 permission indirections */ + CPU_FEATURE_S2POE, /*!< ARM: Stage 1 permission overlays */ + CPU_FEATURE_SPECRES2, /*!< ARM: Enhanced speculation restriction instructions */ + CPU_FEATURE_SPE_DPFZS, /*!< ARM: Disable Cycle Counter on SPE Freeze */ + CPU_FEATURE_SPEV1P4, /*!< ARM: Statistical Profiling Extension version 1.4 */ + CPU_FEATURE_SPMU, /*!< ARM: System Performance Monitors Extension */ + CPU_FEATURE_THE, /*!< ARM: Translation Hardening Extension */ + CPU_FEATURE_SVE2, /*!< ARM: Scalable Vector Extension version 2 */ + CPU_FEATURE_SVE_AES, /*!< ARM: Scalable Vector AES instructions */ + CPU_FEATURE_SVE_BITPERM, /*!< ARM: Scalable Vector Bit Permutes instructions */ + CPU_FEATURE_SVE_PMULL128, /*!< ARM: Scalable Vector PMULL instructions */ + CPU_FEATURE_SVE_SHA3, /*!< ARM: Scalable Vector SHA3 instructions */ + CPU_FEATURE_SVE_SM4, /*!< ARM: Scalable Vector SM4 instructions */ + CPU_FEATURE_TME, /*!< ARM: Transactional Memory Extension */ + CPU_FEATURE_TRBE, /*!< ARM: Trace Buffer Extension */ + CPU_FEATURE_BRBE, /*!< ARM: Branch Record Buffer Extension */ + CPU_FEATURE_RME, /*!< ARM: Realm Management Extension */ + CPU_FEATURE_SME, /*!< ARM: Scalable Matrix Extension */ + CPU_FEATURE_SME_F64F64, /*!< ARM: Double-precision floating-point outer product instructions */ + CPU_FEATURE_SME_FA64, /*!< ARM: Full A64 instruction set support in Streaming SVE mode */ + CPU_FEATURE_SME_I16I64, /*!< ARM: 16-bit to 64-bit integer widening outer product instructions */ + CPU_FEATURE_BRBEV1P1, /*!< ARM: Branch Record Buffer Extension version 1.1 */ + CPU_FEATURE_MEC, /*!< ARM: Memory Encryption Contexts */ + CPU_FEATURE_SME2, /*!< ARM: Scalable Matrix Extensions version 2 */ + CPU_FEATURE_ABLE, /*!< ARM: Address Breakpoint Linking Extension */ + CPU_FEATURE_BWE, /*!< ARM: Breakpoint and watchpoint enhancements */ + CPU_FEATURE_D128, /*!< ARM: 128-bit Translation Tables, 56 bit PA */ + CPU_FEATURE_EBEP, /*!< ARM: Exception-based Event Profiling */ + CPU_FEATURE_GCS, /*!< ARM: Guarded Control Stack Extension */ + CPU_FEATURE_ITE, /*!< ARM: Instrumentation Trace Extension */ + CPU_FEATURE_LSE128, /*!< ARM: 128-bit Atomics */ + CPU_FEATURE_LVA3, /*!< ARM: 56-bit VA */ + CPU_FEATURE_SEBEP, /*!< ARM: Synchronous Exception-based Event Profiling */ + CPU_FEATURE_SME2P1, /*!< ARM: Scalable Matrix Extension version 2.1 */ + CPU_FEATURE_SME_F16F16, /*!< ARM: Non-widening half-precision FP16 to FP16 arithmetic for SME2. */ + CPU_FEATURE_SVE2P1, /*!< ARM: Scalable Vector Extensions version 2.1 */ + CPU_FEATURE_SVE_B16B16, /*!< ARM: Non-widening BFloat16 to BFloat16 arithmetic for SVE2 and SME2. */ + CPU_FEATURE_SYSINSTR128, /*!< ARM: 128-bit System instructions */ + CPU_FEATURE_SYSREG128, /*!< ARM: 128-bit System registers */ + CPU_FEATURE_TRBE_EXT, /*!< ARM: Trace Buffer external mode */ /* termination: */ NUM_CPU_FEATURES, } cpu_feature_t; @@ -873,6 +1309,13 @@ int cpu_request_core_type(cpu_purpose_t purpose, struct cpu_raw_data_array_t* ra */ const char* cpu_architecture_str(cpu_architecture_t architecture); +/** + * @brief Returns the short textual representation of a CPU feature level + * @param level - the feature level, whose textual representation is wanted. + * @returns a constant string like "ARMv8.0-A", "ARMv9.4-A", etc. + */ +const char* cpu_feature_level_str(cpu_feature_level_t level); + /** * @brief Returns the short textual representation of a CPU purpose * @param purpose - the purpose, whose textual representation is wanted. @@ -1106,6 +1549,27 @@ int cpu_clock_measure(int millis, int quad_check); */ int cpu_clock_by_ic(int millis, int runs); +/** + * @brief Measure the CPU clock frequency using TSC frequency from CPUID + * + * @param raw - Optional input - a pointer to the raw CPUID data, which is obtained + * either by cpuid_get_raw_data or cpuid_deserialize_raw_data. + * Can also be NULL, in which case the functions calls + * cpuid_get_raw_data itself. + * + * The function read Time Stamp Counter and Nominal Core Crystal Clock + * Information Leaf from CPUID. It determines the processor base frequency. + * + * NOTE: only x86 Intel CPUs since Skylake (6th generation of Intel Core + * processors) are supported. Other vendors do not support this feature. + * + * @returns the CPU clock frequency in MHz. + * If TSC frequency is not supported, the result is -1. + * If the input parameters are incorrect, or some other internal fault is + * detected, the result is -2. + */ +int cpu_clock_by_tsc(struct cpu_raw_data_t* raw); + /** * @brief Get the CPU clock frequency (all-in-one method) * diff --git a/libcpuid/libcpuid.vcxproj b/libcpuid/libcpuid.vcxproj index 8a9127bb..18dc3e77 100644 --- a/libcpuid/libcpuid.vcxproj +++ b/libcpuid/libcpuid.vcxproj @@ -130,6 +130,7 @@ + @@ -140,6 +141,7 @@ + diff --git a/libcpuid/libcpuid.vcxproj.filters b/libcpuid/libcpuid.vcxproj.filters index f6c1e626..98e33a68 100644 --- a/libcpuid/libcpuid.vcxproj.filters +++ b/libcpuid/libcpuid.vcxproj.filters @@ -57,6 +57,9 @@ 头文件 + + 头文件 + @@ -83,6 +86,9 @@ 源文件 + + 源文件 + diff --git a/libcpuid/libcpuid_constants.h b/libcpuid/libcpuid_constants.h index a88a7d09..3258af04 100644 --- a/libcpuid/libcpuid_constants.h +++ b/libcpuid/libcpuid_constants.h @@ -35,7 +35,7 @@ #define VENDOR_STR_MAX 16 #define BRAND_STR_MAX 64 #define CODENAME_STR_MAX 64 -#define CPU_FLAGS_MAX 128 +#define CPU_FLAGS_MAX 384 #define MAX_CPUID_LEVEL 32 #define MAX_EXT_CPUID_LEVEL 32 #define MAX_INTELFN4_LEVEL 8 @@ -43,6 +43,13 @@ #define MAX_INTELFN12H_LEVEL 4 #define MAX_INTELFN14H_LEVEL 4 #define MAX_AMDFN8000001DH_LEVEL 4 +#define MAX_AMDFN80000026H_LEVEL 4 +#define MAX_ARM_ID_AA64DFR_REGS 2 +#define MAX_ARM_ID_AA64ISAR_REGS 3 +#define MAX_ARM_ID_AA64MMFR_REGS 5 +#define MAX_ARM_ID_AA64PFR_REGS 3 +#define MAX_ARM_ID_AA64SMFR_REGS 1 +#define MAX_ARM_ID_AA64ZFR_REGS 1 #define CPU_HINTS_MAX 16 #define SGX_FLAGS_MAX 14 #define ADDRESS_EXT_CPUID_START 0x80000000 diff --git a/libcpuid/libcpuid_internal.h b/libcpuid/libcpuid_internal.h index 90e3d6b9..9139a442 100644 --- a/libcpuid/libcpuid_internal.h +++ b/libcpuid/libcpuid_internal.h @@ -78,7 +78,7 @@ struct internal_id_info_t { int32_t cache_mask[NUM_CACHE_TYPES]; }; -struct internal_apic_info_t { +struct internal_topology_t { int32_t apic_id; int32_t package_id; int32_t core_id; diff --git a/libcpuid/libcpuid_types.h b/libcpuid/libcpuid_types.h index 3dc9fe50..3a2fe1cc 100644 --- a/libcpuid/libcpuid_types.h +++ b/libcpuid/libcpuid_types.h @@ -63,9 +63,11 @@ typedef unsigned short uint16_t; typedef uint16_t logical_cpu_t; #define __MASK_NCPUBITS 8 #define __MASK_SETSIZE (1ULL << (sizeof(logical_cpu_t) * __MASK_NCPUBITS)) / __MASK_NCPUBITS -struct _cpu_affinity_mask_t { - uint8_t __bits[__MASK_SETSIZE]; -}; -typedef struct _cpu_affinity_mask_t cpu_affinity_mask_t; +/** + * @brief Internal structure, used in affinity_mask_str_r and affinity_mask_str + */ +typedef struct { + uint8_t __bits[__MASK_SETSIZE]; /*!< affinity mask */ +} cpu_affinity_mask_t; #endif /* __LIBCPUID_TYPES_H__ */ diff --git a/libcpuid/libcpuid_util.c b/libcpuid/libcpuid_util.c index aa9e0389..25e2b701 100644 --- a/libcpuid/libcpuid_util.c +++ b/libcpuid/libcpuid_util.c @@ -60,26 +60,28 @@ static int score(const struct match_entry_t* entry, const struct cpu_id_t* data, int brand_code, uint64_t bits, int model_code) { int i, tmp, res = 0; - const struct { const char* field; int entry; int data; int score; } array[] = { - { "family", entry->family, data->family, 2 }, - { "model", entry->model, data->model, 2 }, - { "stepping", entry->stepping, data->stepping, 2 }, - { "ext_family", entry->ext_family, data->ext_family, 2 }, - { "ext_model", entry->ext_model, data->ext_model, 2 }, - { "ncores", entry->ncores, data->num_cores, 2 }, - { "l2cache", entry->l2cache, data->l2_cache, 1 }, - { "l3cache", entry->l3cache, data->l3_cache, 1 }, - { "brand_code", entry->brand_code, brand_code, 2 }, - { "model_code", entry->model_code, model_code, 2 }, + const struct { const char *field; int entry; int data; int score; } array[] = { + { "family", entry->family, data->x86.family, 2 }, + { "model", entry->model, data->x86.model, 2 }, + { "stepping", entry->stepping, data->x86.stepping, 2 }, + { "ext_family", entry->ext_family, data->x86.ext_family, 2 }, + { "ext_model", entry->ext_model, data->x86.ext_model, 2 }, + { "ncores", entry->ncores, data->num_cores, 2 }, + { "l2cache", entry->l2cache, data->l2_cache, 1 }, + { "l3cache", entry->l3cache, data->l3_cache, 1 }, + { "brand_code", entry->brand_code, brand_code, 2 }, + { "model_code", entry->model_code, model_code, 2 }, }; for (i = 0; i < sizeof(array) / sizeof(array[0]); i++) { if (array[i].entry == array[i].data) { res += array[i].score; + debugf(4, "Score: %-12s matches, adding %2i (current score for this entry: %2i)\n", array[i].field, array[i].score, res); } } tmp = popcount64(entry->model_bits & bits) * 2; res += tmp; + debugf(4, "Score: %-12s matches, adding %2i (current score for this entry: %2i)\n", "model_bits", tmp, res); return res; } @@ -91,9 +93,15 @@ int match_cpu_codename(const struct match_entry_t* matchtable, int count, int bestindex = 0; int i, t; + debugf(3, "Matching cpu f:%d, m:%d, s:%d, xf:%d, xm:%d, ncore:%d, l2:%d, bcode:%d, bits:%llu, code:%d\n", + data->x86.family, data->x86.model, data->x86.stepping, data->x86.ext_family, + data->x86.ext_model, data->num_cores, data->l2_cache, brand_code, (unsigned long long) bits, model_code); + for (i = 0; i < count; i++) { t = score(&matchtable[i], data, brand_code, bits, model_code); + debugf(3, "Entry %d, `%s', score %d\n", i, matchtable[i].name, t); if (t > bestscore) { + debugf(2, "Entry `%s' selected - best score so far (%d)\n", matchtable[i].name, t); bestscore = t; bestindex = i; } @@ -303,8 +311,8 @@ void decode_deterministic_cache_info_x86(uint32_t cache_regs[][NUM_REGS], else if (cache_level == 4 && cache_type == 3) type = L4; else { - // Warning: deterministic_cache: unknown level/typenumber combo - // recognize cache type + warnf("deterministic_cache: unknown level/typenumber combo (%d/%d), cannot\n", cache_level, cache_type); + warnf("deterministic_cache: recognize cache type\n"); continue; } num_sharing_cache = EXTRACTS_BITS(cache_regs[i][EAX], 25, 14) + 1; @@ -318,3 +326,105 @@ void decode_deterministic_cache_info_x86(uint32_t cache_regs[][NUM_REGS], assign_cache_data(1, type, size, ways, linesize, data); } } + +void decode_architecture_version_x86(struct cpu_id_t* data) +{ + bool is_compliant, has_all_features; + int i, j; + cpu_feature_level_t feature_level = FEATURE_LEVEL_UNKNOWN; + + const struct { const int family; const cpu_feature_level_t feature_level; } + architecture_matchtable_ia_32[] = { + { 3, CPU_FEATURE_LEVEL_I386 }, + { 4, CPU_FEATURE_LEVEL_I486 }, + { 5, CPU_FEATURE_LEVEL_I586 }, + { 6, CPU_FEATURE_LEVEL_I686 }, + { 15, CPU_FEATURE_LEVEL_I686 }, // Intel Pentium 4, AMD K8 + }; + + const cpu_feature_t architecture_x86_64_v1[] = { + CPU_FEATURE_CMOV, + CPU_FEATURE_CX8, + CPU_FEATURE_FPU, + CPU_FEATURE_FXSR, + CPU_FEATURE_MMX, + CPU_FEATURE_SSE, + CPU_FEATURE_SSE2, + -1 + }; + + const cpu_feature_t architecture_x86_64_v2[] = { + CPU_FEATURE_CX16, + CPU_FEATURE_LAHF_LM, + CPU_FEATURE_POPCNT, + CPU_FEATURE_PNI, + CPU_FEATURE_SSE4_1, + CPU_FEATURE_SSE4_2, + CPU_FEATURE_SSSE3, + -1 + }; + + const cpu_feature_t architecture_x86_64_v3[] = { + CPU_FEATURE_AVX, + CPU_FEATURE_AVX2, + CPU_FEATURE_BMI1, + CPU_FEATURE_BMI2, + CPU_FEATURE_F16C, + CPU_FEATURE_FMA3, + CPU_FEATURE_ABM, + CPU_FEATURE_MOVBE, + CPU_FEATURE_OSXSAVE, + -1 + }; + + const cpu_feature_t architecture_x86_64_v4[] = { + CPU_FEATURE_AVX512F, + CPU_FEATURE_AVX512BW, + CPU_FEATURE_AVX512CD, + CPU_FEATURE_AVX512DQ, + CPU_FEATURE_AVX512VL, + -1 + }; + + const struct { const cpu_feature_t* features_array; const cpu_feature_level_t feature_level; } + architecture_matchtable_x86_64[] = { + { architecture_x86_64_v1, CPU_FEATURE_LEVEL_X86_64_V1 }, + { architecture_x86_64_v2, CPU_FEATURE_LEVEL_X86_64_V2 }, + { architecture_x86_64_v3, CPU_FEATURE_LEVEL_X86_64_V3 }, + { architecture_x86_64_v4, CPU_FEATURE_LEVEL_X86_64_V4 }, + }; + + if (!data->flags[CPU_FEATURE_LM]) { + /* Check Intel Architecture, 32-bit */ + for (i = 0; i < COUNT_OF(architecture_matchtable_ia_32); i++) { + is_compliant = (data->x86.family == architecture_matchtable_ia_32[i].family); + debugf(3, "Check if CPU is %s compliant: %s for family %i\n", cpu_feature_level_str(architecture_matchtable_ia_32[i].feature_level), is_compliant ? "yes" : "no", architecture_matchtable_ia_32[i].family); + if (is_compliant) { + feature_level = architecture_matchtable_ia_32[i].feature_level; + break; + } + } + } + else { + /* Check Intel Architecture, 64-bit */ + for (i = 0; i < COUNT_OF(architecture_matchtable_x86_64); i++) { + debugf(3, "Check if CPU is %s compliant:\n", cpu_feature_level_str(architecture_matchtable_x86_64[i].feature_level)); + has_all_features = true; + for (j = 0; architecture_matchtable_x86_64[i].features_array[j] != -1; j++) { + is_compliant = data->flags[ architecture_matchtable_x86_64[i].features_array[j] ]; + has_all_features = has_all_features && is_compliant; + debugf(3, " - feature %s is %s\n", cpu_feature_str(architecture_matchtable_x86_64[i].features_array[j]), is_compliant ? "present" : "absent"); + } + if (is_compliant) + feature_level = architecture_matchtable_x86_64[i].feature_level; + else + break; + } + } + + data->feature_level = feature_level; + if (feature_level == FEATURE_LEVEL_UNKNOWN) + warnf("Warning: CPU with CPUID signature %02X_%02XH has an unkown architecture version (LM=%i).\n", data->x86.ext_family, data->x86.ext_model, data->flags[CPU_FEATURE_LM]); + else + debugf(2, "x86 architecture version is %s\n", cpu_feature_level_str(feature_level)); +} diff --git a/libcpuid/libcpuid_util.h b/libcpuid/libcpuid_util.h index 2e5d8cb2..1c579e14 100644 --- a/libcpuid/libcpuid_util.h +++ b/libcpuid/libcpuid_util.h @@ -29,6 +29,7 @@ #include "libcpuid_internal.h" #define COUNT_OF(array) (sizeof(array) / sizeof(array[0])) +#define UNUSED(x) (void)(x) struct feature_map_t { unsigned bit; @@ -51,6 +52,10 @@ int match_cpu_codename(const struct match_entry_t* matchtable, int count, struct cpu_id_t* data, int brand_code, uint64_t bits, int model_code); +#define warnf(...) +#define debugf(...) +#define debug_print_lbits(...) + /* * Seek for a pattern in `haystack'. * Pattern may be an fixed string, or contain the special metacharacters @@ -109,4 +114,7 @@ void decode_deterministic_cache_info_x86(uint32_t cache_regs[][NUM_REGS], struct cpu_id_t* data, struct internal_id_info_t* internal); +/* generic way to get microarchitecture levels for x86 CPUs */ +void decode_architecture_version_x86(struct cpu_id_t* data); + #endif /* __LIBCPUID_UTIL_H__ */ diff --git a/libcpuid/rdmsr.c b/libcpuid/rdmsr.c index 9a0348a0..f3564e86 100644 --- a/libcpuid/rdmsr.c +++ b/libcpuid/rdmsr.c @@ -53,7 +53,7 @@ /* Useful links for hackers: - AMD MSRs: - AMD BIOS and Kernel Developer’s Guide (BKDG) + AMD BIOS and Kernel Developer's Guide (BKDG) * AMD Family 10h Processors http://support.amd.com/TechDocs/31116.pdf * AMD Family 11h Processors @@ -91,7 +91,7 @@ Model 70h, Revision A0: https://www.amd.com/system/files/TechDocs/57019-A0-PUB_3.00.zip - Intel MSRs: - Intel 64 and IA-32 Architectures Software Developer’s Manual + Intel 64 and IA-32 Architectures Software Developer's Manual * Volume 3 (3A, 3B, 3C & 3D): System Programming Guide http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-system-programming-manual-325384.pdf */ @@ -179,6 +179,197 @@ static int perfmsr_measure(struct wr0_drv_t* handle, int msr) return (int) ((y - x) / (b - a)); } +static int msr_platform_info_supported(struct msr_info_t *info) +{ + int i; + static int supported = -1; + + /* Return cached result */ + if(supported >= 0) + return supported; + + /* List of microarchitectures that provide both "Maximum Non-Turbo Ratio" and "Maximum Efficiency Ratio" values + Please note Silvermont does not report "Maximum Efficiency Ratio" */ + const struct { int32_t ext_family; int32_t ext_model; } msr_platform_info[] = { + /* Table 2-12. MSRs in Intel Atom Processors Based on Goldmont Microarchitecture */ + { 6, 92 }, + { 6, 122 }, + /* Table 2-15. MSRs in Processors Based on Nehalem Microarchitecture */ + { 6, 26 }, + { 6, 30 }, + { 6, 37 }, + { 6, 44 }, + /* Table 2-20. MSRs Supported by Intel Processors Based on Sandy Bridge Microarchitecture */ + { 6, 42 }, + { 6, 45 }, + /* Table 2-25. Additional MSRs Supported by 3rd Generation Intel CoreTM Processors Based on Ivy Bridge Microarchitecture */ + { 6, 58 }, + /* Table 2-26. MSRs Supported by the Intel Xeon Processor E5 v2 Product Family (Ivy Bridge-E Microarchitecture) */ + { 6, 62 }, + /* Table 2-29. Additional MSRs Supported by Processors Based on the Haswell and Haswell-E Microarchitectures */ + { 6, 60 }, + { 6, 63 }, + { 6, 69 }, + { 6, 70 }, + /* Table 2-36. Additional MSRs Common to the Intel Xeon Processor D and the Intel Xeon Processor E5 v4 Family Based on Broadwell Microarchitecture */ + { 6, 61 }, + { 6, 71 }, + { 6, 79 }, + /* Table 2-39. Additional MSRs Supported by the 6th-13th Generation Intel CoreTM Processors, 1st-5th Generation Intel Xeon Scalable Processor Families, Intel CoreTM Ultra 7 Processors, 8th Generation Intel CoreTM i3 Processors, and Intel Xeon E Processors */ + /* ==> Skylake */ + { 6, 78 }, + { 6, 85 }, + { 6, 94 }, + /* ==> Kaby Lake */ + { 6, 142 }, + { 6, 158 }, + /* ==> Coffee Lake */ + { 6, 102 }, + { 6, 142 }, + { 6, 158 }, + /* ==> Cascade Lake */ + { 6, 85 }, + /* ==> Comet Lake */ + { 6, 142 }, + { 6, 165 }, + /* ==> Ice Lake */ + { 6, 106 }, + { 6, 108 }, + { 6, 126 }, + /* ==> Rocket Lake */ + { 6, 167 }, + /* ==> Tremont */ + { 6, 138 }, + { 6, 150 }, + { 6, 156 }, + /* ==> Tiger Lake */ + { 6, 140 }, + /* ==> Alder Lake */ + { 6, 151 }, + { 6, 154 }, + { 6, 190 }, + /* ==> Raptor Lake */ + { 6, 183 }, + { 6, 186 }, + { 6, 191 }, + /* ==> Sapphire Rapids */ + { 6, 143 }, + /* ==> Emerald Rapids */ + { 6, 207 }, + /* ==> Meteor Lake */ + { 6, 170 }, + /* Table 2-50. MSRs Supported by the Intel Xeon Scalable Processor Family with a CPUID Signature DisplayFamily_DisplayModel Value of 06_55H */ + { 0x6, 0x55 }, + /* Table 2-56. Selected MSRs Supported by Intel Xeon PhiTM Processors with a CPUID Signature DisplayFamily_DisplayModel Value of 06_57H or 06_85H */ + { 0x6, 0x57 }, + { 0x6, 0x85 }, + }; + + if(info->id->vendor == VENDOR_INTEL) { + for(i = 0; i < COUNT_OF(msr_platform_info); i++) { + if((info->id->x86.ext_family == msr_platform_info[i].ext_family) && (info->id->x86.ext_model == msr_platform_info[i].ext_model)) { + debugf(2, "Intel CPU with CPUID signature %02X_%02XH supports MSR_PLATFORM_INFO.\n", info->id->x86.ext_family, info->id->x86.ext_model); + supported = 1; + return supported; + } + } + debugf(2, "Intel CPU with CPUID signature %02X_%02XH does not support MSR_PLATFORM_INFO.\n", info->id->x86.ext_family, info->id->x86.ext_model); + } + + supported = 0; + return supported; +} + +static int msr_perf_status_supported(struct msr_info_t *info) +{ + int i; + static int supported = -1; + + /* Return cached result */ + if(supported >= 0) + return supported; + + /* List of microarchitectures that provide "Core Voltage" values */ + const struct { int32_t ext_family; int32_t ext_model; } msr_perf_status[] = { + /* Table 2-20. MSRs Supported by Intel Processors Based on Sandy Bridge Microarchitecture */ + { 6, 42 }, + { 6, 45 }, + /* ==> Ivy Bridge */ + { 6, 58 }, + { 6, 62 }, + /* ==> Haswell */ + { 6, 60 }, + { 6, 63 }, + { 6, 69 }, + { 6, 70 }, + /* ==> Broadwell */ + { 6, 61 }, + { 6, 71 }, + { 6, 79 }, + /* ==> Skylake */ + { 6, 78 }, + { 6, 85 }, + { 6, 94 }, + /* ==> Kaby Lake */ + { 6, 142 }, + { 6, 158 }, + /* ==> Coffee Lake */ + { 6, 102 }, + { 6, 142 }, + { 6, 158 }, + /* ==> Cascade Lake */ + { 6, 85 }, + /* ==> Comet Lake */ + { 6, 142 }, + { 6, 165 }, + /* ==> Ice Lake */ + { 6, 106 }, + { 6, 108 }, + { 6, 126 }, + /* ==> Rocket Lake */ + { 6, 167 }, + /* ==> Tremont */ + { 6, 138 }, + { 6, 150 }, + { 6, 156 }, + /* ==> Tiger Lake */ + { 6, 140 }, + /* ==> Alder Lake */ + { 6, 151 }, + { 6, 154 }, + { 6, 190 }, + /* ==> Raptor Lake */ + { 6, 183 }, + { 6, 186 }, + { 6, 191 }, + /* ==> Sapphire Rapids */ + { 6, 143 }, + /* ==> Emerald Rapids */ + { 6, 207 }, + /* ==> Meteor Lake */ + { 6, 170 }, + /* Table 2-50. MSRs Supported by the Intel Xeon Scalable Processor Family with a CPUID Signature DisplayFamily_DisplayModel Value of 06_55H */ + { 0x6, 0x55 }, + /* Table 2-56. Selected MSRs Supported by Intel Xeon PhiTM Processors with a CPUID Signature DisplayFamily_DisplayModel Value of 06_57H or 06_85H */ + { 0x6, 0x57 }, + { 0x6, 0x85 }, + }; + + if(info->id->vendor == VENDOR_INTEL) { + for(i = 0; i < COUNT_OF(msr_perf_status); i++) { + if((info->id->x86.ext_family == msr_perf_status[i].ext_family) && (info->id->x86.ext_model == msr_perf_status[i].ext_model)) { + debugf(2, "Intel CPU with CPUID signature %02X_%02XH supports MSR_PERF_STATUS.\n", info->id->x86.ext_family, info->id->x86.ext_model); + supported = 1; + return supported; + } + } + debugf(2, "Intel CPU with CPUID signature %02X_%02XH does not support MSR_PERF_STATUS.\n", info->id->x86.ext_family, info->id->x86.ext_model); + } + + supported = 0; + return supported; +} + static int get_amd_multipliers(struct msr_info_t *info, uint32_t pstate, double *multiplier) { int i, err; @@ -199,7 +390,7 @@ static int get_amd_multipliers(struct msr_info_t *info, uint32_t pstate, double const int num_dids = (int) COUNT_OF(divisor_t); /* Constant values for common families */ - const int magic_constant = (info->id->ext_family == 0x11) ? 0x8 : 0x10; + const int magic_constant = (info->id->x86.ext_family == 0x11) ? 0x8 : 0x10; const int is_apu = ((FUSION_C <= info->internal->code.amd) && (info->internal->code.amd <= FUSION_A)) || (info->internal->bits & _APU_); const double divisor = is_apu ? 1.0 : 2.0; @@ -208,7 +399,7 @@ static int get_amd_multipliers(struct msr_info_t *info, uint32_t pstate, double return 1; /* Overview of AMD CPU microarchitectures: https://en.wikipedia.org/wiki/List_of_AMD_CPU_microarchitectures#Nomenclature */ - switch (info->id->ext_family) { + switch (info->id->x86.ext_family) { case 0x12: /* K10 (Llano) / K12 */ /* BKDG 12h, page 469 MSRC001_00[6B:64][8:4] is CpuFid @@ -288,7 +479,7 @@ static int get_amd_multipliers(struct msr_info_t *info, uint32_t pstate, double *multiplier = ((double) CpuFid / CpuDid) * 2; break; default: - // unsupported CPU extended family + warnf("get_amd_multipliers(): unsupported CPU extended family: %xh\n", info->id->x86.ext_family); err = 1; break; } @@ -320,9 +511,10 @@ static double get_info_min_multiplier(struct msr_info_t *info) { int err; double mult; + uint32_t addr; uint64_t reg; - if(info->id->vendor == VENDOR_INTEL) { + if(msr_platform_info_supported(info)) { /* Refer links above Table 35-12. MSRs in Next Generation Intel Atom Processors Based on the Goldmont Microarchitecture Table 35-13. MSRs in Processors Based on Intel Microarchitecture Code Name Nehalem @@ -340,9 +532,9 @@ static double get_info_min_multiplier(struct msr_info_t *info) else if(info->id->vendor == VENDOR_AMD || info->id->vendor == VENDOR_HYGON) { /* N.B.: Find the last P-state get_amd_last_pstate_addr() returns the last P-state, MSR_PSTATE_0 <= addr <= MSR_PSTATE_7 */ - uint32_t addr = get_amd_last_pstate_addr(info); + addr = get_amd_last_pstate_addr(info); err = get_amd_multipliers(info, addr, &mult); - if (!err && mult > 0.0) return mult; + if (!err) return mult; } return (double) CPU_INVALID_VALUE / 100; @@ -371,7 +563,7 @@ static double get_info_cur_multiplier(struct msr_info_t *info) MSRC001_0063[2:0] is CurPstate */ err = cpu_rdmsr_range(info->handle, MSR_PSTATE_S, 2, 0, ®); err += get_amd_multipliers(info, MSR_PSTATE_0 + (uint32_t) reg, &mult); - if (!err && mult > 0.0) return mult; + if (!err) return mult; } return (double) CPU_INVALID_VALUE / 100; @@ -411,7 +603,7 @@ static double get_info_max_multiplier(struct msr_info_t *info) MSRC001_0064 is Pb0 Pb0 is the highest-performance boosted P-state */ err = get_amd_multipliers(info, MSR_PSTATE_0, &mult); - if (!err && mult > 0.0) return mult; + if (!err) return mult; } return (double) CPU_INVALID_VALUE / 100; @@ -523,10 +715,10 @@ static int amd_k8_temperature(struct msr_info_t* info) uint32_t value; uint32_t addr; int offset = -49; - if (info->id->ext_model >= 0x69 && - info->id->ext_model != 0xc1 && - info->id->ext_model != 0x6c && - info->id->ext_model != 0x7c) + if (info->id->x86.ext_model >= 0x69 && + info->id->x86.ext_model != 0xc1 && + info->id->x86.ext_model != 0x6c && + info->id->x86.ext_model != 0x7c) offset += 21; addr = pci_find_by_id(info->handle, AMD_PCI_VENDOR_ID, AMD_PCI_CONTROL_DEVICE_ID, info->id->index); @@ -562,7 +754,7 @@ static int amd_k10_temperature(struct msr_info_t* info) uint32_t addr; uint16_t did = 0; bool smu = false; - switch (info->id->ext_family) + switch (info->id->x86.ext_family) { case 0x10: did = FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID; @@ -577,7 +769,7 @@ static int amd_k10_temperature(struct msr_info_t* info) did = FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID; break; case 0x15: - switch (info->id->ext_model & 0xF0) + switch (info->id->x86.ext_model & 0xF0) { case 0x00: did = FAMILY_15H_MODEL_00_MISC_CONTROL_DEVICE_ID; @@ -599,7 +791,7 @@ static int amd_k10_temperature(struct msr_info_t* info) } break; case 0x16: - switch (info->id->ext_model & 0xF0) + switch (info->id->x86.ext_model & 0xF0) { case 0x00: did = FAMILY_16H_MODEL_00_MISC_CONTROL_DEVICE_ID; @@ -623,11 +815,11 @@ static int amd_k10_temperature(struct msr_info_t* info) return CPU_INVALID_VALUE; value = pci_conf_read32(info->handle, addr, 0xA4); } - if ((info->id->ext_family == 0x15 || - info->id->ext_family == 0x16) + if ((info->id->x86.ext_family == 0x15 || + info->id->x86.ext_family == 0x16) && (value & 0x30000) == 0x3000) { - if (info->id->ext_family == 0x15 && (info->id->ext_model & 0xF0) == 0x00) + if (info->id->x86.ext_family == 0x15 && (info->id->x86.ext_model & 0xF0) == 0x00) return (int) (((value >> 21) & 0x7FC) / 8.0f) - 49; return (int) (((value >> 21) & 0x7FF) / 8.0f) - 49; } @@ -674,15 +866,15 @@ static int get_info_pkg_temperature(struct msr_info_t* info) } else if (info->id->vendor == VENDOR_AMD || info->id->vendor == VENDOR_HYGON) { - if (info->id->ext_family >= 0x17) + if (info->id->x86.ext_family >= 0x17) { return (int)amd_17h_temperature(info); } - else if (info->id->ext_family > 0x0F) + else if (info->id->x86.ext_family > 0x0F) { return amd_k10_temperature(info); } - else if (info->id->ext_family == 0x0F) + else if (info->id->x86.ext_family == 0x0F) { return amd_k8_temperature(info); } @@ -706,7 +898,7 @@ static double get_info_pkg_energy(struct msr_info_t* info) // 17h: Zen / Zen+ / Zen 2 // 18h: Hygon Dhyana // 19h: Zen 3 / Zen 3+ / Zen 4 - if (info->id->ext_family >= 0x17) + if (info->id->x86.ext_family >= 0x17) { err = cpu_rdmsr_range(info->handle, MSR_PKG_ENERGY_STAT, 31, 0, &TotalEnergyConsumed); err += cpu_rdmsr_range(info->handle, MSR_PWR_UNIT, 12, 8, &EnergyStatusUnits); @@ -782,7 +974,7 @@ static double get_info_voltage(struct msr_info_t *info) double VIDStep; uint64_t reg, CpuVid; - if(info->id->vendor == VENDOR_INTEL) { + if(msr_perf_status_supported(info)) { /* Refer links above Table 35-18. MSRs Supported by Intel Processors based on Intel microarchitecture code name Sandy Bridge (Contd.) MSR_PERF_STATUS[47:32] is Core Voltage @@ -798,9 +990,9 @@ static double get_info_voltage(struct msr_info_t *info) BKDG 10h, page 49: voltage = 1.550V - 0.0125V * SviVid (SVI1) BKDG 15h, page 50: Voltage = 1.5500 - 0.00625 * Vid[7:0] (SVI2) SVI2 since Piledriver (Family 15h, 2nd-gen): Models 10h-1Fh Processors */ - VIDStep = ((info->id->ext_family < 0x15) || ((info->id->ext_family == 0x15) && (info->id->ext_model < 0x10))) ? 0.0125 : 0.00625; + VIDStep = ((info->id->x86.ext_family < 0x15) || ((info->id->x86.ext_family == 0x15) && (info->id->x86.ext_model < 0x10))) ? 0.0125 : 0.00625; err = cpu_rdmsr_range(info->handle, MSR_PSTATE_S, 2, 0, ®); - if(info->id->ext_family < 0x17) + if(info->id->x86.ext_family < 0x17) err += cpu_rdmsr_range(info->handle, MSR_PSTATE_0 + (uint32_t) reg, 15, 9, &CpuVid); else err += cpu_rdmsr_range(info->handle, MSR_PSTATE_0 + (uint32_t) reg, 21, 14, &CpuVid); @@ -817,7 +1009,7 @@ static double get_info_bus_clock(struct msr_info_t *info) uint32_t addr; uint64_t reg; - if(info->id->vendor == VENDOR_INTEL) { + if(msr_platform_info_supported(info)) { /* Refer links above Table 35-12. MSRs in Next Generation Intel Atom Processors Based on the Goldmont Microarchitecture Table 35-13. MSRs in Processors Based on Intel Microarchitecture Code Name Nehalem @@ -844,7 +1036,7 @@ static double get_info_bus_clock(struct msr_info_t *info) } int cpu_rdmsr_range(struct wr0_drv_t* handle, uint32_t msr_index, uint8_t highbit, - uint8_t lowbit, uint64_t* result) + uint8_t lowbit, uint64_t* result) { int err; const uint8_t bits = highbit - lowbit + 1; diff --git a/libcpuid/rdtsc.c b/libcpuid/rdtsc.c index ca32c813..b16e1da5 100644 --- a/libcpuid/rdtsc.c +++ b/libcpuid/rdtsc.c @@ -25,12 +25,12 @@ */ #include #include -#include #include "libcpuid.h" #include "libcpuid_util.h" #include "asm-bits.h" #include "rdtsc.h" +#include void sys_precise_clock(uint64_t *result) { double c, f; @@ -101,7 +101,6 @@ int cpu_clock_by_os(void) return (int)result; } - /* Emulate doing useful CPU intensive work */ static int busy_loop(int amount) { @@ -195,25 +194,25 @@ static void adjust_march_ic_multiplier(const struct cpu_id_t* id, int* numerator * 4. For Skylake and later, it is 1.6 IPC (we multiply by 5/8) */ // - if (id->sse_size < 128) { - // SSE execution path is 64-bit + if (id->x86.sse_size < 128) { + debugf(1, "SSE execution path is 64-bit\n"); // on a CPU with half SSE unit length, SSE instructions execute at 0.5 IPC; // the resulting value must be multiplied by 2: *numerator = 2; } else { - // SSE execution path is 128-bit + debugf(1, "SSE execution path is 128-bit\n"); } // // Bulldozer or later: assume 1.4 IPC - if ((id->vendor == VENDOR_AMD && id->ext_family >= 21) || (id->vendor == VENDOR_HYGON)) { - // Bulldozer (or later) detected, dividing result by 1.4 + if ((id->vendor == VENDOR_AMD && id->x86.ext_family >= 21) || (id->vendor == VENDOR_HYGON)) { + debugf(1, "cpu_clock_by_ic: Bulldozer (or later) detected, dividing result by 1.4\n"); *numerator = 5; *denom = 7; // multiply by 5/7, to divide by 1.4 } // // Skylake or later: assume 1.6 IPC - if (id->vendor == VENDOR_INTEL && id->ext_model >= 94) { - // Skylake (or later) detected, dividing result by 1.6 + if (id->vendor == VENDOR_INTEL && id->x86.ext_model >= 94) { + debugf(1, "cpu_clock_by_ic: Skylake (or later) detected, dividing result by 1.6\n"); *numerator = 5; *denom = 8; // to divide by 1.6, multiply by 5/8 } @@ -243,7 +242,7 @@ int cpu_clock_by_ic(int millis, int runs) busy_sse_loop(cycles_inner); sys_precise_clock(&t1); } while (t1 - t0 < tl); - + debugf(2, "inner: %d, outer: %d\n", cycles_inner, cycles_outer); for (ri = 0; ri < runs; ri++) { sys_precise_clock(&t0); c = 0; @@ -254,6 +253,7 @@ int cpu_clock_by_ic(int millis, int runs) sys_precise_clock(&t1); } while (t1 - t0 < tl * (uint64_t) 8); // cpu_Hz = cycles_inner * cycles_outer * 256 / (t1 - t0) * 1000000 + debugf(2, "c = %d, td = %d\n", c, (int) (t1 - t0)); hz = ((uint64_t) cycles_inner * (uint64_t) 256 + 12) * (uint64_t) cycles_outer * (uint64_t) multiplier_numerator * (uint64_t) c * (uint64_t) 1000000 / ((t1 - t0) * (uint64_t) multiplier_denom); @@ -263,6 +263,99 @@ int cpu_clock_by_ic(int millis, int runs) return max_value; } +int cpu_clock_by_tsc(struct cpu_raw_data_t* raw) +{ + /* Documentation: + * Intel 64 and IA-32 Architectures Software Developer's Manual + * Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D, and 4 + * 20.7.3 Determining the Processor Base Frequency + */ + uint16_t base_freq_mhz; + uint32_t denominator, numerator, nominal_freq_khz; + struct cpu_raw_data_t myraw; + struct cpu_id_t id; + + /* Get CPUID raw data and identy CPU */ + if (!raw) { + if (cpuid_get_raw_data(&myraw) < 0) { + warnf("cpu_clock_by_tsc: raw CPUID cannot be obtained\n"); + return -2; + } + raw = &myraw; + } + if (cpu_identify(raw, &id) != ERR_OK) { + warnf("cpu_clock_by_tsc: CPU cannot be identified\n"); + return -2; + } + + /* Check if Time Stamp Counter and Nominal Core Crystal Clock Information Leaf is supported */ + if ((id.vendor != VENDOR_INTEL) || (raw->basic_cpuid[0][EAX] < 0x15)) { + debugf(1, "cpu_clock_by_tsc: Time Stamp Counter and Nominal Core Crystal Clock Information Leaf is not supported\n"); + return -1; + } + + denominator = raw->basic_cpuid[0x15][EAX]; // Bits 31-00: An unsigned integer which is the denominator of the TSC/"core crystal clock" ratio + numerator = raw->basic_cpuid[0x15][EBX]; // Bits 31-00: An unsigned integer which is the numerator of the TSC/"core crystal clock" ratio + nominal_freq_khz = raw->basic_cpuid[0x15][ECX] / 1000; // Bits 31-00: An unsigned integer which is the nominal frequency of the core crystal clock in Hz + + /* If EBX[31:0] (numerator) is 0, the TSC/"core crystal clock" ratio is not enumerated. */ + if ((numerator == 0) || (denominator == 0)) { + debugf(1, "cpu_clock_by_tsc: TSC/\"core crystal clock\" ratio is not enumerated\n"); + return -1; + } + + /* If ECX is 0, the nominal core crystal clock frequency is not enumerated. + For Intel processors in which CPUID.15H.EBX[31:0] / CPUID.0x15.EAX[31:0] is enumerated but CPUID.15H.ECX + is not enumerated, Table 20-91 can be used to look up the nominal core crystal clock frequency. */ + if ((nominal_freq_khz == 0) && (id.x86.ext_family == 0x6)) { + debugf(1, "cpu_clock_by_tsc: nominal core crystal clock frequency is not enumerated, looking for CPUID signature %02X_%02XH\n", id.x86.ext_family, id.x86.ext_model); + switch (id.x86.ext_model) { + case 0x55: + /* Intel Xeon Scalable Processor Family with CPUID signature 06_55H */ + nominal_freq_khz = 25000; // 25 MHz + break; + case 0x4e: + case 0x5e: + case 0x8e: + case 0x9e: + /* 6th and 7th generation Intel CoreTM processors and Intel Xeon W Processor Family */ + nominal_freq_khz = 24000; // 24 MHz + break; + case 0x5c: + /* Next Generation Intel Atom processors based on Goldmont Microarchitecture with + CPUID signature 06_5CH (does not include Intel Xeon processors) */ + nominal_freq_khz = 19200; // 19.2 MHz + break; + default: + break; + } + } + + /* From native_calibrate_tsc() in Linux: https://github.com/torvalds/linux/blob/master/arch/x86/kernel/tsc.c#L696-L707 + Some Intel SoCs like Skylake and Kabylake don't report the crystal + clock, but we can easily calculate it to a high degree of accuracy + by considering the crystal ratio and the CPU speed. */ + if ((nominal_freq_khz == 0) && (raw->basic_cpuid[0][EAX] >= 0x16)) { + base_freq_mhz = EXTRACTS_BITS(raw->basic_cpuid[0x16][EAX], 15, 0); + nominal_freq_khz = base_freq_mhz * 1000 * denominator / numerator; + debugf(1, "cpu_clock_by_tsc: no crystal clock frequency detected, using base frequency (%u MHz) to calculate it\n", base_freq_mhz); + } + + if (nominal_freq_khz == 0) { + debugf(1, "cpu_clock_by_tsc: no crystal clock frequency detected\n"); + return -1; + } + + /* For Intel processors in which the nominal core crystal clock frequency is enumerated in CPUID.15H.ECX and the + core crystal clock ratio is encoded in CPUID.15H (see Table 3-8 "Information Returned by CPUID Instruction"), the + nominal TSC frequency can be determined by using the following equation: + Nominal TSC frequency = ( CPUID.15H.ECX[31:0] * CPUID.15H.EBX[31:0] ) / CPUID.15H.EAX[31:0] */ + debugf(1, "cpu_clock_by_tsc: denominator=%u, numerator=%u, nominal_freq_khz=%u\n", denominator, numerator, nominal_freq_khz); + + /* Return TSC frequency in MHz */ + return (nominal_freq_khz * numerator) / denominator / 1000; +} + int cpu_clock(void) { int result; diff --git a/libcpuid/recog_amd.c b/libcpuid/recog_amd.c index 26a36e2a..8fe7a577 100644 --- a/libcpuid/recog_amd.c +++ b/libcpuid/recog_amd.c @@ -176,7 +176,7 @@ const struct match_entry_t cpudb_amd[] = { { 15, -1, -1, 15, 0x5f, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 (Manila/256K)" }, { 15, -1, -1, 15, 0x6b, 2, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 Dual (Sherman/256K)"}, { 15, -1, -1, 15, 0x6b, 2, 512, -1, NC, SEMPRON_ , 0, "Sempron 64 Dual (Sherman/512K)"}, - { 15, -1, -1, 15, 0x7c, 1, 512, -1, NC, ATHLON_ , 0, "Athlon 64 (Sherman/515K)" }, + { 15, -1, -1, 15, 0x7c, 1, 512, -1, NC, ATHLON_ , 0, "Athlon 64 (Sherman/512K)" }, { 15, -1, -1, 15, 0x7f, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 (Sparta/256K)" }, { 15, -1, -1, 15, 0x7f, 1, 512, -1, NC, SEMPRON_ , 0, "Sempron 64 (Sparta/512K)" }, { 15, -1, -1, 15, 0x4c, 1, 256, -1, NC, MOBILE_| SEMPRON_ , 0, "Mobile Sempron 64 (Keene/256K)"}, @@ -439,11 +439,11 @@ static void load_amd_features(struct cpu_raw_data_t* raw, struct cpu_id_t* data) - bit 0: FP128 */ data->detection_hints[CPU_HINT_SSE_SIZE_AUTH] = 1; if ((raw->ext_cpuid[0x1a][EAX] >> 2) & 1) - data->sse_size = 256; + data->x86.sse_size = 256; else if ((raw->ext_cpuid[0x1a][EAX]) & 1) - data->sse_size = 128; + data->x86.sse_size = 128; else - data->sse_size = 64; + data->x86.sse_size = 64; } } @@ -496,7 +496,7 @@ static void decode_amd_number_of_cores(struct cpu_raw_data_t* raw, struct cpu_id } if (data->flags[CPU_FEATURE_HT]) { if (num_cores > 1) { - if ((data->ext_family >= 23) && (raw->ext_cpuid[0][EAX] >= 30)) + if ((data->x86.ext_family >= 23) && (raw->ext_cpuid[0][EAX] >= 30)) /* Ryzen 3 has SMT flag, but in fact cores count is equal to threads count. Ryzen 5/7 reports twice as many "real" cores (e.g. 16 cores instead of 8) because of SMT. */ /* On PPR 17h, page 82: @@ -651,6 +651,15 @@ static void decode_amd_codename(struct cpu_id_t* data, struct internal_id_info_t code_and_bits.bits &= ~(ATHLON_ | _64_); code_and_bits.bits |= SEMPRON_; } + if (code_str) + debugf(2, "Detected AMD brand code: %d (%s)\n", code_and_bits.code, code_str); + else + debugf(2, "Detected AMD brand code: %d\n", code_and_bits.code); + + if (code_and_bits.bits) { + debugf(2, "Detected AMD bits: "); + debug_print_lbits(2, code_and_bits.bits); + } internal->code.amd = code_and_bits.code; internal->bits = code_and_bits.bits; @@ -667,18 +676,25 @@ int cpuid_identify_amd(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct decode_amd_cache_info(raw, data); decode_amd_number_of_cores(raw, data); decode_amd_codename(data, internal); + decode_architecture_version_x86(data); + data->purpose = cpuid_identify_purpose_amd(raw); return 0; } cpu_purpose_t cpuid_identify_purpose_amd(struct cpu_raw_data_t* raw) { - //FIXME: ext_cpuid[0x26] => index 38 is past the end of the array (which contains 32 elements) - //TODO: leaf CPUID_Fn80000026 needs to be added in cpu_raw_data_t - (void)(raw); -#if 0 - /* Check for hybrid architecture - From Processor Programming Reference (PPR) for AMD Family 19h Model 70h, Revision A0 Processors - Available at https://www.amd.com/system/files/TechDocs/57019-A0-PUB_3.00.zip + int i; + + /* Check if Extended CPU Topology is supported */ + if (raw->amd_fn80000026h[0][EAX] == 0x0) + return PURPOSE_GENERAL; + + /* Check for heterogeneous cores + From AMD64 Architecture Programmer’s Manual - Volume 3: General-Purpose and System Instructions + Available at https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/programmer-references/24594.pdf + + - CPUID_Fn80000026_EAX [Extended CPU Topology][30] is HeterogeneousCores. + Set to 1 if all components at the current hierarchy level do not consist of the cores that report the same core type (CoreType). - CPUID_Fn80000026_ECX [Extended CPU Topology][15:8] is LevelType. LevelType 01h is Core. @@ -686,14 +702,16 @@ cpu_purpose_t cpuid_identify_purpose_amd(struct cpu_raw_data_t* raw) - CPUID_Fn80000026_EBX [Extended CPU Topology][31:28] is CoreType. Only valid while LevelType=Core. */ - if (EXTRACTS_BITS(raw->ext_cpuid[0x26][ECX], 15, 8) == 0x1) { - debugf(3, "Detected AMD CPU hybrid architecture\n"); - switch (EXTRACTS_BITS(raw->ext_cpuid[0x26][EBX], 31, 28)) { - case 0x0: return PURPOSE_PERFORMANCE; - case 0x1: return PURPOSE_EFFICIENCY; - default: return PURPOSE_GENERAL; + for (i = 0; (raw->amd_fn80000026h[i][EBX] != 0x0) && (raw->amd_fn80000026h[i][ECX] != 0x0) && (i < MAX_AMDFN80000026H_LEVEL); i++) { + if ((EXTRACTS_BIT(raw->amd_fn80000026h[i][EAX], 30) == 0x1) && (EXTRACTS_BITS(raw->amd_fn80000026h[i][ECX], 15, 8) == 0x1)) { + debugf(3, "Detected AMD CPU with heterogeneous cores\n"); + switch (EXTRACTS_BITS(raw->amd_fn80000026h[i][EBX], 31, 28)) { + case 0x0: return PURPOSE_PERFORMANCE; + case 0x1: return PURPOSE_EFFICIENCY; + default: return PURPOSE_GENERAL; + } } } -#endif + return PURPOSE_GENERAL; } diff --git a/libcpuid/recog_arm.c b/libcpuid/recog_arm.c new file mode 100644 index 00000000..97d72e14 --- /dev/null +++ b/libcpuid/recog_arm.c @@ -0,0 +1,959 @@ +/* + * Copyright 2024 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include "libcpuid.h" +#include "libcpuid_util.h" +#include "libcpuid_internal.h" +#include "recog_arm.h" + +struct arm_feature_map_t { + uint8_t highbit, lowbit; + uint8_t value; + cpu_feature_t feature; + cpu_feature_level_t ver_optional, ver_mandatory; +}; + +struct arm_arch_feature_t { + uint8_t optional, mandatory; +}; + +struct arm_arch_extension_t { + struct arm_arch_feature_t present[NUM_CPU_FEATURE_LEVELS], total[NUM_CPU_FEATURE_LEVELS]; +}; + +struct arm_id_part { + const int id; + const char* name; + const char* codename; +}; + +struct arm_hw_impl { + const int32_t id; + const cpu_vendor_t vendor; + const struct arm_id_part *parts; + const char *name; +}; + +/* Decoded PartNum for all implementers + ID taken from lscpu-arm: https://github.com/util-linux/util-linux/blob/master/sys-utils/lscpu-arm.c +*/ +/* Codenames: https://en.wikichip.org/wiki/arm_holdings */ +static const struct arm_id_part arm_part[] = { + { 0x810, "ARM810", "" }, + { 0x920, "ARM920", "" }, + { 0x922, "ARM922", "" }, + { 0x926, "ARM926", "" }, + { 0x940, "ARM940", "" }, + { 0x946, "ARM946", "" }, + { 0x966, "ARM966", "" }, + { 0xa20, "ARM1020", "" }, + { 0xa22, "ARM1022", "" }, + { 0xa26, "ARM1026", "" }, + { 0xb02, "ARM11 MPCore", "" }, + { 0xb36, "ARM1136", "" }, + { 0xb56, "ARM1156", "" }, + { 0xb76, "ARM1176", "" }, + { 0xc05, "Cortex-A5", "Sparrow" }, + { 0xc07, "Cortex-A7", "Kingfisher" }, + { 0xc08, "Cortex-A8", "Tiger" }, + { 0xc09, "Cortex-A9", "Falcon" }, + { 0xc0d, "Cortex-A17", "Owl" }, /* Originally A12 */ + { 0xc0f, "Cortex-A15", "Eagle" }, + { 0xc0e, "Cortex-A17", "Owl" }, + { 0xc14, "Cortex-R4", "" }, + { 0xc15, "Cortex-R5", "" }, + { 0xc17, "Cortex-R7", "" }, + { 0xc18, "Cortex-R8", "" }, + { 0xc20, "Cortex-M0", "Swift" }, + { 0xc21, "Cortex-M1", "Proteus" }, + { 0xc23, "Cortex-M3", "Sandcat" }, + { 0xc24, "Cortex-M4", "Merlin" }, + { 0xc27, "Cortex-M7", "Pelican" }, + { 0xc60, "Cortex-M0+", "Flycatcher" }, + { 0xd01, "Cortex-A32", "Minerva" }, + { 0xd02, "Cortex-A34", "Metis" }, + { 0xd03, "Cortex-A53", "Apollo" }, + { 0xd04, "Cortex-A35", "Mercury" }, + { 0xd05, "Cortex-A55", "Ananke" }, + { 0xd06, "Cortex-A65", "Helios" }, + { 0xd07, "Cortex-A57", "Atlas" }, + { 0xd08, "Cortex-A72", "Maia" }, + { 0xd09, "Cortex-A73", "Artemis" }, + { 0xd0a, "Cortex-A75", "Prometheus" }, + { 0xd0b, "Cortex-A76", "Enyo" }, + { 0xd0c, "Neoverse-N1", "Ares" }, + { 0xd0d, "Cortex-A77", "Deimos" }, + { 0xd0e, "Cortex-A76AE", "Enyo-AE" }, + { 0xd13, "Cortex-R52", "" }, + { 0xd15, "Cortex-R82", "" }, + { 0xd16, "Cortex-R52+", "" }, + { 0xd20, "Cortex-M23", "Grebe" }, + { 0xd21, "Cortex-M33", "Teal" }, + { 0xd22, "Cortex-M55", "Yamin" }, + { 0xd23, "Cortex-M85", "" }, + { 0xd40, "Neoverse-V1", "Zeus" }, + { 0xd41, "Cortex-A78", "Hercules" }, + { 0xd42, "Cortex-A78AE", "Hercules-AE" }, + { 0xd43, "Cortex-A65AE", "Helios-AE" }, + { 0xd44, "Cortex-X1", "Hera" }, + { 0xd46, "Cortex-A510", "Klein" }, + { 0xd47, "Cortex-A710", "Matterhorn" }, + { 0xd48, "Cortex-X2", "Matterhorn ELP" }, + { 0xd49, "Neoverse-N2", "Perseus" }, + { 0xd4a, "Neoverse-E1", "Helios" }, + { 0xd4b, "Cortex-A78C", "Hercules-C" }, + { 0xd4c, "Cortex-X1C", "Hera-C" }, + { 0xd4d, "Cortex-A715", "Makalu" }, + { 0xd4e, "Cortex-X3", "Makalu ELP" }, + { 0xd4f, "Neoverse-V2", "Demeter" }, + { 0xd80, "Cortex-A520", "Hayes" }, + { 0xd81, "Cortex-A720", "Hunter" }, + { 0xd82, "Cortex-X4", "Hunter ELP" }, + { 0xd84, "Neoverse-V3", "Poseidon" }, + { 0xd8e, "Neoverse-N3", "Hermes" }, + { -1, "unknown", "" }, +}; + +static const struct arm_id_part brcm_part[] = { + { 0x0f, "Brahma-B15", "" }, + { 0x100, "Brahma-B53", "" }, + { 0x516, "ThunderX2", "" }, + { -1, "unknown", "" }, +}; + +static const struct arm_id_part dec_part[] = { + { 0xa10, "SA110", "" }, + { 0xa11, "SA1100", "" }, + { -1, "unknown", "" }, +}; + +static const struct arm_id_part cavium_part[] = { + { 0x0a0, "ThunderX", "" }, + { 0x0a1, "ThunderX-88XX", "" }, + { 0x0a2, "ThunderX-81XX", "" }, + { 0x0a3, "ThunderX-83XX", "" }, + { 0x0af, "ThunderX2-99xx", "" }, + { 0x0b0, "OcteonTX2", "" }, + { 0x0b1, "OcteonTX2-98XX", "" }, + { 0x0b2, "OcteonTX2-96XX", "" }, + { 0x0b3, "OcteonTX2-95XX", "" }, + { 0x0b4, "OcteonTX2-95XXN", "" }, + { 0x0b5, "OcteonTX2-95XXMM", "" }, + { 0x0b6, "OcteonTX2-95XXO", "" }, + { 0x0b8, "ThunderX3-T110", "" }, + { -1, "unknown", "" }, +}; + +static const struct arm_id_part apm_part[] = { + { 0x000, "X-Gene", "" }, + { -1, "unknown", "" }, +}; + +static const struct arm_id_part qcom_part[] = { + { 0x001, "Oryon", "" }, + { 0x00f, "Scorpion", "" }, + { 0x02d, "Scorpion", "" }, + { 0x04d, "Krait", "" }, + { 0x06f, "Krait", "" }, + { 0x201, "Kryo", "" }, + { 0x205, "Kryo", "" }, + { 0x211, "Kryo", "" }, + { 0x800, "Falkor-V1/Kryo", "" }, + { 0x801, "Kryo-V2", "" }, + { 0x802, "Kryo-3XX-Gold", "" }, + { 0x803, "Kryo-3XX-Silver", "" }, + { 0x804, "Kryo-4XX-Gold", "" }, + { 0x805, "Kryo-4XX-Silver", "" }, + { 0xc00, "Falkor", "" }, + { 0xc01, "Saphira", "" }, + { -1, "unknown", "" }, +}; + +static const struct arm_id_part samsung_part[] = { + { 0x001, "Exynos M1", "" }, + { 0x002, "Exynos M3", "" }, + { 0x003, "Exynos M4", "" }, + { 0x004, "Exynos M5", "" }, + { -1, "unknown", "" }, +}; + +static const struct arm_id_part nvidia_part[] = { + { 0x000, "Denver", "" }, + { 0x003, "Denver 2", "" }, + { 0x004, "Carmel", "" }, + { -1, "unknown", "" }, +}; + +static const struct arm_id_part marvell_part[] = { + { 0x131, "Feroceon-88FR131", "" }, + { 0x581, "PJ4/PJ4b", "" }, + { 0x584, "PJ4B-MP", "" }, + { -1, "unknown", "" }, +}; + +static const struct arm_id_part apple_part[] = { + { 0x000, "A6", "Swift" }, + { 0x001, "A7", "Cyclone" }, + { 0x002, "A8", "Typhoon" }, + { 0x003, "A8X", "Typhoon" }, + { 0x004, "A9", "Twister" }, + { 0x005, "A9X", "Twister" }, + { 0x006, "A10 Fusion", "Zephyr" }, + { 0x007, "A10 Fusion", "Hurricane" }, + { 0x008, "A11 Bionic", "Monsoon" }, + { 0x009, "A11 Bionic", "Mistral" }, + { 0x00b, "A12", "Vortex" }, + { 0x00c, "A12", "Tempest" }, + { 0x00f, "M9", "Tempest" }, + { 0x010, "A12X Bionic", "Vortex" }, + { 0x011, "A12X Bionic", "Tempest" }, + { 0x012, "A13 Bionic", "Lightning" }, + { 0x013, "A13 Bionic", "Thunder" }, + { 0x020, "A14", "Icestorm" }, + { 0x021, "A14", "Firestorm" }, + { 0x022, "M1", "Icestorm" }, + { 0x023, "M1", "Firestorm" }, + { 0x024, "M1 Pro", "Icestorm" }, + { 0x025, "M1 Pro", "Firestorm" }, + { 0x026, "M10", "Thunder" }, + { 0x028, "M1 Max", "Icestorm" }, + { 0x029, "M1 Max", "Firestorm" }, + { 0x030, "A15", "Blizzard" }, + { 0x031, "A15", "Avalanche" }, + { 0x032, "M2", "Blizzard" }, + { 0x033, "M2", "Avalanche" }, + { 0x034, "M2 Pro", "Blizzard" }, + { 0x035, "M2 Pro", "Avalanche" }, + { 0x036, "A16", "Sawtooth" }, + { 0x037, "A16", "Everest" }, + { 0x038, "M2 Max", "Blizzard" }, + { 0x039, "M2 Max", "Avalanche" }, + { -1, "unknown", "" }, +}; + +static const struct arm_id_part faraday_part[] = { + { 0x526, "FA526", "" }, + { 0x626, "FA626", "" }, + { -1, "unknown", "" }, +}; + +static const struct arm_id_part intel_part[] = { + { 0x200, "i80200", "" }, + { 0x210, "PXA250A", "" }, + { 0x212, "PXA210A", "" }, + { 0x242, "i80321-400", "" }, + { 0x243, "i80321-600", "" }, + { 0x290, "PXA250B/PXA26x", "" }, + { 0x292, "PXA210B", "" }, + { 0x2c2, "i80321-400-B0", "" }, + { 0x2c3, "i80321-600-B0", "" }, + { 0x2d0, "PXA250C/PXA255/PXA26x", "" }, + { 0x2d2, "PXA210C", "" }, + { 0x411, "PXA27x", "" }, + { 0x41c, "IPX425-533", "" }, + { 0x41d, "IPX425-400", "" }, + { 0x41f, "IPX425-266", "" }, + { 0x682, "PXA32x", "" }, + { 0x683, "PXA930/PXA935", "" }, + { 0x688, "PXA30x", "" }, + { 0x689, "PXA31x", "" }, + { 0xb11, "SA1110", "" }, + { 0xc12, "IPX1200", "" }, + { -1, "unknown", "" }, +}; + +static const struct arm_id_part fujitsu_part[] = { + { 0x001, "A64FX", "" }, + { -1, "unknown", "" }, +}; + +static const struct arm_id_part hisi_part[] = { + { 0xd01, "TaiShan-v110", "" }, /* used in Kunpeng-920 SoC */ + { 0xd02, "TaiShan-v120", "" }, /* used in Kirin 990A and 9000S SoCs */ + { 0xd40, "Cortex-A76", "" }, /* HiSilicon uses this ID though advertises A76 */ + { 0xd41, "Cortex-A77", "" }, /* HiSilicon uses this ID though advertises A77 */ + { -1, "unknown", "" }, +}; + +static const struct arm_id_part ampere_part[] = { + { 0xac3, "Ampere-1", "" }, + { 0xac4, "Ampere-1a", "" }, + { -1, "unknown", "" }, +}; + +static const struct arm_id_part ft_part[] = { + { 0x303, "FTC310", "" }, + { 0x660, "FTC660", "" }, + { 0x661, "FTC661", "" }, + { 0x662, "FTC662", "" }, + { 0x663, "FTC663", "" }, + { 0x664, "FTC664", "" }, + { 0x862, "FTC862", "" }, + { -1, "unknown", "" }, +}; + +static const struct arm_id_part ms_part[] = { + { 0xd49, "Azure-Cobalt-100", "" }, + { -1, "unknown", "" }, +}; + +static const struct arm_id_part unknown_part[] = { + { -1, "unknown", "" }, +}; + +/* Implementers list */ +static const struct arm_hw_impl hw_implementer[] = { + { 0x41, VENDOR_ARM, arm_part, "ARM" }, + { 0x42, VENDOR_BROADCOM, brcm_part, "Broadcom" }, + { 0x43, VENDOR_CAVIUM, cavium_part, "Cavium" }, + { 0x44, VENDOR_DEC, dec_part, "DEC" }, + { 0x46, VENDOR_FUJITSU, fujitsu_part, "FUJITSU" }, + { 0x48, VENDOR_HISILICON, hisi_part, "HiSilicon" }, + { 0x49, VENDOR_INFINEON, unknown_part, "Infineon" }, + { 0x4d, VENDOR_FREESCALE, unknown_part, "Motorola/Freescale" }, + { 0x4e, VENDOR_NVIDIA, nvidia_part, "NVIDIA" }, + { 0x50, VENDOR_APM, apm_part, "APM" }, + { 0x51, VENDOR_QUALCOMM, qcom_part, "Qualcomm" }, + { 0x53, VENDOR_SAMSUNG, samsung_part, "Samsung" }, + { 0x56, VENDOR_MARVELL, marvell_part, "Marvell" }, + { 0x61, VENDOR_APPLE, apple_part, "Apple" }, + { 0x66, VENDOR_FARADAY, faraday_part, "Faraday" }, + { 0x69, VENDOR_INTEL, intel_part, "Intel" }, + { 0x6d, VENDOR_MICROSOFT, ms_part, "Microsoft" }, + { 0x70, VENDOR_PHYTIUM, ft_part, "Phytium" }, + { 0xc0, VENDOR_AMPERE, ampere_part, "Ampere" }, + { -1, VENDOR_UNKNOWN, unknown_part, "unknown" }, +}; + +static const struct arm_hw_impl* get_cpu_implementer_from_code(uint8_t implementer) +{ + int i; + + for (i = 0; hw_implementer[i].id >= 0; i++) { + if (hw_implementer[i].id == implementer) + break; + } + + /* This function returns an item with VENDOR_UNKNOWN if implementer is not found in the "hw_implementer" array */ + return &hw_implementer[i]; +} + +static const struct arm_hw_impl* get_cpu_implementer_from_vendor(cpu_vendor_t vendor) +{ + int i; + + for (i = 0; hw_implementer[i].id >= 0; i++) { + if (hw_implementer[i].vendor == vendor) + break; + } + + /* This function returns an item with VENDOR_UNKNOWN if implementer is not found in the "hw_implementer" array */ + return &hw_implementer[i]; +} + +static const struct arm_id_part* get_cpu_implementer_parts(const struct arm_hw_impl* hw_impl, uint16_t part_num) +{ + int i; + + for (i = 0; hw_impl->parts[i].id >= 0; i++) { + if (hw_impl->parts[i].id == part_num) + break; + } + + /* This function retuns "unknown" if PartNum is not found in the "parts" array */ + return &hw_impl->parts[i]; +} + +static void set_feature_status(struct cpu_id_t* data, struct arm_arch_extension_t* ext_status, bool is_present, cpu_feature_t feature, cpu_feature_level_t optional_from, cpu_feature_level_t mandatory_from) +{ + if (optional_from >= 0) + ext_status->total[optional_from].optional++; + if (mandatory_from >= 0) + ext_status->total[mandatory_from].mandatory++; + + if (is_present) { + data->flags[feature] = 1; + debugf(3, "feature %s is present", cpu_feature_str(feature)); + if (optional_from >= 0) { + ext_status->present[optional_from].optional++; + debugf(3, " (optional from %s", cpu_feature_level_str(optional_from)); + if (mandatory_from >= 0) { + ext_status->present[mandatory_from].mandatory++; + debugf(3, ", mandatory from %s", cpu_feature_level_str(mandatory_from)); + } + debugf(3, ")"); + } + debugf(3, "\n"); + } +} + +static void match_arm_features(const struct arm_feature_map_t* matchtable, const char* reg_name, const int reg_number, uint64_t reg_value, struct cpu_id_t* data, struct arm_arch_extension_t* ext_status) +{ + int i; + bool feature_is_present; + + for (i = 0; matchtable[i].feature != -1; i++) { + feature_is_present = (EXTRACTS_BITS(reg_value, matchtable[i].highbit, matchtable[i].lowbit) == matchtable[i].value); + if (feature_is_present) + debugf(3, "Register %8s%i (0x%016" PRIX64 "): match value %u for bits [%2u:%2u], ", reg_name, reg_number, reg_value, matchtable[i].value, matchtable[i].highbit, matchtable[i].lowbit); + set_feature_status(data, ext_status, feature_is_present, matchtable[i].feature, matchtable[i].ver_optional, matchtable[i].ver_mandatory); + } +} + +static void load_arm_feature_pauth(struct cpu_id_t* data, struct arm_arch_extension_t* ext_status) +{ + /* FEAT_PAuth, Pointer authentication + When FEAT_PAuth is implemented, one of the following must be true: + - Exactly one of the PAC algorithms is implemented. + - If the PACGA instruction and other Pointer authentication instructions use different PAC algorithms, exactly two PAC algorithms are implemented. + The PAC algorithm features are: + - FEAT_PACQARMA5. + - FEAT_PACIMP. + - FEAT_PACQARMA3. */ + set_feature_status(data, ext_status, + (data->flags[CPU_FEATURE_PACIMP] || + data->flags[CPU_FEATURE_PACQARMA3] || + data->flags[CPU_FEATURE_PACQARMA5]), + CPU_FEATURE_PAUTH, FEATURE_LEVEL_ARM_V8_2_A, FEATURE_LEVEL_ARM_V8_3_A); +} + +static void load_arm_feature_mpam(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct arm_arch_extension_t* ext_status) +{ + /* FEAT_MPAM, Memory Partitioning and Monitoring Extension + MPAM Extension version: MPAM | MPAM_frac + Not implemented: 0b0000 | 0b0000 + v0.1 is implemented: 0b0000 | 0b0001 + v1.0 is implemented: 0b0001 | 0b0000 + v1.1 is implemented: 0b0001 | 0b0001 */ + const uint8_t mpam = EXTRACTS_BITS(raw->arm_id_aa64pfr[0], 43, 40); + const uint8_t mpam_frac = EXTRACTS_BITS(raw->arm_id_aa64pfr[1], 19, 16); + if ((mpam != 0b0000) || (mpam_frac != 0b0000)) { + debugf(2, "MPAM Extension version is %u.%u\n", mpam, mpam_frac); + set_feature_status(data, ext_status, true, CPU_FEATURE_MPAM, FEATURE_LEVEL_ARM_V8_2_A, -1); + set_feature_status(data, ext_status, (mpam == 0b0000) && (mpam_frac == 0b0001), CPU_FEATURE_MPAMV0P1, FEATURE_LEVEL_ARM_V8_5_A, -1); + set_feature_status(data, ext_status, (mpam == 0b0001) && (mpam_frac == 0b0001), CPU_FEATURE_MPAMV1P1, FEATURE_LEVEL_ARM_V8_5_A, -1); + } +} + +static void load_arm_feature_mte4(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct arm_arch_extension_t* ext_status) +{ + /* FEAT_MTE4, Enhanced Memory Tagging Extension */ + const uint8_t mte = EXTRACTS_BITS(raw->arm_id_aa64pfr[1], 11, 8); + if (mte >= 0b0010) { + /* These fields are valid only if ID_AA64PFR1_EL1.MTE >= 0b0010 */ + const uint8_t mte_frac = EXTRACTS_BITS(raw->arm_id_aa64pfr[1], 43, 40); + const uint8_t mtex = EXTRACTS_BITS(raw->arm_id_aa64pfr[1], 55, 52); + set_feature_status(data, ext_status, (mte_frac == 0b0000), CPU_FEATURE_MTE_ASYNC, FEATURE_LEVEL_ARM_V8_5_A, -1); + set_feature_status(data, ext_status, (mtex == 0b0001), CPU_FEATURE_MTE_CANONICAL_TAGS, FEATURE_LEVEL_ARM_V8_7_A, -1); + set_feature_status(data, ext_status, (mtex == 0b0001), CPU_FEATURE_MTE_NO_ADDRESS_TAGS, FEATURE_LEVEL_ARM_V8_7_A, -1); + } + /* If FEAT_MTE4 is implemented, then FEAT_MTE_CANONICAL_TAGS, + FEAT_MTE_NO_ADDRESS_TAGS, FEAT_MTE_TAGGED_FAR, and + FEAT_MTE_STORE_ONLY are implemented. */ + set_feature_status(data, ext_status, + (data->flags[CPU_FEATURE_MTE_CANONICAL_TAGS] && + data->flags[CPU_FEATURE_MTE_NO_ADDRESS_TAGS] && + data->flags[CPU_FEATURE_MTE_TAGGED_FAR] && + data->flags[CPU_FEATURE_MTE_STORE_ONLY]), + CPU_FEATURE_MTE4, FEATURE_LEVEL_ARM_V8_7_A, -1); +} + +static void decode_arm_architecture_version(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct arm_arch_extension_t* ext_status) +{ + int i; + cpu_feature_level_t feature_level = FEATURE_LEVEL_UNKNOWN; + const cpu_feature_level_t *architecture_arm = NULL; + const uint8_t architecture = EXTRACTS_BITS(raw->arm_midr, 19, 16); + + const struct { uint8_t raw_value; cpu_feature_level_t enum_value; } + architecture_matchtable[] = { + { 0b0000, FEATURE_LEVEL_UNKNOWN }, + { 0b0001, FEATURE_LEVEL_ARM_V4 }, + { 0b0010, FEATURE_LEVEL_ARM_V4T }, + { 0b0011, FEATURE_LEVEL_ARM_V5 }, + { 0b0100, FEATURE_LEVEL_ARM_V5T }, + { 0b0101, FEATURE_LEVEL_ARM_V5TE }, + { 0b0110, FEATURE_LEVEL_ARM_V5TEJ }, + { 0b0111, FEATURE_LEVEL_ARM_V6 }, + { 0b1000, FEATURE_LEVEL_UNKNOWN }, + { 0b1001, FEATURE_LEVEL_UNKNOWN }, + { 0b1010, FEATURE_LEVEL_UNKNOWN }, + { 0b1011, FEATURE_LEVEL_UNKNOWN }, + { 0b1100, FEATURE_LEVEL_ARM_V6_M }, + { 0b1101, FEATURE_LEVEL_UNKNOWN }, + { 0b1110, FEATURE_LEVEL_UNKNOWN } + }; + + /** + * The Armv8.0 architecture extension + * + * The original Armv8-A architecture is called Armv8.0. It contains mandatory and optional architectural features. + * Some features must be implemented together. An implementation is Armv8.0 compliant if it includes all of the + * Armv8.0 architectural features that are mandatory. + * + * An Armv8.0 compliant implementation can additionally include: + * - Armv8.0 features that are optional. + * - Any arbitrary subset of the architectural features of Armv8.1, subject only to those constraints that require that certain features be implemented together. + */ + const cpu_feature_level_t architecture_arm_v8a[] = { + FEATURE_LEVEL_ARM_V8_0_A, /*!< ARMv8.0-A */ + FEATURE_LEVEL_ARM_V8_1_A, /*!< ARMv8.1-A */ + FEATURE_LEVEL_ARM_V8_2_A, /*!< ARMv8.2-A */ + FEATURE_LEVEL_ARM_V8_3_A, /*!< ARMv8.3-A */ + FEATURE_LEVEL_ARM_V8_4_A, /*!< ARMv8.4-A */ + FEATURE_LEVEL_ARM_V8_5_A, /*!< ARMv8.5-A */ + FEATURE_LEVEL_ARM_V8_6_A, /*!< ARMv8.6-A */ + FEATURE_LEVEL_ARM_V8_7_A, /*!< ARMv8.7-A */ + FEATURE_LEVEL_ARM_V8_8_A, /*!< ARMv8.8-A */ + FEATURE_LEVEL_ARM_V8_9_A, /*!< ARMv8.9-A */ + FEATURE_LEVEL_UNKNOWN, + }; + + /** + * The Armv9.0 architecture extension + * + * The Armv9.0 architecture extension adds mandatory and optional architectural features. Some features must be + * implemented together. An implementation is Armv9.0 compliant if all of the following apply: + * - It is Armv8.5 compliant. + * - It includes all of the Armv9.0 architectural features that are mandatory. + * + * An Armv9.0 compliant implementation can additionally include: + * - Armv9.0 features that are optional. + * - Any arbitrary subset of the architectural features of Armv9.1, subject only to those constraints that require that certain features be implemented together. + */ + const cpu_feature_level_t architecture_arm_v9a[] = { + FEATURE_LEVEL_ARM_V9_0_A, /*!< ARMv9.0-A */ + FEATURE_LEVEL_ARM_V9_1_A, /*!< ARMv9.1-A */ + FEATURE_LEVEL_ARM_V9_2_A, /*!< ARMv9.2-A */ + FEATURE_LEVEL_ARM_V9_3_A, /*!< ARMv9.3-A */ + FEATURE_LEVEL_ARM_V9_4_A, /*!< ARMv9.4-A */ + FEATURE_LEVEL_UNKNOWN, + }; + + /* Check if architecture level is explicit */ + for (i = 0; i < COUNT_OF(architecture_matchtable); i++) { + if (architecture == architecture_matchtable[i].raw_value) { + feature_level = architecture_matchtable[i].enum_value; + goto found; + } + } + + /* When architecture is 0b1111, architectural features are individually identified in the ID_* registers */ + //FIXME: it works only for A-profile architecture, M-profile and R-profile are not supported yet + if ((ext_status->present[FEATURE_LEVEL_ARM_V9_0_A].optional > 0) || (ext_status->present[FEATURE_LEVEL_ARM_V9_0_A].mandatory > 0)) + architecture_arm = architecture_arm_v9a; + else if ((ext_status->present[FEATURE_LEVEL_ARM_V8_0_A].optional > 0) || (ext_status->present[FEATURE_LEVEL_ARM_V8_0_A].mandatory > 0)) + architecture_arm = architecture_arm_v8a; + else + goto found; + + for (i = 0; architecture_arm[i] != FEATURE_LEVEL_UNKNOWN; i++) { + debugf(3, "Check if CPU is %s compliant: %2u/%2u optional features detected, %2u/%2u mandatory features required\n", + cpu_feature_level_str(architecture_arm[i]), + ext_status->present[ architecture_arm[i] ].optional, ext_status->total[ architecture_arm[i] ].optional, + ext_status->present[ architecture_arm[i] ].mandatory, ext_status->total[ architecture_arm[i] ].mandatory); + /* CPU is compliant when it includes all of the architectural features that are mandatory */ + if (ext_status->present[ architecture_arm[i] ].mandatory < ext_status->total[ architecture_arm[i] ].mandatory) + break; + /* If there are no mandatory features (like ARMv9.3-A), check that at least one optional feature is implemented */ + else if ((ext_status->total[ architecture_arm[i] ].mandatory == 0) && (ext_status->present[ architecture_arm[i] ].optional == 0)) + break; + else + feature_level = architecture_arm[i]; + } + +found: + data->feature_level = feature_level; + debugf(2, "ARM architecture version is %s\n", cpu_feature_level_str(feature_level)); +} + +#define MAX_MATCHTABLE_ITEMS 32 +#define MATCH_FEATURES_TABLE_WITH_RAW(reg) match_arm_features(matchtable_id_##reg[i], #reg, i, raw->arm_id_##reg[i], data, ext_status) +static void load_arm_features(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct arm_arch_extension_t* ext_status) +{ + int i; + + const struct arm_feature_map_t matchtable_id_aa64dfr[MAX_ARM_ID_AA64DFR_REGS][MAX_MATCHTABLE_ITEMS] = { + [0] /* ID_AA64DFR0 */ = { + { 59, 56, 0b0001, CPU_FEATURE_TRBE_EXT, FEATURE_LEVEL_ARM_V9_3_A, -1 }, + { 55, 52, 0b0001, CPU_FEATURE_BRBE, FEATURE_LEVEL_ARM_V9_1_A, -1 }, + { 55, 52, 0b0010, CPU_FEATURE_BRBEV1P1, FEATURE_LEVEL_ARM_V9_2_A, -1 }, + { 51, 48, 0b0001, CPU_FEATURE_MTPMU, FEATURE_LEVEL_ARM_V8_5_A, -1 }, + { 47, 47, 0b0001, CPU_FEATURE_TRBE, FEATURE_LEVEL_ARM_V9_0_A, -1 }, + { 43, 40, 0b0001, CPU_FEATURE_TRF, FEATURE_LEVEL_ARM_V8_3_A, -1 }, + { 39, 36, 0b0000, CPU_FEATURE_DOUBLELOCK, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 35, 32, 0b0001, CPU_FEATURE_SPE, FEATURE_LEVEL_ARM_V8_1_A, -1 }, + { 35, 32, 0b0010, CPU_FEATURE_SPEV1P1, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 35, 32, 0b0011, CPU_FEATURE_SPEV1P2, FEATURE_LEVEL_ARM_V8_6_A, -1 }, + { 35, 32, 0b0100, CPU_FEATURE_SPEV1P3, FEATURE_LEVEL_ARM_V8_7_A, -1 }, + { 35, 32, 0b0101, CPU_FEATURE_SPEV1P4, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 27, 24, 0b0001, CPU_FEATURE_SEBEP, FEATURE_LEVEL_ARM_V9_3_A, -1 }, + { 19, 16, 0b0001, CPU_FEATURE_PMUV3_SS, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 11, 8, 0b0001, CPU_FEATURE_PMUV3, FEATURE_LEVEL_ARM_V8_0_A, -1 }, /* Performance Monitors Extension, PMUv3 implemented. */ + { 11, 8, 0b0100, CPU_FEATURE_PMUV3P1, FEATURE_LEVEL_ARM_V8_1_A, -1 }, /* PMUv3 for Armv8.1 */ + { 11, 8, 0b0101, CPU_FEATURE_PMUV3P4, FEATURE_LEVEL_ARM_V8_3_A, -1 }, /* PMUv3 for Armv8.4 */ + { 11, 8, 0b0110, CPU_FEATURE_PMUV3P5, FEATURE_LEVEL_ARM_V8_4_A, -1 }, /* PMUv3 for Armv8.5 */ + { 11, 8, 0b0111, CPU_FEATURE_PMUV3P7, FEATURE_LEVEL_ARM_V8_6_A, -1 }, /* PMUv3 for Armv8.7 */ + { 11, 8, 0b1000, CPU_FEATURE_PMUV3P8, FEATURE_LEVEL_ARM_V8_7_A, -1 }, /* PMUv3 for Armv8.8 */ + { 11, 8, 0b1001, CPU_FEATURE_PMUV3P9, FEATURE_LEVEL_ARM_V8_8_A, -1 }, /* PMUv3 for Armv8.9 */ + { 3, 0, 0b1000, CPU_FEATURE_DEBUGV8P2, FEATURE_LEVEL_ARM_V8_1_A, FEATURE_LEVEL_ARM_V8_2_A }, /* Armv8.2 debug architecture */ + { 3, 0, 0b1001, CPU_FEATURE_DEBUGV8P4, FEATURE_LEVEL_ARM_V8_3_A, FEATURE_LEVEL_ARM_V8_4_A }, /* Armv8.4 debug architecture */ + { 3, 0, 0b1010, CPU_FEATURE_DEBUGV8P8, FEATURE_LEVEL_ARM_V8_7_A, FEATURE_LEVEL_ARM_V8_8_A }, /* Armv8.8 debug architecture */ + { 3, 0, 0b1011, CPU_FEATURE_DEBUGV8P9, FEATURE_LEVEL_ARM_V8_8_A, FEATURE_LEVEL_ARM_V8_9_A }, /* Armv8.9 debug architecture */ + { -1, -1, -1, -1, -1, -1 } + }, + [1] /* ID_AA64DFR1 */ = { + { 55, 52, 0b0001, CPU_FEATURE_SPE_DPFZS, FEATURE_LEVEL_ARM_V8_6_A, -1 }, + { 51, 48, 0b0001, CPU_FEATURE_EBEP, FEATURE_LEVEL_ARM_V9_3_A, -1 }, + { 47, 44, 0b0001, CPU_FEATURE_ITE, FEATURE_LEVEL_ARM_V9_3_A, -1 }, + { 43, 40, 0b0001, CPU_FEATURE_ABLE, FEATURE_LEVEL_ARM_V9_3_A, -1 }, + { 43, 40, 0b0001, CPU_FEATURE_BWE, FEATURE_LEVEL_ARM_V9_3_A, -1 }, + { 39, 36, 0b0001, CPU_FEATURE_PMUV3_ICNTR, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 35, 32, 0b0001, CPU_FEATURE_SPMU, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { -1, -1, -1, -1, -1, -1 } + } + }; + + const struct arm_feature_map_t matchtable_id_aa64isar[MAX_ARM_ID_AA64ISAR_REGS][MAX_MATCHTABLE_ITEMS] = { + [0] /* ID_AA64ISAR0 */ = { + { 63, 60, 0b0001, CPU_FEATURE_RNG, FEATURE_LEVEL_ARM_V8_4_A, -1 }, + { 59, 56, 0b0001, CPU_FEATURE_TLBIOS, FEATURE_LEVEL_ARM_V8_3_A, FEATURE_LEVEL_ARM_V8_4_A }, /* Outer Shareable and TLB range maintenance instructions are not implemented */ + { 59, 56, 0b0010, CPU_FEATURE_TLBIOS, FEATURE_LEVEL_ARM_V8_3_A, FEATURE_LEVEL_ARM_V8_4_A }, /* Outer Shareable TLB maintenance instructions are implemented */ + { 59, 56, 0b0010, CPU_FEATURE_TLBIRANGE, FEATURE_LEVEL_ARM_V8_3_A, FEATURE_LEVEL_ARM_V8_4_A }, /* Outer Shareable TLB maintenance instructions are implemented */ + { 55, 52, 0b0001, CPU_FEATURE_FLAGM, FEATURE_LEVEL_ARM_V8_1_A, FEATURE_LEVEL_ARM_V8_4_A }, + { 55, 52, 0b0010, CPU_FEATURE_FLAGM2, FEATURE_LEVEL_ARM_V8_4_A, -1 }, + { 51, 48, 0b0001, CPU_FEATURE_FHM, FEATURE_LEVEL_ARM_V8_1_A, -1 }, + { 47, 44, 0b0001, CPU_FEATURE_DOTPROD, FEATURE_LEVEL_ARM_V8_1_A, -1 }, + { 43, 40, 0b0001, CPU_FEATURE_SM4, FEATURE_LEVEL_ARM_V8_1_A, -1 }, + { 39, 36, 0b0001, CPU_FEATURE_SM3, FEATURE_LEVEL_ARM_V8_1_A, -1 }, + { 35, 32, 0b0001, CPU_FEATURE_SHA3, FEATURE_LEVEL_ARM_V8_1_A, -1 }, + { 31, 28, 0b0001, CPU_FEATURE_RDM, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 27, 24, 0b0001, CPU_FEATURE_TME, FEATURE_LEVEL_ARM_V9_0_A, -1 }, + { 23, 20, 0b0010, CPU_FEATURE_LSE, FEATURE_LEVEL_ARM_V8_0_A, FEATURE_LEVEL_ARM_V8_1_A }, + { 23, 20, 0b0011, CPU_FEATURE_LSE128, FEATURE_LEVEL_ARM_V9_3_A, -1 }, + { 19, 16, 0b0001, CPU_FEATURE_CRC32, FEATURE_LEVEL_ARM_V8_0_A, FEATURE_LEVEL_ARM_V8_1_A }, + { 15, 12, 0b0001, CPU_FEATURE_SHA256, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 15, 12, 0b0010, CPU_FEATURE_SHA512, FEATURE_LEVEL_ARM_V8_1_A, -1 }, + { 11, 8, 0b0001, CPU_FEATURE_SHA1, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 7, 4, 0b0001, CPU_FEATURE_AES, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 7, 4, 0b0010, CPU_FEATURE_PMULL, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { -1, -1, -1, -1, -1, -1 } + }, + [1] /* ID_AA64ISAR1 */ = { + { 63, 60, 0b0001, CPU_FEATURE_LS64, FEATURE_LEVEL_ARM_V8_6_A, -1 }, + { 63, 60, 0b0010, CPU_FEATURE_LS64_V, FEATURE_LEVEL_ARM_V8_6_A, -1 }, + { 63, 60, 0b0011, CPU_FEATURE_LS64_ACCDATA, FEATURE_LEVEL_ARM_V8_6_A, -1 }, + { 59, 56, 0b0001, CPU_FEATURE_XS, FEATURE_LEVEL_ARM_V8_6_A, FEATURE_LEVEL_ARM_V8_7_A }, + { 55, 52, 0b0001, CPU_FEATURE_I8MM, FEATURE_LEVEL_ARM_V8_1_A, FEATURE_LEVEL_ARM_V8_6_A }, + { 51, 48, 0b0001, CPU_FEATURE_DGH, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 47, 44, 0b0001, CPU_FEATURE_BF16, FEATURE_LEVEL_ARM_V8_2_A, FEATURE_LEVEL_ARM_V8_6_A }, + { 47, 44, 0b0010, CPU_FEATURE_EBF16, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 43, 40, 0b0001, CPU_FEATURE_SPECRES, FEATURE_LEVEL_ARM_V8_0_A, FEATURE_LEVEL_ARM_V8_5_A }, + { 43, 40, 0b0010, CPU_FEATURE_SPECRES2, FEATURE_LEVEL_ARM_V8_0_A, FEATURE_LEVEL_ARM_V8_9_A }, + { 39, 36, 0b0001, CPU_FEATURE_SB, FEATURE_LEVEL_ARM_V8_0_A, FEATURE_LEVEL_ARM_V8_5_A }, + { 35, 32, 0b0001, CPU_FEATURE_FRINTTS, FEATURE_LEVEL_ARM_V8_4_A, -1 }, + { 31, 28, 0b0001, CPU_FEATURE_PACIMP, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 27, 24, 0b0001, CPU_FEATURE_PACQARMA5, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 23, 20, 0b0001, CPU_FEATURE_LRCPC, FEATURE_LEVEL_ARM_V8_2_A, FEATURE_LEVEL_ARM_V8_3_A }, + { 23, 20, 0b0010, CPU_FEATURE_LRCPC2, FEATURE_LEVEL_ARM_V8_2_A, FEATURE_LEVEL_ARM_V8_4_A }, + { 23, 20, 0b0011, CPU_FEATURE_LRCPC3, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 19, 16, 0b0001, CPU_FEATURE_FCMA, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 15, 12, 0b0001, CPU_FEATURE_JSCVT, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 11, 8, 0b0001, CPU_FEATURE_PAUTH, FEATURE_LEVEL_ARM_V8_2_A, FEATURE_LEVEL_ARM_V8_3_A }, + { 11, 8, 0b0010, CPU_FEATURE_EPAC, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 11, 8, 0b0011, CPU_FEATURE_PAUTH2, FEATURE_LEVEL_ARM_V8_2_A, FEATURE_LEVEL_ARM_V8_6_A }, + { 11, 8, 0b0100, CPU_FEATURE_FPAC, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 11, 8, 0b0101, CPU_FEATURE_FPACCOMBINE, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 7, 4, 0b0001, CPU_FEATURE_PAUTH, FEATURE_LEVEL_ARM_V8_2_A, FEATURE_LEVEL_ARM_V8_3_A }, + { 7, 4, 0b0010, CPU_FEATURE_EPAC, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 7, 4, 0b0011, CPU_FEATURE_PAUTH2, FEATURE_LEVEL_ARM_V8_2_A, FEATURE_LEVEL_ARM_V8_6_A }, + { 7, 4, 0b0100, CPU_FEATURE_FPAC, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 7, 4, 0b0101, CPU_FEATURE_FPACCOMBINE, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 3, 0, 0b0001, CPU_FEATURE_DPB, FEATURE_LEVEL_ARM_V8_1_A, FEATURE_LEVEL_ARM_V8_2_A }, + { 3, 0, 0b0010, CPU_FEATURE_DPB2, FEATURE_LEVEL_ARM_V8_1_A, FEATURE_LEVEL_ARM_V8_5_A }, + { -1, -1, -1, -1, -1, -1 } + }, + [2] /* ID_AA64ISAR2 */ = { + { 63, 60, 0b0001, CPU_FEATURE_ATS1A, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 55, 52, 0b0001, CPU_FEATURE_CSSC, FEATURE_LEVEL_ARM_V8_7_A, -1 }, + { 51, 48, 0b0001, CPU_FEATURE_RPRFM, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 43, 40, 0b0001, CPU_FEATURE_PRFMSLC, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 39, 36, 0b0001, CPU_FEATURE_SYSINSTR128, FEATURE_LEVEL_ARM_V9_3_A, -1 }, + { 35, 32, 0b0001, CPU_FEATURE_SYSREG128, FEATURE_LEVEL_ARM_V9_3_A, -1 }, + { 31, 28, 0b0001, CPU_FEATURE_CLRBHB, FEATURE_LEVEL_ARM_V8_0_A, FEATURE_LEVEL_ARM_V8_9_A }, + { 27, 24, 0b0001, CPU_FEATURE_CONSTPACFIELD, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 23, 20, 0b0001, CPU_FEATURE_HBC, FEATURE_LEVEL_ARM_V8_7_A, FEATURE_LEVEL_ARM_V8_8_A }, + { 19, 16, 0b0001, CPU_FEATURE_MOPS, FEATURE_LEVEL_ARM_V8_7_A, FEATURE_LEVEL_ARM_V8_8_A }, + { 15, 12, 0b0001, CPU_FEATURE_PAUTH, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 15, 12, 0b0010, CPU_FEATURE_EPAC, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 15, 12, 0b0011, CPU_FEATURE_PAUTH2, FEATURE_LEVEL_ARM_V8_2_A, FEATURE_LEVEL_ARM_V8_6_A }, + { 15, 12, 0b0100, CPU_FEATURE_FPAC, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 15, 12, 0b0101, CPU_FEATURE_FPACCOMBINE, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 11, 8, 0b0001, CPU_FEATURE_PACQARMA3, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 7, 4, 0b0001, CPU_FEATURE_RPRES, FEATURE_LEVEL_ARM_V8_6_A, -1 }, + { 3, 0, 0b0010, CPU_FEATURE_WFXT, FEATURE_LEVEL_ARM_V8_6_A, FEATURE_LEVEL_ARM_V8_7_A }, + { -1, -1, -1, -1, -1, -1 } + } + }; + + const struct arm_feature_map_t matchtable_id_aa64mmfr[MAX_ARM_ID_AA64MMFR_REGS][MAX_MATCHTABLE_ITEMS] = { + [0] /* ID_AA64MMFR0 */ = { + { 63, 60, 0b0001, CPU_FEATURE_ECV, FEATURE_LEVEL_ARM_V8_5_A, FEATURE_LEVEL_ARM_V8_6_A }, + { 63, 60, 0b0010, CPU_FEATURE_ECV, FEATURE_LEVEL_ARM_V8_5_A, FEATURE_LEVEL_ARM_V8_6_A }, + { 59, 56, 0b0001, CPU_FEATURE_FGT, FEATURE_LEVEL_ARM_V8_5_A, -1 }, + { 59, 56, 0b0010, CPU_FEATURE_FGT2, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 47, 44, 0b0001, CPU_FEATURE_EXS, FEATURE_LEVEL_ARM_V8_4_A, -1 }, + { 31, 28, 0b0001, CPU_FEATURE_LPA2, FEATURE_LEVEL_ARM_V8_6_A, -1 }, + { 23, 20, 0b0001, CPU_FEATURE_LPA2, FEATURE_LEVEL_ARM_V8_6_A, -1 }, + { 19, 16, 0b0001, CPU_FEATURE_MIXEDENDEL0, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 11, 8, 0b0001, CPU_FEATURE_MIXEDEND, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 7, 4, 0b0010, CPU_FEATURE_ASID16, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 3, 0, 0b0110, CPU_FEATURE_LPA, FEATURE_LEVEL_ARM_V8_1_A, -1 }, + { -1, -1, -1, -1, -1, -1 } + }, + [1] /* ID_AA64MMFR1 */ = { + { 63, 60, 0b0001, CPU_FEATURE_ECBHB, FEATURE_LEVEL_ARM_V8_0_A, FEATURE_LEVEL_ARM_V8_9_A }, + { 59, 56, 0b0001, CPU_FEATURE_CMOW, FEATURE_LEVEL_ARM_V8_7_A, FEATURE_LEVEL_ARM_V8_8_A }, + { 55, 52, 0b0001, CPU_FEATURE_TIDCP1, FEATURE_LEVEL_ARM_V8_7_A, FEATURE_LEVEL_ARM_V8_8_A }, + { 47, 44, 0b0001, CPU_FEATURE_AFP, FEATURE_LEVEL_ARM_V8_6_A, -1 }, + { 43, 40, 0b0001, CPU_FEATURE_HCX, FEATURE_LEVEL_ARM_V8_6_A, -1 }, + { 39, 36, 0b0010, CPU_FEATURE_ETS2, FEATURE_LEVEL_ARM_V8_0_A, FEATURE_LEVEL_ARM_V8_8_A }, + { 35, 32, 0b0001, CPU_FEATURE_TWED, FEATURE_LEVEL_ARM_V8_5_A, -1 }, + { 31, 28, 0b0001, CPU_FEATURE_XNX, FEATURE_LEVEL_ARM_V8_1_A, -1 }, + { 23, 20, 0b0001, CPU_FEATURE_PAN, FEATURE_LEVEL_ARM_V8_0_A, FEATURE_LEVEL_ARM_V8_1_A }, + { 23, 20, 0b0010, CPU_FEATURE_PAN2, FEATURE_LEVEL_ARM_V8_1_A, FEATURE_LEVEL_ARM_V8_2_A }, + { 23, 20, 0b0011, CPU_FEATURE_PAN3, FEATURE_LEVEL_ARM_V8_0_A, FEATURE_LEVEL_ARM_V8_7_A }, + { 19, 16, 0b0001, CPU_FEATURE_LOR, FEATURE_LEVEL_ARM_V8_0_A, FEATURE_LEVEL_ARM_V8_1_A }, + { 15, 12, 0b0001, CPU_FEATURE_HPDS, FEATURE_LEVEL_ARM_V8_0_A, FEATURE_LEVEL_ARM_V8_1_A }, + { 15, 12, 0b0010, CPU_FEATURE_HPDS2, FEATURE_LEVEL_ARM_V8_1_A, -1 }, + { 11, 8, 0b0001, CPU_FEATURE_VHE, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 7, 4, 0b0010, CPU_FEATURE_VMID16, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 3, 0, 0b0001, CPU_FEATURE_HAFDBS, FEATURE_LEVEL_ARM_V8_1_A, -1 }, + { 3, 0, 0b0010, CPU_FEATURE_HAFDBS, FEATURE_LEVEL_ARM_V8_1_A, -1 }, /* as 0b0001, and adds support for hardware update of the Access flag for Block and Page descriptors */ + { 3, 0, 0b0011, CPU_FEATURE_HAFT, FEATURE_LEVEL_ARM_V8_7_A, -1 }, + { -1, -1, -1, -1, -1, -1 } + }, + [2] /* ID_AA64MMFR2 */ = { + { 63, 60, 0b0001, CPU_FEATURE_E0PD, FEATURE_LEVEL_ARM_V8_4_A, FEATURE_LEVEL_ARM_V8_5_A }, + { 59, 56, 0b0001, CPU_FEATURE_EVT, FEATURE_LEVEL_ARM_V8_2_A, -1 }, /* HCR_EL2.{TOCU, TICAB, TID4} traps are supported. HCR_EL2.{TTLBOS,TTLBIS} traps are not supported */ + { 59, 56, 0b0010, CPU_FEATURE_EVT, FEATURE_LEVEL_ARM_V8_2_A, -1 }, /* HCR_EL2.{TTLBOS, TTLBIS, TOCU, TICAB, TID4} traps are supported */ + { 55, 52, 0b0000, CPU_FEATURE_BBM, FEATURE_LEVEL_ARM_V8_3_A, FEATURE_LEVEL_ARM_V8_4_A }, /* Level 0 support for changing block size is supported */ + { 55, 52, 0b0001, CPU_FEATURE_BBM, FEATURE_LEVEL_ARM_V8_3_A, FEATURE_LEVEL_ARM_V8_4_A }, /* Level 1 support for changing block size is supported */ + { 55, 52, 0b0010, CPU_FEATURE_BBM, FEATURE_LEVEL_ARM_V8_3_A, FEATURE_LEVEL_ARM_V8_4_A }, /* Level 2 support for changing block size is supported */ + { 51, 48, 0b0001, CPU_FEATURE_TTL, FEATURE_LEVEL_ARM_V8_3_A, FEATURE_LEVEL_ARM_V8_4_A }, + { 43, 40, 0b0001, CPU_FEATURE_S2FWB, FEATURE_LEVEL_ARM_V8_3_A, -1 }, + { 39, 36, 0b0001, CPU_FEATURE_IDST, FEATURE_LEVEL_ARM_V8_3_A, FEATURE_LEVEL_ARM_V8_4_A }, + { 35, 32, 0b0001, CPU_FEATURE_LSE2, FEATURE_LEVEL_ARM_V8_2_A, FEATURE_LEVEL_ARM_V8_4_A }, + { 31, 28, 0b0001, CPU_FEATURE_TTST, FEATURE_LEVEL_ARM_V8_3_A, -1 }, + { 23, 20, 0b0001, CPU_FEATURE_CCIDX, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 19, 16, 0b0001, CPU_FEATURE_LVA, FEATURE_LEVEL_ARM_V8_1_A, -1 }, + { 19, 16, 0b0010, CPU_FEATURE_LVA3, FEATURE_LEVEL_ARM_V9_3_A, -1 }, + { 15, 12, 0b0001, CPU_FEATURE_IESB, FEATURE_LEVEL_ARM_V8_1_A, -1 }, + { 11, 8, 0b0001, CPU_FEATURE_LSMAOC, FEATURE_LEVEL_ARM_V8_1_A, -1 }, + { 7, 4, 0b0001, CPU_FEATURE_UAO, FEATURE_LEVEL_ARM_V8_1_A, FEATURE_LEVEL_ARM_V8_2_A }, + { 3, 0, 0b0001, CPU_FEATURE_TTCNP, FEATURE_LEVEL_ARM_V8_1_A, FEATURE_LEVEL_ARM_V8_2_A }, + { -1, -1, -1, -1, -1, -1 } + }, + [3] /* ID_AA64MMFR3 */ = { + { 59, 56, 0b0010, CPU_FEATURE_ADERR, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 55, 52, 0b0010, CPU_FEATURE_ADERR, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 47, 44, 0b0010, CPU_FEATURE_ANERR, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 43, 40, 0b0010, CPU_FEATURE_ANERR, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 35, 32, 0b0001, CPU_FEATURE_D128, FEATURE_LEVEL_ARM_V9_3_A, -1 }, + { 31, 28, 0b0001, CPU_FEATURE_MEC, FEATURE_LEVEL_ARM_V9_2_A, -1 }, + { 27, 24, 0b0001, CPU_FEATURE_AIE, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 23, 20, 0b0001, CPU_FEATURE_S2POE, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 19, 16, 0b0001, CPU_FEATURE_S1POE, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 15, 12, 0b0001, CPU_FEATURE_S2PIE, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 11, 8, 0b0001, CPU_FEATURE_S1PIE, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 7, 4, 0b0001, CPU_FEATURE_SCTLR2, FEATURE_LEVEL_ARM_V8_0_A, FEATURE_LEVEL_ARM_V8_9_A }, + { 3, 0, 0b0001, CPU_FEATURE_TCR2, FEATURE_LEVEL_ARM_V8_0_A, FEATURE_LEVEL_ARM_V8_9_A }, + { -1, -1, -1, -1, -1, -1 } + }, + [4] /* ID_AA64MMFR4 */ = { + { -1, -1, -1, -1, -1, -1 } + } + }; + + const struct arm_feature_map_t matchtable_id_aa64pfr[MAX_ARM_ID_AA64PFR_REGS][MAX_MATCHTABLE_ITEMS] = { + [0] /* ID_AA64PFR0 */ = { + { 63, 60, 0b0001, CPU_FEATURE_CSV3, FEATURE_LEVEL_ARM_V8_0_A, FEATURE_LEVEL_ARM_V8_5_A }, + { 59, 56, 0b0001, CPU_FEATURE_CSV2, FEATURE_LEVEL_ARM_V8_0_A, FEATURE_LEVEL_ARM_V8_5_A }, + { 59, 56, 0b0010, CPU_FEATURE_CSV2_2, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 59, 56, 0b0011, CPU_FEATURE_CSV2_3, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 55, 52, 0b0001, CPU_FEATURE_RME, FEATURE_LEVEL_ARM_V9_1_A, -1 }, + { 51, 48, 0b0001, CPU_FEATURE_DIT, FEATURE_LEVEL_ARM_V8_3_A, FEATURE_LEVEL_ARM_V8_4_A }, + { 47, 44, 0b0001, CPU_FEATURE_AMUV1, FEATURE_LEVEL_ARM_V8_3_A, -1 }, + { 47, 44, 0b0010, CPU_FEATURE_AMUV1P1, FEATURE_LEVEL_ARM_V8_5_A, -1 }, + { 39, 36, 0b0001, CPU_FEATURE_SEL2, FEATURE_LEVEL_ARM_V8_3_A, -1 }, + { 35, 32, 0b0001, CPU_FEATURE_SVE, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 31, 28, 0b0001, CPU_FEATURE_RAS, FEATURE_LEVEL_ARM_V8_0_A, FEATURE_LEVEL_ARM_V8_2_A }, + { 31, 28, 0b0010, CPU_FEATURE_DOUBLEFAULT, FEATURE_LEVEL_ARM_V8_4_A, -1 }, + { 31, 28, 0b0010, CPU_FEATURE_RASV1P1, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 31, 28, 0b0011, CPU_FEATURE_RASV2, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 23, 20, 0b0000, CPU_FEATURE_ADVSIMD, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 23, 20, 0b0001, CPU_FEATURE_ADVSIMD, FEATURE_LEVEL_ARM_V8_0_A, -1 }, /* as for 0b0000, and also includes support for half-precision floating-point arithmetic */ + { 19, 16, 0b0000, CPU_FEATURE_FP, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 19, 16, 0b0001, CPU_FEATURE_FP16, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { -1, -1, -1, -1, -1, -1 } + }, + [1] /* ID_AA64PFR1 */ = { + { 63, 60, 0b0001, CPU_FEATURE_PFAR, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 59, 56, 0b0001, CPU_FEATURE_DOUBLEFAULT2, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 51, 48, 0b0001, CPU_FEATURE_THE, FEATURE_LEVEL_ARM_V8_8_A, -1 }, + { 47, 44, 0b0001, CPU_FEATURE_GCS, FEATURE_LEVEL_ARM_V9_3_A, -1 }, + { 39, 36, 0b0001, CPU_FEATURE_NMI, FEATURE_LEVEL_ARM_V8_7_A, FEATURE_LEVEL_ARM_V8_8_A }, + { 35, 32, 0b0001, CPU_FEATURE_CSV2_1P1, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 35, 32, 0b0010, CPU_FEATURE_CSV2_1P2, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 31, 28, 0b0001, CPU_FEATURE_RNG_TRAP, FEATURE_LEVEL_ARM_V8_4_A, -1 }, + { 27, 24, 0b0001, CPU_FEATURE_SME, FEATURE_LEVEL_ARM_V9_2_A, -1 }, + { 27, 24, 0b0010, CPU_FEATURE_SME2, FEATURE_LEVEL_ARM_V9_2_A, -1 }, + { 11, 8, 0b0001, CPU_FEATURE_MTE, FEATURE_LEVEL_ARM_V8_4_A, -1 }, + { 11, 8, 0b0010, CPU_FEATURE_MTE2, FEATURE_LEVEL_ARM_V8_4_A, -1 }, + { 11, 8, 0b0011, CPU_FEATURE_MTE3, FEATURE_LEVEL_ARM_V8_5_A, -1 }, + { 11, 8, 0b0011, CPU_FEATURE_MTE_ASYM_FAULT, FEATURE_LEVEL_ARM_V8_7_A, -1 }, /* FEAT_MTE3 is implemented if and only if FEAT_MTE_ASYM_FAULT is implemented */ + { 7, 4, 0b0001, CPU_FEATURE_SSBS, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 7, 4, 0b0010, CPU_FEATURE_SSBS2, FEATURE_LEVEL_ARM_V8_0_A, -1 }, + { 3, 0, 0b0001, CPU_FEATURE_BTI, FEATURE_LEVEL_ARM_V8_4_A, FEATURE_LEVEL_ARM_V8_5_A }, + { -1, -1, -1, -1, -1, -1 } + }, + [2] /* ID_AA64PFR2 */ = { + { 11, 8, 0b0001, CPU_FEATURE_MTE_TAGGED_FAR, FEATURE_LEVEL_ARM_V8_7_A, -1 }, + { 7, 4, 0b0001, CPU_FEATURE_MTE_STORE_ONLY, FEATURE_LEVEL_ARM_V8_7_A, -1 }, + { 3, 0, 0b0001, CPU_FEATURE_MTE_PERM, FEATURE_LEVEL_ARM_V8_7_A, -1 }, + { -1, -1, -1, -1, -1, -1 } + } + }; + + const struct arm_feature_map_t matchtable_id_aa64smfr[MAX_ARM_ID_AA64SMFR_REGS][MAX_MATCHTABLE_ITEMS] = { + [0] /* ID_AA64SMFR0 */ = { + { 63, 63, 0b1, CPU_FEATURE_SME_FA64, FEATURE_LEVEL_ARM_V9_2_A, -1 }, + { 59, 56, 0b0001, CPU_FEATURE_SME2, FEATURE_LEVEL_ARM_V9_2_A, -1 }, + { 59, 56, 0b0010, CPU_FEATURE_SME2P1, FEATURE_LEVEL_ARM_V9_2_A, -1 }, + { 55, 52, 0b1111, CPU_FEATURE_SME_I16I64, FEATURE_LEVEL_ARM_V9_2_A, -1 }, + { 48, 48, 0b1, CPU_FEATURE_SME_F64F64, FEATURE_LEVEL_ARM_V9_2_A, -1 }, + { 43, 43, 0b1, CPU_FEATURE_SVE_B16B16, FEATURE_LEVEL_ARM_V9_2_A, -1 }, + { 42, 42, 0b1, CPU_FEATURE_SME_F16F16, FEATURE_LEVEL_ARM_V9_2_A, -1 }, + { -1, -1, -1, -1, -1, -1 } + }, + }; + + const struct arm_feature_map_t matchtable_id_aa64zfr[MAX_ARM_ID_AA64ZFR_REGS][MAX_MATCHTABLE_ITEMS] = { + [0] /* ID_AA64ZFR0 */ = { + { 59, 56, 0b0001, CPU_FEATURE_F64MM, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 55, 52, 0b0001, CPU_FEATURE_F32MM, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 47, 44, 0b0001, CPU_FEATURE_I8MM, FEATURE_LEVEL_ARM_V8_1_A, FEATURE_LEVEL_ARM_V8_6_A }, + { 43, 40, 0b0001, CPU_FEATURE_SVE_SM4, FEATURE_LEVEL_ARM_V9_0_A, -1 }, + { 35, 32, 0b0001, CPU_FEATURE_SVE_SHA3, FEATURE_LEVEL_ARM_V9_0_A, -1 }, + { 27, 24, 0b0001, CPU_FEATURE_SVE_B16B16, FEATURE_LEVEL_ARM_V9_2_A, -1 }, + { 23, 20, 0b0001, CPU_FEATURE_BF16, FEATURE_LEVEL_ARM_V8_2_A, FEATURE_LEVEL_ARM_V8_6_A }, + { 23, 20, 0b0010, CPU_FEATURE_EBF16, FEATURE_LEVEL_ARM_V8_2_A, -1 }, + { 19, 16, 0b0001, CPU_FEATURE_SVE_BITPERM, FEATURE_LEVEL_ARM_V9_0_A, -1 }, + { 7, 4, 0b0001, CPU_FEATURE_SVE_AES, FEATURE_LEVEL_ARM_V9_0_A, -1 }, + { 7, 4, 0b0010, CPU_FEATURE_SVE_PMULL128, FEATURE_LEVEL_ARM_V9_0_A, -1 }, + { 3, 0, 0b0001, CPU_FEATURE_SVE2, FEATURE_LEVEL_ARM_V9_0_A, -1 }, + { 3, 0, 0b0001, CPU_FEATURE_SME, FEATURE_LEVEL_ARM_V9_2_A, -1 }, + { 3, 0, 0b0010, CPU_FEATURE_SME2P1, FEATURE_LEVEL_ARM_V9_2_A, -1 }, + { 3, 0, 0b0010, CPU_FEATURE_SVE2P1, FEATURE_LEVEL_ARM_V9_2_A, -1 }, + { -1, -1, -1, -1, -1, -1 } + }, + }; + + for (i = 0; i < MAX_ARM_ID_AA64DFR_REGS; i++) + MATCH_FEATURES_TABLE_WITH_RAW(aa64dfr); + + for (i = 0; i < MAX_ARM_ID_AA64ISAR_REGS; i++) + MATCH_FEATURES_TABLE_WITH_RAW(aa64isar); + + for (i = 0; i < MAX_ARM_ID_AA64MMFR_REGS; i++) + MATCH_FEATURES_TABLE_WITH_RAW(aa64mmfr); + + for (i = 0; i < MAX_ARM_ID_AA64PFR_REGS; i++) + MATCH_FEATURES_TABLE_WITH_RAW(aa64pfr); + + for (i = 0; i < MAX_ARM_ID_AA64SMFR_REGS; i++) + MATCH_FEATURES_TABLE_WITH_RAW(aa64smfr); + + for (i = 0; i < MAX_ARM_ID_AA64ZFR_REGS; i++) + MATCH_FEATURES_TABLE_WITH_RAW(aa64zfr); + + /* Some fields do not follow the standard ID scheme */ + load_arm_feature_pauth(data, ext_status); + load_arm_feature_mpam(raw, data, ext_status); + load_arm_feature_mte4(raw, data, ext_status); +} +#undef MAX_MATCHTABLE_ITEMS +#undef MATCH_FEATURES_TABLE_WITH_RAW + +int cpuid_identify_arm(struct cpu_raw_data_t* raw, struct cpu_id_t* data) +{ + struct arm_arch_extension_t ext_status; + memset(&ext_status, 0, sizeof(struct arm_arch_extension_t)); + + /* Basic values extraction + Based on "Arm Architecture Reference Manual for A-profile architecture", section D23.2.122 "MIDR_EL1, Main ID Register" + */ + data->arm.implementer = EXTRACTS_BITS(raw->arm_midr, 31, 24); + data->arm.variant = EXTRACTS_BITS(raw->arm_midr, 23, 20); + data->arm.part_num = EXTRACTS_BITS(raw->arm_midr, 15, 4); + data->arm.revision = EXTRACTS_BITS(raw->arm_midr, 3, 0); + data->purpose = cpuid_identify_purpose_arm(raw); + + /* Values decoding */ + const struct arm_hw_impl* hw_impl = get_cpu_implementer_from_code(data->arm.implementer); + const struct arm_id_part* id_part = get_cpu_implementer_parts(hw_impl, data->arm.part_num); + data->vendor = hw_impl->vendor; + strncpy_s(data->vendor_str, VENDOR_STR_MAX, hw_impl->name, VENDOR_STR_MAX); + strncpy_s(data->brand_str, BRAND_STR_MAX, id_part->name, BRAND_STR_MAX); + strncpy_s(data->cpu_codename, CODENAME_STR_MAX, id_part->codename, CODENAME_STR_MAX); + load_arm_features(raw, data, &ext_status); + decode_arm_architecture_version(raw, data, &ext_status); + + return 0; +} + +cpu_purpose_t cpuid_identify_purpose_arm(struct cpu_raw_data_t* raw) +{ + const uint8_t implementer = EXTRACTS_BITS(raw->arm_midr, 31, 24); + const uint16_t part_num = EXTRACTS_BITS(raw->arm_midr, 15, 4); + const struct arm_hw_impl* hw_impl = get_cpu_implementer_from_code(implementer); + const struct arm_id_part* id_part = get_cpu_implementer_parts(hw_impl, part_num); + + /* ARM big.LITTLE Typical Processor Combinations: https://www.arm.com/technologies/big-little */ + if (match_pattern(id_part->name, "Cortex-X[012356789]")) + return PURPOSE_U_PERFORMANCE; + else if (match_pattern(id_part->name, "Cortex-A[67][012356789]") || match_pattern(id_part->name, "Cortex-A[5][6789]")) + return PURPOSE_PERFORMANCE; + else if (match_pattern(id_part->name, "Cortex-A[5][012345]") || match_pattern(id_part->name, "Cortex-A[3][0123456789]")) + return PURPOSE_EFFICIENCY; + + return PURPOSE_GENERAL; +} diff --git a/libcpuid/recog_arm.h b/libcpuid/recog_arm.h new file mode 100644 index 00000000..354be976 --- /dev/null +++ b/libcpuid/recog_arm.h @@ -0,0 +1,32 @@ +/* + * Copyright 2024 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __RECOG_ARM_H__ +#define __RECOG_ARM_H__ + +int cpuid_identify_arm(struct cpu_raw_data_t* raw, struct cpu_id_t* data); +cpu_purpose_t cpuid_identify_purpose_arm(struct cpu_raw_data_t* raw); + +#endif /* __RECOG_ARM_H__ */ diff --git a/libcpuid/recog_centaur.c b/libcpuid/recog_centaur.c index ef13e709..697ab624 100644 --- a/libcpuid/recog_centaur.c +++ b/libcpuid/recog_centaur.c @@ -207,6 +207,7 @@ int cpuid_identify_centaur(struct cpu_raw_data_t* raw, struct cpu_id_t* data, st if (raw->basic_cpuid[0][EAX] >= 4) decode_deterministic_cache_info_x86(raw->intel_fn4, MAX_INTELFN4_LEVEL, data, internal); decode_number_of_cores_x86(raw, data); + decode_architecture_version_x86(data); brand = get_brand_code_and_bits(data); model_code = get_model_code(data, brand); @@ -216,6 +217,15 @@ int cpuid_identify_centaur(struct cpu_raw_data_t* raw, struct cpu_id_t* data, st break; } } + if (brand_code_str) + debugf(2, "Detected Centaur brand code: %d (%s)\n", brand.code, brand_code_str); + else + debugf(2, "Detected Centaur brand code: %d\n", brand.code); + if (brand.bits) { + debugf(2, "Detected Centaur bits: "); + debug_print_lbits(2, brand.bits); + } + debugf(2, "Detected Centaur model code: %d\n", model_code); internal->code.centaur = brand.code; internal->bits = brand.bits; diff --git a/libcpuid/recog_intel.c b/libcpuid/recog_intel.c index 15524181..628a8d0b 100644 --- a/libcpuid/recog_intel.c +++ b/libcpuid/recog_intel.c @@ -702,7 +702,7 @@ static void decode_intel_oldstyle_cache_info(struct cpu_raw_data_t* raw, struct * CPUs (notably Conroe et al), this is L2 cache. In both cases * it means 4MB, 16-way associative, 64-byte line size. */ - if (data->family == 0xf && data->model == 0x6) { + if (data->x86.family == 0xf && data->x86.model == 0x6) { data->l3_cache = 4096; data->l3_assoc = 16; data->l3_cacheline = 64; @@ -874,6 +874,7 @@ static intel_code_and_bits_t get_brand_code_and_bits(struct cpu_id_t* data) code = matchtable[i].c; break; } + debugf(2, "intel matchtable result is %d\n", code); if (bits & XEON_) { if (match_pattern(bs, "W35##") || match_pattern(bs, "[ELXW]75##")) bits |= _7; @@ -881,7 +882,7 @@ static intel_code_and_bits_t get_brand_code_and_bits(struct cpu_id_t* data) code = GAINESTOWN; else if (match_pattern(bs, "[ELXW]56##")) code = WESTMERE; - else if (data->l3_cache > 0 && data->family == 16) + else if (data->l3_cache > 0 && data->x86.family == 16) /* restrict by family, since later Xeons also have L3 ... */ code = IRWIN; } @@ -918,14 +919,14 @@ static intel_code_and_bits_t get_brand_code_and_bits(struct cpu_id_t* data) } } - if (code == CORE_DUO && (bits & MOBILE_) && data->model != 14) { - if (data->ext_model < 23) { + if (code == CORE_DUO && (bits & MOBILE_) && data->x86.model != 14) { + if (data->x86.ext_model < 23) { code = MEROM; } else { code = PENRYN; } } - if (data->ext_model == 23 && + if (data->x86.ext_model == 23 && (code == CORE_DUO || code == PENTIUM_D || (bits & CELERON_))) { code = WOLFDALE; } @@ -1037,29 +1038,31 @@ static void decode_intel_sgx_features(const struct cpu_raw_data_t* raw, struct c if (raw->basic_cpuid[0x12][EAX] == 0) return; // no sub-leafs available, probably it's disabled by BIOS // decode sub-leaf 0: - if (raw->basic_cpuid[0x12][EAX] & 1) data->sgx.flags[INTEL_SGX1] = 1; - if (raw->basic_cpuid[0x12][EAX] & 2) data->sgx.flags[INTEL_SGX2] = 1; - if (data->sgx.flags[INTEL_SGX1] || data->sgx.flags[INTEL_SGX2]) - data->sgx.present = 1; - data->sgx.misc_select = raw->basic_cpuid[0x12][EBX]; - data->sgx.max_enclave_32bit = (raw->basic_cpuid[0x12][EDX] ) & 0xff; - data->sgx.max_enclave_64bit = (raw->basic_cpuid[0x12][EDX] >> 8) & 0xff; + if (raw->basic_cpuid[0x12][EAX] & 1) data->x86.sgx.flags[INTEL_SGX1] = 1; + if (raw->basic_cpuid[0x12][EAX] & 2) data->x86.sgx.flags[INTEL_SGX2] = 1; + if (data->x86.sgx.flags[INTEL_SGX1] || data->x86.sgx.flags[INTEL_SGX2]) + data->x86.sgx.present = 1; + data->x86.sgx.misc_select = raw->basic_cpuid[0x12][EBX]; + data->x86.sgx.max_enclave_32bit = (raw->basic_cpuid[0x12][EDX] ) & 0xff; + data->x86.sgx.max_enclave_64bit = (raw->basic_cpuid[0x12][EDX] >> 8) & 0xff; // decode sub-leaf 1: - data->sgx.secs_attributes = raw->intel_fn12h[1][EAX] | (((uint64_t) raw->intel_fn12h[1][EBX]) << 32); - data->sgx.secs_xfrm = raw->intel_fn12h[1][ECX] | (((uint64_t) raw->intel_fn12h[1][EDX]) << 32); + data->x86.sgx.secs_attributes = raw->intel_fn12h[1][EAX] | (((uint64_t) raw->intel_fn12h[1][EBX]) << 32); + data->x86.sgx.secs_xfrm = raw->intel_fn12h[1][ECX] | (((uint64_t) raw->intel_fn12h[1][EDX]) << 32); // decode higher-order subleafs, whenever present: - data->sgx.num_epc_sections = -1; + data->x86.sgx.num_epc_sections = -1; for (i = 0; i < 1000000; i++) { epc = cpuid_get_epc(i, raw); if (epc.length == 0) { - data->sgx.num_epc_sections = i; + debugf(2, "SGX: epc section request for %d returned null, no more EPC sections.\n", i); + data->x86.sgx.num_epc_sections = i; break; } } - if (data->sgx.num_epc_sections == -1) { - data->sgx.num_epc_sections = 1000000; + if (data->x86.sgx.num_epc_sections == -1) { + debugf(1, "SGX: warning: seems to be infinitude of EPC sections.\n"); + data->x86.sgx.num_epc_sections = 1000000; } } @@ -1105,6 +1108,7 @@ int cpuid_identify_intel(struct cpu_raw_data_t* raw, struct cpu_id_t* data, stru if ((raw->basic_cpuid[0][EAX] < 11) || (decode_intel_extended_topology(raw, data) == 0)) decode_number_of_cores_x86(raw, data); data->purpose = cpuid_identify_purpose_intel(raw); + decode_architecture_version_x86(data); brand = get_brand_code_and_bits(data); model_code = get_model_code(data); @@ -1114,11 +1118,21 @@ int cpuid_identify_intel(struct cpu_raw_data_t* raw, struct cpu_id_t* data, stru break; } } + if (brand_code_str) + debugf(2, "Detected Intel brand code: %d (%s)\n", brand.code, brand_code_str); + else + debugf(2, "Detected Intel brand code: %d\n", brand.code); + if (brand.bits) { + debugf(2, "Detected Intel bits: "); + debug_print_lbits(2, brand.bits); + } + debugf(2, "Detected Intel model code: %d\n", model_code); internal->code.intel = brand.code; internal->bits = brand.bits; if (data->flags[CPU_FEATURE_SGX]) { + debugf(2, "SGX seems to be present, decoding...\n"); // if SGX is indicated by the CPU, verify its presence: decode_intel_sgx_features(raw, data); } @@ -1141,6 +1155,7 @@ cpu_purpose_t cpuid_identify_purpose_intel(struct cpu_raw_data_t* raw) EAX, bits 31-24: Core type */ if (EXTRACTS_BIT(raw->basic_cpuid[0x7][EDX], 15) == 0x1) { + debugf(3, "Detected Intel CPU hybrid architecture\n"); switch (EXTRACTS_BITS(raw->basic_cpuid[0x1a][EAX], 31, 24)) { case 0x20: /* Atom */ /* Acccording to Ramyer M. from Intel, LP E-Cores do not have a L3 cache diff --git a/libnw/cpuid.c b/libnw/cpuid.c index 335d6a80..42f95364 100644 --- a/libnw/cpuid.c +++ b/libnw/cpuid.c @@ -164,11 +164,11 @@ PrintCpuInfo(PNODE node, struct cpu_id_t* data) NWL_NodeAttrSet(node, "Vendor Name", CpuVendorToStr(data->vendor), 0); NWL_NodeAttrSet(node, "Brand", data->brand_str, 0); NWL_NodeAttrSet(node, "Code Name", data->cpu_codename, 0); - NWL_NodeAttrSetf(node, "Family", 0, "%02Xh", data->family); - NWL_NodeAttrSetf(node, "Model", 0, "%02Xh", data->model); - NWL_NodeAttrSetf(node, "Stepping", 0, "%02Xh", data->stepping); - NWL_NodeAttrSetf(node, "Ext.Family", 0, "%02Xh", data->ext_family); - NWL_NodeAttrSetf(node, "Ext.Model", 0, "%02Xh", data->ext_model); + NWL_NodeAttrSetf(node, "Family", 0, "%02Xh", data->x86.family); + NWL_NodeAttrSetf(node, "Model", 0, "%02Xh", data->x86.model); + NWL_NodeAttrSetf(node, "Stepping", 0, "%02Xh", data->x86.stepping); + NWL_NodeAttrSetf(node, "Ext.Family", 0, "%02Xh", data->x86.ext_family); + NWL_NodeAttrSetf(node, "Ext.Model", 0, "%02Xh", data->x86.ext_model); NWL_NodeAttrSetf(node, "Cores", NAFLG_FMT_NUMERIC, "%d", data->num_cores); NWL_NodeAttrSetf(node, "Logical CPUs", NAFLG_FMT_NUMERIC, "%d", data->num_logical_cpus);