diff options
author | Michael Brown | 2008-12-05 00:09:48 +0100 |
---|---|---|
committer | Michael Brown | 2008-12-05 00:19:12 +0100 |
commit | 29480dd7159190b91cca17298a87425a58826d32 (patch) | |
tree | 8fc1e70c689ab20d6ec7e85955a88a03a09354fa /src/interface/smbios | |
parent | [efi] Allow use of EFI configuration tables (diff) | |
download | ipxe-29480dd7159190b91cca17298a87425a58826d32.tar.gz ipxe-29480dd7159190b91cca17298a87425a58826d32.tar.xz ipxe-29480dd7159190b91cca17298a87425a58826d32.zip |
[efi] Use EFI-native mechanism for accessing SMBIOS table
EFI provides a copy of the SMBIOS table accessible via the EFI system
table, which we should use instead of manually scanning through the
F000:0000 segment.
Diffstat (limited to 'src/interface/smbios')
-rw-r--r-- | src/interface/smbios/smbios.c | 178 | ||||
-rw-r--r-- | src/interface/smbios/smbios_settings.c | 201 |
2 files changed, 379 insertions, 0 deletions
diff --git a/src/interface/smbios/smbios.c b/src/interface/smbios/smbios.c new file mode 100644 index 00000000..8207c1fa --- /dev/null +++ b/src/interface/smbios/smbios.c @@ -0,0 +1,178 @@ +/* + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <gpxe/uaccess.h> +#include <gpxe/smbios.h> + +/** @file + * + * System Management BIOS + * + */ + +/** SMBIOS entry point descriptor */ +static struct smbios smbios = { + .address = UNULL, +}; + +/** + * 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 structure SMBIOS structure descriptor to fill in + * @ret rc Return status code + */ +int find_smbios_structure ( unsigned int type, + 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 ) { + 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; +} diff --git a/src/interface/smbios/smbios_settings.c b/src/interface/smbios/smbios_settings.c new file mode 100644 index 00000000..61c2d919 --- /dev/null +++ b/src/interface/smbios/smbios_settings.c @@ -0,0 +1,201 @@ +/* + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <gpxe/settings.h> +#include <gpxe/init.h> +#include <gpxe/uuid.h> +#include <gpxe/smbios.h> + +/** SMBIOS settings tag magic number */ +#define SMBIOS_TAG_MAGIC 0x5B /* "SmBios" */ + +/** + * Construct SMBIOS empty tag + * + * @ret tag SMBIOS setting tag + */ +#define SMBIOS_EMPTY_TAG ( SMBIOS_TAG_MAGIC << 24 ) + +/** + * 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 ) \ + ( ( SMBIOS_TAG_MAGIC << 24 ) | \ + ( (_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 ) \ + ( ( SMBIOS_TAG_MAGIC << 24 ) | \ + ( (_type) << 16 ) | \ + ( offsetof ( _structure, _field ) << 8 ) ) + +/** + * Store value of SMBIOS setting + * + * @v settings Settings block + * @v setting Setting to store + * @v data Setting data, or NULL to clear setting + * @v len Length of setting data + * @ret rc Return status code + */ +static int smbios_store ( struct settings *settings __unused, + struct setting *setting __unused, + const void *data __unused, size_t len __unused ) { + /* Cannot write data into SMBIOS */ + return -ENOTSUP; +} + +/** + * 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_magic; + unsigned int tag_type; + unsigned int tag_offset; + unsigned int tag_len; + int rc; + + /* Split tag into type, offset and length */ + tag_magic = ( setting->tag >> 24 ); + tag_type = ( ( setting->tag >> 16 ) & 0xff ); + tag_offset = ( ( setting->tag >> 8 ) & 0xff ); + tag_len = ( setting->tag & 0xff ); + if ( tag_magic != SMBIOS_TAG_MAGIC ) + return -ENOENT; + + /* Find SMBIOS structure */ + if ( ( rc = find_smbios_structure ( tag_type, &structure ) ) != 0 ) + return rc; + + { + uint8_t buf[structure.header.len]; + + /* Read SMBIOS structure */ + if ( ( rc = read_smbios_structure ( &structure, buf, + sizeof ( buf ) ) ) != 0 ) + return rc; + + if ( tag_len == 0 ) { + /* String */ + return read_smbios_string ( &structure, + buf[tag_offset], + data, len ); + } else { + /* Raw data */ + if ( len > tag_len ) + len = tag_len; + memcpy ( data, &buf[tag_offset], len ); + return tag_len; + } + } +} + +/** SMBIOS settings operations */ +static struct settings_operations smbios_settings_operations = { + .store = smbios_store, + .fetch = smbios_fetch, +}; + +/** SMBIOS settings */ +static struct settings smbios_settings = { + .refcnt = NULL, + .name = "smbios", + .tag_magic = SMBIOS_EMPTY_TAG, + .siblings = LIST_HEAD_INIT ( smbios_settings.siblings ), + .children = LIST_HEAD_INIT ( smbios_settings.children ), + .op = &smbios_settings_operations, +}; + +/** Initialise SMBIOS settings */ +static void smbios_init ( void ) { + int rc; + + if ( ( rc = register_settings ( &smbios_settings, NULL ) ) != 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 */ +struct setting uuid_setting __setting = { + .name = "uuid", + .description = "UUID", + .tag = SMBIOS_RAW_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION, + struct smbios_system_information, uuid ), + .type = &setting_type_uuid, +}; + +/** Other SMBIOS named settings */ +struct setting smbios_named_settings[] __setting = { + { + .name = "manufacturer", + .description = "Manufacturer", + .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION, + struct smbios_system_information, + manufacturer ), + .type = &setting_type_string, + }, + { + .name = "product", + .description = "Product name", + .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION, + struct smbios_system_information, + product ), + .type = &setting_type_string, + }, + { + .name = "serial", + .description = "Serial number", + .tag = SMBIOS_STRING_TAG ( SMBIOS_TYPE_SYSTEM_INFORMATION, + struct smbios_system_information, + serial ), + .type = &setting_type_string, + }, +}; |