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