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, ¤t, &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}