ohmu
·
2026-10-15
1#include "physicalmemory.h"
2#include "common/debug.h"
3#include "common/endian.h"
4#include "common/smbios.h"
5
6// 7.18
7typedef struct [[gnu::packed]] FFSmbiosMemoryDevice {
8 FFSmbiosHeader Header;
9
10 // 2.1+
11 uint16_t PhysicalMemoryArrayHandle; // varies
12 uint16_t MemoryErrorInformationHandle; // varies
13 uint16_t TotalWidth; // varies
14 uint16_t DataWidth; // varies
15 uint16_t Size; // varies
16 uint8_t FormFactor; // enum
17 uint8_t DeviceSet; // varies
18 uint8_t DeviceLocator; // string
19 uint8_t BankLocator; // string
20 uint8_t MemoryType; // enum
21 uint16_t TypeDetail; // bit field
22
23 // 2.3+
24 uint16_t Speed; // varies
25 uint8_t Manufacturer; // string
26 uint8_t SerialNumber; // string
27 uint8_t AssetTag; // string
28 uint8_t PartNumber; // string
29
30 // 2.6+
31 uint8_t Attributes; // varies
32
33 // 2.7+
34 uint32_t ExtendedSize; // varies
35 uint16_t ConfiguredMemorySpeed; // varies
36
37 // 2.8+
38 uint16_t MinimumVoltage; // varies
39 uint16_t MaximumVoltage; // varies
40 uint16_t ConfiguredVoltage; // varies
41
42 // 3.2+
43 uint8_t MemoryTechnology; // varies
44 uint16_t MemoryOperatingMode; // bit field
45 uint8_t FirmwareVersion; // string
46 uint16_t ModuleManufacturerID; // varies
47 uint16_t ModuleProductID; // varies
48 uint16_t MemorySubsystemControllerManufacturerID; // vaies
49 uint16_t MemorySubsystemControllerProductID; // varies
50 uint64_t NonVolatileSize; // varies
51 uint64_t VolatileSize; // varies
52 uint64_t CacheSize; // varies
53 uint64_t LogicalSize; // varies
54
55 // 3.3+
56 uint32_t ExtendedSpeed; // varies
57 uint32_t ExtendedConfiguredSpeed; // varies
58
59 // 3.7+
60 uint16_t Pmic0ManufacturerID; // varies
61 uint16_t Pmic0RevisionNumber; // varies
62 uint16_t RcdManufacturerID; // varies
63 uint16_t RcdRevisionNumber; // varies
64} FFSmbiosMemoryDevice;
65
66static_assert(offsetof(FFSmbiosMemoryDevice, RcdRevisionNumber) == 0x62,
67 "FFSmbiosMemoryDevice: Wrong struct alignment");
68
69const char* ffDetectPhysicalMemory(FFPhysicalMemoryOptions* options, FFlist* result) {
70 const FFSmbiosHeaderTable* smbiosTable = ffGetSmbiosHeaderTable();
71 if (!smbiosTable) {
72 FF_DEBUG("Failed to get SMBIOS data");
73 return "Failed to get SMBIOS data";
74 }
75
76 const FFSmbiosMemoryDevice* data = (const FFSmbiosMemoryDevice*) (*smbiosTable)[FF_SMBIOS_TYPE_MEMORY_DEVICE];
77 if (!data) {
78 FF_DEBUG("Memory device is not found in SMBIOS data");
79 return "Memory device is not found in SMBIOS data";
80 }
81
82 const FFSmbiosMemoryDevice* endOfTable = (const FFSmbiosMemoryDevice*) (*smbiosTable)[FF_SMBIOS_TYPE_END_OF_TABLE];
83 for (; data != endOfTable; data = (const FFSmbiosMemoryDevice*) ffSmbiosNextEntry(&data->Header)) {
84 if (data->Header.Type != FF_SMBIOS_TYPE_MEMORY_DEVICE) {
85 continue;
86 }
87
88 const char* strings = (const char*) data + data->Header.Length;
89 uint16_t size = FF_READ_LE(data->Size);
90 bool installed = size != 0;
91
92 if (!installed && !options->showEmptySlots) {
93 continue;
94 }
95
96 FFPhysicalMemoryResult* device = FF_LIST_ADD(FFPhysicalMemoryResult, *result);
97 ffStrbufInit(&device->type);
98 ffStrbufInit(&device->formFactor);
99 ffStrbufInit(&device->locator);
100 ffStrbufInit(&device->vendor);
101 ffStrbufInit(&device->serial);
102 ffStrbufInit(&device->partNumber);
103 device->size = 0;
104 device->maxSpeed = 0;
105 device->runningSpeed = 0;
106 device->installed = installed;
107 device->ecc = false;
108
109 uint16_t totalWidth = FF_READ_LE(data->TotalWidth);
110 uint16_t dataWidth = FF_READ_LE(data->DataWidth);
111 if (installed && totalWidth != 0xFFFF && dataWidth != 0xFFFF) {
112 device->ecc = totalWidth > dataWidth;
113 }
114
115 if (installed && size != 0xFFFF) {
116 if (size == 0x7FFF) {
117 device->size = (FF_READ_LE(data->ExtendedSize) & ~(1ULL << 31)) * 1024ULL * 1024ULL;
118 } else if (size & (1 << 15)) {
119 // in kB
120 device->size = (size & ~(1ULL << 15)) * 1024ULL;
121 } else {
122 // in MB
123 device->size = size * 1024ULL * 1024ULL;
124 }
125 }
126
127 // https://github.com/fastfetch-cli/fastfetch/issues/1051#issuecomment-2206687345
128 const char* lbank = ffSmbiosLocateString(strings, data->BankLocator);
129 const char* ldevice = ffSmbiosLocateString(strings, data->DeviceLocator);
130 if (lbank && ldevice) {
131 ffStrbufSetF(&device->locator, "%s/%s", lbank, ldevice);
132 } else if (lbank) {
133 ffStrbufSetS(&device->locator, lbank);
134 } else if (ldevice) {
135 ffStrbufSetS(&device->locator, ldevice);
136 }
137
138 const char* formFactorNames[] = {
139 nullptr, // 0x00 (Placeholder for indexing)
140 "Other", // 0x01
141 "Unknown", // 0x02
142 "SIMM", // 0x03
143 "SIP", // 0x04
144 "Chip", // 0x05
145 "DIP", // 0x06
146 "ZIP", // 0x07
147 "Proprietary Card", // 0x08
148 "DIMM", // 0x09
149 "TSOP", // 0x0A
150 "Row of chips", // 0x0B
151 "RIMM", // 0x0C
152 "SODIMM", // 0x0D
153 "SRIMM", // 0x0E
154 "FBDIMM", // 0x0F
155 "Die", // 0x10
156 "CAMM", // 0x11
157 "CUDIMM", // 0x12
158 "CSODIMM", // 0x13
159 };
160 if (data->FormFactor > 0 && data->FormFactor < ARRAY_SIZE(formFactorNames)) {
161 ffStrbufSetS(&device->formFactor, formFactorNames[data->FormFactor]);
162 } else {
163 ffStrbufSetF(&device->formFactor, "Unknown (%d)", (int) data->FormFactor);
164 }
165
166 const char* memoryTypeNames[] = {
167 nullptr, // 0x00 (Placeholder for indexing)
168 "Other", // 0x01
169 "Unknown", // 0x02
170 "DRAM", // 0x03
171 "EDRAM", // 0x04
172 "VRAM", // 0x05
173 "SRAM", // 0x06
174 "RAM", // 0x07
175 "ROM", // 0x08
176 "FLASH", // 0x09
177 "EEPROM", // 0x0A
178 "FEPROM", // 0x0B
179 "EPROM", // 0x0C
180 "CDRAM", // 0x0D
181 "3DRAM", // 0x0E
182 "SDRAM", // 0x0F
183 "SGRAM", // 0x10
184 "RDRAM", // 0x11
185 "DDR", // 0x12
186 "DDR2", // 0x13
187 "DDR2 FB-DIMM", // 0x14
188 "Reserved", // 0x15
189 "Reserved", // 0x16
190 "Reserved", // 0x17
191 "DDR3", // 0x18
192 "FBD2", // 0x19
193 "DDR4", // 0x1A
194 "LPDDR", // 0x1B
195 "LPDDR2", // 0x1C
196 "LPDDR3", // 0x1D
197 "LPDDR4", // 0x1E
198 "Logical non-volatile device", // 0x1F
199 "HBM", // 0x20
200 "HBM2", // 0x21
201 "DDR5", // 0x22
202 "LPDDR5", // 0x23
203 "HBM3", // 0x24
204 "MRDIMM", // 0x25
205 };
206 if (!installed) {
207 ffStrbufSetStatic(&device->type, "Empty");
208 } else if (data->MemoryType > 0 && data->MemoryType < ARRAY_SIZE(memoryTypeNames)) {
209 ffStrbufSetStatic(&device->type, memoryTypeNames[data->MemoryType]);
210 } else {
211 ffStrbufSetF(&device->type, "Unknown (%d)", (int) data->MemoryType);
212 }
213
214 if (installed && data->Header.Length > offsetof(FFSmbiosMemoryDevice, Speed)) // 2.3+
215 {
216 uint16_t speed = FF_READ_LE(data->Speed);
217 if (speed) {
218 device->maxSpeed = speed == 0xFFFF ? FF_READ_LE(data->ExtendedSpeed) : speed;
219 }
220
221 ffStrbufSetStatic(&device->vendor, ffSmbiosLocateString(strings, data->Manufacturer));
222 ffCleanUpSmbiosValue(&device->vendor);
223 FFPhysicalMemoryUpdateVendorString(device);
224
225 ffStrbufSetStatic(&device->serial, ffSmbiosLocateString(strings, data->SerialNumber));
226 ffCleanUpSmbiosValue(&device->serial);
227
228 ffStrbufSetStatic(&device->partNumber, ffSmbiosLocateString(strings, data->PartNumber));
229 ffCleanUpSmbiosValue(&device->partNumber);
230 }
231
232 if (installed && data->Header.Length > offsetof(FFSmbiosMemoryDevice, ConfiguredMemorySpeed)) // 2.7+
233 {
234 uint16_t configuredSpeed = FF_READ_LE(data->ConfiguredMemorySpeed);
235 if (configuredSpeed) {
236 device->runningSpeed = configuredSpeed == 0xFFFF
237 ? FF_READ_LE(data->ExtendedConfiguredSpeed)
238 : configuredSpeed;
239 }
240 }
241 }
242
243 return nullptr;
244}