diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2015-08-28 09:58:54 +0800 |
---|---|---|
committer | Yang Zhang <yang.z.zhang@intel.com> | 2015-09-01 12:44:00 +0800 |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/ipxe/src/interface/smbios | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/ipxe/src/interface/smbios')
-rw-r--r-- | qemu/roms/ipxe/src/interface/smbios/smbios.c | 248 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/interface/smbios/smbios_settings.c | 255 |
2 files changed, 503 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/interface/smbios/smbios.c b/qemu/roms/ipxe/src/interface/smbios/smbios.c new file mode 100644 index 000000000..856943428 --- /dev/null +++ b/qemu/roms/ipxe/src/interface/smbios/smbios.c @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <ipxe/uaccess.h> +#include <ipxe/smbios.h> + +/** @file + * + * System Management BIOS + * + */ + +/** SMBIOS entry point descriptor */ +static struct smbios smbios = { + .address = UNULL, +}; + +/** + * Scan for SMBIOS entry point structure + * + * @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 + */ +int find_smbios_entry ( userptr_t start, size_t len, + struct smbios_entry *entry ) { + uint8_t buf[256]; /* 256 is maximum length possible */ + static size_t offset = 0; /* Avoid repeated attempts to locate SMBIOS */ + size_t entry_len; + unsigned int i; + uint8_t sum; + + /* Try to find SMBIOS */ + for ( ; offset < len ; offset += 0x10 ) { + + /* Read start of header and verify signature */ + copy_from_user ( entry, start, offset, sizeof ( *entry ) ); + if ( entry->signature != SMBIOS_SIGNATURE ) + continue; + + /* Read whole header and verify checksum */ + entry_len = entry->len; + assert ( entry_len <= sizeof ( buf ) ); + copy_from_user ( buf, start, offset, entry_len ); + for ( i = 0, sum = 0 ; i < entry_len ; i++ ) { + sum += buf[i]; + } + if ( sum != 0 ) { + DBG ( "SMBIOS at %08lx has bad checksum %02x\n", + user_to_phys ( start, offset ), 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; + } + + DBG ( "No SMBIOS found\n" ); + return -ENODEV; +} + +/** + * Find SMBIOS strings terminator + * + * @v offset Offset to start of strings + * @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; + + for ( ; offset <= max_offset ; offset++ ) { + copy_from_user ( &nulnul, smbios.address, offset, 2 ); + if ( nulnul == 0 ) + return ( offset + 1 ); + } + return 0; +} + +/** + * Find specific structure type within SMBIOS + * + * @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 + */ +int find_smbios_structure ( unsigned int type, unsigned int instance, + struct smbios_structure *structure ) { + unsigned int count = 0; + size_t offset = 0; + size_t strings_offset; + size_t terminator_offset; + int rc; + + /* Find SMBIOS */ + if ( ( smbios.address == UNULL ) && + ( ( rc = find_smbios ( &smbios ) ) != 0 ) ) + return rc; + assert ( smbios.address != UNULL ); + + /* Scan through list of structures */ + while ( ( ( offset + sizeof ( structure->header ) ) < smbios.len ) + && ( count < smbios.count ) ) { + + /* Read next SMBIOS structure header */ + copy_from_user ( &structure->header, smbios.address, offset, + sizeof ( structure->header ) ); + + /* Determine start and extent of strings block */ + strings_offset = ( offset + structure->header.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; + } + terminator_offset = find_strings_terminator ( strings_offset ); + if ( ! terminator_offset ) { + DBG ( "SMBIOS structure at offset %zx has " + "unterminated strings section\n", offset ); + return -ENOENT; + } + 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 ); + + /* If this is the structure we want, return */ + if ( ( structure->header.type == type ) && + ( instance-- == 0 ) ) { + structure->offset = offset; + return 0; + } + + /* Move to next SMBIOS structure */ + offset = ( terminator_offset + 1 ); + count++; + } + + DBG ( "SMBIOS structure type %d not found\n", type ); + return -ENOENT; +} + +/** + * Copy 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 index String index + * @v data Buffer for string + * @v len Length of string buffer + * @ret rc Length of string, or negative error + */ +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. + */ + 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; + } + } + + DBG ( "SMBIOS string index %d not found\n", index ); + return -ENOENT; +} + +/** + * Get SMBIOS version + * + * @ret version Version, or negative error + */ +int smbios_version ( void ) { + int rc; + + /* Find SMBIOS */ + if ( ( smbios.address == UNULL ) && + ( ( rc = find_smbios ( &smbios ) ) != 0 ) ) + return rc; + assert ( smbios.address != UNULL ); + + return smbios.version; +} diff --git a/qemu/roms/ipxe/src/interface/smbios/smbios_settings.c b/qemu/roms/ipxe/src/interface/smbios/smbios_settings.c new file mode 100644 index 000000000..83e4320e9 --- /dev/null +++ b/qemu/roms/ipxe/src/interface/smbios/smbios_settings.c @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <ipxe/settings.h> +#include <ipxe/init.h> +#include <ipxe/uuid.h> +#include <ipxe/smbios.h> + +/** 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 __unused, + struct setting *setting, + void *data, size_t len ) { + struct smbios_structure structure; + unsigned int tag_instance; + unsigned int tag_type; + unsigned int tag_offset; + unsigned int tag_len; + int rc; + + /* 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 */ + 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; + + /* 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. + */ + 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. + */ + raw = &buf[tag_offset]; + if ( ( setting->type == &setting_type_uuid ) && + ( 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 > tag_len ) + len = tag_len; + memcpy ( data, raw, len ); + if ( ! setting->type ) + setting->type = &setting_type_hex; + return tag_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 ) { + int rc; + + if ( ( rc = register_settings ( &smbios_settings, NULL, + "smbios" ) ) != 0 ) { + DBG ( "SMBIOS could not register settings: %s\n", + strerror ( rc ) ); + return; + } +} + +/** SMBIOS settings initialiser */ +struct init_fn smbios_init_fn __init_fn ( INIT_NORMAL ) = { + .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, +}; |