master ohmu/fastfetch-openbsd / src / detection / physicalmemory / physicalmemory_linux.c
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}