master ohmu/fastfetch-openbsd / src / common / impl / smbios.c
ohmu  ·  2026-10-15
  1#include "common/endian.h"
  2#include "common/smbios.h"
  3#include "common/io.h"
  4#include "common/mallocHelper.h"
  5#include "common/debug.h"
  6
  7bool ffIsSmbiosValueSet(FFstrbuf* value) {
  8    ffStrbufTrimRightSpace(value);
  9    return value->length > 0 &&
 10        !ffStrbufStartsWithIgnCaseS(value, "To be filled") &&
 11        !ffStrbufStartsWithIgnCaseS(value, "To be set") &&
 12        !ffStrbufStartsWithIgnCaseS(value, "OEM") &&
 13        !ffStrbufStartsWithIgnCaseS(value, "O.E.M.") &&
 14        !ffStrbufStartsWithIgnCaseS(value, "System Product") &&
 15        !ffStrbufStartsWithIgnCaseS(value, "Unknown Product") &&
 16        !ffStrbufIgnCaseEqualS(value, "None") &&
 17        !ffStrbufIgnCaseEqualS(value, "System Name") &&
 18        !ffStrbufIgnCaseEqualS(value, "System Version") &&
 19        !ffStrbufIgnCaseEqualS(value, "System SKU#") &&
 20        !ffStrbufIgnCaseEqualS(value, "Default string") &&
 21        !ffStrbufIgnCaseEqualS(value, "Undefined") &&
 22        !ffStrbufIgnCaseEqualS(value, "Not Specified") &&
 23        !ffStrbufIgnCaseEqualS(value, "Not Applicable") &&
 24        !ffStrbufIgnCaseEqualS(value, "Not Defined") &&
 25        !ffStrbufIgnCaseEqualS(value, "Not Available") &&
 26        !ffStrbufIgnCaseEqualS(value, "INVALID") &&
 27        !ffStrbufIgnCaseEqualS(value, "Type1ProductConfigId") &&
 28        !ffStrbufIgnCaseEqualS(value, "TBD by OEM") &&
 29        !ffStrbufIgnCaseEqualS(value, "No Enclosure") &&
 30        !ffStrbufIgnCaseEqualS(value, "Chassis Version") &&
 31        !ffStrbufIgnCaseEqualS(value, "All Series") &&
 32        !ffStrbufIgnCaseEqualS(value, "N/A") &&
 33        !ffStrbufIgnCaseEqualS(value, "Unknown") &&
 34        !ffStrbufIgnCaseEqualS(value, "Standard") && ({
 35            // Some SMBIOS implementations use "0x0000" to indicate an unset value, even for strings.
 36            bool zero = ffStrbufStartsWithS(value, "0x0");
 37            if (zero) {
 38                for (size_t i = 2; i < value->length; i++) {
 39                    char c = value->chars[i];
 40                    if (c != '0') {
 41                        zero = false;
 42                        break;
 43                    }
 44                }
 45            }
 46            !zero;
 47        });
 48}
 49
 50static bool smbiosTableInitialized = false;
 51static FFSmbiosHeaderTable smbiosTable;
 52
 53const FFSmbiosHeader* ffSmbiosNextEntry(const FFSmbiosHeader* header) {
 54    const char* p = ((const char*) header) + header->Length;
 55    if (*p) {
 56        do {
 57            p += strlen(p) + 1;
 58        } while (*p);
 59    } else { // The terminator is always double 0 even if there is no string
 60        p++;
 61    }
 62
 63    return (const FFSmbiosHeader*) (p + 1);
 64}
 65
 66static bool parseSmbiosTable(const uint8_t* data, uint32_t length) {
 67    const FFSmbiosHeader* endOfTable = nullptr;
 68
 69    FF_DEBUG("Parsing SMBIOS table structures with length %u bytes", length);
 70    [[maybe_unused]] int structureCount = 0, totalCount = 0;
 71    for (
 72        const FFSmbiosHeader* header = (const FFSmbiosHeader*) data;
 73        (const uint8_t*) header + sizeof(FFSmbiosHeader) < (const uint8_t*) data + length;
 74        header = ffSmbiosNextEntry(header)) {
 75        ++totalCount;
 76        endOfTable = header;
 77
 78        if (header->Length < sizeof(FFSmbiosHeader)) {
 79            FF_DEBUG("Invalid SMBIOS structure length %u at offset 0x%lx",
 80                header->Length,
 81                (unsigned long) ((const uint8_t*) header - data));
 82            break;
 83        }
 84
 85            if (FF_READ_LE(header->Handle) >= 0xFF00) {
 86            FF_DEBUG("Invalid SMBIOS structure handle 0x%04x at offset 0x%lx",
 87            FF_READ_LE(header->Handle),
 88                (unsigned long) ((const uint8_t*) header - data));
 89            break;
 90        }
 91
 92        // This doesn't verify the entire structure (e.g. string section can still be truncated),
 93        // but at least ensures the formatted section is valid and prevents infinite loops
 94        // when the table is severely malformed.
 95        if (__builtin_expect((const uint8_t*) header + header->Length > (const uint8_t*) data + length, false)) {
 96            FF_DEBUG("Truncated SMBIOS structure at offset 0x%lx: length %u is too small",
 97                (unsigned long) ((const uint8_t*) header - data),
 98                header->Length);
 99            break;
100        }
101
102        if (header->Type < FF_SMBIOS_TYPE_END_OF_TABLE) {
103            if (!smbiosTable[header->Type]) {
104                smbiosTable[header->Type] = header;
105                FF_DEBUG("Found SMBIOS structure type %u, handle 0x%04X, length %u",
106                    header->Type,
107                    FF_READ_LE(header->Handle),
108                    header->Length);
109                structureCount++;
110            } else {
111                FF_DEBUG("Duplicate SMBIOS structure type %u, handle 0x%04X, length %u",
112                    header->Type,
113                    FF_READ_LE(header->Handle),
114                    header->Length);
115            }
116        } else if (header->Type == FF_SMBIOS_TYPE_END_OF_TABLE) {
117            FF_DEBUG("Reached SMBIOS end of type %u, handle 0x%04X, length %u",
118                header->Type,
119                FF_READ_LE(header->Handle),
120                header->Length);
121            break;
122        } else {
123            FF_DEBUG("Found custom SMBIOS structure type %u, handle 0x%04X, length %u; ignoring",
124                header->Type,
125                FF_READ_LE(header->Handle),
126                header->Length);
127        }
128    }
129
130    if (!endOfTable) {
131        FF_DEBUG("No SMBIOS structures found in table");
132        return false;
133    }
134
135    FF_DEBUG("Parsed %d/%d SMBIOS structures, end-of-table (Type 127) %s",
136        structureCount,
137        totalCount,
138        endOfTable->Type == FF_SMBIOS_TYPE_END_OF_TABLE ? "found." : "not found! SMBIOS data may be malformed.");
139    smbiosTable[FF_SMBIOS_TYPE_END_OF_TABLE] = endOfTable;
140
141    return true;
142}
143
144#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__sun) || defined(__HAIKU__) || defined(__OpenBSD__) || defined(__GNU__)
145    #include <fcntl.h>
146    #include <sys/stat.h>
147    #include <sys/types.h>
148    #include <sys/mman.h>
149    #include <stddef.h>
150
151    #ifdef __linux__
152        #include "common/properties.h"
153    #elif defined(__FreeBSD__)
154        #include "common/settings.h"
155    #elif defined(__NetBSD__)
156        #include "common/sysctl.h"
157    #endif
158
159    #ifdef __linux__
160bool ffGetSmbiosValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer) {
161    // /sys/class/dmi/id/* are all pseudo-files with very small content
162    // so reading the whole file at once is efficient
163    ffStrbufEnsureFixedLengthFree(buffer, 127);
164
165    ssize_t len = ffReadFileData(devicesPath, buffer->allocated - 1, buffer->chars);
166    if (len > 0) {
167        assert(len < buffer->allocated);
168        buffer->chars[len] = '\0';
169        buffer->length = (uint32_t) len;
170        ffStrbufTrimRightSpace(buffer);
171        if (ffIsSmbiosValueSet(buffer)) {
172            return true;
173        }
174    }
175
176    len = ffReadFileData(classPath, buffer->allocated - 1, buffer->chars);
177    if (len > 0) {
178        assert(len < buffer->allocated);
179        buffer->chars[len] = '\0';
180        buffer->length = (uint32_t) len;
181        ffStrbufTrimRightSpace(buffer);
182        if (ffIsSmbiosValueSet(buffer)) {
183            return true;
184        }
185    }
186
187    ffStrbufClear(buffer);
188    return false;
189}
190    #endif
191
192static bool readPhysicalMemory(int fd, off_t address, size_t length, void* buffer) {
193    #if !defined(__FreeBSD__) // Either causes kernel panic or returns EFAULT
194    // -1: unknown, 0: failed before (stop trying), 1: succeeded before
195    static int preadState = -1;
196    if (preadState != 0) {
197        ssize_t bytesRead = pread(fd, buffer, length, address);
198        if (bytesRead == (ssize_t) length) {
199            preadState = 1;
200            return true;
201        }
202
203        FF_DEBUG("pread failed at address 0x%lx for %zu bytes: %s. Falling back to mmap%s",
204            (unsigned long) address,
205            length,
206            strerror(errno),
207            preadState < 0 ? " and caching failure" : "");
208        preadState = 0;
209    } else {
210        FF_DEBUG("Skipping pread due to cached failure; using mmap");
211    }
212    #endif
213
214    off_t alignedAddress = address & ~(((off_t) 1 << instance.state.platform.sysinfo.pageSizeShift) - 1);
215    size_t pageOffset = (size_t) (address - alignedAddress);
216    size_t mapLength = pageOffset + length;
217
218    void* p = mmap(nullptr, mapLength, PROT_READ, MAP_SHARED, fd, alignedAddress);
219    if (p == MAP_FAILED) {
220        FF_DEBUG("mmap failed at aligned address 0x%lx for %zu bytes: %s",
221            (unsigned long) alignedAddress,
222            mapLength,
223            strerror(errno));
224        return false;
225    }
226
227    memcpy(buffer, (const uint8_t*) p + pageOffset, length);
228    munmap(p, mapLength);
229    return true;
230}
231
232typedef struct [[gnu::packed]] FFSmbios20EntryPoint {
233    uint8_t AnchorString[4];
234    uint8_t EntryPointStructureChecksum;
235    uint8_t EntryPointLength;
236    uint8_t SmbiosMajorVersion;
237    uint8_t SmbiosMinorVersion;
238    uint16_t MaximumStructureSize;
239    uint8_t EntryPointRevision;
240    uint8_t FormattedArea[5];
241    uint8_t IntermediateAnchorString[5];
242    uint8_t IntermediateChecksum;
243    uint16_t StructureTableLength;
244    uint32_t StructureTableAddress;
245    uint16_t NumberOfSmbiosStructures;
246    uint8_t SmbiosBcdRevision;
247} FFSmbios20EntryPoint;
248static_assert(offsetof(FFSmbios20EntryPoint, SmbiosBcdRevision) == 0x1E,
249    "FFSmbios20EntryPoint: Wrong struct alignment");
250
251typedef struct [[gnu::packed]] FFSmbios30EntryPoint {
252    uint8_t AnchorString[5];
253    uint8_t EntryPointStructureChecksum;
254    uint8_t EntryPointLength;
255    uint8_t SmbiosMajorVersion;
256    uint8_t SmbiosMinorVersion;
257    uint8_t SmbiosDocrev;
258    uint8_t EntryPointRevision;
259    uint8_t Reversed;
260    uint32_t StructureTableMaximumSize;
261    uint64_t StructureTableAddress;
262} FFSmbios30EntryPoint;
263
264static_assert(offsetof(FFSmbios30EntryPoint, StructureTableAddress) == 0x10,
265    "FFSmbios30EntryPoint: Wrong struct alignment");
266
267typedef union FFSmbiosEntryPoint {
268    FFSmbios20EntryPoint Smbios20;
269    FFSmbios30EntryPoint Smbios30;
270} FFSmbiosEntryPoint;
271
272static bool fillTableBufferFallback(FFstrbuf* buffer) {
273    const char* devMem =
274    #if __HAIKU__
275        "/dev/misc/mem";
276    #else
277        "/dev/mem"; // kern.securelevel must be -1
278    #endif
279    FF_DEBUG("Using physical memory searching implementation: %s", devMem);
280
281    uint32_t tableLength = 0;
282    off_t tableAddress = 0;
283    FF_AUTO_CLOSE_FD int fd = open(devMem, O_RDONLY | O_CLOEXEC);
284    if (fd < 0) {
285        FF_DEBUG("Failed to open memory device: %s", strerror(errno));
286        return false;
287    }
288    FF_DEBUG("Memory device opened successfully with fd=%d", fd);
289
290    // Works on legacy BIOS only
291    // See: https://wiki.osdev.org/System_Management_BIOS#UEFI_systems
292    // On BSD systems, we can get EFI system resource table (ESRT) via EFIIOC_GET_TABLE
293    // However, to acquire SMBIOS entry point, we need EFI configuration table (provided by EFI system table)
294    // which is not available via EFIIOC_GET_TABLE.
295    FF_AUTO_FREE uint8_t* smBiosBase = malloc(0x10000);
296    if (!readPhysicalMemory(fd, 0xF0000, 0x10000, smBiosBase)) {
297        FF_DEBUG("Failed to read SMBIOS memory region");
298        return false;
299    }
300    FF_DEBUG("Successfully read 0x10000 bytes from physical address 0xF0000");
301
302    for (off_t offset = 0; offset <= 0xffe0; offset += 0x10) {
303        FFSmbiosEntryPoint* p = (void*) (smBiosBase + offset);
304        if (memcmp(p, "_SM3_", sizeof(p->Smbios30.AnchorString)) == 0) {
305            FF_DEBUG("Found SMBIOS 3.0 entry point at phyaddr 0x%05lX", (unsigned long) (0xF0000 + offset));
306            if (p->Smbios30.EntryPointLength != sizeof(p->Smbios30)) {
307                FF_DEBUG("Invalid SMBIOS 3.0 entry point length: %u (expected %zu)",
308                    p->Smbios30.EntryPointLength,
309                    sizeof(p->Smbios30));
310                return false;
311            }
312                tableLength = FF_READ_LE(p->Smbios30.StructureTableMaximumSize);
313                tableAddress = (off_t) FF_READ_LE(p->Smbios30.StructureTableAddress);
314            FF_DEBUG("SMBIOS 3.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u.%u",
315                tableLength,
316                (unsigned long) tableAddress,
317                p->Smbios30.SmbiosMajorVersion,
318                p->Smbios30.SmbiosMinorVersion,
319                p->Smbios30.SmbiosDocrev);
320            break;
321        } else if (memcmp(p, "_SM_", sizeof(p->Smbios20.AnchorString)) == 0) {
322            FF_DEBUG("Found SMBIOS 2.0 entry point at phyaddr 0x%05lX", (unsigned long) (0xF0000 + offset));
323            if (p->Smbios20.EntryPointLength != sizeof(p->Smbios20)) {
324                FF_DEBUG("Invalid SMBIOS 2.0 entry point length: %u (expected %zu)",
325                    p->Smbios20.EntryPointLength,
326                    sizeof(p->Smbios20));
327                return false;
328            }
329                tableLength = FF_READ_LE(p->Smbios20.StructureTableLength);
330                tableAddress = (off_t) FF_READ_LE(p->Smbios20.StructureTableAddress);
331            FF_DEBUG("SMBIOS 2.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u",
332                tableLength,
333                (unsigned long) tableAddress,
334                p->Smbios20.SmbiosMajorVersion,
335                p->Smbios20.SmbiosMinorVersion);
336            break;
337        }
338    }
339    if (tableLength == 0) {
340        FF_DEBUG("No valid SMBIOS entry point found in memory region");
341        return false;
342    }
343
344    ffStrbufClear(buffer);
345    ffStrbufEnsureFixedLengthFree(buffer, tableLength);
346    FF_DEBUG("Attempting to read SMBIOS table data: %u bytes at 0x%lx", tableLength, (unsigned long) tableAddress);
347    if (readPhysicalMemory(fd, (off_t) tableAddress, tableLength, buffer->chars)) {
348        buffer->length = tableLength;
349        buffer->chars[buffer->length] = '\0';
350        FF_DEBUG("Successfully read SMBIOS table data: %u bytes", tableLength);
351    } else {
352        FF_DEBUG("Failed to read SMBIOS table data");
353        return false;
354    }
355
356    return true;
357}
358
359    #ifdef __OpenBSD__
360static bool detectSmbiosTableLength(const uint8_t* data, uint32_t bufferLength, uint32_t* tableLength) {
361    const uint8_t* p = data;
362    const uint8_t* end = data + bufferLength;
363
364    while (p + sizeof(FFSmbiosHeader) <= end) {
365        const FFSmbiosHeader* header = (const FFSmbiosHeader*) p;
366        if (header->Length < sizeof(*header)) {
367            FF_DEBUG("Invalid SMBIOS structure length %u at offset 0x%lx",
368                header->Length,
369                (unsigned long) (p - data));
370            return false;
371        }
372
373        if (FF_READ_LE(header->Handle) >= 0xFF00) {
374            FF_DEBUG("Invalid SMBIOS structure handle 0x%04x at offset 0x%lx",
375            FF_READ_LE(header->Handle),
376                (unsigned long) (p - data));
377            return false;
378        }
379
380        const uint8_t* formattedEnd = p + header->Length;
381        if (formattedEnd > end) {
382            FF_DEBUG("Truncated SMBIOS structure at offset 0x%lx: length %u is too small",
383                (unsigned long) (p - data),
384                header->Length);
385            return false;
386        }
387
388        const char* string = (const char*) formattedEnd;
389        const char* stringEnd = (const char*) end;
390        while (true) {
391            size_t remaining = (size_t) (stringEnd - string);
392            if (remaining == 0) {
393                return false;
394            }
395
396            const char* nul = memchr(string, '\0', remaining);
397            if (!nul) {
398                return false;
399            }
400
401            string = nul + 1;
402            if (string >= stringEnd) {
403                return false;
404            }
405
406            if (*string == '\0') {
407                ++string;
408                break;
409            }
410        }
411
412        if (header->Type == FF_SMBIOS_TYPE_END_OF_TABLE) {
413            *tableLength = (uint32_t) (string - (const char*) data);
414            return true;
415        }
416
417        p = (const uint8_t*) string;
418    }
419
420    return false;
421}
422
423static bool fillTableBufferPlatform(FFstrbuf* buffer) {
424    FF_DEBUG("Using OpenBSD /var/run/dmesg.boot implementation");
425
426    char dmesg[2048];
427    ssize_t size = ffReadFileData("/var/run/dmesg.boot", sizeof(dmesg) - 1, dmesg);
428    if (size <= 0) {
429        FF_DEBUG("Failed to read /var/run/dmesg.boot");
430        return false;
431    } else {
432        FF_DEBUG("Successfully read %zd bytes from /var/run/dmesg.boot", size);
433    }
434    dmesg[size] = '\0';
435
436    const char* const needle = "\nbios0 at mainbus0: SMBIOS rev. ";
437    size_t needleLen = strlen(needle);
438    char* line = memmem(dmesg, (size_t) size, needle, needleLen);
439    if (!line) {
440        FF_DEBUG("Failed to find SMBIOS line in /var/run/dmesg.boot");
441        return false;
442    }
443    line += needleLen;
444
445    char* lineEnd = memchr(line, '\n', (size_t) ((dmesg + size) - line));
446    if (!lineEnd) {
447        lineEnd = dmesg + size;
448    }
449
450    char* address = memchr(line, '@', (size_t) (lineEnd - line));
451    if (!address) {
452        FF_DEBUG("Failed to find SMBIOS table address in dmesg line");
453        return false;
454    }
455
456    do {
457        ++address;
458    } while (address < lineEnd && (*address == ' ' || *address == '\t'));
459
460    errno = 0;
461    char* addressEnd = nullptr;
462    unsigned long long parsedAddress = strtoull(address, &addressEnd, 16);
463    if (errno != 0 || addressEnd == address || parsedAddress == 0) {
464        FF_DEBUG("Failed to parse OpenBSD SMBIOS table address from line: %.*s",
465            (int) (lineEnd - line),
466            line);
467        return false;
468    }
469
470    off_t tableAddress = (off_t) parsedAddress;
471    FF_DEBUG("Parsed OpenBSD SMBIOS table address: 0x%llx", parsedAddress);
472
473    FF_AUTO_CLOSE_FD int fd = open("/dev/mem", O_RDONLY | O_CLOEXEC);
474    if (fd < 0) {
475        FF_DEBUG("Failed to open /dev/mem: %s", strerror(errno));
476        return false;
477    }
478
479    uint32_t readLength = 0x10000;
480    ffStrbufClear(buffer);
481    ffStrbufEnsureFixedLengthFree(buffer, readLength);
482
483    FF_DEBUG("Attempting to read OpenBSD SMBIOS table data: %u bytes at 0x%lx",
484        readLength,
485        (unsigned long) tableAddress);
486
487    if (!readPhysicalMemory(fd, tableAddress, readLength, buffer->chars)) {
488        FF_DEBUG("Failed to read OpenBSD SMBIOS table data");
489        return false;
490    }
491
492    uint32_t detectedLength = 0;
493    if (detectSmbiosTableLength((const uint8_t*) buffer->chars, readLength, &detectedLength)) {
494        buffer->length = detectedLength;
495        buffer->chars[buffer->length] = '\0';
496        FF_DEBUG("Determined OpenBSD SMBIOS table length: %u bytes", detectedLength);
497        return true;
498    }
499
500    FF_DEBUG("SMBIOS end-of-table marker not found within first %u bytes; give up", readLength);
501    ffStrbufClear(buffer);
502    return false;
503}
504    #else
505static bool fillTableBufferPlatform(FFstrbuf* buffer) {
506        #if __HAIKU__ && __GNU__
507    return false;
508        #elif defined(__linux__)
509    FF_DEBUG("Using Linux implementation - trying /sys/firmware/dmi/tables/DMI");
510    if (ffAppendFileBuffer("/sys/firmware/dmi/tables/DMI", buffer)) {
511        return true;
512    }
513
514    FF_DEBUG("Failed to read /sys/firmware/dmi/tables/DMI, falling back to memory-mapped implementation");
515        #endif
516
517    {
518        #if !defined(__sun) && !defined(__NetBSD__)
519        FF_DEBUG("Using memory-mapped implementation");
520        FF_STRBUF_AUTO_DESTROY strEntryAddress = ffStrbufCreate();
521            #ifdef __FreeBSD__
522        FF_DEBUG("Using FreeBSD kenv implementation");
523        if (!ffSettingsGetFreeBSDKenv("hint.smbios.0.mem", &strEntryAddress)) {
524            FF_DEBUG("Failed to get SMBIOS address from FreeBSD kenv");
525            return false; // non-UEFI systems
526        }
527        FF_DEBUG("Got SMBIOS address from kenv: %s", strEntryAddress.chars);
528            #elif defined(__linux__)
529        {
530            FF_DEBUG("Using Linux EFI systab implementation");
531            FF_STRBUF_AUTO_DESTROY systab = ffStrbufCreate();
532            if (!ffAppendFileBuffer("/sys/firmware/efi/systab", &systab)) {
533                FF_DEBUG("Failed to read /sys/firmware/efi/systab");
534                return false;
535            }
536            if (!ffParsePropLines(systab.chars, "SMBIOS3=", &strEntryAddress) &&
537                !ffParsePropLines(systab.chars, "SMBIOS=", &strEntryAddress)) {
538                FF_DEBUG("Failed to find SMBIOS entry in systab");
539                return false;
540            }
541            FF_DEBUG("Found SMBIOS entry in systab: %s", strEntryAddress.chars);
542        }
543            #endif
544
545        off_t entryAddress = (off_t) strtol(strEntryAddress.chars, nullptr, 16);
546        if (entryAddress == 0) {
547            FF_DEBUG("Invalid SMBIOS entry address: 0");
548            return false;
549        }
550        FF_DEBUG("Parsed SMBIOS entry address: 0x%lx", (unsigned long) entryAddress);
551
552        FF_AUTO_CLOSE_FD int fd = open("/dev/mem", O_RDONLY | O_CLOEXEC);
553        if (fd < 0) {
554            FF_DEBUG("Failed to open /dev/mem: %s", strerror(errno));
555            return false;
556        }
557        FF_DEBUG("/dev/mem opened successfully with fd=%d", fd);
558
559        FFSmbiosEntryPoint entryPoint;
560        FF_DEBUG("Attempting to read %zu bytes from physical address 0x%lx",
561            sizeof(entryPoint),
562            (unsigned long) entryAddress);
563        if (!readPhysicalMemory(fd, entryAddress, sizeof(entryPoint), &entryPoint)) {
564            return false;
565        }
566        FF_DEBUG("Successfully read SMBIOS entry point data");
567        #else
568        // Sun or NetBSD
569        FF_DEBUG("Using %s specific implementation",
570            #ifdef __NetBSD__
571            "NetBSD"
572            #else
573            "SunOS"
574            #endif
575        );
576
577        FF_AUTO_CLOSE_FD int fd = open("/dev/smbios", O_RDONLY | O_CLOEXEC);
578        if (fd < 0) {
579            FF_DEBUG("Failed to open /dev/smbios: %s", strerror(errno));
580            return false;
581        }
582        FF_DEBUG("/dev/smbios opened successfully with fd=%d", fd);
583
584        FFSmbiosEntryPoint entryPoint;
585            #ifdef __NetBSD__
586        off_t addr = (off_t) ffSysctlGetInt64("machdep.smbios", 0);
587        if (addr == 0) {
588            FF_DEBUG("Failed to get SMBIOS address from sysctl");
589            return false;
590        }
591        FF_DEBUG("Got SMBIOS address from sysctl: 0x%lx", (unsigned long) addr);
592
593        if (pread(fd, &entryPoint, sizeof(entryPoint), addr) < 1) {
594            FF_DEBUG("Failed to read SMBIOS entry point: %s", strerror(errno));
595            return false;
596        }
597        FF_DEBUG("Successfully read SMBIOS entry point");
598            #else
599        FF_DEBUG("Reading SMBIOS entry point from /dev/smbios");
600        if (ffReadFDData(fd, sizeof(entryPoint), &entryPoint) < 1) {
601            FF_DEBUG("Failed to read SMBIOS entry point: %s", strerror(errno));
602            return false;
603        }
604        FF_DEBUG("Successfully read SMBIOS entry point");
605            #endif
606        #endif
607
608        uint32_t tableLength = 0;
609        off_t tableAddress = 0;
610        if (memcmp(entryPoint.Smbios20.AnchorString, "_SM_", sizeof(entryPoint.Smbios20.AnchorString)) == 0) {
611            FF_DEBUG("Found SMBIOS 2.0 entry point");
612            if (entryPoint.Smbios20.EntryPointLength != sizeof(entryPoint.Smbios20)) {
613                FF_DEBUG("Invalid SMBIOS 2.0 entry point length: %u (expected %zu)",
614                    entryPoint.Smbios20.EntryPointLength,
615                    sizeof(entryPoint.Smbios20));
616                return false;
617            }
618            tableLength = FF_READ_LE(entryPoint.Smbios20.StructureTableLength);
619            tableAddress = (off_t) FF_READ_LE(entryPoint.Smbios20.StructureTableAddress);
620            FF_DEBUG("SMBIOS 2.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u",
621                tableLength,
622                (unsigned long) tableAddress,
623                entryPoint.Smbios20.SmbiosMajorVersion,
624                entryPoint.Smbios20.SmbiosMinorVersion);
625        } else if (memcmp(entryPoint.Smbios30.AnchorString, "_SM3_", sizeof(entryPoint.Smbios30.AnchorString)) == 0) {
626            FF_DEBUG("Found SMBIOS 3.0 entry point");
627            if (entryPoint.Smbios30.EntryPointLength != sizeof(entryPoint.Smbios30)) {
628                FF_DEBUG("Invalid SMBIOS 3.0 entry point length: %u (expected %zu)",
629                    entryPoint.Smbios30.EntryPointLength,
630                    sizeof(entryPoint.Smbios30));
631                return false;
632            }
633            tableLength = FF_READ_LE(entryPoint.Smbios30.StructureTableMaximumSize);
634            tableAddress = (off_t) FF_READ_LE(entryPoint.Smbios30.StructureTableAddress);
635            FF_DEBUG("SMBIOS 3.0: tableLength=0x%x, tableAddress=0x%lx, version=%u.%u.%u",
636                tableLength,
637                (unsigned long) tableAddress,
638                entryPoint.Smbios30.SmbiosMajorVersion,
639                entryPoint.Smbios30.SmbiosMinorVersion,
640                entryPoint.Smbios30.SmbiosDocrev);
641        } else {
642            FF_DEBUG("Unknown SMBIOS entry point format");
643            return false; // Dragonfly goes here
644        }
645
646        ffStrbufClear(buffer);
647        ffStrbufEnsureFixedLengthFree(buffer, tableLength);
648        FF_DEBUG("Attempting to read SMBIOS table data: %u bytes at 0x%lx", tableLength, (unsigned long) tableAddress);
649        if (readPhysicalMemory(fd, tableAddress, tableLength, buffer->chars)) {
650            buffer->length = tableLength;
651            buffer->chars[buffer->length] = '\0';
652            FF_DEBUG("Successfully read SMBIOS table data: %u bytes", tableLength);
653        } else {
654            ffStrbufClear(buffer);
655            return false;
656        }
657    }
658
659    return true;
660}
661    #endif
662
663const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() {
664    static FFstrbuf buffer;
665    if (!smbiosTableInitialized) {
666        smbiosTableInitialized = true;
667        FF_DEBUG("Initializing SMBIOS buffer");
668        ffStrbufInit(&buffer);
669
670        if (!fillTableBufferPlatform(&buffer)) {
671            FF_DEBUG("Platform specific SMBIOS retrieval failed, trying fallback method");
672            if (!fillTableBufferFallback(&buffer)) {
673                FF_DEBUG("Fallback SMBIOS retrieval also failed");
674                ffStrbufDestroy(&buffer);
675                return nullptr;
676            }
677        }
678
679        if (!parseSmbiosTable((const uint8_t*) buffer.chars, buffer.length)) {
680            ffStrbufClear(&buffer);
681        }
682    }
683
684    if (buffer.length == 0) {
685        FF_DEBUG("No valid SMBIOS data available");
686        return nullptr;
687    }
688
689    return &smbiosTable;
690}
691#elif defined(_WIN32)
692    #include "common/windows/nt.h"
693
694    #pragma GCC diagnostic ignored "-Wmultichar"
695
696typedef struct FFRawSmbiosData {
697    uint8_t Used20CallingMethod;
698    uint8_t SMBIOSMajorVersion;
699    uint8_t SMBIOSMinorVersion;
700    uint8_t DmiRevision;
701    uint32_t Length;
702    uint8_t SMBIOSTableData[];
703} FFRawSmbiosData;
704
705const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() {
706    static SYSTEM_FIRMWARE_TABLE_INFORMATION* buffer;
707
708    if (!smbiosTableInitialized) {
709        smbiosTableInitialized = true;
710        FF_DEBUG("Initializing Windows SMBIOS buffer");
711
712        FF_DEBUG("Querying system firmware table size with signature 'RSMB'");
713        SYSTEM_FIRMWARE_TABLE_INFORMATION sfti = {
714            .ProviderSignature = 'RSMB',
715            .Action = SystemFirmwareTableGet,
716        };
717        ULONG bufSize = 0;
718        NtQuerySystemInformation(SystemFirmwareTableInformation, &sfti, sizeof(sfti), &bufSize);
719        if (bufSize <= sizeof(FFRawSmbiosData) + sizeof(sfti)) {
720            FF_DEBUG("Invalid firmware table size: %lu (must be > %zu)", bufSize, sizeof(FFRawSmbiosData) + sizeof(sfti));
721            return nullptr;
722        }
723        if (bufSize != sfti.TableBufferLength + (ULONG) sizeof(sfti)) {
724            FF_DEBUG("Firmware table size mismatch: NtQuerySystemInformation returned %lu but expected %lu",
725                bufSize,
726                sfti.TableBufferLength + (ULONG) sizeof(sfti));
727            return nullptr;
728        }
729        FF_DEBUG("Firmware table size: %lu bytes", bufSize);
730
731        buffer = malloc(bufSize);
732        *buffer = sfti;
733        FF_DEBUG("Allocated buffer for SMBIOS data");
734
735        if (!NT_SUCCESS(NtQuerySystemInformation(SystemFirmwareTableInformation, buffer, bufSize, &bufSize))) {
736            FF_DEBUG("NtQuerySystemInformation(SystemFirmwareTableInformation) failed");
737            free(buffer);
738            buffer = nullptr;
739            return nullptr;
740        }
741        FFRawSmbiosData* rawData = (FFRawSmbiosData*) buffer->TableBuffer;
742
743        FF_DEBUG("Successfully retrieved SMBIOS data: version %u.%u, length %u bytes",
744            rawData->SMBIOSMajorVersion,
745            rawData->SMBIOSMinorVersion,
746            rawData->Length);
747
748        if (!parseSmbiosTable(rawData->SMBIOSTableData, rawData->Length)) {
749            free(buffer);
750            buffer = nullptr;
751            return nullptr;
752        }
753    }
754
755    if (!buffer) {
756        FF_DEBUG("No valid SMBIOS data available");
757        return nullptr;
758    }
759    return &smbiosTable;
760}
761#elif defined(__APPLE__)
762    #include "common/apple/cf_helpers.h"
763
764const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() {
765    static CFDataRef smbiosDataBuffer;
766
767    if (!smbiosTableInitialized) {
768        smbiosTableInitialized = true;
769        FF_DEBUG("Initializing SMBIOS buffer on Apple platform");
770
771        FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t registryEntry = IOServiceGetMatchingService(MACH_PORT_NULL, IOServiceMatching("AppleSMBIOS"));
772
773        if (!registryEntry) {
774            FF_DEBUG("IOServiceGetMatchingService() failed to find AppleSMBIOS");
775            return nullptr;
776        }
777
778        FF_DEBUG("AppleSMBIOS service found, retrieving SMBIOS data");
779        smbiosDataBuffer = IORegistryEntryCreateCFProperty(registryEntry, CFSTR("SMBIOS"), kCFAllocatorDefault, kNilOptions);
780        if (!smbiosDataBuffer) {
781            FF_DEBUG("IORegistryEntryCreateCFProperty() failed to get SMBIOS data");
782            return nullptr;
783        }
784        if (CFGetTypeID(smbiosDataBuffer) != CFDataGetTypeID()) {
785            FF_DEBUG("Unexpected SMBIOS data type: expected CFData");
786            CFRelease(smbiosDataBuffer);
787            smbiosDataBuffer = nullptr;
788            return nullptr;
789        }
790
791        FF_DEBUG("Successfully retrieved SMBIOS data: %lu bytes", CFDataGetLength(smbiosDataBuffer));
792        if (!parseSmbiosTable((const uint8_t*) CFDataGetBytePtr(smbiosDataBuffer), (uint32_t) CFDataGetLength(smbiosDataBuffer))) {
793            CFRelease(smbiosDataBuffer);
794            smbiosDataBuffer = nullptr;
795            return nullptr;
796        }
797    }
798
799    if (!smbiosDataBuffer) {
800        FF_DEBUG("No valid SMBIOS data available");
801        return nullptr;
802    }
803
804    return &smbiosTable;
805}
806#endif