mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-13 02:42:09 +02:00
806 lines
30 KiB
C
806 lines
30 KiB
C
#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 <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/mman.h>
|
|
#include <stddef.h>
|
|
|
|
#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
|