summaryrefslogtreecommitdiffstats
path: root/src/detection/cpucache
diff options
context:
space:
mode:
authorsumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
committersumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
commit76424950e373d3b04ac3dd13019151bfba3e8423 (patch)
tree4c3cfdbda039e592b9186be3e28f8d7cfd439e8a /src/detection/cpucache
Add the files
Diffstat (limited to 'src/detection/cpucache')
-rw-r--r--src/detection/cpucache/cpucache.h44
-rw-r--r--src/detection/cpucache/cpucache_apple.c63
-rw-r--r--src/detection/cpucache/cpucache_linux.c132
-rw-r--r--src/detection/cpucache/cpucache_nosupport.c5
-rw-r--r--src/detection/cpucache/cpucache_shared.c82
-rw-r--r--src/detection/cpucache/cpucache_windows.c47
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;
+}