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}