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