diff options
| author | sumuel <samuel@yakubos.org> | 2026-08-17 20:44:55 +0000 |
|---|---|---|
| committer | sumuel <samuel@yakubos.org> | 2026-08-17 20:44:55 +0000 |
| commit | 76424950e373d3b04ac3dd13019151bfba3e8423 (patch) | |
| tree | 4c3cfdbda039e592b9186be3e28f8d7cfd439e8a /src/detection/cpucache | |
Add the files
Diffstat (limited to 'src/detection/cpucache')
| -rw-r--r-- | src/detection/cpucache/cpucache.h | 44 | ||||
| -rw-r--r-- | src/detection/cpucache/cpucache_apple.c | 63 | ||||
| -rw-r--r-- | src/detection/cpucache/cpucache_linux.c | 132 | ||||
| -rw-r--r-- | src/detection/cpucache/cpucache_nosupport.c | 5 | ||||
| -rw-r--r-- | src/detection/cpucache/cpucache_shared.c | 82 | ||||
| -rw-r--r-- | src/detection/cpucache/cpucache_windows.c | 47 |
6 files changed, 373 insertions, 0 deletions
diff --git a/src/detection/cpucache/cpucache.h b/src/detection/cpucache/cpucache.h new file mode 100644 index 0000000..287154b --- /dev/null +++ b/src/detection/cpucache/cpucache.h @@ -0,0 +1,44 @@ +#pragma once + +#include "fastfetch.h" +#include "modules/cpucache/option.h" + +typedef enum FF_A_PACKED FFCPUCacheType { + FF_CPU_CACHE_TYPE_UNIFIED = 0, + FF_CPU_CACHE_TYPE_INSTRUCTION = 1, + FF_CPU_CACHE_TYPE_DATA = 2, + FF_CPU_CACHE_TYPE_TRACE = 3, +} FFCPUCacheType; + +typedef struct FFCPUCache { + uint32_t size; + uint32_t num; + uint32_t lineSize; + FFCPUCacheType type; +} FFCPUCache; + +typedef struct FFCPUCacheResult { + FFlist caches[4]; // L1, L2, L3, L4(?) +} FFCPUCacheResult; + +const char* ffDetectCPUCache(FFCPUCacheResult* result); + +static inline FFCPUCache* ffCPUCacheAddItem(FFCPUCacheResult* result, uint32_t level, uint32_t size, uint32_t lineSize, FFCPUCacheType type) { + FFlist* cacheLevel = &result->caches[level - 1]; + + FF_LIST_FOR_EACH (FFCPUCache, item, *cacheLevel) { + if (item->type == type && item->size == size && item->lineSize == lineSize) { + item->num++; + return item; + } + } + + FFCPUCache* item = FF_LIST_ADD(FFCPUCache, *cacheLevel); + *item = (FFCPUCache) { + .size = size, + .num = 1, + .lineSize = lineSize, + .type = type, + }; + return item; +} diff --git a/src/detection/cpucache/cpucache_apple.c b/src/detection/cpucache/cpucache_apple.c new file mode 100644 index 0000000..3d1562c --- /dev/null +++ b/src/detection/cpucache/cpucache_apple.c @@ -0,0 +1,63 @@ +#include "cpucache.h" +#include "common/sysctl.h" +#include "common/strutil.h" + +const char* ffDetectCPUCache(FFCPUCacheResult* result) { + // https://developer.apple.com/documentation/kernel/1387446-sysctlbyname/determining_system_capabilities#3901385 + uint32_t nPerfLevels = (uint32_t) ffSysctlGetInt("hw.nperflevels", 0); + if (nPerfLevels <= 0) { + return "sysctl(hw.nperflevels) failed"; + } + + // macOS provides the global system cache line size + uint32_t lineSize = (uint32_t) ffSysctlGetInt64("hw.cachelinesize", 0); + + char sysctlKey[128] = "hw.perflevelN."; + char* pNum = sysctlKey + strlen("hw.perflevel"); + char* pSubkey = sysctlKey + strlen("hw.perflevelN."); + const size_t lenLeft = ARRAY_SIZE(sysctlKey) - strlen("hw.perflevelN."); + + for (uint32_t i = 0; i < nPerfLevels; ++i) { + *pNum = (char) ('0' + i); + + ffStrCopy(pSubkey, "physicalcpu", lenLeft); + uint32_t ncpu = (uint32_t) ffSysctlGetInt(sysctlKey, 0); + if (ncpu <= 0) { + continue; + } + + ffStrCopy(pSubkey, "l1icachesize", lenLeft); + uint32_t size = (uint32_t) ffSysctlGetInt(sysctlKey, 0); + if (size) { + ffCPUCacheAddItem(result, 1, size, lineSize, FF_CPU_CACHE_TYPE_INSTRUCTION)->num = ncpu; + } + + ffStrCopy(pSubkey, "l1dcachesize", lenLeft); + size = (uint32_t) ffSysctlGetInt(sysctlKey, 0); + if (size) { + ffCPUCacheAddItem(result, 1, size, lineSize, FF_CPU_CACHE_TYPE_DATA)->num = ncpu; + } + + ffStrCopy(pSubkey, "l2cachesize", lenLeft); + size = (uint32_t) ffSysctlGetInt(sysctlKey, 0); + if (size) { + ffStrCopy(pSubkey, "cpusperl2", lenLeft); + uint32_t cpuSper = (uint32_t) ffSysctlGetInt(sysctlKey, 0); + if (cpuSper) { + ffCPUCacheAddItem(result, 2, size, lineSize, FF_CPU_CACHE_TYPE_UNIFIED)->num = ncpu / cpuSper; + } + } + + ffStrCopy(pSubkey, "l3cachesize", lenLeft); + size = (uint32_t) ffSysctlGetInt(sysctlKey, 0); + if (size) { + ffStrCopy(pSubkey, "cpusperl3", lenLeft); + uint32_t cpuSper = (uint32_t) ffSysctlGetInt(sysctlKey, 0); + if (cpuSper) { + ffCPUCacheAddItem(result, 3, size, lineSize, FF_CPU_CACHE_TYPE_UNIFIED)->num = ncpu / cpuSper; + } + } + } + + return NULL; +} diff --git a/src/detection/cpucache/cpucache_linux.c b/src/detection/cpucache/cpucache_linux.c new file mode 100644 index 0000000..0657c84 --- /dev/null +++ b/src/detection/cpucache/cpucache_linux.c @@ -0,0 +1,132 @@ +#include "cpucache.h" +#include "common/io.h" +#include "common/strutil.h" + +static const char* parseCpuCacheIndex(FFstrbuf* path, FFCPUCacheResult* result, FFstrbuf* buffer, FFstrbuf* added) { + uint32_t baseLen = path->length; + ffStrbufAppendS(path, "/level"); + if (!ffReadFileBuffer(path->chars, buffer)) { + return "ffReadFileBuffer(\"/sys/devices/system/cpu/cpuX/cache/indexX/level\") == NULL"; + } + + uint32_t level = (uint32_t) ffStrbufToUInt(buffer, 0); + if (level < 1 || level > 4) { + return "level < 1 || level > 4"; + } + + ffStrbufSubstrBefore(path, baseLen); + ffStrbufAppendS(path, "/size"); + if (!ffReadFileBuffer(path->chars, buffer)) { + return "ffReadFileBuffer(\"/sys/devices/system/cpu/cpuX/cache/indexX/size\") == NULL"; + } + + uint32_t sizeKb = (uint32_t) ffStrbufToUInt(buffer, 0); + if (sizeKb == 0) { + return "size == 0"; + } + + ffStrbufSubstrBefore(path, baseLen); + ffStrbufAppendS(path, "/type"); + if (!ffReadFileBuffer(path->chars, buffer)) { + return "ffReadFileBuffer(\"/sys/devices/system/cpu/cpuX/cache/indexX/type\") == NULL"; + } + ffStrbufTrimRightSpace(buffer); + + FFCPUCacheType cacheType = 0; + switch (buffer->chars[0]) { + case 'I': + cacheType = FF_CPU_CACHE_TYPE_INSTRUCTION; + break; + case 'D': + cacheType = FF_CPU_CACHE_TYPE_DATA; + break; + case 'U': + cacheType = FF_CPU_CACHE_TYPE_UNIFIED; + break; + case 'T': + cacheType = FF_CPU_CACHE_TYPE_TRACE; + break; + default: + return "unknown cache type"; + } + + uint32_t lineSize = 0; + ffStrbufSubstrBefore(path, baseLen); + ffStrbufAppendS(path, "/coherency_line_size"); + if (ffReadFileBuffer(path->chars, buffer)) { + lineSize = (uint32_t) ffStrbufToUInt(buffer, 0); + } + + ffStrbufSubstrBefore(path, baseLen); + ffStrbufAppendS(path, "/shared_cpu_list"); + ffStrbufClear(buffer); + ffStrbufAppendC(buffer, '['); + if (!ffAppendFileBuffer(path->chars, buffer)) { + return "ffAppendFileBuffer(\"/sys/devices/system/cpu/cpuX/cache/indexX/shared_cpu_list\") == NULL"; + } + ffStrbufTrimRightSpace(buffer); + + // deduplicate shared caches + ffStrbufAppendF(buffer, "_%u_%u_%u_%u]", level, sizeKb, lineSize, cacheType); + + if (ffStrbufContain(added, buffer)) { + return NULL; + } + ffStrbufAppend(added, buffer); + ffCPUCacheAddItem(result, level, sizeKb * 1024, lineSize, cacheType); + return NULL; +} + +static const char* parseCpuCache(FFstrbuf* path, FFCPUCacheResult* result, FFstrbuf* buffer, FFstrbuf* added) { + ffStrbufAppendS(path, "/cache/"); + uint32_t baseLen = path->length; + FF_AUTO_CLOSE_DIR DIR* pathCacheDir = opendir(path->chars); + if (!pathCacheDir) { + return "opendir(\"/sys/devices/system/cpu/cpuX/cache/\") == NULL"; + } + + struct dirent* pathCacheEntry; + while ((pathCacheEntry = readdir(pathCacheDir)) != NULL) { + if (!ffStrStartsWith(pathCacheEntry->d_name, "index") || !ffCharIsDigit(pathCacheEntry->d_name[strlen("index")])) { + continue; + } + + ffStrbufAppendS(path, pathCacheEntry->d_name); + const char* error = parseCpuCacheIndex(path, result, buffer, added); + if (error) { + return error; + } + ffStrbufSubstrBefore(path, baseLen); + } + + return NULL; +} + +const char* ffDetectCPUCache(FFCPUCacheResult* result) { + // https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-devices-system-cpu + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateS("/sys/devices/system/cpu/"); + uint32_t baseLen = path.length; + FF_AUTO_CLOSE_DIR DIR* pathCpuDir = opendir(path.chars); + if (!pathCpuDir) { + return "opendir(\"/sys/devices/system/cpu/\") == NULL"; + } + + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + FF_STRBUF_AUTO_DESTROY added = ffStrbufCreate(); + + struct dirent* pathCpuEntry; + while ((pathCpuEntry = readdir(pathCpuDir)) != NULL) { + if (!ffStrStartsWith(pathCpuEntry->d_name, "cpu") || + !ffCharIsDigit(pathCpuEntry->d_name[strlen("cpu")])) { + continue; + } + + ffStrbufAppendS(&path, pathCpuEntry->d_name); + const char* error = parseCpuCache(&path, result, &buffer, &added); + if (error) { + return error; + } + ffStrbufSubstrBefore(&path, baseLen); + } + return NULL; +} diff --git a/src/detection/cpucache/cpucache_nosupport.c b/src/detection/cpucache/cpucache_nosupport.c new file mode 100644 index 0000000..51d2256 --- /dev/null +++ b/src/detection/cpucache/cpucache_nosupport.c @@ -0,0 +1,5 @@ +#include "cpucache.h" + +const char* ffDetectCPUCache(FF_A_UNUSED FFCPUCacheResult* result) { + return "Not supported on this platform"; +} diff --git a/src/detection/cpucache/cpucache_shared.c b/src/detection/cpucache/cpucache_shared.c new file mode 100644 index 0000000..c13e351 --- /dev/null +++ b/src/detection/cpucache/cpucache_shared.c @@ -0,0 +1,82 @@ +#include "cpucache.h" +#include "common/smbios.h" +#include "common/strutil.h" + +typedef struct FFSmbiosCacheInfo { + FFSmbiosHeader Header; + + uint8_t SocketDesignation; // string + uint16_t CacheConfiguration; // varies + uint16_t MaximumCacheSize; // varies + uint16_t InstalledSize; // varies + uint16_t SupportedSramType; // bit field + uint16_t CurrentSramType; // bit field + + // 2.1+ + uint8_t CacheSpeed; // varies + uint8_t ErrorCorrectionType; // enum + uint8_t SystemCacheType; // enum + uint8_t Associativity; // enum + + // 3.1+ + uint32_t MaximumCacheSize2; // bit field + uint32_t InstalledCacheSize2; // bit field +} FF_A_PACKED FFSmbiosCacheInfo; + +static_assert(offsetof(FFSmbiosCacheInfo, InstalledCacheSize2) == 0x17, + "FFSmbiosCacheInfo: Wrong struct alignment"); + +const char* ffDetectCPUCache(FFCPUCacheResult* result) { + const FFSmbiosHeaderTable* smbiosTable = ffGetSmbiosHeaderTable(); + if (!smbiosTable) { + return "Failed to get SMBIOS data"; + } + + const FFSmbiosCacheInfo* data = (const FFSmbiosCacheInfo*) (*smbiosTable)[FF_SMBIOS_TYPE_CACHE_INFO]; + if (!data) { + return "Cache information is not found in SMBIOS data"; + } + + const FFSmbiosCacheInfo* endOfTable = (const FFSmbiosCacheInfo*) (*smbiosTable)[FF_SMBIOS_TYPE_END_OF_TABLE]; + for (; data != endOfTable; data = (const FFSmbiosCacheInfo*) ffSmbiosNextEntry(&data->Header)) { + if (data->Header.Type != FF_SMBIOS_TYPE_CACHE_INFO) { + continue; + } + + bool enabled = !!(data->CacheConfiguration & (1 << 7)); + if (!enabled) { + continue; + } + + uint32_t size = data->InstalledSize; + if (size == 0) { + continue; + } + + if (data->InstalledSize != 0xFFFF) { + size *= (size >> 15 ? 64 : 1) * 1024u; + } else if (data->Header.Length > offsetof(FFSmbiosCacheInfo, InstalledCacheSize2)) { + size = data->InstalledCacheSize2; + size *= (size >> 31 ? 64 : 1) * 1024u; + } + + uint32_t level = (data->CacheConfiguration & 0b111u) + 1; + + FFCPUCacheType type; + switch (data->SystemCacheType) { + case 3: + type = FF_CPU_CACHE_TYPE_INSTRUCTION; + break; + case 4: + type = FF_CPU_CACHE_TYPE_DATA; + break; + default: + type = FF_CPU_CACHE_TYPE_UNIFIED; + break; + } + + ffCPUCacheAddItem(result, level, size, 0, type); + } + + return NULL; +} diff --git a/src/detection/cpucache/cpucache_windows.c b/src/detection/cpucache/cpucache_windows.c new file mode 100644 index 0000000..b9d3815 --- /dev/null +++ b/src/detection/cpucache/cpucache_windows.c @@ -0,0 +1,47 @@ +#include "cpucache.h" +#include "common/mallocHelper.h" +#include "common/windows/nt.h" + +const char* ffDetectCPUCache(FFCPUCacheResult* result) { + LOGICAL_PROCESSOR_RELATIONSHIP lpr = RelationCache; + DWORD length = 0; + NtQuerySystemInformationEx(SystemLogicalProcessorAndGroupInformation, &lpr, sizeof(lpr), NULL, 0, &length); + if (length == 0) { + return "GetLogicalProcessorInformationEx(RelationCache, NULL, &length) failed"; + } + + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* FF_AUTO_FREE + pProcessorInfo = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*) malloc(length); + + if (!NT_SUCCESS(NtQuerySystemInformationEx(SystemLogicalProcessorAndGroupInformation, &lpr, sizeof(lpr), pProcessorInfo, length, &length))) { + return "GetLogicalProcessorInformationEx(RelationCache, pProcessorInfo, &length) failed"; + } + + for ( + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* ptr = pProcessorInfo; + (uint8_t*) ptr < ((uint8_t*) pProcessorInfo) + length; + ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*) (((uint8_t*) ptr) + ptr->Size)) { + if (__builtin_expect(ptr->Relationship == RelationCache && ptr->Cache.Level > 0 && ptr->Cache.Level <= 4, true)) { + FFCPUCacheType cacheType = 0; + switch (ptr->Cache.Type) { + case CacheUnified: + cacheType = FF_CPU_CACHE_TYPE_UNIFIED; + break; + case CacheInstruction: + cacheType = FF_CPU_CACHE_TYPE_INSTRUCTION; + break; + case CacheData: + cacheType = FF_CPU_CACHE_TYPE_DATA; + break; + case CacheTrace: + cacheType = FF_CPU_CACHE_TYPE_TRACE; + break; + default: + break; + } + ffCPUCacheAddItem(result, ptr->Cache.Level, ptr->Cache.CacheSize, ptr->Cache.LineSize, cacheType); + } + } + + return NULL; +} |