/* * Copyright (C) 2008 Michael Brown . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * You can also choose to distribute this program under the terms of * the Unmodified Binary Distribution Licence (as given in the file * COPYING.UBDL), provided that you have satisfied its requirements. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); FILE_SECBOOT ( PERMITTED ); #include #include #include #include #include #include #include /** SMBIOS settings scope */ static const struct settings_scope smbios_settings_scope; /** * Construct SMBIOS raw-data tag * * @v _type SMBIOS structure type number * @v _structure SMBIOS structure data type * @v _field Field within SMBIOS structure data type * @ret tag SMBIOS setting tag */ #define SMBIOS_RAW_TAG( _type, _structure, _field ) \ ( ( (_type) << 16 ) | \ ( offsetof ( _structure, _field ) << 8 ) | \ ( sizeof ( ( ( _structure * ) 0 )->_field ) ) ) /** * Construct SMBIOS string tag * * @v _type SMBIOS structure type number * @v _structure SMBIOS structure data type * @v _field Field within SMBIOS structure data type * @ret tag SMBIOS setting tag */ #define SMBIOS_STRING_TAG( _type, _structure, _field ) \ ( ( (_type) << 16 ) | \ ( offsetof ( _structure, _field ) << 8 ) ) /** * Check applicability of SMBIOS setting * * @v settings Settings block * @v setting Setting * @ret applies Setting applies within this settings block */ static int smbios_applies ( struct settings *settings __unused, const struct setting *setting ) { return ( setting->scope == &smbios_settings_scope ); } /** * Fetch value of SMBIOS setting * * @v settings Settings block, or NULL to search all blocks * @v setting Setting to fetch * @v data Buffer to fill with setting data * @v len Length of buffer * @ret len Length of setting data, or negative error */ static int smbios_fetch ( struct settings *settings, struct setting *setting, void *data, size_t len ) { const struct smbios_header *structure; unsigned int tag_instance; unsigned int tag_type; unsigned int tag_offset; unsigned int tag_len; 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 ); tag_type = ( ( setting->tag >> 16 ) & 0xff ); tag_offset = ( ( setting->tag >> 8 ) & 0xff ); tag_len = ( setting->tag & 0xff ); /* Find SMBIOS structure */ structure = smbios_structure ( tag_type, tag_instance ); if ( ! structure ) return -ENOENT; src = structure; src_len = structure->len; string = 0; /* A of zero indicates that the byte at * contains a string index. An of zero indicates * that the 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 and 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; } /* 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; } /* Return data */ if ( len > src_len ) len = src_len; memcpy ( data, src, len ); /* Set default type */ if ( ! setting->type ) { setting->type = ( string ? &setting_type_string : &setting_type_hex ); } return src_len; } /** SMBIOS settings operations */ static struct settings_operations smbios_settings_operations = { .applies = smbios_applies, .fetch = smbios_fetch, }; /** SMBIOS settings */ static struct settings smbios_settings = { .refcnt = NULL, .siblings = LIST_HEAD_INIT ( smbios_settings.siblings ), .children = LIST_HEAD_INIT ( smbios_settings.children ), .op = &smbios_settings_operations, .default_scope = &smbios_settings_scope, }; /** Initialise SMBIOS settings */ static void smbios_init ( void ) { struct settings *settings = &smbios_settings; int 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, }; /** UUID setting obtained via SMBIOS */ const struct setting uuid_setting __setting ( SETTING_HOST, uuid ) = { .name = "uuid", .description = "UUID", .tag = SMBIOS_RAW_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION, struct smbios_system_information, uuid ), .type = &setting_type_uuid, .scope = &smbios_settings_scope, }; /** Manufacturer name setting */ const struct setting manufacturer_setting __setting ( SETTING_HOST_EXTRA, manufacturer ) = { .name = "manufacturer", .description = "Manufacturer", .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION, struct smbios_system_information, manufacturer ), .type = &setting_type_string, .scope = &smbios_settings_scope, }; /** Product name setting */ const struct setting product_setting __setting ( SETTING_HOST_EXTRA, product )={ .name = "product", .description = "Product name", .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION, struct smbios_system_information, product ), .type = &setting_type_string, .scope = &smbios_settings_scope, }; /** Serial number setting */ const struct setting serial_setting __setting ( SETTING_HOST_EXTRA, serial ) = { .name = "serial", .description = "Serial number", .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION, struct smbios_system_information, serial ), .type = &setting_type_string, .scope = &smbios_settings_scope, }; /** Asset tag setting */ const struct setting asset_setting __setting ( SETTING_HOST_EXTRA, asset ) = { .name = "asset", .description = "Asset tag", .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_ENCLOSURE_INFORMATION, struct smbios_enclosure_information, asset_tag ), .type = &setting_type_string, .scope = &smbios_settings_scope, }; /** Board serial number setting (may differ from chassis serial number) */ const struct setting board_serial_setting __setting ( SETTING_HOST_EXTRA, board-serial ) = { .name = "board-serial", .description = "Base board serial", .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_BASE_BOARD_INFORMATION, struct smbios_base_board_information, serial ), .type = &setting_type_string, .scope = &smbios_settings_scope, };