Skip to content

Commit 683e90c

Browse files
committed
Smbios (FreeBSD): unifies pread/mmap handling
Don't try `pread(/dev/mem)` on FreeBSD, which causes kernel panic
1 parent 08de5ea commit 683e90c

1 file changed

Lines changed: 55 additions & 40 deletions

File tree

src/common/impl/smbios.c

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include "common/smbios.h"
22
#include "common/io.h"
3-
#include "common/unused.h"
43
#include "common/mallocHelper.h"
54
#include "common/debug.h"
65

@@ -138,14 +137,54 @@ static bool parseSmbiosTable(const uint8_t* data, uint32_t length) {
138137
# include "common/properties.h"
139138
# elif defined(__FreeBSD__)
140139
# include "common/settings.h"
141-
# define loff_t off_t // FreeBSD doesn't have loff_t
142-
# elif defined(__sun)
140+
# define loff_t off_t
141+
# elif defined(__sun) || defined(__OpenBSD__)
143142
# define loff_t off_t
144143
# elif defined(__NetBSD__)
145144
# include "common/sysctl.h"
146145
# define loff_t off_t
147146
# endif
148147

148+
static bool readPhysicalMemory(int fd, loff_t address, size_t length, void* buffer) {
149+
# if !defined(__FreeBSD__) // Either causes kernel panic or returns EFAULT
150+
// -1: unknown, 0: failed before (stop trying), 1: succeeded before
151+
static int preadState = -1;
152+
if (preadState != 0) {
153+
ssize_t bytesRead = pread(fd, buffer, length, address);
154+
if (bytesRead == (ssize_t) length) {
155+
preadState = 1;
156+
return true;
157+
}
158+
159+
FF_DEBUG("pread failed at address 0x%lx for %zu bytes: %s. Falling back to mmap%s",
160+
(unsigned long) address,
161+
length,
162+
strerror(errno),
163+
preadState < 0 ? " and caching failure" : "");
164+
preadState = 0;
165+
} else {
166+
FF_DEBUG("Skipping pread due to cached failure; using mmap");
167+
}
168+
# endif
169+
170+
loff_t alignedAddress = address & ~((loff_t) instance.state.platform.sysinfo.pageSize - 1);
171+
size_t pageOffset = (size_t) (address - alignedAddress);
172+
size_t mapLength = pageOffset + length;
173+
174+
void* p = mmap(NULL, mapLength, PROT_READ, MAP_SHARED, fd, alignedAddress);
175+
if (p == MAP_FAILED) {
176+
FF_DEBUG("mmap failed at aligned address 0x%lx for %zu bytes: %s",
177+
(unsigned long) alignedAddress,
178+
mapLength,
179+
strerror(errno));
180+
return false;
181+
}
182+
183+
memcpy(buffer, (const uint8_t*) p + pageOffset, length);
184+
munmap(p, mapLength);
185+
return true;
186+
}
187+
149188
# ifdef __linux__
150189
bool ffGetSmbiosValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer) {
151190
// /sys/class/dmi/id/* are all pseudo-files with very small content
@@ -291,22 +330,10 @@ const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() {
291330
FF_DEBUG("Attempting to read %zu bytes from physical address 0x%lx",
292331
sizeof(entryPoint),
293332
(unsigned long) entryAddress);
294-
295-
if (pread(fd, &entryPoint, sizeof(entryPoint), entryAddress) < 0x10) {
296-
FF_DEBUG("pread failed: %s. Trying mmap", strerror(errno));
297-
// `pread /dev/mem` returns EFAULT in FreeBSD
298-
// https://stackoverflow.com/questions/69372330/how-to-read-dev-mem-using-read
299-
void* p = mmap(NULL, sizeof(entryPoint), PROT_READ, MAP_SHARED, fd, entryAddress);
300-
if (p == MAP_FAILED) {
301-
FF_DEBUG("mmap failed: %s", strerror(errno));
302-
return NULL;
303-
}
304-
memcpy(&entryPoint, p, sizeof(entryPoint));
305-
munmap(p, sizeof(entryPoint));
306-
FF_DEBUG("Successfully read entry point data via mmap");
307-
} else {
308-
FF_DEBUG("Successfully read entry point data via pread");
333+
if (!readPhysicalMemory(fd, entryAddress, sizeof(entryPoint), &entryPoint)) {
334+
return NULL;
309335
}
336+
FF_DEBUG("Successfully read SMBIOS entry point data");
310337
# else
311338
// Sun or NetBSD
312339
FF_DEBUG("Using %s specific implementation",
@@ -388,37 +415,25 @@ const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() {
388415

389416
ffStrbufEnsureFixedLengthFree(&buffer, tableLength);
390417
FF_DEBUG("Attempting to read SMBIOS table data: %u bytes at 0x%lx", tableLength, (unsigned long) tableAddress);
391-
if (pread(fd, buffer.chars, tableLength, tableAddress) == (ssize_t) tableLength) {
418+
if (readPhysicalMemory(fd, tableAddress, tableLength, buffer.chars)) {
392419
buffer.length = tableLength;
393420
buffer.chars[buffer.length] = '\0';
394421
FF_DEBUG("Successfully read SMBIOS table data: %u bytes", tableLength);
395422
} else {
396-
FF_DEBUG("pread failed, trying mmap");
397-
// entryPoint.StructureTableAddress must be page aligned.
398-
// Unaligned physical memory access results in all kinds of crashes.
399-
void* p = mmap(NULL, tableLength, PROT_READ, MAP_SHARED, fd, tableAddress);
400-
if (p == MAP_FAILED) {
401-
FF_DEBUG("mmap failed: %s", strerror(errno));
402-
ffStrbufDestroy(&buffer); // free buffer and reset state
403-
return NULL;
404-
}
405-
ffStrbufSetNS(&buffer, tableLength, (char*) p);
406-
munmap(p, tableLength);
407-
FF_DEBUG("Successfully read SMBIOS table data via mmap: %u bytes", tableLength);
423+
ffStrbufDestroy(&buffer); // free buffer and reset state
424+
return NULL;
408425
}
409426
}
410-
411-
return NULL;
412427
# endif
413428

414429
fallback:
415430
if (buffer.length == 0) {
416431
const char* devMem =
417-
# if __HAIKU__
432+
# if __HAIKU__
418433
"/dev/misc/mem";
419-
# else
434+
# else
420435
"/dev/mem"; // kern.securelevel must be -1
421-
# endif
436+
# endif
422437
FF_DEBUG("Using physical memory searching implementation: %s", devMem);
423438

424439
uint32_t tableLength = 0;
@@ -436,8 +451,8 @@ const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() {
436451
// However, to acquire SMBIOS entry point, we need EFI configuration table (provided by EFI system table)
437452
// which is not available via EFIIOC_GET_TABLE.
438453
FF_AUTO_FREE uint8_t* smBiosBase = malloc(0x10000);
439-
if (pread(fd, smBiosBase, 0x10000, 0xF0000) != 0x10000) {
440-
FF_DEBUG("Failed to read SMBIOS memory region: %s", strerror(errno));
454+
if (!readPhysicalMemory(fd, 0xF0000, 0x10000, smBiosBase)) {
455+
FF_DEBUG("Failed to read SMBIOS memory region");
441456
return NULL;
442457
}
443458
FF_DEBUG("Successfully read 0x10000 bytes from physical address 0xF0000");
@@ -486,12 +501,12 @@ const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() {
486501

487502
ffStrbufEnsureFixedLengthFree(&buffer, tableLength);
488503
FF_DEBUG("Attempting to read SMBIOS table data: %u bytes at 0x%lx", tableLength, (unsigned long) tableAddress);
489-
if (pread(fd, buffer.chars, tableLength, tableAddress) == tableLength) {
504+
if (readPhysicalMemory(fd, (loff_t) tableAddress, tableLength, buffer.chars)) {
490505
buffer.length = tableLength;
491506
buffer.chars[buffer.length] = '\0';
492507
FF_DEBUG("Successfully read SMBIOS table data: %u bytes", tableLength);
493508
} else {
494-
FF_DEBUG("Failed to read SMBIOS table data: %s", strerror(errno));
509+
FF_DEBUG("Failed to read SMBIOS table data");
495510
return NULL;
496511
}
497512
}

0 commit comments

Comments
 (0)