#include "cpu.h" #include "common/sysctl.h" #include "common/apple/smc_temps.h" #include "common/strutil.h" static double detectCpuTemp(const FFCPUOptions* options, const FFstrbuf* cpuName) { double result = 0; const char* error = NULL; if (options->tempSensor.length) { error = ffDetectSmcSpecificTemp(options->tempSensor.chars, &result); } else { if (ffStrbufStartsWithS(cpuName, "Apple M")) { switch (strtol(cpuName->chars + strlen("Apple M"), NULL, 10)) { case 1: error = ffDetectSmcTemps(FF_TEMP_CPU_M1X, &result); break; case 2: error = ffDetectSmcTemps(FF_TEMP_CPU_M2X, &result); break; case 3: error = ffDetectSmcTemps(FF_TEMP_CPU_M3X, &result); break; case 4: error = ffDetectSmcTemps(FF_TEMP_CPU_M4X, &result); break; default: error = "Unsupported Apple Silicon CPU"; } } else { // PPC? error = ffDetectSmcTemps(FF_TEMP_CPU_X64, &result); } } if (error) { return FF_CPU_TEMP_UNSET; } return result; } #ifdef __aarch64__ #include "common/apple/cf_helpers.h" #include static const char* detectFrequency(FFCPUResult* cpu) { // https://github.com/giampaolo/psutil/pull/2222/files FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryDevice = IOServiceGetMatchingService(MACH_PORT_NULL, IOServiceNameMatching("pmgr")); if (!entryDevice) { return "IOServiceGetMatchingService() failed"; } if (!IOObjectConformsTo(entryDevice, "AppleARMIODevice")) { return "\"pmgr\" should conform to \"AppleARMIODevice\""; } FF_CFTYPE_AUTO_RELEASE CFDataRef freqProperty = (CFDataRef) IORegistryEntryCreateCFProperty(entryDevice, CFSTR("voltage-states5-sram"), kCFAllocatorDefault, kNilOptions); if (!freqProperty || CFGetTypeID(freqProperty) != CFDataGetTypeID()) { return "\"voltage-states5-sram\" in \"pmgr\" is not found"; } // voltage-states5-sram stores supported pairs of pcores from the lowest to the highest // voltage-states1-sram stores ecores' CFIndex propLength = CFDataGetLength(freqProperty); if (propLength == 0 || propLength % (CFIndex) sizeof(uint32_t) * 2 != 0) { return "Invalid \"voltage-states5-sram\" length"; } uint32_t* pStart = (uint32_t*) CFDataGetBytePtr(freqProperty); uint32_t pMax = *pStart; for (CFIndex i = 2; i < propLength / (CFIndex) sizeof(uint32_t) && pStart[i] > 0; i += 2 /* skip voltage */) { pMax = pMax > pStart[i] ? pMax : pStart[i]; } if (pMax > 0) { if (pMax > 100000000) { // Assume that pMax is in Hz, M1~M3 cpu->frequencyMax = pMax / 1000 / 1000; } else { // Assume that pMax is in kHz, M4 and later (#1394) cpu->frequencyMax = pMax / 1000; } } return NULL; } #else static const char* detectFrequency(FFCPUResult* cpu) { cpu->frequencyBase = (uint32_t) (ffSysctlGetInt64("hw.cpufrequency", 0) / 1000 / 1000); cpu->frequencyMax = (uint32_t) (ffSysctlGetInt64("hw.cpufrequency_max", 0) / 1000 / 1000); if (cpu->frequencyBase == 0) { unsigned current = 0; size_t size = sizeof(current); if (sysctl((int[]) { CTL_HW, HW_CPU_FREQ }, 2, ¤t, &size, NULL, 0) == 0) { cpu->frequencyBase = (uint32_t) (current / 1000 / 1000); } } return NULL; } #endif static const char* detectCoreCount(FFCPUResult* cpu) { uint32_t nPerfLevels = (uint32_t) ffSysctlGetInt("hw.nperflevels", 0); if (nPerfLevels <= 0) { return "sysctl(hw.nperflevels) failed"; } char sysctlKey[] = "hw.perflevelN.logicalcpu"; if (nPerfLevels > ARRAY_SIZE(cpu->coreTypes)) { nPerfLevels = ARRAY_SIZE(cpu->coreTypes); } for (uint32_t i = 0; i < nPerfLevels; ++i) { sysctlKey[strlen("hw.perflevel")] = (char) ('0' + i); cpu->coreTypes[i] = (FFCPUCore) { .freq = nPerfLevels - i, .count = (uint32_t) ffSysctlGetInt(sysctlKey, 0), }; } return NULL; } const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) { if (ffSysctlGetString("machdep.cpu.brand_string", &cpu->name) != NULL) { return "sysctlbyname(machdep.cpu.brand_string) failed"; } ffSysctlGetString("machdep.cpu.vendor", &cpu->vendor); cpu->packages = (uint16_t) ffSysctlGetInt("hw.packages", 1); if (cpu->vendor.length == 0 && ffStrbufStartsWithS(&cpu->name, "Apple ")) { ffStrbufAppendS(&cpu->vendor, "Apple"); } cpu->coresPhysical = (uint16_t) ffSysctlGetInt("hw.physicalcpu_max", 1); if (cpu->coresPhysical == 1) { cpu->coresPhysical = (uint16_t) ffSysctlGetInt("hw.physicalcpu", 1); } cpu->coresLogical = (uint16_t) ffSysctlGetInt("hw.logicalcpu_max", 1); if (cpu->coresLogical == 1) { cpu->coresLogical = (uint16_t) ffSysctlGetInt("hw.ncpu", 1); } cpu->coresOnline = (uint16_t) ffSysctlGetInt("hw.logicalcpu", 1); if (cpu->coresOnline == 1) { cpu->coresOnline = (uint16_t) ffSysctlGetInt("hw.activecpu", 1); } ffCPUDetectByCpuid(cpu); detectFrequency(cpu); if (options->showPeCoreCount) { detectCoreCount(cpu); } cpu->temperature = options->temp ? detectCpuTemp(options, &cpu->name) : FF_CPU_TEMP_UNSET; return NULL; }