Browse Source

Add ability to read serial number from SMBIOS

tags/v0.9.3
Michael Brown 18 years ago
parent
commit
22ed1fbaf1
2 changed files with 269 additions and 0 deletions
  1. 258
    0
      src/arch/i386/firmware/pcbios/smbios.c
  2. 11
    0
      src/arch/i386/include/smbios.h

+ 258
- 0
src/arch/i386/firmware/pcbios/smbios.c View File

@@ -0,0 +1,258 @@
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
+#include <stdint.h>
20
+#include <string.h>
21
+#include <stdio.h>
22
+#include <errno.h>
23
+#include <realmode.h>
24
+#include <pnpbios.h>
25
+#include <smbios.h>
26
+
27
+/** @file
28
+ *
29
+ * System Management BIOS
30
+ *
31
+ */
32
+
33
+/** Signature for an SMBIOS structure */
34
+#define SMBIOS_SIGNATURE \
35
+        ( ( '_' << 0 ) + ( 'S' << 8 ) + ( 'M' << 16 ) + ( '_' << 24 ) )
36
+
37
+/** SMBIOS entry point */
38
+struct smbios_entry {
39
+	/** Signature
40
+	 *
41
+	 * Must be equal to SMBIOS_SIGNATURE
42
+	 */
43
+	uint32_t signature;
44
+	/** Checksum */
45
+	uint8_t checksum;
46
+	/** Length */
47
+	uint8_t length;
48
+	/** Major version */
49
+	uint8_t major;
50
+	/** Minor version */
51
+	uint8_t minor;
52
+	/** Maximum structure size */
53
+	uint16_t max;
54
+	/** Entry point revision */
55
+	uint8_t revision;
56
+	/** Formatted area */
57
+	uint8_t formatted[5];
58
+	/** DMI Signature */
59
+	uint8_t dmi_signature[5];
60
+	/** DMI checksum */
61
+	uint8_t dmi_checksum;
62
+	/** Structure table length */
63
+	uint16_t smbios_length;
64
+	/** Structure table address */
65
+	physaddr_t smbios_address;
66
+	/** Number of SMBIOS structures */
67
+	uint16_t smbios_count;
68
+	/** BCD revision */
69
+	uint8_t bcd_revision;
70
+} __attribute__ (( packed ));
71
+
72
+/** An SMBIOS structure */
73
+struct smbios {
74
+	/** Type */
75
+	uint8_t type;
76
+	/** Length */
77
+	uint8_t length;
78
+	/** Handle */
79
+	uint16_t handle;
80
+} __attribute__ (( packed ));
81
+
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 ));
89
+
90
+/**
91
+ * Find SMBIOS
92
+ *
93
+ * @v emtry		SMBIOS entry point to fill in
94
+ * @ret rc		Return status code
95
+ */
96
+static int find_smbios_entry ( struct smbios_entry *entry ) {
97
+	union {
98
+		struct smbios_entry entry;
99
+		uint8_t bytes[256]; /* 256 is maximum length possible */
100
+	} u;
101
+	unsigned int offset;
102
+	size_t len;
103
+	unsigned int i;
104
+	uint8_t sum = 0;
105
+
106
+	/* Try to find SMBIOS */
107
+	for ( offset = 0 ; offset < 0x10000 ; offset += 0x10 ) {
108
+
109
+		/* Read start of header and verify signature */
110
+		copy_from_real ( &u.entry, BIOS_SEG, offset,
111
+				 sizeof ( u.entry ));
112
+		if ( u.entry.signature != SMBIOS_SIGNATURE )
113
+			continue;
114
+
115
+		/* Read whole header and verify checksum */
116
+		len = u.entry.length;
117
+		copy_from_real ( &u.bytes, BIOS_SEG, offset, len );
118
+		for ( i = 0 ; i < len ; i++ ) {
119
+			sum += u.bytes[i];
120
+		}
121
+		if ( sum != 0 ) {
122
+			DBG ( "SMBIOS at %04x:%04x has bad checksum %02x\n",
123
+			      BIOS_SEG, offset, sum );
124
+			continue;
125
+		}
126
+
127
+		/* Fill result structure */
128
+		DBG ( "Found SMBIOS entry point at %04x:%04x\n",
129
+		      BIOS_SEG, offset );
130
+		memcpy ( entry, &u.entry, sizeof ( *entry ) );
131
+		return 0;
132
+	}
133
+
134
+	DBG ( "No SMBIOS found\n" );
135
+	return -ENOENT;
136
+}
137
+
138
+/**
139
+ * Find specific structure type within SMBIOS
140
+ *
141
+ * @v entry		SMBIOS entry point
142
+ * @v type		Structure type
143
+ * @v data		SMBIOS structure buffer to fill in
144
+ * @ret rc		Return status code
145
+ *
146
+ * The buffer must be at least @c entry->max bytes in size.
147
+ */
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 );
152
+	unsigned int count = 0;
153
+	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 );
174
+			return -ENOENT;
175
+		}
176
+
177
+		DBG ( "Found SMBIOS structure type %d at offset %zx\n",
178
+		      smbios->type, offset );
179
+
180
+		/* If this is the structure we want, return */
181
+		if ( smbios->type == type )
182
+			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
+		}
194
+
195
+		offset += ( end - data );
196
+		count++;
197
+	}
198
+
199
+	DBG ( "SMBIOS structure type %d not found\n", type );
200
+	return -ENOENT;
201
+}
202
+
203
+/**
204
+ * Find indexed string within SMBIOS structure
205
+ *
206
+ * @v data		SMBIOS structure
207
+ * @v index		String index
208
+ * @ret string		String, or NULL
209
+ */
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;
214
+
215
+	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;
226
+		}
227
+		string += ( len + 1 );
228
+	}
229
+	return string;
230
+}
231
+
232
+/**
233
+ * Find SMBIOS serial number
234
+ *
235
+ * @v data		Buffer to fill
236
+ * @v len		Length of buffer
237
+ */
238
+int find_smbios_serial ( void *data, size_t len ) {
239
+	struct smbios_entry entry;
240
+	const char *string;
241
+	int rc;
242
+
243
+	if ( ( rc = find_smbios_entry ( &entry ) ) != 0 )
244
+		return rc;
245
+
246
+	char buffer[entry.max];
247
+	if ( ( rc = find_smbios ( &entry, 1, buffer ) ) != 0 )
248
+		return rc;
249
+
250
+	struct smbios_system_information *sysinfo = ( void * ) buffer;
251
+	string = find_smbios_string ( buffer, sysinfo->serial );
252
+	if ( ! string )
253
+		return -ENOENT;
254
+
255
+	DBG ( "Found serial number \"%s\"\n", string );
256
+	snprintf ( data, len, "%s", string );
257
+	return 0;
258
+}

+ 11
- 0
src/arch/i386/include/smbios.h View File

@@ -0,0 +1,11 @@
1
+#ifndef _SMBIOS_H
2
+#define _SMBIOS_H
3
+
4
+/** @file
5
+ *
6
+ * System Management BIOS
7
+ */
8
+
9
+extern int find_smbios_serial ( void *data, size_t len );
10
+
11
+#endif /* _SMBIOS_H */

Loading…
Cancel
Save