summaryrefslogtreecommitdiffstats
path: root/src/interface/smbios
diff options
context:
space:
mode:
authorSimon Rettberg2026-01-28 12:53:53 +0100
committerSimon Rettberg2026-01-28 12:53:53 +0100
commit8e82785c584dc13e20f9229decb95bd17bbe9cd1 (patch)
treea8b359e59196be5b2e3862bed189107f4bc9975f /src/interface/smbios
parentMerge branch 'master' into openslx (diff)
parent[prefix] Make unlzma.S compatible with 386 class CPUs (diff)
downloadipxe-openslx.tar.gz
ipxe-openslx.tar.xz
ipxe-openslx.zip
Merge branch 'master' into openslxopenslx
Diffstat (limited to 'src/interface/smbios')
-rw-r--r--src/interface/smbios/smbios.c250
-rw-r--r--src/interface/smbios/smbios_settings.c139
2 files changed, 194 insertions, 195 deletions
diff --git a/src/interface/smbios/smbios.c b/src/interface/smbios/smbios.c
index fdd14499f..a23d9bfa2 100644
--- a/src/interface/smbios/smbios.c
+++ b/src/interface/smbios/smbios.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <string.h>
@@ -38,26 +39,24 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** SMBIOS entry point descriptor */
static struct smbios smbios = {
- .address = UNULL,
+ .address = NULL,
};
/**
* Calculate SMBIOS entry point structure checksum
*
* @v start Start address of region
- * @v offset Offset of SMBIOS entry point structure
* @v len Length of entry point structure
* @ret sum Byte checksum
*/
-static uint8_t smbios_checksum ( userptr_t start, size_t offset, size_t len ) {
- size_t end = ( offset + len );
- uint8_t sum;
- uint8_t byte;
+static uint8_t smbios_checksum ( const void *start, size_t len ) {
+ const uint8_t *byte = start;
+ uint8_t sum = 0;
+
+ /* Compute checksum */
+ while ( len-- )
+ sum += *(byte++);
- for ( sum = 0 ; offset < end ; offset++ ) {
- copy_from_user ( &byte, start, offset, sizeof ( byte ) );
- sum += byte;
- }
return sum;
}
@@ -66,39 +65,45 @@ static uint8_t smbios_checksum ( userptr_t start, size_t offset, size_t len ) {
*
* @v start Start address of region to scan
* @v len Length of region to scan
- * @v entry SMBIOS entry point structure to fill in
- * @ret rc Return status code
+ * @ret entry SMBIOS entry point structure, or NULL if not found
*/
-int find_smbios_entry ( userptr_t start, size_t len,
- struct smbios_entry *entry ) {
+const struct smbios_entry * find_smbios_entry ( const void *start,
+ size_t len ) {
static size_t offset = 0; /* Avoid repeated attempts to locate SMBIOS */
+ const struct smbios_entry *entry;
uint8_t sum;
/* Try to find SMBIOS */
for ( ; ( offset + sizeof ( *entry ) ) <= len ; offset += 0x10 ) {
- /* Read start of header and verify signature */
- copy_from_user ( entry, start, offset, sizeof ( *entry ) );
+ /* Verify signature */
+ entry = ( start + offset );
if ( entry->signature != SMBIOS_SIGNATURE )
continue;
+ /* Verify length */
+ if ( ( entry->len < sizeof ( *entry ) ) ||
+ ( ( offset + entry->len ) > len ) ) {
+ DBGC ( &smbios, "SMBIOS at %#08lx has bad length "
+ "%#02x\n", virt_to_phys ( entry ), entry->len );
+ continue;
+ }
+
/* Verify checksum */
- if ( ( sum = smbios_checksum ( start, offset,
- entry->len ) ) != 0 ) {
- DBG ( "SMBIOS at %08lx has bad checksum %02x\n",
- user_to_phys ( start, offset ), sum );
+ if ( ( sum = smbios_checksum ( entry, entry->len ) ) != 0 ) {
+ DBGC ( &smbios, "SMBIOS at %#08lx has bad checksum "
+ "%#02x\n", virt_to_phys ( entry ), sum );
continue;
}
/* Fill result structure */
- DBG ( "Found SMBIOS v%d.%d entry point at %08lx\n",
- entry->major, entry->minor,
- user_to_phys ( start, offset ) );
- return 0;
+ DBGC ( &smbios, "Found SMBIOS v%d.%d entry point at %#08lx\n",
+ entry->major, entry->minor, virt_to_phys ( entry ) );
+ return entry;
}
- DBG ( "No SMBIOS found\n" );
- return -ENODEV;
+ DBGC ( &smbios, "No SMBIOS found\n" );
+ return NULL;
}
/**
@@ -106,39 +111,45 @@ int find_smbios_entry ( userptr_t start, size_t len,
*
* @v start Start address of region to scan
* @v len Length of region to scan
- * @v entry SMBIOS entry point structure to fill in
- * @ret rc Return status code
+ * @ret entry SMBIOS entry point structure, or NULL if not found
*/
-int find_smbios3_entry ( userptr_t start, size_t len,
- struct smbios3_entry *entry ) {
+const struct smbios3_entry * find_smbios3_entry ( const void *start,
+ size_t len ) {
static size_t offset = 0; /* Avoid repeated attempts to locate SMBIOS */
+ const struct smbios3_entry *entry;
uint8_t sum;
/* Try to find SMBIOS */
for ( ; ( offset + sizeof ( *entry ) ) <= len ; offset += 0x10 ) {
- /* Read start of header and verify signature */
- copy_from_user ( entry, start, offset, sizeof ( *entry ) );
+ /* Verify signature */
+ entry = ( start + offset );
if ( entry->signature != SMBIOS3_SIGNATURE )
continue;
+ /* Verify length */
+ if ( ( entry->len < sizeof ( *entry ) ) ||
+ ( ( offset + entry->len ) > len ) ) {
+ DBGC ( &smbios, "SMBIOS at %#08lx has bad length "
+ "%#02x\n", virt_to_phys ( entry ), entry->len );
+ continue;
+ }
+
/* Verify checksum */
- if ( ( sum = smbios_checksum ( start, offset,
- entry->len ) ) != 0 ) {
- DBG ( "SMBIOS3 at %08lx has bad checksum %02x\n",
- user_to_phys ( start, offset ), sum );
+ if ( ( sum = smbios_checksum ( entry, entry->len ) ) != 0 ) {
+ DBGC ( &smbios, "SMBIOS3 at %#08lx has bad checksum "
+ "%#02x\n", virt_to_phys ( entry ), sum );
continue;
}
/* Fill result structure */
- DBG ( "Found SMBIOS3 v%d.%d entry point at %08lx\n",
- entry->major, entry->minor,
- user_to_phys ( start, offset ) );
- return 0;
+ DBGC ( &smbios, "Found SMBIOS3 v%d.%d entry point at %#08lx\n",
+ entry->major, entry->minor, virt_to_phys ( entry ) );
+ return entry;
}
- DBG ( "No SMBIOS3 found\n" );
- return -ENODEV;
+ DBGC ( &smbios, "No SMBIOS3 found\n" );
+ return NULL;
}
/**
@@ -148,12 +159,15 @@ int find_smbios3_entry ( userptr_t start, size_t len,
* @ret offset Offset to strings terminator, or 0 if not found
*/
static size_t find_strings_terminator ( size_t offset ) {
- size_t max_offset = ( smbios.len - 2 );
- uint16_t nulnul;
+ const uint16_t *nulnul __attribute__ (( aligned ( 1 ) ));
- for ( ; offset <= max_offset ; offset++ ) {
- copy_from_user ( &nulnul, smbios.address, offset, 2 );
- if ( nulnul == 0 )
+ /* Sanity checks */
+ assert ( smbios.address != NULL );
+
+ /* Check for presence of terminating empty string */
+ for ( ; ( offset + sizeof ( *nulnul ) ) <= smbios.len ; offset++ ) {
+ nulnul = ( smbios.address + offset );
+ if ( *nulnul == 0 )
return ( offset + 1 );
}
return 0;
@@ -164,61 +178,59 @@ static size_t find_strings_terminator ( size_t offset ) {
*
* @v type Structure type to search for
* @v instance Instance of this type of structure
- * @v structure SMBIOS structure descriptor to fill in
- * @ret rc Return status code
+ * @ret structure SMBIOS structure header, or NULL if not found
*/
-int find_smbios_structure ( unsigned int type, unsigned int instance,
- struct smbios_structure *structure ) {
+const struct smbios_header * smbios_structure ( unsigned int type,
+ unsigned int instance ) {
+ const struct smbios_header *structure;
unsigned int count = 0;
size_t offset = 0;
size_t strings_offset;
size_t terminator_offset;
+ size_t strings_len;
int rc;
/* Find SMBIOS */
- if ( ( smbios.address == UNULL ) &&
+ if ( ( smbios.address == NULL ) &&
( ( rc = find_smbios ( &smbios ) ) != 0 ) )
- return rc;
- assert ( smbios.address != UNULL );
+ return NULL;
+ assert ( smbios.address != NULL );
/* Scan through list of structures */
- while ( ( ( offset + sizeof ( structure->header ) ) < smbios.len ) &&
+ while ( ( ( offset + sizeof ( *structure ) ) < smbios.len ) &&
( ( smbios.count == 0 ) || ( count < smbios.count ) ) ) {
- /* Read next SMBIOS structure header */
- copy_from_user ( &structure->header, smbios.address, offset,
- sizeof ( structure->header ) );
+ /* Access next SMBIOS structure header */
+ structure = ( smbios.address + offset );
/* Determine start and extent of strings block */
- strings_offset = ( offset + structure->header.len );
+ strings_offset = ( offset + structure->len );
if ( strings_offset > smbios.len ) {
- DBG ( "SMBIOS structure at offset %zx with length "
- "%x extends beyond SMBIOS\n", offset,
- structure->header.len );
- return -ENOENT;
+ DBGC ( &smbios, "SMBIOS structure at offset %#zx "
+ "with length %#x extends beyond SMBIOS\n",
+ offset, structure->len );
+ return NULL;
}
terminator_offset = find_strings_terminator ( strings_offset );
if ( ! terminator_offset ) {
- DBG ( "SMBIOS structure at offset %zx has "
- "unterminated strings section\n", offset );
- return -ENOENT;
+ DBGC ( &smbios, "SMBIOS structure at offset %#zx has "
+ "unterminated strings section\n", offset );
+ return NULL;
}
- structure->strings_len = ( terminator_offset - strings_offset);
-
- DBG ( "SMBIOS structure at offset %zx has type %d, length %x, "
- "strings length %zx\n", offset, structure->header.type,
- structure->header.len, structure->strings_len );
+ strings_len = ( terminator_offset - strings_offset);
+ DBGC ( &smbios, "SMBIOS structure at offset %#zx has type %d, "
+ "length %#x, strings length %#zx\n", offset,
+ structure->type, structure->len, strings_len );
/* Stop if we have reached an end-of-table marker */
if ( ( smbios.count == 0 ) &&
- ( structure->header.type == SMBIOS_TYPE_END ) )
+ ( structure->type == SMBIOS_TYPE_END ) )
break;
/* If this is the structure we want, return */
- if ( ( structure->header.type == type ) &&
+ if ( ( structure->type == type ) &&
( instance-- == 0 ) ) {
- structure->offset = offset;
- return 0;
+ return structure;
}
/* Move to next SMBIOS structure */
@@ -226,69 +238,43 @@ int find_smbios_structure ( unsigned int type, unsigned int instance,
count++;
}
- DBG ( "SMBIOS structure type %d not found\n", type );
- return -ENOENT;
+ DBGC ( &smbios, "SMBIOS structure type %d not found\n", type );
+ return NULL;
}
/**
- * Copy SMBIOS structure
+ * Get indexed string within SMBIOS structure
*
- * @v structure SMBIOS structure descriptor
- * @v data Buffer to hold SMBIOS structure
- * @v len Length of buffer
- * @ret rc Return status code
- */
-int read_smbios_structure ( struct smbios_structure *structure,
- void *data, size_t len ) {
-
- assert ( smbios.address != UNULL );
-
- if ( len > structure->header.len )
- len = structure->header.len;
- copy_from_user ( data, smbios.address, structure->offset, len );
- return 0;
-}
-
-/**
- * Find indexed string within SMBIOS structure
- *
- * @v structure SMBIOS structure descriptor
+ * @v structure SMBIOS structure header
* @v index String index
- * @v data Buffer for string
- * @v len Length of string buffer
- * @ret rc Length of string, or negative error
+ * @ret string SMBIOS string, or NULL if not fond
*/
-int read_smbios_string ( struct smbios_structure *structure,
- unsigned int index, void *data, size_t len ) {
- size_t strings_start = ( structure->offset + structure->header.len );
- size_t strings_end = ( strings_start + structure->strings_len );
- size_t offset;
- size_t string_len;
-
- assert ( smbios.address != UNULL );
-
- /* String numbers start at 1 (0 is used to indicate "no string") */
- if ( ! index )
- return -ENOENT;
-
- for ( offset = strings_start ; offset < strings_end ;
- offset += ( string_len + 1 ) ) {
- /* Get string length. This is known safe, since the
- * smbios_strings struct is constructed so as to
- * always end on a string boundary.
+const char * smbios_string ( const struct smbios_header *structure,
+ unsigned int index ) {
+ const char *string;
+ unsigned int i;
+ size_t len;
+
+ /* Sanity check */
+ assert ( smbios.address != NULL );
+
+ /* Step through strings */
+ string = ( ( ( const void * ) structure ) + structure->len );
+ for ( i = index ; i-- ; ) {
+ /* Get string length. This is known safe, since we
+ * check for the empty-string terminator in
+ * smbios_structure().
*/
- string_len = strlen_user ( smbios.address, offset );
- if ( --index == 0 ) {
- /* Copy string, truncating as necessary. */
- if ( len > string_len )
- len = string_len;
- copy_from_user ( data, smbios.address, offset, len );
- return string_len;
- }
+ len = strlen ( string );
+ if ( ! len )
+ break;
+ if ( i == 0 )
+ return string;
+ string += ( len + 1 /* NUL */ );
}
- DBG ( "SMBIOS string index %d not found\n", index );
- return -ENOENT;
+ DBGC ( &smbios, "SMBIOS string index %d not found\n", index );
+ return NULL;
}
/**
@@ -300,10 +286,10 @@ int smbios_version ( void ) {
int rc;
/* Find SMBIOS */
- if ( ( smbios.address == UNULL ) &&
+ if ( ( smbios.address == NULL ) &&
( ( rc = find_smbios ( &smbios ) ) != 0 ) )
return rc;
- assert ( smbios.address != UNULL );
+ assert ( smbios.address != NULL );
return smbios.version;
}
@@ -315,5 +301,5 @@ int smbios_version ( void ) {
void smbios_clear ( void ) {
/* Clear address */
- smbios.address = UNULL;
+ smbios.address = NULL;
}
diff --git a/src/interface/smbios/smbios_settings.c b/src/interface/smbios/smbios_settings.c
index ec31b43f2..d0ef49d5f 100644
--- a/src/interface/smbios/smbios_settings.c
+++ b/src/interface/smbios/smbios_settings.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdint.h>
#include <string.h>
@@ -81,15 +82,17 @@ static int smbios_applies ( struct settings *settings __unused,
* @v len Length of buffer
* @ret len Length of setting data, or negative error
*/
-static int smbios_fetch ( struct settings *settings __unused,
- struct setting *setting,
+static int smbios_fetch ( struct settings *settings, struct setting *setting,
void *data, size_t len ) {
- struct smbios_structure structure;
+ const struct smbios_header *structure;
unsigned int tag_instance;
unsigned int tag_type;
unsigned int tag_offset;
unsigned int tag_len;
- int rc;
+ const void *src;
+ size_t src_len;
+ unsigned int string;
+ union uuid uuid;
/* Split tag into instance, type, offset and length */
tag_instance = ( ( setting->tag >> 24 ) & 0xff );
@@ -98,66 +101,75 @@ static int smbios_fetch ( struct settings *settings __unused,
tag_len = ( setting->tag & 0xff );
/* Find SMBIOS structure */
- if ( ( rc = find_smbios_structure ( tag_type, tag_instance,
- &structure ) ) != 0 )
- return rc;
-
- {
- uint8_t buf[structure.header.len];
- const void *raw;
- union uuid uuid;
- unsigned int index;
+ structure = smbios_structure ( tag_type, tag_instance );
+ if ( ! structure )
+ return -ENOENT;
+ src = structure;
+ src_len = structure->len;
+ string = 0;
- /* Read SMBIOS structure */
- if ( ( rc = read_smbios_structure ( &structure, buf,
- sizeof ( buf ) ) ) != 0 )
- return rc;
+ /* A <length> of zero indicates that the byte at <offset>
+ * contains a string index. An <offset> of zero indicates
+ * that the <length> contains a literal string index.
+ *
+ * Since the byte at offset zero can never contain a string
+ * index, and a literal string index can never be zero, the
+ * combination of both <length> and <offset> being zero
+ * indicates that the entire structure is to be read.
+ */
+ if ( ( tag_len == 0 ) && ( tag_offset == 0 ) ) {
+ /* Read whole structure */
+ } else if ( ( tag_len == 0 ) || ( tag_offset == 0 ) ) {
+ /* Read string */
+ string = tag_len;
+ if ( ( string == 0 ) && ( tag_offset < src_len ) )
+ string = *( ( uint8_t * ) src + tag_offset );
+ src = smbios_string ( structure, string );
+ if ( ! src )
+ return -ENOENT;
+ assert ( string > 0 );
+ src_len = strlen ( src );
+ } else if ( tag_offset > src_len ) {
+ /* Empty read beyond end of structure */
+ src_len = 0;
+ } else {
+ /* Read partial structure */
+ src += tag_offset;
+ src_len -= tag_offset;
+ if ( src_len > tag_len )
+ src_len = tag_len;
+ }
- /* A <length> of zero indicates that the byte at
- * <offset> contains a string index. An <offset> of
- * zero indicates that the <length> contains a literal
- * string index.
- */
- if ( ( tag_len == 0 ) || ( tag_offset == 0 ) ) {
- index = ( ( tag_offset == 0 ) ?
- tag_len : buf[tag_offset] );
- if ( ( rc = read_smbios_string ( &structure, index,
- data, len ) ) < 0 ) {
- return rc;
- }
- if ( ! setting->type )
- setting->type = &setting_type_string;
- return rc;
- }
+ /* Mangle UUIDs if necessary. iPXE treats UUIDs as being in
+ * network byte order (big-endian). SMBIOS specification
+ * version 2.6 states that UUIDs are stored with little-endian
+ * values in the first three fields; earlier versions did not
+ * specify an endianness. dmidecode assumes that the byte
+ * order is little-endian if and only if the SMBIOS version is
+ * 2.6 or higher; we match this behaviour.
+ */
+ if ( ( ( setting->type == &setting_type_uuid ) ||
+ ( setting->type == &setting_type_guid ) ) &&
+ ( src_len == sizeof ( uuid ) ) &&
+ ( smbios_version() >= SMBIOS_VERSION ( 2, 6 ) ) ) {
+ DBGC ( settings, "SMBIOS detected mangled UUID\n" );
+ memcpy ( &uuid, src, sizeof ( uuid ) );
+ uuid_mangle ( &uuid );
+ src = &uuid;
+ }
- /* Mangle UUIDs if necessary. iPXE treats UUIDs as
- * being in network byte order (big-endian). SMBIOS
- * specification version 2.6 states that UUIDs are
- * stored with little-endian values in the first three
- * fields; earlier versions did not specify an
- * endianness. dmidecode assumes that the byte order
- * is little-endian if and only if the SMBIOS version
- * is 2.6 or higher; we match this behaviour.
- */
- raw = &buf[tag_offset];
- if ( ( ( setting->type == &setting_type_uuid ) ||
- ( setting->type == &setting_type_guid ) ) &&
- ( tag_len == sizeof ( uuid ) ) &&
- ( smbios_version() >= SMBIOS_VERSION ( 2, 6 ) ) ) {
- DBG ( "SMBIOS detected mangled UUID\n" );
- memcpy ( &uuid, &buf[tag_offset], sizeof ( uuid ) );
- uuid_mangle ( &uuid );
- raw = &uuid;
- }
+ /* Return data */
+ if ( len > src_len )
+ len = src_len;
+ memcpy ( data, src, len );
- /* Return data */
- if ( len > tag_len )
- len = tag_len;
- memcpy ( data, raw, len );
- if ( ! setting->type )
- setting->type = &setting_type_hex;
- return tag_len;
+ /* Set default type */
+ if ( ! setting->type ) {
+ setting->type = ( string ? &setting_type_string :
+ &setting_type_hex );
}
+
+ return src_len;
}
/** SMBIOS settings operations */
@@ -177,18 +189,19 @@ static struct settings smbios_settings = {
/** Initialise SMBIOS settings */
static void smbios_init ( void ) {
+ struct settings *settings = &smbios_settings;
int rc;
- if ( ( rc = register_settings ( &smbios_settings, NULL,
- "smbios" ) ) != 0 ) {
- DBG ( "SMBIOS could not register settings: %s\n",
- strerror ( rc ) );
+ if ( ( rc = register_settings ( settings, NULL, "smbios" ) ) != 0 ) {
+ DBGC ( settings, "SMBIOS could not register settings: %s\n",
+ strerror ( rc ) );
return;
}
}
/** SMBIOS settings initialiser */
struct init_fn smbios_init_fn __init_fn ( INIT_NORMAL ) = {
+ .name = "smbios",
.initialise = smbios_init,
};