Browse Source

Update SMBIOS internal API to be easier to use, and not to require

potentially exorbitant amounts of stack space.
tags/v0.9.3
Michael Brown 17 years ago
parent
commit
68c438954d
2 changed files with 214 additions and 101 deletions
  1. 176
    100
      src/arch/i386/firmware/pcbios/smbios.c
  2. 38
    1
      src/arch/i386/include/smbios.h

+ 176
- 100
src/arch/i386/firmware/pcbios/smbios.c View File

@@ -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
 }

+ 38
- 1
src/arch/i386/include/smbios.h View File

@@ -6,6 +6,43 @@
6 6
  * System Management BIOS
7 7
  */
8 8
 
9
-extern int find_smbios_serial ( void *data, size_t len );
9
+#include <stdint.h>
10
+
11
+/** An SMBIOS structure header */
12
+struct smbios_header {
13
+	/** Type */
14
+	uint8_t type;
15
+	/** Length */
16
+	uint8_t length;
17
+	/** Handle */
18
+	uint16_t handle;
19
+} __attribute__ (( packed ));
20
+
21
+/** SMBIOS system information structure */
22
+struct smbios_system_information {
23
+	/** SMBIOS structure header */
24
+	struct smbios_header header;
25
+	/** Manufacturer string */
26
+	uint8_t manufacturer;
27
+	/** Product string */
28
+	uint8_t product;
29
+	/** Version string */
30
+	uint8_t version;
31
+	/** Serial number string */
32
+	uint8_t serial;
33
+	/** UUID */
34
+	uint8_t uuid[16];
35
+} __attribute__ (( packed ));
36
+
37
+/** SMBIOS system information structure type */
38
+#define SMBIOS_TYPE_SYSTEM_INFORMATION 1
39
+
40
+struct smbios_strings;
41
+extern int find_smbios_structure ( unsigned int type,
42
+				   void *structure, size_t length,
43
+				   struct smbios_strings *strings );
44
+extern int find_smbios_string ( struct smbios_strings *strings,
45
+				unsigned int index,
46
+				char *buffer, size_t length );
10 47
 
11 48
 #endif /* _SMBIOS_H */

Loading…
Cancel
Save