diff options
Diffstat (limited to 'src/interface/smbios/smbios_settings.c')
| -rw-r--r-- | src/interface/smbios/smbios_settings.c | 139 |
1 files changed, 76 insertions, 63 deletions
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, }; |
