summaryrefslogtreecommitdiffstats
path: root/src/detection/physicalmemory
diff options
context:
space:
mode:
Diffstat (limited to 'src/detection/physicalmemory')
-rw-r--r--src/detection/physicalmemory/physicalmemory.c73
-rw-r--r--src/detection/physicalmemory/physicalmemory.h22
-rw-r--r--src/detection/physicalmemory/physicalmemory_apple.m163
-rw-r--r--src/detection/physicalmemory/physicalmemory_linux.c235
-rw-r--r--src/detection/physicalmemory/physicalmemory_nosupport.c6
5 files changed, 499 insertions, 0 deletions
diff --git a/src/detection/physicalmemory/physicalmemory.c b/src/detection/physicalmemory/physicalmemory.c
new file mode 100644
index 0000000..0188e05
--- /dev/null
+++ b/src/detection/physicalmemory/physicalmemory.c
@@ -0,0 +1,73 @@
+#include "physicalmemory.h"
+
+static inline const char* getVendorString(unsigned vendorId) {
+ switch (vendorId) {
+ case 0x017A:
+ return "Apacer";
+ case 0x0198:
+ return "Kingston";
+ case 0x029E:
+ return "Corsair";
+ case 0x04CB:
+ return "A-DATA";
+ case 0x04CD:
+ return "G-Skill";
+ case 0x059B:
+ case 0x859B:
+ return "Crucial";
+ case 0x00CE:
+ case 0x80CE:
+ case 0xCE00:
+ return "Samsung";
+ case 0x014F:
+ return "Transcend";
+ case 0x2C00:
+ case 0x802C:
+ return "Micron";
+ case 0xAD00:
+ case 0x80AD:
+ return "SK Hynix";
+ case 0x5105:
+ case 0x8551:
+ return "Qimonda";
+ case 0x02FE:
+ return "Elpida";
+ case 0x0467:
+ return "Ramaxel";
+ default:
+ return NULL;
+ }
+}
+
+void FFPhysicalMemoryUpdateVendorString(FFPhysicalMemoryResult* device) {
+ if (device->vendor.length == 0) {
+ return;
+ }
+ if (ffStrbufEqualS(&device->vendor, "Unknown")) {
+ ffStrbufClear(&device->vendor);
+ return;
+ }
+
+ char vendorIdStr[5];
+ if (ffStrbufStartsWithS(&device->vendor, "0x")) {
+ if (device->vendor.length < 6) {
+ return;
+ }
+ memcpy(vendorIdStr, device->vendor.chars + 2, 4);
+ } else {
+ if (device->vendor.length < 4) {
+ return;
+ }
+ memcpy(vendorIdStr, device->vendor.chars, 4);
+ }
+ vendorIdStr[4] = '\0';
+ char* pEnd = NULL;
+ uint32_t vendorId = (uint32_t) strtoul(vendorIdStr, &pEnd, 16);
+ if (*pEnd != '\0') {
+ return;
+ }
+ const char* vendorStr = getVendorString(vendorId);
+ if (vendorStr) {
+ ffStrbufSetStatic(&device->vendor, vendorStr);
+ }
+}
diff --git a/src/detection/physicalmemory/physicalmemory.h b/src/detection/physicalmemory/physicalmemory.h
new file mode 100644
index 0000000..d4c8511
--- /dev/null
+++ b/src/detection/physicalmemory/physicalmemory.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "fastfetch.h"
+#include "modules/physicalmemory/option.h"
+
+typedef struct FFPhysicalMemoryResult {
+ uint64_t size; // B
+ uint32_t maxSpeed; // MT/s
+ uint32_t runningSpeed; // MT/s
+ bool installed;
+ FFstrbuf type;
+ FFstrbuf formFactor;
+ FFstrbuf locator;
+ FFstrbuf partNumber;
+ FFstrbuf vendor;
+ FFstrbuf serial;
+ bool ecc;
+} FFPhysicalMemoryResult;
+
+const char* ffDetectPhysicalMemory(FFPhysicalMemoryOptions* options, FFlist* result); // list of FFPhysicalMemoryResult
+
+void FFPhysicalMemoryUpdateVendorString(FFPhysicalMemoryResult* device);
diff --git a/src/detection/physicalmemory/physicalmemory_apple.m b/src/detection/physicalmemory/physicalmemory_apple.m
new file mode 100644
index 0000000..e35f538
--- /dev/null
+++ b/src/detection/physicalmemory/physicalmemory_apple.m
@@ -0,0 +1,163 @@
+#include "physicalmemory.h"
+#include "common/processing.h"
+#include "common/smbios.h"
+#include "common/strutil.h"
+#include "common/apple/cf_helpers.h"
+
+#import <Foundation/Foundation.h>
+
+static void appendDevice(
+ FFlist* result,
+ NSString* type,
+ NSString* vendor,
+ NSString* size,
+
+ // Intel only
+ NSString* locator,
+ NSString* serial,
+ NSString* partNumber,
+ NSString* speed,
+ bool ecc)
+{
+ FFPhysicalMemoryResult* device = FF_LIST_ADD(FFPhysicalMemoryResult, *result);
+ ffStrbufInitS(&device->type, type.UTF8String);
+ ffStrbufInit(&device->formFactor);
+ ffStrbufInitS(&device->locator, locator.UTF8String);
+ ffStrbufInitS(&device->vendor, vendor.UTF8String);
+ FFPhysicalMemoryUpdateVendorString(device);
+ ffStrbufInitS(&device->serial, serial.UTF8String);
+ ffCleanUpSmbiosValue(&device->serial);
+ ffStrbufInitS(&device->partNumber, partNumber.UTF8String);
+ ffCleanUpSmbiosValue(&device->partNumber);
+ device->size = 0;
+ device->maxSpeed = 0;
+ device->runningSpeed = 0;
+ device->installed = true;
+ device->ecc = ecc;
+
+ if (size)
+ {
+ char* unit = NULL;
+ device->size = strtoul(size.UTF8String, &unit, 10);
+ if (*unit == ' ') ++unit;
+
+ switch (*unit)
+ {
+ case 'G': device->size *= 1024ULL * 1024 * 1024; break;
+ case 'M': device->size *= 1024ULL * 1024; break;
+ case 'K': device->size *= 1024ULL; break;
+ case 'T': device->size *= 1024ULL * 1024 * 1024 * 1024; break;
+ }
+ }
+
+ if (speed)
+ {
+ char* unit = NULL;
+ device->maxSpeed = (uint32_t) strtoul(speed.UTF8String, &unit, 10);
+ if (*unit == ' ') ++unit;
+
+ switch (*unit)
+ {
+ case 'T': device->maxSpeed *= 1000 * 1000; break;
+ case 'G': device->maxSpeed *= 1000; break;
+ case 'K': device->maxSpeed /= 1000; break;
+ }
+ }
+}
+
+static const char* detectFromSystemProfiler(FFlist* result)
+{
+ FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
+ if (ffProcessAppendStdOut(&buffer, (char* const[]) {
+ "system_profiler",
+ "SPMemoryDataType",
+ "-xml",
+ "-detailLevel",
+ "full",
+ NULL
+ }) != NULL)
+ return "Starting `system_profiler SPMemoryDataType -xml -detailLevel full` failed";
+
+ NSArray* arr = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytes:buffer.chars length:buffer.length]
+ options:NSPropertyListImmutable
+ format:nil
+ error:nil];
+ if (!arr || !arr.count)
+ return "system_profiler SPMemoryDataType returned an empty array";
+
+ for (NSDictionary* data in arr[0][@"_items"])
+ {
+ if (data[@"_items"])
+ {
+ // for Intel
+ for (NSDictionary* item in data[@"_items"])
+ {
+ appendDevice(result,
+ item[@"dimm_type"],
+ item[@"dimm_manufacturer"],
+ item[@"dimm_size"],
+ item[@"_name"],
+ item[@"dimm_serial_number"],
+ item[@"dimm_part_number"],
+ item[@"dimm_speed"],
+ !![data[@"global_ecc_state"] isEqualToString:@"ecc_enabled"]);
+ }
+ }
+ else
+ {
+ // for Apple Silicon
+ appendDevice(result,
+ data[@"dimm_type"],
+ data[@"dimm_manufacturer"],
+ data[@"SPMemoryDataType"],
+ nil,
+ nil,
+ nil,
+ nil,
+ false);
+ }
+ }
+
+ return NULL;
+}
+
+FF_A_UNUSED static const char* detectFromIokit(FFlist* result)
+{
+ FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryDevice = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/chosen");
+ if (!entryDevice)
+ return "IORegistryEntryFromPath() failed";
+
+ FF_CFTYPE_AUTO_RELEASE CFTypeRef dramType = IORegistryEntryCreateCFProperty(entryDevice, CFSTR("dram-type"), kCFAllocatorDefault, 0);
+ FF_CFTYPE_AUTO_RELEASE CFTypeRef dramSize = IORegistryEntryCreateCFProperty(entryDevice, CFSTR("dram-size"), kCFAllocatorDefault, 0);
+ FF_CFTYPE_AUTO_RELEASE CFTypeRef dramVendor = IORegistryEntryCreateCFProperty(entryDevice, CFSTR("dram-vendor"), kCFAllocatorDefault, 0);
+ if (!dramType || !dramSize || !dramVendor)
+ return "IORegistryEntryCreateCFProperty() failed";
+
+ FFPhysicalMemoryResult* device = FF_LIST_ADD(FFPhysicalMemoryResult, *result);
+ ffStrbufInit(&device->type);
+ ffStrbufInit(&device->formFactor);
+ ffStrbufInit(&device->locator);
+ ffStrbufInit(&device->vendor);
+ ffStrbufInit(&device->serial);
+ ffStrbufInit(&device->partNumber);
+ device->size = 0;
+ device->maxSpeed = 0;
+ device->runningSpeed = 0;
+ device->installed = true;
+ device->ecc = false;
+
+ ffCfStrGetString(dramType, &device->type);
+ ffCfStrGetString(dramVendor, &device->vendor);
+ ffCfNumGetInt64(dramSize, (int64_t*) &device->size);
+ return NULL;
+}
+
+const char* ffDetectPhysicalMemory(FF_A_UNUSED FFPhysicalMemoryOptions* options, FFlist* result)
+{
+ #if __aarch64__
+ if (detectFromIokit(result) == NULL)
+ return NULL;
+ #endif
+
+ return detectFromSystemProfiler(result);
+}
diff --git a/src/detection/physicalmemory/physicalmemory_linux.c b/src/detection/physicalmemory/physicalmemory_linux.c
new file mode 100644
index 0000000..02df789
--- /dev/null
+++ b/src/detection/physicalmemory/physicalmemory_linux.c
@@ -0,0 +1,235 @@
+#include "physicalmemory.h"
+#include "common/smbios.h"
+
+// 7.18
+typedef struct FFSmbiosMemoryDevice {
+ FFSmbiosHeader Header;
+
+ // 2.1+
+ uint16_t PhysicalMemoryArrayHandle; // varies
+ uint16_t MemoryErrorInformationHandle; // varies
+ uint16_t TotalWidth; // varies
+ uint16_t DataWidth; // varies
+ uint16_t Size; // varies
+ uint8_t FormFactor; // enum
+ uint8_t DeviceSet; // varies
+ uint8_t DeviceLocator; // string
+ uint8_t BankLocator; // string
+ uint8_t MemoryType; // enum
+ uint16_t TypeDetail; // bit field
+
+ // 2.3+
+ uint16_t Speed; // varies
+ uint8_t Manufacturer; // string
+ uint8_t SerialNumber; // string
+ uint8_t AssetTag; // string
+ uint8_t PartNumber; // string
+
+ // 2.6+
+ uint8_t Attributes; // varies
+
+ // 2.7+
+ uint32_t ExtendedSize; // varies
+ uint16_t ConfiguredMemorySpeed; // varies
+
+ // 2.8+
+ uint16_t MinimumVoltage; // varies
+ uint16_t MaximumVoltage; // varies
+ uint16_t ConfiguredVoltage; // varies
+
+ // 3.2+
+ uint8_t MemoryTechnology; // varies
+ uint16_t MemoryOperatingMode; // bit field
+ uint8_t FirmwareVersion; // string
+ uint16_t ModuleManufacturerID; // varies
+ uint16_t ModuleProductID; // varies
+ uint16_t MemorySubsystemControllerManufacturerID; // vaies
+ uint16_t MemorySubsystemControllerProductID; // varies
+ uint64_t NonVolatileSize; // varies
+ uint64_t VolatileSize; // varies
+ uint64_t CacheSize; // varies
+ uint64_t LogicalSize; // varies
+
+ // 3.3+
+ uint32_t ExtendedSpeed; // varies
+ uint32_t ExtendedConfiguredSpeed; // varies
+
+ // 3.7+
+ uint16_t Pmic0ManufacturerID; // varies
+ uint16_t Pmic0RevisionNumber; // varies
+ uint16_t RcdManufacturerID; // varies
+ uint16_t RcdRevisionNumber; // varies
+} FF_A_PACKED FFSmbiosMemoryDevice;
+
+static_assert(offsetof(FFSmbiosMemoryDevice, RcdRevisionNumber) == 0x62,
+ "FFSmbiosMemoryDevice: Wrong struct alignment");
+
+const char* ffDetectPhysicalMemory(FFPhysicalMemoryOptions* options, FFlist* result) {
+ const FFSmbiosHeaderTable* smbiosTable = ffGetSmbiosHeaderTable();
+ if (!smbiosTable) {
+ return "Failed to get SMBIOS data";
+ }
+
+ const FFSmbiosMemoryDevice* data = (const FFSmbiosMemoryDevice*) (*smbiosTable)[FF_SMBIOS_TYPE_MEMORY_DEVICE];
+ if (!data) {
+ return "Memory device is not found in SMBIOS data";
+ }
+
+ const FFSmbiosMemoryDevice* endOfTable = (const FFSmbiosMemoryDevice*) (*smbiosTable)[FF_SMBIOS_TYPE_END_OF_TABLE];
+ for (; data != endOfTable; data = (const FFSmbiosMemoryDevice*) ffSmbiosNextEntry(&data->Header)) {
+ if (data->Header.Type != FF_SMBIOS_TYPE_MEMORY_DEVICE) {
+ continue;
+ }
+
+ const char* strings = (const char*) data + data->Header.Length;
+ bool installed = data->Size != 0;
+
+ if (!installed && !options->showEmptySlots) {
+ continue;
+ }
+
+ FFPhysicalMemoryResult* device = FF_LIST_ADD(FFPhysicalMemoryResult, *result);
+ ffStrbufInit(&device->type);
+ ffStrbufInit(&device->formFactor);
+ ffStrbufInit(&device->locator);
+ ffStrbufInit(&device->vendor);
+ ffStrbufInit(&device->serial);
+ ffStrbufInit(&device->partNumber);
+ device->size = 0;
+ device->maxSpeed = 0;
+ device->runningSpeed = 0;
+ device->installed = installed;
+ device->ecc = false;
+
+ if (installed && data->TotalWidth != 0xFFFF && data->DataWidth != 0xFFFF) {
+ device->ecc = data->TotalWidth > data->DataWidth;
+ }
+
+ if (installed && data->Size != 0xFFFF) {
+ if (data->Size == 0x7FFF) {
+ device->size = (data->ExtendedSize & ~(1ULL << 31)) * 1024ULL * 1024ULL;
+ } else if (data->Size & (1 << 15)) {
+ // in kB
+ device->size = (data->Size & ~(1ULL << 15)) * 1024ULL;
+ } else {
+ // in MB
+ device->size = data->Size * 1024ULL * 1024ULL;
+ }
+ }
+
+ // https://github.com/fastfetch-cli/fastfetch/issues/1051#issuecomment-2206687345
+ const char* lbank = ffSmbiosLocateString(strings, data->BankLocator);
+ const char* ldevice = ffSmbiosLocateString(strings, data->DeviceLocator);
+ if (lbank && ldevice) {
+ ffStrbufSetF(&device->locator, "%s/%s", lbank, ldevice);
+ } else if (lbank) {
+ ffStrbufSetS(&device->locator, lbank);
+ } else if (ldevice) {
+ ffStrbufSetS(&device->locator, ldevice);
+ }
+
+ const char* formFactorNames[] = {
+ NULL, // 0x00 (Placeholder for indexing)
+ "Other", // 0x01
+ "Unknown", // 0x02
+ "SIMM", // 0x03
+ "SIP", // 0x04
+ "Chip", // 0x05
+ "DIP", // 0x06
+ "ZIP", // 0x07
+ "Proprietary Card", // 0x08
+ "DIMM", // 0x09
+ "TSOP", // 0x0A
+ "Row of chips", // 0x0B
+ "RIMM", // 0x0C
+ "SODIMM", // 0x0D
+ "SRIMM", // 0x0E
+ "FBDIMM", // 0x0F
+ "Die", // 0x10
+ "CAMM", // 0x11
+ "CUDIMM", // 0x12
+ "CSODIMM", // 0x13
+ };
+ if (data->FormFactor > 0 && data->FormFactor < ARRAY_SIZE(formFactorNames)) {
+ ffStrbufSetS(&device->formFactor, formFactorNames[data->FormFactor]);
+ } else {
+ ffStrbufSetF(&device->formFactor, "Unknown (%d)", (int) data->FormFactor);
+ }
+
+ const char* memoryTypeNames[] = {
+ NULL, // 0x00 (Placeholder for indexing)
+ "Other", // 0x01
+ "Unknown", // 0x02
+ "DRAM", // 0x03
+ "EDRAM", // 0x04
+ "VRAM", // 0x05
+ "SRAM", // 0x06
+ "RAM", // 0x07
+ "ROM", // 0x08
+ "FLASH", // 0x09
+ "EEPROM", // 0x0A
+ "FEPROM", // 0x0B
+ "EPROM", // 0x0C
+ "CDRAM", // 0x0D
+ "3DRAM", // 0x0E
+ "SDRAM", // 0x0F
+ "SGRAM", // 0x10
+ "RDRAM", // 0x11
+ "DDR", // 0x12
+ "DDR2", // 0x13
+ "DDR2 FB-DIMM", // 0x14
+ "Reserved", // 0x15
+ "Reserved", // 0x16
+ "Reserved", // 0x17
+ "DDR3", // 0x18
+ "FBD2", // 0x19
+ "DDR4", // 0x1A
+ "LPDDR", // 0x1B
+ "LPDDR2", // 0x1C
+ "LPDDR3", // 0x1D
+ "LPDDR4", // 0x1E
+ "Logical non-volatile device", // 0x1F
+ "HBM", // 0x20
+ "HBM2", // 0x21
+ "DDR5", // 0x22
+ "LPDDR5", // 0x23
+ "HBM3", // 0x24
+ "MRDIMM", // 0x25
+ };
+ if (!installed) {
+ ffStrbufSetStatic(&device->type, "Empty");
+ } else if (data->MemoryType > 0 && data->MemoryType < ARRAY_SIZE(memoryTypeNames)) {
+ ffStrbufSetStatic(&device->type, memoryTypeNames[data->MemoryType]);
+ } else {
+ ffStrbufSetF(&device->type, "Unknown (%d)", (int) data->MemoryType);
+ }
+
+ if (installed && data->Header.Length > offsetof(FFSmbiosMemoryDevice, Speed)) // 2.3+
+ {
+ if (data->Speed) {
+ device->maxSpeed = data->Speed == 0xFFFF ? data->ExtendedSpeed : data->Speed;
+ }
+
+ ffStrbufSetStatic(&device->vendor, ffSmbiosLocateString(strings, data->Manufacturer));
+ ffCleanUpSmbiosValue(&device->vendor);
+ FFPhysicalMemoryUpdateVendorString(device);
+
+ ffStrbufSetStatic(&device->serial, ffSmbiosLocateString(strings, data->SerialNumber));
+ ffCleanUpSmbiosValue(&device->serial);
+
+ ffStrbufSetStatic(&device->partNumber, ffSmbiosLocateString(strings, data->PartNumber));
+ ffCleanUpSmbiosValue(&device->partNumber);
+ }
+
+ if (installed && data->Header.Length > offsetof(FFSmbiosMemoryDevice, ConfiguredMemorySpeed)) // 2.7+
+ {
+ if (data->ConfiguredMemorySpeed) {
+ device->runningSpeed = data->ConfiguredMemorySpeed == 0xFFFF
+ ? data->ExtendedConfiguredSpeed
+ : data->ConfiguredMemorySpeed;
+ }
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/detection/physicalmemory/physicalmemory_nosupport.c b/src/detection/physicalmemory/physicalmemory_nosupport.c
new file mode 100644
index 0000000..2edb3a7
--- /dev/null
+++ b/src/detection/physicalmemory/physicalmemory_nosupport.c
@@ -0,0 +1,6 @@
+#include "physicalmemory.h"
+
+const char* ffDetectPhysicalMemory(FFPhysicalMemoryOptions* options, FFlist* result) {
+ FF_UNUSED(options, result);
+ return "Not supported on this platform";
+}