|
@@ -20,6 +20,7 @@
|
20
|
20
|
#include <string.h>
|
21
|
21
|
#include <stdio.h>
|
22
|
22
|
#include <errno.h>
|
|
23
|
+#include <gpxe/uaccess.h>
|
23
|
24
|
#include <realmode.h>
|
24
|
25
|
#include <pnpbios.h>
|
25
|
26
|
#include <smbios.h>
|
|
@@ -30,11 +31,16 @@
|
30
|
31
|
*
|
31
|
32
|
*/
|
32
|
33
|
|
33
|
|
-/** Signature for an SMBIOS structure */
|
|
34
|
+/** Signature for SMBIOS entry point */
|
34
|
35
|
#define SMBIOS_SIGNATURE \
|
35
|
36
|
( ( '_' << 0 ) + ( 'S' << 8 ) + ( 'M' << 16 ) + ( '_' << 24 ) )
|
36
|
37
|
|
37
|
|
-/** SMBIOS entry point */
|
|
38
|
+/**
|
|
39
|
+ * SMBIOS entry point
|
|
40
|
+ *
|
|
41
|
+ * This is the single table which describes the list of SMBIOS
|
|
42
|
+ * structures. It is located by scanning through the BIOS segment.
|
|
43
|
+ */
|
38
|
44
|
struct smbios_entry {
|
39
|
45
|
/** Signature
|
40
|
46
|
*
|
|
@@ -69,31 +75,44 @@ struct smbios_entry {
|
69
|
75
|
uint8_t bcd_revision;
|
70
|
76
|
} __attribute__ (( packed ));
|
71
|
77
|
|
72
|
|
-/** An SMBIOS structure */
|
|
78
|
+/**
|
|
79
|
+ * SMBIOS entry point descriptor
|
|
80
|
+ *
|
|
81
|
+ * This contains the information from the SMBIOS entry point that we
|
|
82
|
+ * care about.
|
|
83
|
+ */
|
73
|
84
|
struct smbios {
|
74
|
|
- /** Type */
|
75
|
|
- uint8_t type;
|
76
|
|
- /** Length */
|
77
|
|
- uint8_t length;
|
78
|
|
- /** Handle */
|
79
|
|
- uint16_t handle;
|
80
|
|
-} __attribute__ (( packed ));
|
|
85
|
+ /** Start of SMBIOS structures */
|
|
86
|
+ userptr_t address;
|
|
87
|
+ /** Length of SMBIOS structures */
|
|
88
|
+ size_t length;
|
|
89
|
+ /** Number of SMBIOS structures */
|
|
90
|
+ unsigned int count;
|
|
91
|
+};
|
81
|
92
|
|
82
|
|
-struct smbios_system_information {
|
83
|
|
- struct smbios header;
|
84
|
|
- uint8_t manufacturer;
|
85
|
|
- uint8_t product;
|
86
|
|
- uint8_t version;
|
87
|
|
- uint8_t serial;
|
88
|
|
-} __attribute__ (( packed ));
|
|
93
|
+/**
|
|
94
|
+ * SMBIOS strings descriptor
|
|
95
|
+ *
|
|
96
|
+ * This is returned as part of the search for an SMBIOS structure, and
|
|
97
|
+ * contains the information needed for extracting the strings within
|
|
98
|
+ * the "unformatted" portion of the structure.
|
|
99
|
+ */
|
|
100
|
+struct smbios_strings {
|
|
101
|
+ /** Start of strings data */
|
|
102
|
+ userptr_t data;
|
|
103
|
+ /** Length of strings data */
|
|
104
|
+ size_t length;
|
|
105
|
+};
|
89
|
106
|
|
90
|
107
|
/**
|
91
|
108
|
* Find SMBIOS
|
92
|
109
|
*
|
93
|
|
- * @v emtry SMBIOS entry point to fill in
|
94
|
|
- * @ret rc Return status code
|
|
110
|
+ * @ret smbios SMBIOS entry point descriptor, or NULL if not found
|
95
|
111
|
*/
|
96
|
|
-static int find_smbios_entry ( struct smbios_entry *entry ) {
|
|
112
|
+static struct smbios * find_smbios ( void ) {
|
|
113
|
+ static struct smbios smbios = {
|
|
114
|
+ .address = UNULL,
|
|
115
|
+ };
|
97
|
116
|
union {
|
98
|
117
|
struct smbios_entry entry;
|
99
|
118
|
uint8_t bytes[256]; /* 256 is maximum length possible */
|
|
@@ -101,7 +120,11 @@ static int find_smbios_entry ( struct smbios_entry *entry ) {
|
101
|
120
|
unsigned int offset;
|
102
|
121
|
size_t len;
|
103
|
122
|
unsigned int i;
|
104
|
|
- uint8_t sum = 0;
|
|
123
|
+ uint8_t sum;
|
|
124
|
+
|
|
125
|
+ /* Return cached result if available */
|
|
126
|
+ if ( smbios.address != UNULL )
|
|
127
|
+ return &smbios;
|
105
|
128
|
|
106
|
129
|
/* Try to find SMBIOS */
|
107
|
130
|
for ( offset = 0 ; offset < 0x10000 ; offset += 0x10 ) {
|
|
@@ -115,7 +138,7 @@ static int find_smbios_entry ( struct smbios_entry *entry ) {
|
115
|
138
|
/* Read whole header and verify checksum */
|
116
|
139
|
len = u.entry.length;
|
117
|
140
|
copy_from_real ( &u.bytes, BIOS_SEG, offset, len );
|
118
|
|
- for ( i = 0 ; i < len ; i++ ) {
|
|
141
|
+ for ( i = 0 , sum = 0 ; i < len ; i++ ) {
|
119
|
142
|
sum += u.bytes[i];
|
120
|
143
|
}
|
121
|
144
|
if ( sum != 0 ) {
|
|
@@ -127,72 +150,105 @@ static int find_smbios_entry ( struct smbios_entry *entry ) {
|
127
|
150
|
/* Fill result structure */
|
128
|
151
|
DBG ( "Found SMBIOS entry point at %04x:%04x\n",
|
129
|
152
|
BIOS_SEG, offset );
|
130
|
|
- memcpy ( entry, &u.entry, sizeof ( *entry ) );
|
131
|
|
- return 0;
|
|
153
|
+ smbios.address = phys_to_user ( u.entry.smbios_address );
|
|
154
|
+ smbios.length = u.entry.smbios_length;
|
|
155
|
+ smbios.count = u.entry.smbios_count;
|
|
156
|
+ return &smbios;
|
132
|
157
|
}
|
133
|
158
|
|
134
|
159
|
DBG ( "No SMBIOS found\n" );
|
135
|
|
- return -ENOENT;
|
|
160
|
+ return NULL;
|
|
161
|
+}
|
|
162
|
+
|
|
163
|
+/**
|
|
164
|
+ * Find SMBIOS strings terminator
|
|
165
|
+ *
|
|
166
|
+ * @v smbios SMBIOS entry point descriptor
|
|
167
|
+ * @v offset Offset to start of strings
|
|
168
|
+ * @ret offset Offset to strings terminator, or 0 if not found
|
|
169
|
+ */
|
|
170
|
+static size_t find_strings_terminator ( struct smbios *smbios,
|
|
171
|
+ size_t offset ) {
|
|
172
|
+ size_t max_offset = ( smbios->length - 2 );
|
|
173
|
+ uint16_t nulnul;
|
|
174
|
+
|
|
175
|
+ for ( ; offset <= max_offset ; offset++ ) {
|
|
176
|
+ copy_from_user ( &nulnul, smbios->address, offset, 2 );
|
|
177
|
+ if ( nulnul == 0 )
|
|
178
|
+ return ( offset + 1 );
|
|
179
|
+ }
|
|
180
|
+ return 0;
|
136
|
181
|
}
|
137
|
182
|
|
138
|
183
|
/**
|
139
|
184
|
* Find specific structure type within SMBIOS
|
140
|
185
|
*
|
141
|
|
- * @v entry SMBIOS entry point
|
142
|
|
- * @v type Structure type
|
143
|
|
- * @v data SMBIOS structure buffer to fill in
|
|
186
|
+ * @v type Structure type to search for
|
|
187
|
+ * @v structure Buffer to fill in with structure
|
|
188
|
+ * @v length Length of buffer
|
|
189
|
+ * @v strings Strings descriptor to fill in, or NULL
|
144
|
190
|
* @ret rc Return status code
|
145
|
|
- *
|
146
|
|
- * The buffer must be at least @c entry->max bytes in size.
|
147
|
191
|
*/
|
148
|
|
-static int find_smbios ( struct smbios_entry *entry, unsigned int type,
|
149
|
|
- void *data ) {
|
150
|
|
- struct smbios *smbios = data;
|
151
|
|
- userptr_t smbios_address = phys_to_user ( entry->smbios_address );
|
|
192
|
+int find_smbios_structure ( unsigned int type, void *structure,
|
|
193
|
+ size_t length, struct smbios_strings *strings ) {
|
|
194
|
+ struct smbios *smbios;
|
|
195
|
+ struct smbios_header header;
|
|
196
|
+ struct smbios_strings temp_strings;
|
152
|
197
|
unsigned int count = 0;
|
153
|
198
|
size_t offset = 0;
|
154
|
|
- size_t frag_len;
|
155
|
|
- void *end;
|
156
|
|
-
|
157
|
|
- while ( ( offset < entry->smbios_length ) &&
|
158
|
|
- ( count < entry->smbios_count ) ) {
|
159
|
|
- /* Read next SMBIOS structure */
|
160
|
|
- frag_len = ( entry->smbios_length - offset );
|
161
|
|
- if ( frag_len > entry->max )
|
162
|
|
- frag_len = entry->max;
|
163
|
|
- copy_from_user ( data, smbios_address, offset, frag_len );
|
164
|
|
-
|
165
|
|
- /* Sanity protection; ensure the last two bytes of the
|
166
|
|
- * buffer are 0x00,0x00, just so that a terminator
|
167
|
|
- * exists somewhere. Also ensure that this lies
|
168
|
|
- * outside the formatted area.
|
169
|
|
- */
|
170
|
|
- *( ( uint16_t * ) ( data + entry->max - 2 ) ) = 0;
|
171
|
|
- if ( smbios->length > ( entry->max - 2 ) ) {
|
172
|
|
- DBG ( "Invalid SMBIOS structure length %zd\n",
|
173
|
|
- smbios->length );
|
|
199
|
+ size_t strings_offset;
|
|
200
|
+ size_t terminator_offset;
|
|
201
|
+
|
|
202
|
+ /* Locate SMBIOS entry point */
|
|
203
|
+ if ( ! ( smbios = find_smbios() ) )
|
|
204
|
+ return -ENOENT;
|
|
205
|
+
|
|
206
|
+ /* Ensure that we have a usable strings descriptor buffer */
|
|
207
|
+ if ( ! strings )
|
|
208
|
+ strings = &temp_strings;
|
|
209
|
+
|
|
210
|
+ /* Scan through list of structures */
|
|
211
|
+ while ( ( ( offset + sizeof ( header ) ) < smbios->length ) &&
|
|
212
|
+ ( count < smbios->count ) ) {
|
|
213
|
+
|
|
214
|
+ /* Read next SMBIOS structure header */
|
|
215
|
+ copy_from_user ( &header, smbios->address, offset,
|
|
216
|
+ sizeof ( header ) );
|
|
217
|
+
|
|
218
|
+ /* Determine start and extent of strings block */
|
|
219
|
+ strings_offset = ( offset + header.length );
|
|
220
|
+ if ( strings_offset > smbios->length ) {
|
|
221
|
+ DBG ( "SMBIOS structure at offset %zx with length "
|
|
222
|
+ "%zx extends beyond SMBIOS\n", offset,
|
|
223
|
+ header.length );
|
|
224
|
+ return -ENOENT;
|
|
225
|
+ }
|
|
226
|
+ terminator_offset =
|
|
227
|
+ find_strings_terminator ( smbios, strings_offset );
|
|
228
|
+ if ( ! terminator_offset ) {
|
|
229
|
+ DBG ( "SMBIOS structure at offset %zx has "
|
|
230
|
+ "unterminated strings section\n", offset );
|
174
|
231
|
return -ENOENT;
|
175
|
232
|
}
|
|
233
|
+ strings->data = userptr_add ( smbios->address,
|
|
234
|
+ strings_offset );
|
|
235
|
+ strings->length = ( terminator_offset - strings_offset );
|
176
|
236
|
|
177
|
|
- DBG ( "Found SMBIOS structure type %d at offset %zx\n",
|
178
|
|
- smbios->type, offset );
|
|
237
|
+ DBG ( "SMBIOS structure at offset %zx has type %d, "
|
|
238
|
+ "length %zx, strings length %zx\n",
|
|
239
|
+ offset, header.type, header.length, strings->length );
|
179
|
240
|
|
180
|
241
|
/* If this is the structure we want, return */
|
181
|
|
- if ( smbios->type == type )
|
|
242
|
+ if ( header.type == type ) {
|
|
243
|
+ if ( length > header.length )
|
|
244
|
+ length = header.length;
|
|
245
|
+ copy_from_user ( structure, smbios->address,
|
|
246
|
+ offset, length );
|
182
|
247
|
return 0;
|
183
|
|
-
|
184
|
|
- /* Find end of record. This will always exist, thanks
|
185
|
|
- * to our sanity check above.
|
186
|
|
- */
|
187
|
|
- for ( end = ( data + smbios->length ) ;
|
188
|
|
- end < ( data + entry->max ) ; end++ ) {
|
189
|
|
- if ( *( ( uint16_t * ) end ) == 0 ) {
|
190
|
|
- end += 2;
|
191
|
|
- break;
|
192
|
|
- }
|
193
|
248
|
}
|
194
|
249
|
|
195
|
|
- offset += ( end - data );
|
|
250
|
+ /* Move to next SMBIOS structure */
|
|
251
|
+ offset = ( terminator_offset + 1 );
|
196
|
252
|
count++;
|
197
|
253
|
}
|
198
|
254
|
|
|
@@ -203,56 +259,76 @@ static int find_smbios ( struct smbios_entry *entry, unsigned int type,
|
203
|
259
|
/**
|
204
|
260
|
* Find indexed string within SMBIOS structure
|
205
|
261
|
*
|
206
|
|
- * @v data SMBIOS structure
|
|
262
|
+ * @v strings SMBIOS strings descriptor
|
207
|
263
|
* @v index String index
|
208
|
|
- * @ret string String, or NULL
|
|
264
|
+ * @v buffer Buffer for string
|
|
265
|
+ * @v length Length of string buffer
|
|
266
|
+ * @ret rc Return status code
|
209
|
267
|
*/
|
210
|
|
-static const char * find_smbios_string ( void *data, unsigned int index ) {
|
211
|
|
- struct smbios *smbios = data;
|
212
|
|
- const char *string;
|
213
|
|
- size_t len;
|
|
268
|
+int find_smbios_string ( struct smbios_strings *strings, unsigned int index,
|
|
269
|
+ char *buffer, size_t length ) {
|
|
270
|
+ size_t offset = 0;
|
|
271
|
+ size_t string_len;
|
214
|
272
|
|
|
273
|
+ /* Zero buffer. This ensures that a valid NUL terminator is
|
|
274
|
+ * always present (unless length==0).
|
|
275
|
+ */
|
|
276
|
+ memset ( buffer, 0, length );
|
|
277
|
+
|
|
278
|
+ /* String numbers start at 1 (0 is used to indicate "no string") */
|
215
|
279
|
if ( ! index )
|
216
|
|
- return NULL;
|
217
|
|
-
|
218
|
|
- string = ( data + smbios->length );
|
219
|
|
- while ( --index ) {
|
220
|
|
- /* Move to next string */
|
221
|
|
- len = strlen ( string );
|
222
|
|
- if ( len == 0 ) {
|
223
|
|
- /* Reached premature end of string table */
|
224
|
|
- DBG ( "SMBIOS string index %d not found\n", index );
|
225
|
|
- return NULL;
|
|
280
|
+ return 0;
|
|
281
|
+
|
|
282
|
+ while ( offset < strings->length ) {
|
|
283
|
+ /* Get string length. This is known safe, since the
|
|
284
|
+ * smbios_strings struct is constructed so as to
|
|
285
|
+ * always end on a string boundary.
|
|
286
|
+ */
|
|
287
|
+ string_len = strlen_user ( strings->data, offset );
|
|
288
|
+ if ( --index == 0 ) {
|
|
289
|
+ /* Copy string, truncating as necessary. */
|
|
290
|
+ if ( string_len >= length )
|
|
291
|
+ string_len = ( length - 1 );
|
|
292
|
+ copy_from_user ( buffer, strings->data,
|
|
293
|
+ offset, string_len );
|
|
294
|
+ return 0;
|
226
|
295
|
}
|
227
|
|
- string += ( len + 1 );
|
|
296
|
+ offset += ( string_len + 1 );
|
228
|
297
|
}
|
229
|
|
- return string;
|
|
298
|
+
|
|
299
|
+ DBG ( "SMBIOS string index %d not found\n", index );
|
|
300
|
+ return -ENOENT;
|
230
|
301
|
}
|
231
|
302
|
|
232
|
303
|
/**
|
233
|
304
|
* Find SMBIOS serial number
|
234
|
305
|
*
|
235
|
|
- * @v data Buffer to fill
|
236
|
|
- * @v len Length of buffer
|
237
|
306
|
*/
|
238
|
|
-int find_smbios_serial ( void *data, size_t len ) {
|
239
|
|
- struct smbios_entry entry;
|
240
|
|
- const char *string;
|
|
307
|
+int dump_smbios_info ( void ) {
|
|
308
|
+ struct smbios_system_information sysinfo;
|
|
309
|
+ struct smbios_strings strings;
|
|
310
|
+ char buf[64];
|
241
|
311
|
int rc;
|
242
|
312
|
|
243
|
|
- if ( ( rc = find_smbios_entry ( &entry ) ) != 0 )
|
|
313
|
+ if ( ( rc = find_smbios_structure ( SMBIOS_TYPE_SYSTEM_INFORMATION,
|
|
314
|
+ &sysinfo, sizeof ( sysinfo ),
|
|
315
|
+ &strings ) ) != 0 )
|
|
316
|
+ return rc;
|
|
317
|
+
|
|
318
|
+ DBG_HD ( &sysinfo, sizeof ( sysinfo ) );
|
|
319
|
+
|
|
320
|
+ if ( ( rc = find_smbios_string ( &strings, sysinfo.manufacturer,
|
|
321
|
+ buf, sizeof ( buf ) ) ) != 0 )
|
244
|
322
|
return rc;
|
|
323
|
+ DBG ( "Manufacturer: \"%s\"\n", buf );
|
245
|
324
|
|
246
|
|
- char buffer[entry.max];
|
247
|
|
- if ( ( rc = find_smbios ( &entry, 1, buffer ) ) != 0 )
|
|
325
|
+ if ( ( rc = find_smbios_string ( &strings, sysinfo.product,
|
|
326
|
+ buf, sizeof ( buf ) ) ) != 0 )
|
248
|
327
|
return rc;
|
|
328
|
+ DBG ( "Product: \"%s\"\n", buf );
|
249
|
329
|
|
250
|
|
- struct smbios_system_information *sysinfo = ( void * ) buffer;
|
251
|
|
- string = find_smbios_string ( buffer, sysinfo->serial );
|
252
|
|
- if ( ! string )
|
253
|
|
- return -ENOENT;
|
|
330
|
+ DBG ( "UUID:\n" );
|
|
331
|
+ DBG_HD ( &sysinfo.uuid, sizeof ( sysinfo.uuid ) );
|
254
|
332
|
|
255
|
|
- DBG ( "Found serial number \"%s\"\n", string );
|
256
|
|
- snprintf ( data, len, "%s", string );
|
257
|
333
|
return 0;
|
258
|
334
|
}
|