#include "common/smbios.h" #include "common/io.h" #include "common/mallocHelper.h" #include "common/debug.h" bool ffIsSmbiosValueSet(FFstrbuf* value) { ffStrbufTrimRightSpace(value); return value->length > 0 && !ffStrbufStartsWithIgnCaseS(value, "To be filled") && !ffStrbufStartsWithIgnCaseS(value, "To be set") && !ffStrbufStartsWithIgnCaseS(value, "OEM") && !ffStrbufStartsWithIgnCaseS(value, "O.E.M.") && !ffStrbufStartsWithIgnCaseS(value, "System Product") && !ffStrbufStartsWithIgnCaseS(value, "Unknown Product") && !ffStrbufIgnCaseEqualS(value, "None") && !ffStrbufIgnCaseEqualS(value, "System Name") && !ffStrbufIgnCaseEqualS(value, "System Version") && !ffStrbufIgnCaseEqualS(value, "System SKU#") && !ffStrbufIgnCaseEqualS(value, "Default string") && !ffStrbufIgnCaseEqualS(value, "Undefined") && !ffStrbufIgnCaseEqualS(value, "Not Specified") && !ffStrbufIgnCaseEqualS(value, "Not Applicable") && !ffStrbufIgnCaseEqualS(value, "Not Defined") && !ffStrbufIgnCaseEqualS(value, "Not Available") && !ffStrbufIgnCaseEqualS(value, "INVALID") && !ffStrbufIgnCaseEqualS(value, "Type1ProductConfigId") && !ffStrbufIgnCaseEqualS(value, "TBD by OEM") && !ffStrbufIgnCaseEqualS(value, "No Enclosure") && !ffStrbufIgnCaseEqualS(value, "Chassis Version") && !ffStrbufIgnCaseEqualS(value, "All Series") && !ffStrbufIgnCaseEqualS(value, "N/A") && !ffStrbufIgnCaseEqualS(value, "Unknown") && !ffStrbufIgnCaseEqualS(value, "Standard") && ({ // Some SMBIOS implementations use "0x0000" to indicate an unset value, even for strings. bool zero = ffStrbufStartsWithS(value, "0x0"); if (zero) { for (size_t i = 2; i < value->length; i++) { char c = value->chars[i]; if (c != '0') { zero = false; break; } } } !zero; }); } static bool smbiosTableInitialized = false; static FFSmbiosHeaderTable smbiosTable; const FFSmbiosHeader* ffSmbiosNextEntry(const FFSmbiosHeader* header) { const char* p = ((const char*) header) + header->Length; if (*p) { do { p += strlen(p) + 1; } while (*p); } else { // The terminator is always double 0 even if there is no string p++; } return (const FFSmbiosHeader*) (p + 1); } static bool parseSmbiosTable(const uint8_t* data, uint32_t length) { const FFSmbiosHeader* endOfTable = nullptr; FF_DEBUG("Parsing SMBIOS table structures with length %u bytes", length); FF_A_UNUSED int structureCount = 0, totalCount = 0; for ( const FFSmbiosHeader* header = (const FFSmbiosHeader*) data; (const uint8_t*) header + sizeof(FFSmbiosHeader) < (const uint8_t*) data + length; header = ffSmbiosNextEntry(header)) { ++totalCount; endOfTable = header; if (header->Length < sizeof(FFSmbiosHeader)) { FF_DEBUG("Invalid SMBIOS structure length %u at offset 0x%lx", header->Length, (unsigned long) ((const uint8_t*) header - data)); break; } if (header->Handle >= 0xFF00) { FF_DEBUG("Invalid SMBIOS structure handle 0x%04x at offset 0x%lx", header->Handle, (unsigned long) ((const uint8_t*) header - data)); break; } // This doesn't verify the entire structure (e.g. string section can still be truncated), // but at least ensures the formatted section is valid and prevents infinite loops // when the table is severely malformed. if (__builtin_expect((const uint8_t*) header + header->Length > (const uint8_t*) data + length, false)) { FF_DEBUG("Truncated SMBIOS structure at offset 0x%lx: length %u is too small", (unsigned long) ((const uint8_t*) header - data), header->Length); break; } if (header->Type < FF_SMBIOS_TYPE_END_OF_TABLE) { if (!smbiosTable[header->Type]) { smbiosTable[header->Type] = header; FF_DEBUG("Found SMBIOS structure type %u, handle 0x%04X, length %u", header->Type, header->Handle, header->Length); structureCount++; } else { FF_DEBUG("Duplicate SMBIOS structure type %u, handle 0x%04X, length %u", header->Type, header->Handle, header->Length); } } else if (header->Type == FF_SMBIOS_TYPE_END_OF_TABLE) { FF_DEBUG("Reached SMBIOS end of type %u, handle 0x%04X, length %u", header->Type, header->Handle, header->Length); break; } else { FF_DEBUG("Found custom SMBIOS structure type %u, handle 0x%04X, length %u; ignoring", header->Type, header->Handle, header->Length); } } if (!endOfTable) { FF_DEBUG("No SMBIOS structures found in table"); return false; } FF_DEBUG("Parsed %d/%d SMBIOS structures, end-of-table (Type 127) %s", structureCount, totalCount, endOfTable->Type == FF_SMBIOS_TYPE_END_OF_TABLE ? "found." : "not found! SMBIOS data may be malformed."); smbiosTable[FF_SMBIOS_TYPE_END_OF_TABLE] = endOfTable; return true; } #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__sun) || defined(__HAIKU__) || defined(__OpenBSD__) || defined(__GNU__) #include #include #include #include #include #ifdef __linux__ #include "common/properties.h" #elif defined(__FreeBSD__) #include "common/settings.h" #elif defined(__NetBSD__) #include "common/sysctl.h" #endif #ifdef __linux__ bool ffGetSmbiosValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer) { // /sys/class/dmi/id/* are all pseudo-files with very small content // so reading the whole file at once is efficient ffStrbufEnsureFixedLengthFree(buffer, 127); ssize_t len = ffReadFileData(devicesPath, buffer->allocated - 1, buffer->chars); if (len > 0) { assert(len < buffer->allocated); buffer->chars[len] = '\0'; buffer->length = (uint32_t) len; ffStrbufTrimRightSpace(buffer); if (ffIsSmbiosValueSet(buffer)) { return true; } } len = ffReadFileData(classPath, buffer->allocated - 1, buffer->chars); if (len > 0) { assert(len < buffer->allocated); buffer->chars[len] = '\0'; buffer->length = (uint32_t) len; ffStrbufTrimRightSpace(buffer); if (ffIsSmbiosValueSet(buffer)) { return true; } } ffStrbufClear(buffer); return false; } #endif static bool readPhysicalMemory(int fd, off_t address, size_t length, void* buffer) { #if !defined(__FreeBSD__) // Either causes kernel panic or returns EFAULT // -1: unknown, 0: failed before (stop trying), 1: succeeded before static int preadState = -1; if (preadState != 0) { ssize_t bytesRead = pread(fd, buffer, length, address); if (bytesRead == (ssize_t) length) { preadState = 1; return true; } FF_DEBUG("pread failed at address 0x%lx for %zu bytes: %s. Falling back to mmap%s", (unsigned long) address, length, strerror(errno), preadState < 0 ? " and caching failure" : ""); preadState = 0; } else { FF_DEBUG("Skipping pread due to cached failure; using mmap"); } #endif off_t alignedAddress = address & ~((off_t) instance.state.platform.sysinfo.pageSize - 1); size_t pageOffset = (size_t) (address - alignedAddress); size_t mapLength = pageOffset + length; void* p = mmap(nullptr, mapLength, PROT_READ, MAP_SHARED, fd, alignedAddress); if (p == MAP_FAILED) { FF_DEBUG("mmap failed at aligned address 0x%lx for %zu bytes: %s", (unsigned long) alignedAddress, mapLength, strerror(errno)); return false; } memcpy(buffer, (const uint8_t*) p + pageOffset, length); munmap(p, mapLength); return true; } typedef struct FFSmbios20EntryPoint { uint8_t AnchorString[4]; uint8_t EntryPointStructureChecksum; uint8_t EntryPointLength; uint8_t SmbiosMajorVersion; uint8_t SmbiosMinorVersion; uint16_t MaximumStructureSize; uint8_t EntryPointRevision; uint8_t FormattedArea[5]; uint8_t IntermediateAnchorString[5]; uint8_t IntermediateChecksum; uint16_t StructureTableLength; uint32_t StructureTableAddress; uint16_t NumberOfSmbiosStructures; uint8_t SmbiosBcdRevision; } FF_A_PACKED FFSmbios20EntryPoint; static_assert(offsetof(FFSmbios20EntryPoint, SmbiosBcdRevision) == 0x1E, "FFSmbios20EntryPoint: Wrong struct alignment"); typedef struct FFSmbios30EntryPoint { uint8_t AnchorString[5]; uint8_t EntryPointStructureChecksum; uint8_t EntryPointLength; uint8_t SmbiosMajorVersion; uint8_t SmbiosMinorVersion; uint8_t SmbiosDocrev; uint8_t EntryPointRevision; uint8_t Reversed; uint32_t StructureTableMaximumSize; uint64_t StructureTableAddress; } FF_A_PACKED FFSmbios30EntryPoint; static_assert(offsetof(FFSmbios30EntryPoint, StructureTableAddress) == 0x10, "FFSmbios30EntryPoint: Wrong struct alignment"); typedef union FFSmbiosEntryPoint { FFSmbios20EntryPoint Smbios20; FFSmbios30EntryPoint Smbios30; } FFSmbiosEntryPoint; static bool fillTableBufferFallback(FFstrbuf* buffer) { const char* devMem = #if __HAIKU__ "/dev/misc/mem"; #else "/dev/mem"; // kern.securelevel must be -1 #endif FF_DEBUG("Using physical memory searching implementation: %s", devMem); uint32_t tableLength = 0; off_t tableAddress = 0; FF_AUTO_CLOSE_FD int fd = open(devMem, O_RDONLY | O_CLOEXEC); if (fd < 0) { FF_DEBUG("Failed to open memory device: %s", strerror(errno)); return false; } FF_DEBUG("Memory device opened successfully with fd=%d", fd); // Works on legacy BIOS only // See: https://wiki.osdev.org/System_Management_BIOS#UEFI_systems // On BSD systems, we can get EFI system resource table (ESRT) via EFIIOC_GET_TABLE // However, to acquire SMBIOS entry point, we need EFI configuration table (provided by EFI system table) // which is not available via EFIIOC_GET_TABLE. FF_AUTO_FREE uint8_t* smBiosBase = malloc(0x10000); if (!readPhysicalMemory(fd, 0xF0000, 0x10000, smBiosBase)) { FF_DEBUG("Failed to read SMBIOS memory region"); return false; } FF_DEBUG("Successfully read 0x10000 bytes from physical address 0xF0000"); for (off_t offset = 0; offset <= 0xffe0; offset += 0x10) { FFSmbiosEntryPoint* p = (void*) (smBiosBase + offset); if (memcmp(p, "_SM3_", sizeof(p->Smbios30.AnchorString)) == 0) { FF_DEBUG("Found SMBIOS 3.0 entry point at phyaddr 0x%05lX", (unsigned long) (0xF0000 + offset)); if (p->Smbios30.EntryPointLength != sizeof(p->Smbios30)) { FF_DEBUG("Invalid SMBIOS 3.0 entry point length: %u (expected %zu)", p->Smbios30.EntryPointLength, sizeof(p->Smbios30)); return false; } tableLength = p->Smbios30.StructureTableMaximumSize; tableAddress = (off_t) p->Smbios30.StructureTableAddress; FF_DEBUG("SMBIOS 3.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u.%u", tableLength, (unsigned long) tableAddress, p->Smbios30.SmbiosMajorVersion, p->Smbios30.SmbiosMinorVersion, p->Smbios30.SmbiosDocrev); break; } else if (memcmp(p, "_SM_", sizeof(p->Smbios20.AnchorString)) == 0) { FF_DEBUG("Found SMBIOS 2.0 entry point at phyaddr 0x%05lX", (unsigned long) (0xF0000 + offset)); if (p->Smbios20.EntryPointLength != sizeof(p->Smbios20)) { FF_DEBUG("Invalid SMBIOS 2.0 entry point length: %u (expected %zu)", p->Smbios20.EntryPointLength, sizeof(p->Smbios20)); return false; } tableLength = p->Smbios20.StructureTableLength; tableAddress = (off_t) p->Smbios20.StructureTableAddress; FF_DEBUG("SMBIOS 2.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u", tableLength, (unsigned long) tableAddress, p->Smbios20.SmbiosMajorVersion, p->Smbios20.SmbiosMinorVersion); break; } } if (tableLength == 0) { FF_DEBUG("No valid SMBIOS entry point found in memory region"); return false; } ffStrbufClear(buffer); ffStrbufEnsureFixedLengthFree(buffer, tableLength); FF_DEBUG("Attempting to read SMBIOS table data: %u bytes at 0x%lx", tableLength, (unsigned long) tableAddress); if (readPhysicalMemory(fd, (off_t) tableAddress, tableLength, buffer->chars)) { buffer->length = tableLength; buffer->chars[buffer->length] = '\0'; FF_DEBUG("Successfully read SMBIOS table data: %u bytes", tableLength); } else { FF_DEBUG("Failed to read SMBIOS table data"); return false; } return true; } #ifdef __OpenBSD__ static bool detectSmbiosTableLength(const uint8_t* data, uint32_t bufferLength, uint32_t* tableLength) { const uint8_t* p = data; const uint8_t* end = data + bufferLength; while (p + sizeof(FFSmbiosHeader) <= end) { const FFSmbiosHeader* header = (const FFSmbiosHeader*) p; if (header->Length < sizeof(*header)) { FF_DEBUG("Invalid SMBIOS structure length %u at offset 0x%lx", header->Length, (unsigned long) (p - data)); return false; } if (header->Handle >= 0xFF00) { FF_DEBUG("Invalid SMBIOS structure handle 0x%04x at offset 0x%lx", header->Handle, (unsigned long) (p - data)); return false; } const uint8_t* formattedEnd = p + header->Length; if (formattedEnd > end) { FF_DEBUG("Truncated SMBIOS structure at offset 0x%lx: length %u is too small", (unsigned long) (p - data), header->Length); return false; } const char* string = (const char*) formattedEnd; const char* stringEnd = (const char*) end; while (true) { size_t remaining = (size_t) (stringEnd - string); if (remaining == 0) { return false; } const char* nul = memchr(string, '\0', remaining); if (!nul) { return false; } string = nul + 1; if (string >= stringEnd) { return false; } if (*string == '\0') { ++string; break; } } if (header->Type == FF_SMBIOS_TYPE_END_OF_TABLE) { *tableLength = (uint32_t) (string - (const char*) data); return true; } p = (const uint8_t*) string; } return false; } static bool fillTableBufferPlatform(FFstrbuf* buffer) { FF_DEBUG("Using OpenBSD /var/run/dmesg.boot implementation"); char dmesg[2048]; ssize_t size = ffReadFileData("/var/run/dmesg.boot", sizeof(dmesg) - 1, dmesg); if (size <= 0) { FF_DEBUG("Failed to read /var/run/dmesg.boot"); return false; } else { FF_DEBUG("Successfully read %zd bytes from /var/run/dmesg.boot", size); } dmesg[size] = '\0'; const char* const needle = "\nbios0 at mainbus0: SMBIOS rev. "; size_t needleLen = strlen(needle); char* line = memmem(dmesg, (size_t) size, needle, needleLen); if (!line) { FF_DEBUG("Failed to find SMBIOS line in /var/run/dmesg.boot"); return false; } line += needleLen; char* lineEnd = memchr(line, '\n', (size_t) ((dmesg + size) - line)); if (!lineEnd) { lineEnd = dmesg + size; } char* address = memchr(line, '@', (size_t) (lineEnd - line)); if (!address) { FF_DEBUG("Failed to find SMBIOS table address in dmesg line"); return false; } do { ++address; } while (address < lineEnd && (*address == ' ' || *address == '\t')); errno = 0; char* addressEnd = nullptr; unsigned long long parsedAddress = strtoull(address, &addressEnd, 16); if (errno != 0 || addressEnd == address || parsedAddress == 0) { FF_DEBUG("Failed to parse OpenBSD SMBIOS table address from line: %.*s", (int) (lineEnd - line), line); return false; } off_t tableAddress = (off_t) parsedAddress; FF_DEBUG("Parsed OpenBSD SMBIOS table address: 0x%llx", parsedAddress); FF_AUTO_CLOSE_FD int fd = open("/dev/mem", O_RDONLY | O_CLOEXEC); if (fd < 0) { FF_DEBUG("Failed to open /dev/mem: %s", strerror(errno)); return false; } uint32_t readLength = 0x10000; ffStrbufClear(buffer); ffStrbufEnsureFixedLengthFree(buffer, readLength); FF_DEBUG("Attempting to read OpenBSD SMBIOS table data: %u bytes at 0x%lx", readLength, (unsigned long) tableAddress); if (!readPhysicalMemory(fd, tableAddress, readLength, buffer->chars)) { FF_DEBUG("Failed to read OpenBSD SMBIOS table data"); return false; } uint32_t detectedLength = 0; if (detectSmbiosTableLength((const uint8_t*) buffer->chars, readLength, &detectedLength)) { buffer->length = detectedLength; buffer->chars[buffer->length] = '\0'; FF_DEBUG("Determined OpenBSD SMBIOS table length: %u bytes", detectedLength); return true; } FF_DEBUG("SMBIOS end-of-table marker not found within first %u bytes; give up", readLength); ffStrbufClear(buffer); return false; } #else static bool fillTableBufferPlatform(FFstrbuf* buffer) { #if __HAIKU__ && __GNU__ return false; #elif defined(__linux__) FF_DEBUG("Using Linux implementation - trying /sys/firmware/dmi/tables/DMI"); if (ffAppendFileBuffer("/sys/firmware/dmi/tables/DMI", buffer)) { return true; } FF_DEBUG("Failed to read /sys/firmware/dmi/tables/DMI, falling back to memory-mapped implementation"); #endif { #if !defined(__sun) && !defined(__NetBSD__) FF_DEBUG("Using memory-mapped implementation"); FF_STRBUF_AUTO_DESTROY strEntryAddress = ffStrbufCreate(); #ifdef __FreeBSD__ FF_DEBUG("Using FreeBSD kenv implementation"); if (!ffSettingsGetFreeBSDKenv("hint.smbios.0.mem", &strEntryAddress)) { FF_DEBUG("Failed to get SMBIOS address from FreeBSD kenv"); return false; // non-UEFI systems } FF_DEBUG("Got SMBIOS address from kenv: %s", strEntryAddress.chars); #elif defined(__linux__) { FF_DEBUG("Using Linux EFI systab implementation"); FF_STRBUF_AUTO_DESTROY systab = ffStrbufCreate(); if (!ffAppendFileBuffer("/sys/firmware/efi/systab", &systab)) { FF_DEBUG("Failed to read /sys/firmware/efi/systab"); return false; } if (!ffParsePropLines(systab.chars, "SMBIOS3=", &strEntryAddress) && !ffParsePropLines(systab.chars, "SMBIOS=", &strEntryAddress)) { FF_DEBUG("Failed to find SMBIOS entry in systab"); return false; } FF_DEBUG("Found SMBIOS entry in systab: %s", strEntryAddress.chars); } #endif off_t entryAddress = (off_t) strtol(strEntryAddress.chars, nullptr, 16); if (entryAddress == 0) { FF_DEBUG("Invalid SMBIOS entry address: 0"); return false; } FF_DEBUG("Parsed SMBIOS entry address: 0x%lx", (unsigned long) entryAddress); FF_AUTO_CLOSE_FD int fd = open("/dev/mem", O_RDONLY | O_CLOEXEC); if (fd < 0) { FF_DEBUG("Failed to open /dev/mem: %s", strerror(errno)); return false; } FF_DEBUG("/dev/mem opened successfully with fd=%d", fd); FFSmbiosEntryPoint entryPoint; FF_DEBUG("Attempting to read %zu bytes from physical address 0x%lx", sizeof(entryPoint), (unsigned long) entryAddress); if (!readPhysicalMemory(fd, entryAddress, sizeof(entryPoint), &entryPoint)) { return false; } FF_DEBUG("Successfully read SMBIOS entry point data"); #else // Sun or NetBSD FF_DEBUG("Using %s specific implementation", #ifdef __NetBSD__ "NetBSD" #else "SunOS" #endif ); FF_AUTO_CLOSE_FD int fd = open("/dev/smbios", O_RDONLY | O_CLOEXEC); if (fd < 0) { FF_DEBUG("Failed to open /dev/smbios: %s", strerror(errno)); return false; } FF_DEBUG("/dev/smbios opened successfully with fd=%d", fd); FFSmbiosEntryPoint entryPoint; #ifdef __NetBSD__ off_t addr = (off_t) ffSysctlGetInt64("machdep.smbios", 0); if (addr == 0) { FF_DEBUG("Failed to get SMBIOS address from sysctl"); return false; } FF_DEBUG("Got SMBIOS address from sysctl: 0x%lx", (unsigned long) addr); if (pread(fd, &entryPoint, sizeof(entryPoint), addr) < 1) { FF_DEBUG("Failed to read SMBIOS entry point: %s", strerror(errno)); return false; } FF_DEBUG("Successfully read SMBIOS entry point"); #else FF_DEBUG("Reading SMBIOS entry point from /dev/smbios"); if (ffReadFDData(fd, sizeof(entryPoint), &entryPoint) < 1) { FF_DEBUG("Failed to read SMBIOS entry point: %s", strerror(errno)); return false; } FF_DEBUG("Successfully read SMBIOS entry point"); #endif #endif uint32_t tableLength = 0; off_t tableAddress = 0; if (memcmp(entryPoint.Smbios20.AnchorString, "_SM_", sizeof(entryPoint.Smbios20.AnchorString)) == 0) { FF_DEBUG("Found SMBIOS 2.0 entry point"); if (entryPoint.Smbios20.EntryPointLength != sizeof(entryPoint.Smbios20)) { FF_DEBUG("Invalid SMBIOS 2.0 entry point length: %u (expected %zu)", entryPoint.Smbios20.EntryPointLength, sizeof(entryPoint.Smbios20)); return false; } tableLength = entryPoint.Smbios20.StructureTableLength; tableAddress = (off_t) entryPoint.Smbios20.StructureTableAddress; FF_DEBUG("SMBIOS 2.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u", tableLength, (unsigned long) tableAddress, entryPoint.Smbios20.SmbiosMajorVersion, entryPoint.Smbios20.SmbiosMinorVersion); } else if (memcmp(entryPoint.Smbios30.AnchorString, "_SM3_", sizeof(entryPoint.Smbios30.AnchorString)) == 0) { FF_DEBUG("Found SMBIOS 3.0 entry point"); if (entryPoint.Smbios30.EntryPointLength != sizeof(entryPoint.Smbios30)) { FF_DEBUG("Invalid SMBIOS 3.0 entry point length: %u (expected %zu)", entryPoint.Smbios30.EntryPointLength, sizeof(entryPoint.Smbios30)); return false; } tableLength = entryPoint.Smbios30.StructureTableMaximumSize; tableAddress = (off_t) entryPoint.Smbios30.StructureTableAddress; FF_DEBUG("SMBIOS 3.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u.%u", tableLength, (unsigned long) tableAddress, entryPoint.Smbios30.SmbiosMajorVersion, entryPoint.Smbios30.SmbiosMinorVersion, entryPoint.Smbios30.SmbiosDocrev); } else { FF_DEBUG("Unknown SMBIOS entry point format"); return false; // Dragonfly goes here } ffStrbufClear(buffer); ffStrbufEnsureFixedLengthFree(buffer, tableLength); FF_DEBUG("Attempting to read SMBIOS table data: %u bytes at 0x%lx", tableLength, (unsigned long) tableAddress); if (readPhysicalMemory(fd, tableAddress, tableLength, buffer->chars)) { buffer->length = tableLength; buffer->chars[buffer->length] = '\0'; FF_DEBUG("Successfully read SMBIOS table data: %u bytes", tableLength); } else { ffStrbufClear(buffer); return false; } } return true; } #endif const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() { static FFstrbuf buffer; if (!smbiosTableInitialized) { smbiosTableInitialized = true; FF_DEBUG("Initializing SMBIOS buffer"); ffStrbufInit(&buffer); if (!fillTableBufferPlatform(&buffer)) { FF_DEBUG("Platform specific SMBIOS retrieval failed, trying fallback method"); if (!fillTableBufferFallback(&buffer)) { FF_DEBUG("Fallback SMBIOS retrieval also failed"); ffStrbufDestroy(&buffer); return nullptr; } } if (!parseSmbiosTable((const uint8_t*) buffer.chars, buffer.length)) { ffStrbufClear(&buffer); } } if (buffer.length == 0) { FF_DEBUG("No valid SMBIOS data available"); return nullptr; } return &smbiosTable; } #elif defined(_WIN32) #include "common/windows/nt.h" #pragma GCC diagnostic ignored "-Wmultichar" typedef struct FFRawSmbiosData { uint8_t Used20CallingMethod; uint8_t SMBIOSMajorVersion; uint8_t SMBIOSMinorVersion; uint8_t DmiRevision; uint32_t Length; uint8_t SMBIOSTableData[]; } FFRawSmbiosData; const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() { static SYSTEM_FIRMWARE_TABLE_INFORMATION* buffer; if (!smbiosTableInitialized) { smbiosTableInitialized = true; FF_DEBUG("Initializing Windows SMBIOS buffer"); FF_DEBUG("Querying system firmware table size with signature 'RSMB'"); SYSTEM_FIRMWARE_TABLE_INFORMATION sfti = { .ProviderSignature = 'RSMB', .Action = SystemFirmwareTableGet, }; ULONG bufSize = 0; NtQuerySystemInformation(SystemFirmwareTableInformation, &sfti, sizeof(sfti), &bufSize); if (bufSize <= sizeof(FFRawSmbiosData) + sizeof(sfti)) { FF_DEBUG("Invalid firmware table size: %lu (must be > %zu)", bufSize, sizeof(FFRawSmbiosData) + sizeof(sfti)); return nullptr; } if (bufSize != sfti.TableBufferLength + (ULONG) sizeof(sfti)) { FF_DEBUG("Firmware table size mismatch: NtQuerySystemInformation returned %lu but expected %lu", bufSize, sfti.TableBufferLength + (ULONG) sizeof(sfti)); return nullptr; } FF_DEBUG("Firmware table size: %lu bytes", bufSize); buffer = malloc(bufSize); *buffer = sfti; FF_DEBUG("Allocated buffer for SMBIOS data"); if (!NT_SUCCESS(NtQuerySystemInformation(SystemFirmwareTableInformation, buffer, bufSize, &bufSize))) { FF_DEBUG("NtQuerySystemInformation(SystemFirmwareTableInformation) failed"); free(buffer); buffer = nullptr; return nullptr; } FFRawSmbiosData* rawData = (FFRawSmbiosData*) buffer->TableBuffer; FF_DEBUG("Successfully retrieved SMBIOS data: version %u.%u, length %u bytes", rawData->SMBIOSMajorVersion, rawData->SMBIOSMinorVersion, rawData->Length); if (!parseSmbiosTable(rawData->SMBIOSTableData, rawData->Length)) { free(buffer); buffer = nullptr; return nullptr; } } if (!buffer) { FF_DEBUG("No valid SMBIOS data available"); return nullptr; } return &smbiosTable; } #elif defined(__APPLE__) #include "common/apple/cf_helpers.h" const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() { static CFDataRef smbiosDataBuffer; if (!smbiosTableInitialized) { smbiosTableInitialized = true; FF_DEBUG("Initializing SMBIOS buffer on Apple platform"); FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t registryEntry = IOServiceGetMatchingService(MACH_PORT_NULL, IOServiceMatching("AppleSMBIOS")); if (!registryEntry) { FF_DEBUG("IOServiceGetMatchingService() failed to find AppleSMBIOS"); return nullptr; } FF_DEBUG("AppleSMBIOS service found, retrieving SMBIOS data"); smbiosDataBuffer = IORegistryEntryCreateCFProperty(registryEntry, CFSTR("SMBIOS"), kCFAllocatorDefault, kNilOptions); if (!smbiosDataBuffer) { FF_DEBUG("IORegistryEntryCreateCFProperty() failed to get SMBIOS data"); return nullptr; } if (CFGetTypeID(smbiosDataBuffer) != CFDataGetTypeID()) { FF_DEBUG("Unexpected SMBIOS data type: expected CFData"); CFRelease(smbiosDataBuffer); smbiosDataBuffer = nullptr; return nullptr; } FF_DEBUG("Successfully retrieved SMBIOS data: %lu bytes", CFDataGetLength(smbiosDataBuffer)); if (!parseSmbiosTable((const uint8_t*) CFDataGetBytePtr(smbiosDataBuffer), (uint32_t) CFDataGetLength(smbiosDataBuffer))) { CFRelease(smbiosDataBuffer); smbiosDataBuffer = nullptr; return nullptr; } } if (!smbiosDataBuffer) { FF_DEBUG("No valid SMBIOS data available"); return nullptr; } return &smbiosTable; } #endif