[linux] Use generic sysfs mechanism to read SMBIOS table

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2021-03-02 23:43:21 +00:00
parent 6816006808
commit 2a2909cd1f
3 changed files with 88 additions and 65 deletions

View File

@@ -235,5 +235,6 @@ extern int read_smbios_string ( struct smbios_structure *structure,
unsigned int index, unsigned int index,
void *data, size_t len ); void *data, size_t len );
extern int smbios_version ( void ); extern int smbios_version ( void );
extern void smbios_clear ( void );
#endif /* _IPXE_SMBIOS_H */ #endif /* _IPXE_SMBIOS_H */

View File

@@ -21,20 +21,21 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <errno.h> #include <errno.h>
#include <ipxe/linux_api.h> #include <ipxe/linux_api.h>
#include <ipxe/linux_sysfs.h>
#include <ipxe/linux.h> #include <ipxe/linux.h>
#include <ipxe/umalloc.h>
#include <ipxe/init.h>
#include <ipxe/smbios.h> #include <ipxe/smbios.h>
/** SMBIOS entry point filename */
static const char smbios_entry_filename[] =
"/sys/firmware/dmi/tables/smbios_entry_point";
/** SMBIOS filename */ /** SMBIOS filename */
static const char smbios_filename[] = "/dev/mem"; static const char smbios_filename[] = "/sys/firmware/dmi/tables/DMI";
/** SMBIOS entry point scan region start address */ /** Cache SMBIOS data */
#define SMBIOS_ENTRY_START 0xf0000 static userptr_t smbios_data;
/** SMBIOS entry point scan region length */
#define SMBIOS_ENTRY_LEN 0x10000
/** SMBIOS mapping alignment */
#define SMBIOS_ALIGN 0x1000
/** /**
* Find SMBIOS * Find SMBIOS
@@ -43,73 +44,84 @@ static const char smbios_filename[] = "/dev/mem";
* @ret rc Return status code * @ret rc Return status code
*/ */
static int linux_find_smbios ( struct smbios *smbios ) { static int linux_find_smbios ( struct smbios *smbios ) {
struct smbios_entry entry; struct smbios3_entry *smbios3_entry;
void *entry_mem; struct smbios_entry *smbios_entry;
void *smbios_mem; userptr_t entry;
size_t smbios_offset; void *data;
size_t smbios_indent; int len;
size_t smbios_len;
int fd;
int rc; int rc;
/* Open SMBIOS file */ /* Read entry point file */
fd = linux_open ( smbios_filename, O_RDONLY ); len = linux_sysfs_read ( smbios_entry_filename, &entry );
if ( fd < 0 ) { if ( len < 0 ) {
rc = -ELINUX ( linux_errno ); rc = len;
DBGC ( smbios, "SMBIOS could not open %s: %s\n", DBGC ( smbios, "SMBIOS could not read %s: %s\n",
smbios_filename, linux_strerror ( linux_errno ) ); smbios_entry_filename, strerror ( rc ) );
goto err_open; goto err_entry;
}
data = user_to_virt ( entry, 0 );
smbios3_entry = data;
smbios_entry = data;
if ( ( len >= ( ( int ) sizeof ( *smbios3_entry ) ) ) &&
( smbios3_entry->signature == SMBIOS3_SIGNATURE ) ) {
smbios->version = SMBIOS_VERSION ( smbios3_entry->major,
smbios3_entry->minor );
} else if ( ( len >= ( ( int ) sizeof ( *smbios_entry ) ) ) &&
( smbios_entry->signature == SMBIOS_SIGNATURE ) ) {
smbios->version = SMBIOS_VERSION ( smbios_entry->major,
smbios_entry->minor );
} else {
DBGC ( smbios, "SMBIOS invalid entry point %s:\n",
smbios_entry_filename );
DBGC_HDA ( smbios, 0, data, len );
rc = -EINVAL;
goto err_version;
} }
/* Map the region potentially containing the SMBIOS entry point */ /* Read SMBIOS file */
entry_mem = linux_mmap ( NULL, SMBIOS_ENTRY_LEN, PROT_READ, MAP_SHARED, len = linux_sysfs_read ( smbios_filename, &smbios_data );
fd, SMBIOS_ENTRY_START ); if ( len < 0 ) {
if ( entry_mem == MAP_FAILED ) { rc = len;
rc = -ELINUX ( linux_errno ); DBGC ( smbios, "SMBIOS could not read %s: %s\n",
DBGC ( smbios, "SMBIOS could not mmap %s (%#x+%#x): %s\n", smbios_filename, strerror ( rc ) );
smbios_filename, SMBIOS_ENTRY_START, SMBIOS_ENTRY_LEN, goto err_read;
linux_strerror ( linux_errno ) );
goto err_mmap_entry;
} }
/* Scan for the SMBIOS entry point */ /* Populate SMBIOS descriptor */
if ( ( rc = find_smbios_entry ( virt_to_user ( entry_mem ), smbios->address = smbios_data;
SMBIOS_ENTRY_LEN, &entry ) ) != 0 ) smbios->len = len;
goto err_find_entry; smbios->count = 0;
/* Map the region containing the SMBIOS structures */ /* Free entry point */
smbios_indent = ( entry.smbios_address & ( SMBIOS_ALIGN - 1 ) ); ufree ( entry );
smbios_offset = ( entry.smbios_address - smbios_indent );
smbios_len = ( entry.smbios_len + smbios_indent );
smbios_mem = linux_mmap ( NULL, smbios_len, PROT_READ, MAP_SHARED,
fd, smbios_offset );
if ( smbios_mem == MAP_FAILED ) {
rc = -ELINUX ( linux_errno );
DBGC ( smbios, "SMBIOS could not mmap %s (%#zx+%#zx): %s\n",
smbios_filename, smbios_offset, smbios_len,
linux_strerror ( linux_errno ) );
goto err_mmap_smbios;
}
/* Fill in entry point descriptor structure */
smbios->address = virt_to_user ( smbios_mem + smbios_indent );
smbios->len = entry.smbios_len;
smbios->count = entry.smbios_count;
smbios->version = SMBIOS_VERSION ( entry.major, entry.minor );
/* Unmap the entry point region (no longer required) */
linux_munmap ( entry_mem, SMBIOS_ENTRY_LEN );
return 0; return 0;
linux_munmap ( smbios_mem, smbios_len ); ufree ( smbios_data );
err_mmap_smbios: err_read:
err_find_entry: err_version:
linux_munmap ( entry_mem, SMBIOS_ENTRY_LEN ); ufree ( entry );
err_mmap_entry: err_entry:
linux_close ( fd );
err_open:
return rc; return rc;
} }
/**
* Free cached SMBIOS data
*
*/
static void linux_smbios_shutdown ( int booting __unused ) {
/* Clear SMBIOS data pointer */
smbios_clear();
/* Free SMBIOS data */
ufree ( smbios_data );
}
/** SMBIOS shutdown function */
struct startup_fn linux_smbios_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
.name = "linux_smbios",
.shutdown = linux_smbios_shutdown,
};
PROVIDE_SMBIOS ( linux, find_smbios, linux_find_smbios ); PROVIDE_SMBIOS ( linux, find_smbios, linux_find_smbios );

View File

@@ -255,3 +255,13 @@ int smbios_version ( void ) {
return smbios.version; return smbios.version;
} }
/**
* Clear SMBIOS entry point descriptor
*
*/
void smbios_clear ( void ) {
/* Clear address */
smbios.address = UNULL;
}