master ohmu/fastfetch-openbsd / src / detection / physicalmemory / physicalmemory_apple.m
ohmu  ·  2026-10-15
  1#include "physicalmemory.h"
  2#include "common/processing.h"
  3#include "common/smbios.h"
  4#include "common/strutil.h"
  5#include "common/apple/cf_helpers.h"
  6
  7#import <Foundation/Foundation.h>
  8
  9static void appendDevice(
 10    FFlist* result,
 11    NSString* type,
 12    NSString* vendor,
 13    NSString* size,
 14
 15    // Intel only
 16    NSString* locator,
 17    NSString* serial,
 18    NSString* partNumber,
 19    NSString* speed,
 20    bool ecc)
 21{
 22    FFPhysicalMemoryResult* device = FF_LIST_ADD(FFPhysicalMemoryResult, *result);
 23    ffStrbufInitS(&device->type, type.UTF8String);
 24    ffStrbufInit(&device->formFactor);
 25    ffStrbufInitS(&device->locator, locator.UTF8String);
 26    ffStrbufInitS(&device->vendor, vendor.UTF8String);
 27    FFPhysicalMemoryUpdateVendorString(device);
 28    ffStrbufInitS(&device->serial, serial.UTF8String);
 29    ffCleanUpSmbiosValue(&device->serial);
 30    ffStrbufInitS(&device->partNumber, partNumber.UTF8String);
 31    ffCleanUpSmbiosValue(&device->partNumber);
 32    device->size = 0;
 33    device->maxSpeed = 0;
 34    device->runningSpeed = 0;
 35    device->installed = true;
 36    device->ecc = ecc;
 37
 38    if (size)
 39    {
 40        char* unit = nullptr;
 41        device->size = (uint64_t) strtoull(size.UTF8String, &unit, 10);
 42        if (*unit == ' ') ++unit;
 43
 44        switch (*unit)
 45        {
 46            case 'G': device->size *= 1024ULL * 1024 * 1024; break;
 47            case 'M': device->size *= 1024ULL * 1024; break;
 48            case 'K': device->size *= 1024ULL; break;
 49            case 'T': device->size *= 1024ULL * 1024 * 1024 * 1024; break;
 50        }
 51    }
 52
 53    if (speed)
 54    {
 55        char* unit = nullptr;
 56        device->maxSpeed = (uint32_t) strtoul(speed.UTF8String, &unit, 10);
 57        if (*unit == ' ') ++unit;
 58
 59        switch (*unit)
 60        {
 61            case 'T': device->maxSpeed *= 1000 * 1000; break;
 62            case 'G': device->maxSpeed *= 1000; break;
 63            case 'K': device->maxSpeed /= 1000; break;
 64        }
 65    }
 66}
 67
 68static const char* detectFromSystemProfiler(FFlist* result)
 69{
 70    FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
 71    if (ffProcessAppendStdOut(&buffer, (char* const[]) {
 72        "system_profiler",
 73        "SPMemoryDataType",
 74        "-xml",
 75        "-detailLevel",
 76        "full",
 77        nullptr
 78    }) != nullptr)
 79        return "Starting `system_profiler SPMemoryDataType -xml -detailLevel full` failed";
 80
 81    NSArray* arr = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytes:buffer.chars length:buffer.length]
 82                    options:NSPropertyListImmutable
 83                    format:nil
 84                    error:nil];
 85    if (!arr || !arr.count)
 86        return "system_profiler SPMemoryDataType returned an empty array";
 87
 88    for (NSDictionary* data in arr[0][@"_items"])
 89    {
 90        if (data[@"_items"])
 91        {
 92            // for Intel
 93            for (NSDictionary* item in data[@"_items"])
 94            {
 95                appendDevice(result,
 96                    item[@"dimm_type"],
 97                    item[@"dimm_manufacturer"],
 98                    item[@"dimm_size"],
 99                    item[@"_name"],
100                    item[@"dimm_serial_number"],
101                    item[@"dimm_part_number"],
102                    item[@"dimm_speed"],
103                    !![data[@"global_ecc_state"] isEqualToString:@"ecc_enabled"]);
104            }
105        }
106        else
107        {
108            // for Apple Silicon
109            appendDevice(result,
110                data[@"dimm_type"],
111                data[@"dimm_manufacturer"],
112                data[@"SPMemoryDataType"],
113                nil,
114                nil,
115                nil,
116                nil,
117                false);
118        }
119    }
120
121    return nullptr;
122}
123
124[[maybe_unused]] static const char* detectFromIokit(FFlist* result)
125{
126    FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryDevice = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/chosen");
127    if (!entryDevice)
128        return "IORegistryEntryFromPath() failed";
129
130    FF_CFTYPE_AUTO_RELEASE CFTypeRef dramType = IORegistryEntryCreateCFProperty(entryDevice, CFSTR("dram-type"), kCFAllocatorDefault, 0);
131    FF_CFTYPE_AUTO_RELEASE CFTypeRef dramSize = IORegistryEntryCreateCFProperty(entryDevice, CFSTR("dram-size"), kCFAllocatorDefault, 0);
132    FF_CFTYPE_AUTO_RELEASE CFTypeRef dramVendor = IORegistryEntryCreateCFProperty(entryDevice, CFSTR("dram-vendor"), kCFAllocatorDefault, 0);
133    if (!dramType || !dramSize || !dramVendor)
134        return "IORegistryEntryCreateCFProperty() failed";
135
136    FFPhysicalMemoryResult* device = FF_LIST_ADD(FFPhysicalMemoryResult, *result);
137    ffStrbufInit(&device->type);
138    ffStrbufInit(&device->formFactor);
139    ffStrbufInit(&device->locator);
140    ffStrbufInit(&device->vendor);
141    ffStrbufInit(&device->serial);
142    ffStrbufInit(&device->partNumber);
143    device->size = 0;
144    device->maxSpeed = 0;
145    device->runningSpeed = 0;
146    device->installed = true;
147    device->ecc = false;
148
149    ffCfStrGetString(dramType, &device->type);
150    ffCfStrGetString(dramVendor, &device->vendor);
151    ffCfNumGetInt64(dramSize, (int64_t*) &device->size);
152    return nullptr;
153}
154
155const char* ffDetectPhysicalMemory([[maybe_unused]] FFPhysicalMemoryOptions* options, FFlist* result)
156{
157    #if __aarch64__
158    if (detectFromIokit(result) == nullptr)
159        return nullptr;
160    #endif
161
162    return detectFromSystemProfiler(result);
163}