Home | History | Annotate | Download | only in smbios
      1 /*
      2  * Copyright (C) 2007 Michael Brown <mbrown (at) fensystems.co.uk>.
      3  *
      4  * This program is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU General Public License as
      6  * published by the Free Software Foundation; either version 2 of the
      7  * License, or any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful, but
     10  * WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU General Public License
     15  * along with this program; if not, write to the Free Software
     16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     17  */
     18 
     19 FILE_LICENCE ( GPL2_OR_LATER );
     20 
     21 #include <stdint.h>
     22 #include <string.h>
     23 #include <errno.h>
     24 #include <assert.h>
     25 #include <gpxe/uaccess.h>
     26 #include <gpxe/smbios.h>
     27 
     28 /** @file
     29  *
     30  * System Management BIOS
     31  *
     32  */
     33 
     34 /** SMBIOS entry point descriptor */
     35 static struct smbios smbios = {
     36 	.address = UNULL,
     37 };
     38 
     39 /**
     40  * Find SMBIOS strings terminator
     41  *
     42  * @v offset		Offset to start of strings
     43  * @ret offset		Offset to strings terminator, or 0 if not found
     44  */
     45 static size_t find_strings_terminator ( size_t offset ) {
     46 	size_t max_offset = ( smbios.len - 2 );
     47 	uint16_t nulnul;
     48 
     49 	for ( ; offset <= max_offset ; offset++ ) {
     50 		copy_from_user ( &nulnul, smbios.address, offset, 2 );
     51 		if ( nulnul == 0 )
     52 			return ( offset + 1 );
     53 	}
     54 	return 0;
     55 }
     56 
     57 /**
     58  * Find specific structure type within SMBIOS
     59  *
     60  * @v type		Structure type to search for
     61  * @v structure		SMBIOS structure descriptor to fill in
     62  * @ret rc		Return status code
     63  */
     64 int find_smbios_structure ( unsigned int type,
     65 			    struct smbios_structure *structure ) {
     66 	unsigned int count = 0;
     67 	size_t offset = 0;
     68 	size_t strings_offset;
     69 	size_t terminator_offset;
     70 	int rc;
     71 
     72 	/* Find SMBIOS */
     73 	if ( ( smbios.address == UNULL ) &&
     74 	     ( ( rc = find_smbios ( &smbios ) ) != 0 ) )
     75 		return rc;
     76 	assert ( smbios.address != UNULL );
     77 
     78 	/* Scan through list of structures */
     79 	while ( ( ( offset + sizeof ( structure->header ) ) < smbios.len )
     80 		&& ( count < smbios.count ) ) {
     81 
     82 		/* Read next SMBIOS structure header */
     83 		copy_from_user ( &structure->header, smbios.address, offset,
     84 				 sizeof ( structure->header ) );
     85 
     86 		/* Determine start and extent of strings block */
     87 		strings_offset = ( offset + structure->header.len );
     88 		if ( strings_offset > smbios.len ) {
     89 			DBG ( "SMBIOS structure at offset %zx with length "
     90 			      "%x extends beyond SMBIOS\n", offset,
     91 			      structure->header.len );
     92 			return -ENOENT;
     93 		}
     94 		terminator_offset = find_strings_terminator ( strings_offset );
     95 		if ( ! terminator_offset ) {
     96 			DBG ( "SMBIOS structure at offset %zx has "
     97 			      "unterminated strings section\n", offset );
     98 			return -ENOENT;
     99 		}
    100 		structure->strings_len = ( terminator_offset - strings_offset);
    101 
    102 		DBG ( "SMBIOS structure at offset %zx has type %d, length %x, "
    103 		      "strings length %zx\n", offset, structure->header.type,
    104 		      structure->header.len, structure->strings_len );
    105 
    106 		/* If this is the structure we want, return */
    107 		if ( structure->header.type == type ) {
    108 			structure->offset = offset;
    109 			return 0;
    110 		}
    111 
    112 		/* Move to next SMBIOS structure */
    113 		offset = ( terminator_offset + 1 );
    114 		count++;
    115 	}
    116 
    117 	DBG ( "SMBIOS structure type %d not found\n", type );
    118 	return -ENOENT;
    119 }
    120 
    121 /**
    122  * Copy SMBIOS structure
    123  *
    124  * @v structure		SMBIOS structure descriptor
    125  * @v data		Buffer to hold SMBIOS structure
    126  * @v len		Length of buffer
    127  * @ret rc		Return status code
    128  */
    129 int read_smbios_structure ( struct smbios_structure *structure,
    130 			    void *data, size_t len ) {
    131 
    132 	assert ( smbios.address != UNULL );
    133 
    134 	if ( len > structure->header.len )
    135 		len = structure->header.len;
    136 	copy_from_user ( data, smbios.address, structure->offset, len );
    137 	return 0;
    138 }
    139 
    140 /**
    141  * Find indexed string within SMBIOS structure
    142  *
    143  * @v structure		SMBIOS structure descriptor
    144  * @v index		String index
    145  * @v data		Buffer for string
    146  * @v len		Length of string buffer
    147  * @ret rc		Length of string, or negative error
    148  */
    149 int read_smbios_string ( struct smbios_structure *structure,
    150 			 unsigned int index, void *data, size_t len ) {
    151 	size_t strings_start = ( structure->offset + structure->header.len );
    152 	size_t strings_end = ( strings_start + structure->strings_len );
    153 	size_t offset;
    154 	size_t string_len;
    155 
    156 	assert ( smbios.address != UNULL );
    157 
    158 	/* String numbers start at 1 (0 is used to indicate "no string") */
    159 	if ( ! index )
    160 		return -ENOENT;
    161 
    162 	for ( offset = strings_start ; offset < strings_end ;
    163 	      offset += ( string_len + 1 ) ) {
    164 		/* Get string length.  This is known safe, since the
    165 		 * smbios_strings struct is constructed so as to
    166 		 * always end on a string boundary.
    167 		 */
    168 		string_len = strlen_user ( smbios.address, offset );
    169 		if ( --index == 0 ) {
    170 			/* Copy string, truncating as necessary. */
    171 			if ( len > string_len )
    172 				len = string_len;
    173 			copy_from_user ( data, smbios.address, offset, len );
    174 			return string_len;
    175 		}
    176 	}
    177 
    178 	DBG ( "SMBIOS string index %d not found\n", index );
    179 	return -ENOENT;
    180 }
    181