1 /*
2 * Copyright (C) 2007 Michael Brown <mbrown@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 */
find_strings_terminator(size_t offset)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 */
find_smbios_structure(unsigned int type,struct smbios_structure * structure)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 */
read_smbios_structure(struct smbios_structure * structure,void * data,size_t len)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 */
read_smbios_string(struct smbios_structure * structure,unsigned int index,void * data,size_t len)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