master ohmu/fastfetch-openbsd / src / detection / cpu / cpu_apple.c
ohmu  ·  2026-10-15
  1#include "cpu.h"
  2#include "common/debug.h"
  3#include "common/sysctl.h"
  4#include "common/apple/smc_temps.h"
  5#include "common/strutil.h"
  6
  7static double detectCpuTemp(const FFCPUOptions* options, const FFstrbuf* cpuName) {
  8    double result = 0;
  9
 10    const char* error = nullptr;
 11
 12    if (options->tempSensor.length) {
 13        error = ffDetectSmcSpecificTemp(options->tempSensor.chars, &result);
 14    } else {
 15        if (ffStrbufStartsWithS(cpuName, "Apple M")) {
 16            switch (strtol(cpuName->chars + strlen("Apple M"), nullptr, 10)) {
 17                case 1:
 18                    error = ffDetectSmcTemps(FF_TEMP_CPU_M1X, &result);
 19                    break;
 20                case 2:
 21                    error = ffDetectSmcTemps(FF_TEMP_CPU_M2X, &result);
 22                    break;
 23                case 3:
 24                    error = ffDetectSmcTemps(FF_TEMP_CPU_M3X, &result);
 25                    break;
 26                case 4:
 27                    error = ffDetectSmcTemps(FF_TEMP_CPU_M4X, &result);
 28                    break;
 29                case 5:
 30                    error = ffDetectSmcTemps(FF_TEMP_CPU_M5X, &result);
 31                    break;
 32                default:
 33                    error = "Unsupported Apple Silicon CPU";
 34            }
 35        } else { // PPC?
 36            error = ffDetectSmcTemps(FF_TEMP_CPU_X64, &result);
 37        }
 38    }
 39
 40    if (error) {
 41        return FF_CPU_TEMP_UNSET;
 42    }
 43
 44    return result;
 45}
 46
 47#ifdef __aarch64__
 48    #include "common/apple/cf_helpers.h"
 49
 50    #include <IOKit/IOKitLib.h>
 51
 52static const char* detectFrequency(FFCPUResult* cpu) {
 53    // https://github.com/giampaolo/psutil/pull/2222/files
 54
 55    FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryDevice = IOServiceGetMatchingService(MACH_PORT_NULL, IOServiceNameMatching("pmgr"));
 56    if (!entryDevice) {
 57        FF_DEBUG("IOServiceGetMatchingService() failed");
 58        return "IOServiceGetMatchingService() failed";
 59    }
 60
 61    if (!IOObjectConformsTo(entryDevice, "AppleARMIODevice")) {
 62        FF_DEBUG("\"pmgr\" should conform to \"AppleARMIODevice\"");
 63        return "\"pmgr\" should conform to \"AppleARMIODevice\"";
 64    }
 65
 66    FF_CFTYPE_AUTO_RELEASE CFDataRef freqProperty = (CFDataRef) IORegistryEntryCreateCFProperty(entryDevice, CFSTR("voltage-states5-sram"), kCFAllocatorDefault, kNilOptions);
 67    if (!freqProperty || CFGetTypeID(freqProperty) != CFDataGetTypeID()) {
 68        FF_DEBUG("\"voltage-states5-sram\" in \"pmgr\" is not found");
 69        return "\"voltage-states5-sram\" in \"pmgr\" is not found";
 70    }
 71
 72    // voltage-states5-sram stores supported <frequency / voltage> pairs of pcores from the lowest to the highest
 73    // voltage-states1-sram stores ecores'
 74    CFIndex propLength = CFDataGetLength(freqProperty);
 75    if (propLength == 0 || propLength % (CFIndex) (sizeof(uint32_t) * 2) != 0) {
 76        FF_DEBUG("Invalid \"voltage-states5-sram\" length");
 77        return "Invalid \"voltage-states5-sram\" length";
 78    }
 79
 80    uint32_t* pStart = (uint32_t*) CFDataGetBytePtr(freqProperty);
 81    uint32_t pMax = *pStart;
 82    for (CFIndex i = 2; i < propLength / (CFIndex) sizeof(uint32_t) && pStart[i] > 0; i += 2 /* skip voltage */) {
 83        pMax = pMax > pStart[i] ? pMax : pStart[i];
 84    }
 85
 86    if (pMax > 0) {
 87        if (pMax > 100000000) { // Assume that pMax is in Hz, M1~M3
 88            cpu->frequencyMax = pMax / 1000 / 1000;
 89        } else { // Assume that pMax is in kHz, M4 and later (#1394)
 90            cpu->frequencyMax = pMax / 1000;
 91        }
 92    }
 93
 94    return nullptr;
 95}
 96#else
 97static const char* detectFrequency(FFCPUResult* cpu) {
 98    cpu->frequencyBase = (uint32_t) (ffSysctlGetInt64("hw.cpufrequency", 0) / 1000 / 1000);
 99    cpu->frequencyMax = (uint32_t) (ffSysctlGetInt64("hw.cpufrequency_max", 0) / 1000 / 1000);
100    if (cpu->frequencyBase == 0) {
101        unsigned current = 0;
102        size_t size = sizeof(current);
103        if (sysctl((int[]) { CTL_HW, HW_CPU_FREQ }, 2, &current, &size, nullptr, 0) == 0) {
104            cpu->frequencyBase = (uint32_t) (current / 1000 / 1000);
105        }
106    }
107    return nullptr;
108}
109#endif
110
111static const char* detectCoreCount(FFCPUResult* cpu) {
112    uint32_t nPerfLevels = (uint32_t) ffSysctlGetInt("hw.nperflevels", 0);
113    if (nPerfLevels <= 0) {
114        FF_DEBUG("sysctl(hw.nperflevels) failed");
115        return "sysctl(hw.nperflevels) failed";
116    }
117
118    char sysctlKey[] = "hw.perflevelN.logicalcpu";
119    if (nPerfLevels > ARRAY_SIZE(cpu->coreTypes)) {
120        nPerfLevels = ARRAY_SIZE(cpu->coreTypes);
121    }
122    for (uint32_t i = 0; i < nPerfLevels; ++i) {
123        sysctlKey[strlen("hw.perflevel")] = (char) ('0' + i);
124        cpu->coreTypes[i] = (FFCPUCore) {
125            .freq = nPerfLevels - i,
126            .count = (uint32_t) ffSysctlGetInt(sysctlKey, 0),
127        };
128    }
129    return nullptr;
130}
131
132const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) {
133    const char* error = ffSysctlGetString("machdep.cpu.brand_string", &cpu->name);
134    if (error != nullptr) {
135        FF_DEBUG("sysctlbyname(machdep.cpu.brand_string) failed: %s", error);
136        return "sysctlbyname(machdep.cpu.brand_string) failed";
137    }
138
139    ffSysctlGetString("machdep.cpu.vendor", &cpu->vendor);
140    cpu->packages = (uint16_t) ffSysctlGetInt("hw.packages", 1);
141    if (cpu->vendor.length == 0 && ffStrbufStartsWithS(&cpu->name, "Apple ")) {
142        ffStrbufAppendS(&cpu->vendor, "Apple");
143    }
144
145    cpu->coresPhysical = (uint16_t) ffSysctlGetInt("hw.physicalcpu_max", 1);
146    if (cpu->coresPhysical == 1) {
147        cpu->coresPhysical = (uint16_t) ffSysctlGetInt("hw.physicalcpu", 1);
148    }
149
150    cpu->coresLogical = (uint16_t) ffSysctlGetInt("hw.logicalcpu_max", 1);
151    if (cpu->coresLogical == 1) {
152        cpu->coresLogical = (uint16_t) ffSysctlGetInt("hw.ncpu", 1);
153    }
154
155    cpu->coresOnline = (uint16_t) ffSysctlGetInt("hw.logicalcpu", 1);
156    if (cpu->coresOnline == 1) {
157        cpu->coresOnline = (uint16_t) ffSysctlGetInt("hw.activecpu", 1);
158    }
159
160    ffCPUDetectByCpuid(cpu);
161    detectFrequency(cpu);
162    if (options->showPeCoreCount) {
163        detectCoreCount(cpu);
164    }
165
166    cpu->temperature = options->temp ? detectCpuTemp(options, &cpu->name) : FF_CPU_TEMP_UNSET;
167
168    return nullptr;
169}