smbios.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017  */
00018 
00019 FILE_LICENCE ( GPL2_OR_LATER );
00020 
00021 #include <stdint.h>
00022 #include <string.h>
00023 #include <errno.h>
00024 #include <assert.h>
00025 #include <gpxe/uaccess.h>
00026 #include <gpxe/smbios.h>
00027 
00028 /** @file
00029  *
00030  * System Management BIOS
00031  *
00032  */
00033 
00034 /** SMBIOS entry point descriptor */
00035 static struct smbios smbios = {
00036         .address = UNULL,
00037 };
00038 
00039 /**
00040  * Find SMBIOS strings terminator
00041  *
00042  * @v offset            Offset to start of strings
00043  * @ret offset          Offset to strings terminator, or 0 if not found
00044  */
00045 static size_t find_strings_terminator ( size_t offset ) {
00046         size_t max_offset = ( smbios.len - 2 );
00047         uint16_t nulnul;
00048 
00049         for ( ; offset <= max_offset ; offset++ ) {
00050                 copy_from_user ( &nulnul, smbios.address, offset, 2 );
00051                 if ( nulnul == 0 )
00052                         return ( offset + 1 );
00053         }
00054         return 0;
00055 }
00056 
00057 /**
00058  * Find specific structure type within SMBIOS
00059  *
00060  * @v type              Structure type to search for
00061  * @v structure         SMBIOS structure descriptor to fill in
00062  * @ret rc              Return status code
00063  */
00064 int find_smbios_structure ( unsigned int type,
00065                             struct smbios_structure *structure ) {
00066         unsigned int count = 0;
00067         size_t offset = 0;
00068         size_t strings_offset;
00069         size_t terminator_offset;
00070         int rc;
00071 
00072         /* Find SMBIOS */
00073         if ( ( smbios.address == UNULL ) &&
00074              ( ( rc = find_smbios ( &smbios ) ) != 0 ) )
00075                 return rc;
00076         assert ( smbios.address != UNULL );
00077 
00078         /* Scan through list of structures */
00079         while ( ( ( offset + sizeof ( structure->header ) ) < smbios.len )
00080                 && ( count < smbios.count ) ) {
00081 
00082                 /* Read next SMBIOS structure header */
00083                 copy_from_user ( &structure->header, smbios.address, offset,
00084                                  sizeof ( structure->header ) );
00085 
00086                 /* Determine start and extent of strings block */
00087                 strings_offset = ( offset + structure->header.len );
00088                 if ( strings_offset > smbios.len ) {
00089                         DBG ( "SMBIOS structure at offset %zx with length "
00090                               "%x extends beyond SMBIOS\n", offset,
00091                               structure->header.len );
00092                         return -ENOENT;
00093                 }
00094                 terminator_offset = find_strings_terminator ( strings_offset );
00095                 if ( ! terminator_offset ) {
00096                         DBG ( "SMBIOS structure at offset %zx has "
00097                               "unterminated strings section\n", offset );
00098                         return -ENOENT;
00099                 }
00100                 structure->strings_len = ( terminator_offset - strings_offset);
00101 
00102                 DBG ( "SMBIOS structure at offset %zx has type %d, length %x, "
00103                       "strings length %zx\n", offset, structure->header.type,
00104                       structure->header.len, structure->strings_len );
00105 
00106                 /* If this is the structure we want, return */
00107                 if ( structure->header.type == type ) {
00108                         structure->offset = offset;
00109                         return 0;
00110                 }
00111 
00112                 /* Move to next SMBIOS structure */
00113                 offset = ( terminator_offset + 1 );
00114                 count++;
00115         }
00116 
00117         DBG ( "SMBIOS structure type %d not found\n", type );
00118         return -ENOENT;
00119 }
00120 
00121 /**
00122  * Copy SMBIOS structure
00123  *
00124  * @v structure         SMBIOS structure descriptor
00125  * @v data              Buffer to hold SMBIOS structure
00126  * @v len               Length of buffer
00127  * @ret rc              Return status code
00128  */
00129 int read_smbios_structure ( struct smbios_structure *structure,
00130                             void *data, size_t len ) {
00131 
00132         assert ( smbios.address != UNULL );
00133 
00134         if ( len > structure->header.len )
00135                 len = structure->header.len;
00136         copy_from_user ( data, smbios.address, structure->offset, len );
00137         return 0;
00138 }
00139 
00140 /**
00141  * Find indexed string within SMBIOS structure
00142  *
00143  * @v structure         SMBIOS structure descriptor
00144  * @v index             String index
00145  * @v data              Buffer for string
00146  * @v len               Length of string buffer
00147  * @ret rc              Length of string, or negative error
00148  */
00149 int read_smbios_string ( struct smbios_structure *structure,
00150                          unsigned int index, void *data, size_t len ) {
00151         size_t strings_start = ( structure->offset + structure->header.len );
00152         size_t strings_end = ( strings_start + structure->strings_len );
00153         size_t offset;
00154         size_t string_len;
00155 
00156         assert ( smbios.address != UNULL );
00157 
00158         /* String numbers start at 1 (0 is used to indicate "no string") */
00159         if ( ! index )
00160                 return -ENOENT;
00161 
00162         for ( offset = strings_start ; offset < strings_end ;
00163               offset += ( string_len + 1 ) ) {
00164                 /* Get string length.  This is known safe, since the
00165                  * smbios_strings struct is constructed so as to
00166                  * always end on a string boundary.
00167                  */
00168                 string_len = strlen_user ( smbios.address, offset );
00169                 if ( --index == 0 ) {
00170                         /* Copy string, truncating as necessary. */
00171                         if ( len > string_len )
00172                                 len = string_len;
00173                         copy_from_user ( data, smbios.address, offset, len );
00174                         return string_len;
00175                 }
00176         }
00177 
00178         DBG ( "SMBIOS string index %d not found\n", index );
00179         return -ENOENT;
00180 }

Generated on Tue Apr 6 20:01:09 2010 for gPXE by  doxygen 1.5.7.1