Version in base suite: 1.06-2 Base version: cpufetch_1.06-2 Target version: cpufetch_1.07-1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/c/cpufetch/cpufetch_1.06-2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/c/cpufetch/cpufetch_1.07-1.dsc Makefile | 33 +- README.md | 2 debian/changelog | 12 debian/copyright | 6 debian/cpufetch.1 | 2 debian/patches/0001_update_makefile.patch | 7 debian/patches/0002_use_cc_dumpmachine.patch | 9 src/arm/midr.c | 169 +++++++++++ src/arm/soc.c | 388 ++++++++++++++++++++++----- src/arm/socs.h | 64 ++++ src/arm/uarch.c | 95 ++++-- src/arm/uarch.h | 11 src/common/args.c | 3 src/common/ascii.h | 76 +++++ src/common/cpu.c | 6 src/common/cpu.h | 13 src/common/global.c | 2 src/common/pci.c | 55 --- src/common/pci.h | 5 src/common/printer.c | 207 ++++++-------- src/common/soc.c | 61 ++-- src/common/soc.h | 15 - src/common/sysctl.h | 15 - src/common/udev.c | 82 +++++ src/common/udev.h | 7 src/ppc/ppc.c | 8 src/ppc/uarch.c | 3 src/ppc/udev.c | 2 src/riscv/riscv.c | 89 +++++- src/riscv/riscv.h | 125 ++++++++ src/riscv/soc.c | 19 - src/riscv/socs.h | 3 src/riscv/uarch.c | 56 +++ src/riscv/uarch.h | 2 src/riscv/udev.c | 57 +++ src/riscv/udev.h | 8 src/x86/apic.c | 10 src/x86/cpuid.c | 65 +++- src/x86/freq/freq.c | 58 +++- src/x86/freq/freq.h | 2 src/x86/uarch.c | 133 +++++++-- 41 files changed, 1595 insertions(+), 390 deletions(-) diff -Nru cpufetch-1.06/Makefile cpufetch-1.07/Makefile --- cpufetch-1.06/Makefile 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/Makefile 2025-10-31 06:50:04.000000000 +0000 @@ -30,6 +30,10 @@ HEADERS += $(SRC_DIR)freq/freq.h CFLAGS += -pthread endif + ifeq ($(os), FreeBSD) + SOURCE += $(SRC_COMMON)sysctl.c + HEADERS += $(SRC_COMMON)sysctl.h + endif CFLAGS += -DARCH_X86 -std=c99 -fstack-protector-all else ifeq ($(arch), $(filter $(arch), ppc64le ppc64 ppcle ppc)) SRC_DIR=src/ppc/ @@ -66,12 +70,27 @@ OUTPUT=cpufetch else - # Assume x86_64 + arch := $(shell cc -dumpmachine) + arch := $(firstword $(subst -, ,$(arch))) + + ifeq ($(arch), $(filter $(arch), x86_64 amd64 i386 i486 i586 i686)) + SRC_DIR=src/x86/ + SOURCE += $(COMMON_SRC) $(SRC_DIR)cpuid.c $(SRC_DIR)apic.c $(SRC_DIR)cpuid_asm.c $(SRC_DIR)uarch.c + HEADERS += $(COMMON_HDR) $(SRC_DIR)cpuid.h $(SRC_DIR)apic.h $(SRC_DIR)cpuid_asm.h $(SRC_DIR)uarch.h + CFLAGS += -DARCH_X86 -std=c99 + else ifeq ($(arch), $(filter $(arch), arm aarch64_be aarch64 arm64 armv8b armv8l armv7l armv6l)) + SRC_DIR=src/arm/ + SOURCE += $(COMMON_SRC) $(SRC_DIR)midr.c $(SRC_DIR)uarch.c $(SRC_COMMON)soc.c $(SRC_DIR)soc.c $(SRC_COMMON)pci.c $(SRC_DIR)udev.c sve.o + HEADERS += $(COMMON_HDR) $(SRC_DIR)midr.h $(SRC_DIR)uarch.h $(SRC_COMMON)soc.h $(SRC_DIR)soc.h $(SRC_COMMON)pci.h $(SRC_DIR)udev.c $(SRC_DIR)socs.h + CFLAGS += -DARCH_ARM -std=c99 + else + # Error lines should not be tabulated because Makefile complains about it +$(warning Unsupported arch detected: $(arch). See https://github.com/Dr-Noob/cpufetch#1-support) +$(warning If your architecture is supported but the compilation fails, please open an issue in https://github.com/Dr-Noob/cpufetch/issues) +$(error Aborting compilation) + endif + GIT_VERSION := "" - SRC_DIR=src/x86/ - SOURCE += $(COMMON_SRC) $(SRC_DIR)cpuid.c $(SRC_DIR)apic.c $(SRC_DIR)cpuid_asm.c $(SRC_DIR)uarch.c - HEADERS += $(COMMON_HDR) $(SRC_DIR)cpuid.h $(SRC_DIR)apic.h $(SRC_DIR)cpuid_asm.h $(SRC_DIR)uarch.h - CFLAGS += -DARCH_X86 -std=c99 SANITY_FLAGS += -Wno-pedantic-ms-format OUTPUT=cpufetch.exe endif @@ -116,9 +135,9 @@ install: $(OUTPUT) install -Dm755 "cpufetch" "$(DESTDIR)$(PREFIX)/bin/cpufetch" install -Dm644 "LICENSE" "$(DESTDIR)$(PREFIX)/share/licenses/cpufetch-git/LICENSE" - install -Dm644 "cpufetch.1" "$(DESTDIR)$(PREFIX)/share/man/man1/cpufetch.1.gz" + install -Dm644 "cpufetch.1" "$(DESTDIR)$(PREFIX)/share/man/man1/cpufetch.1" uninstall: rm -f "$(DESTDIR)$(PREFIX)/bin/cpufetch" rm -f "$(DESTDIR)$(PREFIX)/share/licenses/cpufetch-git/LICENSE" - rm -f "$(DESTDIR)$(PREFIX)/share/man/man1/cpufetch.1.gz" + rm -f "$(DESTDIR)$(PREFIX)/share/man/man1/cpufetch.1" diff -Nru cpufetch-1.06/README.md cpufetch-1.07/README.md --- cpufetch-1.06/README.md 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/README.md 2025-10-31 06:50:04.000000000 +0000 @@ -63,7 +63,7 @@ | OS | x86_64 / x86 | ARM | RISC-V | PowerPC | |:-----------:|:------------------:|:------------------:|:------------------:|:------------------:| | GNU / Linux | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -| Windows | :heavy_check_mark: | :x: | :x: | :x: | +| Windows | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | | Android | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: | | macOS | :heavy_check_mark: | :heavy_check_mark: | :x: | :heavy_check_mark: | | FreeBSD | :heavy_check_mark: | :x: | :x: | :x: | diff -Nru cpufetch-1.06/debian/changelog cpufetch-1.07/debian/changelog --- cpufetch-1.06/debian/changelog 2025-03-11 08:53:01.000000000 +0000 +++ cpufetch-1.07/debian/changelog 2025-11-20 06:47:32.000000000 +0000 @@ -1,3 +1,15 @@ +cpufetch (1.07-1) unstable; urgency=medium + + * [Apple] Implement fallback frequency calculation + * [X86] Add old Intel and AMD CPUs + * [X86] Fix 2 bugs in --accurate-pp + * [ARM] Add (experimental) Windows on Arm support + * [ARM] Add support for a lot of new SoCs + * [ARM] Add automatic SoC inferring from device tree + * [RISCV] Significantly improved support for RISC-V, specially uarch detection and multi-letter extension support. + + -- Clay Stan Thu, 20 Nov 2025 14:47:32 +0800 + cpufetch (1.06-2) unstable; urgency=medium * Update build Architecture to any (Closes: #1031673). diff -Nru cpufetch-1.06/debian/copyright cpufetch-1.07/debian/copyright --- cpufetch-1.06/debian/copyright 2024-02-19 01:54:42.000000000 +0000 +++ cpufetch-1.07/debian/copyright 2025-11-20 06:47:32.000000000 +0000 @@ -4,12 +4,12 @@ Source: https://github.com/Dr-Noob/cpufetch Files: * -Copyright: 2017-2024 Dr-Noob +Copyright: 2017-2025 Dr-Noob License: GPL-2 Files: debian/* -Copyright: 2024 Clay Stan - 2024 Dr-Noob +Copyright: 2025 Clay Stan + 2025 Dr-Noob 2021 Helmut Grohne License: GPL-2 diff -Nru cpufetch-1.06/debian/cpufetch.1 cpufetch-1.07/debian/cpufetch.1 --- cpufetch-1.06/debian/cpufetch.1 2024-10-09 02:58:26.000000000 +0000 +++ cpufetch-1.07/debian/cpufetch.1 2025-11-20 06:47:32.000000000 +0000 @@ -1,6 +1,6 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.48.3. It was also manually adapted to look correctly .\" help2man -N -n "Simple yet fancy CPU architecture fetching tool" ./cpufetch > cpufetch.1 -.TH CPUFETCH "1" "February 2023" "cpufetch v1.06" "User Commands" +.TH CPUFETCH "1" "November 2025" "cpufetch v1.07" "User Commands" .SH NAME cpufetch \- Simple yet fancy CPU architecture fetching tool .SH SYNOPSIS diff -Nru cpufetch-1.06/debian/patches/0001_update_makefile.patch cpufetch-1.07/debian/patches/0001_update_makefile.patch --- cpufetch-1.06/debian/patches/0001_update_makefile.patch 2024-02-19 01:46:49.000000000 +0000 +++ cpufetch-1.07/debian/patches/0001_update_makefile.patch 2025-11-20 06:47:32.000000000 +0000 @@ -3,12 +3,13 @@ Author: Clay Stan Forwarded: not-needed -Last-Update: 2021-09-08 +Last-Update: 2025-11-20 Index: cpufetch/Makefile =================================================================== ---- cpufetch.orig/Makefile -+++ cpufetch/Makefile + +--- a/Makefile ++++ b/Makefile @@ -1,6 +1,6 @@ CC ?= gcc diff -Nru cpufetch-1.06/debian/patches/0002_use_cc_dumpmachine.patch cpufetch-1.07/debian/patches/0002_use_cc_dumpmachine.patch --- cpufetch-1.06/debian/patches/0002_use_cc_dumpmachine.patch 2024-10-09 02:50:19.000000000 +0000 +++ cpufetch-1.07/debian/patches/0002_use_cc_dumpmachine.patch 2025-11-20 06:47:32.000000000 +0000 @@ -3,8 +3,7 @@ Bug-Debian: https://bugs.debian.org/999880 Forwarded: not-needed Reviewed-By: Clay Stan -Last-Update: 2024-10-09 - +Last-Update: 2025-11-20 --- a/Makefile +++ b/Makefile @@ -26,8 +25,8 @@ SRC_DIR=src/x86/ SOURCE += $(COMMON_SRC) $(SRC_DIR)cpuid.c $(SRC_DIR)apic.c $(SRC_DIR)cpuid_asm.c $(SRC_DIR)uarch.c HEADERS += $(COMMON_HDR) $(SRC_DIR)cpuid.h $(SRC_DIR)apic.h $(SRC_DIR)cpuid_asm.h $(SRC_DIR)uarch.h $(SRC_DIR)freq/freq.h -@@ -31,12 +31,12 @@ - CFLAGS += -pthread +@@ -35,12 +35,12 @@ + HEADERS += $(SRC_COMMON)sysctl.h endif CFLAGS += -DARCH_X86 -std=c99 -fstack-protector-all - else ifeq ($(arch), $(filter $(arch), ppc64le ppc64 ppcle ppc)) @@ -41,7 +40,7 @@ SRC_DIR=src/arm/ SOURCE += $(COMMON_SRC) $(SRC_DIR)midr.c $(SRC_DIR)uarch.c $(SRC_COMMON)soc.c $(SRC_DIR)soc.c $(SRC_COMMON)pci.c $(SRC_DIR)udev.c sve.o HEADERS += $(COMMON_HDR) $(SRC_DIR)midr.h $(SRC_DIR)uarch.h $(SRC_COMMON)soc.h $(SRC_DIR)soc.h $(SRC_COMMON)pci.h $(SRC_DIR)udev.c $(SRC_DIR)socs.h -@@ -52,7 +52,7 @@ +@@ -56,7 +56,7 @@ SOURCE += $(SRC_COMMON)sysctl.c HEADERS += $(SRC_COMMON)sysctl.h endif diff -Nru cpufetch-1.06/src/arm/midr.c cpufetch-1.07/src/arm/midr.c --- cpufetch-1.06/src/arm/midr.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/arm/midr.c 2025-10-31 06:50:04.000000000 +0000 @@ -11,6 +11,10 @@ #include "../common/freq.h" #elif defined __APPLE__ || __MACH__ #include "../common/sysctl.h" +#elif defined _WIN32 + #define WIN32_LEAN_AND_MEAN + #define NOMINMAX + #include #endif #include "../common/global.h" @@ -21,6 +25,60 @@ #include "uarch.h" #include "sve.h" + +#if defined _WIN32 +// Windows stores processor information in registery at: +// "HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor" +// Within this directory, each core will get its own folder with +// registery entries named `CP ####` that map to ARM system registers. +// Ex. the MIDR register for core 0 is the `REG_QWORD` at: +// "HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor\0\CP 4000" +// The name of these `CP ####`-registers follow their register ID encoding in hexadecimal +// (op0&1):op1:crn:crm:op2. +// More registers can be found here: +// https://developer.arm.com/documentation/ddi0601/2024-06/AArch64-Registers +// Some important ones: +// CP 4000: MIDR_EL1 +// CP 4020: ID_AA64PFR0_EL1 +// CP 4021: ID_AA64PFR1_EL1 +// CP 4028: ID_AA64DFR0_EL1 +// CP 4029: ID_AA64DFR1_EL1 +// CP 402C: ID_AA64AFR0_EL1 +// CP 402D: ID_AA64AFR1_EL1 +// CP 4030: ID_AA64ISAR0_EL1 +// CP 4031: ID_AA64ISAR1_EL1 +// CP 4038: ID_AA64MMFR0_EL1 +// CP 4039: ID_AA64MMFR1_EL1 +// CP 403A: ID_AA64MMFR2_EL1 + +bool read_registry_hklm_int(char* path, char* name, void* value, bool is64) { + DWORD value_len; + int reg_type; + if (is64) { + value_len = sizeof(int64_t); + reg_type = RRF_RT_REG_QWORD; + } + else { + value_len = sizeof(int32_t); + reg_type = RRF_RT_REG_DWORD; + } + + if(RegGetValueA(HKEY_LOCAL_MACHINE, path, name, reg_type, NULL, value, &value_len) != ERROR_SUCCESS) { + printBug("Error reading registry entry \"%s\\%s\"", path, name); + return false; + } + return true; +} + +bool get_win32_core_info_int(uint32_t core_index, char* name, void* value, bool is64) { + // path + digits + uint32_t max_path_size = 45+3+1; + char* path = ecalloc(sizeof(char) * max_path_size, sizeof(char)); + snprintf(path, max_path_size, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%u", core_index); + return read_registry_hklm_int(path, name, value, is64); +} +#endif + bool cores_are_equal(int c1pos, int c2pos, uint32_t* midr_array, int32_t* freq_array) { return midr_array[c1pos] == midr_array[c2pos] && freq_array[c1pos] == freq_array[c2pos]; } @@ -176,7 +234,9 @@ printWarn("Unable to retrieve AT_HWCAP2 using getauxval"); } else { - feat->SVE2 = hwcaps & HWCAP2_SVE2; + #ifdef HWCAP2_SVE2 + feat->SVE2 = hwcaps & HWCAP2_SVE2; + #endif } } #else @@ -206,6 +266,46 @@ feat->NEON = true; feat->SVE = false; feat->SVE2 = false; +#elif defined _WIN32 + + // CP 4020 maps to the ID_AA64PFR0_EL1 register on Windows + // https://developer.arm.com/documentation/ddi0601/2024-06/AArch64-Registers/ID-AA64PFR0-EL1--AArch64-Processor-Feature-Register-0 + int64_t pfr0 = 0; + if(!get_win32_core_info_int(0, "CP 4020", &pfr0, true)) { + printWarn("Unable to retrieve PFR0 via registry"); + } + else { + // AdvSimd[23:20] + // -1: Not available + // 0: AdvSimd support + // 1: AdvSimd support + FP16 + int8_t adv_simd = ((int64_t)(pfr0 << (60 - 20)) >> 60); + feat->NEON = (adv_simd >= 0); + + // SVE[35:32] + feat->SVE = (pfr0 >> 32) & 0xF ? true : false; + } + + // Windoes does not expose a registry entry for the ID_AA64ZFR0_EL1 register + // this would have mapped to "CP 4024". + feat->SVE2 = false; + + // CP 4030 maps to the ID_AA64ISAR0_EL1 register on Windows + // https://developer.arm.com/documentation/ddi0601/2024-06/AArch64-Registers/ID-AA64ISAR0-EL1--AArch64-Instruction-Set-Attribute-Register-0 + int64_t isar0 = 0; + if(!get_win32_core_info_int(0, "CP 4030", &isar0, true)) { + printWarn("Unable to retrieve ISAR0 via registry"); + } + else { + // AES[7:4] + feat->AES = (isar0 >> 4) & 0xF ? true : false; + // SHA1[11:8] + feat->SHA1 = (isar0 >> 8) & 0xF ? true : false; + // SHA2[15:12] + feat->SHA2 = (isar0 >> 12) & 0xF ? true : false; + // CRC32[19:16] + feat->CRC32 = (isar0 >> 16) & 0xF ? true : false; + } #endif // ifdef __linux__ if (feat->SVE || feat->SVE2) { @@ -412,6 +512,7 @@ cpu->peak_performance = get_peak_performance(cpu); } else if(cpu_family == CPUFAMILY_ARM_EVEREST_SAWTOOTH || + cpu_family == CPUFAMILY_ARM_EVEREST_SAWTOOTH_2 || cpu_family == CPUFAMILY_ARM_EVEREST_SAWTOOTH_PRO || cpu_family == CPUFAMILY_ARM_EVEREST_SAWTOOTH_MAX) { fill_cpu_info_everest_sawtooth(cpu, pcores, ecores); @@ -425,6 +526,68 @@ return cpu; } +#elif defined _WIN32 +struct cpuInfo* get_cpu_info_windows(struct cpuInfo* cpu) { + init_cpu_info(cpu); + + SYSTEM_INFO sys_info; + GetSystemInfo(&sys_info); + int ncores = sys_info.dwNumberOfProcessors; + + uint32_t* midr_array = emalloc(sizeof(uint32_t) * ncores); + int32_t* freq_array = emalloc(sizeof(uint32_t) * ncores); + uint32_t* ids_array = emalloc(sizeof(uint32_t) * ncores); + for(int i=0; i < ncores; i++) { + // Cast from 64 to 32 bit to be able to re-use the pre-existing + // functions such as fill_ids_from_midr and cores_are_equal + int64_t midr_64; + if(!get_win32_core_info_int(i, "CP 4000", &midr_64, true)) { + return NULL; + } + midr_array[i] = midr_64; + if(!get_win32_core_info_int(i, "~MHz", &freq_array[i], false)) { + return NULL; + } + } + + uint32_t sockets = fill_ids_from_midr(midr_array, freq_array, ids_array, ncores); + + struct cpuInfo* ptr = cpu; + int midr_idx = 0; + int tmp_midr_idx = 0; + for(uint32_t i=0; i < sockets; i++) { + if(i > 0) { + ptr->next_cpu = emalloc(sizeof(struct cpuInfo)); + ptr = ptr->next_cpu; + init_cpu_info(ptr); + + tmp_midr_idx = midr_idx; + while(cores_are_equal(midr_idx, tmp_midr_idx, midr_array, freq_array)) tmp_midr_idx++; + midr_idx = tmp_midr_idx; + } + + ptr->midr = midr_array[midr_idx]; + ptr->arch = get_uarch_from_midr(ptr->midr, ptr); + + ptr->feat = get_features_info(); + + ptr->freq = emalloc(sizeof(struct frequency)); + ptr->freq->measured = false; + ptr->freq->base = freq_array[midr_idx]; + ptr->freq->max = UNKNOWN_DATA; + + ptr->cach = get_cache_info(ptr); + ptr->topo = get_topology_info(ptr, ptr->cach, midr_array, freq_array, i, ncores); + } + + cpu->num_cpus = sockets; + cpu->hv = emalloc(sizeof(struct hypervisor)); + cpu->hv->present = false; + cpu->soc = get_soc(cpu); + cpu->peak_performance = get_peak_performance(cpu); + + return cpu; +} #endif struct cpuInfo* get_cpu_info(void) { @@ -435,6 +598,8 @@ return get_cpu_info_linux(cpu); #elif defined __APPLE__ || __MACH__ return get_cpu_info_mach(cpu); + #elif defined _WIN32 + return get_cpu_info_windows(cpu); #endif } @@ -448,7 +613,7 @@ char* get_str_features(struct cpuInfo* cpu) { struct features* feat = cpu->feat; - uint32_t max_len = strlen("NEON,SHA1,SHA2,AES,CRC32,SVE,SVE2") + 1; + uint32_t max_len = strlen("NEON,SHA1,SHA2,AES,CRC32,SVE,SVE2,") + 1; uint32_t len = 0; char* string = ecalloc(max_len, sizeof(char)); diff -Nru cpufetch-1.06/src/arm/soc.c cpufetch-1.07/src/arm/soc.c --- cpufetch-1.06/src/arm/soc.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/arm/soc.c 2025-10-31 06:50:04.000000000 +0000 @@ -14,10 +14,37 @@ #include "../common/sysctl.h" #endif +#if defined(_WIN32) + #define WIN32_LEAN_AND_MEAN + #define NOMINMAX + #include + +// Gets a RRF_RT_REG_SZ-entry from the Windows registry, returning a newly allocated +// string and its length +bool read_registry_hklm_sz(char* path, char* value, char** string, LPDWORD length) { + // First call to RegGetValueA gets the length of the string and determines how much + // memory should be allocated for the new string + if(RegGetValueA(HKEY_LOCAL_MACHINE, path, value, RRF_RT_REG_SZ, NULL, NULL, length) != ERROR_SUCCESS) { + return false; + } + *string = ecalloc(*length, sizeof(char)); + // Second call actually writes the string data + if(RegGetValueA(HKEY_LOCAL_MACHINE, path, value, RRF_RT_REG_SZ, NULL, *string, length) != ERROR_SUCCESS) { + return false; + } + return true; +} +#endif + #define NA -1 #define min(a,b) (((a)<(b))?(a):(b)) #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#define PROP_MTK_PLATFORM "ro.mediatek.platform" +#define PROP_SOC_MODEL "ro.soc.model" +#define PROP_PRODUCT_BOARD "ro.product.board" +#define PROP_BOARD_PLATFORM "ro.board.platform" + static char* soc_rpi_string[] = { "BCM2835", "BCM2836", @@ -28,8 +55,7 @@ char* toupperstr(char* str) { int len = strlen(str) + 1; - char* ret = emalloc(sizeof(char) * len); - memset(ret, 0, sizeof(char) * len); + char* ret = ecalloc(len, sizeof(char)); for(int i=0; i < len; i++) { ret[i] = toupper((unsigned char) str[i]); @@ -102,7 +128,7 @@ int index = 0; while(socFromSid[index].sid != 0x0) { if(socFromSid[index].sid == sid) { - fill_soc(soc, socFromSid[index].soc.soc_name, socFromSid[index].soc.soc_model, socFromSid[index].soc.process); + fill_soc(soc, socFromSid[index].soc.name, socFromSid[index].soc.model, socFromSid[index].soc.process); return true; } index++; @@ -128,24 +154,24 @@ if((tmp = strstr(soc_name, "BCM")) == NULL) return false; - soc->soc_vendor = SOC_VENDOR_BROADCOM; + soc->vendor = SOC_VENDOR_BROADCOM; SOC_START - SOC_EQ(tmp, "BCM2835", "2835", SOC_BCM_2835, soc, 65) - SOC_EQ(tmp, "BCM2836", "2836", SOC_BCM_2836, soc, 40) - SOC_EQ(tmp, "BCM2837", "2837", SOC_BCM_2837, soc, 40) - SOC_EQ(tmp, "BCM2837B0", "2837B0", SOC_BCM_2837B0, soc, 40) - SOC_EQ(tmp, "BCM21553", "21553", SOC_BCM_21553, soc, 65) - SOC_EQ(tmp, "BCM21553-Thunderbird", "21553 Thunderbird", SOC_BCM_21553T, soc, 65) - SOC_EQ(tmp, "BCM21663", "21663", SOC_BCM_21663, soc, 40) - SOC_EQ(tmp, "BCM21664", "21664", SOC_BCM_21664, soc, 40) - SOC_EQ(tmp, "BCM28155", "28155", SOC_BCM_28155, soc, 40) - SOC_EQ(tmp, "BCM23550", "23550", SOC_BCM_23550, soc, 40) - SOC_EQ(tmp, "BCM28145", "28145", SOC_BCM_28145, soc, 40) - SOC_EQ(tmp, "BCM2157", "2157", SOC_BCM_2157, soc, 65) - SOC_EQ(tmp, "BCM21654", "21654", SOC_BCM_21654, soc, 40) - SOC_EQ(tmp, "BCM2711", "2711", SOC_BCM_2711, soc, 28) - SOC_EQ(tmp, "BCM2712", "2712", SOC_BCM_2712, soc, 16) + SOC_EQ(tmp, "BCM2835", "BCM2835", SOC_BCM_2835, soc, 65) + SOC_EQ(tmp, "BCM2836", "BCM2836", SOC_BCM_2836, soc, 40) + SOC_EQ(tmp, "BCM2837", "BCM2837", SOC_BCM_2837, soc, 40) + SOC_EQ(tmp, "BCM2837B0", "BCM2837B0", SOC_BCM_2837B0, soc, 40) + SOC_EQ(tmp, "BCM21553", "BCM21553", SOC_BCM_21553, soc, 65) + SOC_EQ(tmp, "BCM21553-Thunderbird", "BCM21553 Thunderbird", SOC_BCM_21553T, soc, 65) + SOC_EQ(tmp, "BCM21663", "BCM21663", SOC_BCM_21663, soc, 40) + SOC_EQ(tmp, "BCM21664", "BCM21664", SOC_BCM_21664, soc, 40) + SOC_EQ(tmp, "BCM28155", "BCM28155", SOC_BCM_28155, soc, 40) + SOC_EQ(tmp, "BCM23550", "BCM23550", SOC_BCM_23550, soc, 40) + SOC_EQ(tmp, "BCM28145", "BCM28145", SOC_BCM_28145, soc, 40) + SOC_EQ(tmp, "BCM2157", "BCM2157", SOC_BCM_2157, soc, 65) + SOC_EQ(tmp, "BCM21654", "BCM21654", SOC_BCM_21654, soc, 40) + SOC_EQ(tmp, "BCM2711", "BCM2711", SOC_BCM_2711, soc, 28) + SOC_EQ(tmp, "BCM2712", "BCM2712", SOC_BCM_2712, soc, 16) SOC_END } @@ -156,7 +182,7 @@ if((tmp = strstr(soc_name, "gs")) == NULL) return false; - soc->soc_vendor = SOC_VENDOR_GOOGLE; + soc->vendor = SOC_VENDOR_GOOGLE; SOC_START SOC_EQ(tmp, "gs101", "Tensor", SOC_GOOGLE_TENSOR, soc, 5) @@ -175,7 +201,7 @@ else if((tmp = strstr(soc_name, "kirin")) != NULL); else return false; - soc->soc_vendor = SOC_VENDOR_KIRIN; + soc->vendor = SOC_VENDOR_KIRIN; SOC_START SOC_EQ(tmp, "hi3620GFC", "K3V2", SOC_HISILICON_3620, soc, 40) @@ -217,7 +243,7 @@ else if((tmp = strstr(soc_name, "exynos")) != NULL); else return false; - soc->soc_vendor = SOC_VENDOR_EXYNOS; + soc->vendor = SOC_VENDOR_EXYNOS; // Because exynos are recently using "exynosXXXX" instead // of "universalXXXX" as codenames, SOC_EXY_EQ will check for @@ -277,10 +303,24 @@ if((tmp = strstr(soc_name_upper, "MT")) == NULL) return false; - soc->soc_vendor = SOC_VENDOR_MEDIATEK; + soc->vendor = SOC_VENDOR_MEDIATEK; SOC_START - // Dimensity // + // TODO + // Dimensity 6000 Series // + // Dimensity 7000 Series // + // Dimensity 8000 Series // + // END TODO + // Dimensity 9000 Series // + SOC_EQ(tmp, "MT6983Z", "Dimensity 9000", SOC_MTK_MT6983Z, soc, 4) + SOC_EQ(tmp, "MT8798Z/C","Dimensity 9000", SOC_MTK_MT8798ZC, soc, 4) + SOC_EQ(tmp, "MT6983W", "Dimensity 9000+", SOC_MTK_MT6983W, soc, 4) + SOC_EQ(tmp, "MT8798Z/T","Dimensity 9000+", SOC_MTK_MT8798ZT, soc, 4) + SOC_EQ(tmp, "MT6985W", "Dimensity 9200+", SOC_MTK_MT6985W, soc, 4) + SOC_EQ(tmp, "MT6985", "Dimensity 9200", SOC_MTK_MT6985, soc, 4) + SOC_EQ(tmp, "MT6989", "Dimensity 9300", SOC_MTK_MT6989, soc, 4) + SOC_EQ(tmp, "MT8796", "Dimensity 9300", SOC_MTK_MT8796, soc, 4) + // Dimensity 1000 // SOC_EQ(tmp, "MT6893Z", "Dimensity 1300", SOC_MTK_MT6893Z, soc, 6) SOC_EQ(tmp, "MT6893", "Dimensity 1200", SOC_MTK_MT6893, soc, 6) SOC_EQ(tmp, "MT6891", "Dimensity 1100", SOC_MTK_MT6891, soc, 6) @@ -290,12 +330,21 @@ SOC_EQ(tmp, "MT6885Z", "Dimensity 1000L", SOC_MTK_MT6885Z, soc, 7) SOC_EQ(tmp, "MT6889Z", "Dimensity 1000+", SOC_MTK_MT6889Z, soc, 7) SOC_EQ(tmp, "MT6883Z", "Dimensity 1000C", SOC_MTK_MT6883Z, soc, 7) - SOC_EQ(tmp, "MT6833", "Dimensity 700", SOC_MTK_MT6833, soc, 7) - SOC_EQ(tmp, "MT6853", "Dimensity 720", SOC_MTK_MT6853, soc, 7) + // Dimensity 900 + SOC_EQ(tmp, "MT6877V/Z","Dimensity 900", SOC_MTK_MT6877VZ, soc, 6) + SOC_EQ(tmp, "MT6877T" ,"Dimensity 920", SOC_MTK_MT6877T, soc, 6) + SOC_EQ(tmp, "MT6855" ,"Dimensity 930", SOC_MTK_MT6855, soc, 6) + // Dimensity 800 SOC_EQ(tmp, "MT6873", "Dimensity 800", SOC_MTK_MT6873, soc, 7) - SOC_EQ(tmp, "MT6853V", "Dimensity 800U", SOC_MTK_MT6853V, soc, 7) - SOC_EQ(tmp, "MT6833", "Dimensity 810", SOC_MTK_MT6833, soc, 6) + SOC_EQ(tmp, "MT6853V/T","Dimensity 800U", SOC_MTK_MT6853VT, soc, 7) + SOC_EQ(tmp, "MT6853T", "Dimensity 800U", SOC_MTK_MT6853T, soc, 7) + SOC_EQ(tmp, "MT6833P", "Dimensity 810", SOC_MTK_MT6833P, soc, 6) + SOC_EQ(tmp, "MT6833GP", "Dimensity 810", SOC_MTK_MT6833GP, soc, 6) + SOC_EQ(tmp, "MT6833V", "Dimensity 810", SOC_MTK_MT6833V, soc, 6) SOC_EQ(tmp, "MT6875", "Dimensity 820", SOC_MTK_MT6875, soc, 7) + // Dimensity 700 + SOC_EQ(tmp, "MT6833", "Dimensity 700", SOC_MTK_MT6833, soc, 7) + SOC_EQ(tmp, "MT6853V", "Dimensity 720", SOC_MTK_MT6853, soc, 7) // Helio // SOC_EQ(tmp, "MT6761D", "Helio A20", SOC_MTK_MT6761D, soc, 12) SOC_EQ(tmp, "MT6761", "Helio A22", SOC_MTK_MT6761, soc, 12) @@ -460,7 +509,7 @@ else if((tmp = strstr(soc_name_upper, "QSD")) != NULL); else return false; - soc->soc_vendor = SOC_VENDOR_SNAPDRAGON; + soc->vendor = SOC_VENDOR_SNAPDRAGON; SOC_START // Snapdragon S1 // @@ -613,7 +662,7 @@ if((tmp = strstr(soc_name, "sun")) == NULL) return false; - soc->soc_vendor = SOC_VENDOR_ALLWINNER; + soc->vendor = SOC_VENDOR_ALLWINNER; SOC_START // SoCs we can detect just with with the name @@ -737,7 +786,7 @@ soc->raw_name = emalloc(sizeof(char) * (soc_len + 1)); strncpy(soc->raw_name, soc_str, soc_len + 1); soc->raw_name[soc_len] = '\0'; - soc->soc_vendor = SOC_VENDOR_UNKNOWN; + soc->vendor = SOC_VENDOR_UNKNOWN; parse_soc_from_string(soc); } @@ -745,34 +794,34 @@ char tmp[100]; int property_len = 0; - property_len = android_property_get("ro.mediatek.platform", (char *) &tmp); + property_len = android_property_get(PROP_MTK_PLATFORM, (char *) &tmp); if(property_len > 0) { try_parse_soc_from_string(soc, property_len, tmp); - if(soc->soc_vendor == SOC_VENDOR_UNKNOWN) printWarn("SoC detection failed using Android property ro.mediatek.platform: %s", tmp); + if(soc->vendor == SOC_VENDOR_UNKNOWN) printWarn("SoC detection failed using Android property %s: %s", PROP_MTK_PLATFORM, tmp); else return soc; } // https://github.com/Dr-Noob/cpufetch/issues/253 // ro.soc.model might be more reliable than ro.product.board or // ro.board.platform, so try with it first - property_len = android_property_get("ro.soc.model", (char *) &tmp); + property_len = android_property_get(PROP_SOC_MODEL, (char *) &tmp); if(property_len > 0) { try_parse_soc_from_string(soc, property_len, tmp); - if(soc->soc_vendor == SOC_VENDOR_UNKNOWN) printWarn("SoC detection failed using Android property ro.soc.model: %s", tmp); + if(soc->vendor == SOC_VENDOR_UNKNOWN) printWarn("SoC detection failed using Android property %s: %s", PROP_SOC_MODEL, tmp); else return soc; } - property_len = android_property_get("ro.product.board", (char *) &tmp); + property_len = android_property_get(PROP_PRODUCT_BOARD, (char *) &tmp); if(property_len > 0) { try_parse_soc_from_string(soc, property_len, tmp); - if(soc->soc_vendor == SOC_VENDOR_UNKNOWN) printWarn("SoC detection failed using Android property ro.product.board: %s", tmp); + if(soc->vendor == SOC_VENDOR_UNKNOWN) printWarn("SoC detection failed using Android property %s: %s", PROP_PRODUCT_BOARD, tmp); else return soc; } - property_len = android_property_get("ro.board.platform", (char *) &tmp); + property_len = android_property_get(PROP_BOARD_PLATFORM, (char *) &tmp); if(property_len > 0) { try_parse_soc_from_string(soc, property_len, tmp); - if(soc->soc_vendor == SOC_VENDOR_UNKNOWN) printWarn("SoC detection failed using Android property ro.board.platform: %s", tmp); + if(soc->vendor == SOC_VENDOR_UNKNOWN) printWarn("SoC detection failed using Android property %s: %s", PROP_BOARD_PLATFORM, tmp); else return soc; } @@ -838,7 +887,7 @@ int index = 0; while(socFromRK[index].rk_soc != 0x0) { if(socFromRK[index].rk_soc == rk_soc) { - fill_soc(soc, socFromRK[index].soc.soc_name, socFromRK[index].soc.soc_model, socFromRK[index].soc.process); + fill_soc(soc, socFromRK[index].soc.name, socFromRK[index].soc.model, socFromRK[index].soc.process); return true; } index++; @@ -885,7 +934,7 @@ int index = 0; while(socFromUarch[index].u != UARCH_UNKNOWN) { if(socFromUarch[index].u == get_uarch(arch)) { - fill_soc(soc, socFromUarch[index].soc.soc_name, socFromUarch[index].soc.soc_model, socFromUarch[index].soc.process); + fill_soc(soc, socFromUarch[index].soc.name, socFromUarch[index].soc.model, socFromUarch[index].soc.process); return soc; } index++; @@ -895,6 +944,187 @@ return soc; } +// Return the dt string without the NULL characters. +char* get_dt_str(char* dt, int filelen) { + char* dt_without_null = (char *) malloc(sizeof(char) * filelen); + memcpy(dt_without_null, dt, filelen); + + for (int i=0; i < filelen-1; i++) { + if (dt_without_null[i] == '\0') + dt_without_null[i] = ','; + } + return dt_without_null; +} + +bool match_dt(struct system_on_chip* soc, char* dt, int filelen, char* expected_name, char* soc_name, SOC soc_model, int32_t process) { + // The /proc/device-tree/compatible file (passed by dt) uses NULL + // to separate the strings, so we need to make an special case here + // and iterate over the NULL characters, thus iterating over each + // individual compatible strings. + + if (strstr(dt, expected_name) != NULL) { + fill_soc(soc, soc_name, soc_model, process); + return true; + } + + char *compatible = dt; + char *end_of_dt = dt + filelen; + + while ((compatible = strchr(compatible, '\0')) != end_of_dt) { + compatible++; + if (strstr(compatible, expected_name) != NULL) { + fill_soc(soc, soc_name, soc_model, process); + return true; + } + } + + return false; +} + +#define DT_START if (false) {} +#define DT_EQ(dt, filelen, soc, expected_name, soc_name, soc_model, process) \ + else if (match_dt(soc, dt, filelen, expected_name, soc_name, soc_model, process)) return soc; +#define DT_END(dt, filelen) else { printWarn("guess_soc_from_devtree: No match found for '%s'", get_dt_str(dt, filelen)); return soc; } + +// TODO: Move this to doc +// The number of fields seems non-standard, so for now it seems wiser +// to just get the entire string with all fields and just look for the +// substring. +// TODO: Implement this by going trough NULL-separated fields rather than +// using strstr. +// https://trac.gateworks.com/wiki/linux/devicetree +struct system_on_chip* guess_soc_from_devtree(struct system_on_chip* soc) { + int len; + char* dt = get_devtree_compatible(&len); + if (dt == NULL) { + return soc; + } + + DT_START + // The following are internal codenames of Asahi Linux + // https://github.com/AsahiLinux/docs/wiki/Codenames + // https://github.com/Dr-Noob/cpufetch/issues/263 + DT_EQ(dt, len, soc, "apple,t8103", "M1", SOC_APPLE_M1, 5) + DT_EQ(dt, len, soc, "apple,t6000", "M1 Pro", SOC_APPLE_M1_PRO, 5) + DT_EQ(dt, len, soc, "apple,t6001", "M1 Max", SOC_APPLE_M1_MAX, 5) + DT_EQ(dt, len, soc, "apple,t6002", "M1 Ultra", SOC_APPLE_M1_ULTRA, 5) + DT_EQ(dt, len, soc, "apple,t8112", "M2", SOC_APPLE_M2, 5) + DT_EQ(dt, len, soc, "apple,t6020", "M2 Pro", SOC_APPLE_M2_PRO, 5) + DT_EQ(dt, len, soc, "apple,t6021", "M2 Max", SOC_APPLE_M2_MAX, 5) + DT_EQ(dt, len, soc, "apple,t6022", "M2 Ultra", SOC_APPLE_M2_ULTRA, 5) + DT_EQ(dt, len, soc, "apple,t8122", "M3", SOC_APPLE_M3, 3) + DT_EQ(dt, len, soc, "apple,t6030", "M3 Pro", SOC_APPLE_M3_PRO, 3) + DT_EQ(dt, len, soc, "apple,t6031", "M3 Max", SOC_APPLE_M3_MAX, 3) + DT_EQ(dt, len, soc, "apple,t6034", "M3 Max", SOC_APPLE_M3_MAX, 3) + // NVIDIA + // https://elixir.bootlin.com/linux/v6.10.6/source/arch/arm64/boot/dts/nvidia + // https://elixir.bootlin.com/linux/v6.10.6/source/arch/arm/boot/dts/nvidia + DT_EQ(dt, len, soc, "nvidia,tegra20", "Tegra 2", SOC_TEGRA_2, 40) // https://en.wikipedia.org/wiki/Tegra#Tegra_2 + DT_EQ(dt, len, soc, "nvidia,tegra30", "Tegra 3", SOC_TEGRA_3, 40) // https://en.wikipedia.org/wiki/Tegra#Tegra_3 + DT_EQ(dt, len, soc, "nvidia,tegra114", "Tegra 4", SOC_TEGRA_4, 28) // https://en.wikipedia.org/wiki/Tegra#Tegra_4 + DT_EQ(dt, len, soc, "nvidia,tegra124", "Tegra K1", SOC_TEGRA_K1, 28) // https://en.wikipedia.org/wiki/Tegra#Tegra_K1 + DT_EQ(dt, len, soc, "nvidia,tegra132", "Tegra K1", SOC_TEGRA_K1, 28) // https://en.wikipedia.org/wiki/Tegra#Tegra_K1 + DT_EQ(dt, len, soc, "nvidia,tegra210", "Tegra X1", SOC_TEGRA_X1, 20) // https://en.wikipedia.org/wiki/Tegra#Tegra_X1 + DT_EQ(dt, len, soc, "nvidia,tegra186", "Tegra X2", SOC_TEGRA_X2, 16) // https://en.wikipedia.org/wiki/Tegra#Tegra_X2 + DT_EQ(dt, len, soc, "nvidia,tegra194", "Tegra Xavier", SOC_TEGRA_XAVIER, 12) // https://en.wikipedia.org/wiki/Tegra#Xavier + DT_EQ(dt, len, soc, "nvidia,tegra234", "Tegra Orin", SOC_TEGRA_ORIN, 8) // https://www.phoronix.com/news/NVIDIA-Orin-Tegra234-Audio, https://github.com/Dr-Noob/cpufetch/issues/275, https://en.wikipedia.org/wiki/Tegra#Orin + // Qualcomm now also in devtree... + // TODO: Integrate this with SOC_EQ + DT_EQ(dt, len, soc, "qcom,sc8280", "8cx Gen 3", SOC_SNAPD_SC8280XP, 5) + // grep -oR -h --color -E '"fsl,.*' *.dtsi | sort | uniq | cut -d ',' -f1-2 | grep -v '-' + // https://elixir.bootlin.com/linux/v6.10.6/source/arch/arm64/boot/dts/freescale + DT_EQ(dt, len, soc, "fsl,imx8qm", "i.MX 8QuadMax", SOC_NXP_IMX8QM, 28) // https://www.nxp.com/docs/en/fact-sheet/IMX8FAMFS.pdf + DT_EQ(dt, len, soc, "fsl,imx8qp", "i.MX 8QuadPlus", SOC_NXP_IMX8QP, 28) // Actually not in dtsi, compatible string is just a guess + DT_EQ(dt, len, soc, "fsl,imx8mp", "i.MX 8M Plus", SOC_NXP_IMX8MP, 14) // https://www.nxp.com/docs/en/fact-sheet/IMX8MPLUSFS.pdf https://github.com/Dr-Noob/cpufetch/issues/261 + DT_EQ(dt, len, soc, "fsl,imx8mn", "i.MX 8M Nano", SOC_NXP_IMX8MN, NA) + DT_EQ(dt, len, soc, "fsl,imx8mm", "i.MX 8M Mini", SOC_NXP_IMX8MM, NA) // https://www.nxp.com/docs/en/fact-sheet/IMX8MMINIFS.pdf + DT_EQ(dt, len, soc, "fsl,imx8dxp", "i.MX 8DualXPlus", SOC_NXP_IMX8DXP, NA) + DT_EQ(dt, len, soc, "fsl,imx8qxp", "i.MX 8QuadXPlus", SOC_NXP_IMX8QXP, NA) + DT_EQ(dt, len, soc, "fsl,imx93", "i.MX 93", SOC_NXP_IMX93, NA) + // [1] https://elixir.bootlin.com/linux/v6.10.6/source/arch/arm64/boot/dts/amlogic + // [2] https://github.com/Dr-Noob/cpufetch/issues/268 + // [3] https://www.amlogic.com/#Products/393/index.html + // [4] https://wikimovel.com + // [5] https://wiki.postmarketos.org/wiki/Amlogic_S905W/S905D/S905X/S905L/S805X/S805Y/S905Z + DT_EQ(dt, len, soc, "amlogic,a311d", "A311D", SOC_AMLOGIC_A311D, 12) // [1,2,3,4] + DT_EQ(dt, len, soc, "amlogic,a311d2", "A311D2", SOC_AMLOGIC_A311D2, 12) // [1,4] + DT_EQ(dt, len, soc, "amlogic,s905w", "S905W", SOC_AMLOGIC_S905W, 28) // [1,5] + DT_EQ(dt, len, soc, "amlogic,s905d", "S905D", SOC_AMLOGIC_S905D, 28) // [1,5] + DT_EQ(dt, len, soc, "amlogic,s905x", "S905X", SOC_AMLOGIC_S905X, 28) // [1,4,5] + DT_EQ(dt, len, soc, "amlogic,s805x", "S805X", SOC_AMLOGIC_S805X, 28) // [1,5] + // Marvell + // https://elixir.bootlin.com/linux/v6.10.6/source/arch/arm64/boot/dts/marvell + DT_EQ(dt, len, soc, "marvell,armada3700", "Armada 3700", SOC_MARVELL_A3700, 28) // http://wiki.espressobin.net/tiki-index.php?page=Armada+3700 (pdf), https://github.com/Dr-Noob/cpufetch/issues/279 + DT_EQ(dt, len, soc, "marvell,armada3710", "Armada 3710", SOC_MARVELL_A3710, 28) // https://gzhls.at/blob/ldb/2/7/4/2/6eacf9661c5a2d20c4d7cd3328ffba47bfd6.pdf + DT_EQ(dt, len, soc, "marvell,armada3720", "Armada 3720", SOC_MARVELL_A3720, 28) // https://gzhls.at/blob/ldb/2/7/4/2/6eacf9661c5a2d20c4d7cd3328ffba47bfd6.pdf + DT_EQ(dt, len, soc, "marvell,armada7200", "Armada 7200", SOC_MARVELL_A7200, 28) // Assuming same manufacturing process as 7400 + DT_EQ(dt, len, soc, "marvell,armada7400", "Armada 7400", SOC_MARVELL_A7400, 28) // https://www.marvell.com/content/dam/marvell/en/public-collateral/embedded-processors/marvell-embedded-processors-armada-7040-product-brief-2017-12.pdf + DT_EQ(dt, len, soc, "marvell,armada8020", "Armada 8020", SOC_MARVELL_A8020, 28) // https://datasheet.datasheetarchive.com/originals/crawler/marvell.com/da7b6a997e49e9e93fa4b1f4cfbed71b.pdf + DT_EQ(dt, len, soc, "marvell,armada8040", "Armada 8040", SOC_MARVELL_A8040, 28) // https://www.verical.com/datasheet/marvell-technology-group-application-processors-and-soc-88f8040-a2-bvp4i160-6331367.pdf + DT_EQ(dt, len, soc, "marvell,cn9130", "CN9130", SOC_MARVELL_CN9130, NA) // https://www.marvell.com/content/dam/marvell/en/public-collateral/embedded-processors/marvell-infrastructure-processors-octeon-tx2-cn913x-product-brief.pdf + DT_EQ(dt, len, soc, "marvell,cn9131", "CN9131", SOC_MARVELL_CN9131, NA) // https://www.marvell.com/content/dam/marvell/en/public-collateral/embedded-processors/marvell-infrastructure-processors-octeon-tx2-cn913x-product-brief.pdf + DT_EQ(dt, len, soc, "marvell,cn9132", "CN9132", SOC_MARVELL_CN9132, NA) // https://www.marvell.com/content/dam/marvell/en/public-collateral/embedded-processors/marvell-infrastructure-processors-octeon-tx2-cn913x-product-brief.pdf + DT_END(dt, len) +} + +// This function is different from the rest guess_soc_from_xxx, which try infering +// the exact SoC model by matching some string against a list of known values. +// On the other hand, this function will just try to infer the SoC vendor first by +// matching the device tree vendor name (i.e., the first value, before the comma). +// If that is successfull, then it also fills in the SoC name using the string from +// the device tree. +// The critical difference is that this function does not need a LUT to fill in the +// SoC, it just needs to find a known vendor. On the other hand, the detection is +// less powerful since we cannot get the manufacturing process, and the SoC name will +// come directly from the device tree, meaning that it will likely be less precise. +struct system_on_chip* guess_raw_soc_from_devtree(struct system_on_chip* soc) { + int num_vendors; + struct devtree** dt_vendors = get_devtree_compatible_struct(&num_vendors); + if (dt_vendors == NULL) { + return soc; + } + + typedef struct { + char* compatible; + VENDOR soc_vendor; + } devtreeToVendor; + + // https://elixir.bootlin.com/linux/v6.10.6/source/arch/arm64/boot/dts + // grep -oR --color -E 'compatible = ".*"' | cut -d '=' -f2 | cut -d ',' -f1 | tr -d '"' | sort | uniq -c | sort + // - The following vendors are not included because they dont seem to be present in dts: + // SOC_VENDOR_(KIRIN, KUNPENG, GOOGLE, AMPERE). + // - The commented vendors are not included intentionally, because I prefer updating its LUT manually. + devtreeToVendor socFromDevtree[] = { + // {"qcom", SOC_VENDOR_SNAPDRAGON}, + // {"samsung", SOC_VENDOR_EXYNOS}, + // {"brcm", SOC_VENDOR_BROADCOM}, + // {"apple", SOC_VENDOR_APPLE}, + // {"rockchip", SOC_VENDOR_ROCKCHIP}, + // {"nvidia", SOC_VENDOR_NVIDIA}, + {"mediatek", SOC_VENDOR_MEDIATEK}, + {"fsl", SOC_VENDOR_NXP }, + {"nxp", SOC_VENDOR_NXP }, + {"amlogic", SOC_VENDOR_AMLOGIC }, + {"marvell", SOC_VENDOR_MARVELL }, + {NULL, SOC_VENDOR_UNKNOWN } + }; + + int index = 0; + while (socFromDevtree[index].compatible != 0x0) { + for (int i=0; i < num_vendors; i++) { + if (strcmp(socFromDevtree[index].compatible, dt_vendors[i]->vendor) == 0) { + fill_soc_raw(soc, dt_vendors[i]->model, socFromDevtree[index].soc_vendor); + printWarn("Your SoC is unsupported by cpufetch but could still be detected successfully. If you want to help improve the project, please paste the output of 'cpufetch --verbose' on https://github.com/Dr-Noob/cpufetch/issues"); + return soc; + } + } + index++; + } + + printWarn("guess_raw_soc_from_devtree: No device matched the list"); + return soc; +} + struct system_on_chip* guess_soc_from_pci(struct system_on_chip* soc, struct cpuInfo* cpu) { struct pci_devices * pci = get_pci_devices(); if (pci == NULL) { @@ -909,9 +1139,10 @@ } pciToSoC; pciToSoC socFromPCI[] = { - {PCI_VENDOR_NVIDIA, PCI_DEVICE_TEGRA_X1, {SOC_TEGRA_X1, SOC_VENDOR_NVIDIA, 20, "Tegra X1", NULL} }, - // {PCI_VENDOR_NVIDIA, PCI_DEVICE_GH_200,{SOC_GH_200, SOC_VENDOR_NVIDIA, ?, "Grace Hopper", NULL} }, - {0x0000, 0x0000, {UNKNOWN, SOC_VENDOR_UNKNOWN, -1, "", NULL} } + {PCI_VENDOR_NVIDIA, PCI_DEVICE_TEGRA_X1, {SOC_TEGRA_X1, SOC_VENDOR_NVIDIA, 20, "Tegra X1", NULL} }, + // {PCI_VENDOR_NVIDIA, PCI_DEVICE_GH_200,{SOC_GH_200, SOC_VENDOR_NVIDIA, ?, "Grace Hopper", NULL} }, + {PCI_VENDOR_AMPERE, PCI_DEVICE_ALTRA, {SOC_AMPERE_ALTRA, SOC_VENDOR_AMPERE, 7, "Altra", NULL} }, // https://www.anandtech.com/show/15575/amperes-altra-80-core-n1-soc-for-hyperscalers-against-rome-and-xeon + {0x0000, 0x0000, {UNKNOWN, SOC_VENDOR_UNKNOWN, -1, "", NULL} } }; int index = 0; @@ -921,7 +1152,7 @@ if (socFromPCI[index].vendor_id == dev->vendor_id && socFromPCI[index].device_id == dev->device_id) { - fill_soc(soc, socFromPCI[index].soc.soc_name, socFromPCI[index].soc.soc_model, socFromPCI[index].soc.process); + fill_soc(soc, socFromPCI[index].soc.name, socFromPCI[index].soc.model, socFromPCI[index].soc.process); return soc; } } @@ -1005,12 +1236,12 @@ } else { printBug("Found invalid physical cpu number: %d", physicalcpu); - soc->soc_vendor = SOC_VENDOR_UNKNOWN; + soc->vendor = SOC_VENDOR_UNKNOWN; } } else { printBugCheckRelease("Found invalid cpu_subfamily: 0x%.8X", cpu_subfamily); - soc->soc_vendor = SOC_VENDOR_UNKNOWN; + soc->vendor = SOC_VENDOR_UNKNOWN; } } else if(cpu_family == CPUFAMILY_ARM_AVALANCHE_BLIZZARD) { @@ -1032,19 +1263,21 @@ } else { printBug("Found invalid physical cpu number: %d", physicalcpu); - soc->soc_vendor = SOC_VENDOR_UNKNOWN; + soc->vendor = SOC_VENDOR_UNKNOWN; } } else { printBugCheckRelease("Found invalid cpu_subfamily: 0x%.8X", cpu_subfamily); - soc->soc_vendor = SOC_VENDOR_UNKNOWN; + soc->vendor = SOC_VENDOR_UNKNOWN; } } else if(cpu_family == CPUFAMILY_ARM_EVEREST_SAWTOOTH || + cpu_family == CPUFAMILY_ARM_EVEREST_SAWTOOTH_2 || cpu_family == CPUFAMILY_ARM_EVEREST_SAWTOOTH_PRO || cpu_family == CPUFAMILY_ARM_EVEREST_SAWTOOTH_MAX) { // Check M3 version - if(cpu_family == CPUFAMILY_ARM_EVEREST_SAWTOOTH) { + if(cpu_family == CPUFAMILY_ARM_EVEREST_SAWTOOTH || + cpu_family == CPUFAMILY_ARM_EVEREST_SAWTOOTH_2) { fill_soc(soc, "M3", SOC_APPLE_M3, 3); } else if(cpu_family == CPUFAMILY_ARM_EVEREST_SAWTOOTH_PRO) { @@ -1055,12 +1288,12 @@ } else { printBugCheckRelease("Found invalid cpu_family: 0x%.8X", cpu_family); - soc->soc_vendor = SOC_VENDOR_UNKNOWN; + soc->vendor = SOC_VENDOR_UNKNOWN; } } else { printBugCheckRelease("Found invalid cpu_family: 0x%.8X", cpu_family); - soc->soc_vendor = SOC_VENDOR_UNKNOWN; + soc->vendor = SOC_VENDOR_UNKNOWN; } return soc; } @@ -1069,15 +1302,15 @@ struct system_on_chip* get_soc(struct cpuInfo* cpu) { struct system_on_chip* soc = emalloc(sizeof(struct system_on_chip)); soc->raw_name = NULL; - soc->soc_vendor = SOC_VENDOR_UNKNOWN; - soc->soc_model = SOC_MODEL_UNKNOWN; + soc->vendor = SOC_VENDOR_UNKNOWN; + soc->model = SOC_MODEL_UNKNOWN; soc->process = UNKNOWN; #ifdef __linux__ bool isRPi = is_raspberry_pi(); if(isRPi) { soc = guess_soc_raspbery_pi(soc); - if(soc->soc_vendor == SOC_VENDOR_UNKNOWN) { + if(soc->vendor == SOC_VENDOR_UNKNOWN) { printErr("[RPi] SoC detection failed using revision code, falling back to cpuinfo detection"); } else { @@ -1086,7 +1319,7 @@ } soc = guess_soc_from_cpuinfo(soc); - if(soc->soc_vendor == SOC_VENDOR_UNKNOWN) { + if(soc->vendor == SOC_VENDOR_UNKNOWN) { if(soc->raw_name != NULL) { printWarn("SoC detection failed using /proc/cpuinfo: Found '%s' string", soc->raw_name); } @@ -1098,39 +1331,64 @@ if(soc->raw_name == NULL) { printWarn("SoC detection failed using Android: No string found"); } - else if(soc->soc_vendor == SOC_VENDOR_UNKNOWN) { + else if(soc->vendor == SOC_VENDOR_UNKNOWN) { printWarn("SoC detection failed using Android: Found '%s' string", soc->raw_name); } #endif // ifdef __ANDROID__ + // If previous steps failed, try with the device tree + if (soc->vendor == SOC_VENDOR_UNKNOWN) { + soc = guess_soc_from_devtree(soc); + } // If previous steps failed, try with nvmem - if(soc->soc_vendor == SOC_VENDOR_UNKNOWN) { + if(soc->vendor == SOC_VENDOR_UNKNOWN) { soc = guess_soc_from_nvmem(soc); } // If previous steps failed, try infering it from the microarchitecture - if(soc->soc_vendor == SOC_VENDOR_UNKNOWN) { + if(soc->vendor == SOC_VENDOR_UNKNOWN) { soc = guess_soc_from_uarch(soc, cpu); } // If previous steps failed, try infering it from the pci device id - if(soc->soc_vendor == SOC_VENDOR_UNKNOWN) { + if(soc->vendor == SOC_VENDOR_UNKNOWN) { soc = guess_soc_from_pci(soc, cpu); } + if (soc->vendor == SOC_VENDOR_UNKNOWN) { + // If we fall here it means all previous functions failed to detect the SoC. + // In such case, try with our last resort. If it also fails, we will just give up + soc = guess_raw_soc_from_devtree(soc); + } } #elif defined __APPLE__ || __MACH__ soc = guess_soc_apple(soc); - if(soc->soc_vendor == SOC_VENDOR_UNKNOWN) { + if(soc->vendor == SOC_VENDOR_UNKNOWN) { printWarn("SoC detection failed using cpu_subfamily"); } else { return soc; } -#endif // ifdef __linux__ +#endif + +#if defined _WIN32 + // Use the first core to determine the SoC + char* processor_name_string = NULL; + unsigned long processor_name_string_len = 0; + if(!read_registry_hklm_sz("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", "ProcessorNameString", &processor_name_string, &processor_name_string_len)) { + printWarn("Failed to aquire SoC name from registery"); + return soc; + } - if(soc->soc_model == SOC_MODEL_UNKNOWN) { - // raw_name might not be NULL, but if we were unable to find - // the exact SoC, just print "Unkwnown" + soc->name = processor_name_string; + soc->raw_name = processor_name_string; + soc->vendor = try_match_soc_vendor_name(processor_name_string); + soc->model = SOC_MODEL_UNKNOWN; + soc->process = UNKNOWN; +#else + if(soc->raw_name == NULL) { + // We were unable to find the SoC, so just initialize raw_name + // with the unknown string soc->raw_name = emalloc(sizeof(char) * (strlen(STRING_UNKNOWN)+1)); snprintf(soc->raw_name, strlen(STRING_UNKNOWN)+1, STRING_UNKNOWN); } +#endif return soc; } diff -Nru cpufetch-1.06/src/arm/socs.h cpufetch-1.07/src/arm/socs.h --- cpufetch-1.06/src/arm/socs.h 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/arm/socs.h 2025-10-31 06:50:04.000000000 +0000 @@ -192,6 +192,22 @@ SOC_MTK_MT9950, SOC_MTK_MT9972, SOC_MTK_MT9982, + SOC_MTK_MT6983Z, + SOC_MTK_MT8798ZC, + SOC_MTK_MT6983W, + SOC_MTK_MT8798ZT, + SOC_MTK_MT6985W, + SOC_MTK_MT6985, + SOC_MTK_MT6989, + SOC_MTK_MT8796, + SOC_MTK_MT6877VZ, + SOC_MTK_MT6877T, + SOC_MTK_MT6855, + SOC_MTK_MT6853VT, + SOC_MTK_MT6853T, + SOC_MTK_MT6833P, + SOC_MTK_MT6833GP, + SOC_MTK_MT6833V, // Snapdragon // SOC_SNAPD_QSD8650, SOC_SNAPD_QSD8250, @@ -318,6 +334,7 @@ SOC_SNAPD_SM8550_AB, SOC_SNAPD_SM8635, SOC_SNAPD_SM8650_AB, + SOC_SNAPD_SC8280XP, // APPLE SOC_APPLE_M1, SOC_APPLE_M1_PRO, @@ -379,7 +396,44 @@ SOC_GOOGLE_TENSOR_G2, SOC_GOOGLE_TENSOR_G3, // NVIDIA, + SOC_TEGRA_2, + SOC_TEGRA_3, + SOC_TEGRA_4, + SOC_TEGRA_K1, + SOC_TEGRA_K2, SOC_TEGRA_X1, + SOC_TEGRA_X2, + SOC_TEGRA_XAVIER, + SOC_TEGRA_ORIN, + // ALTRA + SOC_AMPERE_ALTRA, + // NXP + SOC_NXP_IMX8QM, + SOC_NXP_IMX8QP, + SOC_NXP_IMX8MP, + SOC_NXP_IMX8MN, + SOC_NXP_IMX8MM, + SOC_NXP_IMX8DXP, + SOC_NXP_IMX8QXP, + SOC_NXP_IMX93, + // AMLOGIC + SOC_AMLOGIC_A311D, + SOC_AMLOGIC_A311D2, + SOC_AMLOGIC_S905W, + SOC_AMLOGIC_S905D, + SOC_AMLOGIC_S905X, + SOC_AMLOGIC_S805X, + // MARVELL + SOC_MARVELL_A3700, + SOC_MARVELL_A3710, + SOC_MARVELL_A3720, + SOC_MARVELL_A7200, + SOC_MARVELL_A7400, + SOC_MARVELL_A8020, + SOC_MARVELL_A8040, + SOC_MARVELL_CN9130, + SOC_MARVELL_CN9131, + SOC_MARVELL_CN9132, // UNKNOWN SOC_MODEL_UNKNOWN }; @@ -389,13 +443,17 @@ else if(soc >= SOC_HISILICON_3620 && soc <= SOC_HISILICON_9000S) return SOC_VENDOR_KIRIN; else if(soc >= SOC_KUNPENG_920 && soc <= SOC_KUNPENG_930) return SOC_VENDOR_KUNPENG; else if(soc >= SOC_EXYNOS_3475 && soc <= SOC_EXYNOS_880) return SOC_VENDOR_EXYNOS; - else if(soc >= SOC_MTK_MT6893 && soc <= SOC_MTK_MT8783) return SOC_VENDOR_MEDIATEK; - else if(soc >= SOC_SNAPD_QSD8650 && soc <= SOC_SNAPD_SM8650_AB) return SOC_VENDOR_SNAPDRAGON; + else if(soc >= SOC_MTK_MT5327 && soc <= SOC_MTK_MT6833V) return SOC_VENDOR_MEDIATEK; + else if(soc >= SOC_SNAPD_QSD8650 && soc <= SOC_SNAPD_SC8280XP) return SOC_VENDOR_SNAPDRAGON; else if(soc >= SOC_APPLE_M1 && soc <= SOC_APPLE_M3_MAX) return SOC_VENDOR_APPLE; else if(soc >= SOC_ALLWINNER_A10 && soc <= SOC_ALLWINNER_R328) return SOC_VENDOR_ALLWINNER; else if(soc >= SOC_ROCKCHIP_3288 && soc <= SOC_ROCKCHIP_3588) return SOC_VENDOR_ROCKCHIP; else if(soc >= SOC_GOOGLE_TENSOR && soc <= SOC_GOOGLE_TENSOR_G3) return SOC_VENDOR_GOOGLE; - else if(soc >= SOC_TEGRA_X1 && soc <= SOC_TEGRA_X1) return SOC_VENDOR_NVIDIA; + else if(soc >= SOC_TEGRA_2 && soc <= SOC_TEGRA_ORIN) return SOC_VENDOR_NVIDIA; + else if(soc >= SOC_AMPERE_ALTRA && soc <= SOC_AMPERE_ALTRA) return SOC_VENDOR_AMPERE; + else if(soc >= SOC_NXP_IMX8QM && soc <= SOC_NXP_IMX93) return SOC_VENDOR_NXP; + else if(soc >= SOC_AMLOGIC_A311D && soc <= SOC_AMLOGIC_S805X) return SOC_VENDOR_AMLOGIC; + else if(soc >= SOC_MARVELL_A3700 && soc <= SOC_MARVELL_CN9132) return SOC_VENDOR_MARVELL; return SOC_VENDOR_UNKNOWN; } diff -Nru cpufetch-1.06/src/arm/uarch.c cpufetch-1.07/src/arm/uarch.c --- cpufetch-1.06/src/arm/uarch.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/arm/uarch.c 2025-10-31 06:50:04.000000000 +0000 @@ -33,7 +33,9 @@ ISA_ARMv8_3_A, ISA_ARMv8_4_A, ISA_ARMv8_5_A, - ISA_ARMv9_A + ISA_ARMv8_6_A, + ISA_ARMv9_A, + ISA_ARMv9_2_A }; static const ISA isas_uarch[] = { @@ -61,15 +63,26 @@ [UARCH_CORTEX_A76] = ISA_ARMv8_2_A, [UARCH_CORTEX_A77] = ISA_ARMv8_2_A, [UARCH_CORTEX_A78] = ISA_ARMv8_2_A, + [UARCH_CORTEX_A78C] = ISA_ARMv8_2_A, + [UARCH_CORTEX_A78AE] = ISA_ARMv8_2_A, [UARCH_CORTEX_A510] = ISA_ARMv9_A, + [UARCH_CORTEX_A520] = ISA_ARMv9_2_A, [UARCH_CORTEX_A710] = ISA_ARMv9_A, [UARCH_CORTEX_A715] = ISA_ARMv9_A, + [UARCH_CORTEX_A720] = ISA_ARMv9_2_A, + [UARCH_CORTEX_A725] = ISA_ARMv9_2_A, [UARCH_CORTEX_X1] = ISA_ARMv8_2_A, + [UARCH_CORTEX_X1C] = ISA_ARMv8_2_A, // Assuming same as X1 [UARCH_CORTEX_X2] = ISA_ARMv9_A, [UARCH_CORTEX_X3] = ISA_ARMv9_A, + [UARCH_CORTEX_X4] = ISA_ARMv9_2_A, + [UARCH_CORTEX_X925] = ISA_ARMv9_2_A, [UARCH_NEOVERSE_N1] = ISA_ARMv8_2_A, + [UARCH_NEOVERSE_N2] = ISA_ARMv9_A, [UARCH_NEOVERSE_E1] = ISA_ARMv8_2_A, [UARCH_NEOVERSE_V1] = ISA_ARMv8_4_A, + [UARCH_NEOVERSE_V2] = ISA_ARMv9_A, + [UARCH_NEOVERSE_V3] = ISA_ARMv9_2_A, [UARCH_BRAHMA_B15] = ISA_ARMv7_A, // Same as Cortex-A15 [UARCH_BRAHMA_B53] = ISA_ARMv8_A, // Same as Cortex-A53 [UARCH_THUNDERX] = ISA_ARMv8_A, @@ -93,8 +106,10 @@ [UARCH_EXYNOS_M5] = ISA_ARMv8_2_A, [UARCH_ICESTORM] = ISA_ARMv8_5_A, // https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/Support/AArch64TargetParser.def [UARCH_FIRESTORM] = ISA_ARMv8_5_A, - [UARCH_BLIZZARD] = ISA_ARMv8_5_A, // Not confirmed - [UARCH_AVALANCHE] = ISA_ARMv8_5_A, + [UARCH_BLIZZARD] = ISA_ARMv8_6_A, // https://github.com/llvm/llvm-project/blob/main/llvm/unittests/TargetParser/TargetParserTest.cpp + [UARCH_AVALANCHE] = ISA_ARMv8_6_A, // https://github.com/llvm/llvm-project/blob/main/llvm/unittests/TargetParser/TargetParserTest.cpp + [UARCH_SAWTOOTH] = ISA_ARMv8_6_A, // https://github.com/llvm/llvm-project/blob/main/llvm/unittests/TargetParser/TargetParserTest.cpp + [UARCH_EVEREST] = ISA_ARMv8_6_A, // https://github.com/llvm/llvm-project/blob/main/llvm/unittests/TargetParser/TargetParserTest.cpp [UARCH_PJ4] = ISA_ARMv7_A, [UARCH_XIAOMI] = ISA_ARMv8_A, }; @@ -112,7 +127,9 @@ [ISA_ARMv8_3_A] = "ARMv8.3", [ISA_ARMv8_4_A] = "ARMv8.4", [ISA_ARMv8_5_A] = "ARMv8.5", - [ISA_ARMv9_A] = "ARMv9" + [ISA_ARMv8_6_A] = "ARMv8.6", + [ISA_ARMv9_A] = "ARMv9", + [ISA_ARMv9_2_A] = "ARMv9.2", }; #define UARCH_START if (false) {} @@ -184,13 +201,24 @@ CHECK_UARCH(arch, cpu, 'A', 0xD0E, NA, NA, "Cortex-A76", UARCH_CORTEX_A76, CPU_VENDOR_ARM) CHECK_UARCH(arch, cpu, 'A', 0xD40, NA, NA, "Neoverse V1", UARCH_NEOVERSE_V1, CPU_VENDOR_ARM) CHECK_UARCH(arch, cpu, 'A', 0xD41, NA, NA, "Cortex-A78", UARCH_CORTEX_A78, CPU_VENDOR_ARM) + CHECK_UARCH(arch, cpu, 'A', 0xD42, NA, NA, "Cortex-A78AE", UARCH_CORTEX_A78AE, CPU_VENDOR_ARM) CHECK_UARCH(arch, cpu, 'A', 0xD44, NA, NA, "Cortex-X1", UARCH_CORTEX_X1, CPU_VENDOR_ARM) CHECK_UARCH(arch, cpu, 'A', 0xD46, NA, NA, "Cortex‑A510", UARCH_CORTEX_A510, CPU_VENDOR_ARM) CHECK_UARCH(arch, cpu, 'A', 0xD47, NA, NA, "Cortex‑A710", UARCH_CORTEX_A710, CPU_VENDOR_ARM) CHECK_UARCH(arch, cpu, 'A', 0xD48, NA, NA, "Cortex-X2", UARCH_CORTEX_X2, CPU_VENDOR_ARM) + CHECK_UARCH(arch, cpu, 'A', 0xD49, NA, NA, "Neoverse N2", UARCH_NEOVERSE_N2, CPU_VENDOR_ARM) CHECK_UARCH(arch, cpu, 'A', 0xD4A, NA, NA, "Neoverse E1", UARCH_NEOVERSE_E1, CPU_VENDOR_ARM) + CHECK_UARCH(arch, cpu, 'A', 0xD4B, NA, NA, "Cortex-A78C", UARCH_CORTEX_A78C, CPU_VENDOR_ARM) + CHECK_UARCH(arch, cpu, 'A', 0xD4C, NA, NA, "Cortex-X1C", UARCH_CORTEX_X1C, CPU_VENDOR_ARM) CHECK_UARCH(arch, cpu, 'A', 0xD4D, NA, NA, "Cortex-A715", UARCH_CORTEX_A715, CPU_VENDOR_ARM) CHECK_UARCH(arch, cpu, 'A', 0xD4E, NA, NA, "Cortex-X3", UARCH_CORTEX_X3, CPU_VENDOR_ARM) + CHECK_UARCH(arch, cpu, 'A', 0xD4F, NA, NA, "Neoverse V2", UARCH_NEOVERSE_V2, CPU_VENDOR_ARM) + CHECK_UARCH(arch, cpu, 'A', 0xD80, NA, NA, "Cortex-A520", UARCH_CORTEX_A520, CPU_VENDOR_ARM) + CHECK_UARCH(arch, cpu, 'A', 0xD81, NA, NA, "Cortex-A720", UARCH_CORTEX_A720, CPU_VENDOR_ARM) + CHECK_UARCH(arch, cpu, 'A', 0xD82, NA, NA, "Cortex-X4", UARCH_CORTEX_X4, CPU_VENDOR_ARM) + CHECK_UARCH(arch, cpu, 'A', 0xD84, NA, NA, "Neoverse V3", UARCH_NEOVERSE_V3, CPU_VENDOR_ARM) + CHECK_UARCH(arch, cpu, 'A', 0xD85, NA, NA, "Cortex-X925", UARCH_CORTEX_X925, CPU_VENDOR_ARM) + CHECK_UARCH(arch, cpu, 'A', 0xD87, NA, NA, "Cortex-A725", UARCH_CORTEX_A725, CPU_VENDOR_ARM) CHECK_UARCH(arch, cpu, 'B', 0x00F, NA, NA, "Brahma B15", UARCH_BRAHMA_B15, CPU_VENDOR_BROADCOM) CHECK_UARCH(arch, cpu, 'B', 0x100, NA, NA, "Brahma B53", UARCH_BRAHMA_B53, CPU_VENDOR_BROADCOM) @@ -248,6 +276,8 @@ CHECK_UARCH(arch, cpu, 'a', 0x022, NA, NA, "Icestorm", UARCH_ICESTORM, CPU_VENDOR_APPLE) CHECK_UARCH(arch, cpu, 'a', 0x023, NA, NA, "Firestorm", UARCH_FIRESTORM, CPU_VENDOR_APPLE) + CHECK_UARCH(arch, cpu, 'a', 0x024, NA, NA, "Icestorm", UARCH_ICESTORM, CPU_VENDOR_APPLE) // https://github.com/Dr-Noob/cpufetch/issues/263 + CHECK_UARCH(arch, cpu, 'a', 0x025, NA, NA, "Firestorm", UARCH_FIRESTORM, CPU_VENDOR_APPLE) // https://github.com/Dr-Noob/cpufetch/issues/263 CHECK_UARCH(arch, cpu, 'a', 0x030, NA, NA, "Blizzard", UARCH_BLIZZARD, CPU_VENDOR_APPLE) CHECK_UARCH(arch, cpu, 'a', 0x031, NA, NA, "Avalanche", UARCH_AVALANCHE, CPU_VENDOR_APPLE) CHECK_UARCH(arch, cpu, 'a', 0x048, NA, NA, "Sawtooth", UARCH_SAWTOOTH, CPU_VENDOR_APPLE) @@ -262,14 +292,7 @@ } bool is_ARMv8_or_newer(struct cpuInfo* cpu) { - return cpu->arch->isa == ISA_ARMv8_A || - cpu->arch->isa == ISA_ARMv8_A_AArch32 || - cpu->arch->isa == ISA_ARMv8_1_A || - cpu->arch->isa == ISA_ARMv8_2_A || - cpu->arch->isa == ISA_ARMv8_3_A || - cpu->arch->isa == ISA_ARMv8_4_A || - cpu->arch->isa == ISA_ARMv8_5_A || - cpu->arch->isa == ISA_ARMv9_A; + return cpu->arch->isa >= ISA_ARMv8_A; } bool has_fma_support(struct cpuInfo* cpu) { @@ -282,32 +305,26 @@ // If the CPU has NEON, width can be 64 or 128 [1]. // In >= ARMv8, NEON are 128 bits width [2] // If the CPU has SVE/SVE2, width can be between 128-2048 [3], - // so we must check the exact width depending on - // the exact chip (Neoverse V1 uses 256b implementations.) + // so we get the exact value from cntb [4] // // [1] https://en.wikipedia.org/wiki/ARM_architecture_family#Advanced_SIMD_(Neon) // [2] https://developer.arm.com/documentation/102474/0100/Fundamentals-of-Armv8-Neon-technology // [3] https://www.anandtech.com/show/16640/arm-announces-neoverse-v1-n2-platforms-cpus-cmn700-mesh/5 + // [4] https://developer.arm.com/documentation/ddi0596/2020-12/SVE-Instructions/CNTB--CNTD--CNTH--CNTW--Set-scalar-to-multiple-of-predicate-constraint-element-count- - MICROARCH ua = cpu->arch->uarch; - switch(ua) { - case UARCH_NEOVERSE_V1: - return 256; - default: - if (cpu->feat->SVE && cpu->feat->cntb > 0) { - return cpu->feat->cntb * 8; - } - else if (cpu->feat->NEON) { - if(is_ARMv8_or_newer(cpu)) { - return 128; - } - else { - return 64; - } - } - else { - return 32; - } + if (cpu->feat->SVE && cpu->feat->cntb > 0) { + return cpu->feat->cntb * 8; + } + else if (cpu->feat->NEON) { + if(is_ARMv8_or_newer(cpu)) { + return 128; + } + else { + return 64; + } + } + else { + return 32; } } @@ -315,13 +332,19 @@ MICROARCH ua = cpu->arch->uarch; switch(ua) { + case UARCH_CORTEX_X925: // [https://www.anandtech.com/show/21399/arm-unveils-2024-cpu-core-designs-cortex-x925-a725-and-a520-arm-v9-2-redefined-for-3nm-/2] + return 6; case UARCH_EVEREST: // Just a guess, needs confirmation. case UARCH_FIRESTORM: // [https://dougallj.github.io/applecpu/firestorm-simd.html] case UARCH_AVALANCHE: // [https://en.wikipedia.org/wiki/Comparison_of_ARM_processors] case UARCH_CORTEX_X1: // [https://www.anandtech.com/show/15813/arm-cortex-a78-cortex-x1-cpu-ip-diverging/3] + case UARCH_CORTEX_X1C: // Assuming same as X1 case UARCH_CORTEX_X2: // [https://www.anandtech.com/show/16693/arm-announces-mobile-armv9-cpu-microarchitectures-cortexx2-cortexa710-cortexa510/2] case UARCH_CORTEX_X3: // [https://www.hwcooling.net/en/cortex-x3-the-new-fastest-arm-core-architecture-analysis: "The FPU and SIMD unit of the core still has four pipelines"] + case UARCH_CORTEX_X4: // [https://www.anandtech.com/show/18871/arm-unveils-armv92-mobile-architecture-cortex-x4-a720-and-a520-64bit-exclusive/2]: "Cortex-X4: Out-of-Order Core" case UARCH_NEOVERSE_V1: // [https://en.wikichip.org/wiki/arm_holdings/microarchitectures/neoverse_v1] + case UARCH_NEOVERSE_V2: // [https://chipsandcheese.com/2023/09/11/hot-chips-2023-arms-neoverse-v2/] + case UARCH_NEOVERSE_V3: // Assuming same as V2 return 4; case UARCH_SAWTOOTH: // Needs confirmation, rn this is the best we know: https://mastodon.social/@dougall/111118317031041336 case UARCH_EXYNOS_M3: // [https://www.anandtech.com/show/12361/samsung-exynos-m3-architecture] @@ -340,16 +363,22 @@ case UARCH_CORTEX_A76: // [https://www.anandtech.com/show/12785/arm-cortex-a76-cpu-unveiled-7nm-powerhouse/3] case UARCH_CORTEX_A77: // [https://fuse.wikichip.org/news/2339/arm-unveils-cortex-a77-emphasizes-single-thread-performance] case UARCH_CORTEX_A78: // [https://fuse.wikichip.org/news/3536/arm-unveils-the-cortex-a78-when-less-is-more] + case UARCH_CORTEX_A78C: // Assuming same as A78 + case UARCH_CORTEX_A78AE:// Assuming same as A78 case UARCH_EXYNOS_M1: // [https://www.anandtech.com/show/12361/samsung-exynos-m3-architecture] case UARCH_EXYNOS_M2: // [https://www.anandtech.com/show/12361/samsung-exynos-m3-architecture] case UARCH_NEOVERSE_N1: // [https://en.wikichip.org/wiki/arm_holdings/microarchitectures/neoverse_n1#Individual_Core] + case UARCH_NEOVERSE_N2: // [https://chipsandcheese.com/2023/08/18/arms-neoverse-n2-cortex-a710-for-servers/] case UARCH_CORTEX_A710: // [https://chipsandcheese.com/2023/08/11/arms-cortex-a710-winning-by-default/]: Fig in Core Overview. Table in Instruction Scheduling and Execution case UARCH_CORTEX_A715: // [https://www.hwcooling.net/en/arm-introduces-new-cortex-a715-core-architecture-analysis/]: "the numbers of ALU and FPU execution units themselves > + case UARCH_CORTEX_A720: // Assuming same as A715: https://www.anandtech.com/show/18871/arm-unveils-armv92-mobile-architecture-cortex-x4-a720-and-a520-64bit-exclusive/3 + case UARCH_CORTEX_A725: // Assuming same as A720 return 2; case UARCH_NEOVERSE_E1: // [https://www.anandtech.com/show/13959/arm-announces-neoverse-n1-platform/5] // A510 is integrated as part of a Complex. Normally, each complex would incorporate two Cortex-A510 cores. // Each complex incorporates a single VPU with 2 ports, so for each A510 there is theoretically 1 port. case UARCH_CORTEX_A510: // [https://en.wikichip.org/wiki/arm_holdings/microarchitectures/cortex-a510#Vector_Processing_Unit_.28VPU.29] + case UARCH_CORTEX_A520: // Assuming same as A50: https://www.anandtech.com/show/18871/arm-unveils-armv92-mobile-architecture-cortex-x4-a720-and-a520-64bit-exclusive/4 return 1; default: // ARMv6 diff -Nru cpufetch-1.06/src/arm/uarch.h cpufetch-1.07/src/arm/uarch.h --- cpufetch-1.06/src/arm/uarch.h 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/arm/uarch.h 2025-10-31 06:50:04.000000000 +0000 @@ -34,15 +34,26 @@ UARCH_CORTEX_A76, UARCH_CORTEX_A77, UARCH_CORTEX_A78, + UARCH_CORTEX_A78AE, + UARCH_CORTEX_A78C, UARCH_CORTEX_A510, + UARCH_CORTEX_A520, UARCH_CORTEX_A710, UARCH_CORTEX_A715, + UARCH_CORTEX_A720, + UARCH_CORTEX_A725, UARCH_CORTEX_X1, + UARCH_CORTEX_X1C, UARCH_CORTEX_X2, UARCH_CORTEX_X3, + UARCH_CORTEX_X4, + UARCH_CORTEX_X925, UARCH_NEOVERSE_N1, + UARCH_NEOVERSE_N2, UARCH_NEOVERSE_E1, UARCH_NEOVERSE_V1, + UARCH_NEOVERSE_V2, + UARCH_NEOVERSE_V3, UARCH_SCORPION, UARCH_KRAIT, UARCH_KYRO, diff -Nru cpufetch-1.06/src/common/args.c cpufetch-1.07/src/common/args.c --- cpufetch-1.06/src/common/args.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/common/args.c 2025-10-31 06:50:04.000000000 +0000 @@ -225,8 +225,7 @@ char* build_short_options(void) { const char *c = args_chr; int len = sizeof(args_chr) / sizeof(args_chr[0]); - char* str = (char *) emalloc(sizeof(char) * (len*2 + 1)); - memset(str, 0, sizeof(char) * (len*2 + 1)); + char* str = (char *) ecalloc(len*2 + 1, sizeof(char)); #ifdef ARCH_X86 sprintf(str, "%c:%c:%c%c%c%c%c%c%c%c%c%c%c%c", diff -Nru cpufetch-1.06/src/common/ascii.h cpufetch-1.07/src/common/ascii.h --- cpufetch-1.06/src/common/ascii.h 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/common/ascii.h 2025-10-31 06:50:04.000000000 +0000 @@ -394,6 +394,77 @@ $C2## ## ## ## ## ## ## ## ####### \ $C2## ## ### ## ###### ## ## ## " +#define ASCII_AMPERE \ +"$C1 \ +$C1 \ +$C1 ## \ +$C1 #### \ +$C1 ### ## \ +$C1 ### ### \ +$C1 ### ### \ +$C1 ### ### \ +$C1 ## ### \ +$C1 ####### ### ### \ +$C1 ###### ## ###### ### \ +$C1 #### ### ######## \ +$C1 #### ### #### \ +$C1 ### ### #### \ +$C1 ## ### ### \ +$C1 \ +$C1 " + +#define ASCII_NXP \ +"$C1##### # $C2####### ####### $C3########## \ +$C1####### ## $C2####### ####### $C3############### \ +$C1########## #### $C2###### ###### $C3### ###### \ +$C1############ ##### $C2############ $C3##### ##### \ +$C1##### ####### ##### $C2########## $C3################### \ +$C1##### ######### $C2############## $C3############### \ +$C1##### ###### $C2###### ###### $C3#### \ +$C1##### ## $C2###### ###### $C3## " + +#define ASCII_AMLOGIC \ +"$C1 .#####. ### ### \ +$C1 ######## ### \ +$C1 ####..### ########## ### ### ##### ### ### \ +$C1 .## #. ### ## ## ## ### ## ## ## ## ### ## \ +$C1 #### #.# ### ## ## ## ### ## ## ## ## ### ## \ +$C1#########.### ## ## ## ## ### ###### ## ### \ +$C1 ### \ +$C1 ### " + +#define ASCII_MARVELL \ +"$C1 ........... ........... \ +$C1 .### . .## . \ +$C1 .##### . #### . \ +$C1 ####### . ####### . \ +$C1 .#########__________. #########__________. \ +$C1 .###########|__________|#########|__________| \ +$C1 ############ ______############ __________ \ +$C1 .######### |__________|###### |__________| \ +$C1 ########### ___########### __________ \ +$C1.########## |__________| |__________| " + +#define ASCII_SPACEMIT \ +"$C1 :#: \ +$C1 :####: \ +$C1 :#######: \ +$C1 :#########: \ +$C1 :#########: \ +$C1 :#######: \ +$C1 :####: \ +$C1 :#: \ +$C1:##: :#: \ +$C1:####: :###: \ +$C1:#######: :####: \ +$C1:##########: :###: \ +$C1:###########: :#: \ +$C1:###########: \ +$C1 :##########: \ +$C1 :#######: \ +$C1 :####: \ +$C1 :##: " + // --------------------- LONG LOGOS ------------------------- // #define ASCII_AMD_L \ "$C1 \ @@ -569,6 +640,11 @@ asciiL logo_starfive = { ASCII_STARFIVE, 33, 17, false, {C_FG_WHITE}, {C_FG_WHITE, C_FG_BLUE} }; asciiL logo_sipeed = { ASCII_SIPEED, 41, 16, true, {C_BG_RED, C_BG_WHITE}, {C_FG_RED, C_FG_WHITE} }; asciiL logo_nvidia = { ASCII_NVIDIA, 45, 19, false, {C_FG_GREEN, C_FG_WHITE}, {C_FG_WHITE, C_FG_GREEN} }; +asciiL logo_ampere = { ASCII_AMPERE, 50, 17, false, {C_FG_RED}, {C_FG_WHITE, C_FG_RED} }; +asciiL logo_nxp = { ASCII_NXP, 55, 8, false, {C_FG_YELLOW, C_FG_CYAN, C_FG_GREEN}, {C_FG_CYAN, C_FG_WHITE} }; +asciiL logo_amlogic = { ASCII_AMLOGIC, 58, 8, false, {C_FG_BLUE}, {C_FG_BLUE, C_FG_B_WHITE} }; +asciiL logo_marvell = { ASCII_MARVELL, 56, 10, false, {C_FG_B_BLACK}, {C_FG_B_BLACK, C_FG_B_WHITE} }; +asciiL logo_spacemit = { ASCII_SPACEMIT, 27, 18, false, {C_FG_B_GREEN}, {C_FG_B_GREEN, C_FG_B_WHITE} }; // Long variants | ----------------------------------------------------------------------------------------------------------------| asciiL logo_amd_l = { ASCII_AMD_L, 62, 19, true, {C_BG_WHITE, C_BG_GREEN}, {C_FG_WHITE, C_FG_GREEN} }; diff -Nru cpufetch-1.06/src/common/cpu.c cpufetch-1.07/src/common/cpu.c --- cpufetch-1.06/src/common/cpu.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/common/cpu.c 2025-10-31 06:50:04.000000000 +0000 @@ -34,6 +34,12 @@ return freq->max; } +#ifdef ARCH_X86 +int64_t get_freq_pp(struct frequency* freq) { + return freq->max_pp; +} +#endif + #if defined(ARCH_X86) || defined(ARCH_PPC) char* get_str_cpu_name(struct cpuInfo* cpu, bool fcpuname) { #ifdef ARCH_X86 diff -Nru cpufetch-1.06/src/common/cpu.h cpufetch-1.07/src/common/cpu.h --- cpufetch-1.06/src/common/cpu.h 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/common/cpu.h 2025-10-31 06:50:04.000000000 +0000 @@ -25,6 +25,7 @@ CPU_VENDOR_RISCV, CPU_VENDOR_SIFIVE, CPU_VENDOR_THEAD, + CPU_VENDOR_SPACEMIT, // OTHERS CPU_VENDOR_UNKNOWN, CPU_VENDOR_INVALID @@ -60,6 +61,11 @@ int32_t max; // Indicates if max frequency was measured bool measured; +#ifdef ARCH_X86 + // Max frequency when running vectorized code. + // Used only for peak performance computation. + int32_t max_pp; +#endif }; struct hypervisor { @@ -132,7 +138,7 @@ struct extensions { char* str; - uint64_t mask; + bool* mask; // allocated at runtime with size RISCV_ISA_EXT_ID_MAX }; struct cpuInfo { @@ -188,6 +194,8 @@ #ifdef ARCH_X86 // The index of the first core in the module uint32_t first_core_id; + // The index of this module + uint32_t module_id; #endif #endif }; @@ -200,6 +208,9 @@ VENDOR get_cpu_vendor(struct cpuInfo* cpu); int64_t get_freq(struct frequency* freq); +#ifdef ARCH_X86 +int64_t get_freq_pp(struct frequency* freq); +#endif char* get_str_aes(struct cpuInfo* cpu); char* get_str_sha(struct cpuInfo* cpu); diff -Nru cpufetch-1.06/src/common/global.c cpufetch-1.07/src/common/global.c --- cpufetch-1.06/src/common/global.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/common/global.c 2025-10-31 06:50:04.000000000 +0000 @@ -62,7 +62,7 @@ #endif #ifndef GIT_FULL_VERSION - static const char* VERSION = "1.06"; + static const char* VERSION = "1.07"; #endif enum { diff -Nru cpufetch-1.06/src/common/pci.c cpufetch-1.07/src/common/pci.c --- cpufetch-1.06/src/common/pci.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/common/pci.c 2025-10-31 06:50:04.000000000 +0000 @@ -14,14 +14,6 @@ #define PCI_PATH "/sys/bus/pci/devices/" #define MAX_LENGTH_PCI_DIR_NAME 1024 -/* - * doc: https://wiki.osdev.org/PCI#Class_Codes - * https://pci-ids.ucw.cz/read/PC - */ -#define PCI_VENDOR_ID_AMD 0x1002 -#define CLASS_VGA_CONTROLLER 0x0300 -#define CLASS_3D_CONTROLLER 0x0302 - // Return a list of PCI devices containing only // the sysfs path struct pci_devices * get_pci_paths(void) { @@ -74,7 +66,7 @@ if ((stbuf.st_mode & S_IFMT) == S_IFDIR) { int strLen = min(MAX_LENGTH_PCI_DIR_NAME, strlen(dp->d_name)) + 1; pci->devices[i] = emalloc(sizeof(struct pci_device)); - pci->devices[i]->path = ecalloc(sizeof(char), strLen); + pci->devices[i]->path = ecalloc(strLen, sizeof(char)); strncpy(pci->devices[i]->path, dp->d_name, strLen); i++; } @@ -98,9 +90,9 @@ int path_size = strlen(PCI_PATH) + strlen(dev->path) + 2; // Read vendor_id - char *vendor_id_path = emalloc(sizeof(char) * (path_size + strlen("vendor"))); + char *vendor_id_path = emalloc(sizeof(char) * (path_size + strlen("vendor") + 1)); sprintf(vendor_id_path, "%s/%s/%s", PCI_PATH, dev->path, "vendor"); - + if ((buf = read_file(vendor_id_path, &filelen)) == NULL) { printWarn("read_file: %s: %s\n", vendor_id_path, strerror(errno)); dev->vendor_id = 0; @@ -110,7 +102,7 @@ } // Read device_id - char *device_id_path = emalloc(sizeof(char) * (path_size + strlen("device"))); + char *device_id_path = emalloc(sizeof(char) * (path_size + strlen("device") + 1)); sprintf(device_id_path, "%s/%s/%s", PCI_PATH, dev->path, "device"); if ((buf = read_file(device_id_path, &filelen)) == NULL) { @@ -126,43 +118,6 @@ } } -// Right now, we are interested in PCI devices which -// vendor is NVIDIA (to be extended in the future). -// Should we also restrict to VGA controllers only? -bool pci_device_is_useful(struct pci_device* dev) { - return dev->vendor_id == PCI_VENDOR_NVIDIA; -} - -// Filter the input list in order to get only those PCI devices which -// we are interested in (decided by pci_device_is_useful) -// and return the filtered result. -struct pci_devices * filter_pci_devices(struct pci_devices * pci) { - int * devices_to_get = emalloc(sizeof(int) * pci->num_devices); - int dev_ptr = 0; - - for (int i=0; i < pci->num_devices; i++) { - if (pci_device_is_useful(pci->devices[i])) { - devices_to_get[dev_ptr] = i; - dev_ptr++; - } - } - - struct pci_devices * pci_filtered = emalloc(sizeof(struct pci_devices)); - pci_filtered->num_devices = dev_ptr; - - if (pci_filtered->num_devices == 0) { - pci_filtered->devices = NULL; - } - else { - pci_filtered->devices = emalloc(sizeof(struct pci_device) * pci_filtered->num_devices); - - for (int i=0; i < pci_filtered->num_devices; i++) - pci_filtered->devices[i] = pci->devices[devices_to_get[i]]; - } - - return pci_filtered; -} - // Return a list of PCI devices that could be used to infer the SoC. // The criteria to determine which devices are suitable for this task // is decided in filter_pci_devices. @@ -174,5 +129,5 @@ populate_pci_devices(pci); - return filter_pci_devices(pci); + return pci; } diff -Nru cpufetch-1.06/src/common/pci.h cpufetch-1.07/src/common/pci.h --- cpufetch-1.06/src/common/pci.h 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/common/pci.h 2025-10-31 06:50:04.000000000 +0000 @@ -1,8 +1,11 @@ #ifndef __PCI__ #define __PCI__ -#define PCI_VENDOR_NVIDIA 0x10de +#define PCI_VENDOR_NVIDIA 0x10de +#define PCI_VENDOR_AMPERE 0x1def + #define PCI_DEVICE_TEGRA_X1 0x0faf +#define PCI_DEVICE_ALTRA 0xe100 struct pci_device { char * path; diff -Nru cpufetch-1.06/src/common/printer.c cpufetch-1.07/src/common/printer.c --- cpufetch-1.06/src/common/printer.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/common/printer.c 2025-10-31 06:50:04.000000000 +0000 @@ -20,6 +20,7 @@ #include "../arm/uarch.h" #include "../arm/midr.h" #include "../arm/soc.h" + #include "../arm/socs.h" #include "../common/soc.h" #elif ARCH_RISCV #include "../riscv/riscv.h" @@ -44,9 +45,17 @@ #define MAX_ATTRIBUTES 100 #define MAX_TERM_SIZE 1024 +typedef struct { + int id; + const char *name; + const char *shortname; +} AttributeField; + enum { -#if defined(ARCH_X86) || defined(ARCH_PPC) +#if defined(ARCH_X86) ATTRIBUTE_NAME, +#elif defined(ARCH_PPC) + ATTRIBUTE_PART_NUMBER, #elif defined(ARCH_ARM) || defined(ARCH_RISCV) ATTRIBUTE_SOC, #endif @@ -78,76 +87,40 @@ ATTRIBUTE_PEAK }; -static const char* ATTRIBUTE_FIELDS [] = { -#ifdef ARCH_X86 - "Name:", -#elif ARCH_PPC - "Part Number:", +static const AttributeField ATTRIBUTE_INFO[] = { +#if defined(ARCH_X86) + { ATTRIBUTE_NAME, "Name:", "Name:" }, +#elif defined(ARCH_PPC) + { ATTRIBUTE_PART_NUMBER, "Part Number:", "P/N:" }, #elif defined(ARCH_ARM) || defined(ARCH_RISCV) - "SoC:", + { ATTRIBUTE_SOC, "SoC:", "SoC:" }, #endif #if defined(ARCH_X86) || defined(ARCH_ARM) - "", + { ATTRIBUTE_CPU_NUM, "", "" }, #endif - "Hypervisor:", - "Microarchitecture:", - "Technology:", - "Max Frequency:", - "Sockets:", - "Cores:", - "Cores (Total):", + { ATTRIBUTE_HYPERVISOR, "Hypervisor:", "Hypervisor:" }, + { ATTRIBUTE_UARCH, "Microarchitecture:", "uArch:" }, + { ATTRIBUTE_TECHNOLOGY, "Technology:", "Technology:" }, + { ATTRIBUTE_FREQUENCY, "Max Frequency:", "Max Freq:" }, + { ATTRIBUTE_SOCKETS, "Sockets:", "Sockets:" }, + { ATTRIBUTE_NCORES, "Cores:", "Cores:" }, + { ATTRIBUTE_NCORES_DUAL, "Cores (Total):", "Cores (Total):" }, #ifdef ARCH_X86 - "SSE:", - "AVX:", - "FMA:", -#elif ARCH_PPC - "Altivec: ", -#elif defined(ARCH_ARM) - "Features: ", -#elif defined(ARCH_RISCV) - "Extensions: ", -#endif - "L1i Size:", - "L1d Size:", - "L2 Size:", - "L3 Size:", - "Peak Performance:", -}; - -static const char* ATTRIBUTE_FIELDS_SHORT [] = { -#if defined(ARCH_X86) - "Name:", + { ATTRIBUTE_SSE, "SSE:", "SSE:" }, + { ATTRIBUTE_AVX, "AVX:", "AVX:" }, + { ATTRIBUTE_FMA, "FMA:", "FMA:" }, #elif ARCH_PPC - "P/N:", + { ATTRIBUTE_ALTIVEC, "Altivec: ", "Altivec: " }, #elif ARCH_ARM - "SoC:", -#endif -#if defined(ARCH_X86) || defined(ARCH_ARM) - "", -#endif - "Hypervisor:", - "uArch:", - "Technology:", - "Max Freq:", - "Sockets:", - "Cores:", - "Cores (Total):", -#ifdef ARCH_X86 - "SSE:", - "AVX:", - "FMA:", -#elif ARCH_PPC - "Altivec: ", -#elif defined(ARCH_ARM) - "Features: ", -#elif defined(ARCH_RISCV) - "Extensions: ", + { ATTRIBUTE_FEATURES, "Features: ", "Features: " }, +#elif ARCH_RISCV + { ATTRIBUTE_EXTENSIONS, "Extensions: ", "Extensions: " }, #endif - "L1i Size:", - "L1d Size:", - "L2 Size:", - "L3 Size:", - "Peak Perf.:", + { ATTRIBUTE_L1i, "L1i Size:", "L1i Size:" }, + { ATTRIBUTE_L1d, "L1d Size:", "L1d Size:" }, + { ATTRIBUTE_L2, "L2 Size:", "L2 Size:" }, + { ATTRIBUTE_L3, "L3 Size:", "L3 Size:" }, + { ATTRIBUTE_PEAK, "Peak Performance:", "Peak Perf.:" }, }; struct terminal { @@ -389,6 +362,14 @@ art->art = &logo_allwinner; else if(art->vendor == SOC_VENDOR_ROCKCHIP) art->art = &logo_rockchip; + else if(art->vendor == SOC_VENDOR_AMPERE) + art->art = &logo_ampere; + else if(art->vendor == SOC_VENDOR_NXP) + art->art = &logo_nxp; + else if(art->vendor == SOC_VENDOR_AMLOGIC) + art->art = &logo_amlogic; + else if(art->vendor == SOC_VENDOR_MARVELL) + art->art = &logo_marvell; else if(art->vendor == SOC_VENDOR_NVIDIA) art->art = choose_ascii_art_aux(&logo_nvidia_l, &logo_nvidia, term, lf); else { @@ -403,6 +384,8 @@ art->art = &logo_allwinner; else if(art->vendor == SOC_VENDOR_SIPEED) art->art = &logo_sipeed; + else if(art->vendor == SOC_VENDOR_SPACEMIT) + art->art = &logo_spacemit; else art->art = &logo_riscv; #endif @@ -443,13 +426,14 @@ } } -uint32_t longest_attribute_length(struct ascii* art, const char** attribute_fields) { +uint32_t longest_attribute_length(struct ascii* art, bool use_short) { uint32_t max = 0; uint64_t len = 0; for(uint32_t i=0; i < art->n_attributes_set; i++) { if(art->attributes[i]->value != NULL) { - len = strlen(attribute_fields[art->attributes[i]->type]); + const char* str = use_short ? ATTRIBUTE_INFO[art->attributes[i]->type].shortname : ATTRIBUTE_INFO[art->attributes[i]->type].name; + len = strlen(str); if(len > max) max = len; } } @@ -474,7 +458,7 @@ } #if defined(ARCH_X86) || defined(ARCH_PPC) -void print_ascii_generic(struct ascii* art, uint32_t la, int32_t termw, const char** attribute_fields, bool hybrid_architecture) { +void print_ascii_generic(struct ascii* art, uint32_t la, int32_t termw, bool use_short, bool hybrid_architecture) { struct ascii_logo* logo = art->art; int attr_to_print = 0; int attr_type; @@ -536,14 +520,15 @@ else { #endif beg_space = 0; - space_right = 2 + 1 + (la - strlen(attribute_fields[attr_type])); + const char* attr_str = use_short ? ATTRIBUTE_INFO[attr_type].shortname : ATTRIBUTE_INFO[attr_type].name; + space_right = 2 + 1 + (la - strlen(attr_str)); if(hybrid_architecture && add_space) { beg_space = 2; space_right -= 2; } - printOut(lbuf, beg_space + strlen(attribute_fields[attr_type]) + space_right + strlen(attr_value), - "%*s%s%s%s%*s%s%s%s", beg_space, "", logo->color_text[0], attribute_fields[attr_type], art->reset, space_right, "", logo->color_text[1], attr_value, art->reset); + printOut(lbuf, beg_space + strlen(attr_str) + space_right + strlen(attr_value), + "%*s%s%s%s%*s%s%s%s", beg_space, "", logo->color_text[0], attr_str, art->reset, space_right, "", logo->color_text[1], attr_value, art->reset); #ifdef ARCH_X86 } #endif @@ -652,19 +637,19 @@ setAttribute(art, ATTRIBUTE_PEAK, pp); // Step 3. Print output - const char** attribute_fields = ATTRIBUTE_FIELDS; - uint32_t longest_attribute = longest_attribute_length(art, attribute_fields); + bool use_short = false; + uint32_t longest_attribute = longest_attribute_length(art, use_short); uint32_t longest_field = longest_field_length(art, longest_attribute); choose_ascii_art(art, cs, term, longest_field); if(!ascii_fits_screen(term->w, *art->art, longest_field)) { // Despite of choosing the smallest logo, the output does not fit // Choose the shorter field names and recalculate the longest attr - attribute_fields = ATTRIBUTE_FIELDS_SHORT; - longest_attribute = longest_attribute_length(art, attribute_fields); + use_short = true; + longest_attribute = longest_attribute_length(art, use_short); } - print_ascii_generic(art, longest_attribute, term->w, attribute_fields, hybrid_architecture); + print_ascii_generic(art, longest_attribute, term->w, use_short, hybrid_architecture); free(manufacturing_process); free(sockets); @@ -713,7 +698,7 @@ // Step 2. Set attributes if(cpu_name != NULL) { - setAttribute(art, ATTRIBUTE_NAME, cpu_name); + setAttribute(art, ATTRIBUTE_PART_NUMBER, cpu_name); } setAttribute(art, ATTRIBUTE_UARCH, uarch); if(cpu->hv->present) { @@ -740,19 +725,19 @@ setAttribute(art, ATTRIBUTE_PEAK, pp); // Step 3. Print output - const char** attribute_fields = ATTRIBUTE_FIELDS; - uint32_t longest_attribute = longest_attribute_length(art, attribute_fields); + bool use_short = false; + uint32_t longest_attribute = longest_attribute_length(art, use_short); uint32_t longest_field = longest_field_length(art, longest_attribute); choose_ascii_art(art, cs, term, longest_field); if(!ascii_fits_screen(term->w, *art->art, longest_field)) { // Despite of choosing the smallest logo, the output does not fit // Choose the shorter field names and recalculate the longest attr - attribute_fields = ATTRIBUTE_FIELDS_SHORT; - longest_attribute = longest_attribute_length(art, attribute_fields); + use_short = true; + longest_attribute = longest_attribute_length(art, use_short); } - print_ascii_generic(art, longest_attribute, term->w, attribute_fields, false); + print_ascii_generic(art, longest_attribute, term->w, use_short, false); return true; } @@ -780,7 +765,7 @@ return max; } -void print_ascii_arm(struct ascii* art, uint32_t la, int32_t termw, const char** attribute_fields) { +void print_ascii_arm(struct ascii* art, uint32_t la, int32_t termw, bool use_short) { struct ascii_logo* logo = art->art; int attr_to_print = 0; int attr_type; @@ -851,14 +836,15 @@ } else { beg_space = 0; - space_right = 2 + 1 + (la - strlen(attribute_fields[attr_type])); + const char* attr_str = use_short ? ATTRIBUTE_INFO[attr_type].shortname : ATTRIBUTE_INFO[attr_type].name; + space_right = 2 + 1 + (la - strlen(attr_str)); if(add_space) { beg_space = 2; space_right -= 2; } - printOut(lbuf, beg_space + strlen(attribute_fields[attr_type]) + space_right + strlen(attr_value), - "%*s%s%s%s%*s%s%s%s", beg_space, "", logo->color_text[0], attribute_fields[attr_type], art->reset, space_right, "", logo->color_text[1], attr_value, art->reset); + printOut(lbuf, beg_space + strlen(attr_str) + space_right + strlen(attr_value), + "%*s%s%s%s%*s%s%s%s", beg_space, "", logo->color_text[0], attr_str, art->reset, space_right, "", logo->color_text[1], attr_value, art->reset); } } printOutLine(lbuf, art, termw); @@ -879,7 +865,18 @@ char* soc_name = get_soc_name(cpu->soc); char* features = get_str_features(cpu); setAttribute(art, ATTRIBUTE_SOC, soc_name); + + // Currently no reliable way to identify the specific SoC on Windows + // https://github.com/Dr-Noob/cpufetch/pull/273 + // Hide manufacturing process +#if !defined(_WIN32) + // In the case that the model is unknown but the vendor isn't (this is, when + // guess_raw_soc_from_devtree succeeded), do not show the manufacturing process + // (as it will be unknown) + if (cpu->soc->model != SOC_MODEL_UNKNOWN || + (cpu->soc->model == SOC_MODEL_UNKNOWN && cpu->soc->vendor == SOC_VENDOR_UNKNOWN)) setAttribute(art, ATTRIBUTE_TECHNOLOGY, manufacturing_process); +#endif if(cpu->num_cpus == 1) { char* uarch = get_str_uarch(cpu); @@ -917,8 +914,8 @@ setAttribute(art, ATTRIBUTE_HYPERVISOR, cpu->hv->hv_name); } - const char** attribute_fields = ATTRIBUTE_FIELDS; - uint32_t longest_attribute = longest_attribute_length(art, attribute_fields); + bool use_short = false; + uint32_t longest_attribute = longest_attribute_length(art, use_short); uint32_t longest_field = longest_field_length_arm(art, longest_attribute); choose_ascii_art(art, cs, term, longest_field); @@ -930,11 +927,11 @@ if(!ascii_fits_screen(term->w, *art->art, longest_field)) { // Despite of choosing the smallest logo, the output does not fit // Choose the shorter field names and recalculate the longest attr - attribute_fields = ATTRIBUTE_FIELDS_SHORT; - longest_attribute = longest_attribute_length(art, attribute_fields); + use_short = true; + longest_attribute = longest_attribute_length(art, use_short); } - print_ascii_arm(art, longest_attribute, term->w, attribute_fields); + print_ascii_arm(art, longest_attribute, term->w, use_short); free(manufacturing_process); free(pp); @@ -952,14 +949,7 @@ #endif #ifdef ARCH_RISCV -// https://stackoverflow.com/a/2709523 -uint64_t number_of_bits(uint64_t i) { - i = i - ((i >> 1) & 0x5555555555555555); - i = (i & 0x3333333333333333) + ((i >> 2) & 0x3333333333333333); - return (((i + (i >> 4)) & 0xF0F0F0F0F0F0F0F) * 0x101010101010101) >> 56; -} - -void print_ascii_riscv(struct ascii* art, uint32_t la, int32_t termw, const char** attribute_fields, uint64_t extensions_mask) { +void print_ascii_riscv(struct ascii* art, uint32_t la, int32_t termw, bool use_short, bool* extensions_mask) { struct ascii_logo* logo = art->art; int attr_to_print = 0; int attr_type; @@ -969,7 +959,7 @@ int32_t ext_list_size = sizeof(extension_list)/sizeof(extension_list[0]); int32_t ext_num = 0; int32_t ext_to_print = 0; - int32_t num_extensions = number_of_bits(extensions_mask); + int32_t num_extensions = get_num_extensions(extensions_mask); int32_t space_up = ((int)logo->height - (int)(art->n_attributes_set + num_extensions))/2; int32_t space_down = (int)logo->height - (int)(art->n_attributes_set + num_extensions) - (int)space_up; uint32_t logo_pos = 0; @@ -1015,7 +1005,9 @@ // Print extension if(attr_to_print > 0 && art->attributes[attr_to_print-1]->type == ATTRIBUTE_EXTENSIONS && ext_num != num_extensions) { // Search for the extension to print - while(ext_to_print < ext_list_size && !((extensions_mask >> extension_list[ext_to_print].id) & 1U)) ext_to_print++; + while (ext_to_print < ext_list_size && !((extensions_mask[extension_list[ext_to_print].id]))) + ext_to_print++; + if(ext_to_print == ext_list_size) { printBug("print_ascii_riscv: Unable to find the extension to print"); } @@ -1027,10 +1019,11 @@ else { attr_to_print++; beg_space = 0; - space_right = 2 + 1 + (la - strlen(attribute_fields[attr_type])); + const char* attr_str = use_short ? ATTRIBUTE_INFO[attr_type].shortname : ATTRIBUTE_INFO[attr_type].name; + space_right = 2 + 1 + (la - strlen(attr_str)); - printOut(lbuf, beg_space + strlen(attribute_fields[attr_type]) + space_right + strlen(attr_value), - "%*s%s%s%s%*s%s%s%s", beg_space, "", logo->color_text[0], attribute_fields[attr_type], art->reset, space_right, "", logo->color_text[1], attr_value, art->reset); + printOut(lbuf, beg_space + strlen(attr_str) + space_right + strlen(attr_value), + "%*s%s%s%s%*s%s%s%s", beg_space, "", logo->color_text[0], attr_str, art->reset, space_right, "", logo->color_text[1], attr_value, art->reset); } } printOutLine(lbuf, art, termw); @@ -1068,19 +1061,19 @@ setAttribute(art, ATTRIBUTE_PEAK, pp); // Step 3. Print output - const char** attribute_fields = ATTRIBUTE_FIELDS; - uint32_t longest_attribute = longest_attribute_length(art, attribute_fields); + bool use_short = false; + uint32_t longest_attribute = longest_attribute_length(art, use_short); uint32_t longest_field = longest_field_length(art, longest_attribute); choose_ascii_art(art, cs, term, longest_field); if(!ascii_fits_screen(term->w, *art->art, longest_field)) { // Despite of choosing the smallest logo, the output does not fit // Choose the shorter field names and recalculate the longest attr - attribute_fields = ATTRIBUTE_FIELDS_SHORT; - longest_attribute = longest_attribute_length(art, attribute_fields); + use_short = true; + longest_attribute = longest_attribute_length(art, use_short); } - print_ascii_riscv(art, longest_attribute, term->w, attribute_fields, cpu->ext->mask); + print_ascii_riscv(art, longest_attribute, term->w, use_short, cpu->ext->mask); return true; } diff -Nru cpufetch-1.06/src/common/soc.c cpufetch-1.07/src/common/soc.c --- cpufetch-1.06/src/common/soc.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/common/soc.c 2025-10-31 06:50:04.000000000 +0000 @@ -16,21 +16,26 @@ [SOC_VENDOR_EXYNOS] = "Exynos ", [SOC_VENDOR_KIRIN] = "Kirin ", [SOC_VENDOR_KUNPENG] = "Kunpeng ", - [SOC_VENDOR_BROADCOM] = "Broadcom BCM", + [SOC_VENDOR_BROADCOM] = "Broadcom ", [SOC_VENDOR_APPLE] = "Apple ", [SOC_VENDOR_ROCKCHIP] = "Rockchip ", [SOC_VENDOR_GOOGLE] = "Google ", [SOC_VENDOR_NVIDIA] = "NVIDIA ", + [SOC_VENDOR_AMPERE] = "Ampere ", + [SOC_VENDOR_NXP] = "NXP ", + [SOC_VENDOR_AMLOGIC] = "Amlogic ", + [SOC_VENDOR_MARVELL] = "Marvell", // RISC-V [SOC_VENDOR_SIFIVE] = "SiFive ", [SOC_VENDOR_STARFIVE] = "StarFive ", [SOC_VENDOR_SIPEED] = "Sipeed ", + [SOC_VENDOR_SPACEMIT] = "SpacemiT ", // ARM & RISC-V [SOC_VENDOR_ALLWINNER] = "Allwinner " }; VENDOR get_soc_vendor(struct system_on_chip* soc) { - return soc->soc_vendor; + return soc->vendor; } char* get_str_process(struct system_on_chip* soc) { @@ -41,42 +46,62 @@ snprintf(str, strlen(STRING_UNKNOWN)+1, STRING_UNKNOWN); } else { - str = emalloc(sizeof(char) * 5); - memset(str, 0, sizeof(char) * 5); - snprintf(str, 5, "%dnm", soc->process); + int max_process_len = 5 + 1; + str = ecalloc(max_process_len, sizeof(char)); + snprintf(str, max_process_len, "%dnm", soc->process); } return str; } char* get_soc_name(struct system_on_chip* soc) { - if(soc->soc_model == SOC_MODEL_UNKNOWN) + if(soc->model == SOC_MODEL_UNKNOWN) return soc->raw_name; - return soc->soc_name; + return soc->name; } void fill_soc(struct system_on_chip* soc, char* soc_name, SOC soc_model, int32_t process) { - soc->soc_model = soc_model; - soc->soc_vendor = get_soc_vendor_from_soc(soc_model); + soc->model = soc_model; + soc->vendor = get_soc_vendor_from_soc(soc_model); soc->process = process; - if(soc->soc_vendor == SOC_VENDOR_UNKNOWN) { - printBug("fill_soc: soc->soc_vendor == SOC_VENDOR_UNKOWN"); + if(soc->vendor == SOC_VENDOR_UNKNOWN) { + printBug("fill_soc: soc->vendor == SOC_VENDOR_UNKOWN"); // If we fall here there is a bug in socs.h // Reset everything to avoid segfault - soc->soc_vendor = SOC_VENDOR_UNKNOWN; - soc->soc_model = SOC_MODEL_UNKNOWN; + soc->vendor = SOC_VENDOR_UNKNOWN; + soc->model = SOC_MODEL_UNKNOWN; soc->process = UNKNOWN; soc->raw_name = emalloc(sizeof(char) * (strlen(STRING_UNKNOWN)+1)); snprintf(soc->raw_name, strlen(STRING_UNKNOWN)+1, STRING_UNKNOWN); } else { - soc->process = process; - int len = strlen(soc_name) + strlen(soc_trademark_string[soc->soc_vendor]) + 1; - soc->soc_name = emalloc(sizeof(char) * len); - memset(soc->soc_name, 0, sizeof(char) * len); - sprintf(soc->soc_name, "%s%s", soc_trademark_string[soc->soc_vendor], soc_name); + int len = strlen(soc_name) + strlen(soc_trademark_string[soc->vendor]) + 1; + soc->name = emalloc(sizeof(char) * len); + sprintf(soc->name, "%s%s", soc_trademark_string[soc->vendor], soc_name); } } +void fill_soc_raw(struct system_on_chip* soc, char* soc_name, VENDOR vendor) { + soc->model = SOC_MODEL_UNKNOWN; + soc->vendor = vendor; + soc->process = UNKNOWN; + + int len = strlen(soc_name) + strlen(soc_trademark_string[soc->vendor]) + 1; + soc->raw_name = emalloc(sizeof(char) * len); + sprintf(soc->raw_name, "%s%s", soc_trademark_string[soc->vendor], soc_name); +} + +#ifdef _WIN32 +VENDOR try_match_soc_vendor_name(char* vendor_name) +{ + for(size_t i=1; i < sizeof(soc_trademark_string)/sizeof(soc_trademark_string[0]); i++) { + if(strstr(vendor_name, soc_trademark_string[i]) != NULL) { + return i; + } + } + return SOC_VENDOR_UNKNOWN; +} +#endif + bool match_soc(struct system_on_chip* soc, char* raw_name, char* expected_name, char* soc_name, SOC soc_model, int32_t process) { int len1 = strlen(raw_name); int len2 = strlen(expected_name); diff -Nru cpufetch-1.06/src/common/soc.h cpufetch-1.07/src/common/soc.h --- cpufetch-1.06/src/common/soc.h 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/common/soc.h 2025-10-31 06:50:04.000000000 +0000 @@ -25,19 +25,24 @@ SOC_VENDOR_ROCKCHIP, SOC_VENDOR_GOOGLE, SOC_VENDOR_NVIDIA, + SOC_VENDOR_AMPERE, + SOC_VENDOR_NXP, + SOC_VENDOR_AMLOGIC, + SOC_VENDOR_MARVELL, // RISC-V SOC_VENDOR_SIFIVE, SOC_VENDOR_STARFIVE, SOC_VENDOR_SIPEED, + SOC_VENDOR_SPACEMIT, // ARM & RISC-V SOC_VENDOR_ALLWINNER }; struct system_on_chip { - SOC soc_model; - VENDOR soc_vendor; + SOC model; + VENDOR vendor; int32_t process; - char* soc_name; + char* name; char* raw_name; }; @@ -47,6 +52,10 @@ bool match_soc(struct system_on_chip* soc, char* raw_name, char* expected_name, char* soc_name, SOC soc_model, int32_t process); char* get_str_process(struct system_on_chip* soc); void fill_soc(struct system_on_chip* soc, char* soc_name, SOC soc_model, int32_t process); +void fill_soc_raw(struct system_on_chip* soc, char* soc_name, VENDOR vendor); +#ifdef _WIN32 +VENDOR try_match_soc_vendor_name(char* vendor_name); +#endif #define SOC_START if (false) {} #define SOC_EQ(raw_name, expected_name, soc_name, soc_model, soc, process) \ diff -Nru cpufetch-1.06/src/common/sysctl.h cpufetch-1.07/src/common/sysctl.h --- cpufetch-1.06/src/common/sysctl.h 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/common/sysctl.h 2025-10-31 06:50:04.000000000 +0000 @@ -21,9 +21,12 @@ #define CPUFAMILY_ARM_AVALANCHE_BLIZZARD 0xDA33D83D #endif // M3 / A16 / A17 -// https://ratfactor.com/zig/stdlib-browseable2/c/darwin.zig.html -// https://github.com/Dr-Noob/cpufetch/issues/210 +// M3: https://ratfactor.com/zig/stdlib-browseable2/c/darwin.zig.html +// M3_2: https://github.com/Dr-Noob/cpufetch/issues/230 +// PRO: https://github.com/Dr-Noob/cpufetch/issues/225 +// MAX: https://github.com/Dr-Noob/cpufetch/issues/210 #define CPUFAMILY_ARM_EVEREST_SAWTOOTH 0x8765EDEA +#define CPUFAMILY_ARM_EVEREST_SAWTOOTH_2 0xFA33415E #define CPUFAMILY_ARM_EVEREST_SAWTOOTH_PRO 0x5F4DEA93 #define CPUFAMILY_ARM_EVEREST_SAWTOOTH_MAX 0x72015832 @@ -40,6 +43,14 @@ #define CPUSUBFAMILY_ARM_HC_HD 5 #endif +// For alternative way to get CPU frequency on macOS and *BSD +#ifdef __APPLE__ + #define CPUFREQUENCY_SYSCTL "hw.cpufrequency_max" +#else + // For FreeBSD, not sure about other *BSD + #define CPUFREQUENCY_SYSCTL "dev.cpu.0.freq" +#endif + uint32_t get_sys_info_by_name(char* name); #endif diff -Nru cpufetch-1.06/src/common/udev.c cpufetch-1.07/src/common/udev.c --- cpufetch-1.06/src/common/udev.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/common/udev.c 2025-10-31 06:50:04.000000000 +0000 @@ -1,7 +1,10 @@ +#include "../common/global.h" #include "udev.h" #include "global.h" #include "cpu.h" +#define _PATH_DEVTREE "/proc/device-tree/compatible" + // https://www.kernel.org/doc/html/latest/core-api/cpu_hotplug.html int get_ncores_from_cpuinfo(void) { // Examples: @@ -143,8 +146,7 @@ char* tmp2 = strstr(tmp1, "\n"); int strlen = (1 + (tmp2-tmp1)); - char* hardware = emalloc(sizeof(char) * strlen); - memset(hardware, 0, sizeof(char) * strlen); + char* hardware = ecalloc(strlen, sizeof(char)); strncpy(hardware, tmp1, tmp2-tmp1); return hardware; @@ -349,3 +351,79 @@ } return true; } + +char* get_devtree_compatible(int *filelen) { + char* buf; + + if ((buf = read_file(_PATH_DEVTREE, filelen)) == NULL) { + printWarn("read_file: %s: %s", _PATH_DEVTREE, strerror(errno)); + } + + return buf; +} + +// Returns a list of structs devtree, each containing both the vendor and the +// model, coming from the compatible file from the device tree. In this +// context, vendor refers to the first string of every entry and the model to +// the second. For instance, given a compatible file with: +// "str1,foo1.str2,foo2" (where . denotes the NULL byte, i.e., the separator), +// then this function will return a list with two structs, the first one +// containing str1 and foo1 and the other containing str2 and foo2. +struct devtree** get_devtree_compatible_struct(int *num_vendors_ptr) { + int len; + char* dt = get_devtree_compatible(&len); + if (dt == NULL) { + return NULL; + } + + int num_vendors = 0; + char* ptr = dt; + + for (int ptrpos = 0; ptrpos < len; ptrpos = (ptr-dt)) { + ptr = memchr(ptr, '\0', len); + if (ptr == NULL) { + printBug("get_devtree_compatible_struct: Unable to find delimiter (1) (num_vendors=%d)", num_vendors); + return NULL; + } + ptr++; + num_vendors++; + } + + struct devtree** vendors = emalloc(sizeof(struct devtree *) * num_vendors); + ptr = dt; + + for (int ptrpos = 0, i = 0; ptrpos < len; ptrpos = (ptr-dt), i++) { + char* comma_ptr = strstr(ptr, ","); + if (comma_ptr == NULL) { + printBug("get_devtree_compatible_struct: Unable to find comma (num_vendors=%d)", num_vendors); + return NULL; + } + comma_ptr = comma_ptr-1; // Point right before comma + + char* end_ptr = memchr(comma_ptr, '\0', len - ptrpos); + if (end_ptr == NULL) { + printBug("get_devtree_compatible_struct: Unable to find delimiter (2) (num_vendors=%d)", num_vendors); + return NULL; + } + + int vendor_str_len = (comma_ptr-ptr)+1; + int model_str_len = (end_ptr-(comma_ptr+2))+1; + + vendors[i] = emalloc(sizeof(struct devtree)); + vendors[i]->vendor = ecalloc(vendor_str_len, sizeof(char)); + vendors[i]->model = ecalloc(model_str_len, sizeof(char)); + + strncpy(vendors[i]->vendor, ptr, vendor_str_len); + strncpy(vendors[i]->model, comma_ptr+2, model_str_len); + + ptr = memchr(ptr, '\0', len); + if (ptr == NULL) { + printBug("get_devtree_compatible_struct: Unable to find delimiter (3) (num_vendors=%d)", num_vendors); + return NULL; + } + ptr++; // Point right after delimiter + } + + *num_vendors_ptr = num_vendors; + return vendors; +} diff -Nru cpufetch-1.06/src/common/udev.h cpufetch-1.07/src/common/udev.h --- cpufetch-1.06/src/common/udev.h 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/common/udev.h 2025-10-31 06:50:04.000000000 +0000 @@ -31,6 +31,11 @@ #define _PATH_CACHE_MAX_LEN 200 #define _PATH_PACKAGE_MAX_LEN 200 +struct devtree { + char* vendor; + char* model; +}; + char* read_file(char* path, int* len); long get_max_freq_from_file(uint32_t core); long get_min_freq_from_file(uint32_t core); @@ -43,5 +48,7 @@ int get_ncores_from_cpuinfo(void); char* get_field_from_cpuinfo(char* CPUINFO_FIELD); bool is_devtree_compatible(char* str); +char* get_devtree_compatible(int *filelen); +struct devtree** get_devtree_compatible_struct(int *num_vendors); #endif diff -Nru cpufetch-1.06/src/ppc/ppc.c cpufetch-1.07/src/ppc/ppc.c --- cpufetch-1.06/src/ppc/ppc.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/ppc/ppc.c 2025-10-31 06:50:04.000000000 +0000 @@ -81,9 +81,13 @@ if(!fill_package_ids_from_sys(package_ids, topo->total_cores)) { printWarn("fill_package_ids_from_sys failed, output may be incomplete/invalid"); for(int i=0; i < topo->total_cores; i++) package_ids[i] = 0; - // fill_package_ids_from_sys failed, use a - // more sophisticated wat to find the number of sockets + // fill_package_ids_from_sys failed, use udev to try + // to find the number of sockets topo->sockets = get_num_sockets_package_cpus(topo); + if (topo->sockets == UNKNOWN_DATA) { + printWarn("get_num_sockets_package_cpus failed: assuming 1 socket"); + topo->sockets = 1; + } } else { // fill_package_ids_from_sys succeeded, use the diff -Nru cpufetch-1.06/src/ppc/uarch.c cpufetch-1.07/src/ppc/uarch.c --- cpufetch-1.06/src/ppc/uarch.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/ppc/uarch.c 2025-10-31 06:50:04.000000000 +0000 @@ -25,6 +25,7 @@ UARCH_PPC603, UARCH_PPC440, UARCH_PPC470, + UARCH_ESPRESSO, // Not exactly an uarch, but the codename of Wii U UARCH_PPC970, UARCH_PPC970FX, UARCH_PPC970MP, @@ -75,6 +76,7 @@ FILL_UARCH(arch->uarch, UARCH_PPC603, "PowerPC 603", UNK) // varies FILL_UARCH(arch->uarch, UARCH_PPC440, "PowerPC 440", UNK) FILL_UARCH(arch->uarch, UARCH_PPC470, "PowerPC 470", 45) // strange... + FILL_UARCH(arch->uarch, UARCH_ESPRESSO, "Espresso", 45) // https://en.wikipedia.org/wiki/PowerPC_7xx#Espresso, https://en.wikipedia.org/wiki/Espresso_(processor), https://github.com/Dr-Noob/cpufetch/issues/231 FILL_UARCH(arch->uarch, UARCH_PPC970, "PowerPC 970", 130) FILL_UARCH(arch->uarch, UARCH_PPC970FX, "PowerPC 970FX", 90) FILL_UARCH(arch->uarch, UARCH_PPC970MP, "PowerPC 970MP", 90) @@ -234,6 +236,7 @@ CHECK_UARCH(arch, pvr, 0xffff0000, 0x7ff50000, UARCH_PPC470) CHECK_UARCH(arch, pvr, 0xffff0000, 0x00050000, UARCH_PPC470) CHECK_UARCH(arch, pvr, 0xffff0000, 0x11a50000, UARCH_PPC470) + CHECK_UARCH(arch, pvr, 0xffffffff, 0x70010201, UARCH_ESPRESSO) UARCH_END return arch; diff -Nru cpufetch-1.06/src/ppc/udev.c cpufetch-1.07/src/ppc/udev.c --- cpufetch-1.06/src/ppc/udev.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/ppc/udev.c 2025-10-31 06:50:04.000000000 +0000 @@ -13,7 +13,7 @@ char* buf; char* end; char path[128]; - memset(path, 0, 128); + memset(name, 0, sizeof(char) * 128); for(int i=0; i < total_cores; i++) { sprintf(path, "%s%s/cpu%d/%s", _PATH_SYS_SYSTEM, _PATH_SYS_CPU, i, SYS_PATH); diff -Nru cpufetch-1.06/src/riscv/riscv.c cpufetch-1.07/src/riscv/riscv.c --- cpufetch-1.06/src/riscv/riscv.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/riscv/riscv.c 2025-10-31 06:50:04.000000000 +0000 @@ -12,7 +12,7 @@ #define SET_ISA_EXT_MAP(name, bit) \ if(strncmp(multi_letter_extension, name, \ multi_letter_extension_len) == 0) { \ - ext->mask |= 1UL << bit; \ + ext->mask[bit] = true; \ maskset = true; \ } \ @@ -71,6 +71,65 @@ SET_ISA_EXT_MAP("zicsr", RISCV_ISA_EXT_ZICSR) SET_ISA_EXT_MAP("zifencei", RISCV_ISA_EXT_ZIFENCEI) SET_ISA_EXT_MAP("zihpm", RISCV_ISA_EXT_ZIHPM) + SET_ISA_EXT_MAP("smstateen", RISCV_ISA_EXT_SMSTATEEN) + SET_ISA_EXT_MAP("zicond", RISCV_ISA_EXT_ZICOND) + SET_ISA_EXT_MAP("zbc", RISCV_ISA_EXT_ZBC) + SET_ISA_EXT_MAP("zbkb", RISCV_ISA_EXT_ZBKB) + SET_ISA_EXT_MAP("zbkc", RISCV_ISA_EXT_ZBKC) + SET_ISA_EXT_MAP("zbkx", RISCV_ISA_EXT_ZBKX) + SET_ISA_EXT_MAP("zknd", RISCV_ISA_EXT_ZKND) + SET_ISA_EXT_MAP("zkne", RISCV_ISA_EXT_ZKNE) + SET_ISA_EXT_MAP("zknh", RISCV_ISA_EXT_ZKNH) + SET_ISA_EXT_MAP("zkr", RISCV_ISA_EXT_ZKR) + SET_ISA_EXT_MAP("zksed", RISCV_ISA_EXT_ZKSED) + SET_ISA_EXT_MAP("zksh", RISCV_ISA_EXT_ZKSH) + SET_ISA_EXT_MAP("zkt", RISCV_ISA_EXT_ZKT) + SET_ISA_EXT_MAP("zvbb", RISCV_ISA_EXT_ZVBB) + SET_ISA_EXT_MAP("zvbc", RISCV_ISA_EXT_ZVBC) + SET_ISA_EXT_MAP("zvkb", RISCV_ISA_EXT_ZVKB) + SET_ISA_EXT_MAP("zvkg", RISCV_ISA_EXT_ZVKG) + SET_ISA_EXT_MAP("zvkned", RISCV_ISA_EXT_ZVKNED) + SET_ISA_EXT_MAP("zvknha", RISCV_ISA_EXT_ZVKNHA) + SET_ISA_EXT_MAP("zvknhb", RISCV_ISA_EXT_ZVKNHB) + SET_ISA_EXT_MAP("zvksed", RISCV_ISA_EXT_ZVKSED) + SET_ISA_EXT_MAP("zvksh", RISCV_ISA_EXT_ZVKSH) + SET_ISA_EXT_MAP("zvkt", RISCV_ISA_EXT_ZVKT) + SET_ISA_EXT_MAP("zfh", RISCV_ISA_EXT_ZFH) + SET_ISA_EXT_MAP("zfhmin", RISCV_ISA_EXT_ZFHMIN) + SET_ISA_EXT_MAP("zihintntl", RISCV_ISA_EXT_ZIHINTNTL) + SET_ISA_EXT_MAP("zvfh", RISCV_ISA_EXT_ZVFH) + SET_ISA_EXT_MAP("zvfhmin", RISCV_ISA_EXT_ZVFHMIN) + SET_ISA_EXT_MAP("zfa", RISCV_ISA_EXT_ZFA) + SET_ISA_EXT_MAP("ztso", RISCV_ISA_EXT_ZTSO) + SET_ISA_EXT_MAP("zacas", RISCV_ISA_EXT_ZACAS) + SET_ISA_EXT_MAP("zve32x", RISCV_ISA_EXT_ZVE32X) + SET_ISA_EXT_MAP("zve32f", RISCV_ISA_EXT_ZVE32F) + SET_ISA_EXT_MAP("zve64x", RISCV_ISA_EXT_ZVE64X) + SET_ISA_EXT_MAP("zve64f", RISCV_ISA_EXT_ZVE64F) + SET_ISA_EXT_MAP("zve64d", RISCV_ISA_EXT_ZVE64D) + SET_ISA_EXT_MAP("zimop", RISCV_ISA_EXT_ZIMOP) + SET_ISA_EXT_MAP("zca", RISCV_ISA_EXT_ZCA) + SET_ISA_EXT_MAP("zcb", RISCV_ISA_EXT_ZCB) + SET_ISA_EXT_MAP("zcd", RISCV_ISA_EXT_ZCD) + SET_ISA_EXT_MAP("zcf", RISCV_ISA_EXT_ZCF) + SET_ISA_EXT_MAP("zcmop", RISCV_ISA_EXT_ZCMOP) + SET_ISA_EXT_MAP("zawrs", RISCV_ISA_EXT_ZAWRS) + SET_ISA_EXT_MAP("svvptc", RISCV_ISA_EXT_SVVPTC) + SET_ISA_EXT_MAP("smmpm", RISCV_ISA_EXT_SMMPM) + SET_ISA_EXT_MAP("smnpm", RISCV_ISA_EXT_SMNPM) + SET_ISA_EXT_MAP("ssnpm", RISCV_ISA_EXT_SSNPM) + SET_ISA_EXT_MAP("zabha", RISCV_ISA_EXT_ZABHA) + SET_ISA_EXT_MAP("ziccrse", RISCV_ISA_EXT_ZICCRSE) + SET_ISA_EXT_MAP("svade", RISCV_ISA_EXT_SVADE) + SET_ISA_EXT_MAP("svadu", RISCV_ISA_EXT_SVADU) + SET_ISA_EXT_MAP("zfbfmin", RISCV_ISA_EXT_ZFBFMIN) + SET_ISA_EXT_MAP("zvfbfmin", RISCV_ISA_EXT_ZVFBFMIN) + SET_ISA_EXT_MAP("zvfbfwma", RISCV_ISA_EXT_ZVFBFWMA) + SET_ISA_EXT_MAP("zaamo", RISCV_ISA_EXT_ZAAMO) + SET_ISA_EXT_MAP("zalrsc", RISCV_ISA_EXT_ZALRSC) + SET_ISA_EXT_MAP("zicbop", RISCV_ISA_EXT_ZICBOP) + SET_ISA_EXT_MAP("ime", RISCV_ISA_EXT_IME) + if(!maskset) { printBug("parse_multi_letter_extension: Unknown multi-letter extension: %s", multi_letter_extension); return -1; @@ -93,20 +152,21 @@ struct extensions* get_extensions_from_str(char* str) { struct extensions* ext = emalloc(sizeof(struct extensions)); - ext->mask = 0; + ext->mask = ecalloc(RISCV_ISA_EXT_ID_MAX, sizeof(bool)); ext->str = NULL; if(str == NULL) { return ext; } - int len = sizeof(char) * (strlen(str)+1); - ext->str = emalloc(sizeof(char) * len); - memset(ext->str, 0, len); + int len = strlen(str)+1; + ext->str = emalloc(len * sizeof(char)); strncpy(ext->str, str, sizeof(char) * len); // Code inspired in Linux kernel (riscv_fill_hwcap): // https://elixir.bootlin.com/linux/v6.2.10/source/arch/riscv/kernel/cpufeature.c + // Now it seems to be here in riscv_parse_isa_string: + // https://elixir.bootlin.com/linux/v6.16/source/arch/riscv/kernel/cpufeature.c char* isa = str; if (!strncmp(isa, "rv32", 4)) isa += 4; @@ -138,7 +198,7 @@ // adding it to the mask if(valid_extension(*e)) { int n = *e - 'a'; - ext->mask |= 1UL << n; + ext->mask[n] = true; } else { printBug("get_extensions_from_str: Invalid extension: '%c'", *e); @@ -149,6 +209,18 @@ return ext; } +uint32_t get_num_extensions(bool* mask) { + uint32_t num = 0; + for (int i=0; i < RISCV_ISA_EXT_ID_MAX; i++) { + if (mask[i]) num++; + } + return num; +} + +bool is_mask_empty(bool* mask) { + return get_num_extensions(mask) == 0; +} + struct cpuInfo* get_cpu_info(void) { struct cpuInfo* cpu = malloc(sizeof(struct cpuInfo)); //init_cpu_info(cpu); @@ -157,13 +229,12 @@ topo->cach = NULL; cpu->topo = topo; - char* cpuinfo_str = get_uarch_from_cpuinfo(); char* ext_str = get_extensions_from_cpuinfo(); cpu->hv = emalloc(sizeof(struct hypervisor)); cpu->hv->present = false; cpu->ext = get_extensions_from_str(ext_str); - if(cpu->ext->str != NULL && cpu->ext->mask == 0) return NULL; - cpu->arch = get_uarch_from_cpuinfo_str(cpuinfo_str, cpu); + if(cpu->ext->str != NULL && is_mask_empty(cpu->ext->mask)) return NULL; + cpu->arch = get_uarch(cpu); cpu->soc = get_soc(cpu); cpu->freq = get_frequency_info(0); cpu->peak_performance = get_peak_performance(cpu); diff -Nru cpufetch-1.06/src/riscv/riscv.h cpufetch-1.07/src/riscv/riscv.h --- cpufetch-1.06/src/riscv/riscv.h 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/riscv/riscv.h 2025-10-31 06:50:04.000000000 +0000 @@ -32,11 +32,74 @@ RISCV_ISA_EXT_ZICSR, RISCV_ISA_EXT_ZIFENCEI, RISCV_ISA_EXT_ZIHPM, + RISCV_ISA_EXT_SMSTATEEN, + RISCV_ISA_EXT_ZICOND, + RISCV_ISA_EXT_ZBC, + RISCV_ISA_EXT_ZBKB, + RISCV_ISA_EXT_ZBKC, + RISCV_ISA_EXT_ZBKX, + RISCV_ISA_EXT_ZKND, + RISCV_ISA_EXT_ZKNE, + RISCV_ISA_EXT_ZKNH, + RISCV_ISA_EXT_ZKR, + RISCV_ISA_EXT_ZKSED, + RISCV_ISA_EXT_ZKSH, + RISCV_ISA_EXT_ZKT, + RISCV_ISA_EXT_ZVBB, + RISCV_ISA_EXT_ZVBC, + RISCV_ISA_EXT_ZVKB, + RISCV_ISA_EXT_ZVKG, + RISCV_ISA_EXT_ZVKNED, + RISCV_ISA_EXT_ZVKNHA, + RISCV_ISA_EXT_ZVKNHB, + RISCV_ISA_EXT_ZVKSED, + RISCV_ISA_EXT_ZVKSH, + RISCV_ISA_EXT_ZVKT, + RISCV_ISA_EXT_ZFH, + RISCV_ISA_EXT_ZFHMIN, + RISCV_ISA_EXT_ZIHINTNTL, + RISCV_ISA_EXT_ZVFH, + RISCV_ISA_EXT_ZVFHMIN, + RISCV_ISA_EXT_ZFA, + RISCV_ISA_EXT_ZTSO, + RISCV_ISA_EXT_ZACAS, + RISCV_ISA_EXT_ZVE32X, + RISCV_ISA_EXT_ZVE32F, + RISCV_ISA_EXT_ZVE64X, + RISCV_ISA_EXT_ZVE64F, + RISCV_ISA_EXT_ZVE64D, + RISCV_ISA_EXT_ZIMOP, + RISCV_ISA_EXT_ZCA, + RISCV_ISA_EXT_ZCB, + RISCV_ISA_EXT_ZCD, + RISCV_ISA_EXT_ZCF, + RISCV_ISA_EXT_ZCMOP, + RISCV_ISA_EXT_ZAWRS, + RISCV_ISA_EXT_SVVPTC, + RISCV_ISA_EXT_SMMPM, + RISCV_ISA_EXT_SMNPM, + RISCV_ISA_EXT_SSNPM, + RISCV_ISA_EXT_ZABHA, + RISCV_ISA_EXT_ZICCRSE, + RISCV_ISA_EXT_SVADE, + RISCV_ISA_EXT_SVADU, + RISCV_ISA_EXT_ZFBFMIN, + RISCV_ISA_EXT_ZVFBFMIN, + RISCV_ISA_EXT_ZVFBFWMA, + RISCV_ISA_EXT_ZAAMO, + RISCV_ISA_EXT_ZALRSC, + RISCV_ISA_EXT_ZICBOP, + RISCV_ISA_EXT_IME, // This is not in the kernel! but it was seen on a Muse Pi Pro board RISCV_ISA_EXT_ID_MAX }; // https://five-embeddev.com/riscv-isa-manual/latest/preface.html#preface // https://en.wikichip.org/wiki/risc-v/standard_extensions +// (Zicbop) https://github.com/riscv/riscv-CMOs/blob/master/cmobase/Zicbop.adoc +// https://raw.githubusercontent.com/riscv/riscv-CMOs/master/specifications/cmobase-v1.0.1.pdf +// https://www.kernel.org/doc/Documentation/devicetree/bindings/riscv/extensions.yaml +// https://gcc.gnu.org/onlinedocs/gcc/RISC-V-Options.html +// (Ime) https://github.com/riscv/integrated-matrix-extension (not confirmed, just a guess...) // Included all except for G static const struct extension extension_list[] = { { 'i' - 'a', "(I) Integer Instruction Set" }, @@ -64,6 +127,7 @@ { RISCV_ISA_EXT_ZIHINTPAUSE, "(Zihintpause) Pause Hint" }, { RISCV_ISA_EXT_SVNAPOT, "(Svnapot) Naturally Aligned Power of Two Pages" }, { RISCV_ISA_EXT_ZICBOZ, "(Zicboz) Cache Block Zero Operations" }, + { RISCV_ISA_EXT_ZICBOP, "(Zicbop) Cache Block Prefetch Operations" }, { RISCV_ISA_EXT_SMAIA, "(Smaia) Advanced Interrupt Architecture" }, { RISCV_ISA_EXT_SSAIA, "(Ssaia) Advanced Interrupt Architecture" }, { RISCV_ISA_EXT_ZBA, "(Zba) Address Generation" }, @@ -71,12 +135,71 @@ { RISCV_ISA_EXT_ZICNTR, "(Zicntr) Base Counters and Timers" }, { RISCV_ISA_EXT_ZICSR, "(Zicsr) Control and Status Register" }, { RISCV_ISA_EXT_ZIFENCEI, "(Zifencei) Instruction-Fetch Fence" }, - { RISCV_ISA_EXT_ZIHPM, "(Zihpm) Hardware Performance Counters" } + { RISCV_ISA_EXT_ZIHPM, "(Zihpm) Hardware Performance Counters" }, + { RISCV_ISA_EXT_SMSTATEEN, "(Smstateen) Supervisor/Hypervisor State Enable" }, + { RISCV_ISA_EXT_ZICOND, "(Zicond) Integer Conditional Operations" }, + { RISCV_ISA_EXT_ZBC, "(Zbc) Carry-Less Multiplication" }, + { RISCV_ISA_EXT_ZBKB, "(Zbkb) Bit-Manipulation for Cryptography (Byte ops)" }, + { RISCV_ISA_EXT_ZBKC, "(Zbkc) Bit-Manipulation for Cryptography (Carry-less ops)" }, + { RISCV_ISA_EXT_ZBKX, "(Zbkx) Bit-Manipulation for Cryptography (Crossbar ops)" }, + { RISCV_ISA_EXT_ZKND, "(Zknd) NIST AES Decryption Instructions" }, + { RISCV_ISA_EXT_ZKNE, "(Zkne) NIST AES Encryption Instructions" }, + { RISCV_ISA_EXT_ZKNH, "(Zknh) NIST Hash (SHA-2/SHA-3) Instructions" }, + { RISCV_ISA_EXT_ZKR, "(Zkr) Entropy Source Reading (Random)" }, + { RISCV_ISA_EXT_ZKSED, "(Zksed) SM4 Block Cipher Decryption" }, + { RISCV_ISA_EXT_ZKSH, "(Zksh) SM3 Hash Instructions" }, + { RISCV_ISA_EXT_ZKT, "(Zkt) Data-Independent Execution Latency" }, + { RISCV_ISA_EXT_ZVBB, "(Zvbb) Vector Basic Bit-Manipulation" }, + { RISCV_ISA_EXT_ZVBC, "(Zvbc) Vector Carry-Less Multiplication" }, + { RISCV_ISA_EXT_ZVKB, "(Zvkb) Vector Cryptography (Byte ops)" }, + { RISCV_ISA_EXT_ZVKG, "(Zvkg) Vector GCM/GMAC Instructions" }, + { RISCV_ISA_EXT_ZVKNED, "(Zvkned) Vector AES Decryption" }, + { RISCV_ISA_EXT_ZVKNHA, "(Zvknha) Vector SHA-2 Hash (A variant)" }, + { RISCV_ISA_EXT_ZVKNHB, "(Zvknhb) Vector SHA-2 Hash (B variant)" }, + { RISCV_ISA_EXT_ZVKSED, "(Zvksed) Vector SM4 Block Cipher Decryption" }, + { RISCV_ISA_EXT_ZVKSH, "(Zvksh) Vector SM3 Hash Instructions" }, + { RISCV_ISA_EXT_ZVKT, "(Zvkt) Vector Data-Independent Execution Latency" }, + { RISCV_ISA_EXT_ZFH, "(Zfh) Half-Precision Floating Point" }, + { RISCV_ISA_EXT_ZFHMIN, "(Zfhmin) Minimal Half-Precision Floating Point" }, + { RISCV_ISA_EXT_ZIHINTNTL, "(Zihintntl) Non-Temporal Load/Store Hints" }, + { RISCV_ISA_EXT_ZVFH, "(Zvfh) Vector Half-Precision Floating Point" }, + { RISCV_ISA_EXT_ZVFHMIN, "(Zvfhmin) Minimal Vector Half-Precision Floating Point" }, + { RISCV_ISA_EXT_ZFA, "(Zfa) Additional Floating-Point Instructions" }, + { RISCV_ISA_EXT_ZTSO, "(Ztso) Total Store Ordering Memory Model" }, + { RISCV_ISA_EXT_ZACAS, "(Zacas) Atomic Compare-and-Swap" }, + { RISCV_ISA_EXT_ZVE32X, "(Zve32x) Embedded Vector Integer (32-bit elements)" }, + { RISCV_ISA_EXT_ZVE32F, "(Zve32f) Embedded Vector Floating Point (f32)" }, + { RISCV_ISA_EXT_ZVE64X, "(Zve64x) Embedded Vector Integer (64-bit elements)" }, + { RISCV_ISA_EXT_ZVE64F, "(Zve64f) Embedded Vector Floating Point (f64)" }, + { RISCV_ISA_EXT_ZVE64D, "(Zve64d) Embedded Vector Double-Precision FP (f64)" }, + { RISCV_ISA_EXT_ZIMOP, "(Zimop) Integer Multiply-Only Instructions" }, + { RISCV_ISA_EXT_ZCA, "(Zca) Compressed Integer Instructions" }, + { RISCV_ISA_EXT_ZCB, "(Zcb) Compressed Bit-Manipulation Instructions" }, + { RISCV_ISA_EXT_ZCD, "(Zcd) Compressed Double-Precision FP Instructions" }, + { RISCV_ISA_EXT_ZCF, "(Zcf) Compressed Single-Precision FP Instructions" }, + { RISCV_ISA_EXT_ZCMOP, "(Zcmop) Compressed Multiply-Only Instructions" }, + { RISCV_ISA_EXT_ZAWRS, "(Zawrs) Wait-on-Reservation-Set Instruction" }, + { RISCV_ISA_EXT_SVVPTC, "(Svvptc) Supervisor Virtual Page Table Cache Control" }, + { RISCV_ISA_EXT_SMMPM, "(Smmpm) Supervisor Memory Protection Modification" }, + { RISCV_ISA_EXT_SMNPM, "(Smnpm) Supervisor Non-Privileged Memory Access Control" }, + { RISCV_ISA_EXT_SSNPM, "(Ssnpm) Supervisor Secure Non-Privileged Memory" }, + { RISCV_ISA_EXT_ZABHA, "(Zabha) Atomic Byte/Halfword Operations" }, + { RISCV_ISA_EXT_ZICCRSE, "(Ziccrse) Cache Control Range Start/End Operations" }, + { RISCV_ISA_EXT_SVADE, "(Svade) Supervisor Virtual Address Deferred Exception" }, + { RISCV_ISA_EXT_SVADU, "(Svadu) Supervisor Virtual Address Dirty Update" }, + { RISCV_ISA_EXT_ZFBFMIN, "(Zfbfmin) Minimal BFloat16 Floating Point" }, + { RISCV_ISA_EXT_ZVFBFMIN, "(Zvfbfmin) Vector Minimal BFloat16 Floating Point" }, + { RISCV_ISA_EXT_ZVFBFWMA, "(Zvfbfwma) Vector BFloat16 Widening Multiply-Accumulate" }, + { RISCV_ISA_EXT_ZAAMO, "(Zaamo) Atomic Memory Operation (AMO) Instructions" }, + { RISCV_ISA_EXT_ZALRSC, "(Zalrsc) Atomic Load-Reserved/Store-Conditional" }, + { RISCV_ISA_EXT_ZICBOP, "(Zicbop) Cache Block Prefetch/Zero Operations" }, + { RISCV_ISA_EXT_IME, "(Ime) Integrated Matrix Extension" }, }; struct cpuInfo* get_cpu_info(void); char* get_str_topology(struct cpuInfo* cpu, struct topology* topo); char* get_str_extensions(struct cpuInfo* cpu); +uint32_t get_num_extensions(bool* mask); void print_debug(struct cpuInfo* cpu); #endif diff -Nru cpufetch-1.06/src/riscv/soc.c cpufetch-1.07/src/riscv/soc.c --- cpufetch-1.06/src/riscv/soc.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/riscv/soc.c 2025-10-31 06:50:04.000000000 +0000 @@ -12,7 +12,7 @@ /*if((tmp = strstr(soc_name, "???")) == NULL) return false;*/ - //soc->soc_vendor = ??? + //soc->vendor = ??? SOC_START SOC_EQ(tmp, "fu740", "Freedom U740", SOC_SIFIVE_U740, soc, 40) @@ -38,6 +38,12 @@ SOC_END } +bool match_spacemit(char* soc_name, struct system_on_chip* soc) { + SOC_START + SOC_EQ(soc_name, "k1-x", "K1-X", SOC_SPACEMIT_K1X, soc, 22) // https://github.com/Dr-Noob/cpufetch/issues/286 https://www.spacemit.com/en/spacemit-x60-core/ + SOC_END +} + struct system_on_chip* parse_soc_from_string(struct system_on_chip* soc) { char* raw_name = soc->raw_name; @@ -50,6 +56,9 @@ if(match_sifive(raw_name, soc)) return soc; + if(match_spacemit(raw_name, soc)) + return soc; + match_sipeed(raw_name, soc); return soc; } @@ -68,12 +77,12 @@ struct system_on_chip* get_soc(struct cpuInfo* cpu) { struct system_on_chip* soc = emalloc(sizeof(struct system_on_chip)); soc->raw_name = NULL; - soc->soc_vendor = SOC_VENDOR_UNKNOWN; - soc->soc_model = SOC_MODEL_UNKNOWN; + soc->vendor = SOC_VENDOR_UNKNOWN; + soc->model = SOC_MODEL_UNKNOWN; soc->process = UNKNOWN; soc = guess_soc_from_devtree(soc); - if(soc->soc_vendor == SOC_VENDOR_UNKNOWN) { + if(soc->vendor == SOC_VENDOR_UNKNOWN) { if(soc->raw_name != NULL) { printWarn("SoC detection failed using device tree: Found '%s' string", soc->raw_name); } @@ -82,7 +91,7 @@ } } - if(soc->soc_model == SOC_MODEL_UNKNOWN) { + if(soc->model == SOC_MODEL_UNKNOWN) { // raw_name might not be NULL, but if we were unable to find // the exact SoC, just print "Unkwnown" soc->raw_name = emalloc(sizeof(char) * (strlen(STRING_UNKNOWN)+1)); diff -Nru cpufetch-1.06/src/riscv/socs.h cpufetch-1.07/src/riscv/socs.h --- cpufetch-1.06/src/riscv/socs.h 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/riscv/socs.h 2025-10-31 06:50:04.000000000 +0000 @@ -13,6 +13,8 @@ SOC_ALLWINNER_D1H, // SIPEED SOC_SIPEED_LICHEEPI4A, + // SPACEMIT + SOC_SPACEMIT_K1X, // UNKNOWN SOC_MODEL_UNKNOWN }; @@ -22,6 +24,7 @@ if(soc >= SOC_STARFIVE_VF2 && soc <= SOC_STARFIVE_VF2) return SOC_VENDOR_STARFIVE; if(soc >= SOC_ALLWINNER_D1H && soc <= SOC_ALLWINNER_D1H) return SOC_VENDOR_ALLWINNER; if(soc >= SOC_SIPEED_LICHEEPI4A && soc <= SOC_SIPEED_LICHEEPI4A) return SOC_VENDOR_SIPEED; + if(soc >= SOC_SPACEMIT_K1X && soc <= SOC_SPACEMIT_K1X) return SOC_VENDOR_SPACEMIT; return SOC_VENDOR_UNKNOWN; } diff -Nru cpufetch-1.06/src/riscv/uarch.c cpufetch-1.07/src/riscv/uarch.c --- cpufetch-1.06/src/riscv/uarch.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/riscv/uarch.c 2025-10-31 06:50:04.000000000 +0000 @@ -4,6 +4,7 @@ #include #include "uarch.h" +#include "udev.h" #include "../common/global.h" typedef uint32_t MICROARCH; @@ -12,6 +13,7 @@ MICROARCH uarch; char* uarch_str; char* cpuinfo_str; + struct riscv_cpuinfo* ci; }; enum { @@ -21,13 +23,20 @@ UARCH_U74, // THEAD UARCH_C906, - UARCH_C910 + UARCH_C910, + // SPACEMIT + UARCH_X60 }; #define UARCH_START if (false) {} #define CHECK_UARCH(arch, cpu, cpuinfo_str, uarch_str, str, uarch, vendor) \ else if (strcmp(cpuinfo_str, uarch_str) == 0) fill_uarch(arch, cpu, str, uarch, vendor); -#define UARCH_END else { printBug("Unknown microarchitecture detected: uarch='%s'", cpuinfo_str); fill_uarch(arch, cpu, "Unknown", UARCH_UNKNOWN, CPU_VENDOR_UNKNOWN); } +#define UARCH_END else { printWarn("Unknown microarchitecture detected: uarch='%s'", cpuinfo_str); fill_uarch(arch, cpu, "Unknown", UARCH_UNKNOWN, CPU_VENDOR_UNKNOWN); } + +#define ARCHID_START if (false) {} +#define CHECK_ARCHID(arch, marchid_val, str, uarch, vendor) \ + else if (arch->ci->marchid == (unsigned long) marchid_val) fill_uarch(arch, cpu, str, uarch, vendor); +#define ARCHID_END else { printWarn("Unknown microarchitecture detected: marchid=0x%.8X", arch->ci->marchid); fill_uarch(arch, cpu, "Unknown", UARCH_UNKNOWN, CPU_VENDOR_UNKNOWN); } void fill_uarch(struct uarch* arch, struct cpuInfo* cpu, char* str, MICROARCH u, VENDOR vendor) { arch->uarch = u; @@ -39,14 +48,8 @@ // https://elixir.bootlin.com/linux/latest/source/Documentation/devicetree/bindings/riscv/cpus.yaml // SiFive: https://www.sifive.com/risc-v-core-ip // T-Head: https://www.t-head.cn/product/c906 -struct uarch* get_uarch_from_cpuinfo_str(char* cpuinfo_str, struct cpuInfo* cpu) { - struct uarch* arch = emalloc(sizeof(struct uarch)); +struct uarch* get_uarch_from_cpuinfo_str(char* cpuinfo_str, struct cpuInfo* cpu, struct uarch* arch) { arch->cpuinfo_str = cpuinfo_str; - if(cpuinfo_str == NULL) { - printWarn("get_uarch_from_cpuinfo: Unable to detect microarchitecture, cpuinfo_str is NULL"); - fill_uarch(arch, cpu, "Unknown", UARCH_UNKNOWN, CPU_VENDOR_UNKNOWN); - return arch; - } // U74/U74-MC: // SiFive says that U74-MC is "Multicore: four U74 cores and one S76 core" while @@ -69,6 +72,41 @@ return arch; } + +// Use marchid to get the microarchitecture +struct uarch* get_uarch_from_riscv_cpuinfo(struct cpuInfo* cpu, struct uarch* arch) { + + ARCHID_START + CHECK_ARCHID(arch, 0x8000000058000001, "X60", UARCH_X60, CPU_VENDOR_SPACEMIT) // https://github.com/Dr-Noob/cpufetch/issues/286 + ARCHID_END + + return arch; +} + +struct uarch* get_uarch(struct cpuInfo* cpu) { + char* cpuinfo_str = get_uarch_from_cpuinfo(); + struct uarch* arch = emalloc(sizeof(struct uarch)); + arch->uarch = UARCH_UNKNOWN; + arch->ci = NULL; + + if (cpuinfo_str == NULL) { + printWarn("get_uarch_from_cpuinfo: Unable to detect microarchitecture using uarch: cpuinfo_str is NULL"); + arch->ci = get_riscv_cpuinfo(); + + if (arch->ci == NULL || arch->ci->marchid == 0) + printWarn("get_riscv_cpuinfo: Unable to get marchid from udev"); + else + arch = get_uarch_from_riscv_cpuinfo(cpu, arch); + } + else { + arch = get_uarch_from_cpuinfo_str(cpuinfo_str, cpu, arch); + } + + if (arch->uarch == UARCH_UNKNOWN) + fill_uarch(arch, cpu, "Unknown", UARCH_UNKNOWN, CPU_VENDOR_UNKNOWN); + + return arch; +} char* get_str_uarch(struct cpuInfo* cpu) { return cpu->arch->uarch_str; diff -Nru cpufetch-1.06/src/riscv/uarch.h cpufetch-1.07/src/riscv/uarch.h --- cpufetch-1.06/src/riscv/uarch.h 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/riscv/uarch.h 2025-10-31 06:50:04.000000000 +0000 @@ -9,6 +9,6 @@ char* get_arch_cpuinfo_str(struct cpuInfo* cpu); char* get_str_uarch(struct cpuInfo* cpu); void free_uarch_struct(struct uarch* arch); -struct uarch* get_uarch_from_cpuinfo_str(char* cpuinfo_str, struct cpuInfo* cpu); +struct uarch* get_uarch(struct cpuInfo* cpu); #endif diff -Nru cpufetch-1.06/src/riscv/udev.c cpufetch-1.07/src/riscv/udev.c --- cpufetch-1.06/src/riscv/udev.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/riscv/udev.c 2025-10-31 06:50:04.000000000 +0000 @@ -7,6 +7,9 @@ #define _PATH_DEVTREE "/proc/device-tree/compatible" #define CPUINFO_UARCH_STR "uarch\t\t: " #define CPUINFO_EXTENSIONS_STR "isa\t\t: " +#define CPUINFO_RISCV_MVENDORID "mvendorid\t:" +#define CPUINFO_RISCV_MARCHID "marchid\t\t:" +#define CPUINFO_RISCV_MIMPID "mimpid\t\t:" #define DEVTREE_HARDWARE_FIELD 0 char* get_field_from_devtree(int DEVTREE_FIELD) { @@ -40,8 +43,7 @@ tmp1++; int strlen = filelen-(tmp1-buf); - char* hardware = emalloc(sizeof(char) * strlen); - memset(hardware, 0, sizeof(char) * strlen); + char* hardware = ecalloc(strlen, sizeof(char)); strncpy(hardware, tmp1, strlen-1); return hardware; @@ -70,13 +72,58 @@ } int ret_strlen = (end-tmp); - char* ret = emalloc(sizeof(char) * (ret_strlen+1)); - memset(ret, 0, sizeof(char) * (ret_strlen+1)); - strncpy(ret, tmp, ret_strlen); + char* ret = ecalloc(ret_strlen+1, sizeof(char)); + strncpy(ret, tmp, sizeof(char) * ret_strlen); return ret; } +unsigned long parse_cpuinfo_field_uint64(char* field_str) { + int filelen; + char* buf; + if((buf = read_file(_PATH_CPUINFO, &filelen)) == NULL) { + printWarn("read_file: %s: %s", _PATH_CPUINFO, strerror(errno)); + return 0; + } + + char* tmp = strstr(buf, field_str); + if(tmp == NULL) return 0; + tmp += strlen(field_str); + + char* end; + errno = 0; + unsigned long ret = strtoul(tmp, &end, 16); + if (errno != 0) { + printWarn("strtoul: %s: %s", strerror(errno), tmp); + return 0; + } + + return ret; +} + +// Creates and fills in the riscv_cpuinfo struct (which contains +// mvendorid, marchid and mimpid) using cpuinfo to fetch the values. +// +// Every RISC-V hart (hardware thread) [1] provides a +// marchid (Machine Architecture ID register) CSR that encodes its +// base microarchitecture [2]. For more information about +// marchid and the rest of values, see [3]. +// [1] https://groups.google.com/a/groups.riscv.org/g/sw-dev/c/QKjUDjz_vKo +// [2] https://github.com/riscv/riscv-isa-manual/blob/main/marchid.md +// [3] https://five-embeddev.com/riscv-priv-isa-manual/Priv-v1.12/machine.html#machine-architecture-id-register-marchid +struct riscv_cpuinfo *get_riscv_cpuinfo(void) { + struct riscv_cpuinfo* ci = emalloc(sizeof(struct riscv_cpuinfo)); + + ci->mvendorid = parse_cpuinfo_field_uint64(CPUINFO_RISCV_MVENDORID); + ci->marchid = parse_cpuinfo_field_uint64(CPUINFO_RISCV_MARCHID); + ci->mimpid = parse_cpuinfo_field_uint64(CPUINFO_RISCV_MIMPID); + + if (ci->mvendorid == 0 && ci->mvendorid == 0 && ci->mvendorid == 0) + return NULL; + + return ci; +} + char* get_hardware_from_devtree(void) { return get_field_from_devtree(DEVTREE_HARDWARE_FIELD); } diff -Nru cpufetch-1.06/src/riscv/udev.h cpufetch-1.07/src/riscv/udev.h --- cpufetch-1.06/src/riscv/udev.h 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/riscv/udev.h 2025-10-31 06:50:04.000000000 +0000 @@ -5,8 +5,16 @@ #define UNKNOWN -1 +// https://elixir.bootlin.com/linux/v6.10.6/source/arch/riscv/include/asm/cpufeature.h#L21 +struct riscv_cpuinfo { + unsigned long mvendorid; + unsigned long marchid; + unsigned long mimpid; +}; + char* get_hardware_from_devtree(void); char* get_uarch_from_cpuinfo(void); char* get_extensions_from_cpuinfo(void); +struct riscv_cpuinfo *get_riscv_cpuinfo(void); #endif diff -Nru cpufetch-1.06/src/x86/apic.c cpufetch-1.07/src/x86/apic.c --- cpufetch-1.06/src/x86/apic.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/x86/apic.c 2025-10-31 06:50:04.000000000 +0000 @@ -234,15 +234,11 @@ bool build_topo_from_apic(uint32_t* apic_pkg, uint32_t* apic_smt, uint32_t** cache_id_apic, struct topology* topo) { uint32_t size = max_apic_id_size(cache_id_apic, topo); - uint32_t* sockets = emalloc(sizeof(uint32_t) * size); - uint32_t* smt = emalloc(sizeof(uint32_t) * size); - uint32_t* apic_id = emalloc(sizeof(uint32_t) * size); + uint32_t* sockets = ecalloc(size, sizeof(uint32_t)); + uint32_t* smt = ecalloc(size, sizeof(uint32_t)); + uint32_t* apic_id = ecalloc(size, sizeof(uint32_t)); uint32_t num_caches = 0; - memset(sockets, 0, sizeof(uint32_t) * size); - memset(smt, 0, sizeof(uint32_t) * size); - memset(apic_id, 0, sizeof(uint32_t) * size); - // System topology for(int i=0; i < topo->total_cores_module; i++) { sockets[apic_pkg[i]] = 1; diff -Nru cpufetch-1.06/src/x86/cpuid.c cpufetch-1.07/src/x86/cpuid.c --- cpufetch-1.06/src/x86/cpuid.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/x86/cpuid.c 2025-10-31 06:50:04.000000000 +0000 @@ -5,6 +5,9 @@ #include "../common/udev.h" #include #endif +#if defined (__FreeBSD__) || defined (__APPLE__) + #include "../common/sysctl.h" +#endif #ifdef __linux__ #include "../common/freq.h" @@ -88,8 +91,7 @@ uint32_t edx = 0; uint32_t c = 0; - char * name = emalloc(sizeof(char) * CPU_NAME_MAX_LENGTH); - memset(name, 0, CPU_NAME_MAX_LENGTH); + char * name = ecalloc(CPU_NAME_MAX_LENGTH, sizeof(char)); for(int i=0; i < 3; i++) { eax = 0x80000002 + i; @@ -208,18 +210,14 @@ for(int i=0; i < cpu->num_cpus; ptr = ptr->next_cpu, i++) { struct topology* topo = ptr->topo; - int64_t max_freq = get_freq(ptr->freq); + int64_t freq = get_freq(ptr->freq); - int64_t freq; #ifdef __linux__ if(accurate_pp) - freq = measure_frequency(ptr); - else - freq = max_freq; + freq = get_freq_pp(ptr->freq); #else // Silence compiler warning (void)(accurate_pp); - freq = max_freq; #endif //First, check we have consistent data @@ -278,7 +276,7 @@ } else { char name[13]; - memset(name, 0, 13); + memset(name, 0, sizeof(char) * 13); get_name_cpuid(name, ebx, ecx, edx); bool found = false; @@ -335,6 +333,15 @@ bool hv_present = (ecx & (1U << 31)) != 0; if((cpu->hv = get_hp_info(hv_present)) == NULL) return NULL; + if(cpu->hv->present) { + // Hypervisor will likely mess up something and users will think that + // there is something wrong with cpufetch whereas actually cpufetch has + // nothing to do with it. + // https://github.com/Dr-Noob/cpufetch/issues/96 + // https://github.com/Dr-Noob/cpufetch/issues/267 + // https://github.com/Dr-Noob/cpufetch/issues/293 + printWarn("You are running an hypervisor. Please note that it will likely tamper your results, so do not post an issue if you find anything incorrect"); + } } else { printWarn("Can't read features information from cpuid (needed level is 0x%.8X, max is 0x%.8X)", 0x00000001, cpu->maxLevels); @@ -448,6 +455,23 @@ } } +#ifdef __linux__ +// Gets the max frequency for estimating the peak performance, +// filling in the passed cpuInfo parameter with this information. +void fill_frequency_info_pp(struct cpuInfo* cpu) { + int32_t unused; + int32_t *max_freq_pp_vec = malloc(sizeof(int32_t) * cpu->num_cpus); + struct cpuInfo* ptr = cpu; + + for (uint32_t i=0; i < cpu->num_cpus; i++) { + set_cpu_module(i, cpu->num_cpus, &unused); + + ptr->freq->max_pp = measure_frequency(ptr, max_freq_pp_vec); + ptr = ptr->next_cpu; + } +} +#endif + struct cpuInfo* get_cpu_info(void) { struct cpuInfo* cpu = emalloc(sizeof(struct cpuInfo)); cpu->peak_performance = -1; @@ -468,7 +492,7 @@ //Fill vendor char name[13]; - memset(name,0,13); + memset(name, 0, sizeof(char) * 13); get_name_cpuid(name, ebx, edx, ecx); if(strcmp(CPU_VENDOR_INTEL_STRING,name) == 0) @@ -544,6 +568,7 @@ ptr->core_type = get_core_type(); } ptr->first_core_id = first_core; + ptr->module_id = i; ptr->feat = get_features_info(ptr); ptr->arch = get_cpu_uarch(ptr); @@ -568,6 +593,13 @@ if(ptr->topo == NULL) return cpu; } +#ifdef __linux__ + // If accurate_pp is requested, we need to get the max frequency + // after fetching the topology for all CPU modules, since the topology + // is required by fill_frequency_info_pp + if (accurate_pp()) fill_frequency_info_pp(cpu); +#endif + cpu->peak_performance = get_peak_performance(cpu, accurate_pp()); return cpu; @@ -938,10 +970,20 @@ freq->measured = false; if(cpu->maxLevels < 0x00000016) { - #if defined (_WIN32) || defined (__APPLE__) + #if defined (_WIN32) printWarn("Can't read frequency information from cpuid (needed level is 0x%.8X, max is 0x%.8X)", 0x00000016, cpu->maxLevels); freq->base = UNKNOWN_DATA; freq->max = UNKNOWN_DATA; + #elif defined (__FreeBSD__) || defined (__APPLE__) + printWarn("Can't read frequency information from cpuid (needed level is 0x%.8X, max is 0x%.8X). Using sysctl", 0x00000016, cpu->maxLevels); + uint32_t freq_hz = get_sys_info_by_name(CPUFREQUENCY_SYSCTL); + if (freq_hz == 0) { + printWarn("Read max CPU frequency from sysctl and got 0 MHz"); + freq->max = UNKNOWN_DATA; + } + + freq->base = UNKNOWN_DATA; + freq->max = freq_hz; #else printWarn("Can't read frequency information from cpuid (needed level is 0x%.8X, max is 0x%.8X). Using udev", 0x00000016, cpu->maxLevels); freq->base = UNKNOWN_DATA; @@ -993,6 +1035,7 @@ } #endif + freq->max_pp = UNKNOWN_DATA; return freq; } diff -Nru cpufetch-1.06/src/x86/freq/freq.c cpufetch-1.07/src/x86/freq/freq.c --- cpufetch-1.06/src/x86/freq/freq.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/x86/freq/freq.c 2025-10-31 06:50:04.000000000 +0000 @@ -21,9 +21,12 @@ #define FREQ_VECTOR_SIZE 1<<16 struct freq_thread { + // Inputs + struct cpuInfo* cpu; bool end; bool measure; - double freq; + // Output + int32_t *max_pp; }; double vector_average_harmonic(double* v, int len) { @@ -48,6 +51,7 @@ char* line = NULL; size_t len = 0; ssize_t read; + struct cpuInfo* cpu = freq->cpu; int v = 0; double* freq_vector = malloc(sizeof(double) * FREQ_VECTOR_SIZE); @@ -76,18 +80,43 @@ sleep_ms(500); } - freq->freq = vector_average_harmonic(freq_vector, v); - printWarn("AVX2 measured freq=%f\n", freq->freq); + if (cpu->hybrid_flag) { + // We have an heterogeneous architecture. After measuring the + // frequency for all cores, we now need to compute the average + // independently for each CPU module. + struct cpuInfo* ptr = cpu; + double* freq_vector_ptr = freq_vector; + + for (int i=0; i < cpu->num_cpus; ptr = ptr->next_cpu, i++) { + freq->max_pp[i] = vector_average_harmonic(freq_vector_ptr, ptr->topo->total_cores_module); + printWarn("AVX2 measured freq=%d (module %d)", freq->max_pp[i], i); + + freq_vector_ptr = freq_vector_ptr + ptr->topo->total_cores_module; + } + } + else { + freq->max_pp[0] = vector_average_harmonic(freq_vector, v); + printWarn("AVX2 measured freq=%d\n", freq->max_pp[0]); + } return NULL; } -int64_t measure_frequency(struct cpuInfo* cpu) { +int32_t measure_frequency(struct cpuInfo* cpu, int32_t *max_freq_pp_vec) { + if (cpu->hybrid_flag && cpu->module_id > 0) { + // We have a hybrid architecture and we have already + // measured the frequency for this module in a previous + // call to this function, so now just return it. + return max_freq_pp_vec[cpu->module_id]; + } + int ret; int num_spaces; struct freq_thread* freq_struct = malloc(sizeof(struct freq_thread)); freq_struct->end = false; freq_struct->measure = false; + freq_struct->cpu = cpu; + freq_struct->max_pp = max_freq_pp_vec; void* (*compute_function)(void*); @@ -116,8 +145,25 @@ } pthread_t* compute_th = malloc(sizeof(pthread_t) * cpu->topo->total_cores); + cpu_set_t cpus; + pthread_attr_t attr; + if ((ret = pthread_attr_init(&attr)) != 0) { + printErr("pthread_attr_init: %s", strerror(ret)); + return -1; + } + for(int i=0; i < cpu->topo->total_cores; i++) { - ret = pthread_create(&compute_th[i], NULL, compute_function, NULL); + // We might have called bind_to_cpu previously, binding the threads + // to a specific core, so now we must make sure we run the new thread + // on the correct core. + CPU_ZERO(&cpus); + CPU_SET(i, &cpus); + if ((ret = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpus)) != 0) { + printErr("pthread_attr_setaffinity_np: %s", strerror(ret)); + return -1; + } + + ret = pthread_create(&compute_th[i], &attr, compute_function, NULL); if(ret != 0) { fprintf(stderr, "Error creating thread\n"); @@ -142,5 +188,5 @@ } printf("\r%*c", num_spaces, ' '); - return freq_struct->freq; + return max_freq_pp_vec[0]; } diff -Nru cpufetch-1.06/src/x86/freq/freq.h cpufetch-1.07/src/x86/freq/freq.h --- cpufetch-1.06/src/x86/freq/freq.h 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/x86/freq/freq.h 2025-10-31 06:50:04.000000000 +0000 @@ -8,6 +8,6 @@ #define MEASURE_TIME_SECONDS 5 #define LOOP_ITERS 100000000 -int64_t measure_frequency(struct cpuInfo* cpu); +int32_t measure_frequency(struct cpuInfo* cpu, int32_t *max_freq_pp_vec); #endif diff -Nru cpufetch-1.06/src/x86/uarch.c cpufetch-1.07/src/x86/uarch.c --- cpufetch-1.06/src/x86/uarch.c 2024-08-18 14:12:27.000000000 +0000 +++ cpufetch-1.07/src/x86/uarch.c 2025-10-31 06:50:04.000000000 +0000 @@ -47,8 +47,10 @@ enum { UARCH_UNKNOWN, // INTEL // + UARCH_I486, UARCH_P5, UARCH_P5_MMX, + UARCH_P6_PRO, UARCH_P6_PENTIUM_II, UARCH_P6_PENTIUM_III, UARCH_DOTHAN, @@ -91,12 +93,15 @@ UARCH_CEDAR_MILL, UARCH_ITANIUM2, UARCH_ICE_LAKE, + UARCH_SAPPHIRE_RAPIDS, UARCH_TIGER_LAKE, UARCH_ALDER_LAKE, UARCH_RAPTOR_LAKE, // AMD // UARCH_AM486, UARCH_AM5X86, + UARCH_SSA5, + UARCH_K5, UARCH_K6, UARCH_K7, UARCH_K8, @@ -115,7 +120,9 @@ UARCH_ZEN3, UARCH_ZEN3_PLUS, UARCH_ZEN4, - UARCH_ZEN4C + UARCH_ZEN4C, + UARCH_ZEN5, + UARCH_ZEN5C, }; struct uarch { @@ -149,16 +156,30 @@ // ------------------------------------------------------------------------------- // // EF F EM M S // UARCH_START + CHECK_UARCH(arch, 0, 4, 0, 0, NA, "i80486DX", UARCH_I486, UNK) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 4, 0, 1, NA, "i80486DX-50", UARCH_I486, UNK) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 4, 0, 2, NA, "i80486SX", UARCH_I486, UNK) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 4, 0, 3, NA, "i80486DX2", UARCH_I486, UNK) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 4, 0, 4, NA, "i80486SL", UARCH_I486, UNK) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 4, 0, 5, NA, "i80486SX2", UARCH_I486, UNK) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 4, 0, 7, NA, "i80486DX2WB", UARCH_I486, UNK) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 4, 0, 8, NA, "i80486DX4", UARCH_I486, UNK) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 4, 0, 9, NA, "i80486DX4WB", UARCH_I486, UNK) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h CHECK_UARCH(arch, 0, 5, 0, 0, NA, "P5", UARCH_P5, 800) CHECK_UARCH(arch, 0, 5, 0, 1, NA, "P5", UARCH_P5, 800) - CHECK_UARCH(arch, 0, 5, 0, 2, NA, "P5", UARCH_P5, UNK) - CHECK_UARCH(arch, 0, 5, 0, 3, NA, "P5", UARCH_P5, 600) - CHECK_UARCH(arch, 0, 5, 0, 4, NA, "P5 (MMX)", UARCH_P5_MMX, UNK) - CHECK_UARCH(arch, 0, 5, 0, 7, NA, "P5 (MMX)", UARCH_P5_MMX, UNK) - CHECK_UARCH(arch, 0, 5, 0, 8, NA, "P5 (MMX)", UARCH_P5_MMX, 250) + CHECK_UARCH(arch, 0, 5, 0, 2, NA, "P54C", UARCH_P5, UNK) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 5, 0, 3, NA, "P24T (Overdrive)", UARCH_P5, 600) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 5, 0, 4, NA, "P55C (MMX)", UARCH_P5_MMX, 350) // https://www.cpu-world.com/CPUs/Pentium/TYPE-Pentium%20MMX.html + CHECK_UARCH(arch, 0, 5, 0, 7, NA, "P54C", UARCH_P5, 350) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 5, 0, 8, NA, "Tillamook", UARCH_P5_MMX, 250) // http://instlatx64.atw.hu./ CHECK_UARCH(arch, 0, 5, 0, 9, 0, "Lakemont", UARCH_LAKEMONT, 32) CHECK_UARCH(arch, 0, 5, 0, 9, NA, "P5 (MMX)", UARCH_P5_MMX, UNK) CHECK_UARCH(arch, 0, 5, 0, 10, 0, "Lakemont", UARCH_LAKEMONT, 32) + CHECK_UARCH(arch, 0, 6, 0, 1, 1, "P6", UARCH_P6_PRO, UNK) + CHECK_UARCH(arch, 0, 6, 0, 1, 2, "P6", UARCH_P6_PRO, UNK) + CHECK_UARCH(arch, 0, 6, 0, 1, 6, "P6", UARCH_P6_PRO, 350) + CHECK_UARCH(arch, 0, 6, 0, 1, 7, "P6", UARCH_P6_PRO, 350) + CHECK_UARCH(arch, 0, 6, 0, 1, 9, "P6", UARCH_P6_PRO, 350) CHECK_UARCH(arch, 0, 6, 0, 0, NA, "P6 (Pentium II)", UARCH_P6_PENTIUM_II, UNK) CHECK_UARCH(arch, 0, 6, 0, 1, NA, "P6 (Pentium II)", UARCH_P6_PENTIUM_II, UNK) // process depends on core CHECK_UARCH(arch, 0, 6, 0, 2, NA, "P6 (Pentium II)", UARCH_P6_PENTIUM_II, UNK) @@ -234,7 +255,8 @@ // CHECK_UARCH(arch, 0, 6, 8, 14, 9, ...) It is not possible to determine uarch only from CPUID dump (can be Kaby Lake or Amber Lake) // CHECK_UARCH(arch, 0, 6, 8, 14, 10, ...) It is not possible to determine uarch only from CPUID dump (can be Kaby Lake R or Coffee Lake U) CHECK_UARCH(arch, 0, 6, 8, 14, 11, "Whiskey Lake", UARCH_WHISKEY_LAKE, 14) // wikichip - CHECK_UARCH(arch, 0, 6, 8, 14, 12, "Comet Lake", UARCH_COMET_LAKE, 14) // wikichip + // CHECK_UARCH(arch, 0, 6, 8, 14, 12, ...) It is not possible to determine uarch only from CPUID dump (can be Comet Lake U or Whiskey Lake U) + CHECK_UARCH(arch, 0, 6, 8, 15, 8, "Sapphire Rapids", UARCH_SAPPHIRE_RAPIDS, 7) // wikichip CHECK_UARCH(arch, 0, 6, 9, 6, NA, "Tremont", UARCH_TREMONT, 10) // LX* CHECK_UARCH(arch, 0, 6, 9, 7, NA, "Alder Lake", UARCH_ALDER_LAKE, 10) // instlatx64 (Alder Lake-S) CHECK_UARCH(arch, 0, 6, 9, 10, NA, "Alder Lake", UARCH_ALDER_LAKE, 10) // instlatx64 (Alder Lake-P) @@ -279,11 +301,16 @@ // ----------------------------------------------------------------------------- // // EF F EM M S // UARCH_START - CHECK_UARCH(arch, 0, 4, 0, 3, NA, "Am486", UARCH_AM486, UNK) - CHECK_UARCH(arch, 0, 4, 0, 7, NA, "Am486", UARCH_AM486, UNK) - CHECK_UARCH(arch, 0, 4, 0, 8, NA, "Am486", UARCH_AM486, UNK) - CHECK_UARCH(arch, 0, 4, 0, 9, NA, "Am486", UARCH_AM486, UNK) - CHECK_UARCH(arch, 0, 4, NA, NA, NA, "Am5x86", UARCH_AM5X86, UNK) + CHECK_UARCH(arch, 0, 4, 0, 3, NA, "Am486DX2", UARCH_AM486, UNK) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 4, 0, 7, NA, "Am486DX2WB", UARCH_AM486, UNK) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 4, 0, 8, NA, "Am486DX4", UARCH_AM486, UNK) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 4, 0, 9, NA, "Am486DX4WB", UARCH_AM486, UNK) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 4, 0, 14, NA, "Am5x86", UARCH_AM5X86, 350) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 4, 0, 15, NA, "Am5x86WB", UARCH_AM5X86, 350) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 5, 0, 0, NA, "SSA5 (K5)", UARCH_SSA5, 350) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 5, 0, 1, NA, "K5", UARCH_K5, 350) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 5, 0, 2, NA, "K5", UARCH_K5, 350) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h + CHECK_UARCH(arch, 0, 5, 0, 3, NA, "K5", UARCH_K5, 350) // https://sandpile.org/x86/cpuid.htm#level_0000_0001h CHECK_UARCH(arch, 0, 5, 0, 6, NA, "K6", UARCH_K6, 300) CHECK_UARCH(arch, 0, 5, 0, 7, NA, "K6", UARCH_K6, 250) // *p from sandpile.org CHECK_UARCH(arch, 0, 5, 0, 10, NA, "K7", UARCH_K7, 130) // Geode NX @@ -387,6 +414,12 @@ CHECK_UARCH(arch, 10, 15, 8, NA, NA, "Zen 4", UARCH_ZEN4, 5) // instlatx64 (AMD MI300C) CHECK_UARCH(arch, 10, 15, 9, NA, NA, "Zen 4", UARCH_ZEN4, 5) // instlatx64 (AMD MI300A) CHECK_UARCH(arch, 10, 15, 10, NA, NA, "Zen 4c", UARCH_ZEN4C, 5) // instlatx64 + CHECK_UARCH(arch, 11, 15, 0, NA, NA, "Zen 5", UARCH_ZEN5, 4) // Turin/EPYC (instlatx64) + CHECK_UARCH(arch, 11, 15, 1, NA, NA, "Zen 5c", UARCH_ZEN5C, 3) // Zen5c EPYC (instlatx64, https://en.wikipedia.org/wiki/Zen_5#cite_note-10) + CHECK_UARCH(arch, 11, 15, 2, NA, NA, "Zen 5", UARCH_ZEN5, 4) // Strix Point (instlatx64) + CHECK_UARCH(arch, 11, 15, 4, NA, NA, "Zen 5", UARCH_ZEN5, 4) // Granite Ridge (instlatx64) + CHECK_UARCH(arch, 11, 15, 6, NA, NA, "Zen 5", UARCH_ZEN5, 4) // Krackan Point (instlatx64) + CHECK_UARCH(arch, 11, 15, 7, NA, NA, "Zen 5", UARCH_ZEN5, 4) // Strix Halo (instlatx64) UARCH_END return arch; @@ -414,6 +447,7 @@ struct uarch* get_uarch_from_cpuid(struct cpuInfo* cpu, uint32_t dump, uint32_t ef, uint32_t f, uint32_t em, uint32_t m, int s) { if(cpu->cpu_vendor == CPU_VENDOR_INTEL) { struct uarch* arch = emalloc(sizeof(struct uarch)); + // TODO: Refactor these 3 checks in a common function. if(dump == 0x000806E9) { if (cpu->cpu_name == NULL) { printErr("Unable to find uarch without CPU name"); @@ -453,6 +487,30 @@ return arch; } + else if (dump == 0x000806EC) { + if (cpu->cpu_name == NULL) { + printErr("Unable to find uarch without CPU name"); + fill_uarch(arch, STRING_UNKNOWN, UARCH_UNKNOWN, UNK); + return arch; + } + + // It is not possible to determine uarch only from CPUID dump (can be Comet Lake U or Whiskey Lake U) + // https://github.com/Dr-Noob/cpufetch/issues/298 + if (strstr(cpu->cpu_name, "i3-8145U") != NULL || + strstr(cpu->cpu_name, "i5-8265U") != NULL || + strstr(cpu->cpu_name, "i5-8365U") != NULL || + strstr(cpu->cpu_name, "i7-8565U") != NULL || + strstr(cpu->cpu_name, "i7-8665U") != NULL || + strstr(cpu->cpu_name, "5405U") != NULL || + strstr(cpu->cpu_name, "4205U") != NULL) { + fill_uarch(arch, "Whiskey Lake", UARCH_WHISKEY_LAKE, 14); + } + else { + fill_uarch(arch, "Comet Lake", UARCH_COMET_LAKE, 14); + } + + return arch; + } return get_uarch_from_cpuid_intel(ef, f, em, m, s); } else if(cpu->cpu_vendor == CPU_VENDOR_AMD) { @@ -479,16 +537,42 @@ char *str = NULL; - if (arch->uarch == UARCH_P5) - str = "Intel Pentium"; - else if (arch->uarch == UARCH_P5_MMX) - str = "Intel Pentium MMX"; - else if (arch->uarch == UARCH_P6_PENTIUM_II) - str = "Intel Pentium II"; - else if (arch->uarch == UARCH_P6_PENTIUM_III) - str = "Intel Pentium III"; - else - printErr("Unable to find name from uarch: %d", arch->uarch); + switch (arch->uarch) { + // Intel + case UARCH_I486: + str = "Intel 486"; + break; + case UARCH_P5: + str = "Intel Pentium"; + break; + case UARCH_P5_MMX: + str = "Intel Pentium MMX"; + break; + case UARCH_P6_PRO: + str = "Intel Pentium Pro"; + break; + case UARCH_P6_PENTIUM_II: + str = "Intel Pentium II"; + break; + case UARCH_P6_PENTIUM_III: + str = "Intel Pentium III"; + break; + + // AMD + case UARCH_AM486: + str = "AMD 486"; + break; + case UARCH_AM5X86: + str = "AMD 5x86"; + break; + case UARCH_SSA5: + str = "AMD 5k86"; + break; + + default: + printErr("Unable to find name from uarch: %d", arch->uarch); + break; + } if (str == NULL) { cpu_name = ecalloc(strlen(STRING_UNKNOWN) + 1, sizeof(char)); @@ -503,6 +587,8 @@ } bool vpus_are_AVX512(struct cpuInfo* cpu) { + // Zen5 actually has 2 x AVX512 units + // https://www.anandtech.com/show/21469/amd-details-ryzen-ai-300-series-for-mobile-strix-point-with-rdna-35-igpu-xdna-2-npu return cpu->arch->uarch != UARCH_ICE_LAKE && cpu->arch->uarch != UARCH_TIGER_LAKE && cpu->arch->uarch != UARCH_ZEN4 && @@ -532,6 +618,7 @@ case UARCH_KNIGHTS_LANDING: case UARCH_KNIGHTS_MILL: + case UARCH_SAPPHIRE_RAPIDS: case UARCH_ICE_LAKE: case UARCH_TIGER_LAKE: case UARCH_ALDER_LAKE: @@ -543,6 +630,8 @@ case UARCH_ZEN3_PLUS: case UARCH_ZEN4: case UARCH_ZEN4C: + case UARCH_ZEN5: + case UARCH_ZEN5C: return 2; default: return 1;